Merge "add IGNORE INDEX option to mysql database handler"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 12 Sep 2016 12:33:59 +0000 (12:33 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 12 Sep 2016 12:33:59 +0000 (12:33 +0000)
1188 files changed:
Gemfile
Gemfile.lock
RELEASE-NOTES-1.28
UPGRADE
autoload.php
composer.json
docs/README
docs/database.txt
docs/distributors.txt
docs/extension.schema.json
docs/extension.schema.v1.json [new file with mode: 0644]
docs/hooks.txt
docs/kss/Makefile
docs/uidesign/mediawiki.action.history.diff.html [deleted file]
docs/uidesign/mediawiki.diff.html [new file with mode: 0644]
extensions/README
img_auth.php
includes/AutoLoader.php
includes/Block.php
includes/Category.php
includes/CategoryFinder.php
includes/CategoryViewer.php
includes/DefaultSettings.php
includes/Defines.php
includes/DummyLinker.php
includes/EditPage.php
includes/FauxRequest.php
includes/FeedUtils.php
includes/FileDeleteForm.php
includes/GlobalFunctions.php
includes/HistoryBlob.php
includes/Hooks.php
includes/Html.php
includes/HttpFunctions.php
includes/LinkFilter.php
includes/Linker.php
includes/MWTimestamp.php
includes/MagicWord.php
includes/MediaWiki.php
includes/MediaWikiServices.php
includes/MergeHistory.php
includes/Message.php
includes/MovePage.php
includes/OutputPage.php
includes/PageProps.php
includes/PathRouter.php
includes/Pingback.php [new file with mode: 0644]
includes/Preferences.php
includes/PrefixSearch.php
includes/Revision.php
includes/RevisionList.php
includes/Sanitizer.php
includes/ServiceWiring.php
includes/Services/ServiceContainer.php
includes/Setup.php
includes/SiteConfiguration.php
includes/SiteStats.php
includes/Status.php
includes/StreamFile.php
includes/StubObject.php
includes/Title.php
includes/WatchedItem.php
includes/WatchedItemQueryService.php
includes/WatchedItemStore.php
includes/WebRequest.php
includes/WebResponse.php
includes/WikiMap.php
includes/actions/Action.php
includes/actions/HistoryAction.php
includes/actions/InfoAction.php
includes/actions/PurgeAction.php
includes/actions/RevertAction.php
includes/actions/RollbackAction.php
includes/actions/UnwatchAction.php
includes/actions/ViewAction.php
includes/actions/WatchAction.php
includes/api/ApiAMCreateAccount.php
includes/api/ApiAuthManagerHelper.php
includes/api/ApiBase.php
includes/api/ApiCSPReport.php
includes/api/ApiClientLogin.php
includes/api/ApiContinuationManager.php
includes/api/ApiCreateAccount.php [deleted file]
includes/api/ApiEditPage.php
includes/api/ApiErrorFormatter.php
includes/api/ApiExpandTemplates.php
includes/api/ApiHelp.php
includes/api/ApiLinkAccount.php
includes/api/ApiLogin.php
includes/api/ApiMain.php
includes/api/ApiModuleManager.php
includes/api/ApiOpenSearch.php
includes/api/ApiPageSet.php
includes/api/ApiParamInfo.php
includes/api/ApiParse.php
includes/api/ApiPurge.php
includes/api/ApiQuery.php
includes/api/ApiQueryAllDeletedRevisions.php
includes/api/ApiQueryAuthManagerInfo.php
includes/api/ApiQueryBacklinksprop.php
includes/api/ApiQueryBase.php
includes/api/ApiQueryBlocks.php
includes/api/ApiQueryDuplicateFiles.php
includes/api/ApiQueryInfo.php
includes/api/ApiQueryPrefixSearch.php
includes/api/ApiQueryRecentChanges.php
includes/api/ApiQuerySearch.php
includes/api/ApiQuerySiteinfo.php
includes/api/ApiQueryUserContributions.php
includes/api/ApiQueryUsers.php
includes/api/ApiQueryWatchlist.php
includes/api/ApiQueryWatchlistRaw.php
includes/api/ApiResult.php
includes/api/ApiStashEdit.php
includes/api/ApiTag.php
includes/api/ApiUpload.php
includes/api/ApiWatch.php
includes/api/SearchApi.php
includes/api/i18n/azb.json [new file with mode: 0644]
includes/api/i18n/ba.json
includes/api/i18n/be-tarask.json
includes/api/i18n/bg.json
includes/api/i18n/ca.json
includes/api/i18n/cs.json
includes/api/i18n/de.json
includes/api/i18n/diq.json
includes/api/i18n/en.json
includes/api/i18n/eo.json
includes/api/i18n/es.json
includes/api/i18n/eu.json
includes/api/i18n/fa.json
includes/api/i18n/fr.json
includes/api/i18n/gl.json
includes/api/i18n/he.json
includes/api/i18n/hu.json
includes/api/i18n/it.json
includes/api/i18n/ja.json
includes/api/i18n/ko.json
includes/api/i18n/ksh.json
includes/api/i18n/ku-latn.json
includes/api/i18n/lij.json [new file with mode: 0644]
includes/api/i18n/lt.json
includes/api/i18n/mr.json
includes/api/i18n/oc.json
includes/api/i18n/pl.json
includes/api/i18n/ps.json
includes/api/i18n/pt.json
includes/api/i18n/qqq.json
includes/api/i18n/ru.json
includes/api/i18n/sv.json
includes/api/i18n/uk.json
includes/api/i18n/zh-hans.json
includes/auth/AuthManager.php
includes/auth/AuthManagerAuthPlugin.php
includes/auth/AuthenticationProvider.php
includes/auth/AuthenticationRequest.php
includes/auth/AuthenticationResponse.php
includes/auth/CheckBlocksSecondaryAuthenticationProvider.php
includes/auth/ConfirmLinkSecondaryAuthenticationProvider.php
includes/auth/EmailNotificationSecondaryAuthenticationProvider.php
includes/auth/LocalPasswordPrimaryAuthenticationProvider.php
includes/auth/PasswordAuthenticationRequest.php
includes/auth/PreAuthenticationProvider.php
includes/auth/PrimaryAuthenticationProvider.php
includes/auth/SecondaryAuthenticationProvider.php
includes/auth/TemporaryPasswordPrimaryAuthenticationProvider.php
includes/auth/Throttler.php
includes/cache/BacklinkCache.php
includes/cache/GenderCache.php
includes/cache/HTMLFileCache.php
includes/cache/LinkBatch.php
includes/cache/LinkCache.php
includes/cache/MessageBlobStore.php
includes/cache/MessageCache.php
includes/cache/ObjectFileCache.php [deleted file]
includes/cache/UserCache.php
includes/cache/localisation/LCStoreDB.php
includes/changes/CategoryMembershipChange.php
includes/changes/ChangesList.php
includes/changes/EnhancedChangesList.php
includes/changes/RecentChange.php
includes/changetags/ChangeTags.php
includes/collation/Collation.php
includes/collation/IcuCollation.php
includes/collation/NumericUppercaseCollation.php [new file with mode: 0644]
includes/content/CodeContentHandler.php
includes/content/ContentHandler.php
includes/content/CssContentHandler.php
includes/content/JavaScriptContentHandler.php
includes/content/JsonContent.php
includes/content/JsonContentHandler.php
includes/content/TextContent.php
includes/content/TextContentHandler.php
includes/content/WikiTextStructure.php [new file with mode: 0644]
includes/content/WikitextContent.php
includes/content/WikitextContentHandler.php
includes/dao/DBAccessObjectUtils.php
includes/dao/IDBAccessObject.php
includes/db/ChronologyProtector.php
includes/db/CloneDatabase.php
includes/db/DBConnRef.php
includes/db/Database.php
includes/db/DatabaseError.php
includes/db/DatabaseMssql.php
includes/db/DatabaseMysql.php
includes/db/DatabaseMysqlBase.php
includes/db/DatabaseMysqli.php
includes/db/DatabaseOracle.php
includes/db/DatabasePostgres.php
includes/db/DatabaseSqlite.php
includes/db/DatabaseUtility.php
includes/db/IDatabase.php
includes/db/loadbalancer/LBFactory.php
includes/db/loadbalancer/LBFactoryFake.php
includes/db/loadbalancer/LBFactoryMulti.php
includes/db/loadbalancer/LBFactorySimple.php
includes/db/loadbalancer/LBFactorySingle.php
includes/db/loadbalancer/LoadBalancer.php
includes/debug/MWDebug.php
includes/debug/logger/LegacySpi.php
includes/debug/logger/MonologSpi.php
includes/debug/logger/NullSpi.php
includes/deferred/AtomicSectionUpdate.php
includes/deferred/AutoCommitUpdate.php [new file with mode: 0644]
includes/deferred/CallableUpdate.php [deleted file]
includes/deferred/CdnCacheUpdate.php
includes/deferred/DataUpdate.php
includes/deferred/DeferrableCallback.php [new file with mode: 0644]
includes/deferred/DeferredUpdates.php
includes/deferred/EnqueueableDataUpdate.php [new file with mode: 0644]
includes/deferred/LinksDeletionUpdate.php
includes/deferred/LinksUpdate.php
includes/deferred/MWCallableUpdate.php [new file with mode: 0644]
includes/deferred/SiteStatsUpdate.php
includes/deferred/SqlDataUpdate.php
includes/diff/DifferenceEngine.php
includes/exception/MWException.php
includes/exception/MWExceptionHandler.php
includes/exception/UserNotLoggedIn.php
includes/export/DumpStringOutput.php [new file with mode: 0644]
includes/externalstore/ExternalStoreDB.php
includes/filebackend/FileBackend.php
includes/filebackend/FileBackendStore.php
includes/filebackend/MemoryFileBackend.php
includes/filebackend/SwiftFileBackend.php
includes/filebackend/lockmanager/DBLockManager.php
includes/filebackend/lockmanager/LockManager.php
includes/filebackend/lockmanager/MemcLockManager.php
includes/filebackend/lockmanager/MySqlLockManager.php [new file with mode: 0644]
includes/filebackend/lockmanager/PostgreSqlLockManager.php [new file with mode: 0644]
includes/filebackend/lockmanager/RedisLockManager.php
includes/filerepo/FileBackendDBRepoWrapper.php
includes/filerepo/FileRepo.php
includes/filerepo/FileRepoStatus.php
includes/filerepo/ForeignAPIRepo.php
includes/filerepo/ForeignDBViaLBRepo.php
includes/filerepo/LocalRepo.php
includes/filerepo/RepoGroup.php
includes/filerepo/file/ArchivedFile.php
includes/filerepo/file/File.php
includes/filerepo/file/LocalFile.php
includes/gallery/ImageGalleryBase.php
includes/gallery/SlideshowImageGallery.php [new file with mode: 0644]
includes/gallery/TraditionalImageGallery.php
includes/htmlform/HTMLApiField.php [deleted file]
includes/htmlform/HTMLAutoCompleteSelectField.php [deleted file]
includes/htmlform/HTMLButtonField.php [deleted file]
includes/htmlform/HTMLCheckField.php [deleted file]
includes/htmlform/HTMLCheckMatrix.php [deleted file]
includes/htmlform/HTMLComboboxField.php [deleted file]
includes/htmlform/HTMLEditTools.php [deleted file]
includes/htmlform/HTMLFloatField.php [deleted file]
includes/htmlform/HTMLForm.php
includes/htmlform/HTMLFormElement.php [new file with mode: 0644]
includes/htmlform/HTMLFormField.php
includes/htmlform/HTMLFormFieldCloner.php [deleted file]
includes/htmlform/HTMLFormFieldWithButton.php [deleted file]
includes/htmlform/HTMLHiddenField.php [deleted file]
includes/htmlform/HTMLInfoField.php [deleted file]
includes/htmlform/HTMLIntField.php [deleted file]
includes/htmlform/HTMLMultiSelectField.php [deleted file]
includes/htmlform/HTMLRadioField.php [deleted file]
includes/htmlform/HTMLSelectAndOtherField.php [deleted file]
includes/htmlform/HTMLSelectField.php [deleted file]
includes/htmlform/HTMLSelectLimitField.php [deleted file]
includes/htmlform/HTMLSelectNamespace.php [deleted file]
includes/htmlform/HTMLSelectNamespaceWithButton.php [deleted file]
includes/htmlform/HTMLSelectOrOtherField.php [deleted file]
includes/htmlform/HTMLSubmitField.php [deleted file]
includes/htmlform/HTMLTagFilter.php [deleted file]
includes/htmlform/HTMLTextAreaField.php [deleted file]
includes/htmlform/HTMLTextField.php [deleted file]
includes/htmlform/HTMLTextFieldWithButton.php [deleted file]
includes/htmlform/HTMLTitleTextField.php [deleted file]
includes/htmlform/HTMLUserTextField.php [deleted file]
includes/htmlform/fields/HTMLApiField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLAutoCompleteSelectField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLButtonField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLCheckField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLCheckMatrix.php [new file with mode: 0644]
includes/htmlform/fields/HTMLComboboxField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLEditTools.php [new file with mode: 0644]
includes/htmlform/fields/HTMLFloatField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLFormFieldCloner.php [new file with mode: 0644]
includes/htmlform/fields/HTMLFormFieldWithButton.php [new file with mode: 0644]
includes/htmlform/fields/HTMLHiddenField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLInfoField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLIntField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLMultiSelectField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLRadioField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLSelectAndOtherField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLSelectField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLSelectLimitField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLSelectNamespace.php [new file with mode: 0644]
includes/htmlform/fields/HTMLSelectNamespaceWithButton.php [new file with mode: 0644]
includes/htmlform/fields/HTMLSelectOrOtherField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLSubmitField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLTagFilter.php [new file with mode: 0644]
includes/htmlform/fields/HTMLTextAreaField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLTextField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLTextFieldWithButton.php [new file with mode: 0644]
includes/htmlform/fields/HTMLTitleTextField.php [new file with mode: 0644]
includes/htmlform/fields/HTMLUserTextField.php [new file with mode: 0644]
includes/import/WikiImporter.php
includes/import/WikiRevision.php
includes/installer/DatabaseUpdater.php
includes/installer/Installer.php
includes/installer/LocalSettingsGenerator.php
includes/installer/MssqlUpdater.php
includes/installer/MysqlUpdater.php
includes/installer/OracleUpdater.php
includes/installer/PostgresUpdater.php
includes/installer/SqliteUpdater.php
includes/installer/WebInstallerName.php
includes/installer/WebInstallerOutput.php
includes/installer/i18n/ast.json
includes/installer/i18n/ba.json
includes/installer/i18n/be-tarask.json
includes/installer/i18n/bg.json
includes/installer/i18n/bn.json
includes/installer/i18n/cs.json
includes/installer/i18n/de.json
includes/installer/i18n/diq.json
includes/installer/i18n/dty.json [new file with mode: 0644]
includes/installer/i18n/en.json
includes/installer/i18n/eo.json
includes/installer/i18n/es.json
includes/installer/i18n/et.json
includes/installer/i18n/eu.json
includes/installer/i18n/fa.json
includes/installer/i18n/fi.json
includes/installer/i18n/fr.json
includes/installer/i18n/gl.json
includes/installer/i18n/he.json
includes/installer/i18n/hu.json
includes/installer/i18n/ia.json
includes/installer/i18n/it.json
includes/installer/i18n/ja.json
includes/installer/i18n/ko.json
includes/installer/i18n/ksh.json
includes/installer/i18n/ku-latn.json
includes/installer/i18n/lb.json
includes/installer/i18n/lij.json [new file with mode: 0644]
includes/installer/i18n/lt.json
includes/installer/i18n/mk.json
includes/installer/i18n/my.json
includes/installer/i18n/ne.json
includes/installer/i18n/pl.json
includes/installer/i18n/qqq.json
includes/installer/i18n/ru.json
includes/installer/i18n/sk.json
includes/installer/i18n/sv.json
includes/installer/i18n/tcy.json
includes/installer/i18n/uk.json
includes/installer/i18n/vi.json
includes/installer/i18n/wuu.json
includes/installer/i18n/zh-hans.json
includes/installer/i18n/zh-hant.json
includes/interwiki/ClassicInterwikiLookup.php
includes/interwiki/InterwikiLookup.php
includes/jobqueue/JobQueue.php
includes/jobqueue/JobQueueDB.php
includes/jobqueue/JobQueueGroup.php
includes/jobqueue/JobRunner.php
includes/jobqueue/jobs/AssembleUploadChunksJob.php
includes/jobqueue/jobs/CategoryMembershipChangeJob.php
includes/jobqueue/jobs/DeleteLinksJob.php
includes/jobqueue/jobs/HTMLCacheUpdateJob.php
includes/jobqueue/jobs/NullJob.php
includes/jobqueue/jobs/RecentChangesUpdateJob.php
includes/jobqueue/jobs/RefreshLinksJob.php
includes/jobqueue/utils/PurgeJobUtils.php [new file with mode: 0644]
includes/libs/HtmlArmor.php
includes/libs/MapCacheLRU.php
includes/libs/MultiHttpClient.php
includes/libs/ProcessCacheLRU.php
includes/libs/ReverseArrayIterator.php [new file with mode: 0644]
includes/libs/SamplingStatsdClient.php
includes/libs/WaitConditionLoop.php [new file with mode: 0644]
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/CachedBagOStuff.php
includes/libs/objectcache/EmptyBagOStuff.php
includes/libs/objectcache/IExpiringStore.php
includes/libs/objectcache/MemcachedBagOStuff.php
includes/libs/objectcache/MemcachedClient.php
includes/libs/objectcache/MultiWriteBagOStuff.php
includes/libs/objectcache/RESTBagOStuff.php [new file with mode: 0644]
includes/libs/objectcache/ReplicatedBagOStuff.php
includes/libs/objectcache/WANObjectCache.php
includes/libs/virtualrest/RestbaseVirtualRESTService.php
includes/libs/virtualrest/VirtualRESTServiceClient.php
includes/linker/LinkRenderer.php
includes/linker/LinkRendererFactory.php
includes/logging/LogEntry.php
includes/logging/LogPager.php
includes/media/DjVuImage.php
includes/media/MediaHandler.php
includes/media/MediaHandlerFactory.php [new file with mode: 0644]
includes/media/MediaTransformOutput.php
includes/objectcache/MemcachedPeclBagOStuff.php
includes/objectcache/ObjectCache.php
includes/objectcache/RedisBagOStuff.php
includes/objectcache/SqlBagOStuff.php
includes/page/Article.php
includes/page/ImageHistoryList.php
includes/page/ImagePage.php
includes/page/WikiPage.php
includes/pager/IndexPager.php
includes/parser/LinkHolderArray.php
includes/parser/MWTidy.php
includes/parser/Parser.php
includes/parser/ParserOutput.php
includes/parser/Preprocessor_DOM.php
includes/parser/Preprocessor_Hash.php
includes/poolcounter/PoolCounterRedis.php
includes/profiler/ProfileSection.php
includes/profiler/ProfilerFunctions.php
includes/profiler/ProfilerStub.php
includes/profiler/SectionProfiler.php
includes/profiler/TransactionProfiler.php
includes/registration/ExtensionProcessor.php
includes/registration/ExtensionRegistry.php
includes/resourceloader/DerivativeResourceLoaderContext.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderClientHtml.php [new file with mode: 0644]
includes/resourceloader/ResourceLoaderContext.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/resourceloader/ResourceLoaderImage.php
includes/resourceloader/ResourceLoaderImageModule.php
includes/resourceloader/ResourceLoaderModule.php
includes/resourceloader/ResourceLoaderSiteModule.php
includes/resourceloader/ResourceLoaderSiteStylesModule.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php
includes/resourceloader/ResourceLoaderUserModule.php
includes/resourceloader/ResourceLoaderUserOptionsModule.php
includes/resourceloader/ResourceLoaderUserStylesModule.php [new file with mode: 0644]
includes/resourceloader/ResourceLoaderUserTokensModule.php
includes/resourceloader/ResourceLoaderWikiModule.php
includes/revisiondelete/RevDelArchiveList.php
includes/revisiondelete/RevDelArchivedFileItem.php
includes/revisiondelete/RevDelFileItem.php
includes/revisiondelete/RevDelFileList.php
includes/revisiondelete/RevDelItem.php
includes/revisiondelete/RevDelList.php
includes/revisiondelete/RevDelLogList.php
includes/revisiondelete/RevDelRevisionList.php
includes/revisiondelete/RevisionDeleter.php
includes/search/DummySearchIndexFieldDefinition.php [new file with mode: 0644]
includes/search/NullIndexField.php [new file with mode: 0644]
includes/search/ParserOutputSearchDataExtractor.php [new file with mode: 0644]
includes/search/SearchDatabase.php
includes/search/SearchEngine.php
includes/search/SearchEngineFactory.php
includes/search/SearchHighlighter.php
includes/search/SearchIndexField.php [new file with mode: 0644]
includes/search/SearchIndexFieldDefinition.php [new file with mode: 0644]
includes/search/SearchMySQL.php
includes/session/Session.php
includes/session/SessionBackend.php
includes/session/SessionInfo.php
includes/session/SessionManager.php
includes/session/SessionManagerInterface.php
includes/session/SessionProvider.php
includes/site/DBSiteStore.php
includes/site/SiteSQLStore.php
includes/skins/BaseTemplate.php
includes/skins/Skin.php
includes/skins/SkinTemplate.php
includes/specialpage/AuthManagerSpecialPage.php
includes/specialpage/ChangesListSpecialPage.php
includes/specialpage/LoginSignupSpecialPage.php
includes/specialpage/QueryPage.php
includes/specialpage/SpecialPage.php
includes/specialpage/SpecialPageFactory.php
includes/specials/SpecialActiveusers.php
includes/specials/SpecialAllPages.php
includes/specials/SpecialBlock.php
includes/specials/SpecialBlockList.php
includes/specials/SpecialBotPasswords.php
includes/specials/SpecialBrokenRedirects.php
includes/specials/SpecialChangeContentModel.php
includes/specials/SpecialChangeCredentials.php
includes/specials/SpecialConfirmemail.php
includes/specials/SpecialContributions.php
includes/specials/SpecialCreateAccount.php
includes/specials/SpecialDeletedContributions.php
includes/specials/SpecialDoubleRedirects.php
includes/specials/SpecialEditWatchlist.php
includes/specials/SpecialEmailInvalidate.php
includes/specials/SpecialEmailuser.php
includes/specials/SpecialExport.php
includes/specials/SpecialJavaScriptTest.php
includes/specials/SpecialLinkSearch.php
includes/specials/SpecialLog.php
includes/specials/SpecialMediaStatistics.php
includes/specials/SpecialMergeHistory.php
includes/specials/SpecialMostlinkedcategories.php
includes/specials/SpecialMovepage.php
includes/specials/SpecialMyLanguage.php
includes/specials/SpecialNewimages.php
includes/specials/SpecialPageLanguage.php
includes/specials/SpecialPagesWithProp.php
includes/specials/SpecialPreferences.php
includes/specials/SpecialPrefixindex.php
includes/specials/SpecialProtectedpages.php
includes/specials/SpecialRandomInCategory.php
includes/specials/SpecialRandompage.php
includes/specials/SpecialRandomrootpage.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialRedirect.php
includes/specials/SpecialResetTokens.php
includes/specials/SpecialRunJobs.php
includes/specials/SpecialSearch.php
includes/specials/SpecialShortpages.php
includes/specials/SpecialTags.php
includes/specials/SpecialTrackingCategories.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialUpload.php
includes/specials/SpecialUploadStash.php
includes/specials/SpecialUserLogin.php
includes/specials/SpecialUserrights.php
includes/specials/SpecialVersion.php
includes/specials/SpecialWatchlist.php
includes/specials/SpecialWhatlinkshere.php
includes/specials/SpecialWithoutinterwiki.php
includes/specials/pagers/AllMessagesTablePager.php
includes/specials/pagers/ContribsPager.php
includes/specials/pagers/DeletedContribsPager.php
includes/specials/pagers/ImageListPager.php
includes/specials/pagers/MergeHistoryPager.php
includes/specials/pagers/NewFilesPager.php
includes/specials/pagers/UsersPager.php
includes/specials/pre-authmanager/README [deleted file]
includes/specials/pre-authmanager/SpecialChangeEmail.php [deleted file]
includes/specials/pre-authmanager/SpecialChangePassword.php [deleted file]
includes/specials/pre-authmanager/SpecialCreateAccount.php [deleted file]
includes/specials/pre-authmanager/SpecialPasswordReset.php [deleted file]
includes/specials/pre-authmanager/SpecialUserlogin.php [deleted file]
includes/specials/pre-authmanager/SpecialUserlogout.php [deleted file]
includes/templates/Usercreate.php [deleted file]
includes/templates/Userlogin.php [deleted file]
includes/tidy/Balancer.php [new file with mode: 0644]
includes/tidy/Html5Internal.php [new file with mode: 0644]
includes/tidy/RaggettWrapper.php
includes/upload/UploadBase.php
includes/upload/UploadFromChunks.php
includes/upload/UploadFromStash.php
includes/upload/UploadStash.php
includes/user/BotPassword.php
includes/user/LocalIdLookup.php
includes/user/PasswordReset.php
includes/user/User.php
includes/user/UserArray.php
includes/user/UserNamePrefixSearch.php
includes/utils/AutoloadGenerator.php
includes/utils/BatchRowIterator.php
includes/utils/BatchRowUpdate.php
includes/utils/BatchRowWriter.php
includes/utils/MWCryptRand.php
includes/utils/RowUpdateGenerator.php
includes/widget/ComplexTitleInputWidget.php
includes/widget/SearchInputWidget.php
includes/widget/TitleInputWidget.php
includes/widget/UserInputWidget.php
languages/Language.php
languages/classes/LanguageEo.php [deleted file]
languages/data/Names.php
languages/data/ZhConversion.php
languages/i18n/aeb-arab.json
languages/i18n/af.json
languages/i18n/ang.json
languages/i18n/ar.json
languages/i18n/arz.json
languages/i18n/ast.json
languages/i18n/az.json
languages/i18n/azb.json
languages/i18n/ba.json
languages/i18n/bar.json
languages/i18n/bcl.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/bg.json
languages/i18n/bho.json
languages/i18n/bn.json
languages/i18n/bo.json
languages/i18n/br.json
languages/i18n/bs.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/ckb.json
languages/i18n/cs.json
languages/i18n/cy.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/dsb.json
languages/i18n/dty.json
languages/i18n/egl.json
languages/i18n/el.json
languages/i18n/en-gb.json
languages/i18n/en.json
languages/i18n/eo.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/ext.json
languages/i18n/fa.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/frp.json
languages/i18n/ga.json
languages/i18n/gd.json
languages/i18n/gl.json
languages/i18n/gor.json
languages/i18n/got.json
languages/i18n/gu.json
languages/i18n/hak.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hr.json
languages/i18n/hsb.json
languages/i18n/ht.json
languages/i18n/hu.json
languages/i18n/hy.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/ilo.json
languages/i18n/io.json
languages/i18n/is.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/jv.json
languages/i18n/ka.json
languages/i18n/khw.json
languages/i18n/kiu.json
languages/i18n/kk-cyrl.json
languages/i18n/km.json
languages/i18n/kn.json
languages/i18n/ko.json
languages/i18n/krl.json
languages/i18n/ksh.json
languages/i18n/ku-latn.json
languages/i18n/ky.json
languages/i18n/la.json
languages/i18n/lb.json
languages/i18n/li.json
languages/i18n/lij.json
languages/i18n/lki.json
languages/i18n/lt.json
languages/i18n/lv.json
languages/i18n/lzh.json
languages/i18n/mai.json
languages/i18n/map-bms.json
languages/i18n/mg.json
languages/i18n/min.json
languages/i18n/mk.json
languages/i18n/ml.json
languages/i18n/mr.json
languages/i18n/ms.json
languages/i18n/my.json
languages/i18n/nah.json
languages/i18n/nan.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/nds-nl.json
languages/i18n/nds.json
languages/i18n/ne.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/oc.json
languages/i18n/olo.json
languages/i18n/or.json
languages/i18n/pa.json
languages/i18n/pl.json
languages/i18n/pms.json
languages/i18n/ps.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/qu.json
languages/i18n/ro.json
languages/i18n/roa-tara.json
languages/i18n/ru.json
languages/i18n/sa.json
languages/i18n/sah.json
languages/i18n/sat.json
languages/i18n/sco.json
languages/i18n/sd.json
languages/i18n/sgs.json
languages/i18n/sh.json
languages/i18n/si.json
languages/i18n/sk.json
languages/i18n/sl.json
languages/i18n/so.json
languages/i18n/sq.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/sv.json
languages/i18n/szl.json
languages/i18n/ta.json
languages/i18n/tcy.json
languages/i18n/te.json
languages/i18n/th.json
languages/i18n/tr.json
languages/i18n/tt-cyrl.json
languages/i18n/tyv.json
languages/i18n/uk.json
languages/i18n/ur.json
languages/i18n/uz.json
languages/i18n/vec.json
languages/i18n/vi.json
languages/i18n/vo.json
languages/i18n/wa.json
languages/i18n/wuu.json
languages/i18n/yi.json
languages/i18n/yo.json
languages/i18n/yue.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/messages/MessagesCs.php
languages/messages/MessagesGom_deva.php
languages/messages/MessagesPs.php
languages/messages/MessagesSk.php
languages/messages/MessagesTcy.php
maintenance/Maintenance.php
maintenance/Makefile
maintenance/archives/patch-add-rc_name_type_patrolled_timestamp_index.sql [new file with mode: 0644]
maintenance/archives/patch-pl-tl-il-nonunique.sql [new file with mode: 0644]
maintenance/archives/patch-pl-tl-il-unique.sql [deleted file]
maintenance/archives/patch-revision-page-rev-index-nonunique.sql [new file with mode: 0644]
maintenance/archives/patch-user_rights.sql
maintenance/backup.inc
maintenance/benchmarks/benchmarkParse.php
maintenance/changePassword.php
maintenance/checkBadRedirects.php
maintenance/checkImages.php
maintenance/checkLess.php
maintenance/checkUsernames.php
maintenance/cleanupCaps.php
maintenance/cleanupEmptyCategories.php [new file with mode: 0644]
maintenance/cleanupSpam.php
maintenance/cleanupTable.inc
maintenance/cleanupTitles.php
maintenance/clearInterwikiCache.php
maintenance/compareParserCache.php
maintenance/convertExtensionToRegistration.php
maintenance/createAndPromote.php
maintenance/deleteArchivedFiles.php
maintenance/deleteDefaultMessages.php
maintenance/doMaintenance.php
maintenance/dumpLinks.php
maintenance/dumpTextPass.php
maintenance/dumpUploads.php
maintenance/fetchText.php
maintenance/findDeprecated.php
maintenance/fixDefaultJsonContentPages.php
maintenance/fixDoubleRedirects.php
maintenance/fixUserRegistration.php
maintenance/generateLocalAutoload.php
maintenance/generateSitemap.php
maintenance/getReplicaServer.php [new file with mode: 0644]
maintenance/getSlaveServer.php
maintenance/hhvm/makeRepo.php [new file with mode: 0644]
maintenance/hhvm/run-server [new file with mode: 0755]
maintenance/hhvm/server.conf [new file with mode: 0644]
maintenance/hiphop/run-server [deleted file]
maintenance/hiphop/server.conf [deleted file]
maintenance/importImages.php
maintenance/importTextFiles.php
maintenance/initEditCount.php
maintenance/interwiki.list
maintenance/interwiki.sql
maintenance/jsduck/categories.json
maintenance/language/zhtable/simp2trad.manual
maintenance/language/zhtable/simp2trad_noconvert.manual
maintenance/language/zhtable/toCN.manual
maintenance/language/zhtable/toHK.manual
maintenance/language/zhtable/toSimp.manual
maintenance/language/zhtable/toTW.manual
maintenance/language/zhtable/toTrad.manual
maintenance/language/zhtable/trad2simp_noconvert.manual
maintenance/language/zhtable/tradphrases.manual
maintenance/language/zhtable/tradphrases_exclude.manual
maintenance/mssql/tables.sql
maintenance/namespaceDupes.php
maintenance/oracle/archives/patch-add-rc_name_type_patrolled_timestamp_index.sql [new file with mode: 0644]
maintenance/oracle/tables.sql
maintenance/populateFilearchiveSha1.php
maintenance/populateRevisionLength.php
maintenance/postgres/tables.sql
maintenance/purgeChangedFiles.php
maintenance/purgeChangedPages.php
maintenance/purgeList.php
maintenance/rebuildFileCache.php
maintenance/rebuildImages.php
maintenance/rebuildall.php
maintenance/refreshFileHeaders.php
maintenance/refreshImageMetadata.php
maintenance/refreshLinks.php
maintenance/removeInvalidEmails.php
maintenance/removeUnusedAccounts.php
maintenance/resetUserTokens.php
maintenance/rollbackEdits.php
maintenance/runBatchedQuery.php
maintenance/runJobs.php
maintenance/showSiteStats.php
maintenance/sql.php
maintenance/sqlite.php
maintenance/storage/checkStorage.php
maintenance/storage/compressOld.php
maintenance/storage/dumpRev.php
maintenance/storage/fixBug20757.php
maintenance/storage/moveToExternal.php
maintenance/storage/orphanStats.php
maintenance/storage/recompressTracked.php
maintenance/storage/resolveStubs.php
maintenance/storage/storageTypeStats.php
maintenance/storage/testCompression.php
maintenance/storage/trackBlobs.php
maintenance/syncFileBackend.php
maintenance/tables.sql
maintenance/tidyUpBug37714.php
maintenance/updateArticleCount.php
maintenance/updateCollation.php
maintenance/updateExtensionJsonSchema.php [new file with mode: 0644]
maintenance/updateSpecialPages.php
maintenance/userOptions.inc
maintenance/validateRegistrationFile.php
mw-config/overrides/README
package.json
resources/Resources.php
resources/ResourcesOOUI.php
resources/assets/licenses/README [new file with mode: 0644]
resources/lib/oojs-ui/i18n/ckb.json
resources/lib/oojs-ui/i18n/cy.json
resources/lib/oojs-ui/i18n/da.json
resources/lib/oojs-ui/i18n/diq.json
resources/lib/oojs-ui/i18n/et.json
resources/lib/oojs-ui/i18n/ko.json
resources/lib/oojs-ui/i18n/sq.json
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-core-apex.css
resources/lib/oojs-ui/oojs-ui-core-mediawiki.css
resources/lib/oojs-ui/oojs-ui-core.js
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui-toolbars-apex.css
resources/lib/oojs-ui/oojs-ui-toolbars-mediawiki.css
resources/lib/oojs-ui/oojs-ui-toolbars.js
resources/lib/oojs-ui/oojs-ui-widgets-apex.css
resources/lib/oojs-ui/oojs-ui-widgets-mediawiki.css
resources/lib/oojs-ui/oojs-ui-widgets.js
resources/lib/oojs-ui/oojs-ui-windows-apex.css
resources/lib/oojs-ui/oojs-ui-windows-mediawiki.css
resources/lib/oojs-ui/oojs-ui-windows.js
resources/lib/oojs-ui/themes/apex/icons-alerts.json [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/bell.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/bell.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/bellOn-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/bellOn-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/bellOn-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/bellOn-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/eye.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/eye.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/eyeClosed.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/eyeClosed.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/flag-rtl.png
resources/lib/oojs-ui/themes/apex/images/icons/flag-rtl.svg
resources/lib/oojs-ui/themes/apex/images/icons/flagUndo-ltr.png
resources/lib/oojs-ui/themes/apex/images/icons/flagUndo-ltr.svg
resources/lib/oojs-ui/themes/apex/images/icons/flagUndo-rtl.png
resources/lib/oojs-ui/themes/apex/images/icons/flagUndo-rtl.svg
resources/lib/oojs-ui/themes/apex/images/icons/message-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/message-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/message-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/message-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/signature-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/signature-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/signature-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/signature-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/tray.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/tray.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/icons-alerts.json
resources/lib/oojs-ui/themes/mediawiki/images/icons/bell-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bell-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bell.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bell.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stop-invert.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/stop-invert.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/stop.png
resources/lib/oojs-ui/themes/mediawiki/images/icons/stop.svg
resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-invert.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-invert.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/tray.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/tray.svg [new file with mode: 0644]
resources/lib/phpjs-sha1/sha1.js [deleted file]
resources/src/jquery/jquery.accessKeyLabel.js
resources/src/jquery/jquery.makeCollapsible.js
resources/src/jquery/jquery.tablesorter.js
resources/src/mediawiki.action/mediawiki.action.edit.stash.js
resources/src/mediawiki.action/mediawiki.action.edit.styles.css
resources/src/mediawiki.action/mediawiki.action.history.diff.css [deleted file]
resources/src/mediawiki.action/mediawiki.action.history.diff.print.css [deleted file]
resources/src/mediawiki.legacy/shared.css
resources/src/mediawiki.skinning/content.css
resources/src/mediawiki.skinning/images/external link icons.svg [deleted file]
resources/src/mediawiki.skinning/images/external-link-icons.svg [new file with mode: 0644]
resources/src/mediawiki.special/mediawiki.special.apisandbox.js
resources/src/mediawiki.special/mediawiki.special.changeslist.css
resources/src/mediawiki.special/mediawiki.special.css
resources/src/mediawiki.special/mediawiki.special.movePage.css
resources/src/mediawiki.special/mediawiki.special.movePage.js
resources/src/mediawiki.special/mediawiki.special.preferences.js
resources/src/mediawiki.special/mediawiki.special.preferences.styles.css
resources/src/mediawiki.special/mediawiki.special.search.styles.css
resources/src/mediawiki.special/mediawiki.special.upload.styles.css
resources/src/mediawiki.special/mediawiki.special.userrights.js [new file with mode: 0644]
resources/src/mediawiki.toolbar/toolbar.less
resources/src/mediawiki.ui/components/icons.less
resources/src/mediawiki.widgets/mw.widgets.CategoryCapsuleItemWidget.js
resources/src/mediawiki.widgets/mw.widgets.CategorySelector.js
resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.less
resources/src/mediawiki.widgets/mw.widgets.TitleOptionWidget.js
resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js
resources/src/mediawiki/ForeignApi.js
resources/src/mediawiki/api.js
resources/src/mediawiki/api/messages.js
resources/src/mediawiki/api/options.js
resources/src/mediawiki/api/rollback.js
resources/src/mediawiki/htmlform/autocomplete.js [new file with mode: 0644]
resources/src/mediawiki/htmlform/autoinfuse.js [new file with mode: 0644]
resources/src/mediawiki/htmlform/checkmatrix.js [new file with mode: 0644]
resources/src/mediawiki/htmlform/cloner.js [new file with mode: 0644]
resources/src/mediawiki/htmlform/hide-if.js [new file with mode: 0644]
resources/src/mediawiki/htmlform/htmlform.Element.js [new file with mode: 0644]
resources/src/mediawiki/htmlform/htmlform.js [new file with mode: 0644]
resources/src/mediawiki/htmlform/images/question.png [new file with mode: 0644]
resources/src/mediawiki/htmlform/images/question.svg [new file with mode: 0644]
resources/src/mediawiki/htmlform/multiselect.js [new file with mode: 0644]
resources/src/mediawiki/htmlform/ooui.styles.css [new file with mode: 0644]
resources/src/mediawiki/htmlform/selectandother.js [new file with mode: 0644]
resources/src/mediawiki/htmlform/selectorother.js [new file with mode: 0644]
resources/src/mediawiki/htmlform/styles.css [new file with mode: 0644]
resources/src/mediawiki/images/question.png [deleted file]
resources/src/mediawiki/images/question.svg [deleted file]
resources/src/mediawiki/mediawiki.Title.js
resources/src/mediawiki/mediawiki.Upload.BookletLayout.js
resources/src/mediawiki/mediawiki.content.json.css [deleted file]
resources/src/mediawiki/mediawiki.content.json.less [new file with mode: 0644]
resources/src/mediawiki/mediawiki.debug.init.js [deleted file]
resources/src/mediawiki/mediawiki.debug.js
resources/src/mediawiki/mediawiki.diff.styles.css [new file with mode: 0644]
resources/src/mediawiki/mediawiki.diff.styles.print.css [new file with mode: 0644]
resources/src/mediawiki/mediawiki.htmlform.css [deleted file]
resources/src/mediawiki/mediawiki.htmlform.js [deleted file]
resources/src/mediawiki/mediawiki.htmlform.ooui.css [deleted file]
resources/src/mediawiki/mediawiki.inspect.js
resources/src/mediawiki/mediawiki.js
resources/src/mediawiki/mediawiki.notification.convertmessagebox.js [new file with mode: 0644]
resources/src/mediawiki/mediawiki.notification.convertmessagebox.styles.less [new file with mode: 0644]
resources/src/mediawiki/mediawiki.notification.hideForPrint.css [deleted file]
resources/src/mediawiki/mediawiki.notification.print.css [new file with mode: 0644]
resources/src/mediawiki/mediawiki.raggett.css [deleted file]
resources/src/mediawiki/mediawiki.toc.js
resources/src/mediawiki/mediawiki.user.js
resources/src/mediawiki/mediawiki.util.js
resources/src/mediawiki/page/gallery-print.css [deleted file]
resources/src/mediawiki/page/gallery-slideshow.js [new file with mode: 0644]
resources/src/mediawiki/page/gallery.css
resources/src/mediawiki/page/gallery.print.css [new file with mode: 0644]
resources/src/mediawiki/page/ready.js
resources/src/mediawiki/page/rollback.js
resources/src/mediawiki/page/startup.js
resources/src/mediawiki/page/watch.js
resources/src/oojs-ui-styles-skip.js [deleted file]
resources/src/startup.js
tests/TestsAutoLoader.php [deleted file]
tests/browser/environments.yml
tests/common/TestSetup.php [new file with mode: 0644]
tests/common/TestsAutoLoader.php [new file with mode: 0644]
tests/parser/DbTestPreviewer.php [new file with mode: 0644]
tests/parser/DbTestRecorder.php [new file with mode: 0644]
tests/parser/DjVuSupport.php [new file with mode: 0644]
tests/parser/MultiTestRecorder.php [new file with mode: 0644]
tests/parser/ParserTestParserHook.php [new file with mode: 0644]
tests/parser/ParserTestPrinter.php [new file with mode: 0644]
tests/parser/ParserTestResult.php
tests/parser/ParserTestResultNormalizer.php [new file with mode: 0644]
tests/parser/ParserTestRunner.php [new file with mode: 0644]
tests/parser/PhpunitTestRecorder.php [new file with mode: 0644]
tests/parser/README
tests/parser/TestFileReader.php [new file with mode: 0644]
tests/parser/TestRecorder.php [new file with mode: 0644]
tests/parser/TidySupport.php [new file with mode: 0644]
tests/parser/fuzzTest.php [new file with mode: 0644]
tests/parser/parserTest.inc [deleted file]
tests/parser/parserTests.php [new file with mode: 0644]
tests/parser/parserTests.txt
tests/parser/parserTestsParserHook.php [deleted file]
tests/parserTests.php [deleted file]
tests/phpunit/Makefile
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/ResourceLoaderTestCase.php
tests/phpunit/data/templates/conds.mustache [deleted file]
tests/phpunit/includes/EditPageTest.php
tests/phpunit/includes/ExportTest.php
tests/phpunit/includes/FauxRequestTest.php
tests/phpunit/includes/GlobalFunctions/GlobalTest.php
tests/phpunit/includes/GlobalFunctions/wfBCP47Test.php
tests/phpunit/includes/GlobalFunctions/wfThumbIsStandardTest.php
tests/phpunit/includes/GlobalFunctions/wfUrlencodeTest.php
tests/phpunit/includes/HtmlTest.php
tests/phpunit/includes/HttpTest.php
tests/phpunit/includes/LinkerTest.php
tests/phpunit/includes/MWNamespaceTest.php
tests/phpunit/includes/MWTimestampTest.php
tests/phpunit/includes/MediaWikiServicesTest.php
tests/phpunit/includes/OutputPageTest.php
tests/phpunit/includes/SanitizerTest.php
tests/phpunit/includes/TitleTest.php
tests/phpunit/includes/WatchedItemIntegrationTest.php
tests/phpunit/includes/WatchedItemQueryServiceUnitTest.php
tests/phpunit/includes/WatchedItemStoreUnitTest.php
tests/phpunit/includes/WatchedItemUnitTest.php
tests/phpunit/includes/WebRequestTest.php
tests/phpunit/includes/WikiMapTest.php
tests/phpunit/includes/api/ApiBaseTest.php
tests/phpunit/includes/api/ApiEditPageTest.php
tests/phpunit/includes/api/ApiErrorFormatterTest.php
tests/phpunit/includes/api/ApiLoginTest.php
tests/phpunit/includes/api/ApiOpenSearchTest.php [new file with mode: 0644]
tests/phpunit/includes/api/ApiPageSetTest.php
tests/phpunit/includes/api/ApiQueryWatchlistRawIntegrationTest.php
tests/phpunit/includes/api/ApiTestCase.php
tests/phpunit/includes/api/MockApi.php
tests/phpunit/includes/api/query/ApiQueryTest.php
tests/phpunit/includes/auth/AbstractAuthenticationProviderTest.php
tests/phpunit/includes/auth/AbstractPasswordPrimaryAuthenticationProviderTest.php
tests/phpunit/includes/auth/AbstractPreAuthenticationProviderTest.php
tests/phpunit/includes/auth/AbstractPrimaryAuthenticationProviderTest.php
tests/phpunit/includes/auth/AbstractSecondaryAuthenticationProviderTest.php
tests/phpunit/includes/auth/AuthManagerTest.php
tests/phpunit/includes/auth/AuthPluginPrimaryAuthenticationProviderTest.php
tests/phpunit/includes/auth/AuthenticationRequestTest.php
tests/phpunit/includes/auth/AuthenticationRequestTestCase.php
tests/phpunit/includes/auth/AuthenticationResponseTest.php
tests/phpunit/includes/auth/CheckBlocksSecondaryAuthenticationProviderTest.php
tests/phpunit/includes/auth/ConfirmLinkSecondaryAuthenticationProviderTest.php
tests/phpunit/includes/auth/EmailNotificationSecondaryAuthenticationProviderTest.php
tests/phpunit/includes/auth/LegacyHookPreAuthenticationProviderTest.php
tests/phpunit/includes/auth/LocalPasswordPrimaryAuthenticationProviderTest.php
tests/phpunit/includes/auth/ResetPasswordSecondaryAuthenticationProviderTest.php
tests/phpunit/includes/auth/TemporaryPasswordPrimaryAuthenticationProviderTest.php
tests/phpunit/includes/auth/ThrottlePreAuthenticationProviderTest.php
tests/phpunit/includes/auth/ThrottlerTest.php
tests/phpunit/includes/changes/RCCacheEntryFactoryTest.php
tests/phpunit/includes/changes/TestRecentChangesHelper.php
tests/phpunit/includes/content/ContentHandlerTest.php
tests/phpunit/includes/content/CssContentTest.php
tests/phpunit/includes/content/JsonContentHandlerTest.php [new file with mode: 0644]
tests/phpunit/includes/content/TextContentHandlerTest.php
tests/phpunit/includes/content/TextContentTest.php
tests/phpunit/includes/content/WikitextContentHandlerTest.php
tests/phpunit/includes/content/WikitextStructureTest.php [new file with mode: 0644]
tests/phpunit/includes/db/DatabaseMysqlBaseTest.php
tests/phpunit/includes/db/DatabaseSQLTest.php
tests/phpunit/includes/db/DatabaseTest.php
tests/phpunit/includes/db/DatabaseTestHelper.php
tests/phpunit/includes/db/LBFactoryTest.php
tests/phpunit/includes/deferred/DeferredUpdatesTest.php
tests/phpunit/includes/deferred/LinksUpdateTest.php
tests/phpunit/includes/filebackend/FileBackendTest.php
tests/phpunit/includes/filerepo/file/FileTest.php
tests/phpunit/includes/installer/DatabaseUpdaterTest.php
tests/phpunit/includes/libs/ObjectFactoryTest.php
tests/phpunit/includes/libs/SamplingStatsdClientTest.php
tests/phpunit/includes/libs/WaitConditionLoopTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/XmlTypeCheckTest.php
tests/phpunit/includes/libs/objectcache/BagOStuffTest.php
tests/phpunit/includes/libs/objectcache/CachedBagOStuffTest.php
tests/phpunit/includes/libs/objectcache/HashBagOStuffTest.php
tests/phpunit/includes/libs/objectcache/MultiWriteBagOStuffTest.php
tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
tests/phpunit/includes/linker/LinkRendererFactoryTest.php
tests/phpunit/includes/linker/LinkRendererTest.php
tests/phpunit/includes/logging/LogFormatterTestCase.php
tests/phpunit/includes/objectcache/RESTBagOStuffTest.php [new file with mode: 0644]
tests/phpunit/includes/objectcache/RedisBagOStuffTest.php
tests/phpunit/includes/parser/MediaWikiParserTest.php [deleted file]
tests/phpunit/includes/parser/NewParserTest.php [deleted file]
tests/phpunit/includes/parser/ParserIntegrationTest.php [new file with mode: 0644]
tests/phpunit/includes/parser/PreprocessorTest.php
tests/phpunit/includes/registration/CoreVersionCheckerTest.php
tests/phpunit/includes/registration/ExtensionProcessorTest.php
tests/phpunit/includes/resourceloader/DerivativeResourceLoaderContextTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderClientHtmlTest.php [new file with mode: 0644]
tests/phpunit/includes/resourceloader/ResourceLoaderContextTest.php [new file with mode: 0644]
tests/phpunit/includes/resourceloader/ResourceLoaderFileModuleTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderStartUpModuleTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderWikiModuleTest.php
tests/phpunit/includes/search/ParserOutputSearchDataExtractorTest.php [new file with mode: 0644]
tests/phpunit/includes/search/SearchEngineTest.php
tests/phpunit/includes/search/SearchIndexFieldTest.php [new file with mode: 0644]
tests/phpunit/includes/session/SessionManagerTest.php
tests/phpunit/includes/skins/SkinTemplateTest.php
tests/phpunit/includes/specials/SpecialSearchTest.php
tests/phpunit/includes/tidy/BalancerTest.php [new file with mode: 0644]
tests/phpunit/includes/tidy/html5lib-tests.json [new file with mode: 0644]
tests/phpunit/includes/title/MediaWikiTitleCodecTest.php
tests/phpunit/includes/upload/UploadBaseTest.php
tests/phpunit/includes/user/UserTest.php
tests/phpunit/includes/utils/FileContentsHasherTest.php
tests/phpunit/includes/utils/MWCryptHashTest.php
tests/phpunit/mocks/media/MockMediaHandlerFactory.php [new file with mode: 0644]
tests/phpunit/phpunit.php
tests/phpunit/specials/SpecialSearchTest.php [new file with mode: 0644]
tests/phpunit/structure/AutoLoaderTest.php
tests/phpunit/structure/ExtensionJsonValidationTest.php
tests/phpunit/structure/ResourcesTest.php
tests/phpunit/suite.xml
tests/phpunit/suites/CoreParserTestSuite.php [new file with mode: 0644]
tests/phpunit/suites/ExtensionsParserTestSuite.php
tests/phpunit/suites/ExtensionsTestSuite.php
tests/phpunit/suites/ParserTestFileSuite.php [new file with mode: 0644]
tests/phpunit/suites/ParserTestTopLevelSuite.php [new file with mode: 0644]
tests/qunit/QUnitTestResources.php
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/mediawiki.Title.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki/mediawiki.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js
tests/testHelpers.inc [deleted file]

diff --git a/Gemfile b/Gemfile
index 19d2f52..8a349bf 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -1,5 +1,5 @@
 source 'https://rubygems.org'
 
-gem 'mediawiki_selenium', '~> 1.7', '>= 1.7.1'
+gem 'mediawiki_selenium', '~> 1.7', '>= 1.7.2'
 gem 'rake', '~> 11.1', '>= 11.1.1'
 gem 'rubocop', '~> 0.32.1', require: false
index 2d6e655..982619a 100644 (file)
@@ -17,16 +17,18 @@ GEM
       faker (>= 1.1.2)
       yml_reader (>= 0.6)
     diff-lcs (1.2.5)
-    domain_name (0.5.20160310)
+    domain_name (0.5.20160615)
       unf (>= 0.0.5, < 1.0.0)
-    faker (1.6.3)
+    faker (1.6.6)
       i18n (~> 0.5)
     faraday (0.9.2)
       multipart-post (>= 1.2, < 3)
     faraday-cookie_jar (0.0.6)
       faraday (>= 0.7.4)
       http-cookie (~> 1.0.0)
-    ffi (1.9.10)
+    faraday_middleware (0.10.0)
+      faraday (>= 0.7.4, < 0.10)
+    ffi (1.9.14)
     gherkin (2.12.2)
       multi_json (~> 1.3)
     headless (2.2.3)
@@ -34,14 +36,15 @@ GEM
       domain_name (~> 0.5)
     i18n (0.7.0)
     json (1.8.3)
-    mediawiki_api (0.6.0)
+    mediawiki_api (0.7.0)
       faraday (~> 0.9, >= 0.9.0)
       faraday-cookie_jar (~> 0.0, >= 0.0.6)
-    mediawiki_selenium (1.7.1)
+      faraday_middleware (~> 0.10, >= 0.10.0)
+    mediawiki_selenium (1.7.2)
       cucumber (~> 1.3, >= 1.3.20)
       headless (~> 2.0, >= 2.1.0)
       json (~> 1.8, >= 1.8.1)
-      mediawiki_api (~> 0.6, >= 0.6.0)
+      mediawiki_api (~> 0.7, >= 0.7.0)
       page-object (~> 1.0)
       rest-client (~> 1.6, >= 1.6.7)
       rspec-core (~> 2.14, >= 2.14.4)
@@ -53,7 +56,7 @@ GEM
     multi_test (0.1.2)
     multipart-post (2.0.0)
     netrc (0.11.0)
-    page-object (1.1.1)
+    page-object (1.2.0)
       page_navigation (>= 0.9)
       selenium-webdriver (>= 2.44.0)
       watir-webdriver (>= 0.6.11)
@@ -79,7 +82,7 @@ GEM
       ruby-progressbar (~> 1.4)
     ruby-progressbar (1.7.5)
     rubyzip (1.2.0)
-    selenium-webdriver (2.53.1)
+    selenium-webdriver (2.53.4)
       childprocess (~> 0.5)
       rubyzip (~> 1.0)
       websocket (~> 1.0)
@@ -88,7 +91,7 @@ GEM
     unf (0.1.4)
       unf_ext
     unf_ext (0.0.7.2)
-    watir-webdriver (0.9.1)
+    watir-webdriver (0.9.3)
       selenium-webdriver (>= 2.46.2)
     websocket (1.2.3)
     yml_reader (0.7)
@@ -97,9 +100,6 @@ PLATFORMS
   ruby
 
 DEPENDENCIES
-  mediawiki_selenium (~> 1.7, >= 1.7.1)
+  mediawiki_selenium (~> 1.7, >= 1.7.2)
   rake (~> 11.1, >= 11.1.1)
   rubocop (~> 0.32.1)
-
-BUNDLED WITH
-   1.10.6
index d0bb57f..979da5c 100644 (file)
@@ -6,6 +6,9 @@ MediaWiki 1.28 is an alpha-quality branch and is not recommended for use in
 production.
 
 === Configuration changes in 1.28 ===
+* BREAKING CHANGE: $wgHTTPProxy is now *required* for all external requests
+  made by MediaWiki via a proxy. Relying on the http_proxy environment
+  variable is no longer supported.
 * The load.php entry point now enforces the existing policy of not allowing
   access to session data, which includes the session user and the session
   user's language. If such access is attempted, an exception will be thrown.
@@ -16,12 +19,41 @@ production.
 * $wgForeignUploadTargets now defaults to `[ 'local' ]`, where `'local'`
   signifies local uploads. A value of `[]` (empty array) now means that
   no upload targets are allowed, effectively disabling the upload dialog.
+* The deprecated $wgEditEncoding variable has been removed; it was only used
+  for Esperanto language character conversion. You are now recommended to use
+  input methods provided by the UniversalLanguageSelector extension.
+* When $wgPingback is true, MediaWiki will periodically ping
+  https://www.mediawiki.org/beacon with basic information about the local
+  MediaWiki installation. This data includes, for example, the type of system,
+  PHP version, and chosen database backend. This behavior is off by default.
+* When $EditSubmitButtonLabelPublish is true, MediaWiki will label the button
+  to store-to-database-and-show-to-others as "Publish page"/"Publish changes";
+  if false, the default, they will be "Save page"/"Save changes".
+* The 'editcontentmodel' permission is now granted to all logged-in users ('user').
+  instead of just administrators ('sysop'). Documentation for this feature is
+  available at <https://www.mediawiki.org/wiki/Help:ChangeContentModel>.
+* $wgRevisionCacheExpiry is now set to one week by default instead of being disabled.
 
 === New features in 1.28 ===
 * User::isBot() method for checking if an account is a bot role account.
+* Added a new 'slideshow' mode for galleries.
 * Added a new hook, 'UserIsBot', to aid in determining if a user is a bot.
 * Added a new hook, 'ApiMakeParserOptions', to allow extensions to better
   interact with API parsing.
+* Added a new hook, 'UploadVerifyUpload', which can be used to reject a file
+  upload. Unlike 'UploadVerifyFile' it provides information about upload comment
+  and the file description page, but does not run for uploads to stash.
+* (T141604) Extensions can now provide a better error message when their
+  maintenance scripts are run without the extension being installed.
+* (T8948) Numeric sorting in categories is now supported by setting $wgCategoryCollation
+  to 'uca-default-u-kn' or 'uca-<langcode>-u-kn'. If you can't use UCA collations,
+  a 'numeric' collation is also available. If migrating from another
+  collation, you will need to run the updateCollation.php maintenance script.
+* Two new codes have been added to #time parser function: "xit" for days in current
+  month, and "xiz" for days passed in the year, both in Iranian calendar.
+* mw.Api has a new option, useUS, to use U+001F (Unit Separator) when
+  appropriate for sending multi-valued parameters. This defaults to true when
+  the mw.Api instance seems to be for the local wiki.
 
 === External library changes in 1.28 ===
 
@@ -33,12 +65,49 @@ production.
 ==== Removed and replaced external libraries ====
 
 === Bug fixes in 1.28 ===
+* (T137264) SECURITY: XSS in unclosed internal links
+* (T133147) SECURITY: Escape '<' and ']]>' in inline <style> blocks
+* (T133147) SECURITY: Require login to preview user CSS pages
+* (T132926) SECURITY: Do not allow undeleting a revision deleted file if it is
+  the top file
+* (T129738) SECURITY: Make $wgBlockDisablesLogin also restrict logged in
+  permissions
+* (T129738) SECURITY: Make blocks log users out if $wgBlockDisablesLogin is true
+* (T139670) Move 'UserGetRights' call before application of
+  Session::getAllowedUserRights()
 
 === Action API changes in 1.28 ===
+* Added 'maxarticlesize' property to action=query&meta=siteinfo which contains
+  the value of $wgMaxArticleSize.
+* Property 'modulemessages' from action=parse&prop=modules was removed
+  (deprecated since 1.26).
+* The following response properties from action=login, deprecated in 1.27, are
+  now removed: lgtoken, cookieprefix, sessionid. Clients should handle cookies
+  to properly manage session state.
+* Submitting the lgtoken and lgpassword parameters in the query string to
+  action=login is now deprecated and outputs a warning. They should be submitted
+  in the POST body instead.
+* Submitting sensitive authentication request parameters to action=clientlogin,
+  action=createaccount, action=linkaccount, and action=changeauthenticationdata
+  in the query string is now deprecated and outputs a warning. They should be
+  submitted in the POST body instead.
+* (T141960) Multi-valued parameters may now be separated using U+001F (Unit Separator)
+  instead of the pipe character. This will be useful if some of the multiple
+  values need to contain pipes, e.g. for action=options.
+* The API will now warn if input is not NFC-normalized Unicode or if it
+  contains invalid characters.
+* The 'normalized' list output by action=query and other modules that use
+  ApiPageSet may contain entries where the 'from' value is percent-encoded as
+  the raw value cannot be represented in a valid API response. These are
+  indicated by a 'fromencoded' boolean alongside the existing 'from' parameter.
+* (T28680) action=paraminfo can now return info about all submodules of a
+  module without listing them all explicitly.
 
 === Action API internal changes in 1.28 ===
 * Added a new hook, 'ApiMakeParserOptions', to allow extensions to better
   interact with ApiParse and ApiExpandTemplates.
+* (T139565) SECURITY: API: Generate head items in the context of the given title
+* (T115333) SECURITY: Check read permission when loading page content in ApiParse
 
 === Languages updated in 1.28 ===
 
@@ -48,13 +117,53 @@ changes to languages because of Phabricator reports.
 
 * (T137411) ban (Balinese), thanks to translators Adi Mayndra, Andru,
   BASAbali, M. Adiputra, Naval Scene, Nemo bis, NoiX180, and 아라.
+* (T135867) shn (Shan), thanks to translators Khun Sar, Piangpha,
+  Saiddzone Saimawnkham, Saosukham, and Sengwan.
+* Czech (cs) and Slovak (sk) set as reciprocal fallbacks
 
 === Other changes in 1.28 ===
 * (T128697) Improved handling of large diffs.
-* [BREAKING CHANGE] $wgExtendedLoginCookies has been removed.  You can
+* [BREAKING CHANGE] $wgExtendedLoginCookies has been removed. You can
   use or update a custom session provider if needed.
 * Deprecated APIEditBeforeSave hook in favor of EditFilterMergedContent.
 * The 'UploadVerification' hook is deprecated. Use 'UploadVerifyFile' instead.
+* SiteConfiguration::isLocalVHost() was removed (deprecated since 1.25).
+* The 'UserLoginComplete' hook has a new parameter to differentiate between actual
+  login and visiting the login page while already logged in.
+* ResourceLoader::makeLoaderURL() was removed (deprecated since 1.24).
+* $.fn.liveAndTestAtStart was removed (deprecated since 1.24).
+* mw.util.tooltipAccessKeyPrefix was removed (deprecated since 1.24).
+* mw.util.tooltipAccessKeyRegexp was removed (deprecated since 1.24).
+* Linker::link() and Linker::linkKnown() were deprecated; please instead use
+  MediaWiki\Linker\LinkRenderer. In addition, the LinkBegin and LinkEnd hooks
+  were replaced by HtmlPageLinkRendererBegin and HtmlPageLinkRendererEnd
+  respectively. See docs/hooks.txt for the specific changes needed for those hooks.
+* Aliases for Linker methods, deprecated since 1.21, were removed from Skin:
+  * Skin::commentBlock() (use Linker::commentBlock() instead)
+  * Skin::generateRollback() (use Linker::generateRollback() instead)
+  * Skin::link() (use MediaWiki\Linker\LinkRenderer instead)
+  * Skin::linkKnown() (use MediaWiki\Linker\LinkRenderer instead)
+  * Skin::userLink() (use Linker::userLink() instead)
+  * Skin::userToolLinks() (use Linker::userToolLinks() instead)
+* The 'ParserLimitReportFormat' hook was removed.
+* Disabled "bug 2702" HTML tidying of parsed UI messages on wikis where Tidy is
+  disabled.
+* DifferenceEngine::generateDiffBody() was removed (deprecated since 1.21).
+* UploadBase::stashFileGetKey() and UploadBase::stashSession() were deprecated.
+  Use ...->stashFile()->getFileKey() instead.
+* "Public domain" was removed as a wiki license option from the installer, in
+  favour of CC-0.
+* AuthenticationRequest::$required is now changed from REQUIRED to PRIMARY_REQUIRED
+  on requests needed by primary providers even if all primaries need them.
+  Primary providers are discouraged from returning multiple REQUIRED requests.
+* OOjs UI PHP widgets constructed with the `'infusable' => true` config option
+  will no longer be automatically infused. You should call `OO.ui.infuse()`
+  on them yourself from your JavaScript code.
+* parserTests.php has moved to tests/parser/parserTests.php
+* The command line options specific to parser tests have been removed from
+  phpunit.php: --regex and --keep-uploads. Instead of --regex, use --filter.
+  Instead of --keep-uploads, use the same option to parserTests.php, but you
+  must specify a directory with --upload-dir.
 
 == Compatibility ==
 
@@ -103,7 +212,7 @@ Documentation for both end-users and site administrators is available on
 MediaWiki.org, and is covered under the GNU Free Documentation License (except
 for pages that explicitly state that their contents are in the public domain):
 
-       https://www.mediawiki.org/wiki/Documentation
+       https://www.mediawiki.org/wiki/Special:MyLanguage/Documentation
 
 == Mailing list ==
 
diff --git a/UPGRADE b/UPGRADE
index 088701a..3ec1a22 100644 (file)
--- a/UPGRADE
+++ b/UPGRADE
@@ -1,18 +1,17 @@
-
 This file provides an overview of the MediaWiki upgrade process. For help with
 specific problems, check
 
-* the documentation at https://www.mediawiki.org
+* the documentation at https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents
 * the mediawiki-l mailing list archive at
   http://lists.wikimedia.org/pipermail/mediawiki-l/
-* the bug tracker at https://phabricator.wikimedia.org
+* the bug tracker at https://phabricator.wikimedia.org/
 
 for information and workarounds to common issues.
 
 == Overview ==
 
 Comprehensive documentation on upgrading to the latest version of the software
-is available at https://www.mediawiki.org/wiki/Manual:Upgrading.
+is available at https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Upgrading
 
 === Consult the release notes ===
 
@@ -28,7 +27,7 @@ you take a complete backup of your wiki database and files and verify it. While
 the upgrade scripts are somewhat robust, there is no guarantee that things will
 not fail, leaving the database in an inconsistent state.
 
-https://www.mediawiki.org/wiki/Manual:Backing_up_a_wiki provides an overview of
+https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Backing_up_a_wiki provides an overview of
 the backup process. You should also refer to the documentation for your
 database management system for information on backing up a database, and to
 your operating system documentation for information on making copies of files.
index d994016..71f1809 100644 (file)
@@ -21,14 +21,13 @@ $wgAutoloadLocalClasses = [
        'ApiAuthManagerHelper' => __DIR__ . '/includes/api/ApiAuthManagerHelper.php',
        'ApiBase' => __DIR__ . '/includes/api/ApiBase.php',
        'ApiBlock' => __DIR__ . '/includes/api/ApiBlock.php',
+       'ApiCSPReport' => __DIR__ . '/includes/api/ApiCSPReport.php',
        'ApiChangeAuthenticationData' => __DIR__ . '/includes/api/ApiChangeAuthenticationData.php',
        'ApiCheckToken' => __DIR__ . '/includes/api/ApiCheckToken.php',
        'ApiClearHasMsg' => __DIR__ . '/includes/api/ApiClearHasMsg.php',
        'ApiClientLogin' => __DIR__ . '/includes/api/ApiClientLogin.php',
        'ApiComparePages' => __DIR__ . '/includes/api/ApiComparePages.php',
        'ApiContinuationManager' => __DIR__ . '/includes/api/ApiContinuationManager.php',
-       'ApiCreateAccount' => __DIR__ . '/includes/api/ApiCreateAccount.php',
-       'ApiCSPReport' => __DIR__ . '/includes/api/ApiCSPReport.php',
        'ApiDelete' => __DIR__ . '/includes/api/ApiDelete.php',
        'ApiDisabled' => __DIR__ . '/includes/api/ApiDisabled.php',
        'ApiEditPage' => __DIR__ . '/includes/api/ApiEditPage.php',
@@ -157,6 +156,7 @@ $wgAutoloadLocalClasses = [
        'AuthManagerSpecialPage' => __DIR__ . '/includes/specialpage/AuthManagerSpecialPage.php',
        'AuthPlugin' => __DIR__ . '/includes/AuthPlugin.php',
        'AuthPluginUser' => __DIR__ . '/includes/AuthPlugin.php',
+       'AutoCommitUpdate' => __DIR__ . '/includes/deferred/AutoCommitUpdate.php',
        'AutoLoader' => __DIR__ . '/includes/AutoLoader.php',
        'AutoloadGenerator' => __DIR__ . '/includes/utils/AutoloadGenerator.php',
        'Autopromote' => __DIR__ . '/includes/Autopromote.php',
@@ -245,6 +245,7 @@ $wgAutoloadLocalClasses = [
        'ClassCollector' => __DIR__ . '/includes/utils/AutoloadGenerator.php',
        'CleanupAncientTables' => __DIR__ . '/maintenance/cleanupAncientTables.php',
        'CleanupBlocks' => __DIR__ . '/maintenance/cleanupBlocks.php',
+       'CleanupEmptyCategories' => __DIR__ . '/maintenance/cleanupEmptyCategories.php',
        'CleanupPreferences' => __DIR__ . '/maintenance/cleanupPreferences.php',
        'CleanupRemovedModules' => __DIR__ . '/maintenance/cleanupRemovedModules.php',
        'CleanupSpam' => __DIR__ . '/maintenance/cleanupSpam.php',
@@ -296,7 +297,7 @@ $wgAutoloadLocalClasses = [
        'CsvStatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
        'CurlHttpRequest' => __DIR__ . '/includes/HttpFunctions.php',
        'DBAccessBase' => __DIR__ . '/includes/dao/DBAccessBase.php',
-       'DBAccessError' => __DIR__ . '/includes/db/loadbalancer/LBFactory.php',
+       'DBAccessError' => __DIR__ . '/includes/db/DatabaseError.php',
        'DBAccessObjectUtils' => __DIR__ . '/includes/dao/DBAccessObjectUtils.php',
        'DBConnRef' => __DIR__ . '/includes/db/DBConnRef.php',
        'DBConnectionError' => __DIR__ . '/includes/db/DatabaseError.php',
@@ -307,7 +308,7 @@ $wgAutoloadLocalClasses = [
        'DBMasterPos' => __DIR__ . '/includes/db/DatabaseUtility.php',
        'DBQueryError' => __DIR__ . '/includes/db/DatabaseError.php',
        'DBReadOnlyError' => __DIR__ . '/includes/db/DatabaseError.php',
-       'DBReplicationWaitError' => __DIR__ . '/includes/db/loadbalancer/LBFactory.php',
+       'DBReplicationWaitError' => __DIR__ . '/includes/db/DatabaseError.php',
        'DBSiteStore' => __DIR__ . '/includes/site/DBSiteStore.php',
        'DBTransactionError' => __DIR__ . '/includes/db/DatabaseError.php',
        'DBUnexpectedError' => __DIR__ . '/includes/db/DatabaseError.php',
@@ -328,6 +329,7 @@ $wgAutoloadLocalClasses = [
        'DateFormats' => __DIR__ . '/maintenance/language/date-formats.php',
        'DateFormatter' => __DIR__ . '/includes/parser/DateFormatter.php',
        'DeadendPagesPage' => __DIR__ . '/includes/specials/SpecialDeadendpages.php',
+       'DeferrableCallback' => __DIR__ . '/includes/deferred/DeferrableCallback.php',
        'DeferrableUpdate' => __DIR__ . '/includes/deferred/DeferrableUpdate.php',
        'DeferredStringifier' => __DIR__ . '/includes/libs/DeferredStringifier.php',
        'DeferredUpdates' => __DIR__ . '/includes/deferred/DeferredUpdates.php',
@@ -370,6 +372,7 @@ $wgAutoloadLocalClasses = [
        'DoubleRedirectsPage' => __DIR__ . '/includes/specials/SpecialDoubleRedirects.php',
        'DoubleReplacer' => __DIR__ . '/includes/libs/replacers/DoubleReplacer.php',
        'DummyLinker' => __DIR__ . '/includes/DummyLinker.php',
+       'DummySearchIndexFieldDefinition' => __DIR__ . '/includes/search/DummySearchIndexFieldDefinition.php',
        'DummyTermColorer' => __DIR__ . '/maintenance/term/MWTerm.php',
        'Dump7ZipOutput' => __DIR__ . '/includes/export/Dump7ZipOutput.php',
        'DumpBZip2Output' => __DIR__ . '/includes/export/DumpBZip2Output.php',
@@ -389,6 +392,7 @@ $wgAutoloadLocalClasses = [
        'DumpPipeOutput' => __DIR__ . '/includes/export/DumpPipeOutput.php',
        'DumpRenderer' => __DIR__ . '/maintenance/renderDump.php',
        'DumpRev' => __DIR__ . '/maintenance/storage/dumpRev.php',
+       'DumpStringOutput' => __DIR__ . '/includes/export/DumpStringOutput.php',
        'DuplicateJob' => __DIR__ . '/includes/jobqueue/jobs/DuplicateJob.php',
        'EditAction' => __DIR__ . '/includes/actions/EditAction.php',
        'EditCLI' => __DIR__ . '/maintenance/edit.php',
@@ -404,7 +408,7 @@ $wgAutoloadLocalClasses = [
        'EnhancedChangesList' => __DIR__ . '/includes/changes/EnhancedChangesList.php',
        'EnotifNotifyJob' => __DIR__ . '/includes/jobqueue/jobs/EnotifNotifyJob.php',
        'EnqueueJob' => __DIR__ . '/includes/jobqueue/jobs/EnqueueJob.php',
-       'EnqueueableDataUpdate' => __DIR__ . '/includes/deferred/DataUpdate.php',
+       'EnqueueableDataUpdate' => __DIR__ . '/includes/deferred/EnqueueableDataUpdate.php',
        'EraseArchivedFile' => __DIR__ . '/maintenance/eraseArchivedFile.php',
        'ErrorPageError' => __DIR__ . '/includes/exception/ErrorPageError.php',
        'EventRelayer' => __DIR__ . '/includes/libs/eventrelayer/EventRelayer.php',
@@ -505,46 +509,50 @@ $wgAutoloadLocalClasses = [
        'GenericArrayObject' => __DIR__ . '/includes/libs/GenericArrayObject.php',
        'GetConfiguration' => __DIR__ . '/maintenance/getConfiguration.php',
        'GetLagTimes' => __DIR__ . '/maintenance/getLagTimes.php',
-       'GetSlaveServer' => __DIR__ . '/maintenance/getSlaveServer.php',
+       'GetSlaveServer' => __DIR__ . '/maintenance/getReplicaServer.php',
        'GetTextMaint' => __DIR__ . '/maintenance/getText.php',
        'GitInfo' => __DIR__ . '/includes/GitInfo.php',
        'GlobalDependency' => __DIR__ . '/includes/cache/CacheDependency.php',
        'GlobalVarConfig' => __DIR__ . '/includes/config/GlobalVarConfig.php',
-       'HTMLApiField' => __DIR__ . '/includes/htmlform/HTMLApiField.php',
-       'HTMLAutoCompleteSelectField' => __DIR__ . '/includes/htmlform/HTMLAutoCompleteSelectField.php',
-       'HTMLButtonField' => __DIR__ . '/includes/htmlform/HTMLButtonField.php',
+       'HHVMMakeRepo' => __DIR__ . '/maintenance/hhvm/makeRepo.php',
+       'HTMLApiField' => __DIR__ . '/includes/htmlform/fields/HTMLApiField.php',
+       'HTMLAutoCompleteSelectField' => __DIR__ . '/includes/htmlform/fields/HTMLAutoCompleteSelectField.php',
+       'HTMLButtonField' => __DIR__ . '/includes/htmlform/fields/HTMLButtonField.php',
        'HTMLCacheUpdate' => __DIR__ . '/includes/deferred/HTMLCacheUpdate.php',
        'HTMLCacheUpdateJob' => __DIR__ . '/includes/jobqueue/jobs/HTMLCacheUpdateJob.php',
-       'HTMLCheckField' => __DIR__ . '/includes/htmlform/HTMLCheckField.php',
-       'HTMLCheckMatrix' => __DIR__ . '/includes/htmlform/HTMLCheckMatrix.php',
-       'HTMLComboboxField' => __DIR__ . '/includes/htmlform/HTMLComboboxField.php',
-       'HTMLEditTools' => __DIR__ . '/includes/htmlform/HTMLEditTools.php',
+       'HTMLCheckField' => __DIR__ . '/includes/htmlform/fields/HTMLCheckField.php',
+       'HTMLCheckMatrix' => __DIR__ . '/includes/htmlform/fields/HTMLCheckMatrix.php',
+       'HTMLComboboxField' => __DIR__ . '/includes/htmlform/fields/HTMLComboboxField.php',
+       'HTMLEditTools' => __DIR__ . '/includes/htmlform/fields/HTMLEditTools.php',
        'HTMLFileCache' => __DIR__ . '/includes/cache/HTMLFileCache.php',
-       'HTMLFloatField' => __DIR__ . '/includes/htmlform/HTMLFloatField.php',
+       'HTMLFloatField' => __DIR__ . '/includes/htmlform/fields/HTMLFloatField.php',
        'HTMLForm' => __DIR__ . '/includes/htmlform/HTMLForm.php',
+       'HTMLFormActionFieldLayout' => __DIR__ . '/includes/htmlform/HTMLFormElement.php',
+       'HTMLFormElement' => __DIR__ . '/includes/htmlform/HTMLFormElement.php',
        'HTMLFormField' => __DIR__ . '/includes/htmlform/HTMLFormField.php',
-       'HTMLFormFieldCloner' => __DIR__ . '/includes/htmlform/HTMLFormFieldCloner.php',
+       'HTMLFormFieldCloner' => __DIR__ . '/includes/htmlform/fields/HTMLFormFieldCloner.php',
+       'HTMLFormFieldLayout' => __DIR__ . '/includes/htmlform/HTMLFormElement.php',
        'HTMLFormFieldRequiredOptionsException' => __DIR__ . '/includes/htmlform/HTMLFormFieldRequiredOptionsException.php',
-       'HTMLFormFieldWithButton' => __DIR__ . '/includes/htmlform/HTMLFormFieldWithButton.php',
-       'HTMLHiddenField' => __DIR__ . '/includes/htmlform/HTMLHiddenField.php',
-       'HTMLInfoField' => __DIR__ . '/includes/htmlform/HTMLInfoField.php',
-       'HTMLIntField' => __DIR__ . '/includes/htmlform/HTMLIntField.php',
-       'HTMLMultiSelectField' => __DIR__ . '/includes/htmlform/HTMLMultiSelectField.php',
+       'HTMLFormFieldWithButton' => __DIR__ . '/includes/htmlform/fields/HTMLFormFieldWithButton.php',
+       'HTMLHiddenField' => __DIR__ . '/includes/htmlform/fields/HTMLHiddenField.php',
+       'HTMLInfoField' => __DIR__ . '/includes/htmlform/fields/HTMLInfoField.php',
+       'HTMLIntField' => __DIR__ . '/includes/htmlform/fields/HTMLIntField.php',
+       'HTMLMultiSelectField' => __DIR__ . '/includes/htmlform/fields/HTMLMultiSelectField.php',
        'HTMLNestedFilterable' => __DIR__ . '/includes/htmlform/HTMLNestedFilterable.php',
-       'HTMLRadioField' => __DIR__ . '/includes/htmlform/HTMLRadioField.php',
-       'HTMLSelectAndOtherField' => __DIR__ . '/includes/htmlform/HTMLSelectAndOtherField.php',
-       'HTMLSelectField' => __DIR__ . '/includes/htmlform/HTMLSelectField.php',
-       'HTMLSelectLimitField' => __DIR__ . '/includes/htmlform/HTMLSelectLimitField.php',
-       'HTMLSelectNamespace' => __DIR__ . '/includes/htmlform/HTMLSelectNamespace.php',
-       'HTMLSelectNamespaceWithButton' => __DIR__ . '/includes/htmlform/HTMLSelectNamespaceWithButton.php',
-       'HTMLSelectOrOtherField' => __DIR__ . '/includes/htmlform/HTMLSelectOrOtherField.php',
-       'HTMLSubmitField' => __DIR__ . '/includes/htmlform/HTMLSubmitField.php',
-       'HTMLTagFilter' => __DIR__ . '/includes/htmlform/HTMLTagFilter.php',
-       'HTMLTextAreaField' => __DIR__ . '/includes/htmlform/HTMLTextAreaField.php',
-       'HTMLTextField' => __DIR__ . '/includes/htmlform/HTMLTextField.php',
-       'HTMLTextFieldWithButton' => __DIR__ . '/includes/htmlform/HTMLTextFieldWithButton.php',
-       'HTMLTitleTextField' => __DIR__ . '/includes/htmlform/HTMLTitleTextField.php',
-       'HTMLUserTextField' => __DIR__ . '/includes/htmlform/HTMLUserTextField.php',
+       'HTMLRadioField' => __DIR__ . '/includes/htmlform/fields/HTMLRadioField.php',
+       'HTMLSelectAndOtherField' => __DIR__ . '/includes/htmlform/fields/HTMLSelectAndOtherField.php',
+       'HTMLSelectField' => __DIR__ . '/includes/htmlform/fields/HTMLSelectField.php',
+       'HTMLSelectLimitField' => __DIR__ . '/includes/htmlform/fields/HTMLSelectLimitField.php',
+       'HTMLSelectNamespace' => __DIR__ . '/includes/htmlform/fields/HTMLSelectNamespace.php',
+       'HTMLSelectNamespaceWithButton' => __DIR__ . '/includes/htmlform/fields/HTMLSelectNamespaceWithButton.php',
+       'HTMLSelectOrOtherField' => __DIR__ . '/includes/htmlform/fields/HTMLSelectOrOtherField.php',
+       'HTMLSubmitField' => __DIR__ . '/includes/htmlform/fields/HTMLSubmitField.php',
+       'HTMLTagFilter' => __DIR__ . '/includes/htmlform/fields/HTMLTagFilter.php',
+       'HTMLTextAreaField' => __DIR__ . '/includes/htmlform/fields/HTMLTextAreaField.php',
+       'HTMLTextField' => __DIR__ . '/includes/htmlform/fields/HTMLTextField.php',
+       'HTMLTextFieldWithButton' => __DIR__ . '/includes/htmlform/fields/HTMLTextFieldWithButton.php',
+       'HTMLTitleTextField' => __DIR__ . '/includes/htmlform/fields/HTMLTitleTextField.php',
+       'HTMLUserTextField' => __DIR__ . '/includes/htmlform/fields/HTMLUserTextField.php',
        'HWLDFWordAccumulator' => __DIR__ . '/includes/diff/DairikiDiff.php',
        'HashBagOStuff' => __DIR__ . '/includes/libs/objectcache/HashBagOStuff.php',
        'HashConfig' => __DIR__ . '/includes/config/HashConfig.php',
@@ -663,7 +671,6 @@ $wgAutoloadLocalClasses = [
        'LanguageConverter' => __DIR__ . '/languages/LanguageConverter.php',
        'LanguageCu' => __DIR__ . '/languages/classes/LanguageCu.php',
        'LanguageDsb' => __DIR__ . '/languages/classes/LanguageDsb.php',
-       'LanguageEo' => __DIR__ . '/languages/classes/LanguageEo.php',
        'LanguageEs' => __DIR__ . '/languages/classes/LanguageEs.php',
        'LanguageEt' => __DIR__ . '/languages/classes/LanguageEt.php',
        'LanguageFi' => __DIR__ . '/languages/classes/LanguageFi.php',
@@ -745,14 +752,12 @@ $wgAutoloadLocalClasses = [
        'LoggedOutEditToken' => __DIR__ . '/includes/user/LoggedOutEditToken.php',
        'LoggedUpdateMaintenance' => __DIR__ . '/maintenance/Maintenance.php',
        'LoginForm' => __DIR__ . '/includes/specialpage/LoginSignupSpecialPage.php',
-       'LoginFormAuthManager' => __DIR__ . '/includes/specialpage/LoginSignupSpecialPage.php',
-       'LoginFormPreAuthManager' => __DIR__ . '/includes/specials/pre-authmanager/SpecialUserlogin.php',
        'LoginHelper' => __DIR__ . '/includes/specials/helpers/LoginHelper.php',
        'LoginSignupSpecialPage' => __DIR__ . '/includes/specialpage/LoginSignupSpecialPage.php',
        'LonelyPagesPage' => __DIR__ . '/includes/specials/SpecialLonelypages.php',
        'LongPagesPage' => __DIR__ . '/includes/specials/SpecialLongpages.php',
        'MIMEsearchPage' => __DIR__ . '/includes/specials/SpecialMIMEsearch.php',
-       'MWCallableUpdate' => __DIR__ . '/includes/deferred/CallableUpdate.php',
+       'MWCallableUpdate' => __DIR__ . '/includes/deferred/MWCallableUpdate.php',
        'MWContentSerializationException' => __DIR__ . '/includes/content/ContentHandler.php',
        'MWCryptHKDF' => __DIR__ . '/includes/utils/MWCryptHKDF.php',
        'MWCryptHash' => __DIR__ . '/includes/utils/MWCryptHash.php',
@@ -787,6 +792,7 @@ $wgAutoloadLocalClasses = [
        'MarkpatrolledAction' => __DIR__ . '/includes/actions/MarkpatrolledAction.php',
        'McTest' => __DIR__ . '/maintenance/mctest.php',
        'MediaHandler' => __DIR__ . '/includes/media/MediaHandler.php',
+       'MediaHandlerFactory' => __DIR__ . '/includes/media/MediaHandlerFactory.php',
        'MediaStatisticsPage' => __DIR__ . '/includes/specials/SpecialMediaStatistics.php',
        'MediaTransformError' => __DIR__ . '/includes/media/MediaTransformOutput.php',
        'MediaTransformInvalidParametersException' => __DIR__ . '/includes/media/MediaTransformInvalidParametersException.php',
@@ -855,6 +861,7 @@ $wgAutoloadLocalClasses = [
        'MediaWiki\\Logger\\NullSpi' => __DIR__ . '/includes/debug/logger/NullSpi.php',
        'MediaWiki\\Logger\\Spi' => __DIR__ . '/includes/debug/logger/Spi.php',
        'MediaWiki\\MediaWikiServices' => __DIR__ . '/includes/MediaWikiServices.php',
+       'MediaWiki\\Search\\ParserOutputSearchDataExtractor' => __DIR__ . '/includes/search/ParserOutputSearchDataExtractor.php',
        'MediaWiki\\Services\\CannotReplaceActiveServiceException' => __DIR__ . '/includes/Services/CannotReplaceActiveServiceException.php',
        'MediaWiki\\Services\\ContainerDisabledException' => __DIR__ . '/includes/Services/ContainerDisabledException.php',
        'MediaWiki\\Services\\DestructibleService' => __DIR__ . '/includes/Services/DestructibleService.php',
@@ -879,7 +886,14 @@ $wgAutoloadLocalClasses = [
        'MediaWiki\\Session\\Token' => __DIR__ . '/includes/session/Token.php',
        'MediaWiki\\Session\\UserInfo' => __DIR__ . '/includes/session/UserInfo.php',
        'MediaWiki\\Site\\MediaWikiPageNameNormalizer' => __DIR__ . '/includes/site/MediaWikiPageNameNormalizer.php',
+       'MediaWiki\\Tidy\\BalanceActiveFormattingElements' => __DIR__ . '/includes/tidy/Balancer.php',
+       'MediaWiki\\Tidy\\BalanceElement' => __DIR__ . '/includes/tidy/Balancer.php',
+       'MediaWiki\\Tidy\\BalanceMarker' => __DIR__ . '/includes/tidy/Balancer.php',
+       'MediaWiki\\Tidy\\BalanceSets' => __DIR__ . '/includes/tidy/Balancer.php',
+       'MediaWiki\\Tidy\\BalanceStack' => __DIR__ . '/includes/tidy/Balancer.php',
+       'MediaWiki\\Tidy\\Balancer' => __DIR__ . '/includes/tidy/Balancer.php',
        'MediaWiki\\Tidy\\Html5Depurate' => __DIR__ . '/includes/tidy/Html5Depurate.php',
+       'MediaWiki\\Tidy\\Html5Internal' => __DIR__ . '/includes/tidy/Html5Internal.php',
        'MediaWiki\\Tidy\\RaggettBase' => __DIR__ . '/includes/tidy/RaggettBase.php',
        'MediaWiki\\Tidy\\RaggettExternal' => __DIR__ . '/includes/tidy/RaggettExternal.php',
        'MediaWiki\\Tidy\\RaggettInternalHHVM' => __DIR__ . '/includes/tidy/RaggettInternalHHVM.php',
@@ -939,7 +953,7 @@ $wgAutoloadLocalClasses = [
        'MwSql' => __DIR__ . '/maintenance/sql.php',
        'MySQLField' => __DIR__ . '/includes/db/DatabaseMysqlBase.php',
        'MySQLMasterPos' => __DIR__ . '/includes/db/DatabaseMysqlBase.php',
-       'MySqlLockManager' => __DIR__ . '/includes/filebackend/lockmanager/DBLockManager.php',
+       'MySqlLockManager' => __DIR__ . '/includes/filebackend/lockmanager/MySqlLockManager.php',
        'MysqlInstaller' => __DIR__ . '/includes/installer/MysqlInstaller.php',
        'MysqlUpdater' => __DIR__ . '/includes/installer/MysqlUpdater.php',
        'NaiveForeignTitleFactory' => __DIR__ . '/includes/title/NaiveForeignTitleFactory.php',
@@ -956,16 +970,17 @@ $wgAutoloadLocalClasses = [
        'NukePage' => __DIR__ . '/maintenance/nukePage.php',
        'NullFileJournal' => __DIR__ . '/includes/filebackend/filejournal/FileJournal.php',
        'NullFileOp' => __DIR__ . '/includes/filebackend/FileOp.php',
+       'NullIndexField' => __DIR__ . '/includes/search/NullIndexField.php',
        'NullJob' => __DIR__ . '/includes/jobqueue/jobs/NullJob.php',
        'NullLockManager' => __DIR__ . '/includes/filebackend/lockmanager/LockManager.php',
        'NullRepo' => __DIR__ . '/includes/filerepo/NullRepo.php',
        'NullStatsdDataFactory' => __DIR__ . '/includes/libs/stats/NullStatsdDataFactory.php',
+       'NumericUppercaseCollation' => __DIR__ . '/includes/collation/NumericUppercaseCollation.php',
        'OOUIHTMLForm' => __DIR__ . '/includes/htmlform/OOUIHTMLForm.php',
        'ORAField' => __DIR__ . '/includes/db/DatabaseOracle.php',
        'ORAResult' => __DIR__ . '/includes/db/DatabaseOracle.php',
        'ObjectCache' => __DIR__ . '/includes/objectcache/ObjectCache.php',
        'ObjectFactory' => __DIR__ . '/includes/libs/ObjectFactory.php',
-       'ObjectFileCache' => __DIR__ . '/includes/cache/ObjectFileCache.php',
        'OldChangesList' => __DIR__ . '/includes/changes/OldChangesList.php',
        'OldLocalFile' => __DIR__ . '/includes/filerepo/file/OldLocalFile.php',
        'OracleInstaller' => __DIR__ . '/includes/installer/OracleInstaller.php',
@@ -977,7 +992,6 @@ $wgAutoloadLocalClasses = [
        'PNGMetadataExtractor' => __DIR__ . '/includes/media/PNGMetadataExtractor.php',
        'PPCustomFrame_DOM' => __DIR__ . '/includes/parser/Preprocessor_DOM.php',
        'PPCustomFrame_Hash' => __DIR__ . '/includes/parser/Preprocessor_Hash.php',
-       'PPDAccum_Hash' => __DIR__ . '/includes/parser/Preprocessor_Hash.php',
        'PPDPart' => __DIR__ . '/includes/parser/Preprocessor_DOM.php',
        'PPDPart_Hash' => __DIR__ . '/includes/parser/Preprocessor_Hash.php',
        'PPDStack' => __DIR__ . '/includes/parser/Preprocessor_DOM.php',
@@ -1029,6 +1043,7 @@ $wgAutoloadLocalClasses = [
        'PermissionsError' => __DIR__ . '/includes/exception/PermissionsError.php',
        'PhpHttpRequest' => __DIR__ . '/includes/HttpFunctions.php',
        'PhpXmlBugTester' => __DIR__ . '/includes/installer/PhpBugTests.php',
+       'Pingback' => __DIR__ . '/includes/Pingback.php',
        'PoolCounter' => __DIR__ . '/includes/poolcounter/PoolCounter.php',
        'PoolCounterRedis' => __DIR__ . '/includes/poolcounter/PoolCounterRedis.php',
        'PoolCounterWork' => __DIR__ . '/includes/poolcounter/PoolCounterWork.php',
@@ -1046,7 +1061,7 @@ $wgAutoloadLocalClasses = [
        'PopulateRecentChangesSource' => __DIR__ . '/maintenance/populateRecentChangesSource.php',
        'PopulateRevisionLength' => __DIR__ . '/maintenance/populateRevisionLength.php',
        'PopulateRevisionSha1' => __DIR__ . '/maintenance/populateRevisionSha1.php',
-       'PostgreSqlLockManager' => __DIR__ . '/includes/filebackend/lockmanager/DBLockManager.php',
+       'PostgreSqlLockManager' => __DIR__ . '/includes/filebackend/lockmanager/PostgreSqlLockManager.php',
        'PostgresBlob' => __DIR__ . '/includes/db/DatabasePostgres.php',
        'PostgresField' => __DIR__ . '/includes/db/DatabasePostgres.php',
        'PostgresInstaller' => __DIR__ . '/includes/installer/PostgresInstaller.php',
@@ -1081,6 +1096,7 @@ $wgAutoloadLocalClasses = [
        'PurgeAction' => __DIR__ . '/includes/actions/PurgeAction.php',
        'PurgeChangedFiles' => __DIR__ . '/maintenance/purgeChangedFiles.php',
        'PurgeChangedPages' => __DIR__ . '/maintenance/purgeChangedPages.php',
+       'PurgeJobUtils' => __DIR__ . '/includes/jobqueue/utils/PurgeJobUtils.php',
        'PurgeList' => __DIR__ . '/maintenance/purgeList.php',
        'PurgeOldText' => __DIR__ . '/maintenance/purgeOldText.php',
        'PurgeParserCache' => __DIR__ . '/maintenance/purgeParserCache.php',
@@ -1092,6 +1108,7 @@ $wgAutoloadLocalClasses = [
        'RCDatabaseLogEntry' => __DIR__ . '/includes/logging/LogEntry.php',
        'RCFeedEngine' => __DIR__ . '/includes/rcfeed/RCFeedEngine.php',
        'RCFeedFormatter' => __DIR__ . '/includes/rcfeed/RCFeedFormatter.php',
+       'RESTBagOStuff' => __DIR__ . '/includes/libs/objectcache/RESTBagOStuff.php',
        'RSSFeed' => __DIR__ . '/includes/Feed.php',
        'RandomPage' => __DIR__ . '/includes/specials/SpecialRandompage.php',
        'RangeDifference' => __DIR__ . '/includes/diff/DiffEngine.php',
@@ -1134,6 +1151,7 @@ $wgAutoloadLocalClasses = [
        'ResetUserTokens' => __DIR__ . '/maintenance/resetUserTokens.php',
        'ResourceFileCache' => __DIR__ . '/includes/cache/ResourceFileCache.php',
        'ResourceLoader' => __DIR__ . '/includes/resourceloader/ResourceLoader.php',
+       'ResourceLoaderClientHtml' => __DIR__ . '/includes/resourceloader/ResourceLoaderClientHtml.php',
        'ResourceLoaderContext' => __DIR__ . '/includes/resourceloader/ResourceLoaderContext.php',
        'ResourceLoaderEditToolbarModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderEditToolbarModule.php',
        'ResourceLoaderFileModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderFileModule.php',
@@ -1157,6 +1175,7 @@ $wgAutoloadLocalClasses = [
        'ResourceLoaderUserDefaultsModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserDefaultsModule.php',
        'ResourceLoaderUserModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserModule.php',
        'ResourceLoaderUserOptionsModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserOptionsModule.php',
+       'ResourceLoaderUserStylesModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserStylesModule.php',
        'ResourceLoaderUserTokensModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderUserTokensModule.php',
        'ResourceLoaderWikiModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderWikiModule.php',
        'RestbaseVirtualRESTService' => __DIR__ . '/includes/libs/virtualrest/RestbaseVirtualRESTService.php',
@@ -1174,6 +1193,7 @@ $wgAutoloadLocalClasses = [
        'RevDelLogList' => __DIR__ . '/includes/revisiondelete/RevDelLogList.php',
        'RevDelRevisionItem' => __DIR__ . '/includes/revisiondelete/RevDelRevisionItem.php',
        'RevDelRevisionList' => __DIR__ . '/includes/revisiondelete/RevDelRevisionList.php',
+       'ReverseArrayIterator' => __DIR__ . '/includes/libs/ReverseArrayIterator.php',
        'ReverseChronologicalPager' => __DIR__ . '/includes/pager/ReverseChronologicalPager.php',
        'RevertAction' => __DIR__ . '/includes/actions/RevertAction.php',
        'Revision' => __DIR__ . '/includes/Revision.php',
@@ -1208,6 +1228,8 @@ $wgAutoloadLocalClasses = [
        'SearchEngineFactory' => __DIR__ . '/includes/search/SearchEngineFactory.php',
        'SearchExactMatchRescorer' => __DIR__ . '/includes/search/SearchExactMatchRescorer.php',
        'SearchHighlighter' => __DIR__ . '/includes/search/SearchHighlighter.php',
+       'SearchIndexField' => __DIR__ . '/includes/search/SearchIndexField.php',
+       'SearchIndexFieldDefinition' => __DIR__ . '/includes/search/SearchIndexFieldDefinition.php',
        'SearchMssql' => __DIR__ . '/includes/search/SearchMssql.php',
        'SearchMySQL' => __DIR__ . '/includes/search/SearchMySQL.php',
        'SearchNearMatchResultSet' => __DIR__ . '/includes/search/SearchNearMatchResultSet.php',
@@ -1247,6 +1269,7 @@ $wgAutoloadLocalClasses = [
        'SkinFallback' => __DIR__ . '/includes/skins/SkinFallback.php',
        'SkinFallbackTemplate' => __DIR__ . '/includes/skins/SkinFallbackTemplate.php',
        'SkinTemplate' => __DIR__ . '/includes/skins/SkinTemplate.php',
+       'SlideshowImageGallery' => __DIR__ . '/includes/gallery/SlideshowImageGallery.php',
        'SpecialActiveUsers' => __DIR__ . '/includes/specials/SpecialActiveusers.php',
        'SpecialAllMessages' => __DIR__ . '/includes/specials/SpecialAllMessages.php',
        'SpecialAllMyUploads' => __DIR__ . '/includes/specials/SpecialMyRedirectPages.php',
@@ -1263,13 +1286,10 @@ $wgAutoloadLocalClasses = [
        'SpecialChangeContentModel' => __DIR__ . '/includes/specials/SpecialChangeContentModel.php',
        'SpecialChangeCredentials' => __DIR__ . '/includes/specials/SpecialChangeCredentials.php',
        'SpecialChangeEmail' => __DIR__ . '/includes/specials/SpecialChangeEmail.php',
-       'SpecialChangeEmailPreAuthManager' => __DIR__ . '/includes/specials/pre-authmanager/SpecialChangeEmail.php',
        'SpecialChangePassword' => __DIR__ . '/includes/specials/SpecialChangePassword.php',
-       'SpecialChangePasswordPreAuthManager' => __DIR__ . '/includes/specials/pre-authmanager/SpecialChangePassword.php',
        'SpecialComparePages' => __DIR__ . '/includes/specials/SpecialComparePages.php',
        'SpecialContributions' => __DIR__ . '/includes/specials/SpecialContributions.php',
        'SpecialCreateAccount' => __DIR__ . '/includes/specials/SpecialCreateAccount.php',
-       'SpecialCreateAccountPreAuthManager' => __DIR__ . '/includes/specials/pre-authmanager/SpecialCreateAccount.php',
        'SpecialDiff' => __DIR__ . '/includes/specials/SpecialDiff.php',
        'SpecialEditTags' => __DIR__ . '/includes/specials/SpecialEditTags.php',
        'SpecialEditWatchlist' => __DIR__ . '/includes/specials/SpecialEditWatchlist.php',
@@ -1302,7 +1322,6 @@ $wgAutoloadLocalClasses = [
        'SpecialPageLanguage' => __DIR__ . '/includes/specials/SpecialPageLanguage.php',
        'SpecialPagesWithProp' => __DIR__ . '/includes/specials/SpecialPagesWithProp.php',
        'SpecialPasswordReset' => __DIR__ . '/includes/specials/SpecialPasswordReset.php',
-       'SpecialPasswordResetPreAuthManager' => __DIR__ . '/includes/specials/pre-authmanager/SpecialPasswordReset.php',
        'SpecialPermanentLink' => __DIR__ . '/includes/specials/SpecialPermanentLink.php',
        'SpecialPreferences' => __DIR__ . '/includes/specials/SpecialPreferences.php',
        'SpecialPrefixindex' => __DIR__ . '/includes/specials/SpecialPrefixindex.php',
@@ -1333,7 +1352,6 @@ $wgAutoloadLocalClasses = [
        'SpecialUploadStashTooLargeException' => __DIR__ . '/includes/specials/SpecialUploadStash.php',
        'SpecialUserLogin' => __DIR__ . '/includes/specials/SpecialUserLogin.php',
        'SpecialUserLogout' => __DIR__ . '/includes/specials/SpecialUserLogout.php',
-       'SpecialUserlogoutPreAuthManager' => __DIR__ . '/includes/specials/pre-authmanager/SpecialUserlogout.php',
        'SpecialVersion' => __DIR__ . '/includes/specials/SpecialVersion.php',
        'SpecialWatchlist' => __DIR__ . '/includes/specials/SpecialWatchlist.php',
        'SpecialWhatLinksHere' => __DIR__ . '/includes/specials/SpecialWhatlinkshere.php',
@@ -1425,6 +1443,7 @@ $wgAutoloadLocalClasses = [
        'UpdateArticleCount' => __DIR__ . '/maintenance/updateArticleCount.php',
        'UpdateCollation' => __DIR__ . '/maintenance/updateCollation.php',
        'UpdateDoubleWidthSearch' => __DIR__ . '/maintenance/updateDoubleWidthSearch.php',
+       'UpdateExtensionJsonSchema' => __DIR__ . '/maintenance/updateExtensionJsonSchema.php',
        'UpdateLogging' => __DIR__ . '/maintenance/archives/upgradeLogging.php',
        'UpdateMediaWiki' => __DIR__ . '/maintenance/update.php',
        'UpdateRestrictions' => __DIR__ . '/maintenance/updateRestrictions.php',
@@ -1468,8 +1487,6 @@ $wgAutoloadLocalClasses = [
        'UserOptions' => __DIR__ . '/maintenance/userOptions.inc',
        'UserPasswordPolicy' => __DIR__ . '/includes/password/UserPasswordPolicy.php',
        'UserRightsProxy' => __DIR__ . '/includes/user/UserRightsProxy.php',
-       'UsercreateTemplate' => __DIR__ . '/includes/templates/Usercreate.php',
-       'UserloginTemplate' => __DIR__ . '/includes/templates/Userlogin.php',
        'UserrightsPage' => __DIR__ . '/includes/specials/SpecialUserrights.php',
        'UsersPager' => __DIR__ . '/includes/specials/pagers/UsersPager.php',
        'UtfNormal' => __DIR__ . '/includes/compat/normal/UtfNormal.php',
@@ -1480,6 +1497,7 @@ $wgAutoloadLocalClasses = [
        'VirtualRESTService' => __DIR__ . '/includes/libs/virtualrest/VirtualRESTService.php',
        'VirtualRESTServiceClient' => __DIR__ . '/includes/libs/virtualrest/VirtualRESTServiceClient.php',
        'WANObjectCache' => __DIR__ . '/includes/libs/objectcache/WANObjectCache.php',
+       'WaitConditionLoop' => __DIR__ . '/includes/libs/WaitConditionLoop.php',
        'WantedCategoriesPage' => __DIR__ . '/includes/specials/SpecialWantedcategories.php',
        'WantedFilesPage' => __DIR__ . '/includes/specials/SpecialWantedfiles.php',
        'WantedPagesPage' => __DIR__ . '/includes/specials/SpecialWantedpages.php',
@@ -1522,6 +1540,7 @@ $wgAutoloadLocalClasses = [
        'WikiReference' => __DIR__ . '/includes/WikiMap.php',
        'WikiRevision' => __DIR__ . '/includes/import/WikiRevision.php',
        'WikiStatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
+       'WikiTextStructure' => __DIR__ . '/includes/content/WikiTextStructure.php',
        'WikitextContent' => __DIR__ . '/includes/content/WikitextContent.php',
        'WikitextContentHandler' => __DIR__ . '/includes/content/WikitextContentHandler.php',
        'WinCacheBagOStuff' => __DIR__ . '/includes/libs/objectcache/WinCacheBagOStuff.php',
index 0e512a6..2243b7c 100644 (file)
@@ -16,7 +16,7 @@
                "wiki": "https://www.mediawiki.org/"
        },
        "require": {
-               "composer/semver": "1.4.0",
+               "composer/semver": "1.4.1",
                "cssjanus/cssjanus": "1.1.2",
                "ext-ctype": "*",
                "ext-iconv": "*",
@@ -25,7 +25,7 @@
                "ext-xml": "*",
                "liuggio/statsd-php-client": "1.0.18",
                "mediawiki/at-ease": "1.1.0",
-               "oojs/oojs-ui": "0.17.5",
+               "oojs/oojs-ui": "0.17.8",
                "oyejorge/less.php": "1.7.0.10",
                "php": ">=5.5.9",
                "psr/log": "1.0.0",
                "wikimedia/relpath": "1.0.3",
                "wikimedia/running-stat": "1.1.0",
                "wikimedia/utfnormal": "1.0.3",
-               "wikimedia/wrappedstring": "2.0.0",
+               "wikimedia/wrappedstring": "2.2.0",
                "zordius/lightncandy": "0.23"
        },
        "require-dev": {
                "jakub-onderka/php-parallel-lint": "0.9.2",
-               "justinrainbow/json-schema": "~1.3",
+               "justinrainbow/json-schema": "~3.0",
                "mediawiki/mediawiki-codesniffer": "0.7.2",
                "monolog/monolog": "~1.18.2",
-               "nikic/php-parser": "1.4.1",
+               "nikic/php-parser": "2.1.0",
                "nmred/kafka-php": "0.1.5",
                "phpunit/phpunit": "4.8.24",
                "wikimedia/avro": "1.7.7"
index 5bc8bfc..6f3da97 100644 (file)
@@ -5,7 +5,7 @@ The 'docs' directory contain various text files that should help you understand
 the most important parts of the code of MediaWiki. More in-depth documentation
 can be found at:
   https://www.mediawiki.org/wiki/Manual:Code
-
+  https://www.mediawiki.org/wiki/Developer_hub
 API documentation is automatically generated and updated daily at:
   https://doc.wikimedia.org/mediawiki-core/master/php/html/
 
index ba3045e..44ec764 100644 (file)
@@ -8,7 +8,7 @@ By Tim Starling, January 2006.
 For information about the MediaWiki database layout, such as a 
 description of the tables and their contents, please see:
   https://www.mediawiki.org/wiki/Manual:Database_layout
-  https://gerrit.wikimedia.org/r/gitweb?p=mediawiki/core.git;a=blob_plain;f=maintenance/tables.sql;hb=HEAD
+  https://phabricator.wikimedia.org/diffusion/MW/browse/master/maintenance/tables.sql
 
 
 ------------------------------------------------------------------------
index efa573d..f19574c 100644 (file)
@@ -1,23 +1,23 @@
 This document is intended to provide useful advice for parties seeking to
-redistribute MediaWiki to end users.  It's targeted particularly at maintainers
+redistribute MediaWiki to end users. It's targeted particularly at maintainers
 for Linux distributions, since it's been observed that distribution packages of
-MediaWiki often break.  We've consistently had to recommend that users seeking
+MediaWiki often break. We've consistently had to recommend that users seeking
 support use official tarballs instead of their distribution's packages, and
-this often solves whatever problem the user is having.  It would be nice if
+this often solves whatever problem the user is having. It would be nice if
 this could change.
 
 == Background: why web applications are different ==
 
 MediaWiki is intended to be usable on any web host that provides support for
-PHP and a database.  Many users of low-end shared hosting have very limited
+PHP and a database. Many users of low-end shared hosting have very limited
 access to their machine: often only FTP access to some subdirectory of the web
-root.  Support for these users entails several restrictions, such as:
+root. Support for these users entails several restrictions, such as:
 
-  1) We cannot require installation of any files outside the web root.  Few of
+  1) We cannot require installation of any files outside the web root. Few of
   our users have access to directories like /usr or /etc.
   2) We cannot require the ability to run any utility on the command line.
   Many shared hosts have exec() and similar PHP functions disabled.
-  3) We cannot assume that the software has write access anywhere useful.  The
+  3) We cannot assume that the software has write access anywhere useful. The
   user account that MediaWiki (including its installer) runs under is often
   different from the account the user used to upload the files, and we might be
   restricted by PHP settings such as safe mode or open_basedir.
@@ -30,28 +30,28 @@ root.  Support for these users entails several restrictions, such as:
 
 Since anything that works on cheap shared hosting will work if you have shell
 or root access too, MediaWiki's design is based around catering to the lowest
-common denominator.  Although we support higher-end setups as well (like
+common denominator. Although we support higher-end setups as well (like
 Wikipedia!), the way many things work by default is tailored toward shared
-hosting.  These defaults are unconventional from the point of view of normal
+hosting. These defaults are unconventional from the point of view of normal
 (non-web) applications -- they might conflict with distributors' policies, and
 they certainly aren't ideal for someone who's installing MediaWiki as root.
 
 == Directory structure ==
 
 Because of constraint (1) above, MediaWiki does not conform to normal
-Unix filesystem layout.  Hopefully we'll offer direct support for standard
+Unix filesystem layout. Hopefully we'll offer direct support for standard
 layouts in the future, but for now *any change to the location of files is
-unsupported*.  Moving things and leaving symlinks will *probably* not break
+unsupported*. Moving things and leaving symlinks will *probably* not break
 anything, but it is *strongly* advised not to try any more intrusive changes to
-get MediaWiki to conform more closely to your filesystem hierarchy.  Any such
+get MediaWiki to conform more closely to your filesystem hierarchy. Any such
 attempt will almost certainly result in unnecessary bugs.
 
 The standard recommended location to install MediaWiki, relative to the web
-root, is /w (so, e.g., /var/www/w).  Rewrite rules can then be used to enable
-"pretty URLs" like /wiki/Article instead of /w/index.php?title=Article.  (This
+root, is /w (so, e.g., /var/www/w). Rewrite rules can then be used to enable
+"pretty URLs" like /wiki/Article instead of /w/index.php?title=Article. (This
 is the convention Wikipedia uses.)  In theory, it should be possible to enable
 the appropriate rewrite rules by default, if you can reconfigure the web
-server, but you'd need to alter LocalSettings.php too.  See
+server, but you'd need to alter LocalSettings.php too. See
 <https://www.mediawiki.org/wiki/Manual:Short_URL> for details on short URLs.
 
 If you really must mess around with the directory structure, note that the
@@ -59,37 +59,38 @@ following files *must* all be web-accessible for MediaWiki to function
 correctly:
 
   * api.php, img_auth.php, index.php, load.php, opensearch_desc.php, thumb.php,
-  profileinfo.php, redirect.php, trackback.php.  These are the entry points for
-  normal usage.  This list may be incomplete and is subject to change.
+  profileinfo.php. These are the entry points for normal usage. This list may be
+  incomplete and is subject to change.
   * mw-config/index.php: Used for web-based installation (sets up the database,
   prompts for the name of the wiki, etc.).
-  * images/: Used for uploaded files.  This could be somewhere else if
+  * images/: Used for uploaded files. This could be somewhere else if
   $wgUploadDirectory and $wgUploadPath are changed appropriately.
   * skins/*/: Subdirectories of skins/ contain CSS and JavaScript files that
-  must be accessible to web browsers.  The PHP files and Skin.sample in skins/
-  don't need to be accessible.  This could be somewhere else if
+  must be accessible to web browsers. The PHP files and Skin.sample in skins/
+  don't need to be accessible. This could be somewhere else if
   $wgStyleDirectory and $wgStylePath are changed appropriately.
   * extensions/: Many extensions include CSS and JavaScript files in their
-  extensions directory, and will break if they aren't web-accessible.  Some
+  extensions directory, and will break if they aren't web-accessible. Some
   extensions might theoretically provide additional entry points as well, at
   least in principle.
 
 But all files should keep their position relative to the web-visible
-installation directory no matter what.  If you must move includes/ somewhere in
-/usr/share, provide a symlink from /var/www/w.  If you don't, you *will* break
-something.  You have been warned.
+installation directory no matter what. If you must move includes/ somewhere in
+/usr/share, provide a symlink from /var/www/w. If you don't, you *will* break
+something. You have been warned.
 
 == Configuration ==
 
-MediaWiki is configured using LocalSettings.php.  This is a PHP file that's
+MediaWiki is configured using LocalSettings.php. This is a PHP file that's
 generated when the user visits mw-config/index.php to install the software, and
-which the user can edit by hand thereafter.  It's just a plain old PHP file,
-and can contain any PHP statements.  It usually sets global variables that are
+which the user can edit by hand thereafter. It's just a plain old PHP file,
+and can contain any PHP statements. It usually sets global variables that are
 used for configuration, and includes files used by any extensions.
 
-Distributors can easily add extra statements to the autogenerated
-LocalSettings.php by changing mw-config/overrides.php (see that file for details
-and examples).
+Distributors can easily change the installer behavior, including LocalSettings
+generated, by placing their overrides into mw-config/overrides directory. Doing
+that is highly preferred to modifying MediaWiki code directly. See
+mw-config/overrides/README for more details and examples.
 
 There's a new maintenance/install.php script which could be used for performing
 an install through the command line.
@@ -98,7 +99,7 @@ Some configuration options that distributors might be in a position to set
 intelligently:
 
   * $wgEmergencyContact: An e-mail address that can be used to contact the wiki
-  administrator.  By default, "wikiadmin@ServerName".
+  administrator. By default, "wikiadmin@ServerName".
   * $wgPasswordSender: The e-mail address to use when sending password e-mails.
   By default, "MediaWiki Mail <apache@ServerName>".
        (with ServerName guessed from the http request)
@@ -115,16 +116,16 @@ Any package manager which replaces the files but doesn't update the db is leavin
 an inconsistent wiki that may produce blank pages (php errors) when new features 
 using the changed schema would be used.
 
-Since MediaWiki 1.17 it is possible to upgrade using the installer by providing 
+Since MediaWiki 1.17 it is possible to upgrade using the web installer by providing
 an arbitrary secret value stored as $wgUpgradeKey in LocalSettings (older versions 
 needed to rename LocalSettings.php in order to upgrade using the installer).
 
 == Documentation ==
 
 MediaWiki's official documentation is split between two places: the source
-code, and <https://www.mediawiki.org/>.  The source code documentation is written
+code, and <https://www.mediawiki.org/>. The source code documentation is written
 exclusively by developers, and so is likely to be reliable (at worst,
-outdated).  However, it can be pretty sparse.  mediawiki.org documentation is
+outdated). However, it can be pretty sparse. mediawiki.org documentation is
 often much more thorough, but it's maintained by a wiki that's open to
 anonymous edits, so its quality is sometimes sketchy -- don't assume that
 anything there is officially endorsed!
@@ -132,31 +133,27 @@ anything there is officially endorsed!
 == Upstream ==
 
 MediaWiki is a project hosted and led by the Wikimedia Foundation, the
-not-for-profit charity that operates Wikipedia.  Wikimedia employs the lead
+not-for-profit charity that operates Wikipedia. Wikimedia employs the lead
 developer and several other paid developers, but commit access is given out
-liberally and there are multiple very active volunteer developers as well.  A
+liberally and there are multiple very active volunteer developers as well. A
 list of developers can be found at <https://www.mediawiki.org/wiki/Developers>.
 
-MediaWiki's bug tracker is at <https://bugzilla.wikimedia.org>.  However, most
-developers follow the bug tracker little or not at all.  The best place to
-post if you want to get developers' attention is the wikitech-l mailing list
-<https://lists.wikimedia.org/mailman/listinfo/wikitech-l>.  Posts to wikitech-l
-will inevitably be read by multiple experienced MediaWiki developers.  There's
+MediaWiki's bug tracker is at <https://phabricator.wikimedia.org>. However, you
+might find that the best place to post if you want to get developers' attention
+is the wikitech-l mailing list
+<https://lists.wikimedia.org/mailman/listinfo/wikitech-l>. Posts to wikitech-l
+will inevitably be read by multiple experienced MediaWiki developers. There's
 also an active IRC chat at <irc://irc.freenode.net/mediawiki>, where there are
 usually several developers at reasonably busy times of day.
 
-Unfortunately, we don't have a very good system for patch review.  Patches
-should be submitted on Bugzilla (as unified diffs produced with "svn diff"
-against the latest trunk revision), but many patches languish without review
-until they bitrot into uselessness.  You might want to get a developer to
-commit to reviewing your patch before you put too much effort into it.
-Reasonably straightforward patches shouldn't be too hard to get accepted if
-there's an interested developer, however -- posting to Bugzilla and then
-dropping a note on wikitech-l if nobody responds is a good tactic.
+Our Git repositories are hosted at <https://gerrit.wikimedia.org>, see
+<https://www.mediawiki.org/wiki/Gerrit> for more information. Patches should
+be submitted there. If you know which developers are best suited to review your
+patch, add them to it, otherwise ask on IRC to get better review time.
 
 All redistributors of MediaWiki should be subscribed to mediawiki-announce
-<https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce>.  It's
-extremely low-traffic, with an average of less than one post per month.  All
+<https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce>. It's
+extremely low-traffic, with an average of less than one post per month. All
 new releases are announced here, including critical security updates.
 
 == Useful software to install ==
@@ -167,41 +164,40 @@ perhaps configure it to use them (see Configuration section of this document):
 
   * APC (Alternative PHP Cache), XCache, or similar: Will greatly speed up the
   execution of MediaWiki, and all other PHP applications, at some cost in
-  memory usage.  Will be used automatically for the most part.
-  * clamav: Can be used for virus scanning of uploaded files.  Enable with
+  memory usage. Will be used automatically for the most part.
+  * clamav: Can be used for virus scanning of uploaded files. Enable with
   "$wgAntivirus = 'clamav';".
-  * DjVuLibre: Allows processing of DjVu files.  To enable this, set
+  * DjVuLibre: Allows processing of DjVu files. To enable this, set
   "$wgDjvuDump = 'djvudump'; $wgDjvuRenderer = 'ddjvu'; $wgDjvuTxt = 'djvutxt';".
-  * HTML Tidy: Fixes errors in HTML at runtime.  Can be enabled with 
+  * HTML Tidy: Fixes errors in HTML at runtime. Can be enabled with 
        "$wgUseTidy = true;".
-  * ImageMagick: For resizing images.  "$wgUseImageMagick = true;" will enable
-  it.  PHP's GD can also be used, but ImageMagick is preferable.
-  * Squid: Can provide a drastic speedup and a major cut in resource
-  consumption, but enabling it may interfere with other applications.  It might
-  be suitable for a separate mediawiki-squid package.  For setup details, see:
-  <https://www.mediawiki.org/wiki/Manual:Squid_caching>
+  * ImageMagick: For resizing images. "$wgUseImageMagick = true;" will enable
+  it. PHP's GD can also be used, but ImageMagick is preferable.
+  * HTTP cache such as Varnish or Squid: can provide a drastic speedup and a
+  major cut in resource consumption, but enabling it may interfere with other
+  applications. It might be suitable for a separate package. For setup details, see:
+  - <https://www.mediawiki.org/wiki/Manual:Varnish_caching>
+  - <https://www.mediawiki.org/wiki/Manual:Squid_caching>
   * rsvg or other SVG rasterizer: ImageMagick can be used for SVG support, but
-  is not ideal.  Wikipedia (as of the time of this writing) uses rsvg.  To
+  is not ideal. Wikipedia (as of the time of this writing) uses rsvg. To
   enable, set "$wgSVGConverter = 'rsvg';" (or other as appropriate).
-  * texvc: Included with MediaWiki.  Instructions for compiling and
-  installing it are in the math/ directory.
 
-MediaWiki uses some standard GNU utilities as well, such as diff and diff3.  If
+MediaWiki uses some standard GNU utilities as well, such as diff and diff3. If
 these are present in /usr/bin or some other reasonable location, they will be
 configured automatically on install.
 
-MediaWiki also has a "job queue" that handles background processing.  Because
+MediaWiki also has a "job queue" that handles background processing. Because
 shared hosts often don't provide access to cron, the job queue is run on every
-page view by default.  This means the background tasks aren't really done in
-the background.  Busy wikis can set $wgJobRunRate to 0 and run
-maintenance/runJobs.php periodically out of cron.  Distributors probably
+page view by default. This means the background tasks aren't really done in
+the background. Busy wikis can set $wgJobRunRate to 0 and run
+maintenance/runJobs.php periodically out of cron. Distributors probably
 shouldn't set this up as a default, however, since the extra cron job is
 unnecessary overhead for a little-used wiki.
 
 == Web server configuration ==
 
 MediaWiki includes several .htaccess files to restrict access to some
-directories.  If the web server is not configured to support these files, and
+directories. If the web server is not configured to support these files, and
 the relevant directories haven't been moved someplace inaccessible anyway (e.g.
 symlinked in /usr/share with the web server configured to not follow symlinks),
 then it might be useful to deny web access to those directories in the web
index 11adc61..c010014 100644 (file)
                                                                        "type": "string",
                                                                        "description": "Group with which this module should be loaded"
                                                                },
+                                                               "deprecated": {
+                                                                       "type": ["object", "boolean"],
+                                                                       "description": "Whether the module is deprecated and usage is discouraged. Either a boolean or an object with key message can be used to customise deprecation message."
+                                                               },
                                                                "position": {
                                                                        "type": "string",
                                                                        "description": "Position on the page to load this module at",
                        "type": "object",
                        "description": "ResourceLoader LESS variables"
                },
-               "ResourceLoaderLESSImportPaths": {
-                       "type": "object",
-                       "description": "ResourceLoader import paths"
-               },
                "ConfigRegistry": {
                        "type": "object",
                        "description": "Registry of factory functions to create Config objects"
                                        "capitallinkoverride": {
                                                "type": "boolean",
                                                "description": "Set $wgCapitalLinks on a per-namespace basis"
+                                       },
+                                       "conditional": {
+                                               "type": "boolean",
+                                               "description": "Whether the namespace is conditional upon configuration and should not be registered (requires separate registration via a hook)",
+                                               "default": false
                                        }
                                },
                                "required": ["id", "constant", "name"]
                        ],
                        "description": "A function to be called right after MediaWiki processes this file"
                },
+               "config_prefix": {
+                       "type": "string",
+                       "default": "wg",
+                       "description": "Prefix to put in front of configuration settings when exporting them to $GLOBALS"
+               },
                "config": {
                        "type": "object",
                        "description": "Configuration options for this extension",
-                       "properties": {
-                               "_prefix": {
-                                       "type": "string",
-                                       "default": "wg",
-                                       "description": "Prefix to put in front of configuration settings when exporting them to $GLOBALS"
-                               }
-                       },
                        "patternProperties": {
                                "^[a-zA-Z_\u007f-\u00ff][a-zA-Z0-9_\u007f-\u00ff]*$": {
+                                       "type": "object",
                                        "properties": {
-                                               "_merge_strategy": {
+                                               "value": {
+                                                       "required": true
+                                               },
+                                               "merge_strategy": {
                                                        "type": "string",
                                                        "enum": [
                                                                "array_merge_recursive",
                                                                "array_merge"
                                                        ],
                                                        "default": "array_merge"
+                                               },
+                                               "path": {
+                                                       "description": "Whether this should be interpreted as a filesystem path, relative to extension directory root",
+                                                       "type": "boolean",
+                                                       "default": false
+                                               },
+                                               "description": {
+                                                       "type": ["string", "array"],
+                                                       "description": "A description of the config setting, mostly for documentation/developers"
                                                }
                                        }
                                }
diff --git a/docs/extension.schema.v1.json b/docs/extension.schema.v1.json
new file mode 100644 (file)
index 0000000..d707864
--- /dev/null
@@ -0,0 +1,895 @@
+{
+       "$schema": "http://json-schema.org/schema#",
+       "description": "MediaWiki extension.json schema",
+       "type": "object",
+       "properties": {
+               "manifest_version": {
+                       "type": "integer",
+                       "description": "Version of the extension.json schema the extension.json file is in.",
+                       "required": true
+               },
+               "name": {
+                       "type": "string",
+                       "description": "The extension's canonical name.",
+                       "required": true
+               },
+               "namemsg": {
+                       "type": "string",
+                       "description": "i18n message key of the extension's name."
+               },
+               "type": {
+                       "type": "string",
+                       "description": "The extension's type, as an index to $wgExtensionCredits.",
+                       "default": "other"
+               },
+               "author": {
+                       "type": [
+                               "string",
+                               "array"
+                       ],
+                       "description": "Extension's authors.",
+                       "items": {
+                               "type": "string"
+                       }
+               },
+               "version": {
+                       "type": "string",
+                       "description": "The version of this release of the extension."
+               },
+               "url": {
+                       "type": "string",
+                       "description": "URL to the homepage for the extension.",
+                       "format": "uri"
+               },
+               "description": {
+                       "type": "string",
+                       "description": "Raw description of the extension."
+               },
+               "descriptionmsg": {
+                       "type": "string",
+                       "description": "Message key for a i18n message describing the extension."
+               },
+               "license-name": {
+                       "type": "string",
+                       "description": "Short identifier for the license under which the extension is released.",
+                       "enum": [
+                               "AFL-1.1",
+                               "AFL-1.2",
+                               "AFL-2.0",
+                               "AFL-2.1",
+                               "AFL-3.0",
+                               "APL-1.0",
+                               "Aladdin",
+                               "ANTLR-PD",
+                               "Apache-1.0",
+                               "Apache-1.1",
+                               "Apache-2.0",
+                               "APSL-1.0",
+                               "APSL-1.1",
+                               "APSL-1.2",
+                               "APSL-2.0",
+                               "Artistic-1.0",
+                               "Artistic-1.0-cl8",
+                               "Artistic-1.0-Perl",
+                               "Artistic-2.0",
+                               "AAL",
+                               "BitTorrent-1.0",
+                               "BitTorrent-1.1",
+                               "BSL-1.0",
+                               "BSD-2-Clause",
+                               "BSD-2-Clause-FreeBSD",
+                               "BSD-2-Clause-NetBSD",
+                               "BSD-3-Clause",
+                               "BSD-3-Clause-Clear",
+                               "BSD-4-Clause",
+                               "BSD-4-Clause-UC",
+                               "CECILL-1.0",
+                               "CECILL-1.1",
+                               "CECILL-2.0",
+                               "CECILL-B",
+                               "CECILL-C",
+                               "ClArtistic",
+                               "CNRI-Python",
+                               "CNRI-Python-GPL-Compatible",
+                               "CPOL-1.02",
+                               "CDDL-1.0",
+                               "CDDL-1.1",
+                               "CPAL-1.0",
+                               "CPL-1.0",
+                               "CATOSL-1.1",
+                               "Condor-1.1",
+                               "CC-BY-1.0",
+                               "CC-BY-2.0",
+                               "CC-BY-2.5",
+                               "CC-BY-3.0",
+                               "CC-BY-ND-1.0",
+                               "CC-BY-ND-2.0",
+                               "CC-BY-ND-2.5",
+                               "CC-BY-ND-3.0",
+                               "CC-BY-NC-1.0",
+                               "CC-BY-NC-2.0",
+                               "CC-BY-NC-2.5",
+                               "CC-BY-NC-3.0",
+                               "CC-BY-NC-ND-1.0",
+                               "CC-BY-NC-ND-2.0",
+                               "CC-BY-NC-ND-2.5",
+                               "CC-BY-NC-ND-3.0",
+                               "CC-BY-NC-SA-1.0",
+                               "CC-BY-NC-SA-2.0",
+                               "CC-BY-NC-SA-2.5",
+                               "CC-BY-NC-SA-3.0",
+                               "CC-BY-SA-1.0",
+                               "CC-BY-SA-2.0",
+                               "CC-BY-SA-2.5",
+                               "CC-BY-SA-3.0",
+                               "CC0-1.0",
+                               "CUA-OPL-1.0",
+                               "D-FSL-1.0",
+                               "WTFPL",
+                               "EPL-1.0",
+                               "eCos-2.0",
+                               "ECL-1.0",
+                               "ECL-2.0",
+                               "EFL-1.0",
+                               "EFL-2.0",
+                               "Entessa",
+                               "ErlPL-1.1",
+                               "EUDatagrid",
+                               "EUPL-1.0",
+                               "EUPL-1.1",
+                               "Fair",
+                               "Frameworx-1.0",
+                               "FTL",
+                               "AGPL-1.0",
+                               "AGPL-3.0",
+                               "GFDL-1.1",
+                               "GFDL-1.2",
+                               "GFDL-1.3",
+                               "GPL-1.0",
+                               "GPL-1.0+",
+                               "GPL-2.0",
+                               "GPL-2.0+",
+                               "GPL-2.0-with-autoconf-exception",
+                               "GPL-2.0-with-bison-exception",
+                               "GPL-2.0-with-classpath-exception",
+                               "GPL-2.0-with-font-exception",
+                               "GPL-2.0-with-GCC-exception",
+                               "GPL-3.0",
+                               "GPL-3.0+",
+                               "GPL-3.0-with-autoconf-exception",
+                               "GPL-3.0-with-GCC-exception",
+                               "LGPL-2.1",
+                               "LGPL-2.1+",
+                               "LGPL-3.0",
+                               "LGPL-3.0+",
+                               "LGPL-2.0",
+                               "LGPL-2.0+",
+                               "gSOAP-1.3b",
+                               "HPND",
+                               "IBM-pibs",
+                               "IPL-1.0",
+                               "Imlib2",
+                               "IJG",
+                               "Intel",
+                               "IPA",
+                               "ISC",
+                               "JSON",
+                               "LPPL-1.3a",
+                               "LPPL-1.0",
+                               "LPPL-1.1",
+                               "LPPL-1.2",
+                               "LPPL-1.3c",
+                               "Libpng",
+                               "LPL-1.02",
+                               "LPL-1.0",
+                               "MS-PL",
+                               "MS-RL",
+                               "MirOS",
+                               "MIT",
+                               "Motosoto",
+                               "MPL-1.0",
+                               "MPL-1.1",
+                               "MPL-2.0",
+                               "MPL-2.0-no-copyleft-exception",
+                               "Multics",
+                               "NASA-1.3",
+                               "Naumen",
+                               "NBPL-1.0",
+                               "NGPL",
+                               "NOSL",
+                               "NPL-1.0",
+                               "NPL-1.1",
+                               "Nokia",
+                               "NPOSL-3.0",
+                               "NTP",
+                               "OCLC-2.0",
+                               "ODbL-1.0",
+                               "PDDL-1.0",
+                               "OGTSL",
+                               "OLDAP-2.2.2",
+                               "OLDAP-1.1",
+                               "OLDAP-1.2",
+                               "OLDAP-1.3",
+                               "OLDAP-1.4",
+                               "OLDAP-2.0",
+                               "OLDAP-2.0.1",
+                               "OLDAP-2.1",
+                               "OLDAP-2.2",
+                               "OLDAP-2.2.1",
+                               "OLDAP-2.3",
+                               "OLDAP-2.4",
+                               "OLDAP-2.5",
+                               "OLDAP-2.6",
+                               "OLDAP-2.7",
+                               "OPL-1.0",
+                               "OSL-1.0",
+                               "OSL-2.0",
+                               "OSL-2.1",
+                               "OSL-3.0",
+                               "OLDAP-2.8",
+                               "OpenSSL",
+                               "PHP-3.0",
+                               "PHP-3.01",
+                               "PostgreSQL",
+                               "Python-2.0",
+                               "QPL-1.0",
+                               "RPSL-1.0",
+                               "RPL-1.1",
+                               "RPL-1.5",
+                               "RHeCos-1.1",
+                               "RSCPL",
+                               "Ruby",
+                               "SAX-PD",
+                               "SGI-B-1.0",
+                               "SGI-B-1.1",
+                               "SGI-B-2.0",
+                               "OFL-1.0",
+                               "OFL-1.1",
+                               "SimPL-2.0",
+                               "Sleepycat",
+                               "SMLNJ",
+                               "SugarCRM-1.1.3",
+                               "SISSL",
+                               "SISSL-1.2",
+                               "SPL-1.0",
+                               "Watcom-1.0",
+                               "NCSA",
+                               "VSL-1.0",
+                               "W3C",
+                               "WXwindows",
+                               "Xnet",
+                               "X11",
+                               "XFree86-1.1",
+                               "YPL-1.0",
+                               "YPL-1.1",
+                               "Zimbra-1.3",
+                               "Zlib",
+                               "ZPL-1.1",
+                               "ZPL-2.0",
+                               "ZPL-2.1",
+                               "Unlicense"
+                       ]
+               },
+               "requires": {
+                       "type": "object",
+                       "description": "Indicates what versions of MediaWiki core are required. This syntax may be extended in the future, for example to check dependencies between other extensions.",
+                       "properties": {
+                               "MediaWiki": {
+                                       "type": "string",
+                                       "description": "Version constraint string against MediaWiki core."
+                               }
+                       }
+               },
+               "ResourceFileModulePaths": {
+                       "type": "object",
+                       "description": "Default paths to use for all ResourceLoader file modules",
+                       "additionalProperties": false,
+                       "properties": {
+                               "localBasePath": {
+                                       "type": "string",
+                                       "description": "Base path to prepend to all local paths, relative to current directory"
+                               },
+                               "remoteExtPath": {
+                                       "type": "string",
+                                       "description": "Base path to prepend to all remote paths, relative to $wgExtensionAssetsPath"
+                               },
+                               "remoteSkinPath": {
+                                       "type": "string",
+                                       "description": "Base path to prepend to all remote paths, relative to $wgStylePath"
+                               }
+                       }
+               },
+               "ResourceModules": {
+                       "type": "object",
+                       "description": "ResourceLoader modules to register",
+                       "patternProperties": {
+                               "^[a-zA-Z0-9-\\.]+$": {
+                                       "type": "object",
+                                       "anyOf": [
+                                               {
+                                                       "description": "A ResourceLoaderFileModule definition",
+                                                       "additionalProperties": false,
+                                                       "properties": {
+                                                               "localBasePath": {
+                                                                       "type": "string",
+                                                                       "description": "Base path to prepend to all local paths in $options. Defaults to $IP"
+                                                               },
+                                                               "remoteBasePath": {
+                                                                       "type": "string",
+                                                                       "description": "Base path to prepend to all remote paths in $options. Defaults to $wgScriptPath"
+                                                               },
+                                                               "remoteExtPath": {
+                                                                       "type": "string",
+                                                                       "description": "Equivalent of remoteBasePath, but relative to $wgExtensionAssetsPath"
+                                                               },
+                                                               "skipFunction": {
+                                                                       "type": "string",
+                                                                       "description": "Path to a file containing a JavaScript \"skip function\", if desired."
+                                                               },
+                                                               "scripts": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Scripts to always include (array of file paths)",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "languageScripts": {
+                                                                       "type": "object",
+                                                                       "description": "Scripts to include in specific language contexts (mapping of language code to file path(s))",
+                                                                       "patternProperties": {
+                                                                               "^[a-zA-Z0-9-]{2,}$": {
+                                                                                       "type": [
+                                                                                               "string",
+                                                                                               "array"
+                                                                                       ],
+                                                                                       "items": {
+                                                                                               "type": "string"
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                               },
+                                                               "skinScripts": {
+                                                                       "type": "object",
+                                                                       "description": "Scripts to include in specific skin contexts (mapping of skin name to script(s)",
+                                                                       "patternProperties": {
+                                                                               ".+": {
+                                                                                       "type": [
+                                                                                               "string",
+                                                                                               "array"
+                                                                                       ],
+                                                                                       "items": {
+                                                                                               "type": "string"
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                               },
+                                                               "debugScripts": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Scripts to include in debug contexts",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "loaderScripts": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Scripts to include in the startup module",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "dependencies": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Modules which must be loaded before this module",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "styles": {
+                                                                       "type": ["string", "array", "object"],
+                                                                       "description": "Styles to always load",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "skinStyles": {
+                                                                       "type": "object",
+                                                                       "description": "Styles to include in specific skin contexts (mapping of skin name to style(s))",
+                                                                       "patternProperties": {
+                                                                               ".+": {
+                                                                                       "type": [
+                                                                                               "string",
+                                                                                               "array"
+                                                                                       ],
+                                                                                       "items": {
+                                                                                               "type": "string"
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                               },
+                                                               "messages": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Messages to always load",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "group": {
+                                                                       "type": "string",
+                                                                       "description": "Group which this module should be loaded together with"
+                                                               },
+                                                               "deprecated": {
+                                                                       "type": ["object", "boolean"],
+                                                                       "description": "Whether the module is deprecated and usage is discouraged. Either a boolean or an object with key message can be used to customise deprecation message."
+                                                               },
+                                                               "position": {
+                                                                       "type": "string",
+                                                                       "description": "Position on the page to load this module at",
+                                                                       "enum": [
+                                                                               "bottom",
+                                                                               "top"
+                                                                       ]
+                                                               },
+                                                               "templates": {
+                                                                       "type": ["object", "array"],
+                                                                       "description": "Templates to be loaded for client-side usage"
+                                                               },
+                                                               "targets": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "ResourceLoader target the module can run on",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               }
+                                                       }
+                                               },
+                                               {
+                                                       "description": "A ResourceLoaderWikiModule definition",
+                                                       "additionalProperties": false,
+                                                       "properties": {
+                                                               "class": {
+                                                                       "enum": ["ResourceLoaderWikiModule"]
+                                                               },
+                                                               "group": {
+                                                                       "type": "string",
+                                                                       "description": "Group which this module should be loaded together with"
+                                                               },
+                                                               "position": {
+                                                                       "type": "string",
+                                                                       "description": "Position on the page to load this module at",
+                                                                       "enum": [
+                                                                               "bottom",
+                                                                               "top"
+                                                                       ]
+                                                               },
+                                                               "targets": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "ResourceLoader target the module can run on",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "scripts": {
+                                                                       "type": "array",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "styles": {
+                                                                       "type": "array",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               }
+                                                       }
+                                               },
+                                               {
+                                                       "description": "A ResourceLoaderImageModule definition",
+                                                       "additionalProperties": false,
+                                                       "properties": {
+                                                               "class": {
+                                                                       "enum": ["ResourceLoaderImageModule"]
+                                                               },
+                                                               "data": {
+                                                                       "type": "string"
+                                                               },
+                                                               "prefix": {
+                                                                       "type": "string"
+                                                               },
+                                                               "selector": {
+                                                                       "type": "string"
+                                                               },
+                                                               "selectorWithoutVariant": {
+                                                                       "type": "string"
+                                                               },
+                                                               "selectorWithVariant": {
+                                                                       "type": "string"
+                                                               },
+                                                               "variants": {
+                                                                       "type": "object"
+                                                               },
+                                                               "images": {
+                                                                       "type": "object"
+                                                               },
+                                                               "position": {
+                                                                       "enum": [
+                                                                               "top",
+                                                                               "bottom"
+                                                                       ]
+                                                               }
+                                                       }
+                                               },
+                                               {
+                                                       "description": "An arbitrary ResourceLoaderModule definition",
+                                                       "properties": {
+                                                               "class": {
+                                                                       "type": "string",
+                                                                       "pattern": "^((?!ResourceLoader(File|Image)Module).)*$"
+                                                               }
+                                                       },
+                                                       "required": ["class"]
+                                               }
+                                       ]
+                               }
+                       }
+               },
+               "ResourceModuleSkinStyles": {
+                       "type": "object",
+                       "description": "ResourceLoader modules for custom skin styles"
+               },
+               "ResourceLoaderSources": {
+                       "type": "object",
+                       "description": "ResourceLoader sources to register"
+               },
+               "ResourceLoaderLESSVars": {
+                       "type": "object",
+                       "description": "ResourceLoader LESS variables"
+               },
+               "ConfigRegistry": {
+                       "type": "object",
+                       "description": "Registry of factory functions to create Config objects"
+               },
+               "SessionProviders": {
+                       "type": "object",
+                       "description": "Session providers"
+               },
+               "AuthManagerAutoConfig": {
+                       "type": "object",
+                       "description": "AuthManager auto-configuration",
+                       "additionalProperties": false,
+                       "properties": {
+                               "preauth": {
+                                       "type": "object",
+                                       "description": "Pre-authentication providers"
+                               },
+                               "primaryauth": {
+                                       "type": "object",
+                                       "description": "Primary authentication providers"
+                               },
+                               "secondaryauth": {
+                                       "type": "object",
+                                       "description": "Secondary authentication providers"
+                               }
+                       }
+               },
+               "CentralIdLookupProviders": {
+                       "type": "object",
+                       "description": "Central ID lookup providers"
+               },
+               "namespaces": {
+                       "type": "array",
+                       "description": "Method to add extra namespaces",
+                       "items": {
+                               "type": "object",
+                               "properties": {
+                                       "id": {
+                                               "type": "integer"
+                                       },
+                                       "constant": {
+                                               "type": "string"
+                                       },
+                                       "name": {
+                                               "type": "string"
+                                       },
+                                       "gender": {
+                                               "type": "object",
+                                               "properties": {
+                                                       "male": {
+                                                               "type": "string"
+                                                       },
+                                                       "female": {
+                                                               "type": "string"
+                                                       }
+                                               }
+                                       },
+                                       "subpages": {
+                                               "type": "boolean",
+                                               "default": false
+                                       },
+                                       "content": {
+                                               "type": "boolean",
+                                               "default": false
+                                       },
+                                       "defaultcontentmodel": {
+                                               "type": "string"
+                                       },
+                                       "protection": {
+                                               "type": ["string", "array"],
+                                               "description": "Userright(s) required to edit in this namespace"
+                                       },
+                                       "capitallinkoverride": {
+                                               "type": "boolean",
+                                               "description": "Set $wgCapitalLinks on a per-namespace basis"
+                                       },
+                                       "conditional": {
+                                               "type": "boolean",
+                                               "description": "Whether the namespace is conditional upon configuration and should not be registered (requires separate registration via a hook)",
+                                               "default": false
+                                       }
+                               },
+                               "required": ["id", "constant", "name"]
+                       }
+               },
+               "TrackingCategories": {
+                       "type": "array",
+                       "description": "Tracking category message keys",
+                       "items": {
+                               "type": "string"
+                       }
+               },
+               "DefaultUserOptions": {
+                       "type": "object",
+                       "description": "Default values of user options"
+               },
+               "HiddenPrefs": {
+                       "type": "array",
+                       "description": "Preferences users cannot set",
+                       "items": {
+                               "type": "string"
+                       }
+               },
+               "GroupPermissions": {
+                       "type": "object",
+                       "description": "Default permissions to give to user groups",
+                       "patternProperties": {
+                               "^[a-z]+$": {
+                                       "type": "object",
+                                       "patternProperties": {
+                                               "^[a-z]+$": {
+                                                       "type": "boolean"
+                                               }
+                                       }
+                               }
+                       }
+               },
+               "RevokePermissions": {
+                       "type": "object",
+                       "description": "Default permissions to revoke from user groups",
+                       "patternProperties": {
+                               "^[a-z]+$": {
+                                       "type": "object",
+                                       "patternProperties": {
+                                               "^[a-z]+$": {
+                                                       "type": "boolean"
+                                               }
+                                       }
+                               }
+                       }
+               },
+               "GrantPermissions": {
+                       "type": "object",
+                       "description": "Map of permissions granted to authorized consumers to their bundles, called 'grants'",
+                       "patternProperties": {
+                               "^[a-z]+$": {
+                                       "type": "object",
+                                       "patternProperties": {
+                                               "^[a-z]+$": {
+                                                       "type": "boolean"
+                                               }
+                                       }
+                               }
+                       }
+               },
+               "GrantPermissionGroups": {
+                       "type": "object",
+                       "description": "Map of grants to their UI grouping",
+                       "patternProperties": {
+                               "^[a-z]+$": {
+                                       "type": "string"
+                               }
+                       }
+               },
+               "ImplicitGroups": {
+                       "type": "array",
+                       "description": "Implicit groups"
+               },
+               "GroupsAddToSelf": {
+                       "type": "object",
+                       "description": "Groups a user can add to themselves"
+               },
+               "GroupsRemoveFromSelf": {
+                       "type": "object",
+                       "description": "Groups a user can remove from themselves"
+               },
+               "AddGroups": {
+                       "type": "object",
+                       "description": "Groups a user can add to users"
+               },
+               "RemoveGroups": {
+                       "type": "object",
+                       "description": "Groups a user can remove from users"
+               },
+               "AvailableRights": {
+                       "type": "array",
+                       "description": "User rights added by the extension",
+                       "items": {
+                               "type": "string"
+                       }
+               },
+               "ContentHandlers": {
+                       "type": "object",
+                       "description": "Mapping of model ID to class name",
+                       "patternProperties": {
+                               "^[A-Za-z]+$": {
+                                       "type": "string"
+                               }
+                       }
+               },
+               "RateLimits": {
+                       "type": "object",
+                       "description": "Rate limits"
+               },
+               "RecentChangesFlags": {
+                       "type": "object",
+                       "description": "Flags (letter symbols) shown on RecentChanges pages"
+               },
+               "MediaHandlers": {
+                       "type": "object",
+                       "description": "Plugins for media file type handling. Each entry in the array maps a MIME type to a PHP class name."
+               },
+               "ExtensionFunctions": {
+                       "type": [
+                               "array",
+                               "string"
+                       ],
+                       "description": "Function to call after setup has finished",
+                       "items": {
+                               "type": "string"
+                       }
+               },
+               "ExtensionMessagesFiles": {
+                       "type": "object",
+                       "description": "File paths containing PHP internationalization data"
+               },
+               "MessagesDirs": {
+                       "type": "object",
+                       "description": "Directory paths containing JSON internationalization data"
+               },
+               "ExtensionEntryPointListFiles": {
+                       "type": "object"
+               },
+               "SpecialPages": {
+                       "type": "object",
+                       "description": "SpecialPages implemented in this extension (mapping of page name to class name)"
+               },
+               "AutoloadClasses": {
+                       "type": "object"
+               },
+               "Hooks": {
+                       "type": [ "string", "object" ],
+                       "description": "Hooks this extension uses (mapping of hook name to callback)"
+               },
+               "JobClasses": {
+                       "type": "object",
+                       "description": "Job types this extension implements (mapping of job type to class name)"
+               },
+               "LogTypes": {
+                       "type": "array",
+                       "description": "List of new log types this extension uses"
+               },
+               "LogRestrictions": {
+                       "type": "object"
+               },
+               "FilterLogTypes": {
+                       "type": "object"
+               },
+               "ActionFilteredLogs": {
+                       "type": "object",
+                       "description": "List of log types which can be filtered by log actions",
+                       "patternProperties": {
+                               "^[a-z-]+$": {
+                                       "type": "object",
+                                       "patternProperties": {
+                                               "^[a-z-]+$": {
+                                                       "type": "array",
+                                                       "items": {
+                                                               "type": "string"
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               },
+               "LogNames": {
+                       "type": "object"
+               },
+               "LogHeaders": {
+                       "type": "object"
+               },
+               "LogActions": {
+                       "type": "object"
+               },
+               "LogActionsHandlers": {
+                       "type": "object"
+               },
+               "Actions": {
+                       "type": "object"
+               },
+               "APIModules": {
+                       "type": "object"
+               },
+               "APIFormatModules": {
+                       "type": "object"
+               },
+               "APIMetaModules": {
+                       "type": "object"
+               },
+               "APIPropModules": {
+                       "type": "object"
+               },
+               "APIListModules": {
+                       "type": "object"
+               },
+               "ValidSkinNames": {
+                       "type": "object"
+               },
+               "FeedClasses": {
+                       "type": "object",
+                       "description": "Available feeds objects"
+               },
+               "SkinOOUIThemes": {
+                       "type": "object"
+               },
+               "callback": {
+                       "type": [
+                               "array",
+                               "string"
+                       ],
+                       "description": "A function to be called right after MediaWiki processes this file"
+               },
+               "config": {
+                       "type": "object",
+                       "description": "Configuration options for this extension",
+                       "properties": {
+                               "_prefix": {
+                                       "type": "string",
+                                       "default": "wg",
+                                       "description": "Prefix to put in front of configuration settings when exporting them to $GLOBALS"
+                               }
+                       },
+                       "patternProperties": {
+                               "^[a-zA-Z_\u007f-\u00ff][a-zA-Z0-9_\u007f-\u00ff]*$": {
+                                       "properties": {
+                                               "_merge_strategy": {
+                                                       "type": "string",
+                                                       "enum": [
+                                                               "array_merge_recursive",
+                                                               "array_plus_2d",
+                                                               "array_plus",
+                                                               "array_merge"
+                                                       ],
+                                                       "default": "array_merge"
+                                               }
+                                       }
+                               }
+                       }
+               },
+               "ParserTestFiles": {
+                       "type": "array",
+                       "description": "Parser test suite files to be run by parserTests.php when no specific filename is passed to it"
+               },
+               "load_composer_autoloader": {
+                       "type": "boolean",
+                       "description": "Load the composer autoloader for this extension, if one is present"
+               }
+       }
+}
index f9f8333..a7fb873 100644 (file)
@@ -297,16 +297,6 @@ After a user account is created.
 $user: the User object that was created. (Parameter added in 1.7)
 $byEmail: true when account was created "by email" (added in 1.12)
 
-'AddNewAccountApiForm': Allow modifying internal login form when creating an
-account via API.
-$apiModule: the ApiCreateAccount module calling
-$loginForm: the LoginForm used
-
-'AddNewAccountApiResult': Modify API output when creating a new account via API.
-$apiModule: the ApiCreateAccount module calling
-$loginForm: the LoginForm used
-&$result: associative array for API result data
-
 'AfterBuildFeedLinks': Executed in OutputPage.php after all feed links (atom, rss,...)
 are created. Can be used to omit specific feeds from being outputted. You must not use
 this hook to add feeds, use OutputPage::addFeedLink() instead.
@@ -606,8 +596,9 @@ $outputPage: OutputPage that can be used to append the output.
 &$user: the user that deleted the article
 $reason: the reason the article was deleted
 $id: id of the article that was deleted
-$content: the Content of the deleted page
+$content: the Content of the deleted page (or null, when deleting a broken page)
 $logEntry: the ManualLogEntry used to record the deletion
+$archivedRevisionCount: the number of revisions archived during the deletion
 
 'ArticleEditUpdateNewTalk': Before updating user_newtalk when a user talk page
 was changed.
@@ -683,6 +674,10 @@ $oldPageID: the page ID of the revision when archived (may be null)
 revisions of an article.
 $title: Title object of the article
 $ids: Ids to set the visibility for
+$visibilityChangeMap: Map of revision id to oldBits and newBits.  This array can be
+  examined to determine exactly what visibility bits have changed for each
+  revision.  This array is of the form
+  [id => ['oldBits' => $oldBits, 'newBits' => $newBits], ... ]
 
 'ArticleRollbackComplete': After an article rollback is completed.
 $wikiPage: the WikiPage that was edited
@@ -899,6 +894,7 @@ $image: File
 'BlockIpComplete': After an IP address or user is blocked.
 $block: the Block object that was saved
 $user: the user who did the block (not the one being blocked)
+$priorBlock: the Block object for the prior block or null if there was none
 
 'BookInformation': Before information output on Special:Booksources.
 $isbn: ISBN to show information for
@@ -1098,6 +1094,9 @@ $row: the DB row for this line
 $id: User identifier
 $title: User page title
 &$tools: Array of tool links
+$specialPage: SpecialPage instance for context and services. Can be either
+  SpecialContributions or DeletedContributionsPage. Extensions should type
+  hint against a generic SpecialPage though.
 
 'ConvertContent': Called by AbstractContent::convert when a conversion to
 another content model is requested.
@@ -1197,7 +1196,7 @@ $out: OutputPage object
 $parserOutput: ParserOutput object
 $wikiPage: WikiPage object
 
-'DifferenceEngineRenderRevisionShowFinalPatrolLink': An extension can hook into this hook
+DifferenceEngineRenderRevisionShowFinalPatrolLink': An extension can hook into this hook
 point and return false to not show the final "mark as patrolled" link on the bottom
 of a page.
 This hook has no arguments.
@@ -1938,8 +1937,8 @@ LinkRenderer, before processing starts.  Return false to skip default
 processing and return $ret.
 $linkRenderer: the LinkRenderer object
 $target: the LinkTarget that the link is pointing to
-&$html: the contents that the <a> tag should have (raw HTML); null means
-  "default".
+&$text: the contents that the <a> tag should have; either a plain, unescaped
+  string or a HtmlArmor object; null means "default".
 &$customAttribs: the HTML attributes that the <a> tag should have, in
   associative array form, with keys and values unescaped.  Should be merged
   with default values, with a value of false meaning to suppress the
@@ -1956,7 +1955,8 @@ return false, $ret will be returned.
 $linkRenderer: the LinkRenderer object
 $target: the LinkTarget object that the link is pointing to
 $isKnown: boolean indicating whether the page is known or not
-&$html: the final (raw HTML) contents of the <a> tag, after processing.
+&$text: the contents that the <a> tag should have; either a plain, unescaped
+  string or a HtmlArmor object.
 &$attribs: the final HTML attributes of the <a> tag, after processing, in
   associative array form.
 &$ret: the value to return if your hook returns false.
@@ -2060,13 +2060,6 @@ $e: The exception (in case of a plain old PHP error, a wrapping ErrorException)
 $suppressed: true if the error was suppressed via
   error_reporting()/wfSuppressWarnings()
 
-'LoginAuthenticateAudit': A login attempt for a valid user account either
-succeeded or failed. No return data is accepted; this hook is for auditing only.
-$user: the User object being authenticated against
-$password: the password being submitted and found wanting
-$retval: a LoginForm class constant with authenticateUserData() return
-  value (SUCCESS, WRONG_PASS, etc.).
-
 'LoginFormValidErrorMessages': Called in LoginForm when a function gets valid
 error messages. Allows to add additional error messages (except messages already
 in LoginForm::$validErrorMessages).
@@ -2434,24 +2427,12 @@ cache or return false to not use it.
 &$parser: Parser object
 &$varCache: variable cache (array)
 
-'ParserLimitReport': DEPRECATED! Use ParserLimitReportPrepare and
-ParserLimitReportFormat instead.
+'ParserLimitReport': DEPRECATED! Use ParserLimitReportPrepare instead.
 Called at the end of Parser:parse() when the parser will
 include comments about size of the text parsed.
 $parser: Parser object
 &$limitReport: text that will be included (without comment tags)
 
-'ParserLimitReportFormat': Called for each row in the parser limit report that
-needs formatting. If nothing handles this hook, the default is to use "$key" to
-get the label, and "$key-value" or "$key-value-text"/"$key-value-html" to
-format the value.
-$key: Key for the limit report item (string)
-&$value: Value of the limit report item
-&$report: String onto which to append the data
-$isHTML: If true, $report is an HTML table with two columns; if false, it's
-  text intended for display in a monospaced font.
-$localize: If false, $report should be output in English.
-
 'ParserLimitReportPrepare': Called at the end of Parser:parse() when the parser
 will include comments about size of the text parsed. Hooks should use
 $output->setLimitReportData() to populate data. Functions for this hook should
@@ -2706,6 +2687,18 @@ search results.
 $title: Current Title object being displayed in search results.
 &$id: Revision ID (default is false, for latest)
 
+'SearchIndexFields': Add fields to search index mapping.
+&$fields: Array of fields, all implement SearchIndexField
+$engine: SearchEngine instance for which mapping is being built.
+
+'SearchDataForIndex': Add data to search document. Allows to add any data to
+the field map used to index the document.
+&$fields: Array of name => value pairs for fields
+$handler: ContentHandler for the content being indexed
+$page: WikiPage that is being indexed
+$output: ParserOutput that is produced from the page
+$engine: SearchEngine for which the indexing is intended
+
 'SecondaryDataUpdates': Allows modification of the list of DataUpdates to
 perform when page content is modified. Currently called by
 AbstractContent::getSecondaryDataUpdates.
@@ -3364,6 +3357,18 @@ added to the descriptor
 &$radio: Boolean, if source type should be shown as radio button
 $selectedSourceType: The selected source type
 
+'UploadStashFile': Before a file is stashed (uploaded to stash).
+Note that code which has not been updated for MediaWiki 1.28 may not call this
+hook. If your extension absolutely, positively must prevent some files from
+being uploaded, use UploadVerifyFile or UploadVerifyUpload.
+$upload: (object) An instance of UploadBase, with all info about the upload
+$user: (object) An instance of User, the user uploading this file
+$props: (array) File properties, as returned by FSFile::getPropsFromPath()
+&$error: output: If the file stashing should be prevented, set this to the reason
+  in the form of array( messagename, param1, param2, ... ) or a MessageSpecifier
+  instance (you might want to use ApiMessage to provide machine-readable details
+  for the API).
+
 'UploadVerification': DEPRECATED! Use UploadVerifyFile instead.
 Additional chances to reject an uploaded file.
 $saveName: (string) destination file name
@@ -3377,9 +3382,23 @@ in most cases over UploadVerification.
 $upload: (object) an instance of UploadBase, with all info about the upload
 $mime: (string) The uploaded file's MIME type, as detected by MediaWiki.
   Handlers will typically only apply for specific MIME types.
-&$error: (object) output: true if the file is valid. Otherwise, an indexed array
-  representing the problem with the file, where the first element is the message
-  key and the remaining elements are used as parameters to the message.
+&$error: (object) output: true if the file is valid. Otherwise, set this to the reason
+  in the form of array( messagename, param1, param2, ... ) or a MessageSpecifier
+  instance (you might want to use ApiMessage to provide machine-readable details
+  for the API).
+
+'UploadVerifyUpload': Upload verification, based on both file properties like
+MIME type (same as UploadVerifyFile) and the information entered by the user
+(upload comment, file page contents etc.).
+$upload: (object) An instance of UploadBase, with all info about the upload
+$user: (object) An instance of User, the user uploading this file
+$props: (array) File properties, as returned by FSFile::getPropsFromPath()
+$comment: (string) Upload log comment (also used as edit summary)
+$pageText: (string) File description page text (only used for new uploads)
+&$error: output: If the file upload should be prevented, set this to the reason
+  in the form of array( messagename, param1, param2, ... ) or a MessageSpecifier
+  instance (you might want to use ApiMessage to provide machine-readable details
+  for the API).
 
 'UserIsBot': when determining whether a user is a bot account
 $user: the user
@@ -3527,6 +3546,9 @@ $user: User object for the logged-in user
 For functionality that needs to run after any login (API or web) use UserLoggedIn.
 &$user: the user object that was created on login
 &$inject_html: Any HTML to inject after the "logged in" message.
+$direct: (bool) The hook is called directly after a successful login. This will only happen once
+  per login. A UserLoginComplete call with direct=false can happen when the user visits the login
+  page while already logged in.
 
 'UserLoginForm': DEPRECATED! Create an AuthenticationProvider instead.
 Manipulate the login form.
@@ -3628,6 +3650,16 @@ $userId: User id of the current user
 $userText: User name of the current user
 &$items: Array of user tool links as HTML fragments
 
+'UsersPagerDoBatchLookups': Called in UsersPager::doBatchLookups() to give
+extensions providing user group data from an alternate source a chance to add
+their data into the cache array so that things like global user groups are
+displayed correctly in Special:ListUsers.
+$dbr: Read-only database handle
+$userIds: Array of user IDs whose groups we should look up
+&$cache: Array of user ID -> internal user group name (e.g. 'sysop') mappings
+&$groups: Array of group name -> bool true mappings for members of a given user
+group
+
 'ValidateExtendedMetadataCache': Called to validate the cached metadata in
 FormatMetadata::getExtendedMeta (return false means cache will be
 invalidated and GetExtendedMetadata hook called again).
@@ -3700,7 +3732,8 @@ a page is deleted. Called in WikiPage::getDeletionUpdates(). Note that updates
 specific to a content model should be provided by the respective Content's
 getDeletionUpdates() method.
 $page: the WikiPage
-$content: the Content to generate updates for
+$content: the Content to generate updates for (or null, if the Content could not be loaded
+due to an error)
 &$updates: the array of DataUpdate objects. Hook function may want to add to it.
 
 'XmlDumpWriterOpenPage': Called at the end of XmlDumpWriter::openPage, to allow
index dadfb47..392ad1a 100644 (file)
@@ -5,7 +5,7 @@ kss: kssnodecheck
 # KSS style guide
        $(eval KSS_RL_TMP := $(shell mktemp /tmp/tmp.XXXXXXXXXX))
        $(eval MODULE_STR := $(shell paste -sd "|" styleGuideModules.txt))
-# See OutputPage::makeResourceLoaderLink.
+# See ResourceLoaderClientHtml::makeLoad.
        @curl -sG "${MEDIAWIKI_LOAD_URL}?modules=${MODULE_STR}&only=styles" > $(KSS_RL_TMP)
        @node_modules/.bin/kss-node ../../resources/src/mediawiki.ui static/ --css $(KSS_RL_TMP) -t styleguide-template
        @rm $(KSS_RL_TMP)
diff --git a/docs/uidesign/mediawiki.action.history.diff.html b/docs/uidesign/mediawiki.action.history.diff.html
deleted file mode 100644 (file)
index 615558f..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-<!DOCTYPE html>
-<html lang="en" dir="ltr">
-<head>
-       <meta charset="utf-8">
-       <link rel="stylesheet" href="../../resources/src/mediawiki.action/mediawiki.action.history.diff.css">
-       <link rel="stylesheet" media="print" href="../../resources/src/mediawiki.action/mediawiki.action.history.diff.print.css">
-</head>
-<body>
-
-<p>This show various styles for our diff action. Style sheet: <code><a href="../../resources/src/mediawiki.action/mediawiki.action.history.diff.css">resources/src/mediawiki.action/mediawiki.action.history.diff.css</a></code>.</p>
-<p>This file might help us fix our diff colors which have been a recurring issues among the community for a loooong time.</p>
-<p>Try it out in print mode, too. Style sheet: <code><a href="../../resources/src/mediawiki.action/mediawiki.action.history.diff.print.css">resources/src/mediawiki.action/mediawiki.action.history.diff.print.css</a></code>.</p>
-
-<p>Practical example copied from MediaWiki's HTML output:</p>
-
-<table class="diff diff-contentalign-left">
-       <colgroup><col class="diff-marker">
-       <col class="diff-content">
-       <col class="diff-marker">
-       <col class="diff-content">
-       </colgroup>
-<tbody>
-<tr>
-       <td class="diff-marker">−</td>
-       <td class="diff-deletedline"><div>Lorem ipsum dolor sit amet<del class="diffchange diffchange-inline">, consectetur adipisicing elit</del>, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div></td>
-       <td class="diff-marker">+</td>
-       <td class="diff-addedline"><div>Lorem ipsum dolor sit amet, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div></td>
-</tr>
-<tr>
-       <td class="diff-marker">−</td>
-       <td class="diff-deletedline"></td>
-       <td colspan="2" class="diff-empty">&nbsp;</td>
-</tr>
-<tr>
-       <td class="diff-marker">−</td>
-       <td class="diff-deletedline"><div>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div></td>
-       <td colspan="2" class="diff-empty">&nbsp;</td>
-</tr>
-<tr>
-       <td class="diff-marker">&nbsp;</td>
-       <td class="diff-context"></td>
-       <td class="diff-marker">&nbsp;</td>
-       <td class="diff-context"></td>
-</tr>
-<tr>
-       <td class="diff-marker">&nbsp;</td>
-       <td class="diff-context"><div>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</div></td>
-       <td class="diff-marker">&nbsp;</td>
-       <td class="diff-context"><div>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</div></td>
-</tr>
-<tr>
-       <td class="diff-marker">&nbsp;</td>
-       <td class="diff-context"></td>
-       <td class="diff-marker">&nbsp;</td>
-       <td class="diff-context"></td>
-</tr>
-<tr>
-       <td class="diff-marker">−</td>
-       <td class="diff-deletedline"><div>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim<del class="diffchange diffchange-inline"> id est laborum</del>.</div></td>
-       <td class="diff-marker">+</td>
-       <td class="diff-addedline"><div>Excepteur sint occaecat cupidatat non proident, sunt<ins class="diffchange diffchange-inline"> reprehenderit in voluptate</ins> in culpa qui officia deserunt mollit anim.</div></td>
-</tr>
-<tr>
-       <td colspan="2" class="diff-empty">&nbsp;</td>
-       <td class="diff-marker">+</td>
-       <td class="diff-addedline"></td>
-</tr>
-<tr>
-       <td colspan="2" class="diff-empty">&nbsp;</td>
-       <td class="diff-marker">+</td>
-       <td class="diff-addedline"><div>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div></td>
-</tr>
-</tbody></table>
-
-<p>Below are some basic lines being applied one or two classes. Mainly for debugging purposes.</p>
-
-<table class="diff">
-       <tr><th>Diff</th></tr>
-
-       <tr><td class="diff-addedline"><code>diff-addedline</code>: added line</td></tr>
-       <tr><td class="diff-deletedline"><code>diff-deletedline</code>: deleted line</td></tr>
-       <tr><td class="diff-context"><code>diff-context</code>: context</td></tr>
-
-       <tr><th>Same as above with a <code>&lt;ins&gt;</code> or <code>&lt;del&gt;</code> child element having the <code>diffchange</code> class:</th></tr>
-
-       <tr><td class="diffchange">Diffchange</td></tr>
-       <tr><td class="diff-addedline"><ins class="diffchange">Added line + diffchange</ins></td></tr>
-       <tr><td class="diff-deletedline"><del class="diffchange">Deleted line + diffchange</del></td></tr>
-</table>
-
-</body>
-</html>
diff --git a/docs/uidesign/mediawiki.diff.html b/docs/uidesign/mediawiki.diff.html
new file mode 100644 (file)
index 0000000..0372595
--- /dev/null
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+<head>
+       <meta charset="utf-8">
+       <link rel="stylesheet" href="../../resources/src/mediawiki/mediawiki.diff.styles.css">
+       <link rel="stylesheet" media="print" href="../../resources/src/mediawiki/mediawiki.diff.styles.print.css">
+</head>
+<body>
+
+<p>This show various styles for our diff action. Style sheet: <code><a href="../../resources/src/mediawiki/mediawiki.diff.styles.css">resources/src/mediawiki/mediawiki.diff.styles.css</a></code>.</p>
+<p>This file might help us fix our diff colors which have been a recurring issues among the community for a loooong time.</p>
+<p>Try it out in print mode, too. Style sheet: <code><a href="../../resources/src/mediawiki/mediawiki.diff.styles.print.css">resources/src/mediawiki/mediawiki.diff.styles.print.css</a></code>.</p>
+
+<p>Practical example copied from MediaWiki's HTML output:</p>
+
+<table class="diff diff-contentalign-left">
+       <colgroup><col class="diff-marker">
+       <col class="diff-content">
+       <col class="diff-marker">
+       <col class="diff-content">
+       </colgroup>
+<tbody>
+<tr>
+       <td class="diff-marker">−</td>
+       <td class="diff-deletedline"><div>Lorem ipsum dolor sit amet<del class="diffchange diffchange-inline">, consectetur adipisicing elit</del>, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div></td>
+       <td class="diff-marker">+</td>
+       <td class="diff-addedline"><div>Lorem ipsum dolor sit amet, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div></td>
+</tr>
+<tr>
+       <td class="diff-marker">−</td>
+       <td class="diff-deletedline"></td>
+       <td colspan="2" class="diff-empty">&nbsp;</td>
+</tr>
+<tr>
+       <td class="diff-marker">−</td>
+       <td class="diff-deletedline"><div>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div></td>
+       <td colspan="2" class="diff-empty">&nbsp;</td>
+</tr>
+<tr>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"></td>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"></td>
+</tr>
+<tr>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"><div>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</div></td>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"><div>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</div></td>
+</tr>
+<tr>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"></td>
+       <td class="diff-marker">&nbsp;</td>
+       <td class="diff-context"></td>
+</tr>
+<tr>
+       <td class="diff-marker">−</td>
+       <td class="diff-deletedline"><div>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim<del class="diffchange diffchange-inline"> id est laborum</del>.</div></td>
+       <td class="diff-marker">+</td>
+       <td class="diff-addedline"><div>Excepteur sint occaecat cupidatat non proident, sunt<ins class="diffchange diffchange-inline"> reprehenderit in voluptate</ins> in culpa qui officia deserunt mollit anim.</div></td>
+</tr>
+<tr>
+       <td colspan="2" class="diff-empty">&nbsp;</td>
+       <td class="diff-marker">+</td>
+       <td class="diff-addedline"></td>
+</tr>
+<tr>
+       <td colspan="2" class="diff-empty">&nbsp;</td>
+       <td class="diff-marker">+</td>
+       <td class="diff-addedline"><div>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div></td>
+</tr>
+</tbody></table>
+
+<p>Below are some basic lines being applied one or two classes. Mainly for debugging purposes.</p>
+
+<table class="diff">
+       <tr><th>Diff</th></tr>
+
+       <tr><td class="diff-addedline"><code>diff-addedline</code>: added line</td></tr>
+       <tr><td class="diff-deletedline"><code>diff-deletedline</code>: deleted line</td></tr>
+       <tr><td class="diff-context"><code>diff-context</code>: context</td></tr>
+
+       <tr><th>Same as above with a <code>&lt;ins&gt;</code> or <code>&lt;del&gt;</code> child element having the <code>diffchange</code> class:</th></tr>
+
+       <tr><td class="diffchange">Diffchange</td></tr>
+       <tr><td class="diff-addedline"><ins class="diffchange">Added line + diffchange</ins></td></tr>
+       <tr><td class="diff-deletedline"><del class="diffchange">Deleted line + diffchange</del></td></tr>
+</table>
+
+</body>
+</html>
index 923a19b..5c9bf53 100644 (file)
@@ -1,9 +1,12 @@
+== User Information ==
+
 Extensions are distributed separately. Drop them into this directory and enable
 as per the extension's installation instructions.
 
 You can find a list of extensions and documentation at
-<https://www.mediawiki.org/wiki/Category:Extensions>.
+<https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions>.
 
+== Development Information ==
 
 If you are a developer, you might want to fetch the extension tree in another
 directory and make a symbolic link:
index d636188..fa1609f 100644 (file)
@@ -162,13 +162,21 @@ function wfImageAuthMain() {
                }
        }
 
+       $options = []; // HTTP header options
+       if ( isset( $_SERVER['HTTP_RANGE'] ) ) {
+               $options['range'] = $_SERVER['HTTP_RANGE'];
+       }
+       if ( isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
+               $options['if-modified-since'] = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
+       }
+
        if ( $request->getCheck( 'download' ) ) {
                $headers[] = 'Content-Disposition: attachment';
        }
 
        // Stream the requested file
        wfDebugLog( 'img_auth', "Streaming `" . $filename . "`." );
-       $repo->streamFile( $filename, $headers );
+       $repo->streamFile( $filename, $headers, $options );
 }
 
 /**
index 883b8a3..8dc7d40 100644 (file)
@@ -81,18 +81,6 @@ class AutoLoader {
                require $filename;
        }
 
-       /**
-        * Force a class to be run through the autoloader, helpful for things like
-        * Sanitizer that have define()s outside of their class definition. Of course
-        * this wouldn't be necessary if everything in MediaWiki was class-based. Sigh.
-        *
-        * @param string $class
-        * @return bool Return the results of class_exists() so we know if we were successful
-        */
-       static function loadClass( $class ) {
-               return class_exists( $class );
-       }
-
        /**
         * Method to clear the protected class property $autoloadLocalClassesLower.
         * Used in tests.
index 93df004..19ba0a2 100644 (file)
@@ -145,7 +145,7 @@ class Block {
 
                $this->mReason = $options['reason'];
                $this->mTimestamp = wfTimestamp( TS_MW, $options['timestamp'] );
-               $this->mExpiry = wfGetDB( DB_SLAVE )->decodeExpiry( $options['expiry'] );
+               $this->mExpiry = wfGetDB( DB_REPLICA )->decodeExpiry( $options['expiry'] );
 
                # Boolean settings
                $this->mAuto = (bool)$options['auto'];
@@ -168,7 +168,7 @@ class Block {
         * @return Block|null
         */
        public static function newFromID( $id ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->selectRow(
                        'ipblocks',
                        self::selectFields(),
@@ -242,7 +242,7 @@ class Block {
         * @return bool Whether a relevant block was found
         */
        protected function newLoad( $vagueTarget = null ) {
-               $db = wfGetDB( $this->mFromMaster ? DB_MASTER : DB_SLAVE );
+               $db = wfGetDB( $this->mFromMaster ? DB_MASTER : DB_REPLICA );
 
                if ( $this->type !== null ) {
                        $conds = [
@@ -351,7 +351,7 @@ class Block {
                # range. We know that all blocks must be smaller than $wgBlockCIDRLimit,
                # so we can improve performance by filtering on a LIKE clause
                $chunk = self::getIpFragment( $start );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $like = $dbr->buildLike( $chunk, $dbr->anyString() );
 
                # Fairly hard to make a malicious SQL statement out of hex characters,
@@ -405,7 +405,7 @@ class Block {
                $this->mParentBlockId = $row->ipb_parent_block_id;
 
                // I wish I didn't have to do this
-               $this->mExpiry = wfGetDB( DB_SLAVE )->decodeExpiry( $row->ipb_expiry );
+               $this->mExpiry = wfGetDB( DB_REPLICA )->decodeExpiry( $row->ipb_expiry );
 
                $this->isHardblock( !$row->ipb_anon_only );
                $this->isAutoblocking( $row->ipb_enable_autoblock );
@@ -457,6 +457,7 @@ class Block {
         *      ('id' => block ID, 'autoIds' => array of autoblock IDs)
         */
        public function insert( $dbw = null ) {
+               global $wgBlockDisablesLogin;
                wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" );
 
                if ( $dbw === null ) {
@@ -499,6 +500,13 @@ class Block {
 
                if ( $affected ) {
                        $auto_ipd_ids = $this->doRetroactiveAutoblock();
+
+                       if ( $wgBlockDisablesLogin && $this->target instanceof User ) {
+                               // Change user login token to force them to be logged out.
+                               $this->target->setToken();
+                               $this->target->saveSettings();
+                       }
+
                        return [ 'id' => $this->mId, 'autoIds' => $auto_ipd_ids ];
                }
 
@@ -561,7 +569,7 @@ class Block {
         */
        protected function getDatabaseArray( $db = null ) {
                if ( !$db ) {
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                }
                $expiry = $db->encodeExpiry( $this->mExpiry );
 
@@ -645,7 +653,7 @@ class Block {
                        return;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $options = [ 'ORDER BY' => 'rc_timestamp DESC' ];
                $conds = [ 'rc_user_text' => (string)$block->getTarget() ];
@@ -961,28 +969,40 @@ class Block {
 
        /**
         * Get/set whether the Block prevents a given action
-        * @param string $action
-        * @param bool|null $x
-        * @return bool
+        *
+        * @param string $action Action to check
+        * @param bool|null $x Value for set, or null to just get value
+        * @return bool|null Null for unrecognized rights.
         */
        public function prevents( $action, $x = null ) {
+               global $wgBlockDisablesLogin;
+               $res = null;
                switch ( $action ) {
                        case 'edit':
                                # For now... <evil laugh>
-                               return true;
-
+                               $res = true;
+                               break;
                        case 'createaccount':
-                               return wfSetVar( $this->mCreateAccount, $x );
-
+                               $res = wfSetVar( $this->mCreateAccount, $x );
+                               break;
                        case 'sendemail':
-                               return wfSetVar( $this->mBlockEmail, $x );
-
+                               $res = wfSetVar( $this->mBlockEmail, $x );
+                               break;
                        case 'editownusertalk':
-                               return wfSetVar( $this->mDisableUsertalk, $x );
-
-                       default:
-                               return null;
+                               $res = wfSetVar( $this->mDisableUsertalk, $x );
+                               break;
+                       case 'read':
+                               $res = false;
+                               break;
                }
+               if ( !$res && $wgBlockDisablesLogin ) {
+                       // If a block would disable login, then it should
+                       // prevent any action that all users cannot do
+                       $anon = new User;
+                       $res = $anon->isAllowed( $action ) ? $res : true;
+               }
+
+               return $res;
        }
 
        /**
@@ -1090,7 +1110,7 @@ class Block {
         * @param array $ipChain List of IPs (strings), usually retrieved from the
         *         X-Forwarded-For header of the request
         * @param bool $isAnon Exclude anonymous-only blocks if false
-        * @param bool $fromMaster Whether to query the master or slave database
+        * @param bool $fromMaster Whether to query the master or replica DB
         * @return array Array of Blocks
         * @since 1.22
         */
@@ -1126,7 +1146,7 @@ class Block {
                if ( $fromMaster ) {
                        $db = wfGetDB( DB_MASTER );
                } else {
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                }
                $conds = $db->makeList( $conds, LIST_OR );
                if ( !$isAnon ) {
index 28b566a..1ebd605 100644 (file)
@@ -60,7 +60,7 @@ class Category {
                        return true;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $row = $dbr->selectRow(
                        'category',
                        [ 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ],
@@ -79,6 +79,11 @@ class Category {
                                $this->mSubcats = 0;
                                $this->mFiles = 0;
 
+                               # If the title exists, call refreshCounts to add a row for it.
+                               if ( $this->mTitle->exists() ) {
+                                       DeferredUpdates::addCallableUpdate( [ $this, 'refreshCounts' ] );
+                               }
+
                                return true;
                        } else {
                                return false; # Fail
@@ -259,7 +264,7 @@ class Category {
         */
        public function getMembers( $limit = false, $offset = '' ) {
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $conds = [ 'cl_to' => $this->getName(), 'cl_from = page_id' ];
                $options = [ 'ORDER BY' => 'cl_sortkey' ];
@@ -331,21 +336,35 @@ class Category {
                        [ 'LOCK IN SHARE MODE' ]
                );
 
+               $shouldExist = $result->pages > 0 || $this->getTitle()->exists();
+
                if ( $this->mID ) {
-                       # The category row already exists, so do a plain UPDATE instead
-                       # of INSERT...ON DUPLICATE KEY UPDATE to avoid creating a gap
-                       # in the cat_id sequence. The row may or may not be "affected".
-                       $dbw->update(
-                               'category',
-                               [
-                                       'cat_pages' => $result->pages,
-                                       'cat_subcats' => $result->subcats,
-                                       'cat_files' => $result->files
-                               ],
-                               [ 'cat_title' => $this->mName ],
-                               __METHOD__
-                       );
-               } else {
+                       if ( $shouldExist ) {
+                               # The category row already exists, so do a plain UPDATE instead
+                               # of INSERT...ON DUPLICATE KEY UPDATE to avoid creating a gap
+                               # in the cat_id sequence. The row may or may not be "affected".
+                               $dbw->update(
+                                       'category',
+                                       [
+                                               'cat_pages' => $result->pages,
+                                               'cat_subcats' => $result->subcats,
+                                               'cat_files' => $result->files
+                                       ],
+                                       [ 'cat_title' => $this->mName ],
+                                       __METHOD__
+                               );
+                       } else {
+                               # The category is empty and has no description page, delete it
+                               $dbw->delete(
+                                       'category',
+                                       [ 'cat_title' => $this->mName ],
+                                       __METHOD__
+                               );
+                               $this->mID = false;
+                       }
+               } elseif ( $shouldExist ) {
+                       # The category row doesn't exist but should, so create it. Use
+                       # upsert in case of races.
                        $dbw->upsert(
                                'category',
                                [
@@ -362,6 +381,8 @@ class Category {
                                ],
                                __METHOD__
                        );
+                       // @todo: Should we update $this->mID here? Or not since Category
+                       // objects tend to be short lived enough to not matter?
                }
 
                $dbw->endAtomic( __METHOD__ );
index 3f0528e..4efa2ff 100644 (file)
@@ -33,8 +33,8 @@
  *
  *     $cf = new CategoryFinder;
  *     $cf->seed(
- *         array( 12345 ),
- *         array( 'Category 1', 'Category 2' ),
+ *         [ 12345 ],
+ *         [ 'Category 1', 'Category 2' ],
  *         'AND'
  *     );
  *     $a = $cf->run();
@@ -49,7 +49,7 @@ class CategoryFinder {
        /** @var array Array of DBKEY category names for categories that don't have a page */
        protected $deadend = [];
 
-       /** @var array Array of [ID => array()] */
+       /** @var array Array of [ ID => [] ] */
        protected $parents = [];
 
        /** @var array Array of article/category IDs */
@@ -64,7 +64,7 @@ class CategoryFinder {
        /** @var string "AND" or "OR" */
        protected $mode;
 
-       /** @var IDatabase Read-DB slave */
+       /** @var IDatabase Read-DB replica DB */
        protected $dbr;
 
        /**
@@ -96,7 +96,7 @@ class CategoryFinder {
         * @return array Array of page_ids (those given to seed() that match the conditions)
         */
        public function run() {
-               $this->dbr = wfGetDB( DB_SLAVE );
+               $this->dbr = wfGetDB( DB_REPLICA );
                while ( count( $this->next ) > 0 ) {
                        $this->scanNextLayer();
                }
index 389b077..a8e988f 100644 (file)
@@ -286,7 +286,7 @@ class CategoryViewer extends ContextSource {
        }
 
        function doCategoryQuery() {
-               $dbr = wfGetDB( DB_SLAVE, 'category' );
+               $dbr = wfGetDB( DB_REPLICA, 'category' );
 
                $this->nextPage = [
                        'page' => null,
@@ -407,11 +407,26 @@ class CategoryViewer extends ContextSource {
                return $r;
        }
 
+       /**
+        * Return pretty name which is display name if given and different from prefix text or
+        * the unprefixed page name.
+        *
+        * @return string HTML safe name.
+        */
+       function getPrettyPageNameHtml() {
+               $displayTitle = $this->getOutput()->getPageTitle();
+               if ( $displayTitle === $this->getTitle()->getPrefixedText() ) {
+                       return htmlspecialchars( $this->getTitle()->getText() );
+               } else {
+                       return $displayTitle;
+               }
+       }
+
        /**
         * @return string
         */
        function getPagesSection() {
-               $ti = wfEscapeWikiText( $this->title->getText() );
+               $name = $this->getPrettyPageNameHtml();
                # Don't show articles section if there are none.
                $r = '';
 
@@ -427,7 +442,7 @@ class CategoryViewer extends ContextSource {
 
                if ( $rescnt > 0 ) {
                        $r = "<div id=\"mw-pages\">\n";
-                       $r .= '<h2>' . $this->msg( 'category_header', $ti )->parse() . "</h2>\n";
+                       $r .= '<h2>' . $this->msg( 'category_header' )->rawParams( $name )->parse() . "</h2>\n";
                        $r .= $countmsg;
                        $r .= $this->getSectionPagingLinks( 'page' );
                        $r .= $this->formatList( $this->articles, $this->articles_start_char );
@@ -441,6 +456,7 @@ class CategoryViewer extends ContextSource {
         * @return string
         */
        function getImageSection() {
+               $name = $this->getPrettyPageNameHtml();
                $r = '';
                $rescnt = $this->showGallery ? $this->gallery->count() : count( $this->imgsNoGallery );
                $dbcnt = $this->cat->getFileCount();
@@ -450,10 +466,7 @@ class CategoryViewer extends ContextSource {
                if ( $rescnt > 0 ) {
                        $r .= "<div id=\"mw-category-media\">\n";
                        $r .= '<h2>' .
-                               $this->msg(
-                                       'category-media-header',
-                                       wfEscapeWikiText( $this->title->getText() )
-                               )->text() .
+                               $this->msg( 'category-media-header' )->rawParams( $name )->parse() .
                                "</h2>\n";
                        $r .= $countmsg;
                        $r .= $this->getSectionPagingLinks( 'file' );
@@ -532,17 +545,17 @@ class CategoryViewer extends ContextSource {
        }
 
        /**
-        * Format a list of articles chunked by letter in a three-column
-        * list, ordered vertically.
+        * Format a list of articles chunked by letter in a three-column list, ordered
+        * vertically. This is used for categories with a significant number of pages.
         *
         * TODO: Take the headers into account when creating columns, so they're
         * more visually equal.
         *
         * TODO: shortList and columnList are similar, need merging
         *
-        * @param array $articles
-        * @param string[] $articles_start_char
-        * @return string
+        * @param string[] $articles HTML links to each article
+        * @param string[] $articles_start_char The header characters for each article
+        * @return string HTML to output
         * @private
         */
        static function columnList( $articles, $articles_start_char ) {
@@ -579,10 +592,11 @@ class CategoryViewer extends ContextSource {
        }
 
        /**
-        * Format a list of articles chunked by letter in a bullet list.
-        * @param array $articles
-        * @param string[] $articles_start_char
-        * @return string
+        * Format a list of articles chunked by letter in a bullet list. This is used
+        * for categories with a small number of pages (when columns aren't needed).
+        * @param string[] $articles HTML links to each article
+        * @param string[] $articles_start_char The header characters for each article
+        * @return string HTML to output
         * @private
         */
        static function shortList( $articles, $articles_start_char ) {
index 6d08eec..c7fda14 100644 (file)
@@ -398,9 +398,13 @@ $wgAllowImageMoving = true;
 $wgEnableAsyncUploads = false;
 
 /**
- * These are additional characters that should be replaced with '-' in filenames
+ * Additional characters that are not allowed in filenames. They are replaced with '-' when
+ * uploading. Like $wgLegalTitleChars, this is a regexp character class.
+ *
+ * Slashes and backslashes are disallowed regardless of this setting, but included here for
+ * completeness.
  */
-$wgIllegalFileChars = ":";
+$wgIllegalFileChars = ":\\/\\\\";
 
 /**
  * What directory to place deleted uploads in.
@@ -681,7 +685,7 @@ $wgUseSharedUploads = false;
 /**
  * Full path on the web server where shared uploads can be found
  */
-$wgSharedUploadPath = "http://commons.wikimedia.org/shared/images";
+$wgSharedUploadPath = null;
 
 /**
  * Fetch commons image description pages and display them on the local wiki?
@@ -691,7 +695,7 @@ $wgFetchCommonsDescriptions = false;
 /**
  * Path on the file system where shared uploads can be found.
  */
-$wgSharedUploadDirectory = "/var/www/wiki3/images";
+$wgSharedUploadDirectory = null;
 
 /**
  * DB name with metadata about shared directory.
@@ -941,22 +945,11 @@ $wgTrustedMediaFormats = [
 /**
  * Plugins for media file type handling.
  * Each entry in the array maps a MIME type to a class name
+ *
+ * Core media handlers are listed in MediaHandlerFactory,
+ * and extensions should use extension.json.
  */
-$wgMediaHandlers = [
-       'image/jpeg' => 'JpegHandler',
-       'image/png' => 'PNGHandler',
-       'image/gif' => 'GIFHandler',
-       'image/tiff' => 'TiffHandler',
-       'image/webp' => 'WebPHandler',
-       'image/x-ms-bmp' => 'BmpHandler',
-       'image/x-bmp' => 'BmpHandler',
-       'image/x-xcf' => 'XCFHandler',
-       'image/svg+xml' => 'SvgHandler', // official
-       'image/svg' => 'SvgHandler', // compat
-       'image/vnd.djvu' => 'DjVuHandler', // official
-       'image/x.djvu' => 'DjVuHandler', // compat
-       'image/x-djvu' => 'DjVuHandler', // compat
-];
+$wgMediaHandlers = [];
 
 /**
  * Plugins for page content model handling.
@@ -1444,7 +1437,10 @@ $wgGalleryOptions = [
        'imagesPerRow' => 0, // Default number of images per-row in the gallery. 0 -> Adapt to screensize
        'imageWidth' => 120, // Width of the cells containing images in galleries (in "px")
        'imageHeight' => 120, // Height of the cells containing images in galleries (in "px")
-       'captionLength' => 25, // Length of caption to truncate (in characters)
+       'captionLength' => true, // Deprecated @since 1.28
+                                // Length to truncate filename to in caption when using "showfilename".
+                                // A value of 'true' will truncate the filename to one line using CSS
+                                // and will be the behaviour after deprecation.
        'showBytes' => true, // Show the filesize in bytes in categories
        'mode' => 'traditional',
 ];
@@ -1674,6 +1670,9 @@ $wgEnotifWatchlist = false;
 /**
  * Allow users to enable email notification ("enotif") when someone edits their
  * user talk page.
+ *
+ * The owner of the user talk page must also have the 'enotifusertalkpages' user
+ * preference set to true.
  */
 $wgEnotifUserTalk = false;
 
@@ -1684,9 +1683,17 @@ $wgEnotifUserTalk = false;
 $wgEnotifRevealEditorAddress = false;
 
 /**
- * Send notification mails on minor edits to watchlist pages. This is enabled
- * by default. User talk notifications are affected by this, $wgEnotifUserTalk, and
- * the nominornewtalk user right.
+ * Potentially send notification mails on minor edits to pages. This is enabled
+ * by default.  If this is false, users will never be notified on minor edits.
+ *
+ * If it is true, editors with the 'nominornewtalk' right (typically bots) will still not
+ * trigger notifications for minor edits they make (to any page, not just user talk).
+ *
+ * Finally, if the watcher/recipient has the 'enotifminoredits' user preference set to
+ * false, they will not receive notifications for minor edits.
+ *
+ * User talk notifications are also affected by $wgEnotifMinorEdits, the above settings,
+ * $wgEnotifUserTalk, and the preference described there.
  */
 $wgEnotifMinorEdits = true;
 
@@ -1888,7 +1895,7 @@ $wgSharedSchema = false;
  *   - password:    DB password
  *   - type:        DB type
  *
- *   - load:        Ratio of DB_SLAVE load, must be >=0, the sum of all loads must be >0.
+ *   - load:        Ratio of DB_REPLICA load, must be >=0, the sum of all loads must be >0.
  *                  If this is zero for any given server, no normal query traffic will be
  *                  sent to it. It will be excluded from lag checks in maintenance scripts.
  *                  The only way it can receive traffic is if groupLoads is used.
@@ -1897,7 +1904,7 @@ $wgSharedSchema = false;
  *                  to several groups, the most specific group defined here is used.
  *
  *   - flags:       bit field
- *                  - DBO_DEFAULT -- turns on DBO_TRX only if !$wgCommandLineMode (recommended)
+ *                  - DBO_DEFAULT -- turns on DBO_TRX only if "cliMode" is off (recommended)
  *                  - DBO_DEBUG -- equivalent of $wgDebugDumpSql
  *                  - DBO_TRX -- wrap entire request in a transaction
  *                  - DBO_NOBUFFER -- turn off buffering (not useful in LocalSettings.php)
@@ -1906,8 +1913,11 @@ $wgSharedSchema = false;
  *                  - DBO_COMPRESS -- uses internal compression in database connections,
  *                                    if available
  *
- *   - max lag:     (optional) Maximum replication lag before a slave will taken out of rotation
+ *   - max lag:     (optional) Maximum replication lag before a replica DB goes out of rotation
  *   - is static:   (optional) Set to true if the dataset is static and no replication is used.
+ *   - cliMode:     (optional) Connection handles will not assume that requests are short-lived
+ *                  nor that INSERT..SELECT can be rewritten into a buffered SELECT and INSERT.
+ *                  [Default: uses value of $wgCommandLineMode]
  *
  *   These and any other user-defined properties will be assigned to the mLBInfo member
  *   variable of the Database object.
@@ -1917,15 +1927,15 @@ $wgSharedSchema = false;
  * perhaps in some command-line scripts).
  *
  * The first server listed in this array (with key 0) will be the master. The
- * rest of the servers will be slaves. To prevent writes to your slaves due to
+ * rest of the servers will be replica DBs. To prevent writes to your replica DBs due to
  * accidental misconfiguration or MediaWiki bugs, set read_only=1 on all your
- * slaves in my.cnf. You can set read_only mode at runtime using:
+ * replica DBs in my.cnf. You can set read_only mode at runtime using:
  *
  * @code
  *     SET @@read_only=1;
  * @endcode
  *
- * Since the effect of writing to a slave is so damaging and difficult to clean
+ * Since the effect of writing to a replica DB is so damaging and difficult to clean
  * up, we at Wikimedia set read_only=1 in my.cnf on all our DB servers, even
  * our masters, and then set read_only=0 on masters at runtime.
  */
@@ -2113,7 +2123,7 @@ $wgDefaultExternalStore = false;
  *
  * Set to 0 to disable, or number of seconds before cache expiry.
  */
-$wgRevisionCacheExpiry = 0;
+$wgRevisionCacheExpiry = 86400 * 7;
 
 /** @} */ # end text storage }
 
@@ -2271,7 +2281,8 @@ $wgObjectCaches = [
                        'class' => 'SqlBagOStuff',
                        'args'  => [ [ 'slaveOnly' => false ] ]
                ],
-               'loggroup'  => 'SQLBagOStuff'
+               'loggroup'  => 'SQLBagOStuff',
+               'reportDupes' => false
        ],
 
        'apc' => [ 'class' => 'APCBagOStuff', 'reportDupes' => false ],
@@ -2517,7 +2528,7 @@ $wgFileCacheDepth = 2;
 
 /**
  * Kept for extension compatibility; see $wgParserCacheType
- * @deprecated 1.26
+ * @deprecated since 1.26
  */
 $wgEnableParserCache = true;
 
@@ -2650,7 +2661,7 @@ $wgInternalServer = false;
 $wgSquidMaxage = 18000;
 
 /**
- * Cache timeout for the CDN when DB slave lag is high
+ * Cache timeout for the CDN when DB replica DB lag is high
  * @see $wgSquidMaxage
  * @since 1.27
  */
@@ -2660,7 +2671,7 @@ $wgCdnMaxageLagged = 30;
  * If set, any SquidPurge call on a URL or URLs will send a second purge no less than
  * this many seconds later via the job queue. This requires delayed job support.
  * This should be safely higher than the 'max lag' value in $wgLBFactoryConf, so that
- * slave lag does not cause page to be stuck in stales states in CDN.
+ * replica DB lag does not cause page to be stuck in stales states in CDN.
  *
  * This also fixes race conditions in two-tiered CDN setups (e.g. cdn2 => cdn1 => MediaWiki).
  * If a purge for a URL reaches cdn2 before cdn1 and a request reaches cdn2 for that URL,
@@ -2882,15 +2893,6 @@ $wgDummyLanguageCodes = [
        'zh-yue' => 'yue',
 ];
 
-/**
- * Character set for use in the article edit box. Language-specific encodings
- * may be defined.
- *
- * This historic feature is one of the first that was added by former MediaWiki
- * team leader Brion Vibber, and is used to support the Esperanto x-system.
- */
-$wgEditEncoding = '';
-
 /**
  * Set this to true to replace Arabic presentation forms with their standard
  * forms in the U+0600-U+06FF block. This only works if $wgLanguageCode is
@@ -3195,6 +3197,15 @@ $wgHTMLFormAllowTableFormat = true;
  */
 $wgUseMediaWikiUIEverywhere = false;
 
+/**
+ * Whether to label the store-to-database-and-show-to-others button in the editor
+ * as "Save page"/"Save changes" if false (the default) or, if true, instead as
+ * "Publish page"/"Publish changes".
+ *
+ * @since 1.28
+ */
+$wgEditSubmitButtonLabelPublish = false;
+
 /**
  * Permit other namespaces in addition to the w3.org default.
  *
@@ -3749,10 +3760,8 @@ $wgResourceLoaderLESSImportPaths = [
 /**
  * Whether ResourceLoader should attempt to persist modules in localStorage on
  * browsers that support the Web Storage API.
- *
- * @since 1.23 - Client-side module persistence is experimental. Exercise care.
  */
-$wgResourceLoaderStorageEnabled = false;
+$wgResourceLoaderStorageEnabled = true;
 
 /**
  * Cache version for client-side ResourceLoader module storage. You can trigger
@@ -4216,6 +4225,8 @@ $wgAllowImageTag = false;
  *    - RaggettInternalHHVM: Use the limited-functionality HHVM extension
  *    - RaggettInternalPHP: Use the PECL extension
  *    - RaggettExternal: Shell out to an external binary (tidyBin)
+ *    - Html5Depurate: Use external Depurate service
+ *    - Html5Internal: Use the built-in HTML5 balancer
  *
  *  - tidyConfigFile: Path to configuration file for any of the Raggett drivers
  *  - debugComment: True to add a comment to the output with warning messages
@@ -4458,13 +4469,6 @@ $wgPasswordPolicy = [
        ],
 ];
 
-/**
- * Disable AuthManager
- * @since 1.27
- * @deprecated since 1.27, for use during development only
- */
-$wgDisableAuthManager = false;
-
 /**
  * Configure AuthManager
  *
@@ -5069,10 +5073,11 @@ $wgGroupPermissions['user']['upload'] = true;
 $wgGroupPermissions['user']['reupload'] = true;
 $wgGroupPermissions['user']['reupload-shared'] = true;
 $wgGroupPermissions['user']['minoredit'] = true;
-$wgGroupPermissions['user']['purge'] = true; // can use ?action=purge without clicking "ok"
+$wgGroupPermissions['user']['purge'] = true;
 $wgGroupPermissions['user']['sendemail'] = true;
 $wgGroupPermissions['user']['applychangetags'] = true;
 $wgGroupPermissions['user']['changetags'] = true;
+$wgGroupPermissions['user']['editcontentmodel'] = true;
 
 // Implicit group for accounts that pass $wgAutoConfirmAge
 $wgGroupPermissions['autoconfirmed']['autoconfirmed'] = true;
@@ -5103,7 +5108,6 @@ $wgGroupPermissions['sysop']['undelete'] = true;
 $wgGroupPermissions['sysop']['editinterface'] = true;
 $wgGroupPermissions['sysop']['editusercss'] = true;
 $wgGroupPermissions['sysop']['edituserjs'] = true;
-$wgGroupPermissions['sysop']['editcontentmodel'] = true;
 $wgGroupPermissions['sysop']['import'] = true;
 $wgGroupPermissions['sysop']['importupload'] = true;
 $wgGroupPermissions['sysop']['move'] = true;
@@ -5577,6 +5581,11 @@ $wgRateLimits = [
                'ip' => [ 8, 60 ],
                'newbie' => [ 8, 60 ],
        ],
+       // Changing the content model of a page
+       'editcontentmodel' => [
+               'newbie' => [ 2, 120 ],
+               'user' => [ 8, 60 ],
+       ],
 ];
 
 /**
@@ -5717,6 +5726,8 @@ $wgGrantPermissions['sendemail']['sendemail'] = true;
 
 $wgGrantPermissions['createaccount']['createaccount'] = true;
 
+$wgGrantPermissions['privateinfo']['viewmyprivateinfo'] = true;
+
 /**
  * @var Array Map of grants to their UI grouping
  * @since 1.27
@@ -5750,6 +5761,8 @@ $wgGrantPermissionGroups = [
        'createaccount'       => 'administration',
 
        'highvolume'          => 'high-volume',
+
+       'privateinfo'         => 'private-information',
 ];
 
 /**
@@ -6155,6 +6168,14 @@ $wgStatsdServer = false;
  */
 $wgStatsdMetricPrefix = 'MediaWiki';
 
+/**
+ * Sampling rate for statsd metrics as an associative array of patterns and rates.
+ * Patterns are Unix shell patterns (e.g. 'MediaWiki.api.*').
+ * Rates are sampling probabilities (e.g. 0.1 means 1 in 10 events are sampled).
+ * @since 1.28
+ */
+$wgStatsdSamplingRates = [];
+
 /**
  * InfoAction retrieves a list of transclusion links (both to and from).
  * This number puts a limit on that query in the case of highly transcluded
@@ -7204,8 +7225,8 @@ $wgJobTypesExcludedFromDefaultQueue = [ 'AssembleUploadChunks', 'PublishStashedF
 $wgJobBackoffThrottling = [];
 
 /**
- * Make job runners commit changes for slave-lag prone jobs one job at a time.
- * This is useful if there are many job workers that race on slave lag checks.
+ * Make job runners commit changes for replica DB-lag prone jobs one job at a time.
+ * This is useful if there are many job workers that race on replica DB lag checks.
  * If set, jobs taking this many seconds of DB write time have serialized commits.
  *
  * Note that affected jobs may have worse lock contention. Also, if they affect
@@ -7827,7 +7848,7 @@ $wgAPIMaxResultSize = 8388608;
 $wgAPIMaxUncachedDiffs = 1;
 
 /**
- * Maximum amount of DB lag on a majority of DB slaves to tolerate
+ * Maximum amount of DB lag on a majority of DB replica DBs to tolerate
  * before forcing bots to retry any write requests via API errors.
  * This should be lower than the 'max lag' value in $wgLBFactoryConf.
  */
@@ -8036,9 +8057,13 @@ $wgJobRunRate = 1;
  * When $wgJobRunRate > 0, try to run jobs asynchronously, spawning a new process
  * to handle the job execution, instead of blocking the request until the job
  * execution finishes.
+ *
  * @since 1.23
  */
-$wgRunJobsAsync = true;
+$wgRunJobsAsync = (
+       !function_exists( 'register_postsend_function' ) &&
+       !function_exists( 'fastcgi_finish_request' )
+);
 
 /**
  * Number of rows to update per job
@@ -8059,10 +8084,9 @@ $wgUpdateRowsPerQuery = 100;
 
 /**
  * Name of the external diff engine to use. Supported values:
- * * false: default PHP implementation
- * * 'wikidiff2': Wikimedia's fast difference engine implemented as a PHP/HHVM module
- * * 'wikidiff' and 'wikidiff3' are treated as false for backwards compatibility
- * * any other string is treated as a path to external diff executable
+ * * string: path to an external diff executable
+ * * false: wikidiff2 PHP/HHVM module if installed, otherwise the default PHP implementation
+ * * 'wikidiff', 'wikidiff2', and 'wikidiff3' are treated as false for backwards compatibility
  */
 $wgExternalDiffEngine = false;
 
@@ -8185,13 +8209,6 @@ $wgTextModelsToParse = [
        CONTENT_MODEL_CSS, // Make categories etc work, people put them into comments.
 ];
 
-/**
- * Whether the user must enter their password to change their e-mail address
- *
- * @since 1.20
- */
-$wgRequirePasswordforEmailChange = true;
-
 /**
  * Register handlers for specific types of sites.
  *
@@ -8261,11 +8278,29 @@ $wgPageLanguageUseDB = false;
 
 /**
  * Global configuration variable for Virtual REST Services.
- * Parameters for different services are to be declared inside
- * $wgVirtualRestConfig['modules'], which is to be treated as an associative
- * array. Global parameters will be merged with service-specific ones. The
- * result will then be passed to VirtualRESTService::__construct() in the
- * module.
+ *
+ * Use the 'path' key to define automatically mounted services. The value for this
+ * key is a map of path prefixes to service configuration. The latter is an array of:
+ *   - class : the fully qualified class name
+ *   - options : map of arguments to the class constructor
+ * Such services will be available to handle queries under their path from the VRS
+ * singleton, e.g. MediaWikiServices::getInstance()->getVirtualRESTServiceClient();
+ *
+ * Auto-mounting example for Parsoid:
+ *
+ * $wgVirtualRestConfig['paths']['/parsoid/'] = [
+ *     'class' => 'ParsoidVirtualRESTService',
+ *     'options' => [
+ *         'url' => 'http://localhost:8000',
+ *         'prefix' => 'enwiki',
+ *         'domain' => 'en.wikipedia.org'
+ *     ]
+ * ];
+ *
+ * Parameters for different services can also be declared inside the 'modules' value,
+ * which is to be treated as an associative array. The parameters in 'global' will be
+ * merged with service-specific ones. The result will then be passed to
+ * VirtualRESTService::__construct() in the module.
  *
  * Example config for Parsoid:
  *
@@ -8279,6 +8314,7 @@ $wgPageLanguageUseDB = false;
  * @since 1.25
  */
 $wgVirtualRestConfig = [
+       'paths' => [],
        'modules' => [],
        'global' => [
                # Timeout in seconds
@@ -8345,6 +8381,43 @@ $wgEventRelayerConfig = [
        ]
 ];
 
+/**
+ * Share data about this installation with MediaWiki developers
+ *
+ * When set to true, MediaWiki will periodically ping https://www.mediawiki.org/ with basic
+ * data about this MediaWiki instance. This data includes, for example, the type of system,
+ * PHP version, and chosen database backend. The Wikimedia Foundation shares this data with
+ * MediaWiki developers to help guide future development efforts.
+ *
+ * For details about what data is sent, see: https://www.mediawiki.org/wiki/Manual:$wgPingback
+ *
+ * @var bool
+ * @since 1.28
+ */
+$wgPingback = false;
+
+/**
+ * List of urls which appear often to be triggering CSP reports
+ * but do not appear to be caused by actual content, but by client
+ * software inserting scripts (i.e. Ad-Ware).
+ * List based on results from Wikimedia logs.
+ *
+ * @since 1.28
+ */
+$wgCSPFalsePositiveUrls = [
+       'https://3hub.co' => true,
+       'https://morepro.info' => true,
+       'https://p.ato.mx' => true,
+       'https://s.ato.mx' => true,
+       'https://adserver.adtech.de' => true,
+       'https://ums.adtechus.com' => true,
+       'https://cas.criteo.com' => true,
+       'https://cat.nl.eu.criteo.com' => true,
+       'https://atpixel.alephd.com' => true,
+       'https://rtb.metrigo.com' => true,
+       'https://d5p.de17a.com' => true,
+];
+
 /**
  * For really cool vim folding this needs to be at the end:
  * vim: foldmarker=@{,@} foldmethod=marker
index fe5083e..ab02a8e 100644 (file)
@@ -43,13 +43,12 @@ define( 'DBO_COMPRESS', 512 );
  * Valid database indexes
  * Operation-based indexes
  */
-define( 'DB_SLAVE', -1 );     # Read from the slave (or only server)
+define( 'DB_REPLICA', -1 );     # Read from a replica (or only server)
 define( 'DB_MASTER', -2 );    # Write to master (or only server)
 /**@}*/
 
 # Obsolete aliases
-define( 'DB_READ', -1 );
-define( 'DB_WRITE', -2 );
+define( 'DB_SLAVE', -1 );
 
 /**@{
  * Virtual namespaces; don't appear in the page database
index d9330ee..ba24799 100644 (file)
@@ -47,7 +47,11 @@ class DummyLinker {
                );
        }
 
+       /**
+        * @deprecated since 1.28, use LinkRenderer::getLinkClasses() instead
+        */
        public function getLinkColour( $t, $threshold ) {
+               wfDeprecated( __METHOD__, '1.28' );
                return Linker::getLinkColour( $t, $threshold );
        }
 
index 9c7ccdf..c5330ee 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\Logger\LoggerFactory;
+
 /**
  * The edit page/HTML interface (split from Article)
  * The actual database and text munging is still in Article,
@@ -258,9 +260,6 @@ class EditPage {
        /** @var bool */
        public $tooBig = false;
 
-       /** @var bool */
-       public $kblength = false;
-
        /** @var bool */
        public $missingComment = false;
 
@@ -394,11 +393,19 @@ class EditPage {
        /** @var bool */
        protected $edit;
 
+       /** @var bool|int */
+       protected $contentLength = false;
+
        /**
         * @var bool Set in ApiEditPage, based on ContentHandler::allowsDirectApiEditing
         */
        private $enableApiEditOverride = false;
 
+       /**
+        * @var IContextSource
+        */
+       protected $context;
+
        /**
         * @param Article $article
         */
@@ -406,6 +413,7 @@ class EditPage {
                $this->mArticle = $article;
                $this->page = $article->getPage(); // model object
                $this->mTitle = $article->getTitle();
+               $this->context = $article->getContext();
 
                $this->contentModel = $this->mTitle->getContentModel();
 
@@ -420,6 +428,14 @@ class EditPage {
                return $this->mArticle;
        }
 
+       /**
+        * @since 1.28
+        * @return IContextSource
+        */
+       public function getContext() {
+               return $this->context;
+       }
+
        /**
         * @since 1.19
         * @return Title
@@ -546,16 +562,29 @@ class EditPage {
 
                $revision = $this->mArticle->getRevisionFetched();
                // Disallow editing revisions with content models different from the current one
-               if ( $revision && $revision->getContentModel() !== $this->contentModel ) {
-                       $this->displayViewSourcePage(
-                               $this->getContentObject(),
-                               wfMessage(
-                                       'contentmodelediterror',
-                                       $revision->getContentModel(),
-                                       $this->contentModel
-                               )->plain()
-                       );
-                       return;
+               // Undo edits being an exception in order to allow reverting content model changes.
+               if ( $revision
+                       && $revision->getContentModel() !== $this->contentModel
+               ) {
+                       $prevRev = null;
+                       if ( $this->undidRev ) {
+                               $undidRevObj = Revision::newFromId( $this->undidRev );
+                               $prevRev = $undidRevObj ? $undidRevObj->getPrevious() : null;
+                       }
+                       if ( !$this->undidRev
+                               || !$prevRev
+                               || $prevRev->getContentModel() !== $this->contentModel
+                       ) {
+                               $this->displayViewSourcePage(
+                                       $this->getContentObject(),
+                                       wfMessage(
+                                               'contentmodelediterror',
+                                               $revision->getContentModel(),
+                                               $this->contentModel
+                                       )->plain()
+                               );
+                               return;
+                       }
                }
 
                $this->isConflict = false;
@@ -1118,6 +1147,14 @@ class EditPage {
                                                        $oldContent = $this->page->getContent( Revision::RAW );
                                                        $popts = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang );
                                                        $newContent = $content->preSaveTransform( $this->mTitle, $wgUser, $popts );
+                                                       if ( $newContent->getModel() !== $oldContent->getModel() ) {
+                                                               // The undo may change content
+                                                               // model if its reverting the top
+                                                               // edit. This can result in
+                                                               // mismatched content model/format.
+                                                               $this->contentModel = $newContent->getModel();
+                                                               $this->contentFormat = $oldrev->getContentFormat();
+                                                       }
 
                                                        if ( $newContent->equals( $oldContent ) ) {
                                                                # Tell the user that the undo results in no change,
@@ -1248,13 +1285,36 @@ class EditPage {
                        $handler = ContentHandler::getForModelID( $this->contentModel );
 
                        return $handler->makeEmptyContent();
-               } else {
-                       # nasty side-effect, but needed for consistency
-                       $this->contentModel = $rev->getContentModel();
-                       $this->contentFormat = $rev->getContentFormat();
+               } elseif ( !$this->undidRev ) {
+                       // Content models should always be the same since we error
+                       // out if they are different before this point (in ->edit()).
+                       // The exception being, during an undo, the current revision might
+                       // differ from the prior revision.
+                       $logger = LoggerFactory::getInstance( 'editpage' );
+                       if ( $this->contentModel !== $rev->getContentModel() ) {
+                               $logger->warning( "Overriding content model from current edit {prev} to {new}", [
+                                       'prev' => $this->contentModel,
+                                       'new' => $rev->getContentModel(),
+                                       'title' => $this->getTitle()->getPrefixedDBkey(),
+                                       'method' => __METHOD__
+                               ] );
+                               $this->contentModel = $rev->getContentModel();
+                       }
 
-                       return $content;
+                       // Given that the content models should match, the current selected
+                       // format should be supported.
+                       if ( !$content->isSupportedFormat( $this->contentFormat ) ) {
+                               $logger->warning( "Current revision content format unsupported. Overriding {prev} to {new}", [
+
+                                       'prev' => $this->contentFormat,
+                                       'new' => $rev->getContentFormat(),
+                                       'title' => $this->getTitle()->getPrefixedDBkey(),
+                                       'method' => __METHOD__
+                               ] );
+                               $this->contentFormat = $rev->getContentFormat();
+                       }
                }
+               return $content;
        }
 
        /**
@@ -1286,7 +1346,7 @@ class EditPage {
                        return $this->mPreloadContent;
                }
 
-               $handler = ContentHandler::getForTitle( $this->getTitle() );
+               $handler = ContentHandler::getForModelID( $this->contentModel );
 
                if ( $preload === '' ) {
                        return $handler->makeEmptyContent();
@@ -1386,7 +1446,7 @@ class EditPage {
 
        /**
         * Attempt submission
-        * @param array $resultDetails See docs for $result in internalAttemptSave
+        * @param array|bool $resultDetails See docs for $result in internalAttemptSave
         * @throws UserBlockedError|ReadOnlyError|ThrottledError|PermissionsError
         * @return Status The resulting status object.
         */
@@ -1450,7 +1510,7 @@ class EditPage {
 
                        case self::AS_CANNOT_USE_CUSTOM_MODEL:
                        case self::AS_PARSE_ERROR:
-                               $wgOut->addWikiText( '<div class="error">' . $status->getWikiText() . '</div>' );
+                               $wgOut->addWikiText( '<div class="error">' . "\n" . $status->getWikiText() . '</div>' );
                                return true;
 
                        case self::AS_SUCCESS_NEW_ARTICLE:
@@ -1527,7 +1587,7 @@ class EditPage {
                                // is if an extension hook aborted from inside ArticleSave.
                                // Render the status object into $this->hookError
                                // FIXME this sucks, we should just use the Status object throughout
-                               $this->hookError = '<div class="error">' . $status->getWikiText() .
+                               $this->hookError = '<div class="error">' ."\n" . $status->getWikiText() .
                                        '</div>';
                                return true;
                }
@@ -1750,8 +1810,8 @@ class EditPage {
                        return $status;
                }
 
-               $this->kblength = (int)( strlen( $this->textbox1 ) / 1024 );
-               if ( $this->kblength > $wgMaxArticleSize ) {
+               $this->contentLength = strlen( $this->textbox1 );
+               if ( $this->contentLength > $wgMaxArticleSize * 1024 ) {
                        // Error will be displayed by showEditForm()
                        $this->tooBig = true;
                        $status->setResult( false, self::AS_CONTENT_TOO_BIG );
@@ -1798,7 +1858,9 @@ class EditPage {
                        $status->value = self::AS_READ_ONLY_PAGE;
                        return $status;
                }
-               if ( $wgUser->pingLimiter() || $wgUser->pingLimiter( 'linkpurge', 0 ) ) {
+               if ( $wgUser->pingLimiter() || $wgUser->pingLimiter( 'linkpurge', 0 )
+                       || ( $changingContentModel && $wgUser->pingLimiter( 'editcontentmodel' ) )
+               ) {
                        $status->fatal( 'actionthrottledtext' );
                        $status->value = self::AS_RATE_LIMITED;
                        return $status;
@@ -2038,8 +2100,8 @@ class EditPage {
                }
 
                // Check for length errors again now that the section is merged in
-               $this->kblength = (int)( strlen( $this->toEditText( $content ) ) / 1024 );
-               if ( $this->kblength > $wgMaxArticleSize ) {
+               $this->contentLength = strlen( $this->toEditText( $content ) );
+               if ( $this->contentLength > $wgMaxArticleSize * 1024 ) {
                        $this->tooBig = true;
                        $status->setResult( false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED );
                        return $status;
@@ -2453,8 +2515,7 @@ class EditPage {
                }
 
                if ( !$this->isSupportedContentModel( $content->getModel() ) ) {
-                       throw new MWException( 'This content model is not supported: '
-                               . ContentHandler::getLocalizedName( $content->getModel() ) );
+                       throw new MWException( 'This content model is not supported: ' . $content->getModel() );
                }
 
                return $content->serialize( $this->contentFormat );
@@ -2469,7 +2530,7 @@ class EditPage {
         * content.
         *
         * @param string|null|bool $text Text to unserialize
-        * @return Content The content object created from $text. If $text was false
+        * @return Content|bool|null The content object created from $text. If $text was false
         *   or null, false resp. null will be  returned instead.
         *
         * @throws MWException If unserializing the text results in a Content
@@ -2485,8 +2546,7 @@ class EditPage {
                        $this->contentModel, $this->contentFormat );
 
                if ( !$this->isSupportedContentModel( $content->getModel() ) ) {
-                       throw new MWException( 'This content model is not supported: '
-                               . ContentHandler::getLocalizedName( $content->getModel() ) );
+                       throw new MWException( 'This content model is not supported: ' . $content->getModel() );
                }
 
                return $content;
@@ -2690,8 +2750,9 @@ class EditPage {
                $wgOut->addHTML( Html::rawElement( 'div', [ 'class' => 'hiddencats' ],
                        Linker::formatHiddenCategories( $this->page->getHiddenCategories() ) ) );
 
-               $wgOut->addHTML( Html::rawElement( 'div', [ 'class' => 'limitreport' ],
-                       self::getPreviewLimitReport( $this->mParserOutput ) ) );
+               if ( $this->mParserOutput ) {
+                       $wgOut->setLimitReportData( $this->mParserOutput->getLimitReportData() );
+               }
 
                $wgOut->addModules( 'mediawiki.action.edit.collapsibleFooter' );
 
@@ -2889,6 +2950,9 @@ class EditPage {
                                        );
                                }
                                if ( $this->getTitle()->isSubpageOf( $wgUser->getUserPage() ) ) {
+                                       $wgOut->wrapWikiMsg( '<div class="mw-usercssjspublic">$1</div>',
+                                               $this->isCssSubpage ? 'usercssispublic' : 'userjsispublic'
+                                       );
                                        if ( $this->formtype !== 'preview' ) {
                                                if ( $this->isCssSubpage && $wgAllowUserCss ) {
                                                        $wgOut->wrapWikiMsg(
@@ -2944,15 +3008,15 @@ class EditPage {
                                        'wrap' => "<div class=\"mw-titleprotectedwarning\">\n$1</div>" ] );
                }
 
-               if ( $this->kblength === false ) {
-                       $this->kblength = (int)( strlen( $this->textbox1 ) / 1024 );
+               if ( $this->contentLength === false ) {
+                       $this->contentLength = strlen( $this->textbox1 );
                }
 
-               if ( $this->tooBig || $this->kblength > $wgMaxArticleSize ) {
+               if ( $this->tooBig || $this->contentLength > $wgMaxArticleSize * 1024 ) {
                        $wgOut->wrapWikiMsg( "<div class='error' id='mw-edit-longpageerror'>\n$1\n</div>",
                                [
                                        'longpageerror',
-                                       $wgLang->formatNum( $this->kblength ),
+                                       $wgLang->formatNum( round( $this->contentLength / 1024, 3 ) ),
                                        $wgLang->formatNum( $wgMaxArticleSize )
                                ]
                        );
@@ -2978,14 +3042,14 @@ class EditPage {
         * subclasses may reorganize the form.
         * Note that you do not need to worry about the label's for=, it will be
         * inferred by the id given to the input. You can remove them both by
-        * passing array( 'id' => false ) to $userInputAttrs.
+        * passing [ 'id' => false ] to $userInputAttrs.
         *
         * @param string $summary The value of the summary input
         * @param string $labelText The html to place inside the label
         * @param array $inputAttrs Array of attrs to use on the input
         * @param array $spanLabelAttrs Array of attrs to use on the span inside the label
         *
-        * @return array An array in the format array( $label, $input )
+        * @return array An array in the format [ $label, $input ]
         */
        function getSummaryInput( $summary = "", $labelText = null,
                $inputAttrs = null, $spanLabelAttrs = null
@@ -3026,7 +3090,7 @@ class EditPage {
         * @param string $summary The text of the summary to display
         */
        protected function showSummaryInput( $isSubjectPreview, $summary = "" ) {
-               global $wgOut, $wgContLang;
+               global $wgOut;
                # Add a class if 'missingsummary' is triggered to allow styling of the summary line
                $summaryClass = $this->missingSummary ? 'mw-summarymissed' : 'mw-summary';
                if ( $isSubjectPreview ) {
@@ -3038,7 +3102,6 @@ class EditPage {
                                return;
                        }
                }
-               $summary = $wgContLang->recodeForEdit( $summary );
                $labelText = wfMessage( $isSubjectPreview ? 'subject' : 'summary' )->parse();
                list( $label, $input ) = $this->getSummaryInput(
                        $summary,
@@ -3419,41 +3482,12 @@ HTML
                        return '';
                }
 
-               $limitReport = Html::rawElement( 'div', [ 'class' => 'mw-limitReportExplanation' ],
-                       wfMessage( 'limitreport-title' )->parseAsBlock()
+               return ResourceLoader::makeInlineScript(
+                       ResourceLoader::makeConfigSetScript(
+                               [ 'wgPageParseReport' => $output->getLimitReportData() ],
+                               true
+                       )
                );
-
-               // Show/hide animation doesn't work correctly on a table, so wrap it in a div.
-               $limitReport .= Html::openElement( 'div', [ 'class' => 'preview-limit-report-wrapper' ] );
-
-               $limitReport .= Html::openElement( 'table', [
-                       'class' => 'preview-limit-report wikitable'
-               ] ) .
-                       Html::openElement( 'tbody' );
-
-               foreach ( $output->getLimitReportData() as $key => $value ) {
-                       if ( Hooks::run( 'ParserLimitReportFormat',
-                               [ $key, &$value, &$limitReport, true, true ]
-                       ) ) {
-                               $keyMsg = wfMessage( $key );
-                               $valueMsg = wfMessage( [ "$key-value-html", "$key-value" ] );
-                               if ( !$valueMsg->exists() ) {
-                                       $valueMsg = new RawMessage( '$1' );
-                               }
-                               if ( !$keyMsg->isDisabled() && !$valueMsg->isDisabled() ) {
-                                       $limitReport .= Html::openElement( 'tr' ) .
-                                               Html::rawElement( 'th', null, $keyMsg->parse() ) .
-                                               Html::rawElement( 'td', null, $valueMsg->params( $value )->parse() ) .
-                                               Html::closeElement( 'tr' );
-                               }
-                       }
-               }
-
-               $limitReport .= Html::closeElement( 'tbody' ) .
-                       Html::closeElement( 'table' ) .
-                       Html::closeElement( 'div' );
-
-               return $limitReport;
        }
 
        protected function showStandardInputs( &$tabindex = 2 ) {
@@ -3513,13 +3547,12 @@ HTML
                if ( Hooks::run( 'EditPageBeforeConflictDiff', [ &$this, &$wgOut ] ) ) {
                        $stats = $wgOut->getContext()->getStats();
                        $stats->increment( 'edit.failures.conflict' );
-                       if ( $this->mTitle->isTalkPage() ) {
-                               $stats->increment( 'edit.failures.conflict.byType.talk' );
-                       } else {
-                               $stats->increment( 'edit.failures.conflict.byType.subject' );
-                       }
-                       if ( $this->mTitle->getNamespace() === NS_PROJECT ) {
-                               $stats->increment( 'edit.failures.conflict.byNamespace.project' );
+                       // Only include 'standard' namespaces to avoid creating unknown numbers of statsd metrics
+                       if (
+                               $this->mTitle->getNamespace() >= NS_MAIN &&
+                               $this->mTitle->getNamespace() <= NS_CATEGORY_TALK
+                       ) {
+                               $stats->increment( 'edit.failures.conflict.byNamespaceId.' . $this->mTitle->getNamespace() );
                        }
 
                        $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourdiff" );
@@ -3604,7 +3637,7 @@ HTML
         * @return bool|stdClass
         */
        protected function getLastDelete() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $data = $dbr->selectRow(
                        [ 'logging', 'user' ],
                        [
@@ -3801,8 +3834,8 @@ HTML
         * Parse the page for a preview. Subclasses may override this class, in order
         * to parse with different options, or to otherwise modify the preview HTML.
         *
-        * @param Content @content The page content
-        * @return Associative array with keys:
+        * @param Content $content The page content
+        * @return array with keys:
         *   - parserOutput: The ParserOutput object
         *   - html: The HTML to be displayed
         */
@@ -4053,13 +4086,22 @@ HTML
        public function getEditButtons( &$tabindex ) {
                $buttons = [];
 
+               $labelAsPublish =
+                       $this->mArticle->getContext()->getConfig()->get( 'EditSubmitButtonLabelPublish' );
+
+               // Can't use $this->isNew as that's also true if we're adding a new section to an extant page
+               if ( $labelAsPublish ) {
+                       $buttonLabelKey = !$this->mTitle->exists() ? 'publishpage' : 'publishchanges';
+               } else {
+                       $buttonLabelKey = !$this->mTitle->exists() ? 'savearticle' : 'savechanges';
+               }
+               $buttonLabel = wfMessage( $buttonLabelKey )->text();
                $attribs = [
                        'id' => 'wpSave',
                        'name' => 'wpSave',
                        'tabindex' => ++$tabindex,
                ] + Linker::tooltipAndAccesskeyAttribs( 'save' );
-               $buttons['save'] = Html::submitButton( wfMessage( 'savearticle' )->text(),
-                       $attribs, [ 'mw-ui-constructive' ] );
+               $buttons['save'] = Html::submitButton( $buttonLabel, $attribs, [ 'mw-ui-constructive' ] );
 
                ++$tabindex; // use the same for preview and live preview
                $attribs = [
@@ -4175,11 +4217,9 @@ HTML
         * @return string
         */
        protected function safeUnicodeOutput( $text ) {
-               global $wgContLang;
-               $codedText = $wgContLang->recodeForEdit( $text );
                return $this->checkUnicodeCompliantBrowser()
-                       ? $codedText
-                       : $this->makeSafe( $codedText );
+                       ? $text
+                       : $this->makesafe( $text );
        }
 
        /**
index 158c852..3b2283b 100644 (file)
@@ -226,6 +226,7 @@ class FauxRequest extends WebRequest {
        }
 
        /**
+        * @codeCoverageIgnore
         * @param array $extWhitelist
         * @return bool
         */
@@ -234,6 +235,7 @@ class FauxRequest extends WebRequest {
        }
 
        /**
+        * @codeCoverageIgnore
         * @return string
         */
        protected function getRawIP() {
index d7dbd22..071a3db 100644 (file)
@@ -39,6 +39,7 @@ class FeedUtils {
                global $wgRequest, $wgUser;
 
                $purge = $wgRequest->getVal( 'action' ) === 'purge';
+               // Allow users with 'purge' right to clear feed caches
                if ( $purge && $wgUser->isAllowed( 'purge' ) ) {
                        $cache = ObjectCache::getMainWANInstance();
                        $cache->delete( $timekey, 1 );
index 98f8283..65638f2 100644 (file)
@@ -189,31 +189,24 @@ class FileDeleteForm {
                        );
                        $page = WikiPage::factory( $title );
                        $dbw = wfGetDB( DB_MASTER );
-                       try {
-                               $dbw->startAtomic( __METHOD__ );
-                               // delete the associated article first
-                               $error = '';
-                               $deleteStatus = $page->doDeleteArticleReal( $reason, $suppress, 0, false, $error, $user );
-                               // doDeleteArticleReal() returns a non-fatal error status if the page
-                               // or revision is missing, so check for isOK() rather than isGood()
-                               if ( $deleteStatus->isOK() ) {
-                                       $status = $file->delete( $reason, $suppress, $user );
-                                       if ( $status->isOK() ) {
-                                               $status->value = $deleteStatus->value; // log id
-                                               $dbw->endAtomic( __METHOD__ );
-                                       } else {
-                                               // Page deleted but file still there? rollback page delete
-                                               $dbw->rollback( __METHOD__ );
-                                       }
-                               } else {
-                                       // Done; nothing changed
+                       $dbw->startAtomic( __METHOD__ );
+                       // delete the associated article first
+                       $error = '';
+                       $deleteStatus = $page->doDeleteArticleReal( $reason, $suppress, 0, false, $error, $user );
+                       // doDeleteArticleReal() returns a non-fatal error status if the page
+                       // or revision is missing, so check for isOK() rather than isGood()
+                       if ( $deleteStatus->isOK() ) {
+                               $status = $file->delete( $reason, $suppress, $user );
+                               if ( $status->isOK() ) {
+                                       $status->value = $deleteStatus->value; // log id
                                        $dbw->endAtomic( __METHOD__ );
+                               } else {
+                                       // Page deleted but file still there? rollback page delete
+                                       wfGetLBFactory()->rollbackMasterChanges( __METHOD__ );
                                }
-                       } catch ( Exception $e ) {
-                               // Rollback before returning to prevent UI from displaying
-                               // incorrect "View or restore N deleted edits?"
-                               $dbw->rollback( __METHOD__ );
-                               throw $e;
+                       } else {
+                               // Done; nothing changed
+                               $dbw->endAtomic( __METHOD__ );
                        }
                }
 
index 66e2440..bc78be5 100644 (file)
@@ -222,17 +222,17 @@ function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
  * Merge arrays in the style of getUserPermissionsErrors, with duplicate removal
  * e.g.
  *     wfMergeErrorArrays(
- *             array( array( 'x' ) ),
- *             array( array( 'x', '2' ) ),
- *             array( array( 'x' ) ),
- *             array( array( 'y' ) )
+ *             [ [ 'x' ] ],
+ *             [ [ 'x', '2' ] ],
+ *             [ [ 'x' ] ],
+ *             [ [ 'y' ] ]
  *     );
  * returns:
- *             array(
- *             array( 'x', '2' ),
- *             array( 'x' ),
- *             array( 'y' )
- *     )
+ *             [
+ *             [ 'x', '2' ],
+ *             [ 'x' ],
+ *             [ 'y' ]
+ *     ]
  *
  * @param array $array1,...
  * @return array
@@ -811,7 +811,7 @@ function wfUrlProtocolsWithoutProtRel() {
  * 3) Adds a "delimiter" element to the array, either '://', ':' or '//' (see (2)).
  *
  * @param string $url A URL to parse
- * @return string[] Bits of the URL in an associative array, per PHP docs
+ * @return string[]|bool Bits of the URL in an associative array, per PHP docs, false on failure
  */
 function wfParseUrl( $url ) {
        global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
@@ -827,7 +827,7 @@ function wfParseUrl( $url ) {
        $bits = parse_url( $url );
        MediaWiki\restoreWarnings();
        // parse_url() returns an array without scheme for some invalid URLs, e.g.
-       // parse_url("%0Ahttp://example.com") == array( 'host' => '%0Ahttp', 'path' => 'example.com' )
+       // parse_url("%0Ahttp://example.com") == [ 'host' => '%0Ahttp', 'path' => 'example.com' ]
        if ( !$bits || !isset( $bits['scheme'] ) ) {
                return false;
        }
@@ -1195,6 +1195,7 @@ function wfLogProfilingData() {
                        $statsdPort = isset( $statsdServer[1] ) ? $statsdServer[1] : 8125;
                        $statsdSender = new SocketSender( $statsdHost, $statsdPort );
                        $statsdClient = new SamplingStatsdClient( $statsdSender, true, false );
+                       $statsdClient->setSamplingRates( $config->get( 'StatsdSamplingRates' ) );
                        $statsdClient->send( $context->getStats()->getBuffer() );
                } catch ( Exception $ex ) {
                        MWExceptionHandler::logException( $ex );
@@ -1277,7 +1278,7 @@ function wfReadOnly() {
  * Check if the site is in read-only mode and return the message if so
  *
  * This checks wfConfiguredReadOnlyReason() and the main load balancer
- * for slave lag. This may result in DB_SLAVE connection being made.
+ * for replica DB lag. This may result in DB connection being made.
  *
  * @return string|bool String when in read-only mode; false otherwise
  */
@@ -2950,7 +2951,7 @@ function wfRelativePath( $path, $from ) {
  * Supports base 2 through 36; digit values 10-36 are represented
  * as lowercase letters a-z. Input is case-insensitive.
  *
- * @deprecated 1.27 Use Wikimedia\base_convert() directly
+ * @deprecated since 1.27 Use Wikimedia\base_convert() directly
  *
  * @param string $input Input number
  * @param int $sourceBase Base of the input number
@@ -3124,7 +3125,7 @@ function wfSplitWikiID( $wiki ) {
  * Get a Database object.
  *
  * @param int $db Index of the connection to get. May be DB_MASTER for the
- *            master (for write queries), DB_SLAVE for potentially lagged read
+ *            master (for write queries), DB_REPLICA for potentially lagged read
  *            queries, or an integer >= 0 for a particular server.
  *
  * @param string|string[] $groups Query groups. An array of group names that this query
@@ -3133,7 +3134,7 @@ function wfSplitWikiID( $wiki ) {
  *
  * @param string|bool $wiki The wiki ID, or false for the current wiki
  *
- * Note: multiple calls to wfGetDB(DB_SLAVE) during the course of one request
+ * Note: multiple calls to wfGetDB(DB_REPLICA) during the course of one request
  * will always return the same object, unless the underlying connection or load
  * balancer is manually destroyed.
  *
@@ -3278,10 +3279,10 @@ function wfGetNull() {
 }
 
 /**
- * Waits for the slaves to catch up to the master position
+ * Waits for the replica DBs to catch up to the master position
  *
  * Use this when updating very large numbers of rows, as in maintenance scripts,
- * to avoid causing too much lag. Of course, this is a no-op if there are no slaves.
+ * to avoid causing too much lag. Of course, this is a no-op if there are no replica DBs.
  *
  * By default this waits on the main DB cluster of the current wiki.
  * If $cluster is set to "*" it will wait on all DB clusters, including
@@ -3350,9 +3351,9 @@ function wfCountDown( $seconds ) {
 }
 
 /**
- * Replace all invalid characters with -
- * Additional characters can be defined in $wgIllegalFileChars (see bug 20489)
- * By default, $wgIllegalFileChars = ':'
+ * Replace all invalid characters with '-'.
+ * Additional characters can be defined in $wgIllegalFileChars (see T22489).
+ * By default, $wgIllegalFileChars includes ':', '/', '\'.
  *
  * @param string $name Filename to process
  * @return string
@@ -3360,12 +3361,13 @@ function wfCountDown( $seconds ) {
 function wfStripIllegalFilenameChars( $name ) {
        global $wgIllegalFileChars;
        $illegalFileChars = $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : '';
-       $name = wfBaseName( $name );
        $name = preg_replace(
                "/[^" . Title::legalChars() . "]" . $illegalFileChars . "/",
                '-',
                $name
        );
+       // $wgIllegalFileChars may not include '/' and '\', so we still need to do this
+       $name = wfBaseName( $name );
        return $name;
 }
 
@@ -3524,7 +3526,7 @@ function wfGetParserCacheStorage() {
  * @param string|null $deprecatedVersion Optionally mark hook as deprecated with version number
  *
  * @return bool True if no handler aborted the hook
- * @deprecated 1.25 - use Hooks::run
+ * @deprecated since 1.25 - use Hooks::run
  */
 function wfRunHooks( $event, array $args = [], $deprecatedVersion = null ) {
        return Hooks::run( $event, $args, $deprecatedVersion );
index 8673125..b17a2f5 100644 (file)
@@ -245,7 +245,7 @@ class HistoryBlobStub {
                if ( isset( self::$blobCache[$this->mOldId] ) ) {
                        $obj = self::$blobCache[$this->mOldId];
                } else {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $row = $dbr->selectRow(
                                'text',
                                [ 'old_flags', 'old_text' ],
@@ -336,7 +336,7 @@ class HistoryBlobCurStub {
         * @return string|bool
         */
        function getText() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $row = $dbr->selectRow( 'cur', [ 'cur_text' ], [ 'cur_id' => $this->mCurId ] );
                if ( !$row ) {
                        return false;
index b6c194c..511781d 100644 (file)
@@ -64,7 +64,7 @@ class Hooks {
         * @throws MWException If not in testing mode.
         */
        public static function clear( $name ) {
-               if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
+               if ( !defined( 'MW_PHPUNIT_TEST' ) && !defined( 'MW_PARSER_TEST' ) ) {
                        throw new MWException( 'Cannot reset hooks in operation.' );
                }
 
index e5128d1..8c01448 100644 (file)
@@ -627,6 +627,17 @@ class Html {
         * @return string Raw HTML
         */
        public static function inlineStyle( $contents, $media = 'all' ) {
+               // Don't escape '>' since that is used
+               // as direct child selector.
+               // Remember, in css, there is no "x" for hexadecimal escapes, and
+               // the space immediately after an escape sequence is swallowed.
+               $contents = strtr( $contents, [
+                       '<' => '\3C ',
+                       // CDATA end tag for good measure, but the main security
+                       // is from escaping the '<'.
+                       ']]>' => '\5D\5D\3E '
+               ] );
+
                if ( preg_match( '/[<&]/', $contents ) ) {
                        $contents = "/*<![CDATA[*/$contents/*]]>*/";
                }
@@ -935,13 +946,7 @@ class Html {
                        $attribs['version'] = $wgHtml5Version;
                }
 
-               $html = self::openElement( 'html', $attribs );
-
-               if ( $html ) {
-                       $html .= "\n";
-               }
-
-               $ret .= $html;
+               $ret .= self::openElement( 'html', $attribs );
 
                return $ret;
        }
@@ -1020,9 +1025,21 @@ class Html {
        static function srcSet( array $urls ) {
                $candidates = [];
                foreach ( $urls as $density => $url ) {
-                       // Cast density to float to strip 'x'.
-                       $candidates[] = $url . ' ' . (float)$density . 'x';
+                       // Cast density to float to strip 'x', then back to string to serve
+                       // as array index.
+                       $density = (string)(float)$density;
+                       $candidates[$density] = $url;
                }
+
+               // Remove duplicates that are the same as a smaller value
+               ksort( $candidates, SORT_NUMERIC );
+               $candidates = array_unique( $candidates );
+
+               // Append density info to the url
+               foreach ( $candidates as $density => $url ) {
+                       $candidates[$density] = $url . ' ' . $density . 'x';
+               }
+
                return implode( ", ", $candidates );
        }
 }
index b12f49f..2ca5e1b 100644 (file)
@@ -124,47 +124,6 @@ class Http {
                return Http::request( 'POST', $url, $options, $caller );
        }
 
-       /**
-        * Check if the URL can be served by localhost
-        *
-        * @param string $url Full url to check
-        * @return bool
-        */
-       public static function isLocalURL( $url ) {
-               global $wgCommandLineMode, $wgLocalVirtualHosts;
-
-               if ( $wgCommandLineMode ) {
-                       return false;
-               }
-
-               // Extract host part
-               $matches = [];
-               if ( preg_match( '!^http://([\w.-]+)[/:].*$!', $url, $matches ) ) {
-                       $host = $matches[1];
-                       // Split up dotwise
-                       $domainParts = explode( '.', $host );
-                       // Check if this domain or any superdomain is listed as a local virtual host
-                       $domainParts = array_reverse( $domainParts );
-
-                       $domain = '';
-                       $countParts = count( $domainParts );
-                       for ( $i = 0; $i < $countParts; $i++ ) {
-                               $domainPart = $domainParts[$i];
-                               if ( $i == 0 ) {
-                                       $domain = $domainPart;
-                               } else {
-                                       $domain = $domainPart . '.' . $domain;
-                               }
-
-                               if ( in_array( $domain, $wgLocalVirtualHosts ) ) {
-                                       return true;
-                               }
-                       }
-               }
-
-               return false;
-       }
-
        /**
         * A standard user-agent we can use for external requests.
         * @return string
@@ -194,7 +153,7 @@ class Http {
        }
 
        /**
-        * Gets the relevant proxy from $wgHTTPProxy/http_proxy (when set).
+        * Gets the relevant proxy from $wgHTTPProxy
         *
         * @return mixed The proxy address or an empty string if not set.
         */
@@ -205,11 +164,6 @@ class Http {
                        return $wgHTTPProxy;
                }
 
-               $envHttpProxy = getenv( "http_proxy" );
-               if ( $envHttpProxy ) {
-                       return $envHttpProxy;
-               }
-
                return "";
        }
 }
@@ -393,15 +347,56 @@ class MWHttpRequest {
                        return;
                }
 
-               // Otherwise, fallback to $wgHTTPProxy/http_proxy (when set) if this is not a machine
+               // Otherwise, fallback to $wgHTTPProxy if this is not a machine
                // local URL and proxies are not disabled
-               if ( Http::isLocalURL( $this->url ) || $this->noProxy ) {
+               if ( self::isLocalURL( $this->url ) || $this->noProxy ) {
                        $this->proxy = '';
                } else {
                        $this->proxy = Http::getProxy();
                }
        }
 
+       /**
+        * Check if the URL can be served by localhost
+        *
+        * @param string $url Full url to check
+        * @return bool
+        */
+       private static function isLocalURL( $url ) {
+               global $wgCommandLineMode, $wgLocalVirtualHosts;
+
+               if ( $wgCommandLineMode ) {
+                       return false;
+               }
+
+               // Extract host part
+               $matches = [];
+               if ( preg_match( '!^https?://([\w.-]+)[/:].*$!', $url, $matches ) ) {
+                       $host = $matches[1];
+                       // Split up dotwise
+                       $domainParts = explode( '.', $host );
+                       // Check if this domain or any superdomain is listed as a local virtual host
+                       $domainParts = array_reverse( $domainParts );
+
+                       $domain = '';
+                       $countParts = count( $domainParts );
+                       for ( $i = 0; $i < $countParts; $i++ ) {
+                               $domainPart = $domainParts[$i];
+                               if ( $i == 0 ) {
+                                       $domain = $domainPart;
+                               } else {
+                                       $domain = $domainPart . '.' . $domain;
+                               }
+
+                               if ( in_array( $domain, $wgLocalVirtualHosts ) ) {
+                                       return true;
+                               }
+                       }
+               }
+
+               return false;
+       }
+
        /**
         * Set the user agent
         * @param string $UA
@@ -604,7 +599,7 @@ class MWHttpRequest {
         * Returns the value of the given response header.
         *
         * @param string $header
-        * @return string
+        * @return string|null
         */
        public function getResponseHeader( $header ) {
                if ( !$this->respHeaders ) {
index 802bfbe..7b3d72b 100644 (file)
@@ -89,10 +89,10 @@ class LinkFilter {
         *
         * @param string $filterEntry Domainparts
         * @param string $protocol Protocol (default http://)
-        * @return array Array to be passed to Database::buildLike() or false on error
+        * @return array|bool Array to be passed to Database::buildLike() or false on error
         */
        public static function makeLikeArray( $filterEntry, $protocol = 'http://' ) {
-               $db = wfGetDB( DB_SLAVE );
+               $db = wfGetDB( DB_REPLICA );
 
                $target = $protocol . $filterEntry;
                $bits = wfParseUrl( $target );
index 0b2d3a7..bcc348e 100644 (file)
@@ -137,31 +137,24 @@ class Linker {
        /**
         * Return the CSS colour of a known link
         *
+        * @deprecated since 1.28, use LinkRenderer::getLinkClasses() instead
+        *
         * @since 1.16.3
         * @param LinkTarget $t
         * @param int $threshold User defined threshold
         * @return string CSS class
         */
        public static function getLinkColour( LinkTarget $t, $threshold ) {
-               $linkCache = MediaWikiServices::getInstance()->getLinkCache();
-               // Make sure the target is in the cache
-               $id = $linkCache->addLinkObj( $t );
-               if ( $id == 0 ) {
-                       // Doesn't exist
-                       return '';
-               }
-
-               if ( $linkCache->getGoodLinkFieldObj( $t, 'redirect' ) ) {
-                       # Page is a redirect
-                       return 'mw-redirect';
-               } elseif ( $threshold > 0 && MWNamespace::isContent( $t->getNamespace() )
-                       && $linkCache->getGoodLinkFieldObj( $t, 'length' ) < $threshold
-               ) {
-                       # Page is a stub
-                       return 'stub';
+               wfDeprecated( __METHOD__, '1.28' );
+               $services = MediaWikiServices::getInstance();
+               $linkRenderer = $services->getLinkRenderer();
+               if ( $threshold !== $linkRenderer->getStubThreshold() ) {
+                       // Need to create a new instance with the right stub threshold...
+                       $linkRenderer = $services->getLinkRendererFactory()->create();
+                       $linkRenderer->setStubThreshold( $threshold );
                }
 
-               return '';
+               return $linkRenderer->getLinkClasses( $t );
        }
 
        /**
@@ -177,6 +170,7 @@ class Linker {
         * link() replaces the old functions in the makeLink() family.
         *
         * @since 1.18 Method exists since 1.16 as non-static, made static in 1.18.
+        * @deprecated since 1.28, use MediaWiki\Linker\LinkRenderer instead
         *
         * @param Title $target Can currently only be a Title, but this may
         *   change to support Images, literal URLs, etc.
@@ -252,7 +246,9 @@ class Linker {
 
        /**
         * Identical to link(), except $options defaults to 'known'.
+        *
         * @since 1.16.3
+        * @deprecated since 1.28, use MediaWiki\Linker\LinkRenderer instead
         * @see Linker::link
         * @return string
         */
@@ -320,7 +316,7 @@ class Linker {
        /**
         * @since 1.16.3
         * @param LinkTarget $target
-        * @return LinkTarget|Title You will get back the same type you passed in, or a Title object
+        * @return LinkTarget
         */
        public static function normaliseSpecialPage( LinkTarget $target ) {
                if ( $target->getNamespace() == NS_SPECIAL ) {
@@ -328,7 +324,7 @@ class Linker {
                        if ( !$name ) {
                                return $target;
                        }
-                       $ret = SpecialPage::getTitleFor( $name, $subpage, $target->getFragment() );
+                       $ret = SpecialPage::getTitleValueFor( $name, $subpage, $target->getFragment() );
                        return $ret;
                } else {
                        return $target;
@@ -432,47 +428,43 @@ class Linker {
                        return self::link( $title );
                }
 
-               // Shortcuts
-               $fp =& $frameParams;
-               $hp =& $handlerParams;
-
                // Clean up parameters
-               $page = isset( $hp['page'] ) ? $hp['page'] : false;
-               if ( !isset( $fp['align'] ) ) {
-                       $fp['align'] = '';
+               $page = isset( $handlerParams['page'] ) ? $handlerParams['page'] : false;
+               if ( !isset( $frameParams['align'] ) ) {
+                       $frameParams['align'] = '';
                }
-               if ( !isset( $fp['alt'] ) ) {
-                       $fp['alt'] = '';
+               if ( !isset( $frameParams['alt'] ) ) {
+                       $frameParams['alt'] = '';
                }
-               if ( !isset( $fp['title'] ) ) {
-                       $fp['title'] = '';
+               if ( !isset( $frameParams['title'] ) ) {
+                       $frameParams['title'] = '';
                }
-               if ( !isset( $fp['class'] ) ) {
-                       $fp['class'] = '';
+               if ( !isset( $frameParams['class'] ) ) {
+                       $frameParams['class'] = '';
                }
 
                $prefix = $postfix = '';
 
-               if ( 'center' == $fp['align'] ) {
+               if ( 'center' == $frameParams['align'] ) {
                        $prefix = '<div class="center">';
                        $postfix = '</div>';
-                       $fp['align'] = 'none';
+                       $frameParams['align'] = 'none';
                }
-               if ( $file && !isset( $hp['width'] ) ) {
-                       if ( isset( $hp['height'] ) && $file->isVectorized() ) {
+               if ( $file && !isset( $handlerParams['width'] ) ) {
+                       if ( isset( $handlerParams['height'] ) && $file->isVectorized() ) {
                                // If its a vector image, and user only specifies height
                                // we don't want it to be limited by its "normal" width.
                                global $wgSVGMaxSize;
-                               $hp['width'] = $wgSVGMaxSize;
+                               $handlerParams['width'] = $wgSVGMaxSize;
                        } else {
-                               $hp['width'] = $file->getWidth( $page );
+                               $handlerParams['width'] = $file->getWidth( $page );
                        }
 
-                       if ( isset( $fp['thumbnail'] )
-                               || isset( $fp['manualthumb'] )
-                               || isset( $fp['framed'] )
-                               || isset( $fp['frameless'] )
-                               || !$hp['width']
+                       if ( isset( $frameParams['thumbnail'] )
+                               || isset( $frameParams['manualthumb'] )
+                               || isset( $frameParams['framed'] )
+                               || isset( $frameParams['frameless'] )
+                               || !$handlerParams['width']
                        ) {
                                global $wgThumbLimits, $wgThumbUpright;
 
@@ -481,73 +473,77 @@ class Linker {
                                }
 
                                // Reduce width for upright images when parameter 'upright' is used
-                               if ( isset( $fp['upright'] ) && $fp['upright'] == 0 ) {
-                                       $fp['upright'] = $wgThumbUpright;
+                               if ( isset( $frameParams['upright'] ) && $frameParams['upright'] == 0 ) {
+                                       $frameParams['upright'] = $wgThumbUpright;
                                }
 
                                // For caching health: If width scaled down due to upright
                                // parameter, round to full __0 pixel to avoid the creation of a
                                // lot of odd thumbs.
-                               $prefWidth = isset( $fp['upright'] ) ?
-                                       round( $wgThumbLimits[$widthOption] * $fp['upright'], -1 ) :
+                               $prefWidth = isset( $frameParams['upright'] ) ?
+                                       round( $wgThumbLimits[$widthOption] * $frameParams['upright'], -1 ) :
                                        $wgThumbLimits[$widthOption];
 
                                // Use width which is smaller: real image width or user preference width
                                // Unless image is scalable vector.
-                               if ( !isset( $hp['height'] ) && ( $hp['width'] <= 0 ||
-                                               $prefWidth < $hp['width'] || $file->isVectorized() ) ) {
-                                       $hp['width'] = $prefWidth;
+                               if ( !isset( $handlerParams['height'] ) && ( $handlerParams['width'] <= 0 ||
+                                               $prefWidth < $handlerParams['width'] || $file->isVectorized() ) ) {
+                                       $handlerParams['width'] = $prefWidth;
                                }
                        }
                }
 
-               if ( isset( $fp['thumbnail'] ) || isset( $fp['manualthumb'] ) || isset( $fp['framed'] ) ) {
+               if ( isset( $frameParams['thumbnail'] ) || isset( $frameParams['manualthumb'] )
+                       || isset( $frameParams['framed'] )
+               ) {
                        # Create a thumbnail. Alignment depends on the writing direction of
                        # the page content language (right-aligned for LTR languages,
                        # left-aligned for RTL languages)
                        # If a thumbnail width has not been provided, it is set
                        # to the default user option as specified in Language*.php
-                       if ( $fp['align'] == '' ) {
-                               $fp['align'] = $parser->getTargetLanguage()->alignEnd();
+                       if ( $frameParams['align'] == '' ) {
+                               $frameParams['align'] = $parser->getTargetLanguage()->alignEnd();
                        }
-                       return $prefix . self::makeThumbLink2( $title, $file, $fp, $hp, $time, $query ) . $postfix;
+                       return $prefix .
+                               self::makeThumbLink2( $title, $file, $frameParams, $handlerParams, $time, $query ) .
+                               $postfix;
                }
 
-               if ( $file && isset( $fp['frameless'] ) ) {
+               if ( $file && isset( $frameParams['frameless'] ) ) {
                        $srcWidth = $file->getWidth( $page );
                        # For "frameless" option: do not present an image bigger than the
                        # source (for bitmap-style images). This is the same behavior as the
                        # "thumb" option does it already.
-                       if ( $srcWidth && !$file->mustRender() && $hp['width'] > $srcWidth ) {
-                               $hp['width'] = $srcWidth;
+                       if ( $srcWidth && !$file->mustRender() && $handlerParams['width'] > $srcWidth ) {
+                               $handlerParams['width'] = $srcWidth;
                        }
                }
 
-               if ( $file && isset( $hp['width'] ) ) {
+               if ( $file && isset( $handlerParams['width'] ) ) {
                        # Create a resized image, without the additional thumbnail features
-                       $thumb = $file->transform( $hp );
+                       $thumb = $file->transform( $handlerParams );
                } else {
                        $thumb = false;
                }
 
                if ( !$thumb ) {
-                       $s = self::makeBrokenImageLinkObj( $title, $fp['title'], '', '', '', $time == true );
+                       $s = self::makeBrokenImageLinkObj( $title, $frameParams['title'], '', '', '', $time == true );
                } else {
-                       self::processResponsiveImages( $file, $thumb, $hp );
+                       self::processResponsiveImages( $file, $thumb, $handlerParams );
                        $params = [
-                               'alt' => $fp['alt'],
-                               'title' => $fp['title'],
-                               'valign' => isset( $fp['valign'] ) ? $fp['valign'] : false,
-                               'img-class' => $fp['class'] ];
-                       if ( isset( $fp['border'] ) ) {
+                               'alt' => $frameParams['alt'],
+                               'title' => $frameParams['title'],
+                               'valign' => isset( $frameParams['valign'] ) ? $frameParams['valign'] : false,
+                               'img-class' => $frameParams['class'] ];
+                       if ( isset( $frameParams['border'] ) ) {
                                $params['img-class'] .= ( $params['img-class'] !== '' ? ' ' : '' ) . 'thumbborder';
                        }
-                       $params = self::getImageLinkMTOParams( $fp, $query, $parser ) + $params;
+                       $params = self::getImageLinkMTOParams( $frameParams, $query, $parser ) + $params;
 
                        $s = $thumb->toHtml( $params );
                }
-               if ( $fp['align'] != '' ) {
-                       $s = "<div class=\"float{$fp['align']}\">{$s}</div>";
+               if ( $frameParams['align'] != '' ) {
+                       $s = "<div class=\"float{$frameParams['align']}\">{$s}</div>";
                }
                return str_replace( "\n", ' ', $prefix . $s . $postfix );
        }
@@ -575,7 +571,9 @@ class Linker {
                                }
                        }
                } elseif ( isset( $frameParams['link-title'] ) && $frameParams['link-title'] !== '' ) {
-                       $mtoParams['custom-title-link'] = self::normaliseSpecialPage( $frameParams['link-title'] );
+                       $mtoParams['custom-title-link'] = Title::newFromLinkTarget(
+                               self::normaliseSpecialPage( $frameParams['link-title'] )
+                       );
                } elseif ( !empty( $frameParams['no-link'] ) ) {
                        // No link
                } else {
@@ -628,65 +626,61 @@ class Linker {
        ) {
                $exists = $file && $file->exists();
 
-               # Shortcuts
-               $fp =& $frameParams;
-               $hp =& $handlerParams;
-
-               $page = isset( $hp['page'] ) ? $hp['page'] : false;
-               if ( !isset( $fp['align'] ) ) {
-                       $fp['align'] = 'right';
+               $page = isset( $handlerParams['page'] ) ? $handlerParams['page'] : false;
+               if ( !isset( $frameParams['align'] ) ) {
+                       $frameParams['align'] = 'right';
                }
-               if ( !isset( $fp['alt'] ) ) {
-                       $fp['alt'] = '';
+               if ( !isset( $frameParams['alt'] ) ) {
+                       $frameParams['alt'] = '';
                }
-               if ( !isset( $fp['title'] ) ) {
-                       $fp['title'] = '';
+               if ( !isset( $frameParams['title'] ) ) {
+                       $frameParams['title'] = '';
                }
-               if ( !isset( $fp['caption'] ) ) {
-                       $fp['caption'] = '';
+               if ( !isset( $frameParams['caption'] ) ) {
+                       $frameParams['caption'] = '';
                }
 
-               if ( empty( $hp['width'] ) ) {
+               if ( empty( $handlerParams['width'] ) ) {
                        // Reduce width for upright images when parameter 'upright' is used
-                       $hp['width'] = isset( $fp['upright'] ) ? 130 : 180;
+                       $handlerParams['width'] = isset( $frameParams['upright'] ) ? 130 : 180;
                }
                $thumb = false;
                $noscale = false;
                $manualthumb = false;
 
                if ( !$exists ) {
-                       $outerWidth = $hp['width'] + 2;
+                       $outerWidth = $handlerParams['width'] + 2;
                } else {
-                       if ( isset( $fp['manualthumb'] ) ) {
+                       if ( isset( $frameParams['manualthumb'] ) ) {
                                # Use manually specified thumbnail
-                               $manual_title = Title::makeTitleSafe( NS_FILE, $fp['manualthumb'] );
+                               $manual_title = Title::makeTitleSafe( NS_FILE, $frameParams['manualthumb'] );
                                if ( $manual_title ) {
                                        $manual_img = wfFindFile( $manual_title );
                                        if ( $manual_img ) {
-                                               $thumb = $manual_img->getUnscaledThumb( $hp );
+                                               $thumb = $manual_img->getUnscaledThumb( $handlerParams );
                                                $manualthumb = true;
                                        } else {
                                                $exists = false;
                                        }
                                }
-                       } elseif ( isset( $fp['framed'] ) ) {
+                       } elseif ( isset( $frameParams['framed'] ) ) {
                                // Use image dimensions, don't scale
-                               $thumb = $file->getUnscaledThumb( $hp );
+                               $thumb = $file->getUnscaledThumb( $handlerParams );
                                $noscale = true;
                        } else {
                                # Do not present an image bigger than the source, for bitmap-style images
                                # This is a hack to maintain compatibility with arbitrary pre-1.10 behavior
                                $srcWidth = $file->getWidth( $page );
-                               if ( $srcWidth && !$file->mustRender() && $hp['width'] > $srcWidth ) {
-                                       $hp['width'] = $srcWidth;
+                               if ( $srcWidth && !$file->mustRender() && $handlerParams['width'] > $srcWidth ) {
+                                       $handlerParams['width'] = $srcWidth;
                                }
-                               $thumb = $file->transform( $hp );
+                               $thumb = $file->transform( $handlerParams );
                        }
 
                        if ( $thumb ) {
                                $outerWidth = $thumb->getWidth() + 2;
                        } else {
-                               $outerWidth = $hp['width'] + 2;
+                               $outerWidth = $handlerParams['width'] + 2;
                        }
                }
 
@@ -698,35 +692,35 @@ class Linker {
                        $url = wfAppendQuery( $url, [ 'page' => $page ] );
                }
                if ( $manualthumb
-                       && !isset( $fp['link-title'] )
-                       && !isset( $fp['link-url'] )
-                       && !isset( $fp['no-link'] ) ) {
-                       $fp['link-url'] = $url;
+                       && !isset( $frameParams['link-title'] )
+                       && !isset( $frameParams['link-url'] )
+                       && !isset( $frameParams['no-link'] ) ) {
+                       $frameParams['link-url'] = $url;
                }
 
-               $s = "<div class=\"thumb t{$fp['align']}\">"
+               $s = "<div class=\"thumb t{$frameParams['align']}\">"
                        . "<div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
 
                if ( !$exists ) {
-                       $s .= self::makeBrokenImageLinkObj( $title, $fp['title'], '', '', '', $time == true );
+                       $s .= self::makeBrokenImageLinkObj( $title, $frameParams['title'], '', '', '', $time == true );
                        $zoomIcon = '';
                } elseif ( !$thumb ) {
                        $s .= wfMessage( 'thumbnail_error', '' )->escaped();
                        $zoomIcon = '';
                } else {
                        if ( !$noscale && !$manualthumb ) {
-                               self::processResponsiveImages( $file, $thumb, $hp );
+                               self::processResponsiveImages( $file, $thumb, $handlerParams );
                        }
                        $params = [
-                               'alt' => $fp['alt'],
-                               'title' => $fp['title'],
-                               'img-class' => ( isset( $fp['class'] ) && $fp['class'] !== ''
-                                       ? $fp['class'] . ' '
+                               'alt' => $frameParams['alt'],
+                               'title' => $frameParams['title'],
+                               'img-class' => ( isset( $frameParams['class'] ) && $frameParams['class'] !== ''
+                                       ? $frameParams['class'] . ' '
                                        : '' ) . 'thumbimage'
                        ];
-                       $params = self::getImageLinkMTOParams( $fp, $query ) + $params;
+                       $params = self::getImageLinkMTOParams( $frameParams, $query ) + $params;
                        $s .= $thumb->toHtml( $params );
-                       if ( isset( $fp['framed'] ) ) {
+                       if ( isset( $frameParams['framed'] ) ) {
                                $zoomIcon = "";
                        } else {
                                $zoomIcon = Html::rawElement( 'div', [ 'class' => 'magnify' ],
@@ -737,7 +731,7 @@ class Linker {
                                                "" ) );
                        }
                }
-               $s .= '  <div class="thumbcaption">' . $zoomIcon . $fp['caption'] . "</div></div></div>";
+               $s .= '  <div class="thumbcaption">' . $zoomIcon . $frameParams['caption'] . "</div></div></div>";
                return str_replace( "\n", ' ', $s );
        }
 
@@ -999,9 +993,10 @@ class Linker {
                        $page = Title::makeTitle( NS_USER, $userName );
                }
 
+               // Wrap the output with <bdi> tags for directionality isolation
                return self::link(
                        $page,
-                       htmlspecialchars( $altUserName !== false ? $altUserName : $userName ),
+                       '<bdi>' . htmlspecialchars( $altUserName !== false ? $altUserName : $userName ) . '</bdi>',
                        [ 'class' => $classes ]
                );
        }
@@ -1809,7 +1804,7 @@ class Linker {
                        return null;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Up to the value of $wgShowRollbackEditCount revisions are counted
                $res = $dbr->select(
index 4964fe6..defdc96 100644 (file)
@@ -56,10 +56,14 @@ class MWTimestamp {
         *
         * @since 1.20
         *
-        * @param bool|string|int|float $timestamp Timestamp to set, or false for current time
+        * @param bool|string|int|float|DateTime $timestamp Timestamp to set, or false for current time
         */
        public function __construct( $timestamp = false ) {
-               $this->setTimestamp( $timestamp );
+               if ( $timestamp instanceof DateTime ) {
+                       $this->timestamp = $timestamp;
+               } else {
+                       $this->setTimestamp( $timestamp );
+               }
        }
 
        /**
index 13f706d..391e05a 100644 (file)
  *
  * @par Example:
  * @code
- * $magicWords = array();
+ * $magicWords = [];
  *
- * $magicWords['en'] = array(
- *     'magicwordkey' => array( 0, 'case_insensitive_magic_word' ),
- *     'magicwordkey2' => array( 1, 'CASE_sensitive_magic_word2' ),
- * );
+ * $magicWords['en'] = [
+ *     'magicwordkey' => [ 0, 'case_insensitive_magic_word' ],
+ *     'magicwordkey2' => [ 1, 'CASE_sensitive_magic_word2' ],
+ * ];
  * @endcode
  *
  * For magic words which are also Parser variables, add a MagicWordwgVariableIDs
index 21857b9..bca7a21 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
 
 /**
  * The MediaWiki class is the helper class for the index.php entry point.
@@ -553,34 +554,35 @@ class MediaWiki {
 
                $config = $context->getConfig();
 
-               $factory = wfGetLBFactory();
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                // Commit all changes
-               $factory->commitMasterChanges(
+               $lbFactory->commitMasterChanges(
                        __METHOD__,
                        // Abort if any transaction was too big
                        [ 'maxWriteDuration' => $config->get( 'MaxUserDBWriteDuration' ) ]
                );
-               // Record ChronologyProtector positions
-               $factory->shutdown();
-               wfDebug( __METHOD__ . ': all transactions committed' );
 
                DeferredUpdates::doUpdates( 'enqueue', DeferredUpdates::PRESEND );
                wfDebug( __METHOD__ . ': pre-send deferred updates completed' );
 
+               // Record ChronologyProtector positions
+               $lbFactory->shutdown();
+               wfDebug( __METHOD__ . ': all transactions committed' );
+
                // Set a cookie to tell all CDN edge nodes to "stick" the user to the DC that handles this
                // POST request (e.g. the "master" data center). Also have the user briefly bypass CDN so
                // ChronologyProtector works for cacheable URLs.
                $request = $context->getRequest();
-               if ( $request->wasPosted() && $factory->hasOrMadeRecentMasterChanges() ) {
+               if ( $request->wasPosted() && $lbFactory->hasOrMadeRecentMasterChanges() ) {
                        $expires = time() + $config->get( 'DataCenterUpdateStickTTL' );
                        $options = [ 'prefix' => '' ];
                        $request->response()->setCookie( 'UseDC', 'master', $expires, $options );
                        $request->response()->setCookie( 'UseCDNCache', 'false', $expires, $options );
                }
 
-               // Avoid letting a few seconds of slave lag cause a month of stale data. This logic is
+               // Avoid letting a few seconds of replica DB lag cause a month of stale data. This logic is
                // also intimately related to the value of $wgCdnReboundPurgeDelay.
-               if ( $factory->laggedSlaveUsed() ) {
+               if ( $lbFactory->laggedReplicaUsed() ) {
                        $maxAge = $config->get( 'CdnMaxageLagged' );
                        $context->getOutput()->lowerCdnMaxage( $maxAge );
                        $request->response()->header( "X-Database-Lagged: true" );
@@ -630,7 +632,7 @@ class MediaWiki {
                                fastcgi_finish_request();
                        } else {
                                // Either all DB and deferred updates should happen or none.
-                               // The later should not be cancelled due to client disconnect.
+                               // The latter should not be cancelled due to client disconnect.
                                ignore_user_abort( true );
                        }
 
@@ -761,8 +763,9 @@ class MediaWiki {
         * @param string $mode Use 'fast' to always skip job running
         */
        public function restInPeace( $mode = 'fast' ) {
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                // Assure deferred updates are not in the main transaction
-               wfGetLBFactory()->commitMasterChanges( __METHOD__ );
+               $lbFactory->commitMasterChanges( __METHOD__ );
 
                // Loosen DB query expectations since the HTTP client is unblocked
                $trxProfiler = Profiler::instance()->getTransactionProfiler();
@@ -788,9 +791,8 @@ class MediaWiki {
                wfLogProfilingData();
 
                // Commit and close up!
-               $factory = wfGetLBFactory();
-               $factory->commitMasterChanges( __METHOD__ );
-               $factory->shutdown( LBFactory::SHUTDOWN_NO_CHRONPROT );
+               $lbFactory->commitMasterChanges( __METHOD__ );
+               $lbFactory->shutdown( LBFactory::SHUTDOWN_NO_CHRONPROT );
 
                wfDebug( "Request ended normally\n" );
        }
@@ -802,10 +804,10 @@ class MediaWiki {
         */
        public function triggerJobs() {
                $jobRunRate = $this->config->get( 'JobRunRate' );
-               if ( $jobRunRate <= 0 || wfReadOnly() ) {
-                       return;
-               } elseif ( $this->getTitle()->isSpecial( 'RunJobs' ) ) {
+               if ( $this->getTitle()->isSpecial( 'RunJobs' ) ) {
                        return; // recursion guard
+               } elseif ( $jobRunRate <= 0 || wfReadOnly() ) {
+                       return;
                }
 
                if ( $jobRunRate < 1 ) {
@@ -820,16 +822,18 @@ class MediaWiki {
 
                $runJobsLogger = LoggerFactory::getInstance( 'runJobs' );
 
+               // Fall back to running the job(s) while the user waits if needed
                if ( !$this->config->get( 'RunJobsAsync' ) ) {
-                       // Fall back to running the job here while the user waits
                        $runner = new JobRunner( $runJobsLogger );
-                       $runner->run( [ 'maxJobs'  => $n ] );
+                       $runner->run( [ 'maxJobs' => $n ] );
                        return;
                }
 
+               // Do not send request if there are probably no jobs
                try {
-                       if ( !JobQueueGroup::singleton()->queuesHaveJobs( JobQueueGroup::TYPE_DEFAULT ) ) {
-                               return; // do not send request if there are probably no jobs
+                       $group = JobQueueGroup::singleton();
+                       if ( !$group->queuesHaveJobs( JobQueueGroup::TYPE_DEFAULT ) ) {
+                               return;
                        }
                } catch ( JobQueueError $e ) {
                        MWExceptionHandler::logException( $e );
@@ -842,9 +846,8 @@ class MediaWiki {
                        $query, $this->config->get( 'SecretKey' ) );
 
                $errno = $errstr = null;
-               $info = wfParseUrl( $this->config->get( 'Server' ) );
-               MediaWiki\suppressWarnings();
-               $host = $info['host'];
+               $info = wfParseUrl( $this->config->get( 'CanonicalServer' ) );
+               $host = $info ? $info['host'] : null;
                $port = 80;
                if ( isset( $info['scheme'] ) && $info['scheme'] == 'https' ) {
                        $host = "tls://" . $host;
@@ -853,47 +856,60 @@ class MediaWiki {
                if ( isset( $info['port'] ) ) {
                        $port = $info['port'];
                }
-               $sock = fsockopen(
+
+               MediaWiki\suppressWarnings();
+               $sock = $host ? fsockopen(
                        $host,
                        $port,
                        $errno,
                        $errstr,
-                       // If it takes more than 100ms to connect to ourselves there
-                       // is a problem elsewhere.
-                       0.1
-               );
+                       // If it takes more than 100ms to connect to ourselves there is a problem...
+                       0.100
+               ) : false;
                MediaWiki\restoreWarnings();
-               if ( !$sock ) {
+
+               $invokedWithSuccess = true;
+               if ( $sock ) {
+                       $special = SpecialPageFactory::getPage( 'RunJobs' );
+                       $url = $special->getPageTitle()->getCanonicalURL( $query );
+                       $req = (
+                               "POST $url HTTP/1.1\r\n" .
+                               "Host: {$info['host']}\r\n" .
+                               "Connection: Close\r\n" .
+                               "Content-Length: 0\r\n\r\n"
+                       );
+
+                       $runJobsLogger->info( "Running $n job(s) via '$url'" );
+                       // Send a cron API request to be performed in the background.
+                       // Give up if this takes too long to send (which should be rare).
+                       stream_set_timeout( $sock, 2 );
+                       $bytes = fwrite( $sock, $req );
+                       if ( $bytes !== strlen( $req ) ) {
+                               $invokedWithSuccess = false;
+                               $runJobsLogger->error( "Failed to start cron API (socket write error)" );
+                       } else {
+                               // Do not wait for the response (the script should handle client aborts).
+                               // Make sure that we don't close before that script reaches ignore_user_abort().
+                               $start = microtime( true );
+                               $status = fgets( $sock );
+                               $sec = microtime( true ) - $start;
+                               if ( !preg_match( '#^HTTP/\d\.\d 202 #', $status ) ) {
+                                       $invokedWithSuccess = false;
+                                       $runJobsLogger->error( "Failed to start cron API: received '$status' ($sec)" );
+                               }
+                       }
+                       fclose( $sock );
+               } else {
+                       $invokedWithSuccess = false;
                        $runJobsLogger->error( "Failed to start cron API (socket error $errno): $errstr" );
-                       // Fall back to running the job here while the user waits
-                       $runner = new JobRunner( $runJobsLogger );
-                       $runner->run( [ 'maxJobs'  => $n ] );
-                       return;
                }
 
-               $url = wfAppendQuery( wfScript( 'index' ), $query );
-               $req = (
-                       "POST $url HTTP/1.1\r\n" .
-                       "Host: {$info['host']}\r\n" .
-                       "Connection: Close\r\n" .
-                       "Content-Length: 0\r\n\r\n"
-               );
+               // Fall back to running the job(s) while the user waits if needed
+               if ( !$invokedWithSuccess ) {
+                       $runJobsLogger->warning( "Jobs switched to blocking; Special:RunJobs disabled" );
 
-               $runJobsLogger->info( "Running $n job(s) via '$url'" );
-               // Send a cron API request to be performed in the background.
-               // Give up if this takes too long to send (which should be rare).
-               stream_set_timeout( $sock, 1 );
-               $bytes = fwrite( $sock, $req );
-               if ( $bytes !== strlen( $req ) ) {
-                       $runJobsLogger->error( "Failed to start cron API (socket write error)" );
-               } else {
-                       // Do not wait for the response (the script should handle client aborts).
-                       // Make sure that we don't close before that script reaches ignore_user_abort().
-                       $status = fgets( $sock );
-                       if ( !preg_match( '#^HTTP/\d\.\d 202 #', $status ) ) {
-                               $runJobsLogger->error( "Failed to start cron API: received '$status'" );
-                       }
+                       $runner = new JobRunner( $runJobsLogger );
+                       $runner->run( [ 'maxJobs'  => $n ] );
                }
-               fclose( $sock );
        }
 }
index ff292cf..f621867 100644 (file)
@@ -11,10 +11,12 @@ use LBFactory;
 use LinkCache;
 use Liuggio\StatsdClient\Factory\StatsdDataFactory;
 use LoadBalancer;
+use MediaHandlerFactory;
 use MediaWiki\Linker\LinkRenderer;
 use MediaWiki\Linker\LinkRendererFactory;
 use MediaWiki\Services\SalvageableService;
 use MediaWiki\Services\ServiceContainer;
+use MediaWiki\Services\NoSuchServiceException;
 use MWException;
 use ObjectCache;
 use SearchEngine;
@@ -27,6 +29,7 @@ use WatchedItemQueryService;
 use SkinFactory;
 use TitleFormatter;
 use TitleParser;
+use VirtualRESTServiceClient;
 use MediaWiki\Interwiki\InterwikiLookup;
 
 /**
@@ -196,7 +199,14 @@ class MediaWikiServices extends ServiceContainer {
         */
        private function salvage( self $other ) {
                foreach ( $this->getServiceNames() as $name ) {
-                       $oldService = $other->peekService( $name );
+                       // The service could be new in the new instance and not registered in the
+                       // other instance (e.g. an extension that was loaded after the instantiation of
+                       // the other instance. Skip this service in this case. See T143974
+                       try {
+                               $oldService = $other->peekService( $name );
+                       } catch ( NoSuchServiceException $e ) {
+                               continue;
+                       }
 
                        if ( $oldService instanceof SalvageableService ) {
                                /** @var SalvageableService $newService */
@@ -511,6 +521,14 @@ class MediaWikiServices extends ServiceContainer {
                return $this->getService( 'WatchedItemQueryService' );
        }
 
+       /**
+        * @since 1.28
+        * @return MediaHandlerFactory
+        */
+       public function getMediaHandlerFactory() {
+               return $this->getService( 'MediaHandlerFactory' );
+       }
+
        /**
         * @since 1.28
         * @return GenderCache
@@ -562,6 +580,14 @@ class MediaWikiServices extends ServiceContainer {
                return $this->getService( 'TitleParser' );
        }
 
+       /**
+        * @since 1.28
+        * @return VirtualRESTServiceClient
+        */
+       public function getVirtualRESTServiceClient() {
+               return $this->getService( 'VirtualRESTServiceClient' );
+       }
+
        ///////////////////////////////////////////////////////////////////////////
        // NOTE: When adding a service getter here, don't forget to add a test
        // case for it in MediaWikiServicesTest::provideGetters() and in
index 441fe9e..dd1fd37 100644 (file)
@@ -33,7 +33,7 @@
  */
 class MergeHistory {
 
-       /** @const int Maximum number of revisions that can be merged at once (avoid too much slave lag) */
+       /** @const int Maximum number of revisions that can be merged at once */
        const REVISION_LIMIT = 5000;
 
        /** @var Title Page from which history will be merged */
index 2c979de..c2c954a 100644 (file)
  *
  * @code
  *     // old style:
- *     wfMsgExt( 'key', array( 'parseinline' ), 'apple' );
+ *     wfMsgExt( 'key', [ 'parseinline' ], 'apple' );
  *     // new style:
  *     wfMessage( 'key', 'apple' )->parse();
  * @endcode
  * Places where HTML cannot be used. {{-transformation is done.
  * @code
  *     // old style:
- *     wfMsgExt( 'key', array( 'parsemag' ), 'apple', 'pear' );
+ *     wfMsgExt( 'key', [ 'parsemag' ], 'apple', 'pear' );
  *     // new style:
  *     wfMessage( 'key', 'apple', 'pear' )->text();
  * @endcode
index 708dea1..d17f234 100644 (file)
@@ -256,7 +256,7 @@ class MovePage {
                $pageid = $this->oldTitle->getArticleID( Title::GAID_FOR_UPDATE );
                $protected = $this->oldTitle->isProtected();
 
-               // Do the actual move
+               // Do the actual move; if this fails, it will throw an MWException(!)
                $nullRevision = $this->moveToInternal( $user, $this->newTitle, $reason, $createRedirect );
 
                // Refresh the sortkey for this row.  Be careful to avoid resetting
@@ -297,19 +297,25 @@ class MovePage {
 
                if ( $protected ) {
                        # Protect the redirect title as the title used to be...
-                       $dbw->insertSelect( 'page_restrictions', 'page_restrictions',
-                               [
-                                       'pr_page' => $redirid,
-                                       'pr_type' => 'pr_type',
-                                       'pr_level' => 'pr_level',
-                                       'pr_cascade' => 'pr_cascade',
-                                       'pr_user' => 'pr_user',
-                                       'pr_expiry' => 'pr_expiry'
-                               ],
+                       $res = $dbw->select(
+                               'page_restrictions',
+                               '*',
                                [ 'pr_page' => $pageid ],
                                __METHOD__,
-                               [ 'IGNORE' ]
+                               'FOR UPDATE'
                        );
+                       $rowsInsert = [];
+                       foreach ( $res as $row ) {
+                               $rowsInsert[] = [
+                                       'pr_page' => $redirid,
+                                       'pr_type' => $row->pr_type,
+                                       'pr_level' => $row->pr_level,
+                                       'pr_cascade' => $row->pr_cascade,
+                                       'pr_user' => $row->pr_user,
+                                       'pr_expiry' => $row->pr_expiry
+                               ];
+                       }
+                       $dbw->insert( 'page_restrictions', $rowsInsert, __METHOD__, [ 'IGNORE' ] );
 
                        // Build comment for log
                        $comment = wfMessage(
@@ -392,11 +398,16 @@ class MovePage {
                        $reason,
                        $nullRevision
                ];
-               $dbw->onTransactionIdle( function () use ( $params, $dbw ) {
-                       // Keep each single hook handler atomic
-                       $dbw->setFlag( DBO_TRX ); // flag is automatically reset by DB layer
-                       Hooks::run( 'TitleMoveComplete', $params );
-               } );
+               // Keep each single hook handler atomic
+               DeferredUpdates::addUpdate(
+                       new AtomicSectionUpdate(
+                               $dbw,
+                               __METHOD__,
+                               function () use ( $params ) {
+                                       Hooks::run( 'TitleMoveComplete', $params );
+                               }
+                       )
+               );
 
                return Status::newGood();
        }
@@ -407,7 +418,7 @@ class MovePage {
         *
         * @fixme This was basically directly moved from Title, it should be split into smaller functions
         * @param User $user the User doing the move
-        * @param Title $nt The page to move to, which should be a redirect or nonexistent
+        * @param Title $nt The page to move to, which should be a redirect or non-existent
         * @param string $reason The reason for the move
         * @param bool $createRedirect Whether to leave a redirect at the old title. Does not check
         *   if the user has the suppressredirect right
@@ -425,6 +436,29 @@ class MovePage {
                        $logType = 'move';
                }
 
+               if ( $moveOverRedirect ) {
+                       $overwriteMessage = wfMessage(
+                                       'delete_and_move_reason',
+                                       $this->oldTitle->getPrefixedText()
+                               )->text();
+                       $newpage = WikiPage::factory( $nt );
+                       $errs = [];
+                       $status = $newpage->doDeleteArticleReal(
+                               $overwriteMessage,
+                               /* $suppress */ false,
+                               $nt->getArticleId(),
+                               /* $commit */ false,
+                               $errs,
+                               $user
+                       );
+
+                       if ( !$status->isGood() ) {
+                               throw new MWException( 'Failed to delete page-move revision: ' . $status );
+                       }
+
+                       $nt->resetArticleID( false );
+               }
+
                if ( $createRedirect ) {
                        if ( $this->oldTitle->getNamespace() == NS_CATEGORY
                                && !wfMessage( 'category-move-redirect-override' )->inContentLanguage()->isDisabled()
@@ -479,19 +513,6 @@ class MovePage {
 
                $newpage = WikiPage::factory( $nt );
 
-               if ( $moveOverRedirect ) {
-                       $newid = $nt->getArticleID();
-                       $newcontent = $newpage->getContent();
-
-                       # Delete the old redirect. We don't save it to history since
-                       # by definition if we've got here it's rather uninteresting.
-                       # We have to remove it so that the next step doesn't trigger
-                       # a conflict on the unique namespace+title index...
-                       $dbw->delete( 'page', [ 'page_id' => $newid ], __METHOD__ );
-
-                       $newpage->doDeleteUpdates( $newid, $newcontent );
-               }
-
                # Save a null revision in the page's history notifying of the move
                $nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true, $user );
                if ( !is_object( $nullRevision ) ) {
@@ -537,9 +558,7 @@ class MovePage {
                        );
                }
 
-               if ( !$moveOverRedirect ) {
-                       WikiPage::onArticleCreate( $nt );
-               }
+               WikiPage::onArticleCreate( $nt );
 
                # Recreate the redirect, this time in the other direction.
                if ( $redirectContent ) {
index 15b70c8..9b2d8da 100644 (file)
@@ -23,6 +23,7 @@
 use MediaWiki\Logger\LoggerFactory;
 use MediaWiki\Session\SessionManager;
 use WrappedString\WrappedString;
+use WrappedString\WrappedStringList;
 
 /**
  * This class should be covered by a general architecture document which does
@@ -153,6 +154,18 @@ class OutputPage extends ContextSource {
        /** @var ResourceLoader */
        protected $mResourceLoader;
 
+       /** @var ResourceLoaderClientHtml */
+       private $rlClient;
+
+       /** @var ResourceLoaderContext */
+       private $rlClientContext;
+
+       /** @var string */
+       private $rlUserModuleState;
+
+       /** @var array */
+       private $rlExemptStyleModules;
+
        /** @var array */
        protected $mJsConfigVars = [];
 
@@ -250,11 +263,6 @@ class OutputPage extends ContextSource {
         */
        protected $styles = [];
 
-       /**
-        * Whether jQuery is already handled.
-        */
-       protected $mJQueryDone = false;
-
        private $mIndexPolicy = 'index';
        private $mFollowPolicy = 'follow';
        private $mVaryHeader = [
@@ -294,6 +302,9 @@ class OutputPage extends ContextSource {
         */
        private $copyrightUrl;
 
+       /** @var array Profiling data */
+       private $limitReportData = [];
+
        /**
         * Constructor for OutputPage. This should not be called directly.
         * Instead a new RequestContext should be created and it will implicitly create
@@ -502,7 +513,7 @@ class OutputPage extends ContextSource {
         * Add a self-contained script tag with the given contents
         * Internal use only. Use OutputPage::addModules() if possible.
         *
-        * @param string $script JavaScript text, no "<script>" tags
+        * @param string $script JavaScript text, no script tags
         */
        public function addInlineScript( $script ) {
                $this->mScripts .= Html::inlineScript( $script );
@@ -542,10 +553,12 @@ class OutputPage extends ContextSource {
         * @param string $param
         * @return array Array of module names
         */
-       public function getModules( $filter = false, $position = null, $param = 'mModules' ) {
+       public function getModules( $filter = false, $position = null, $param = 'mModules',
+               $type = ResourceLoaderModule::TYPE_COMBINED
+       ) {
                $modules = array_values( array_unique( $this->$param ) );
                return $filter
-                       ? $this->filterModules( $modules, $position )
+                       ? $this->filterModules( $modules, $position, $type )
                        : $modules;
        }
 
@@ -565,11 +578,12 @@ class OutputPage extends ContextSource {
         *
         * @param bool $filter
         * @param string|null $position
-        *
         * @return array Array of module names
         */
        public function getModuleScripts( $filter = false, $position = null ) {
-               return $this->getModules( $filter, $position, 'mModuleScripts' );
+               return $this->getModules( $filter, $position, 'mModuleScripts',
+                       ResourceLoaderModule::TYPE_SCRIPTS
+               );
        }
 
        /**
@@ -588,11 +602,12 @@ class OutputPage extends ContextSource {
         *
         * @param bool $filter
         * @param string|null $position
-        *
         * @return array Array of module names
         */
        public function getModuleStyles( $filter = false, $position = null ) {
-               return $this->getModules( $filter, $position, 'mModuleStyles' );
+               return $this->getModules( $filter, $position, 'mModuleStyles',
+                       ResourceLoaderModule::TYPE_STYLES
+               );
        }
 
        /**
@@ -608,29 +623,6 @@ class OutputPage extends ContextSource {
                $this->mModuleStyles = array_merge( $this->mModuleStyles, (array)$modules );
        }
 
-       /**
-        * Get the list of module messages to include on this page
-        *
-        * @deprecated since 1.26 Obsolete
-        * @param bool $filter
-        * @param string|null $position
-        * @return array Array of module names
-        */
-       public function getModuleMessages( $filter = false, $position = null ) {
-               wfDeprecated( __METHOD__, '1.26' );
-               return [];
-       }
-
-       /**
-        * Load messages of one or more ResourceLoader modules.
-        *
-        * @deprecated since 1.26 Use addModules() instead
-        * @param string|array $modules Module name (string) or array of module names
-        */
-       public function addModuleMessages( $modules ) {
-               wfDeprecated( __METHOD__, '1.26' );
-       }
-
        /**
         * @return null|string ResourceLoader target
         */
@@ -657,7 +649,7 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * Add or replace an header item to the output
+        * Add or replace a head item to the output
         *
         * Whenever possible, use more specific options like ResourceLoader modules,
         * OutputPage::addLink(), OutputPage::addMetaLink() and OutputPage::addFeedLink()
@@ -672,6 +664,16 @@ class OutputPage extends ContextSource {
                $this->mHeadItems[$name] = $value;
        }
 
+       /**
+        * Add one or more head items to the output
+        *
+        * @since 1.28
+        * @param string|string[] $value Raw HTML
+        */
+       public function addHeadItems( $values ) {
+               $this->mHeadItems = array_merge( $this->mHeadItems, (array)$values );
+       }
+
        /**
         * Check if the header item $name is already set
         *
@@ -1263,7 +1265,7 @@ class OutputPage extends ContextSource {
                $lb->setArray( $arr );
 
                # Fetch existence plus the hiddencat property
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $fields = array_merge(
                        LinkCache::getSelectFields(),
                        [ 'page_namespace', 'page_title', 'pp_value' ]
@@ -1781,11 +1783,14 @@ class OutputPage extends ContextSource {
                        }
                }
 
-               // enable OOUI if requested via ParserOutput
+               // Enable OOUI if requested via ParserOutput
                if ( $parserOutput->getEnableOOUI() ) {
                        $this->enableOOUI();
                }
 
+               // Include profiling data
+               $this->setLimitReportData( $parserOutput->getLimitReportData() );
+
                // Link flags are ignored for now, but may in the future be
                // used to mark individual language links.
                $linkFlags = [];
@@ -2276,7 +2281,7 @@ class OutputPage extends ContextSource {
                        // add skin specific modules
                        $modules = $sk->getDefaultModules();
 
-                       // Enforce various default modules for all skins
+                       // Enforce various default modules for all pages and all skins
                        $coreModules = [
                                // Keep this list as small as possible
                                'site',
@@ -2530,7 +2535,7 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * Show a warning about slave lag
+        * Show a warning about replica DB lag
         *
         * If the lag is higher than $wgSlaveLagCritical seconds,
         * then the warning is a bit more obvious. If the lag is
@@ -2541,6 +2546,7 @@ class OutputPage extends ContextSource {
        public function showLagWarning( $lag ) {
                $config = $this->getConfig();
                if ( $lag >= $config->get( 'SlaveLagWarning' ) ) {
+                       $lag = floor( $lag ); // floor to avoid nano seconds to display
                        $message = $lag < $config->get( 'SlaveLagCritical' )
                                ? 'lag-warn-normal'
                                : 'lag-warn-high';
@@ -2622,6 +2628,112 @@ class OutputPage extends ContextSource {
                $this->addReturnTo( $titleObj, wfCgiToArray( $returntoquery ) );
        }
 
+       private function getRlClientContext() {
+               if ( !$this->rlClientContext ) {
+                       $query = ResourceLoader::makeLoaderQuery(
+                               [], // modules; not relevant
+                               $this->getLanguage()->getCode(),
+                               $this->getSkin()->getSkinName(),
+                               $this->getUser()->isLoggedIn() ? $this->getUser()->getName() : null,
+                               null, // version; not relevant
+                               ResourceLoader::inDebugMode(),
+                               null, // only; not relevant
+                               $this->isPrintable(),
+                               $this->getRequest()->getBool( 'handheld' )
+                       );
+                       $this->rlClientContext = new ResourceLoaderContext(
+                               $this->getResourceLoader(),
+                               new FauxRequest( $query )
+                       );
+               }
+               return $this->rlClientContext;
+       }
+
+       /**
+        * Call this to freeze the module queue and JS config and create a formatter.
+        *
+        * Depending on the Skin, this may get lazy-initialised in either headElement() or
+        * getBottomScripts(). See SkinTemplate::prepareQuickTemplate(). Calling this too early may
+        * cause unexpected side-effects since disallowUserJs() may be called at any time to change
+        * the module filters retroactively. Skins and extension hooks may also add modules until very
+        * late in the request lifecycle.
+        *
+        * @return ResourceLoaderClientHtml
+        */
+       public function getRlClient() {
+               if ( !$this->rlClient ) {
+                       $context = $this->getRlClientContext();
+                       $rl = $this->getResourceLoader();
+                       $this->addModules( [
+                               'user.options',
+                               'user.tokens',
+                       ] );
+                       $this->addModuleStyles( [
+                               'site.styles',
+                               'noscript',
+                               'user.styles',
+                               'user.cssprefs',
+                       ] );
+                       $this->getSkin()->setupSkinUserCss( $this );
+
+                       // Prepare exempt modules for buildExemptModules()
+                       $exemptGroups = [ 'site' => [], 'noscript' => [], 'private' => [], 'user' => [] ];
+                       $exemptStates = [];
+                       $moduleStyles = $this->getModuleStyles( /*filter*/ true );
+
+                       // Batch preload getTitleInfo for isKnownEmpty() calls below
+                       $exemptModules = array_filter( $moduleStyles,
+                               function ( $name ) use ( $rl, &$exemptGroups ) {
+                                       $module = $rl->getModule( $name );
+                                       return $module && isset( $exemptGroups[ $module->getGroup() ] );
+                               }
+                       );
+                       ResourceLoaderWikiModule::preloadTitleInfo(
+                               $context, wfGetDB( DB_REPLICA ), $exemptModules );
+
+                       // Filter out modules handled by buildExemptModules()
+                       $moduleStyles = array_filter( $moduleStyles,
+                               function ( $name ) use ( $rl, $context, &$exemptGroups, &$exemptStates ) {
+                                       $module = $rl->getModule( $name );
+                                       if ( $module ) {
+                                               if ( $name === 'user.styles' && $this->isUserCssPreview() ) {
+                                                       $exemptStates[$name] = 'ready';
+                                                       // Special case in buildExemptModules()
+                                                       return false;
+                                               }
+                                               $group = $module->getGroup();
+                                               if ( isset( $exemptGroups[$group] ) ) {
+                                                       $exemptStates[$name] = 'ready';
+                                                       if ( !$module->isKnownEmpty( $context ) ) {
+                                                               // E.g. Don't output empty <styles>
+                                                               $exemptGroups[$group][] = $name;
+                                                       }
+                                                       return false;
+                                               }
+                                       }
+                                       return true;
+                               }
+                       );
+                       $this->rlExemptStyleModules = $exemptGroups;
+
+                       // Manually handled by getBottomScripts()
+                       $userModule = $rl->getModule( 'user' );
+                       $userState = $userModule->isKnownEmpty( $context ) && !$this->isUserJsPreview()
+                               ? 'ready'
+                               : 'loading';
+                       $this->rlUserModuleState = $exemptStates['user'] = $userState;
+
+                       $rlClient = new ResourceLoaderClientHtml( $context, $this->getTarget() );
+                       $rlClient->setConfig( $this->getJSVars() );
+                       $rlClient->setModules( $this->getModules( /*filter*/ true ) );
+                       $rlClient->setModuleStyles( $moduleStyles );
+                       $rlClient->setModuleScripts( $this->getModuleScripts( /*filter*/ true ) );
+                       $rlClient->setExemptStates( $exemptStates );
+                       $this->rlClient = $rlClient;
+               }
+               return $this->rlClient;
+       }
+
        /**
         * @param Skin $sk The given Skin
         * @param bool $includeStyle Unused
@@ -2633,18 +2745,17 @@ class OutputPage extends ContextSource {
                $userdir = $this->getLanguage()->getDir();
                $sitedir = $wgContLang->getDir();
 
-               $ret = Html::htmlHeader( $sk->getHtmlElementAttributes() );
+               $pieces = [];
+               $pieces[] = Html::htmlHeader( Sanitizer::mergeAttributes(
+                       $this->getRlClient()->getDocumentAttributes(),
+                       $sk->getHtmlElementAttributes()
+               ) );
+               $pieces[] = Html::openElement( 'head' );
 
                if ( $this->getHTMLTitle() == '' ) {
                        $this->setHTMLTitle( $this->msg( 'pagetitle', $this->getPageTitle() )->inContentLanguage() );
                }
 
-               $openHead = Html::openElement( 'head' );
-               if ( $openHead ) {
-                       # Don't bother with the newline if $head == ''
-                       $ret .= "$openHead\n";
-               }
-
                if ( !Html::isXmlMimeType( $this->getConfig()->get( 'MimeType' ) ) ) {
                        // Add <meta charset="UTF-8">
                        // This should be before <title> since it defines the charset used by
@@ -2654,26 +2765,15 @@ class OutputPage extends ContextSource {
                        // Our XML declaration is output by Html::htmlHeader.
                        // http://www.whatwg.org/html/semantics.html#attr-meta-http-equiv-content-type
                        // http://www.whatwg.org/html/semantics.html#charset
-                       $ret .= Html::element( 'meta', [ 'charset' => 'UTF-8' ] ) . "\n";
-               }
-
-               $ret .= Html::element( 'title', null, $this->getHTMLTitle() ) . "\n";
-               $ret .= $this->getInlineHeadScripts() . "\n";
-               $ret .= $this->buildCssLinks() . "\n";
-               $ret .= $this->getExternalHeadScripts() . "\n";
-
-               foreach ( $this->getHeadLinksArray() as $item ) {
-                       $ret .= $item . "\n";
+                       $pieces[] = Html::element( 'meta', [ 'charset' => 'UTF-8' ] );
                }
 
-               foreach ( $this->mHeadItems as $item ) {
-                       $ret .= $item . "\n";
-               }
-
-               $closeHead = Html::closeElement( 'head' );
-               if ( $closeHead ) {
-                       $ret .= "$closeHead\n";
-               }
+               $pieces[] = Html::element( 'title', null, $this->getHTMLTitle() );
+               $pieces[] = $this->getRlClient()->getHeadHtml();
+               $pieces[] = $this->buildExemptModules();
+               $pieces = array_merge( $pieces, array_values( $this->getHeadLinksArray() ) );
+               $pieces = array_merge( $pieces, array_values( $this->mHeadItems ) );
+               $pieces[] = Html::closeElement( 'head' );
 
                $bodyClasses = [];
                $bodyClasses[] = 'mediawiki';
@@ -2687,6 +2787,11 @@ class OutputPage extends ContextSource {
                        $bodyClasses[] = 'capitalize-all-nouns';
                }
 
+               // Parser feature migration class
+               // The idea is that this will eventually be removed, after the wikitext
+               // which requires it is cleaned up.
+               $bodyClasses[] = 'mw-hide-empty-elt';
+
                $bodyClasses[] = $sk->getPageClasses( $this->getTitle() );
                $bodyClasses[] = 'skin-' . Sanitizer::escapeClass( $sk->getSkinName() );
                $bodyClasses[] =
@@ -2701,9 +2806,9 @@ class OutputPage extends ContextSource {
                $sk->addToBodyAttributes( $this, $bodyAttrs );
                Hooks::run( 'OutputPageBodyAttributes', [ $this, $sk, &$bodyAttrs ] );
 
-               $ret .= Html::openElement( 'body', $bodyAttrs ) . "\n";
+               $pieces[] = Html::openElement( 'body', $bodyAttrs );
 
-               return $ret;
+               return self::combineWrappedStrings( $pieces );
        }
 
        /**
@@ -2722,373 +2827,106 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * Construct neccecary html and loader preset states to load modules on a page.
-        *
-        * Use getHtmlFromLoaderLinks() to convert this array to HTML.
+        * Explicily load or embed modules on a page.
         *
         * @param array|string $modules One or more module names
         * @param string $only ResourceLoaderModule TYPE_ class constant
         * @param array $extraQuery [optional] Array with extra query parameters for the request
-        * @return array A list of HTML strings and array of client loader preset states
+        * @return string|WrappedStringList HTML
         */
        public function makeResourceLoaderLink( $modules, $only, array $extraQuery = [] ) {
-               $modules = (array)$modules;
-
-               $links = [
-                       // List of html strings
-                       'html' => [],
-                       // Associative array of module names and their states
-                       'states' => [],
-               ];
-
-               if ( !count( $modules ) ) {
-                       return $links;
-               }
-
-               if ( count( $modules ) > 1 ) {
-                       // Remove duplicate module requests
-                       $modules = array_unique( $modules );
-                       // Sort module names so requests are more uniform
-                       sort( $modules );
-
-                       if ( ResourceLoader::inDebugMode() ) {
-                               // Recursively call us for every item
-                               foreach ( $modules as $name ) {
-                                       $link = $this->makeResourceLoaderLink( $name, $only, $extraQuery );
-                                       $links['html'] = array_merge( $links['html'], $link['html'] );
-                                       $links['states'] += $link['states'];
-                               }
-                               return $links;
-                       }
-               }
-
-               if ( !is_null( $this->mTarget ) ) {
-                       $extraQuery['target'] = $this->mTarget;
-               }
-
-               // Create keyed-by-source and then keyed-by-group list of module objects from modules list
-               $sortedModules = [];
-               $resourceLoader = $this->getResourceLoader();
-               foreach ( $modules as $name ) {
-                       $module = $resourceLoader->getModule( $name );
-                       # Check that we're allowed to include this module on this page
-                       if ( !$module
-                               || ( $module->getOrigin() > $this->getAllowedModules( ResourceLoaderModule::TYPE_SCRIPTS )
-                                       && $only == ResourceLoaderModule::TYPE_SCRIPTS )
-                               || ( $module->getOrigin() > $this->getAllowedModules( ResourceLoaderModule::TYPE_STYLES )
-                                       && $only == ResourceLoaderModule::TYPE_STYLES )
-                               || ( $module->getOrigin() > $this->getAllowedModules( ResourceLoaderModule::TYPE_COMBINED )
-                                       && $only == ResourceLoaderModule::TYPE_COMBINED )
-                               || ( $this->mTarget && !in_array( $this->mTarget, $module->getTargets() ) )
-                       ) {
-                               continue;
-                       }
-
-                       $sortedModules[$module->getSource()][$module->getGroup()][$name] = $module;
-               }
-
-               foreach ( $sortedModules as $source => $groups ) {
-                       foreach ( $groups as $group => $grpModules ) {
-                               // Special handling for user-specific groups
-                               $user = null;
-                               if ( ( $group === 'user' || $group === 'private' ) && $this->getUser()->isLoggedIn() ) {
-                                       $user = $this->getUser()->getName();
-                               }
-
-                               // Create a fake request based on the one we are about to make so modules return
-                               // correct timestamp and emptiness data
-                               $query = ResourceLoader::makeLoaderQuery(
-                                       [], // modules; not determined yet
-                                       $this->getLanguage()->getCode(),
-                                       $this->getSkin()->getSkinName(),
-                                       $user,
-                                       null, // version; not determined yet
-                                       ResourceLoader::inDebugMode(),
-                                       $only === ResourceLoaderModule::TYPE_COMBINED ? null : $only,
-                                       $this->isPrintable(),
-                                       $this->getRequest()->getBool( 'handheld' ),
-                                       $extraQuery
-                               );
-                               $context = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) );
-
-                               // Extract modules that know they're empty and see if we have one or more
-                               // raw modules
-                               $isRaw = false;
-                               foreach ( $grpModules as $key => $module ) {
-                                       // Inline empty modules: since they're empty, just mark them as 'ready' (bug 46857)
-                                       // If we're only getting the styles, we don't need to do anything for empty modules.
-                                       if ( $module->isKnownEmpty( $context ) ) {
-                                               unset( $grpModules[$key] );
-                                               if ( $only !== ResourceLoaderModule::TYPE_STYLES ) {
-                                                       $links['states'][$key] = 'ready';
-                                               }
-                                       }
-
-                                       $isRaw |= $module->isRaw();
-                               }
-
-                               // If there are no non-empty modules, skip this group
-                               if ( count( $grpModules ) === 0 ) {
-                                       continue;
-                               }
-
-                               // Inline private modules. These can't be loaded through load.php for security
-                               // reasons, see bug 34907. Note that these modules should be loaded from
-                               // getExternalHeadScripts() before the first loader call. Otherwise other modules can't
-                               // properly use them as dependencies (bug 30914)
-                               if ( $group === 'private' ) {
-                                       if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
-                                               $links['html'][] = Html::inlineStyle(
-                                                       $resourceLoader->makeModuleResponse( $context, $grpModules )
-                                               );
-                                       } else {
-                                               $links['html'][] = ResourceLoader::makeInlineScript(
-                                                       $resourceLoader->makeModuleResponse( $context, $grpModules )
-                                               );
-                                       }
-                                       continue;
-                               }
-
-                               // 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 (bug 27564) because anons get that too
-                               // and we shouldn't be putting timestamps in CDN-cached HTML
-                               $version = null;
-                               if ( $group === 'user' ) {
-                                       $query['version'] = $resourceLoader->getCombinedVersion( $context, array_keys( $grpModules ) );
-                               }
-
-                               $query['modules'] = ResourceLoader::makePackedModulesString( array_keys( $grpModules ) );
-                               $moduleContext = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) );
-                               $url = $resourceLoader->createLoaderURL( $source, $moduleContext, $extraQuery );
-
-                               // Automatically select style/script elements
-                               if ( $only === ResourceLoaderModule::TYPE_STYLES ) {
-                                       $link = Html::linkedStyle( $url );
-                               } else {
-                                       if ( $context->getRaw() || $isRaw ) {
-                                               // Startup module can't load itself, needs to use <script> instead of mw.loader.load
-                                               $link = Html::element( 'script', [
-                                                       // In SpecialJavaScriptTest, QUnit must load synchronous
-                                                       'async' => !isset( $extraQuery['sync'] ),
-                                                       'src' => $url
-                                               ] );
-                                       } else {
-                                               $link = ResourceLoader::makeInlineScript(
-                                                       Xml::encodeJsCall( 'mw.loader.load', [ $url ] )
-                                               );
-                                       }
-
-                                       // For modules requested directly in the html via <script> or mw.loader.load
-                                       // tell mw.loader they are being loading to prevent duplicate requests.
-                                       foreach ( $grpModules as $key => $module ) {
-                                               // Don't output state=loading for the startup module.
-                                               if ( $key !== 'startup' ) {
-                                                       $links['states'][$key] = 'loading';
-                                               }
-                                       }
-                               }
-
-                               if ( $group == 'noscript' ) {
-                                       $links['html'][] = Html::rawElement( 'noscript', [], $link );
-                               } else {
-                                       $links['html'][] = $link;
-                               }
-                       }
-               }
-
-               return $links;
+               // Apply 'target' and 'origin' filters
+               $modules = $this->filterModules( (array)$modules, null, $only );
+
+               return ResourceLoaderClientHtml::makeLoad(
+                       $this->getRlClientContext(),
+                       $modules,
+                       $only,
+                       $extraQuery
+               );
        }
 
        /**
-        * Build html output from an array of links from makeResourceLoaderLink.
-        * @param array $links
-        * @return string HTML
+        * Combine WrappedString chunks and filter out empty ones
+        *
+        * @param array $chunks
+        * @return string|WrappedStringList HTML
         */
-       protected static function getHtmlFromLoaderLinks( array $links ) {
-               $html = [];
-               $states = [];
-               foreach ( $links as $link ) {
-                       if ( !is_array( $link ) ) {
-                               $html[] = $link;
-                       } else {
-                               $html = array_merge( $html, $link['html'] );
-                               $states += $link['states'];
-                       }
-               }
+       protected static function combineWrappedStrings( array $chunks ) {
                // Filter out empty values
-               $html = array_filter( $html, 'strlen' );
-
-               if ( count( $states ) ) {
-                       array_unshift( $html, ResourceLoader::makeInlineScript(
-                               ResourceLoader::makeLoaderStateScript( $states )
-                       ) );
-               }
-
-               return WrappedString::join( "\n", $html );
+               $chunks = array_filter( $chunks, 'strlen' );
+               return WrappedString::join( "\n", $chunks );
        }
 
-       /**
-        * JS stuff to put in the "<head>". This is the startup module, config
-        * vars and modules marked with position 'top'
-        *
-        * @return string HTML fragment
-        */
-       function getHeadScripts() {
-               return $this->getInlineHeadScripts() . $this->getExternalHeadScripts();
+       private function isUserJsPreview() {
+               return $this->getConfig()->get( 'AllowUserJs' )
+                       && $this->getTitle()
+                       && $this->getTitle()->isJsSubpage()
+                       && $this->userCanPreview();
        }
 
-       /**
-        * <script src="..."> tags for "<head>". This is the startup module
-        * and other modules marked with position 'top'.
-        *
-        * @return string HTML fragment
-        */
-       function getExternalHeadScripts() {
-               $links = [];
-
-               // Startup - this provides the client with the module
-               // manifest and loads jquery and mediawiki base modules
-               $links[] = $this->makeResourceLoaderLink( 'startup', ResourceLoaderModule::TYPE_SCRIPTS );
-
-               return self::getHtmlFromLoaderLinks( $links );
+       private function isUserCssPreview() {
+               return $this->getConfig()->get( 'AllowUserCss' )
+                       && $this->getTitle()
+                       && $this->getTitle()->isCssSubpage()
+                       && $this->userCanPreview();
        }
 
        /**
-        * <script>...</script> tags to put in "<head>".
+        * JS stuff to put at the bottom of the `<body>`. These are modules with position 'bottom',
+        * legacy scripts ($this->mScripts), and user JS.
         *
-        * @return string HTML fragment
+        * @return string|WrappedStringList HTML
         */
-       function getInlineHeadScripts() {
-               $links = [];
+       public function getBottomScripts() {
+               $chunks = [];
+               $chunks[] = $this->getRlClient()->getBodyHtml();
 
-               // Client profile classes for <html>. Allows for easy hiding/showing of UI components.
-               // Must be done synchronously on every page to avoid flashes of wrong content.
-               // Note: This class distinguishes MediaWiki-supported JavaScript from the rest.
-               // The "rest" includes browsers that support JavaScript but not supported by our runtime.
-               // For the performance benefit of the majority, this is added unconditionally here and is
-               // then fixed up by the startup module for unsupported browsers.
-               $links[] = Html::inlineScript(
-                       'document.documentElement.className = document.documentElement.className'
-                       . '.replace( /(^|\s)client-nojs(\s|$)/, "$1client-js$2" );'
-               );
+               // Legacy non-ResourceLoader scripts
+               $chunks[] = $this->mScripts;
 
-               // Load config before anything else
-               $links[] = ResourceLoader::makeInlineScript(
-                       ResourceLoader::makeConfigSetScript( $this->getJSVars() )
-               );
-
-               // Load embeddable private modules before any loader links
-               // This needs to be TYPE_COMBINED so these modules are properly wrapped
-               // in mw.loader.implement() calls and deferred until mw.user is available
-               $embedScripts = [ 'user.options' ];
-               $links[] = $this->makeResourceLoaderLink(
-                       $embedScripts,
-                       ResourceLoaderModule::TYPE_COMBINED
-               );
-               // Separate user.tokens as otherwise caching will be allowed (T84960)
-               $links[] = $this->makeResourceLoaderLink(
-                       'user.tokens',
-                       ResourceLoaderModule::TYPE_COMBINED
-               );
-
-               // Modules requests - let the client calculate dependencies and batch requests as it likes
-               // Only load modules that have marked themselves for loading at the top
-               $modules = $this->getModules( true, 'top' );
-               if ( $modules ) {
-                       $links[] = ResourceLoader::makeInlineScript(
-                               Xml::encodeJsCall( 'mw.loader.load', [ $modules ] )
-                       );
+               // Exempt 'user' module
+               // - May need excludepages for live preview. (T28283)
+               // - Must use TYPE_COMBINED so its response is handled by mw.loader.implement() which
+               //   ensures execution is scheduled after the "site" module.
+               // - Don't load if module state is already resolved as "ready".
+               if ( $this->rlUserModuleState === 'loading' ) {
+                       if ( $this->isUserJsPreview() ) {
+                               $chunks[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_COMBINED,
+                                       [ 'excludepage' => $this->getTitle()->getPrefixedDBkey() ]
+                               );
+                               $chunks[] = ResourceLoader::makeInlineScript(
+                                       Xml::encodeJsCall( 'mw.loader.using', [
+                                               [ 'user', 'site' ],
+                                               new XmlJsCode(
+                                                       'function () {'
+                                                               . Xml::encodeJsCall( '$.globalEval', [
+                                                                       $this->getRequest()->getText( 'wpTextbox1' )
+                                                               ] )
+                                                               . '}'
+                                               )
+                                       ] )
+                               );
+                               // FIXME: If the user is previewing, say, ./vector.js, his ./common.js will be loaded
+                               // asynchronously and may arrive *after* the inline script here. So the previewed code
+                               // may execute before ./common.js runs. Normally, ./common.js runs before ./vector.js.
+                               // Similarly, when previewing ./common.js and the user module does arrive first,
+                               // it will arrive without common.js and the inline script runs after.
+                               // Thus running common after the excluded subpage.
+                       } else {
+                               // Load normally
+                               $chunks[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_COMBINED );
+                       }
                }
 
-               // "Scripts only" modules marked for top inclusion
-               $links[] = $this->makeResourceLoaderLink(
-                       $this->getModuleScripts( true, 'top' ),
-                       ResourceLoaderModule::TYPE_SCRIPTS
+               $chunks[] = ResourceLoader::makeInlineScript(
+                       ResourceLoader::makeConfigSetScript(
+                               [ 'wgPageParseReport' => $this->limitReportData ],
+                               true
+                       )
                );
 
-               return self::getHtmlFromLoaderLinks( $links );
-       }
-
-       /**
-        * JS stuff to put at the 'bottom', which goes at the bottom of the `<body>`.
-        * These are modules marked with position 'bottom', legacy scripts ($this->mScripts),
-        * site JS, and user JS.
-        *
-        * @param bool $unused Previously used to let this method change its output based
-        *  on whether it was called by getExternalHeadScripts() or getBottomScripts().
-        * @return string
-        */
-       function getScriptsForBottomQueue( $unused = null ) {
-               // Scripts "only" requests marked for bottom inclusion
-               // If we're in the <head>, use load() calls rather than <script src="..."> tags
-               $links = [];
-
-               $links[] = $this->makeResourceLoaderLink( $this->getModuleScripts( true, 'bottom' ),
-                       ResourceLoaderModule::TYPE_SCRIPTS
-               );
-
-               // Modules requests - let the client calculate dependencies and batch requests as it likes
-               // Only load modules that have marked themselves for loading at the bottom
-               $modules = $this->getModules( true, 'bottom' );
-               if ( $modules ) {
-                       $links[] = ResourceLoader::makeInlineScript(
-                               Xml::encodeJsCall( 'mw.loader.load', [ $modules ] )
-                       );
-               }
-
-               // Legacy Scripts
-               $links[] = $this->mScripts;
-
-               // Add user JS if enabled
-               // This must use TYPE_COMBINED instead of only=scripts so that its request is handled by
-               // mw.loader.implement() which ensures that execution is scheduled after the "site" module.
-               if ( $this->getConfig()->get( 'AllowUserJs' )
-                       && $this->getUser()->isLoggedIn()
-                       && $this->getTitle()
-                       && $this->getTitle()->isJsSubpage()
-                       && $this->userCanPreview()
-               ) {
-                       // We're on a preview of a JS subpage. Exclude this page from the user module (T28283)
-                       // and include the draft contents as a raw script instead.
-                       $links[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_COMBINED,
-                               [ 'excludepage' => $this->getTitle()->getPrefixedDBkey() ]
-                       );
-                       // Load the previewed JS
-                       $links[] = ResourceLoader::makeInlineScript(
-                               Xml::encodeJsCall( 'mw.loader.using', [
-                                       [ 'user', 'site' ],
-                                       new XmlJsCode(
-                                               'function () {'
-                                                       . Xml::encodeJsCall( '$.globalEval', [
-                                                               $this->getRequest()->getText( 'wpTextbox1' )
-                                                       ] )
-                                                       . '}'
-                                       )
-                               ] )
-                       );
-
-                       // FIXME: If the user is previewing, say, ./vector.js, his ./common.js will be loaded
-                       // asynchronously and may arrive *after* the inline script here. So the previewed code
-                       // may execute before ./common.js runs. Normally, ./common.js runs before ./vector.js.
-                       // Similarly, when previewing ./common.js and the user module does arrive first,
-                       // it will arrive without common.js and the inline script runs after.
-                       // Thus running common after the excluded subpage.
-               } else {
-                       // Include the user module normally, i.e., raw to avoid it being wrapped in a closure.
-                       $links[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_COMBINED );
-               }
-
-               return self::getHtmlFromLoaderLinks( $links );
-       }
-
-       /**
-        * JS stuff to put at the bottom of the "<body>"
-        * @return string
-        */
-       function getBottomScripts() {
-               return $this->getScriptsForBottomQueue();
+               return self::combineWrappedStrings( $chunks );
        }
 
        /**
@@ -3265,6 +3103,11 @@ class OutputPage extends ContextSource {
                }
 
                $user = $this->getUser();
+
+               if ( !$user->isLoggedIn() ) {
+                       // Anons have predictable edit tokens
+                       return false;
+               }
                if ( !$user->matchEditToken( $request->getVal( 'wpEditToken' ) ) ) {
                        return false;
                }
@@ -3617,112 +3460,58 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * Build a set of "<link>" elements for the stylesheets specified in the $this->styles array.
-        * These will be applied to various media & IE conditionals.
+        * Build exempt modules and legacy non-ResourceLoader styles.
         *
-        * @return string
+        * @return string|WrappedStringList HTML
         */
-       public function buildCssLinks() {
+       protected function buildExemptModules() {
                global $wgContLang;
 
-               $this->getSkin()->setupSkinUserCss( $this );
-
-               // Add ResourceLoader styles
-               // Split the styles into these groups
-               $styles = [
-                       'other' => [],
-                       'user' => [],
-                       'site' => [],
-                       'private' => [],
-                       'noscript' => []
-               ];
-               $links = [];
-               $otherTags = []; // Tags to append after the normal <link> tags
                $resourceLoader = $this->getResourceLoader();
-
-               $moduleStyles = $this->getModuleStyles();
-
-               // Per-site custom styles
-               $moduleStyles[] = 'site.styles';
-               $moduleStyles[] = 'noscript';
-
-               // Per-user custom styles
-               if ( $this->getConfig()->get( 'AllowUserCss' ) && $this->getTitle()->isCssSubpage()
-                       && $this->userCanPreview()
-               ) {
-                       // We're on a preview of a CSS subpage
-                       // Exclude this page from the user module in case it's in there (bug 26283)
-                       $link = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_STYLES,
+               $chunks = [];
+               // Things that go after the ResourceLoaderDynamicStyles marker
+               $append = [];
+
+               // Exempt 'user' styles module (may need 'excludepages' for live preview)
+               if ( $this->isUserCssPreview() ) {
+                       $append[] = $this->makeResourceLoaderLink(
+                               'user.styles',
+                               ResourceLoaderModule::TYPE_STYLES,
                                [ 'excludepage' => $this->getTitle()->getPrefixedDBkey() ]
                        );
-                       $otherTags = array_merge( $otherTags, $link['html'] );
 
-                       // Load the previewed CSS
-                       // If needed, Janus it first. This is user-supplied CSS, so it's
-                       // assumed to be right for the content language directionality.
+                       // Load the previewed CSS. Janus it if needed.
+                       // User-supplied CSS is assumed to in the wiki's content language.
                        $previewedCSS = $this->getRequest()->getText( 'wpTextbox1' );
                        if ( $this->getLanguage()->getDir() !== $wgContLang->getDir() ) {
                                $previewedCSS = CSSJanus::transform( $previewedCSS, true, false );
                        }
-                       $otherTags[] = Html::inlineStyle( $previewedCSS );
-               } else {
-                       // Load the user styles normally
-                       $moduleStyles[] = 'user';
+                       $append[] = Html::inlineStyle( $previewedCSS );
                }
 
-               // Per-user preference styles
-               $moduleStyles[] = 'user.cssprefs';
+               // We want site, private and user styles to override dynamically added styles from
+               // general modules, but we want dynamically added styles to override statically added
+               // style modules. So the order has to be:
+               // - page style modules (formatted by ResourceLoaderClientHtml::getHeadHtml())
+               // - dynamically loaded styles (added by mw.loader before ResourceLoaderDynamicStyles)
+               // - ResourceLoaderDynamicStyles marker
+               // - site/private/user styles
 
-               foreach ( $moduleStyles as $name ) {
-                       $module = $resourceLoader->getModule( $name );
-                       if ( !$module ) {
-                               continue;
-                       }
-                       if ( $name === 'site.styles' ) {
-                               // HACK: The site module shouldn't be fragmented with a cache group and
-                               // http request. But in order to ensure its styles are separated and after the
-                               // ResourceLoaderDynamicStyles marker, pretend it is in a group called 'site'.
-                               // The scripts remain ungrouped and rides the bottom queue.
-                               $styles['site'][] = $name;
-                               continue;
-                       }
-                       $group = $module->getGroup();
-                       // Modules in groups other than the ones needing special treatment
-                       // (see $styles assignment)
-                       // will be placed in the "other" style category.
-                       $styles[isset( $styles[$group] ) ? $group : 'other'][] = $name;
-               }
-
-               // We want site, private and user styles to override dynamically added
-               // styles from modules, but we want dynamically added styles to override
-               // statically added styles from other modules. So the order has to be
-               // other, dynamic, site, private, user. Add statically added styles for
-               // other modules
-               $links[] = $this->makeResourceLoaderLink(
-                       $styles['other'],
-                       ResourceLoaderModule::TYPE_STYLES
-               );
-               // Add normal styles added through addStyle()/addInlineStyle() here
-               $links[] = implode( '', $this->buildCssLinksArray() ) . $this->mInlineStyles;
-               // Add marker tag to mark the place where the client-side
-               // loader should inject dynamic styles
-               // We use a <meta> tag with a made-up name for this because that's valid HTML
-               $links[] = Html::element(
+               // Add legacy styles added through addStyle()/addInlineStyle() here
+               $chunks[] = implode( '', $this->buildCssLinksArray() ) . $this->mInlineStyles;
+
+               $chunks[] = Html::element(
                        'meta',
                        [ 'name' => 'ResourceLoaderDynamicStyles', 'content' => '' ]
                );
 
-               // Add site-specific and user-specific styles
-               // 'private' at present only contains user.options, so put that before 'user'
-               // Any future private modules will likely have a similar user-specific character
-               foreach ( [ 'site', 'noscript', 'private', 'user' ] as $group ) {
-                       $links[] = $this->makeResourceLoaderLink( $styles[$group],
+               foreach ( $this->rlExemptStyleModules as $group => $moduleNames ) {
+                       $chunks[] = $this->makeResourceLoaderLink( $moduleNames,
                                ResourceLoaderModule::TYPE_STYLES
                        );
                }
 
-               // Add stuff in $otherTags (previewed user CSS if applicable)
-               return self::getHtmlFromLoaderLinks( $links ) . implode( '', $otherTags );
+               return self::combineWrappedStrings( array_merge( $chunks, $append ) );
        }
 
        /**
@@ -4037,8 +3826,13 @@ class OutputPage extends ContextSource {
                        'oojs-ui.styles.textures',
                        'mediawiki.widgets.styles',
                ] );
-               // Used by 'skipFunction' of the four 'oojs-ui.styles.*' modules. Please don't treat this as a
-               // public API or you'll be severely disappointed when T87871 is fixed and it disappears.
-               $this->addMeta( 'X-OOUI-PHP', '1' );
+       }
+
+       /**
+        * @param array $data Data from ParserOutput::getLimitReportData()
+        * @since 1.28
+        */
+       public function setLimitReportData( array $data ) {
+               $this->limitReportData = $data;
        }
 }
index 3654384..ed06935 100644 (file)
@@ -84,6 +84,16 @@ class PageProps {
                $this->cache = new ProcessCacheLRU( self::CACHE_SIZE );
        }
 
+       /**
+        * Ensure that cache has at least this size
+        * @param int $size
+        */
+       public function ensureCacheSize( $size ) {
+               if ( $this->cache->getSize() < $size ) {
+                       $this->cache->resize( $size );
+               }
+       }
+
        /**
         * Given one or more Titles and one or more names of properties,
         * returns an associative array mapping page ID to property value.
@@ -92,7 +102,7 @@ class PageProps {
         * single Title is provided, it does not need to be passed in an array,
         * but an array will always be returned. If a single property name is
         * provided, it does not need to be passed in an array. In that case,
-        * an associtive array mapping page ID to property value will be
+        * an associative array mapping page ID to property value will be
         * returned; otherwise, an associative array mapping page ID to
         * an associative array mapping property name to property value will be
         * returned. An empty array will be returned if no matching properties
@@ -130,7 +140,7 @@ class PageProps {
                }
 
                if ( $queryIDs ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $result = $dbr->select(
                                'page_props',
                                [
@@ -188,7 +198,7 @@ class PageProps {
                }
 
                if ( $queryIDs != [] ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $result = $dbr->select(
                                'page_props',
                                [
index 005c341..049b32f 100644 (file)
  *
  * $router->add( "/wiki/$1" );
  *   - Matches /wiki/Foo style urls and extracts the title
- * $router->add( array( 'edit' => "/edit/$key" ), array( 'action' => '$key' ) );
+ * $router->add( [ 'edit' => "/edit/$key" ], [ 'action' => '$key' ] );
  *   - Matches /edit/Foo style urls and sets action=edit
  * $router->add( '/$2/$1',
- *   array( 'variant' => '$2' ),
- *   array( '$2' => array( 'zh-hant', 'zh-hans' )
+ *   [ 'variant' => '$2' ],
+ *   [ '$2' => [ 'zh-hant', 'zh-hans' ] ]
  * );
  *   - Matches /zh-hant/Foo or /zh-hans/Foo
- * $router->addStrict( "/foo/Bar", array( 'title' => 'Baz' ) );
+ * $router->addStrict( "/foo/Bar", [ 'title' => 'Baz' ] );
  *   - Matches /foo/Bar explicitly and uses "Baz" as the title
- * $router->add( '/help/$1', array( 'title' => 'Help:$1' ) );
+ * $router->add( '/help/$1', [ 'title' => 'Help:$1' ] );
  *   - Matches /help/Foo with "Help:Foo" as the title
- * $router->add( '/$1', array( 'foo' => array( 'value' => 'bar$2' ) );
+ * $router->add( '/$1', [ 'foo' => [ 'value' => 'bar$2' ] ] );
  *   - Matches /Foo and sets 'foo' to 'bar$2' without $2 being replaced
- * $router->add( '/$1', array( 'data:foo' => 'bar' ), array( 'callback' => 'functionname' ) );
+ * $router->add( '/$1', [ 'data:foo' => 'bar' ], [ 'callback' => 'functionname' ] );
  *   - Matches /Foo, adds the key 'foo' with the value 'bar' to the data array
  *     and calls functionname( &$matches, $data );
  *
@@ -56,7 +56,7 @@
  *   - The default behavior is equivalent to `array( 'title' => '$1' )`,
  *     if you don't want the title parameter you can explicitly use `array( 'title' => false )`
  *   - You can specify a value that won't have replacements in it
- *     using `'foo' => array( 'value' => 'bar' );`
+ *     using `'foo' => [ 'value' => 'bar' ];`
  *
  * Options:
  *   - The option keys $1, $2, etc... can be specified to restrict the possible values
diff --git a/includes/Pingback.php b/includes/Pingback.php
new file mode 100644 (file)
index 0000000..bd1b2a2
--- /dev/null
@@ -0,0 +1,261 @@
+<?php
+/**
+ * Send information about this MediaWiki instance to MediaWiki.org.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+use Psr\Log\LoggerInterface;
+use MediaWiki\Logger\LoggerFactory;
+
+/**
+ * Send information about this MediaWiki instance to MediaWiki.org.
+ *
+ * @since 1.28
+ */
+class Pingback {
+
+       /**
+        * @var int Revision ID of the JSON schema that describes the pingback
+        *   payload. The schema lives on MetaWiki, at
+        *   <https://meta.wikimedia.org/wiki/Schema:MediaWikiPingback>.
+        */
+       const SCHEMA_REV = 15781718;
+
+       /** @var LoggerInterface */
+       protected $logger;
+
+       /** @var Config */
+       protected $config;
+
+       /** @var string updatelog key (also used as cache/db lock key) */
+       protected $key;
+
+       /** @var string Randomly-generated identifier for this wiki */
+       protected $id;
+
+       /**
+        * @param Config $config
+        * @param LoggerInterface $logger
+        */
+       public function __construct( Config $config = null, LoggerInterface $logger = null ) {
+               $this->config = $config ?: RequestContext::getMain()->getConfig();
+               $this->logger = $logger ?: LoggerFactory::getInstance( __CLASS__ );
+               $this->key = 'Pingback-' . $this->config->get( 'Version' );
+       }
+
+       /**
+        * Should a pingback be sent?
+        * @return bool
+        */
+       private function shouldSend() {
+               return $this->config->get( 'Pingback' ) && !$this->checkIfSent();
+       }
+
+       /**
+        * Has a pingback already been sent for this MediaWiki version?
+        * @return bool
+        */
+       private function checkIfSent() {
+               $dbr = wfGetDB( DB_REPLICA );
+               $sent = $dbr->selectField(
+                       'updatelog', '1', [ 'ul_key' => $this->key ], __METHOD__ );
+               return $sent !== false;
+       }
+
+       /**
+        * Record the fact that we have sent a pingback for this MediaWiki version,
+        * to ensure we don't submit data multiple times.
+        */
+       private function markSent() {
+               $dbw = wfGetDB( DB_MASTER );
+               return $dbw->insert(
+                       'updatelog', [ 'ul_key' => $this->key ], __METHOD__, 'IGNORE' );
+       }
+
+       /**
+        * Acquire lock for sending a pingback
+        *
+        * This ensures only one thread can attempt to send a pingback at any given
+        * time and that we wait an hour before retrying failed attempts.
+        *
+        * @return bool Whether lock was acquired
+        */
+       private function acquireLock() {
+               $cache = ObjectCache::getLocalClusterInstance();
+               if ( !$cache->add( $this->key, 1, 60 * 60 ) ) {
+                       return false;  // throttled
+               }
+
+               $dbw = wfGetDB( DB_MASTER );
+               if ( !$dbw->lock( $this->key, __METHOD__, 0 ) ) {
+                       return false;  // already in progress
+               }
+
+               return true;
+       }
+
+       /**
+        * Collect basic data about this MediaWiki installation and return it
+        * as an associative array conforming to the Pingback schema on MetaWiki
+        * (<https://meta.wikimedia.org/wiki/Schema:MediaWikiPingback>).
+        *
+        * This is public so we can display it in the installer
+        *
+        * Developers: If you're adding a new piece of data to this, please ensure
+        * that you update https://www.mediawiki.org/wiki/Manual:$wgPingback
+        *
+        * @return array
+        */
+       public function getSystemInfo() {
+               $event = [
+                       'database'   => $this->config->get( 'DBtype' ),
+                       'MediaWiki'  => $this->config->get( 'Version' ),
+                       'PHP'        => PHP_VERSION,
+                       'OS'         => PHP_OS . ' ' . php_uname( 'r' ),
+                       'arch'       => PHP_INT_SIZE === 8 ? 64 : 32,
+                       'machine'    => php_uname( 'm' ),
+               ];
+
+               if ( isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
+                       $event['serverSoftware'] = $_SERVER['SERVER_SOFTWARE'];
+               }
+
+               $limit = ini_get( 'memory_limit' );
+               if ( $limit && $limit != -1 ) {
+                       $event['memoryLimit'] = $limit;
+               }
+
+               return $event;
+       }
+
+       /**
+        * Get the EventLogging packet to be sent to the server
+        *
+        * @return array
+        */
+       private function getData() {
+               return [
+                       'schema'           => 'MediaWikiPingback',
+                       'revision'         => self::SCHEMA_REV,
+                       'wiki'             => $this->getOrCreatePingbackId(),
+                       'event'            => $this->getSystemInfo(),
+               ];
+       }
+
+       /**
+        * Get a unique, stable identifier for this wiki
+        *
+        * If the identifier does not already exist, create it and save it in the
+        * database. The identifier is randomly-generated.
+        *
+        * @return string 32-character hex string
+        */
+       private function getOrCreatePingbackId() {
+               if ( !$this->id ) {
+                       $id = wfGetDB( DB_REPLICA )->selectField(
+                               'updatelog', 'ul_value', [ 'ul_key' => 'PingBack' ] );
+
+                       if ( $id == false ) {
+                               $id = MWCryptRand::generateHex( 32 );
+                               $dbw = wfGetDB( DB_MASTER );
+                               $dbw->insert(
+                                       'updatelog',
+                                       [ 'ul_key' => 'PingBack', 'ul_value' => $id ],
+                                       __METHOD__,
+                                       'IGNORE'
+                               );
+
+                               if ( !$dbw->affectedRows() ) {
+                                       $id = $dbw->selectField(
+                                               'updatelog', 'ul_value', [ 'ul_key' => 'PingBack' ] );
+                               }
+                       }
+
+                       $this->id = $id;
+               }
+
+               return $this->id;
+       }
+
+       /**
+        * Serialize pingback data and send it to MediaWiki.org via a POST
+        * to its event beacon endpoint.
+        *
+        * The data encoding conforms to the expectations of EventLogging,
+        * a software suite used by the Wikimedia Foundation for logging and
+        * processing analytic data.
+        *
+        * Compare:
+        * <https://github.com/wikimedia/mediawiki-extensions-EventLogging/
+        *   blob/7e5fe4f1ef/includes/EventLogging.php#L32-L74>
+        *
+        * @param array $data Pingback data as an associative array
+        * @return bool true on success, false on failure
+        */
+       private function postPingback( array $data ) {
+               $json = FormatJson::encode( $data );
+               $queryString = rawurlencode( str_replace( ' ', '\u0020', $json ) ) . ';';
+               $url = 'https://www.mediawiki.org/beacon/event?' . $queryString;
+               return Http::post( $url ) !== false;
+       }
+
+       /**
+        * Send information about this MediaWiki instance to MediaWiki.org.
+        *
+        * The data is structured and serialized to match the expectations of
+        * EventLogging, a software suite used by the Wikimedia Foundation for
+        * logging and processing analytic data.
+        *
+        * Compare:
+        * <https://github.com/wikimedia/mediawiki-extensions-EventLogging/
+        *   blob/7e5fe4f1ef/includes/EventLogging.php#L32-L74>
+        *
+        * The schema for the data is located at:
+        * <https://meta.wikimedia.org/wiki/Schema:MediaWikiPingback>
+        */
+       public function sendPingback() {
+               if ( !$this->acquireLock() ) {
+                       $this->logger->debug( __METHOD__ . ": couldn't acquire lock" );
+                       return false;
+               }
+
+               $data = $this->getData();
+               if ( !$this->postPingback( $data ) ) {
+                       $this->logger->warning( __METHOD__ . ": failed to send pingback; check 'http' log" );
+                       return false;
+               }
+
+               $this->markSent();
+               $this->logger->debug( __METHOD__ . ": pingback sent OK ({$this->key})" );
+               return true;
+       }
+
+       /**
+        * Schedule a deferred callable that will check if a pingback should be
+        * sent and (if so) proceed to send it.
+        */
+       public static function schedulePingback() {
+               DeferredUpdates::addCallableUpdate( function () {
+                       $instance = new Pingback;
+                       if ( $instance->shouldSend() ) {
+                               $instance->sendPingback();
+                       }
+               } );
+       }
+}
index 3083a8d..9f8c06b 100644 (file)
@@ -207,8 +207,9 @@ class Preferences {
         * @return void
         */
        static function profilePreferences( $user, IContextSource $context, &$defaultPreferences ) {
-               global $wgAuth, $wgContLang, $wgParser, $wgDisableAuthManager;
+               global $wgContLang, $wgParser;
 
+               $authManager = AuthManager::singleton();
                $config = $context->getConfig();
                // retrieving user name for GENDER and misc.
                $userName = $user->getName();
@@ -283,21 +284,19 @@ class Preferences {
                $canEditPrivateInfo = $user->isAllowed( 'editmyprivateinfo' );
 
                // Actually changeable stuff
-               $realnameChangeAllowed = $wgDisableAuthManager ? $wgAuth->allowPropChange( 'realname' )
-                       : AuthManager::singleton()->allowsPropertyChange( 'realname' );
                $defaultPreferences['realname'] = [
                        // (not really "private", but still shouldn't be edited without permission)
-                       'type' => $canEditPrivateInfo && $realnameChangeAllowed ? 'text' : 'info',
+                       'type' => $canEditPrivateInfo && $authManager->allowsPropertyChange( 'realname' )
+                               ? 'text' : 'info',
                        'default' => $user->getRealName(),
                        'section' => 'personal/info',
                        'label-message' => 'yourrealname',
                        'help-message' => 'prefs-help-realname',
                ];
 
-               $allowPasswordChange = $wgDisableAuthManager ? $wgAuth->allowPasswordChange()
-                       : AuthManager::singleton()->allowsAuthenticationDataChange(
-                               new PasswordAuthenticationRequest(), false )->isGood();
-               if ( $canEditPrivateInfo && $allowPasswordChange ) {
+               if ( $canEditPrivateInfo && $authManager->allowsAuthenticationDataChange(
+                       new PasswordAuthenticationRequest(), false )->isGood()
+               ) {
                        $link = Linker::link( SpecialPage::getTitleFor( 'ChangePassword' ),
                                $context->msg( 'prefs-resetpass' )->escaped(), [],
                                [ 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText() ] );
@@ -418,10 +417,8 @@ class Preferences {
                        'default' => $oldsigHTML,
                        'section' => 'personal/signature',
                ];
-               $nicknameChangeAllowed = $wgDisableAuthManager ? $wgAuth->allowPropChange( 'nickname' )
-                       : AuthManager::singleton()->allowsPropertyChange( 'nickname' );
                $defaultPreferences['nickname'] = [
-                       'type' => $nicknameChangeAllowed ? 'text' : 'info',
+                       'type' => $authManager->allowsPropertyChange( 'nickname' ) ? 'text' : 'info',
                        'maxlength' => $config->get( 'MaxSigChars' ),
                        'label-message' => 'yournick',
                        'validation-callback' => [ 'Preferences', 'validateSignature' ],
@@ -450,9 +447,7 @@ class Preferences {
                                }
 
                                $emailAddress = $user->getEmail() ? htmlspecialchars( $user->getEmail() ) : '';
-                               $emailChangeAllowed = $wgDisableAuthManager ? $wgAuth->allowPropChange( 'emailaddress' )
-                                       : AuthManager::singleton()->allowsPropertyChange( 'emailaddress' );
-                               if ( $canEditPrivateInfo && $emailChangeAllowed ) {
+                               if ( $canEditPrivateInfo && $authManager->allowsPropertyChange( 'emailaddress' ) ) {
                                        $link = Linker::link(
                                                SpecialPage::getTitleFor( 'ChangeEmail' ),
                                                $context->msg( $user->getEmail() ? 'prefs-changeemail' : 'prefs-setemail' )->escaped(),
@@ -1499,7 +1494,7 @@ class Preferences {
 
                        $context = $form->getContext();
                        // Set session data for the success message
-                       $context->getRequest()->setSessionData( 'specialPreferencesSaveSuccess', 1 );
+                       $context->getRequest()->getSession()->set( 'specialPreferencesSaveSuccess', 1 );
 
                        $context->getOutput()->redirect( $url );
                }
@@ -1599,7 +1594,7 @@ class PreferencesForm extends HTMLForm {
         * Get extra parameters for the query string when redirecting after
         * successful save.
         *
-        * @return array()
+        * @return array
         */
        public function getExtraSuccessRedirectParameters() {
                return [];
index 04c68ca..49e596d 100644 (file)
@@ -272,7 +272,7 @@ abstract class PrefixSearch {
 
                $t = Title::newFromText( $search, $ns );
                $prefix = $t ? $t->getDBkey() : '';
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select( 'page',
                        [ 'page_id', 'page_namespace', 'page_title' ],
                        [
index eda8989..6acc528 100644 (file)
@@ -25,52 +25,60 @@ use MediaWiki\Linker\LinkTarget;
  * @todo document
  */
 class Revision implements IDBAccessObject {
+       /** @var int|null */
        protected $mId;
-
-       /**
-        * @var int|null
-        */
+       /** @var int|null */
        protected $mPage;
+       /** @var string */
        protected $mUserText;
+       /** @var string */
        protected $mOrigUserText;
+       /** @var int */
        protected $mUser;
+       /** @var bool */
        protected $mMinorEdit;
+       /** @var string */
        protected $mTimestamp;
+       /** @var int */
        protected $mDeleted;
+       /** @var int */
        protected $mSize;
+       /** @var string */
        protected $mSha1;
+       /** @var int */
        protected $mParentId;
+       /** @var string */
        protected $mComment;
+       /** @var string */
        protected $mText;
+       /** @var int */
        protected $mTextId;
+       /** @var int */
+       protected $mUnpatrolled;
 
-       /**
-        * @var stdClass|null
-        */
+       /** @var stdClass|null */
        protected $mTextRow;
 
-       /**
-        * @var null|Title
-        */
+       /**  @var null|Title */
        protected $mTitle;
+       /** @var bool */
        protected $mCurrent;
+       /** @var string */
        protected $mContentModel;
+       /** @var string */
        protected $mContentFormat;
 
-       /**
-        * @var Content|null|bool
-        */
+       /** @var Content|null|bool */
        protected $mContent;
-
-       /**
-        * @var null|ContentHandler
-        */
+       /** @var null|ContentHandler */
        protected $mContentHandler;
 
-       /**
-        * @var int
-        */
+       /** @var int */
        protected $mQueryFlags = 0;
+       /** @var bool Used for cached values to reload user text and rev_deleted */
+       protected $mRefreshMutableFields = false;
+       /** @var string Wiki ID; false means the current wiki */
+       protected $mWiki = false;
 
        // Revision deletion constants
        const DELETED_TEXT = 1;
@@ -84,6 +92,8 @@ class Revision implements IDBAccessObject {
        const FOR_THIS_USER = 2;
        const RAW = 3;
 
+       const TEXT_CACHE_GROUP = 'revisiontext:10'; // process cache name and max key count
+
        /**
         * Load a page revision from a given revision ID number.
         * Returns null if no such revision can be found.
@@ -126,7 +136,7 @@ class Revision implements IDBAccessObject {
                } else {
                        // Use a join to get the latest revision
                        $conds[] = 'rev_id=page_latest';
-                       $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+                       $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
                        return self::loadFromConds( $db, $conds, $flags );
                }
        }
@@ -153,7 +163,7 @@ class Revision implements IDBAccessObject {
                } else {
                        // Use a join to get the latest revision
                        $conds[] = 'rev_id = page_latest';
-                       $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+                       $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
                        return self::loadFromConds( $db, $conds, $flags );
                }
        }
@@ -301,14 +311,14 @@ class Revision implements IDBAccessObject {
         * Given a set of conditions, fetch a revision
         *
         * This method is used then a revision ID is qualified and
-        * will incorporate some basic slave/master fallback logic
+        * will incorporate some basic replica DB/master fallback logic
         *
         * @param array $conditions
         * @param int $flags (optional)
         * @return Revision|null
         */
        private static function newFromConds( $conditions, $flags = 0 ) {
-               $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+               $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
 
                $rev = self::loadFromConds( $db, $conditions, $flags );
                // Make sure new pending/committed revision are visibile later on
@@ -340,16 +350,15 @@ class Revision implements IDBAccessObject {
         * @return Revision|null
         */
        private static function loadFromConds( $db, $conditions, $flags = 0 ) {
-               $res = self::fetchFromConds( $db, $conditions, $flags );
-               if ( $res ) {
-                       $row = $res->fetchObject();
-                       if ( $row ) {
-                               $ret = new Revision( $row );
-                               return $ret;
-                       }
+               $row = self::fetchFromConds( $db, $conditions, $flags );
+               if ( $row ) {
+                       $rev = new Revision( $row );
+                       $rev->mWiki = $db->getWikiID();
+
+                       return $rev;
                }
-               $ret = null;
-               return $ret;
+
+               return null;
        }
 
        /**
@@ -357,18 +366,21 @@ class Revision implements IDBAccessObject {
         * fetch all of a given page's revisions in turn.
         * Each row can be fed to the constructor to get objects.
         *
-        * @param Title $title
+        * @param LinkTarget $title
         * @return ResultWrapper
+        * @deprecated Since 1.28
         */
-       public static function fetchRevision( $title ) {
-               return self::fetchFromConds(
-                       wfGetDB( DB_SLAVE ),
+       public static function fetchRevision( LinkTarget $title ) {
+               $row = self::fetchFromConds(
+                       wfGetDB( DB_REPLICA ),
                        [
                                'rev_id=page_latest',
                                'page_namespace' => $title->getNamespace(),
                                'page_title' => $title->getDBkey()
                        ]
                );
+
+               return new FakeResultWrapper( $row ? [ $row ] : [] );
        }
 
        /**
@@ -379,7 +391,7 @@ class Revision implements IDBAccessObject {
         * @param IDatabase $db
         * @param array $conditions
         * @param int $flags (optional)
-        * @return ResultWrapper
+        * @return stdClass
         */
        private static function fetchFromConds( $db, $conditions, $flags = 0 ) {
                $fields = array_merge(
@@ -387,11 +399,11 @@ class Revision implements IDBAccessObject {
                        self::selectPageFields(),
                        self::selectUserFields()
                );
-               $options = [ 'LIMIT' => 1 ];
+               $options = [];
                if ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING ) {
                        $options[] = 'FOR UPDATE';
                }
-               return $db->select(
+               return $db->selectRow(
                        [ 'revision', 'page', 'user' ],
                        $fields,
                        $conditions,
@@ -793,20 +805,24 @@ class Revision implements IDBAccessObject {
                }
                // rev_id is defined as NOT NULL, but this revision may not yet have been inserted.
                if ( $this->mId !== null ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki );
                        $row = $dbr->selectRow(
                                [ 'page', 'revision' ],
                                self::selectPageFields(),
-                               [ 'page_id=rev_page',
-                                       'rev_id' => $this->mId ],
-                               __METHOD__ );
+                               [ 'page_id=rev_page', 'rev_id' => $this->mId ],
+                               __METHOD__
+                       );
                        if ( $row ) {
+                               // @TODO: better foreign title handling
                                $this->mTitle = Title::newFromRow( $row );
                        }
                }
 
-               if ( !$this->mTitle && $this->mPage !== null && $this->mPage > 0 ) {
-                       $this->mTitle = Title::newFromID( $this->mPage );
+               if ( $this->mWiki === false || $this->mWiki === wfWikiID() ) {
+                       // Loading by ID is best, though not possible for foreign titles
+                       if ( !$this->mTitle && $this->mPage !== null && $this->mPage > 0 ) {
+                               $this->mTitle = Title::newFromID( $this->mPage );
+                       }
                }
 
                return $this->mTitle;
@@ -878,6 +894,8 @@ class Revision implements IDBAccessObject {
         * @return string
         */
        public function getUserText( $audience = self::FOR_PUBLIC, User $user = null ) {
+               $this->loadMutableFields();
+
                if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) {
                        return '';
                } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $user ) ) {
@@ -973,7 +991,7 @@ class Revision implements IDBAccessObject {
         * @return RecentChange|null
         */
        public function getRecentChange( $flags = 0 ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                list( $dbType, ) = DBAccessObjectUtils::getDBOptions( $flags );
 
@@ -994,7 +1012,14 @@ class Revision implements IDBAccessObject {
         * @return bool
         */
        public function isDeleted( $field ) {
-               return ( $this->mDeleted & $field ) == $field;
+               if ( $this->isCurrent() && $field === self::DELETED_TEXT ) {
+                       // Current revisions of pages cannot have the content hidden. Skipping this
+                       // check is very useful for Parser as it fetches templates using newKnownCurrent().
+                       // Calling getVisibility() in that case triggers a verification database query.
+                       return false; // no need to check
+               }
+
+               return ( $this->getVisibility() & $field ) == $field;
        }
 
        /**
@@ -1003,6 +1028,8 @@ class Revision implements IDBAccessObject {
         * @return int
         */
        public function getVisibility() {
+               $this->loadMutableFields();
+
                return (int)$this->mDeleted;
        }
 
@@ -1054,13 +1081,14 @@ class Revision implements IDBAccessObject {
        }
 
        /**
-        * Fetch original serialized data without regard for view restrictions
+        * Get original serialized data (without checking view restrictions)
         *
         * @since 1.21
         * @return string
         */
        public function getSerializedData() {
                if ( $this->mText === null ) {
+                       // Revision is immutable. Load on demand.
                        $this->mText = $this->loadText();
                }
 
@@ -1078,17 +1106,14 @@ class Revision implements IDBAccessObject {
         */
        protected function getContentInternal() {
                if ( $this->mContent === null ) {
-                       // Revision is immutable. Load on demand:
-                       if ( $this->mText === null ) {
-                               $this->mText = $this->loadText();
-                       }
+                       $text = $this->getSerializedData();
 
-                       if ( $this->mText !== null && $this->mText !== false ) {
+                       if ( $text !== null && $text !== false ) {
                                // Unserialize content
                                $handler = $this->getContentHandler();
                                $format = $this->getContentFormat();
 
-                               $this->mContent = $handler->unserializeContent( $this->mText, $format );
+                               $this->mContent = $handler->unserializeContent( $text, $format );
                        }
                }
 
@@ -1551,29 +1576,30 @@ class Revision implements IDBAccessObject {
         *
         * @return string|bool The revision's text, or false on failure
         */
-       protected function loadText() {
-               // Caching may be beneficial for massive use of external storage
+       private function loadText() {
                global $wgRevisionCacheExpiry;
-               static $processCache = null;
 
-               if ( !$processCache ) {
-                       $processCache = new MapCacheLRU( 10 );
+               $cache = ObjectCache::getMainWANInstance();
+               if ( $cache->getQoS( $cache::ATTR_EMULATION ) <= $cache::QOS_EMULATION_SQL ) {
+                       // Do not cache RDBMs blobs in...the RDBMs store
+                       $ttl = $cache::TTL_UNCACHEABLE;
+               } else {
+                       $ttl = $wgRevisionCacheExpiry ?: $cache::TTL_UNCACHEABLE;
                }
 
-               $cache = ObjectCache::getMainWANInstance();
-               $textId = $this->getTextId();
-               $key = wfMemcKey( 'revisiontext', 'textid', $textId );
+               // No negative caching; negative hits on text rows may be due to corrupted replica DBs
+               return $cache->getWithSetCallback(
+                       $cache->makeKey( 'revisiontext', 'textid', $this->getTextId() ),
+                       $ttl,
+                       function () {
+                               return $this->fetchText();
+                       },
+                       [ 'pcGroup' => self::TEXT_CACHE_GROUP, 'pcTTL' => $cache::TTL_PROC_LONG ]
+               );
+       }
 
-               if ( $wgRevisionCacheExpiry ) {
-                       if ( $processCache->has( $key ) ) {
-                               return $processCache->get( $key );
-                       }
-                       $text = $cache->get( $key );
-                       if ( is_string( $text ) ) {
-                               $processCache->set( $key, $text );
-                               return $text;
-                       }
-               }
+       private function fetchText() {
+               $textId = $this->getTextId();
 
                // If we kept data for lazy extraction, use it now...
                if ( $this->mTextRow !== null ) {
@@ -1583,25 +1609,38 @@ class Revision implements IDBAccessObject {
                        $row = null;
                }
 
+               // Callers doing updates will pass in READ_LATEST as usual. Since the text/blob tables
+               // do not normally get rows changed around, set READ_LATEST_IMMUTABLE in those cases.
+               $flags = $this->mQueryFlags;
+               $flags |= DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST )
+                       ? self::READ_LATEST_IMMUTABLE
+                       : 0;
+
+               list( $index, $options, $fallbackIndex, $fallbackOptions ) =
+                       DBAccessObjectUtils::getDBOptions( $flags );
+
                if ( !$row ) {
-                       // Text data is immutable; check slaves first.
-                       $dbr = wfGetDB( DB_SLAVE );
-                       $row = $dbr->selectRow( 'text',
+                       // Text data is immutable; check replica DBs first.
+                       $row = wfGetDB( $index )->selectRow(
+                               'text',
                                [ 'old_text', 'old_flags' ],
                                [ 'old_id' => $textId ],
-                               __METHOD__ );
+                               __METHOD__,
+                               $options
+                       );
                }
 
-               // Fallback to the master in case of slave lag. Also use FOR UPDATE if it was
-               // used to fetch this revision to avoid missing the row due to REPEATABLE-READ.
-               $forUpdate = ( $this->mQueryFlags & self::READ_LOCKING == self::READ_LOCKING );
-               if ( !$row && ( $forUpdate || wfGetLB()->getServerCount() > 1 ) ) {
-                       $dbw = wfGetDB( DB_MASTER );
-                       $row = $dbw->selectRow( 'text',
+               // Fallback to DB_MASTER in some cases if the row was not found
+               if ( !$row && $fallbackIndex !== null ) {
+                       // Use FOR UPDATE if it was used to fetch this revision. This avoids missing the row
+                       // due to REPEATABLE-READ. Also fallback to the master if READ_LATEST is provided.
+                       $row = wfGetDB( $fallbackIndex )->selectRow(
+                               'text',
                                [ 'old_text', 'old_flags' ],
                                [ 'old_id' => $textId ],
                                __METHOD__,
-                               $forUpdate ? [ 'FOR UPDATE' ] : [] );
+                               $fallbackOptions
+                       );
                }
 
                if ( !$row ) {
@@ -1613,13 +1652,7 @@ class Revision implements IDBAccessObject {
                        wfDebugLog( 'Revision', "No blob for text row '$textId' (revision {$this->getId()})." );
                }
 
-               # No negative caching -- negative hits on text rows may be due to corrupted slave servers
-               if ( $wgRevisionCacheExpiry && $text !== false ) {
-                       $processCache->set( $key, $text );
-                       $cache->set( $key, $text, $wgRevisionCacheExpiry );
-               }
-
-               return $text;
+               return is_string( $text ) ? $text : false;
        }
 
        /**
@@ -1706,7 +1739,7 @@ class Revision implements IDBAccessObject {
         * @return bool
         */
        public function userCan( $field, User $user = null ) {
-               return self::userCanBitfield( $this->mDeleted, $field, $user );
+               return self::userCanBitfield( $this->getVisibility(), $field, $user );
        }
 
        /**
@@ -1767,7 +1800,7 @@ class Revision implements IDBAccessObject {
        static function getTimestampFromId( $title, $id, $flags = 0 ) {
                $db = ( $flags & self::READ_LATEST )
                        ? wfGetDB( DB_MASTER )
-                       : wfGetDB( DB_SLAVE );
+                       : wfGetDB( DB_REPLICA );
                // Casting fix for databases that can't take '' for rev_id
                if ( $id == '' ) {
                        $id = 0;
@@ -1850,4 +1883,60 @@ class Revision implements IDBAccessObject {
                }
                return true;
        }
+
+       /**
+        * Load a revision based on a known page ID and current revision ID from the DB
+        *
+        * This method allows for the use of caching, though accessing anything that normally
+        * requires permission checks (aside from the text) will trigger a small DB lookup.
+        * The title will also be lazy loaded, though setTitle() can be used to preload it.
+        *
+        * @param IDatabase $db
+        * @param int $pageId Page ID
+        * @param int $revId Known current revision of this page
+        * @return Revision|bool Returns false if missing
+        * @since 1.28
+        */
+       public static function newKnownCurrent( IDatabase $db, $pageId, $revId ) {
+               $cache = ObjectCache::getMainWANInstance();
+               return $cache->getWithSetCallback(
+                       // Page/rev IDs passed in from DB to reflect history merges
+                       $cache->makeGlobalKey( 'revision', $db->getWikiID(), $pageId, $revId ),
+                       $cache::TTL_WEEK,
+                       function ( $curValue, &$ttl, array &$setOpts ) use ( $db, $pageId, $revId ) {
+                               $setOpts += Database::getCacheSetOptions( $db );
+
+                               $rev = Revision::loadFromPageId( $db, $pageId, $revId );
+                               // Reflect revision deletion and user renames
+                               if ( $rev ) {
+                                       $rev->mTitle = null; // mutable; lazy-load
+                                       $rev->mRefreshMutableFields = true;
+                               }
+
+                               return $rev ?: false; // don't cache negatives
+                       }
+               );
+       }
+
+       /**
+        * For cached revisions, make sure the user name and rev_deleted is up-to-date
+        */
+       private function loadMutableFields() {
+               if ( !$this->mRefreshMutableFields ) {
+                       return; // not needed
+               }
+
+               $this->mRefreshMutableFields = false;
+               $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki );
+               $row = $dbr->selectRow(
+                       [ 'revision', 'user' ],
+                       [ 'rev_deleted', 'user_name' ],
+                       [ 'rev_id' => $this->mId, 'user_id = rev_user' ],
+                       __METHOD__
+               );
+               if ( $row ) { // update values
+                       $this->mDeleted = (int)$row->rev_deleted;
+                       $this->mUserText = $row->user_name;
+               }
+       }
 }
index 731d1b3..fb444bd 100644 (file)
@@ -23,7 +23,7 @@
 /**
  * List for revision table items for a single page
  */
-abstract class RevisionListBase extends ContextSource {
+abstract class RevisionListBase extends ContextSource implements Iterator {
        /** @var Title */
        public $title;
 
@@ -81,7 +81,7 @@ abstract class RevisionListBase extends ContextSource {
         */
        public function reset() {
                if ( !$this->res ) {
-                       $this->res = $this->doQuery( wfGetDB( DB_SLAVE ) );
+                       $this->res = $this->doQuery( wfGetDB( DB_REPLICA ) );
                } else {
                        $this->res->rewind();
                }
@@ -89,6 +89,10 @@ abstract class RevisionListBase extends ContextSource {
                return $this->current;
        }
 
+       public function rewind() {
+               $this->reset();
+       }
+
        /**
         * Get the current list item, or false if we are at the end
         * @return Revision
@@ -107,6 +111,14 @@ abstract class RevisionListBase extends ContextSource {
                return $this->current;
        }
 
+       public function key() {
+               return $this->res ? $this->res->key(): 0;
+       }
+
+       public function valid() {
+               return $this->res ? $this->res->valid() : false;
+       }
+
        /**
         * Get the number of items in the list.
         * @return int
index d321e9f..8f1fc99 100644 (file)
@@ -381,14 +381,17 @@ class Sanitizer {
                                'kbd', 'samp', 'data', 'time', 'mark'
                        ];
                        $htmlsingle = [
-                               'br', 'wbr', 'hr', 'li', 'dt', 'dd'
-                       ];
-                       $htmlsingleonly = [ # Elements that cannot have close tags
-                               'br', 'wbr', 'hr'
+                               'br', 'wbr', 'hr', 'li', 'dt', 'dd', 'meta', 'link'
                        ];
 
-                       $htmlsingle[] = $htmlsingleonly[] = 'meta';
-                       $htmlsingle[] = $htmlsingleonly[] = 'link';
+                       # Elements that cannot have close tags. This is (not coincidentally)
+                       # also the list of tags for which the HTML 5 parsing algorithm
+                       # requires you to "acknowledge the token's self-closing flag", i.e.
+                       # a self-closing tag like <br/> is not an HTML 5 parse error only
+                       # for this list.
+                       $htmlsingleonly = [
+                               'br', 'wbr', 'hr', 'meta', 'link'
+                       ];
 
                        $htmlnest = [ # Tags that can be nested--??
                                'table', 'tr', 'td', 'th', 'div', 'blockquote', 'ol', 'ul',
@@ -450,10 +453,14 @@ class Sanitizer {
         * @param array|bool $args Arguments for the processing callback
         * @param array $extratags For any extra tags to include
         * @param array $removetags For any tags (default or extra) to exclude
+        * @param callable $warnCallback (Deprecated) Callback allowing the
+        *   addition of a tracking category when bad input is encountered.
+        *   DO NOT ADD NEW PARAMETERS AFTER $warnCallback, since it will be
+        *   removed shortly.
         * @return string
         */
        public static function removeHTMLtags( $text, $processCallback = null,
-               $args = [], $extratags = [], $removetags = []
+               $args = [], $extratags = [], $removetags = [], $warnCallback = null
        ) {
                extract( self::getRecognizedTagData( $extratags, $removetags ) );
 
@@ -540,6 +547,14 @@ class Sanitizer {
                                                        $badtag = true;
                                                #  Is it a self closed htmlpair ? (bug 5487)
                                                } elseif ( $brace == '/>' && isset( $htmlpairs[$t] ) ) {
+                                                       // Eventually we'll just remove the self-closing
+                                                       // slash, in order to be consistent with HTML5
+                                                       // semantics.
+                                                       // $brace = '>';
+                                                       // For now, let's just warn authors to clean up.
+                                                       if ( is_callable( $warnCallback ) ) {
+                                                               call_user_func_array( $warnCallback, [ 'deprecated-self-close-category' ] );
+                                                       }
                                                        $badtag = true;
                                                } elseif ( isset( $htmlsingleonly[$t] ) ) {
                                                        # Hack to force empty tag for unclosable elements
@@ -604,12 +619,29 @@ class Sanitizer {
                                                        call_user_func_array( $processCallback, [ &$params, $args ] );
                                                }
 
+                                               if ( $brace == '/>' && !( isset( $htmlsingle[$t] ) || isset( $htmlsingleonly[$t] ) ) ) {
+                                                       // Eventually we'll just remove the self-closing
+                                                       // slash, in order to be consistent with HTML5
+                                                       // semantics.
+                                                       // $brace = '>';
+                                                       // For now, let's just warn authors to clean up.
+                                                       if ( is_callable( $warnCallback ) ) {
+                                                               call_user_func_array( $warnCallback, [ 'deprecated-self-close-category' ] );
+                                                       }
+                                               }
                                                if ( !Sanitizer::validateTag( $params, $t ) ) {
                                                        $badtag = true;
                                                }
 
                                                $newparams = Sanitizer::fixTagAttributes( $params, $t );
                                                if ( !$badtag ) {
+                                                       if ( $brace === '/>' && !isset( $htmlsingleonly[$t] ) ) {
+                                                               # Interpret self-closing tags as empty tags even when
+                                                               # HTML 5 would interpret them as start tags. Such input
+                                                               # is commonly seen on Wikimedia wikis with this intention.
+                                                               $brace = "></$t>";
+                                                       }
+
                                                        $rest = str_replace( '>', '&gt;', $rest );
                                                        $text .= "<$slash$t$newparams$brace$rest";
                                                        continue;
@@ -1028,12 +1060,14 @@ class Sanitizer {
         * - Double attributes are discarded
         * - Unsafe style attributes are discarded
         * - Prepends space if there are attributes.
+        * - (Optionally) Sorts attributes by name.
         *
         * @param string $text
         * @param string $element
+        * @param bool $sorted Whether to sort the attributes (default: false)
         * @return string
         */
-       static function fixTagAttributes( $text, $element ) {
+       static function fixTagAttributes( $text, $element, $sorted = false ) {
                if ( trim( $text ) == '' ) {
                        return '';
                }
@@ -1041,6 +1075,10 @@ class Sanitizer {
                $decoded = Sanitizer::decodeTagAttributes( $text );
                $stripped = Sanitizer::validateTagAttributes( $decoded, $element );
 
+               if ( $sorted ) {
+                       ksort( $stripped );
+               }
+
                return Sanitizer::safeEncodeTagAttributes( $stripped );
        }
 
index 9ee4236..8734bd6 100644 (file)
@@ -158,15 +158,23 @@ return [
                return new WatchedItemQueryService( $services->getDBLoadBalancer() );
        },
 
+       'MediaHandlerFactory' => function( MediaWikiServices $services ) {
+               return new MediaHandlerFactory(
+                       $services->getMainConfig()->get( 'MediaHandlers' )
+               );
+       },
+
        'LinkCache' => function( MediaWikiServices $services ) {
                return new LinkCache(
-                       $services->getTitleFormatter()
+                       $services->getTitleFormatter(),
+                       ObjectCache::getMainWANInstance()
                );
        },
 
        'LinkRendererFactory' => function( MediaWikiServices $services ) {
                return new LinkRendererFactory(
-                       $services->getTitleFormatter()
+                       $services->getTitleFormatter(),
+                       $services->getLinkCache()
                );
        },
 
@@ -202,6 +210,24 @@ return [
                return $services->getService( '_MediaWikiTitleCodec' );
        },
 
+       'VirtualRESTServiceClient' => function( MediaWikiServices $services ) {
+               $config = $services->getMainConfig()->get( 'VirtualRestConfig' );
+
+               $vrsClient = new VirtualRESTServiceClient( new MultiHttpClient( [] ) );
+               foreach ( $config['paths'] as $prefix => $serviceConfig ) {
+                       $class = $serviceConfig['class'];
+                       // Merge in the global defaults
+                       $constructArg = isset( $serviceConfig['options'] )
+                               ? $serviceConfig['options']
+                               : [];
+                       $constructArg += $config['global'];
+                       // Make the VRS service available at the mount point
+                       $vrsClient->mount( $prefix, [ 'class' => $class, 'config' => $constructArg ] );
+               }
+
+               return $vrsClient;
+       },
+
        ///////////////////////////////////////////////////////////////////////////
        // NOTE: When adding a service here, don't forget to add a getter function
        // in the MediaWikiServices class. The convenience getter should just call
index b336795..bad0ef9 100644 (file)
@@ -367,4 +367,12 @@ class ServiceContainer implements DestructibleService {
                return $service;
        }
 
+       /**
+        * @param string $name
+        * @return bool Whether the service is disabled
+        * @since 1.28
+        */
+       public function isServiceDisabled( $name ) {
+               return isset( $this->disabled[$name] );
+       }
 }
index 5877932..2f462b8 100644 (file)
@@ -452,22 +452,6 @@ if ( $wgProfileOnly ) {
        $wgDebugLogFile = '';
 }
 
-// Disable AuthManager API modules if $wgDisableAuthManager
-if ( $wgDisableAuthManager ) {
-       $wgAPIModules += [
-               'clientlogin' => 'ApiDisabled',
-               'createaccount' => 'ApiCreateAccount', // Use the non-AuthManager version
-               'linkaccount' => 'ApiDisabled',
-               'unlinkaccount' => 'ApiDisabled',
-               'changeauthenticationdata' => 'ApiDisabled',
-               'removeauthenticationdata' => 'ApiDisabled',
-               'resetpassword' => 'ApiDisabled',
-       ];
-       $wgAPIMetaModules += [
-               'authmanagerinfo' => 'ApiQueryDisabled',
-       ];
-}
-
 // Backwards compatibility with old password limits
 if ( $wgMinimalPasswordLength !== false ) {
        $wgPasswordPolicy['policies']['default']['MinimalPasswordLength'] = $wgMinimalPasswordLength;
@@ -654,6 +638,9 @@ date_default_timezone_set( $wgLocaltimezone );
 if ( is_null( $wgLocalTZoffset ) ) {
        $wgLocalTZoffset = date( 'Z' ) / 60;
 }
+// The part after the System| is ignored, but rest of MW fills it
+// out as the local offset.
+$wgDefaultUserOptions['timecorrection'] = "System|$wgLocalTZoffset";
 
 if ( !$wgDBerrorLogTZ ) {
        $wgDBerrorLogTZ = $wgLocaltimezone;
@@ -687,10 +674,12 @@ $parserMemc = wfGetParserCacheStorage();
 
 wfDebugLog( 'caches',
        'cluster: ' . get_class( $wgMemc ) .
-       ', WAN: ' . $wgMainWANCache .
+       ', WAN: ' . ( $wgMainWANCache === CACHE_NONE ? 'CACHE_NONE' : $wgMainWANCache ) .
        ', stash: ' . $wgMainStash .
        ', message: ' . get_class( $messageMemc ) .
-       ', parser: ' . get_class( $parserMemc ) );
+       ', parser: ' . get_class( $parserMemc ) .
+       ', session: ' . get_class( ObjectCache::getInstance( $wgSessionCacheType ) )
+);
 
 Profiler::instance()->scopedProfileOut( $ps_memcached );
 
@@ -703,19 +692,16 @@ $ps_globals = Profiler::instance()->scopedProfileIn( $fname . '-globals' );
  * @var Language $wgContLang
  */
 $wgContLang = Language::factory( $wgLanguageCode );
-$wgContLang->initEncoding();
 $wgContLang->initContLang();
 
 // Now that variant lists may be available...
 $wgRequest->interpolateTitle();
 
 if ( !is_object( $wgAuth ) ) {
-       $wgAuth = $wgDisableAuthManager ? new AuthPlugin : new MediaWiki\Auth\AuthManagerAuthPlugin;
+       $wgAuth = new MediaWiki\Auth\AuthManagerAuthPlugin;
        Hooks::run( 'AuthPluginSetup', [ &$wgAuth ] );
 }
-if ( !$wgDisableAuthManager &&
-       $wgAuth && !$wgAuth instanceof MediaWiki\Auth\AuthManagerAuthPlugin
-) {
+if ( $wgAuth && !$wgAuth instanceof MediaWiki\Auth\AuthManagerAuthPlugin ) {
        MediaWiki\Auth\AuthManager::singleton()->forcePrimaryAuthenticationProviders( [
                new MediaWiki\Auth\TemporaryPasswordPrimaryAuthenticationProvider( [
                        'authoritative' => false,
@@ -851,17 +837,13 @@ if ( !defined( 'MW_NO_SESSION' ) && !$wgCommandLineMode ) {
        $sessionUser = MediaWiki\Session\SessionManager::getGlobalSession()->getUser();
        if ( $sessionUser->getId() === 0 && User::isValidUserName( $sessionUser->getName() ) ) {
                $ps_autocreate = Profiler::instance()->scopedProfileIn( $fname . '-autocreate' );
-               if ( $wgDisableAuthManager ) {
-                       $res = MediaWiki\Session\SessionManager::autoCreateUser( $sessionUser );
-               } else {
-                       $res = MediaWiki\Auth\AuthManager::singleton()->autoCreateUser(
-                               $sessionUser,
-                               MediaWiki\Auth\AuthManager::AUTOCREATE_SOURCE_SESSION,
-                               true
-                       );
-               }
+               $res = MediaWiki\Auth\AuthManager::singleton()->autoCreateUser(
+                       $sessionUser,
+                       MediaWiki\Auth\AuthManager::AUTOCREATE_SOURCE_SESSION,
+                       true
+               );
                Profiler::instance()->scopedProfileOut( $ps_autocreate );
-               \MediaWiki\Logger\LoggerFactory::getInstance( 'authmanager' )->info( 'Autocreation attempt', [
+               \MediaWiki\Logger\LoggerFactory::getInstance( 'authevents' )->info( 'Autocreation attempt', [
                        'event' => 'autocreate',
                        'status' => $res,
                ] );
@@ -870,6 +852,10 @@ if ( !defined( 'MW_NO_SESSION' ) && !$wgCommandLineMode ) {
        unset( $sessionUser );
 }
 
+if ( !$wgCommandLineMode ) {
+       Pingback::schedulePingback();
+}
+
 wfDebug( "Fully initialised\n" );
 $wgFullyInitialised = true;
 
index 1a92fb2..885f926 100644 (file)
  *
  * @code
  * $conf = new SiteConfiguration;
- * $conf->wikis = array( 'de', 'en', 'beta' );
+ * $conf->wikis = [ 'de', 'en', 'beta' ];
  * @endcode
  *
  * When configuring the MediaWiki global settings (the $wg variables),
  * the identifiers will be available to specify settings on a per wiki basis.
  *
  * @code
- * $conf->settings = array(
- *     'wgSomeSetting' => array(
+ * $conf->settings = [
+ *     'wgSomeSetting' => [
  *
  *             # production:
  *             'de'     => false,
@@ -52,8 +52,8 @@
  *
  *             # test:
  *             'beta    => true,
- *     ),
- * );
+ *     ],
+ * ];
  * @endcode
  *
  * With three wikis, that is easy to manage. But what about a farm with
  * the above code could be written:
  *
  * @code
- * $conf->settings = array(
- *     'wgSomeSetting' => array(
+ * $conf->settings = [
+ *     'wgSomeSetting' => [
  *
  *             'default' => false,
  *
  *             # Enable feature on test
  *             'beta'    => true,
- *     ),
- * );
+ *     ],
+ * ];
  * @endcode
  *
  *
  * on a per wiki basis.
  *
  * @code
- * $conf->settings = array(
- *     'wgMergeSetting' = array(
+ * $conf->settings = [
+ *     'wgMergeSetting' = [
  *             # Value that will be shared among all wikis:
- *             'default' => array( NS_USER => true ),
+ *             'default' => [ NS_USER => true ],
  *
  *             # Leading '+' means merging the array of value with the defaults
- *             '+beta' => array( NS_HELP => true ),
- *     ),
- * );
+ *             '+beta' => [ NS_HELP => true ],
+ *     ],
+ * ];
  *
  * # Get configuration for the German site:
  * $conf->get( 'wgMergeSetting', 'de' );
- * // --> array( NS_USER => true );
+ * // --> [ NS_USER => true ];
  *
  * # Get configuration for the testing site:
  * $conf->get( 'wgMergeSetting', 'beta' );
- * // --> array( NS_USER => true, NS_HELP => true );
+ * // --> [ NS_USER => true, NS_HELP => true ];
  * @endcode
  *
  * Finally, to load all configuration settings, extract them in global context:
  * extract( $globals );
  * @endcode
  *
+ * @note For WikiMap to function, the configuration must define string values for
+ *  $wgServer (or $wgCanonicalServer) and $wgArticlePath, even if these are the
+ *  same for all wikis or can be correctly determined by the logic in
+ *  Setup.php.
+ *
  * @todo Give examples for,
  * suffixes:
- * $conf->suffixes = array( 'wiki' );
+ * $conf->suffixes = [ 'wiki' ];
  * localVHosts
  * callbacks!
  */
@@ -565,17 +570,6 @@ class SiteConfiguration {
                return $multi ? $res : current( $res );
        }
 
-       /**
-        * Returns true if the given vhost is handled locally.
-        *
-        * @deprecated since 1.25; check if the host is in $wgLocalVirtualHosts instead.
-        * @param string $vhost
-        * @return bool
-        */
-       public function isLocalVHost( $vhost ) {
-               return in_array( $vhost, $this->localVHosts );
-       }
-
        /**
         * Merge multiple arrays together.
         * On encountering duplicate keys, merge the two, but ONLY if they're arrays.
index 6c536dd..ff7875c 100644 (file)
@@ -24,7 +24,7 @@
  * Static accessor class for site_stats and related things
  */
 class SiteStats {
-       /** @var bool|ResultWrapper */
+       /** @var bool|stdClass */
        private static $row;
 
        /** @var bool */
@@ -36,6 +36,10 @@ class SiteStats {
        /** @var int[] */
        private static $pageCount = [];
 
+       static function unload() {
+               self::$loaded = false;
+       }
+
        static function recache() {
                self::load( true );
        }
@@ -55,24 +59,24 @@ class SiteStats {
                        # Update schema
                        $u = new SiteStatsUpdate( 0, 0, 0 );
                        $u->doUpdate();
-                       self::$row = self::doLoad( wfGetDB( DB_SLAVE ) );
+                       self::$row = self::doLoad( wfGetDB( DB_REPLICA ) );
                }
 
                self::$loaded = true;
        }
 
        /**
-        * @return bool|ResultWrapper
+        * @return bool|stdClass
         */
        static function loadAndLazyInit() {
                global $wgMiserMode;
 
-               wfDebug( __METHOD__ . ": reading site_stats from slave\n" );
-               $row = self::doLoad( wfGetDB( DB_SLAVE ) );
+               wfDebug( __METHOD__ . ": reading site_stats from replica DB\n" );
+               $row = self::doLoad( wfGetDB( DB_REPLICA ) );
 
                if ( !self::isSane( $row ) ) {
                        // Might have just been initialized during this request? Underflow?
-                       wfDebug( __METHOD__ . ": site_stats damaged or missing on slave\n" );
+                       wfDebug( __METHOD__ . ": site_stats damaged or missing on replica DB\n" );
                        $row = self::doLoad( wfGetDB( DB_MASTER ) );
                }
 
@@ -83,7 +87,7 @@ class SiteStats {
                        // clean schema with mwdumper.
                        wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" );
 
-                       SiteStatsInit::doAllAndCommit( wfGetDB( DB_SLAVE ) );
+                       SiteStatsInit::doAllAndCommit( wfGetDB( DB_REPLICA ) );
 
                        $row = self::doLoad( wfGetDB( DB_MASTER ) );
                }
@@ -96,7 +100,7 @@ class SiteStats {
 
        /**
         * @param IDatabase $db
-        * @return bool|ResultWrapper
+        * @return bool|stdClass
         */
        static function doLoad( $db ) {
                return $db->selectRow( 'site_stats', [
@@ -107,7 +111,7 @@ class SiteStats {
                                'ss_users',
                                'ss_active_users',
                                'ss_images',
-                       ], false, __METHOD__ );
+                       ], [], __METHOD__ );
        }
 
        /**
@@ -182,7 +186,7 @@ class SiteStats {
                        wfMemcKey( 'SiteStats', 'groupcounts', $group ),
                        $cache::TTL_HOUR,
                        function ( $oldValue, &$ttl, array &$setOpts ) use ( $group ) {
-                               $dbr = wfGetDB( DB_SLAVE );
+                               $dbr = wfGetDB( DB_REPLICA );
 
                                $setOpts += Database::getCacheSetOptions( $dbr );
 
@@ -225,7 +229,7 @@ class SiteStats {
         */
        static function pagesInNs( $ns ) {
                if ( !isset( self::$pageCount[$ns] ) ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        self::$pageCount[$ns] = (int)$dbr->selectField(
                                'page',
                                'COUNT(*)',
@@ -292,7 +296,7 @@ class SiteStatsInit {
                } elseif ( $database ) {
                        $this->db = wfGetDB( DB_MASTER );
                } else {
-                       $this->db = wfGetDB( DB_SLAVE, 'vslow' );
+                       $this->db = wfGetDB( DB_REPLICA, 'vslow' );
                }
        }
 
index 45d8bed..e578873 100644 (file)
@@ -385,7 +385,7 @@ class Status {
         *
         * @return array A list in which each entry is an array with a message key as its first element.
         *         The remaining array elements are the message parameters.
-        * @deprecated 1.25
+        * @deprecated since 1.25
         */
        public function getErrorsArray() {
                return $this->getStatusArray( 'error' );
@@ -396,7 +396,7 @@ class Status {
         *
         * @return array A list in which each entry is an array with a message key as its first element.
         *         The remaining array elements are the message parameters.
-        * @deprecated 1.25
+        * @deprecated since 1.25
         */
        public function getWarningsArray() {
                return $this->getStatusArray( 'warning' );
index 8d0b8f1..0fc7980 100644 (file)
  * Functions related to the output of file content
  */
 class StreamFile {
-       const READY_STREAM = 1;
-       const NOT_MODIFIED = 2;
+       // Do not send any HTTP headers unless requested by caller (e.g. body only)
+       const STREAM_HEADLESS = 1;
+       // Do not try to tear down any PHP output buffers
+       const STREAM_ALLOW_OB = 2;
 
        /**
         * Stream a file to the browser, adding all the headings and fun stuff.
@@ -33,107 +35,183 @@ class StreamFile {
         * and Content-Disposition.
         *
         * @param string $fname Full name and path of the file to stream
-        * @param array $headers Any additional headers to send
+        * @param array $headers Any additional headers to send if the file exists
         * @param bool $sendErrors Send error messages if errors occur (like 404)
+        * @param array $optHeaders HTTP request header map (e.g. "range") (use lowercase keys)
+        * @param integer $flags Bitfield of STREAM_* constants
         * @throws MWException
         * @return bool Success
         */
-       public static function stream( $fname, $headers = [], $sendErrors = true ) {
+       public static function stream(
+               $fname, $headers = [], $sendErrors = true, $optHeaders = [], $flags = 0
+       ) {
+               $section = new ProfileSection( __METHOD__ );
 
                if ( FileBackend::isStoragePath( $fname ) ) { // sanity
                        throw new MWException( __FUNCTION__ . " given storage path '$fname'." );
                }
 
-               MediaWiki\suppressWarnings();
-               $stat = stat( $fname );
-               MediaWiki\restoreWarnings();
-
-               $res = self::prepareForStream( $fname, $stat, $headers, $sendErrors );
-               if ( $res == self::NOT_MODIFIED ) {
-                       $ok = true; // use client cache
-               } elseif ( $res == self::READY_STREAM ) {
-                       $ok = readfile( $fname );
-               } else {
-                       $ok = false; // failed
+               // Don't stream it out as text/html if there was a PHP error
+               if ( ( ( $flags & self::STREAM_HEADLESS ) == 0 || $headers ) && headers_sent() ) {
+                       echo "Headers already sent, terminating.\n";
+                       return false;
                }
 
-               return $ok;
-       }
+               $headerFunc = ( $flags & self::STREAM_HEADLESS )
+                       ? function ( $header ) {
+                                // no-op
+                       }
+                       : function ( $header ) {
+                               is_int( $header ) ? HttpStatus::header( $header ) : header( $header );
+                       };
+
+               MediaWiki\suppressWarnings();
+               $info = stat( $fname );
+               MediaWiki\restoreWarnings();
 
-       /**
-        * Call this function used in preparation before streaming a file.
-        * This function does the following:
-        * (a) sends Last-Modified, Content-type, and Content-Disposition headers
-        * (b) cancels any PHP output buffering and automatic gzipping of output
-        * (c) sends Content-Length header based on HTTP_IF_MODIFIED_SINCE check
-        *
-        * @param string $path Storage path or file system path
-        * @param array|bool $info File stat info with 'mtime' and 'size' fields
-        * @param array $headers Additional headers to send
-        * @param bool $sendErrors Send error messages if errors occur (like 404)
-        * @return int|bool READY_STREAM, NOT_MODIFIED, or false on failure
-        */
-       public static function prepareForStream(
-               $path, $info, $headers = [], $sendErrors = true
-       ) {
                if ( !is_array( $info ) ) {
                        if ( $sendErrors ) {
-                               HttpStatus::header( 404 );
-                               header( 'Cache-Control: no-cache' );
-                               header( 'Content-Type: text/html; charset=utf-8' );
-                               $encFile = htmlspecialchars( $path );
-                               $encScript = htmlspecialchars( $_SERVER['SCRIPT_NAME'] );
-                               echo "<html><body>
-                                       <h1>File not found</h1>
-                                       <p>Although this PHP script ($encScript) exists, the file requested for output
-                                       ($encFile) does not.</p>
-                                       </body></html>
-                                       ";
+                               self::send404Message( $fname, $flags );
                        }
                        return false;
                }
 
-               // Sent Last-Modified HTTP header for client-side caching
-               header( 'Last-Modified: ' . wfTimestamp( TS_RFC2822, $info['mtime'] ) );
+               // Send Last-Modified HTTP header for client-side caching
+               $headerFunc( 'Last-Modified: ' . wfTimestamp( TS_RFC2822, $info['mtime'] ) );
 
-               // Cancel output buffering and gzipping if set
-               wfResetOutputBuffers();
+               if ( ( $flags & self::STREAM_ALLOW_OB ) == 0 ) {
+                       // Cancel output buffering and gzipping if set
+                       wfResetOutputBuffers();
+               }
 
-               $type = self::contentTypeFromPath( $path );
+               $type = self::contentTypeFromPath( $fname );
                if ( $type && $type != 'unknown/unknown' ) {
-                       header( "Content-type: $type" );
+                       $headerFunc( "Content-type: $type" );
                } else {
                        // Send a content type which is not known to Internet Explorer, to
                        // avoid triggering IE's content type detection. Sending a standard
                        // unknown content type here essentially gives IE license to apply
                        // whatever content type it likes.
-                       header( 'Content-type: application/x-wiki' );
+                       $headerFunc( 'Content-type: application/x-wiki' );
                }
 
-               // Don't stream it out as text/html if there was a PHP error
-               if ( headers_sent() ) {
-                       echo "Headers already sent, terminating.\n";
-                       return false;
+               // Don't send if client has up to date cache
+               if ( isset( $optHeaders['if-modified-since'] ) ) {
+                       $modsince = preg_replace( '/;.*$/', '', $optHeaders['if-modified-since'] );
+                       if ( wfTimestamp( TS_UNIX, $info['mtime'] ) <= strtotime( $modsince ) ) {
+                               ini_set( 'zlib.output_compression', 0 );
+                               $headerFunc( 304 );
+                               return true; // ok
+                       }
                }
 
                // Send additional headers
                foreach ( $headers as $header ) {
-                       header( $header );
+                       header( $header ); // always use header(); specifically requested
                }
 
-               // Don't send if client has up to date cache
-               if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
-                       $modsince = preg_replace( '/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
-                       if ( wfTimestamp( TS_UNIX, $info['mtime'] ) <= strtotime( $modsince ) ) {
-                               ini_set( 'zlib.output_compression', 0 );
-                               HttpStatus::header( 304 );
-                               return self::NOT_MODIFIED; // ok
+               if ( isset( $optHeaders['range'] ) ) {
+                       $range = self::parseRange( $optHeaders['range'], $info['size'] );
+                       if ( is_array( $range ) ) {
+                               $headerFunc( 206 );
+                               $headerFunc( 'Content-Length: ' . $range[2] );
+                               $headerFunc( "Content-Range: bytes {$range[0]}-{$range[1]}/{$info['size']}" );
+                       } elseif ( $range === 'invalid' ) {
+                               if ( $sendErrors ) {
+                                       $headerFunc( 416 );
+                                       $headerFunc( 'Cache-Control: no-cache' );
+                                       $headerFunc( 'Content-Type: text/html; charset=utf-8' );
+                                       $headerFunc( 'Content-Range: bytes */' . $info['size'] );
+                               }
+                               return false;
+                       } else { // unsupported Range request (e.g. multiple ranges)
+                               $range = null;
+                               $headerFunc( 'Content-Length: ' . $info['size'] );
+                       }
+               } else {
+                       $range = null;
+                       $headerFunc( 'Content-Length: ' . $info['size'] );
+               }
+
+               if ( is_array( $range ) ) {
+                       $handle = fopen( $fname, 'rb' );
+                       if ( $handle ) {
+                               $ok = true;
+                               fseek( $handle, $range[0] );
+                               $remaining = $range[2];
+                               while ( $remaining > 0 && $ok ) {
+                                       $bytes = min( $remaining, 8 * 1024 );
+                                       $data = fread( $handle, $bytes );
+                                       $remaining -= $bytes;
+                                       $ok = ( $data !== false );
+                                       print $data;
+                               }
+                       } else {
+                               return false;
                        }
+               } else {
+                       return readfile( $fname ) !== false; // faster
                }
 
-               header( 'Content-Length: ' . $info['size'] );
+               return true;
+       }
+
+       /**
+        * Send out a standard 404 message for a file
+        *
+        * @param string $fname Full name and path of the file to stream
+        * @param integer $flags Bitfield of STREAM_* constants
+        * @since 1.24
+        */
+       public static function send404Message( $fname, $flags = 0 ) {
+               if ( ( $flags & self::STREAM_HEADLESS ) == 0 ) {
+                       HttpStatus::header( 404 );
+                       header( 'Cache-Control: no-cache' );
+                       header( 'Content-Type: text/html; charset=utf-8' );
+               }
+               $encFile = htmlspecialchars( $fname );
+               $encScript = htmlspecialchars( $_SERVER['SCRIPT_NAME'] );
+               echo "<!DOCTYPE html><html><body>
+                       <h1>File not found</h1>
+                       <p>Although this PHP script ($encScript) exists, the file requested for output
+                       ($encFile) does not.</p>
+                       </body></html>
+                       ";
+       }
 
-               return self::READY_STREAM; // ok
+       /**
+        * Convert a Range header value to an absolute (start, end) range tuple
+        *
+        * @param string $range Range header value
+        * @param integer $size File size
+        * @return array|string Returns error string on failure (start, end, length)
+        * @since 1.24
+        */
+       public static function parseRange( $range, $size ) {
+               $m = [];
+               if ( preg_match( '#^bytes=(\d*)-(\d*)$#', $range, $m ) ) {
+                       list( , $start, $end ) = $m;
+                       if ( $start === '' && $end === '' ) {
+                               $absRange = [ 0, $size - 1 ];
+                       } elseif ( $start === '' ) {
+                               $absRange = [ $size - $end, $size - 1 ];
+                       } elseif ( $end === '' ) {
+                               $absRange = [ $start, $size - 1 ];
+                       } else {
+                               $absRange = [ $start, $end ];
+                       }
+                       if ( $absRange[0] >= 0 && $absRange[1] >= $absRange[0] ) {
+                               if ( $absRange[0] < $size ) {
+                                       $absRange[1] = min( $absRange[1], $size - 1 ); // stop at EOF
+                                       $absRange[2] = $absRange[1] - $absRange[0] + 1;
+                                       return $absRange;
+                               } elseif ( $absRange[0] == 0 && $size == 0 ) {
+                                       return 'unrecognized'; // the whole file should just be sent
+                               }
+                       }
+                       return 'invalid';
+               }
+               return 'unrecognized';
        }
 
        /**
index 0b4d048..0210ed9 100644 (file)
@@ -48,6 +48,9 @@ class StubObject {
        /** @var null|string */
        protected $class;
 
+       /** @var null|callable */
+       protected $factory;
+
        /** @var array */
        protected $params;
 
@@ -55,12 +58,17 @@ class StubObject {
         * Constructor.
         *
         * @param string $global Name of the global variable.
-        * @param string $class Name of the class of the real object.
+        * @param string|callable $class Name of the class of the real object
+        *                               or a factory function to call
         * @param array $params Parameters to pass to constructor of the real object.
         */
        public function __construct( $global = null, $class = null, $params = [] ) {
                $this->global = $global;
-               $this->class = $class;
+               if ( is_callable( $class ) ) {
+                       $this->factory = $class;
+               } else {
+                       $this->class = $class;
+               }
                $this->params = $params;
        }
 
@@ -110,8 +118,10 @@ class StubObject {
         * @return object
         */
        public function _newObject() {
-               return ObjectFactory::getObjectFromSpec( [
-                       'class' => $this->class,
+               $params = $this->factory
+                       ? [ 'factory' => $this->factory ]
+                       : [ 'class' => $this->class ];
+               return ObjectFactory::getObjectFromSpec( $params + [
                        'args' => $this->params,
                        'closure_expansion' => false,
                ] );
index f291a69..3475b26 100644 (file)
@@ -394,7 +394,7 @@ class Title implements LinkTarget {
         * @return Title|null The new object, or null on an error
         */
        public static function newFromID( $id, $flags = 0 ) {
-               $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+               $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
                $row = $db->selectRow(
                        'page',
                        self::getSelectFields(),
@@ -419,7 +419,7 @@ class Title implements LinkTarget {
                if ( !count( $ids ) ) {
                        return [];
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $res = $dbr->select(
                        'page',
@@ -500,7 +500,7 @@ class Title implements LinkTarget {
         * @param string $interwiki The interwiki prefix
         * @return Title The new object
         */
-       public static function &makeTitle( $ns, $title, $fragment = '', $interwiki = '' ) {
+       public static function makeTitle( $ns, $title, $fragment = '', $interwiki = '' ) {
                $t = new Title();
                $t->mInterwiki = $interwiki;
                $t->mFragment = $fragment;
@@ -561,7 +561,7 @@ class Title implements LinkTarget {
         * @return Title|null An object representing the article, or null if no such article was found
         */
        public static function nameOf( $id ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $s = $dbr->selectRow(
                        'page',
@@ -751,12 +751,12 @@ class Title implements LinkTarget {
        /**
         * Callback for usort() to do title sorts by (namespace, title)
         *
-        * @param Title $a
-        * @param Title $b
+        * @param LinkTarget $a
+        * @param LinkTarget $b
         *
         * @return int Result of string comparison, or namespace comparison
         */
-       public static function compare( $a, $b ) {
+       public static function compare( LinkTarget $a, LinkTarget $b ) {
                if ( $a->getNamespace() == $b->getNamespace() ) {
                        return strcmp( $a->getText(), $b->getText() );
                } else {
@@ -1773,12 +1773,13 @@ class Title implements LinkTarget {
         *
         * @param array $query
         * @param bool $query2
-        * @param string $proto Protocol to use; setting this will cause a full URL to be used
+        * @param string|int|bool $proto A PROTO_* constant on how the URL should be expanded,
+        *                               or false (default) for no expansion
         * @see self::getLocalURL for the arguments.
         * @return string The URL
         */
-       public function getLinkURL( $query = '', $query2 = false, $proto = PROTO_RELATIVE ) {
-               if ( $this->isExternal() || $proto !== PROTO_RELATIVE ) {
+       public function getLinkURL( $query = '', $query2 = false, $proto = false ) {
+               if ( $this->isExternal() || $proto !== false ) {
                        $ret = $this->getFullURL( $query, $query2, $proto );
                } elseif ( $this->getPrefixedText() === '' && $this->hasFragment() ) {
                        $ret = $this->getFragmentForURL();
@@ -1885,8 +1886,8 @@ class Title implements LinkTarget {
         * @param string $action Action that permission needs to be checked for
         * @param User $user User to check
         * @param string $rigor One of (quick,full,secure)
-        *   - quick  : does cheap permission checks from slaves (usable for GUI creation)
-        *   - full   : does cheap and expensive checks possibly from a slave
+        *   - quick  : does cheap permission checks from replica DBs (usable for GUI creation)
+        *   - full   : does cheap and expensive checks possibly from a replica DB
         *   - secure : does cheap and expensive checks, using the master as needed
         * @param array $ignoreErrors Array of Strings Set this to a list of message keys
         *   whose corresponding errors may be ignored.
@@ -2270,13 +2271,17 @@ class Title implements LinkTarget {
         * @return array List of errors
         */
        private function checkUserBlock( $action, $user, $errors, $rigor, $short ) {
+               global $wgEmailConfirmToEdit, $wgBlockDisablesLogin;
                // Account creation blocks handled at userlogin.
                // Unblocking handled in SpecialUnblock
                if ( $rigor === 'quick' || in_array( $action, [ 'createaccount', 'unblock' ] ) ) {
                        return $errors;
                }
 
-               global $wgEmailConfirmToEdit;
+               // Optimize for a very common case
+               if ( $action === 'read' && !$wgBlockDisablesLogin ) {
+                       return $errors;
+               }
 
                if ( $wgEmailConfirmToEdit && !$user->isEmailConfirmed() ) {
                        $errors[] = [ 'confirmedittext' ];
@@ -2318,8 +2323,8 @@ class Title implements LinkTarget {
                        # If the user is allowed to read pages, he is allowed to read all pages
                        $whitelisted = true;
                } elseif ( $this->isSpecial( 'Userlogin' )
-                       || $this->isSpecial( 'ChangePassword' )
                        || $this->isSpecial( 'PasswordReset' )
+                       || $this->isSpecial( 'Userlogout' )
                ) {
                        # Always grant access to the login page.
                        # Even anons need to be able to log in.
@@ -2411,8 +2416,8 @@ class Title implements LinkTarget {
         * @param string $action Action that permission needs to be checked for
         * @param User $user User to check
         * @param string $rigor One of (quick,full,secure)
-        *   - quick  : does cheap permission checks from slaves (usable for GUI creation)
-        *   - full   : does cheap and expensive checks possibly from a slave
+        *   - quick  : does cheap permission checks from replica DBs (usable for GUI creation)
+        *   - full   : does cheap and expensive checks possibly from a replica DB
         *   - secure : does cheap and expensive checks, using the master as needed
         * @param bool $short Set this to true to stop after the first permission error.
         * @return array Array of arrays of the arguments to wfMessage to explain permissions problems.
@@ -2433,6 +2438,7 @@ class Title implements LinkTarget {
                        $checks = [
                                'checkPermissionHooks',
                                'checkReadPermissions',
+                               'checkUserBlock', // for wgBlockDisablesLogin
                        ];
                # Don't call checkSpecialsAndNSPermissions or checkCSSandJSPermissions
                # here as it will lead to duplicate error messages. This is okay to do
@@ -2534,7 +2540,7 @@ class Title implements LinkTarget {
                }
 
                if ( $this->mTitleProtection === null ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $res = $dbr->select(
                                'protected_titles',
                                [
@@ -2702,7 +2708,7 @@ class Title implements LinkTarget {
                        return [ $this->mHasCascadingRestrictions, $pagerestrictions ];
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                if ( $this->getNamespace() == NS_FILE ) {
                        $tables = [ 'imagelinks', 'page_restrictions' ];
@@ -2869,7 +2875,7 @@ class Title implements LinkTarget {
         *   restrictions from page table (pre 1.10)
         */
        public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $restrictionTypes = $this->getRestrictionTypes();
 
@@ -2943,7 +2949,7 @@ class Title implements LinkTarget {
         */
        public function loadRestrictions( $oldFashionedRestrictions = null ) {
                if ( !$this->mRestrictionsLoaded ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        if ( $this->exists() ) {
                                $res = $dbr->select(
                                        'page_restrictions',
@@ -3063,7 +3069,7 @@ class Title implements LinkTarget {
                        return [];
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $conds['page_namespace'] = $this->getNamespace();
                $conds[] = 'page_title ' . $dbr->buildLike( $this->getDBkey() . '/', $dbr->anyString() );
                $options = [];
@@ -3090,7 +3096,7 @@ class Title implements LinkTarget {
                if ( $this->getNamespace() < 0 ) {
                        $n = 0;
                } else {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
 
                        $n = $dbr->selectField( 'archive', 'COUNT(*)',
                                [ 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ],
@@ -3115,7 +3121,7 @@ class Title implements LinkTarget {
                if ( $this->getNamespace() < 0 ) {
                        return false;
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $deleted = (bool)$dbr->selectField( 'archive', '1',
                        [ 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ],
                        __METHOD__
@@ -3369,7 +3375,7 @@ class Title implements LinkTarget {
                if ( count( $options ) > 0 ) {
                        $db = wfGetDB( DB_MASTER );
                } else {
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                }
 
                $res = $db->select(
@@ -3431,7 +3437,7 @@ class Title implements LinkTarget {
                        return [];
                }
 
-               $db = wfGetDB( DB_SLAVE );
+               $db = wfGetDB( DB_REPLICA );
 
                $blNamespace = "{$prefix}_namespace";
                $blTitle = "{$prefix}_title";
@@ -3494,7 +3500,7 @@ class Title implements LinkTarget {
                        return [];
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select(
                        [ 'page', 'pagelinks' ],
                        [ 'pl_namespace', 'pl_title' ],
@@ -3854,7 +3860,7 @@ class Title implements LinkTarget {
                        return $data;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $res = $dbr->select(
                        'categorylinks',
@@ -3922,7 +3928,7 @@ class Title implements LinkTarget {
         * @return int|bool Old revision ID, or false if none exists
         */
        public function getPreviousRevisionID( $revId, $flags = 0 ) {
-               $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+               $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
                $revId = $db->selectField( 'revision', 'rev_id',
                        [
                                'rev_page' => $this->getArticleID( $flags ),
@@ -3947,7 +3953,7 @@ class Title implements LinkTarget {
         * @return int|bool Next revision ID, or false if none exists
         */
        public function getNextRevisionID( $revId, $flags = 0 ) {
-               $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+               $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
                $revId = $db->selectField( 'revision', 'rev_id',
                        [
                                'rev_page' => $this->getArticleID( $flags ),
@@ -3973,7 +3979,7 @@ class Title implements LinkTarget {
        public function getFirstRevision( $flags = 0 ) {
                $pageId = $this->getArticleID( $flags );
                if ( $pageId ) {
-                       $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+                       $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA );
                        $row = $db->selectRow( 'revision', Revision::selectFields(),
                                [ 'rev_page' => $pageId ],
                                __METHOD__,
@@ -4003,7 +4009,7 @@ class Title implements LinkTarget {
         * @return bool
         */
        public function isNewPage() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                return (bool)$dbr->selectField( 'page', 'page_is_new', $this->pageCond(), __METHOD__ );
        }
 
@@ -4020,7 +4026,7 @@ class Title implements LinkTarget {
                }
 
                if ( $this->mIsBigDeletion === null ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
 
                        $revCount = $dbr->selectRowCount(
                                'revision',
@@ -4047,7 +4053,7 @@ class Title implements LinkTarget {
                }
 
                if ( $this->mEstimateRevisions === null ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $this->mEstimateRevisions = $dbr->estimateRowCount( 'revision', '*',
                                [ 'rev_page' => $this->getArticleID() ], __METHOD__ );
                }
@@ -4074,7 +4080,7 @@ class Title implements LinkTarget {
                if ( !$old || !$new ) {
                        return 0; // nothing to compare
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $conds = [
                        'rev_page' => $this->getArticleID(),
                        'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ),
@@ -4152,7 +4158,7 @@ class Title implements LinkTarget {
                        }
                        return $authors;
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
                        [
                                'rev_page' => $this->getArticleID(),
@@ -4366,18 +4372,24 @@ class Title implements LinkTarget {
                        return true; // avoid gap locking if we know it's not there
                }
 
-               $method = __METHOD__;
-               $dbw = wfGetDB( DB_MASTER );
                $conds = $this->pageCond();
-               $dbw->onTransactionIdle( function () use ( $dbw, $conds, $method, $purgeTime ) {
-                       $dbTimestamp = $dbw->timestamp( $purgeTime ?: time() );
-                       $dbw->update(
-                               'page',
-                               [ 'page_touched' => $dbTimestamp ],
-                               $conds + [ 'page_touched < ' . $dbw->addQuotes( $dbTimestamp ) ],
-                               $method
-                       );
-               } );
+               DeferredUpdates::addUpdate(
+                       new AutoCommitUpdate(
+                               wfGetDB( DB_MASTER ),
+                               __METHOD__,
+                               function ( IDatabase $dbw, $fname ) use ( $conds, $purgeTime ) {
+                                       $dbTimestamp = $dbw->timestamp( $purgeTime ?: time() );
+                                       $dbw->update(
+                                               'page',
+                                               [ 'page_touched' => $dbTimestamp ],
+                                               $conds + [ 'page_touched < ' . $dbw->addQuotes( $dbTimestamp ) ],
+                                               $fname
+                                       );
+                                       MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $this );
+                               }
+                       ),
+                       DeferredUpdates::PRESEND
+               );
 
                return true;
        }
@@ -4402,7 +4414,7 @@ class Title implements LinkTarget {
         */
        public function getTouched( $db = null ) {
                if ( $db === null ) {
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                }
                $touched = $db->selectField( 'page', 'page_touched', $this->pageCond(), __METHOD__ );
                return $touched;
@@ -4486,7 +4498,7 @@ class Title implements LinkTarget {
        public function getRedirectsHere( $ns = null ) {
                $redirs = [];
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $where = [
                        'rd_namespace' => $this->getNamespace(),
                        'rd_title' => $this->getDBkey(),
@@ -4553,10 +4565,10 @@ class Title implements LinkTarget {
         * @return bool
         */
        public function canUseNoindex() {
-               global $wgContentNamespaces, $wgExemptFromUserRobotsControl;
+               global $wgExemptFromUserRobotsControl;
 
                $bannedNamespaces = is_null( $wgExemptFromUserRobotsControl )
-                       ? $wgContentNamespaces
+                       ? MWNamespace::getContentNamespaces()
                        : $wgExemptFromUserRobotsControl;
 
                return !in_array( $this->mNamespace, $bannedNamespaces );
index b070e1e..bfd1d61 100644 (file)
@@ -156,54 +156,6 @@ class WatchedItem {
                return new self( $user, $title, self::DEPRECATED_USAGE_TIMESTAMP, (bool)$checkRights );
        }
 
-       /**
-        * @deprecated since 1.27 Use WatchedItemStore::resetNotificationTimestamp()
-        */
-       public function resetNotificationTimestamp( $force = '', $oldid = 0 ) {
-               wfDeprecated( __METHOD__, '1.27' );
-               if ( $this->checkRights && !$this->user->isAllowed( 'editmywatchlist' ) ) {
-                       return;
-               }
-               MediaWikiServices::getInstance()->getWatchedItemStore()->resetNotificationTimestamp(
-                       $this->user,
-                       $this->getTitle(),
-                       $force,
-                       $oldid
-               );
-       }
-
-       /**
-        * @deprecated since 1.27 Use WatchedItemStore::addWatchBatch()
-        */
-       public static function batchAddWatch( array $items ) {
-               wfDeprecated( __METHOD__, '1.27' );
-               if ( !$items ) {
-                       return false;
-               }
-
-               $targets = [];
-               $users = [];
-               /** @var WatchedItem $watchedItem */
-               foreach ( $items as $watchedItem ) {
-                       $user = $watchedItem->getUser();
-                       if ( $watchedItem->checkRights && !$user->isAllowed( 'editmywatchlist' ) ) {
-                               continue;
-                       }
-                       $userId = $user->getId();
-                       $users[$userId] = $user;
-                       $targets[$userId][] = $watchedItem->getTitle()->getSubjectPage();
-                       $targets[$userId][] = $watchedItem->getTitle()->getTalkPage();
-               }
-
-               $store = MediaWikiServices::getInstance()->getWatchedItemStore();
-               $success = true;
-               foreach ( $users as $userId => $user ) {
-                       $success &= $store->addWatchBatchForUser( $user, $targets[$userId] );
-               }
-
-               return $success;
-       }
-
        /**
         * @deprecated since 1.27 Use User::addWatch()
         * @return bool
index 14d6aac..4ac4dea 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use MediaWiki\Linker\LinkTarget;
 use Wikimedia\Assert\Assert;
 
 /**
@@ -25,8 +26,8 @@ class WatchedItemQueryService {
        const INCLUDE_SIZES = 'sizes';
        const INCLUDE_LOG_INFO = 'loginfo';
 
-       // FILTER_* constants are part of public API (are used
-       // in ApiQueryWatchlist class) and should not be changed.
+       // FILTER_* constants are part of public API (are used in ApiQueryWatchlist and
+       // ApiQueryWatchlistRaw classes) and should not be changed.
        // Changing values of those constants will result in a breaking change in the API
        const FILTER_MINOR = 'minor';
        const FILTER_NOT_MINOR = '!minor';
@@ -38,6 +39,11 @@ class WatchedItemQueryService {
        const FILTER_NOT_PATROLLED = '!patrolled';
        const FILTER_UNREAD = 'unread';
        const FILTER_NOT_UNREAD = '!unread';
+       const FILTER_CHANGED = 'changed';
+       const FILTER_NOT_CHANGED = '!changed';
+
+       const SORT_ASC = 'ASC';
+       const SORT_DESC = 'DESC';
 
        /**
         * @var LoadBalancer
@@ -53,7 +59,7 @@ class WatchedItemQueryService {
         * @throws MWException
         */
        private function getConnection() {
-               return $this->loadBalancer->getConnection( DB_SLAVE, [ 'watchlist' ] );
+               return $this->loadBalancer->getConnection( DB_REPLICA, [ 'watchlist' ] );
        }
 
        /**
@@ -161,10 +167,10 @@ class WatchedItemQueryService {
 
                $db = $this->getConnection();
 
-               $fields = $this->getFields( $options );
-               $conds = $this->getConds( $db, $user, $options );
-               $dbOptions = $this->getDbOptions( $options );
-               $joinConds = $this->getJoinConds( $options );
+               $fields = $this->getWatchedItemsWithRCInfoQueryFields( $options );
+               $conds = $this->getWatchedItemsWithRCInfoQueryConds( $db, $user, $options );
+               $dbOptions = $this->getWatchedItemsWithRCInfoQueryDbOptions( $options );
+               $joinConds = $this->getWatchedItemsWithRCInfoQueryJoinConds( $options );
 
                $res = $db->select(
                        $tables,
@@ -192,6 +198,81 @@ class WatchedItemQueryService {
                return $items;
        }
 
+       /**
+        * For simple listing of user's watchlist items, see WatchedItemStore::getWatchedItemsForUser
+        *
+        * @param User $user
+        * @param array $options Allowed keys:
+        *        'sort'         => string optional sorting by namespace ID and title
+        *                          one of the self::SORT_* constants
+        *        'namespaceIds' => int[] optional namespace IDs to filter by (defaults to all namespaces)
+        *        'limit'        => int maximum number of items to return
+        *        'filter'       => string optional filter, one of the self::FILTER_* contants
+        *        'from'         => LinkTarget requires 'sort' key, only return items starting from
+        *                          those related to the link target
+        *        'until'        => LinkTarget requires 'sort' key, only return items until
+        *                          those related to the link target
+        *        'startFrom'    => LinkTarget requires 'sort' key, only return items starting from
+        *                          those related to the link target, allows to skip some link targets
+        *                          specified using the form option
+        * @return WatchedItem[]
+        */
+       public function getWatchedItemsForUser( User $user, array $options = [] ) {
+               if ( $user->isAnon() ) {
+                       // TODO: should this just return an empty array or rather complain loud at this point
+                       // as e.g. ApiBase::getWatchlistUser does?
+                       return [];
+               }
+
+               $options += [ 'namespaceIds' => [] ];
+
+               Assert::parameter(
+                       !isset( $options['sort'] ) || in_array( $options['sort'], [ self::SORT_ASC, self::SORT_DESC ] ),
+                       '$options[\'sort\']',
+                       'must be SORT_ASC or SORT_DESC'
+               );
+               Assert::parameter(
+                       !isset( $options['filter'] ) || in_array(
+                               $options['filter'], [ self::FILTER_CHANGED, self::FILTER_NOT_CHANGED ]
+                       ),
+                       '$options[\'filter\']',
+                       'must be FILTER_CHANGED or FILTER_NOT_CHANGED'
+               );
+               Assert::parameter(
+                       !isset( $options['from'] ) && !isset( $options['until'] ) && !isset( $options['startFrom'] )
+                       || isset( $options['sort'] ),
+                       '$options[\'sort\']',
+                       'must be provided if any of "from", "until", "startFrom" options is provided'
+               );
+
+               $db = $this->getConnection();
+
+               $conds = $this->getWatchedItemsForUserQueryConds( $db, $user, $options );
+               $dbOptions = $this->getWatchedItemsForUserQueryDbOptions( $options );
+
+               $res = $db->select(
+                       'watchlist',
+                       [ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
+                       $conds,
+                       __METHOD__,
+                       $dbOptions
+               );
+
+               $this->reuseConnection( $db );
+
+               $watchedItems = [];
+               foreach ( $res as $row ) {
+                       // todo these could all be cached at some point?
+                       $watchedItems[] = new WatchedItem(
+                               $user,
+                               new TitleValue( (int)$row->wl_namespace, $row->wl_title ),
+                               $row->wl_notificationtimestamp
+                       );
+               }
+
+               return $watchedItems;
+       }
+
        private function getRecentChangeFieldsFromRow( stdClass $row ) {
                // This can be simplified to single array_filter call filtering by key value,
                // once we stop supporting PHP 5.5
@@ -205,7 +286,7 @@ class WatchedItemQueryService {
                return array_intersect_key( $allFields, array_flip( $rcKeys ) );
        }
 
-       private function getFields( array $options ) {
+       private function getWatchedItemsWithRCInfoQueryFields( array $options ) {
                $fields = [
                        'rc_id',
                        'rc_namespace',
@@ -255,7 +336,11 @@ class WatchedItemQueryService {
                return $fields;
        }
 
-       private function getConds( DatabaseBase $db, User $user, array $options ) {
+       private function getWatchedItemsWithRCInfoQueryConds(
+               DatabaseBase $db,
+               User $user,
+               array $options
+       ) {
                $watchlistOwnerId = $this->getWatchlistOwnerId( $user, $options );
                $conds = [ 'wl_user' => $watchlistOwnerId ];
 
@@ -274,7 +359,10 @@ class WatchedItemQueryService {
                        $conds['rc_type'] = array_map( 'intval',  $options['rcTypes'] );
                }
 
-               $conds = array_merge( $conds, $this->getFilterConds( $user, $options ) );
+               $conds = array_merge(
+                       $conds,
+                       $this->getWatchedItemsWithRCInfoQueryFilterConds( $user, $options )
+               );
 
                $conds = array_merge( $conds, $this->getStartEndConds( $db, $options ) );
 
@@ -316,7 +404,7 @@ class WatchedItemQueryService {
                return $user->getId();
        }
 
-       private function getFilterConds( User $user, array $options ) {
+       private function getWatchedItemsWithRCInfoQueryFilterConds( User $user, array $options ) {
                $conds = [];
 
                if ( in_array( self::FILTER_MINOR, $options['filters'] ) ) {
@@ -441,7 +529,62 @@ class WatchedItemQueryService {
                );
        }
 
-       private function getDbOptions( array $options ) {
+       private function getWatchedItemsForUserQueryConds( DatabaseBase $db, User $user, array $options ) {
+               $conds = [ 'wl_user' => $user->getId() ];
+               if ( $options['namespaceIds'] ) {
+                       $conds['wl_namespace'] = array_map( 'intval', $options['namespaceIds'] );
+               }
+               if ( isset( $options['filter'] ) ) {
+                       $filter = $options['filter'];
+                       if ( $filter ===  self::FILTER_CHANGED ) {
+                               $conds[] = 'wl_notificationtimestamp IS NOT NULL';
+                       } else {
+                               $conds[] = 'wl_notificationtimestamp IS NULL';
+                       }
+               }
+
+               if ( isset( $options['from'] ) ) {
+                       $op = $options['sort'] === self::SORT_ASC ? '>' : '<';
+                       $conds[] = $this->getFromUntilTargetConds( $db, $options['from'], $op );
+               }
+               if ( isset( $options['until'] ) ) {
+                       $op = $options['sort'] === self::SORT_ASC ? '<' : '>';
+                       $conds[] = $this->getFromUntilTargetConds( $db, $options['until'], $op );
+               }
+               if ( isset( $options['startFrom'] ) ) {
+                       $op = $options['sort'] === self::SORT_ASC ? '>' : '<';
+                       $conds[] = $this->getFromUntilTargetConds( $db, $options['startFrom'], $op );
+               }
+
+               return $conds;
+       }
+
+       /**
+        * Creates a query condition part for getting only items before or after the given link target
+        * (while ordering using $sort mode)
+        *
+        * @param DatabaseBase $db
+        * @param LinkTarget $target
+        * @param string $op comparison operator to use in the conditions
+        * @return string
+        */
+       private function getFromUntilTargetConds( DatabaseBase $db, LinkTarget $target, $op ) {
+               return $db->makeList(
+                       [
+                               "wl_namespace $op " . $target->getNamespace(),
+                               $db->makeList(
+                                       [
+                                               'wl_namespace = ' . $target->getNamespace(),
+                                               "wl_title $op= " . $db->addQuotes( $target->getDBkey() )
+                                       ],
+                                       LIST_AND
+                               )
+                       ],
+                       LIST_OR
+               );
+       }
+
+       private function getWatchedItemsWithRCInfoQueryDbOptions( array $options ) {
                $dbOptions = [];
 
                if ( array_key_exists( 'dir', $options ) ) {
@@ -456,7 +599,24 @@ class WatchedItemQueryService {
                return $dbOptions;
        }
 
-       private function getJoinConds( array $options ) {
+       private function getWatchedItemsForUserQueryDbOptions( array $options ) {
+               $dbOptions = [];
+               if ( array_key_exists( 'sort', $options ) ) {
+                       $dbOptions['ORDER BY'] = [
+                               "wl_namespace {$options['sort']}",
+                               "wl_title {$options['sort']}"
+                       ];
+                       if ( count( $options['namespaceIds'] ) === 1 ) {
+                               $dbOptions['ORDER BY'] = "wl_title {$options['sort']}";
+                       }
+               }
+               if ( array_key_exists( 'limit', $options ) ) {
+                       $dbOptions['LIMIT'] = (int)$options['limit'];
+               }
+               return $dbOptions;
+       }
+
+       private function getWatchedItemsWithRCInfoQueryJoinConds( array $options ) {
                $joinConds = [
                        'watchlist' => [ 'INNER JOIN',
                                [
index 515fbfc..9a74401 100644 (file)
@@ -190,13 +190,13 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * @param int $slaveOrMaster DB_MASTER or DB_SLAVE
+        * @param int $dbIndex DB_MASTER or DB_REPLICA
         *
         * @return DatabaseBase
         * @throws MWException
         */
-       private function getConnection( $slaveOrMaster ) {
-               return $this->loadBalancer->getConnection( $slaveOrMaster, [ 'watchlist' ] );
+       private function getConnection( $dbIndex ) {
+               return $this->loadBalancer->getConnection( $dbIndex, [ 'watchlist' ] );
        }
 
        /**
@@ -217,7 +217,7 @@ class WatchedItemStore implements StatsdAwareInterface {
         * @return int
         */
        public function countWatchedItems( User $user ) {
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
                $return = (int)$dbr->selectField(
                        'watchlist',
                        'COUNT(*)',
@@ -237,7 +237,7 @@ class WatchedItemStore implements StatsdAwareInterface {
         * @return int
         */
        public function countWatchers( LinkTarget $target ) {
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
                $return = (int)$dbr->selectField(
                        'watchlist',
                        'COUNT(*)',
@@ -263,7 +263,7 @@ class WatchedItemStore implements StatsdAwareInterface {
         * @throws MWException
         */
        public function countVisitingWatchers( LinkTarget $target, $threshold ) {
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
                $visitingWatchers = (int)$dbr->selectField(
                        'watchlist',
                        'COUNT(*)',
@@ -293,7 +293,7 @@ class WatchedItemStore implements StatsdAwareInterface {
        public function countWatchersMultiple( array $targets, array $options = [] ) {
                $dbOptions = [ 'GROUP BY' => [ 'wl_namespace', 'wl_title' ] ];
 
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
 
                if ( array_key_exists( 'minimumWatchers', $options ) ) {
                        $dbOptions['HAVING'] = 'COUNT(*) >= ' . (int)$options['minimumWatchers'];
@@ -341,7 +341,7 @@ class WatchedItemStore implements StatsdAwareInterface {
                array $targetsWithVisitThresholds,
                $minimumWatchers = null
        ) {
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
 
                $conds = $this->getVisitingWatchersCondition( $dbr, $targetsWithVisitThresholds );
 
@@ -452,7 +452,7 @@ class WatchedItemStore implements StatsdAwareInterface {
                        return false;
                }
 
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
                $row = $dbr->selectRow(
                        'watchlist',
                        'wl_notificationtimestamp',
@@ -499,7 +499,7 @@ class WatchedItemStore implements StatsdAwareInterface {
                                "wl_title {$options['sort']}"
                        ];
                }
-               $db = $this->getConnection( $options['forWrite'] ? DB_MASTER : DB_SLAVE );
+               $db = $this->getConnection( $options['forWrite'] ? DB_MASTER : DB_REPLICA );
 
                $res = $db->select(
                        'watchlist',
@@ -569,7 +569,7 @@ class WatchedItemStore implements StatsdAwareInterface {
                        return $timestamps;
                }
 
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
 
                $lb = new LinkBatch( $targetsToLoad );
                $res = $dbr->select(
@@ -719,28 +719,31 @@ class WatchedItemStore implements StatsdAwareInterface {
         */
        public function updateNotificationTimestamp( User $editor, LinkTarget $target, $timestamp ) {
                $dbw = $this->getConnection( DB_MASTER );
-               $res = $dbw->select( [ 'watchlist' ],
-                       [ 'wl_user' ],
+               $uids = $dbw->selectFieldValues(
+                       'watchlist',
+                       'wl_user',
                        [
                                'wl_user != ' . intval( $editor->getId() ),
                                'wl_namespace' => $target->getNamespace(),
                                'wl_title' => $target->getDBkey(),
                                'wl_notificationtimestamp IS NULL',
-                       ], __METHOD__
+                       ],
+                       __METHOD__
                );
+               $this->reuseConnection( $dbw );
 
-               $watchers = [];
-               foreach ( $res as $row ) {
-                       $watchers[] = intval( $row->wl_user );
-               }
-
+               $watchers = array_map( 'intval', $uids );
                if ( $watchers ) {
                        // Update wl_notificationtimestamp for all watching users except the editor
                        $fname = __METHOD__;
-                       $dbw->onTransactionIdle(
-                               function () use ( $dbw, $timestamp, $watchers, $target, $fname ) {
+                       DeferredUpdates::addCallableUpdate(
+                               function () use ( $timestamp, $watchers, $target, $fname ) {
                                        global $wgUpdateRowsPerQuery;
 
+                                       $dbw = $this->getConnection( DB_MASTER );
+                                       $factory = wfGetLBFactory();
+                                       $ticket = $factory->getEmptyTransactionTicket( __METHOD__ );
+
                                        $watchersChunks = array_chunk( $watchers, $wgUpdateRowsPerQuery );
                                        foreach ( $watchersChunks as $watchersChunk ) {
                                                $dbw->update( 'watchlist',
@@ -753,17 +756,20 @@ class WatchedItemStore implements StatsdAwareInterface {
                                                        ], $fname
                                                );
                                                if ( count( $watchersChunks ) > 1 ) {
-                                                       $dbw->commit( __METHOD__, 'flush' );
-                                                       wfGetLBFactory()->waitForReplication( [ 'wiki' => $dbw->getWikiID() ] );
+                                                       $factory->commitAndWaitForReplication(
+                                                               __METHOD__, $ticket, [ 'wiki' => $dbw->getWikiID() ]
+                                                       );
                                                }
                                        }
                                        $this->uncacheLinkTarget( $target );
-                               }
+
+                                       $this->reuseConnection( $dbw );
+                               },
+                               DeferredUpdates::POSTSEND,
+                               $dbw
                        );
                }
 
-               $this->reuseConnection( $dbw );
-
                return $watchers;
        }
 
@@ -879,7 +885,7 @@ class WatchedItemStore implements StatsdAwareInterface {
                        $queryOptions['LIMIT'] = $unreadLimit;
                }
 
-               $dbr = $this->getConnection( DB_SLAVE );
+               $dbr = $this->getConnection( DB_REPLICA );
                $rowCount = $dbr->selectRowCount(
                        'watchlist',
                        '1',
index 152a3d2..8f78164 100644 (file)
@@ -83,6 +83,9 @@ class WebRequest {
        /** @var bool Whether this HTTP request is "safe" (even if it is an HTTP post) */
        protected $markedAsSafe = false;
 
+       /**
+        * @codeCoverageIgnore
+        */
        public function __construct() {
                $this->requestTime = isset( $_SERVER['REQUEST_TIME_FLOAT'] )
                        ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime( true );
@@ -351,7 +354,7 @@ class WebRequest {
         * @return array|string Cleaned-up version of the given
         * @private
         */
-       function normalizeUnicode( $data ) {
+       public function normalizeUnicode( $data ) {
                if ( is_array( $data ) ) {
                        foreach ( $data as $key => $val ) {
                                $data[$key] = $this->normalizeUnicode( $val );
@@ -394,6 +397,32 @@ class WebRequest {
                }
        }
 
+       /**
+        * Fetch a scalar from the input without normalization, or return $default
+        * if it's not set.
+        *
+        * Unlike self::getVal(), this does not perform any normalization on the
+        * input value.
+        *
+        * @since 1.28
+        * @param string $name
+        * @param string|null $default Optional default
+        * @return string
+        */
+       public function getRawVal( $name, $default = null ) {
+               $name = strtr( $name, '.', '_' ); // See comment in self::getGPCVal()
+               if ( isset( $this->data[$name] ) && !is_array( $this->data[$name] ) ) {
+                       $val = $this->data[$name];
+               } else {
+                       $val = $default;
+               }
+               if ( is_null( $val ) ) {
+                       return $val;
+               } else {
+                       return (string)$val;
+               }
+       }
+
        /**
         * Fetch a scalar from the input or return $default if it's not set.
         * Returns a string. Arrays are discarded. Useful for
@@ -565,21 +594,17 @@ class WebRequest {
 
        /**
         * Fetch a text string from the given array or return $default if it's not
-        * set. Carriage returns are stripped from the text, and with some language
-        * modules there is an input transliteration applied. This should generally
-        * be used for form "<textarea>" and "<input>" fields. Used for
-        * user-supplied freeform text input (for which input transformations may
-        * be required - e.g.  Esperanto x-coding).
+        * set. Carriage returns are stripped from the text. This should generally
+        * be used for form "<textarea>" and "<input>" fields, and for
+        * user-supplied freeform text input.
         *
         * @param string $name
         * @param string $default Optional
         * @return string
         */
        public function getText( $name, $default = '' ) {
-               global $wgContLang;
                $val = $this->getVal( $name, $default );
-               return str_replace( "\r\n", "\n",
-                       $wgContLang->recodeInput( $val ) );
+               return str_replace( "\r\n", "\n", $val );
        }
 
        /**
@@ -619,6 +644,7 @@ class WebRequest {
         * Get the values passed in the query string.
         * No transformation is performed on the values.
         *
+        * @codeCoverageIgnore
         * @return array
         */
        public function getQueryValues() {
@@ -629,6 +655,7 @@ class WebRequest {
         * Return the contents of the Query with no decoding. Use when you need to
         * know exactly what was sent, e.g. for an OAuth signature over the elements.
         *
+        * @codeCoverageIgnore
         * @return string
         */
        public function getRawQueryString() {
@@ -687,6 +714,9 @@ class WebRequest {
 
        /**
         * Return the session for this request
+        *
+        * This might unpersist an existing session if it was invalid.
+        *
         * @since 1.27
         * @note For performance, keep the session locally if you will be making
         *  much use of it instead of calling this method repeatedly.
index 458c207..90b76e3 100644 (file)
@@ -89,26 +89,12 @@ class WebResponse {
         *     path: string, cookie path ($wgCookiePath)
         *     secure: bool, secure attribute ($wgCookieSecure)
         *     httpOnly: bool, httpOnly attribute ($wgCookieHttpOnly)
-        *     raw: bool, if true uses PHP's setrawcookie() instead of setcookie()
-        *   For backwards compatibility, if $options is not an array then it and
-        *   the following two parameters will be interpreted as values for
-        *   'prefix', 'domain', and 'secure'
         * @since 1.22 Replaced $prefix, $domain, and $forceSecure with $options
         */
        public function setCookie( $name, $value, $expire = 0, $options = [] ) {
                global $wgCookiePath, $wgCookiePrefix, $wgCookieDomain;
                global $wgCookieSecure, $wgCookieExpiration, $wgCookieHttpOnly;
 
-               if ( !is_array( $options ) ) {
-                       // Backwards compatibility
-                       $options = [ 'prefix' => $options ];
-                       if ( func_num_args() >= 5 ) {
-                               $options['domain'] = func_get_arg( 4 );
-                       }
-                       if ( func_num_args() >= 6 ) {
-                               $options['secure'] = func_get_arg( 5 );
-                       }
-               }
                $options = array_filter( $options, function ( $a ) {
                        return $a !== null;
                } ) + [
@@ -268,16 +254,6 @@ class FauxResponse extends WebResponse {
                global $wgCookiePath, $wgCookiePrefix, $wgCookieDomain;
                global $wgCookieSecure, $wgCookieExpiration, $wgCookieHttpOnly;
 
-               if ( !is_array( $options ) ) {
-                       // Backwards compatibility
-                       $options = [ 'prefix' => $options ];
-                       if ( func_num_args() >= 5 ) {
-                               $options['domain'] = func_get_arg( 4 );
-                       }
-                       if ( func_num_args() >= 6 ) {
-                               $options['secure'] = func_get_arg( 5 );
-                       }
-               }
                $options = array_filter( $options, function ( $a ) {
                        return $a !== null;
                } ) + [
index cf97984..37f85ea 100644 (file)
@@ -65,6 +65,14 @@ class WikiMap {
 
                $path = $wgConf->get( 'wgArticlePath', $wikiID, $major,
                        [ 'lang' => $minor, 'site' => $major ] );
+
+               // If we don't have a canonical server or a path containing $1, the
+               // WikiReference isn't going to function properly. Just return null in
+               // that case.
+               if ( !is_string( $canonicalServer ) || !is_string( $path ) || strpos( $path, '$1' ) === false ) {
+                       return null;
+               }
+
                return new WikiReference( $canonicalServer, $path, $server );
        }
 
index 84bf16e..f06f828 100644 (file)
@@ -88,7 +88,7 @@ abstract class Action {
         * @since 1.17
         * @param string $action
         * @param Page $page
-        * @param IContextSource $context
+        * @param IContextSource|null $context
         * @return Action|bool|null False if the action is disabled, null
         *     if it is not recognised
         */
@@ -264,7 +264,7 @@ abstract class Action {
         * Only public since 1.21
         *
         * @param Page $page
-        * @param IContextSource $context
+        * @param IContextSource|null $context
         */
        public function __construct( Page $page, IContextSource $context = null ) {
                if ( $context === null ) {
index 700e201..41378fb 100644 (file)
@@ -116,8 +116,10 @@ class HistoryAction extends FormlessAction {
                // Setup page variables.
                $out->setFeedAppendQuery( 'action=history' );
                $out->addModules( 'mediawiki.action.history' );
-               $out->addModuleStyles( 'mediawiki.action.history.styles' );
-               $out->addModuleStyles( 'mediawiki.special.changeslist' );
+               $out->addModuleStyles( [
+                       'mediawiki.action.history.styles',
+                       'mediawiki.special.changeslist',
+               ] );
                if ( $config->get( 'UseMediaWikiUIEverywhere' ) ) {
                        $out = $this->getOutput();
                        $out->addModuleStyles( [
@@ -232,7 +234,7 @@ class HistoryAction extends FormlessAction {
                        return new FakeResultWrapper( [] );
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                if ( $direction === self::DIR_PREV ) {
                        list( $dirs, $oper ) = [ "ASC", ">=" ];
index 7be2aa7..abc7cb2 100644 (file)
@@ -202,6 +202,7 @@ class InfoAction extends FormlessAction {
                $title = $this->getTitle();
                $id = $title->getArticleID();
                $config = $this->context->getConfig();
+               $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
 
                $pageCounts = $this->pageCounts( $this->page );
 
@@ -279,9 +280,18 @@ class InfoAction extends FormlessAction {
                        . ' ' . $this->msg( 'parentheses', $pageLang )->escaped() ];
 
                // Content model of the page
+               $modelHtml = htmlspecialchars( ContentHandler::getLocalizedName( $title->getContentModel() ) );
+               // If the user can change it, add a link to Special:ChangeContentModel
+               if ( $title->quickUserCan( 'editcontentmodel' ) ) {
+                       $modelHtml .= ' ' . $this->msg( 'parentheses' )->rawParams( $linkRenderer->makeLink(
+                               SpecialPage::getTitleValueFor( 'ChangeContentModel', $title->getPrefixedText() ),
+                               $this->msg( 'pageinfo-content-model-change' )->text()
+                       ) )->escaped();
+               }
+
                $pageInfo['header-basic'][] = [
                        $this->msg( 'pageinfo-content-model' ),
-                       htmlspecialchars( ContentHandler::getLocalizedName( $title->getContentModel() ) )
+                       $modelHtml
                ];
 
                // Search engine status
@@ -671,13 +681,13 @@ class InfoAction extends FormlessAction {
 
                return ObjectCache::getMainWANInstance()->getWithSetCallback(
                        self::getCacheKey( $page->getTitle(), $page->getLatest() ),
-                       86400 * 7,
+                       WANObjectCache::TTL_WEEK,
                        function ( $oldValue, &$ttl, &$setOpts ) use ( $page, $config, $fname ) {
                                $title = $page->getTitle();
                                $id = $title->getArticleID();
 
-                               $dbr = wfGetDB( DB_SLAVE );
-                               $dbrWatchlist = wfGetDB( DB_SLAVE, 'watchlist' );
+                               $dbr = wfGetDB( DB_REPLICA );
+                               $dbrWatchlist = wfGetDB( DB_REPLICA, 'watchlist' );
 
                                $setOpts += Database::getCacheSetOptions( $dbr, $dbrWatchlist );
 
index 576533d..b2002ff 100644 (file)
  */
 
 /**
- * User-requested page cache purging.
- *
- * For users with 'purge', this will directly trigger the cache purging and
- * for users without that right, it will show a confirmation form.
+ * User-requested page cache purging
  *
  * @ingroup Actions
  */
@@ -48,10 +45,6 @@ class PurgeAction extends FormAction {
                return $this->page->doPurge();
        }
 
-       /**
-        * purge is slightly weird because it can be either formed or formless depending
-        * on user permissions
-        */
        public function show() {
                $this->setHeaders();
 
@@ -65,11 +58,7 @@ class PurgeAction extends FormAction {
                        return;
                }
 
-               if ( $user->isAllowed( 'purge' ) ) {
-                       // This will update the database immediately, even on HTTP GET.
-                       // Lots of uses may exist for this feature, so just ignore warnings.
-                       Profiler::instance()->getTransactionProfiler()->resetExpectations();
-
+               if ( $this->getRequest()->wasPosted() ) {
                        $this->redirectParams = wfArrayToCgi( array_diff_key(
                                $this->getRequest()->getQueryValues(),
                                [ 'title' => null, 'action' => null ]
index c800ce7..e466e65 100644 (file)
@@ -112,13 +112,19 @@ class RevertAction extends FormAction {
        public function onSubmit( $data ) {
                $this->useTransactionalTimeLimit();
 
-               $source = $this->page->getFile()->getArchiveVirtualUrl(
-                       $this->getRequest()->getText( 'oldimage' )
-               );
+               $old = $this->getRequest()->getText( 'oldimage' );
+               $localFile = $this->page->getFile();
+               $oldFile = OldLocalFile::newFromArchiveName( $this->getTitle(), $localFile->getRepo(), $old );
+
+               $source = $localFile->getArchiveVirtualUrl( $old );
                $comment = $data['comment'];
 
+               if ( $localFile->getSha1() === $oldFile->getSha1() ) {
+                       return Status::newFatal( 'filerevert-identical' );
+               }
+
                // TODO: Preserve file properties from database instead of reloading from file
-               return $this->page->getFile()->upload(
+               return $localFile->upload(
                        $source,
                        $comment,
                        $comment,
index 3e760fd..aa2858d 100644 (file)
@@ -54,9 +54,12 @@ class RollbackAction extends FormlessAction {
                $user = $this->getUser();
                $from = $request->getVal( 'from' );
                $rev = $this->page->getRevision();
-               if ( $from === null || $from === '' ) {
+               if ( $from === null ) {
                        throw new ErrorPageError( 'rollbackfailed', 'rollback-missingparam' );
                }
+               if ( !$rev ) {
+                       throw new ErrorPageError( 'rollbackfailed', 'rollback-missingrevision' );
+               }
                if ( $from !== $rev->getUserText() ) {
                        throw new ErrorPageError( 'rollbackfailed', 'alreadyrolled', [
                                $this->getTitle()->getPrefixedText(),
index f8f1dc1..7f043e4 100644 (file)
@@ -50,7 +50,8 @@ class UnwatchAction extends WatchAction {
        }
 
        public function onSuccess() {
-               $this->getOutput()->addWikiMsg( 'removedwatchtext', $this->getTitle()->getPrefixedText() );
+               $msgKey = $this->getTitle()->isTalkPage() ? 'removedwatchtext-talk' : 'removedwatchtext';
+               $this->getOutput()->addWikiMsg( $msgKey, $this->getTitle()->getPrefixedText() );
        }
 
        public function doesWrites() {
index 3a24565..55507f5 100644 (file)
@@ -41,6 +41,30 @@ class ViewAction extends FormlessAction {
        }
 
        public function show() {
+               $config = $this->context->getConfig();
+
+               if (
+                       $config->get( 'DebugToolbar' ) == false && // don't let this get stuck on pages
+                       $this->page->checkTouched() // page exists and is not a redirect
+               ) {
+                       // Include any redirect in the last-modified calculation
+                       $redirFromTitle = $this->page->getRedirectedFrom();
+                       if ( !$redirFromTitle ) {
+                               $touched = $this->page->getTouched();
+                       } elseif ( $config->get( 'MaxRedirects' ) <= 1 ) {
+                               $touched = max( $this->page->getTouched(), $redirFromTitle->getTouched() );
+                       } else {
+                               // Don't bother following the chain and getting the max mtime
+                               $touched = null;
+                       }
+
+                       // Send HTTP 304 if the IMS matches or otherwise set expiry/last-modified headers
+                       if ( $touched && $this->getOutput()->checkLastModified( $touched ) ) {
+                               wfDebug( __METHOD__ . ": done 304\n" );
+                               return;
+                       }
+               }
+
                $this->page->view();
        }
 }
index 890740f..23505c0 100644 (file)
@@ -67,7 +67,8 @@ class WatchAction extends FormAction {
        }
 
        public function onSuccess() {
-               $this->getOutput()->addWikiMsg( 'addedwatchtext', $this->getTitle()->getPrefixedText() );
+               $msgKey = $this->getTitle()->isTalkPage() ? 'addedwatchtext-talk' : 'addedwatchtext';
+               $this->getOutput()->addWikiMsg( $msgKey, $this->getTitle()->getPrefixedText() );
        }
 
        /* Static utility methods */
index 0a4b6dc..2511e3b 100644 (file)
@@ -21,7 +21,6 @@
  */
 
 use MediaWiki\Auth\AuthManager;
-use MediaWiki\Auth\AuthenticationRequest;
 use MediaWiki\Auth\AuthenticationResponse;
 
 /**
@@ -67,13 +66,15 @@ class ApiAMCreateAccount extends ApiBase {
                $helper = new ApiAuthManagerHelper( $this );
                $manager = AuthManager::singleton();
 
-               // Make sure it's possible to log in
+               // Make sure it's possible to create accounts
                if ( !$manager->canCreateAccounts() ) {
                        $this->getResult()->addValue( null, 'createaccount', $helper->formatAuthenticationResponse(
                                AuthenticationResponse::newFail(
                                        $this->msg( 'userlogin-cannot-' . AuthManager::ACTION_CREATE )
                                )
                        ) );
+                       $helper->logAuthenticationResult( 'accountcreation',
+                               'userlogin-cannot-' . AuthManager::ACTION_CREATE );
                        return;
                }
 
@@ -94,6 +95,7 @@ class ApiAMCreateAccount extends ApiBase {
 
                $this->getResult()->addValue( null, 'createaccount',
                        $helper->formatAuthenticationResponse( $res ) );
+               $helper->logAuthenticationResult( 'accountcreation', $res );
        }
 
        public function isReadMode() {
index e30f22b..8e57f93 100644 (file)
@@ -25,6 +25,7 @@ use MediaWiki\Auth\AuthManager;
 use MediaWiki\Auth\AuthenticationRequest;
 use MediaWiki\Auth\AuthenticationResponse;
 use MediaWiki\Auth\CreateFromLoginAuthenticationRequest;
+use MediaWiki\Logger\LoggerFactory;
 
 /**
  * Helper class for AuthManager-using API modules. Intended for use via
@@ -84,6 +85,7 @@ class ApiAuthManagerHelper {
                                        'key' => $message->getKey(),
                                        'params' => $message->getParams(),
                                ];
+                               ApiResult::setIndexedTagName( $res[$key]['params'], 'param' );
                                break;
                }
        }
@@ -156,8 +158,13 @@ class ApiAuthManagerHelper {
 
                // Collect the fields for all the requests
                $fields = [];
+               $sensitive = [];
                foreach ( $reqs as $req ) {
-                       $fields += (array)$req->getFieldInfo();
+                       $info = (array)$req->getFieldInfo();
+                       $fields += $info;
+                       $sensitive += array_filter( $info, function ( $opts ) {
+                               return !empty( $opts['sensitive'] );
+                       } );
                }
 
                // Extract the request data for the fields and mark those request
@@ -165,6 +172,16 @@ class ApiAuthManagerHelper {
                $data = array_intersect_key( $this->module->getRequest()->getValues(), $fields );
                $this->module->getMain()->markParamsUsed( array_keys( $data ) );
 
+               if ( $sensitive ) {
+                       try {
+                               $this->module->requirePostedParameters( array_keys( $sensitive ), 'noprefix' );
+                       } catch ( UsageException $ex ) {
+                               // Make this a warning for now, upgrade to an error in 1.29.
+                               $this->module->setWarning( $ex->getMessage() );
+                               $this->module->logFeatureUsage( $this->module->getModuleName() . '-params-in-query-string' );
+                       }
+               }
+
                return AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
        }
 
@@ -220,6 +237,30 @@ class ApiAuthManagerHelper {
                return $ret;
        }
 
+       /**
+        * Logs successful or failed authentication.
+        * @param string|AuthenticationResponse $result Response or error message
+        * @param string $event Event type (e.g. 'accountcreation')
+        */
+       public function logAuthenticationResult( $event, $result ) {
+               if ( is_string( $result ) ) {
+                       $status = Status::newFatal( $result );
+               } elseif ( $result->status === AuthenticationResponse::PASS ) {
+                       $status = Status::newGood();
+               } elseif ( $result->status === AuthenticationResponse::FAIL ) {
+                       $status = Status::newFatal( $result->message );
+               } else {
+                       return;
+               }
+
+               $module = $this->module->getModuleName();
+               LoggerFactory::getInstance( 'authevents' )->info( "$module API attempt", [
+                       'event' => $event,
+                       'status' => $status,
+                       'module' => $module,
+               ] );
+       }
+
        /**
         * Fetch the preserved CreateFromLoginAuthenticationRequest, if any
         * @return CreateFromLoginAuthenticationRequest|null
@@ -301,6 +342,7 @@ class ApiAuthManagerHelper {
                        $this->formatMessage( $ret, 'label', $field['label'] );
                        $this->formatMessage( $ret, 'help', $field['help'] );
                        $ret['optional'] = !empty( $field['optional'] );
+                       $ret['sensitive'] = !empty( $field['sensitive'] );
 
                        $retFields[$name] = $ret;
                }
index 3e57e89..f763e45 100644 (file)
@@ -599,12 +599,12 @@ abstract class ApiBase extends ContextSource {
        }
 
        /**
-        * Gets a default slave database connection object
+        * Gets a default replica DB connection object
         * @return DatabaseBase
         */
        protected function getDB() {
                if ( !isset( $this->mSlaveDB ) ) {
-                       $this->mSlaveDB = wfGetDB( DB_SLAVE, 'api' );
+                       $this->mSlaveDB = wfGetDB( DB_REPLICA, 'api' );
                }
 
                return $this->mSlaveDB;
@@ -776,6 +776,39 @@ abstract class ApiBase extends ContextSource {
                }
        }
 
+       /**
+        * Die if any of the specified parameters were found in the query part of
+        * the URL rather than the post body.
+        * @since 1.28
+        * @param string[] $params Parameters to check
+        * @param string $prefix Set to 'noprefix' to skip calling $this->encodeParamName()
+        */
+       public function requirePostedParameters( $params, $prefix = 'prefix' ) {
+               // Skip if $wgDebugAPI is set or we're in internal mode
+               if ( $this->getConfig()->get( 'DebugAPI' ) || $this->getMain()->isInternalMode() ) {
+                       return;
+               }
+
+               $queryValues = $this->getRequest()->getQueryValues();
+               $badParams = [];
+               foreach ( $params as $param ) {
+                       if ( $prefix !== 'noprefix' ) {
+                               $param = $this->encodeParamName( $param );
+                       }
+                       if ( array_key_exists( $param, $queryValues ) ) {
+                               $badParams[] = $param;
+                       }
+               }
+
+               if ( $badParams ) {
+                       $this->dieUsage(
+                               'The following parameters were found in the query string, but must be in the POST body: '
+                                       . join( ', ', $badParams ),
+                               'mustpostparams'
+                       );
+               }
+       }
+
        /**
         * Callback function used in requireOnlyOneParameter to check whether required parameters are set
         *
@@ -793,7 +826,7 @@ abstract class ApiBase extends ContextSource {
         * @param array $params
         * @param bool|string $load Whether load the object's state from the database:
         *        - false: don't load (if the pageid is given, it will still be loaded)
-        *        - 'fromdb': load from a slave database
+        *        - 'fromdb': load from a replica DB
         *        - 'fromdbmaster': load from the master database
         * @return WikiPage
         */
@@ -967,6 +1000,31 @@ abstract class ApiBase extends ContextSource {
                                        $type = $this->getModuleManager()->getNames( $paramName );
                                }
                        }
+
+                       $request = $this->getMain()->getRequest();
+                       $rawValue = $request->getRawVal( $encParamName );
+                       if ( $rawValue === null ) {
+                               $rawValue = $default;
+                       }
+
+                       // Preserve U+001F for self::parseMultiValue(), or error out if that won't be called
+                       if ( isset( $value ) && substr( $rawValue, 0, 1 ) === "\x1f" ) {
+                               if ( $multi ) {
+                                       // This loses the potential $wgContLang->checkTitleEncoding() transformation
+                                       // done by WebRequest for $_GET. Let's call that a feature.
+                                       $value = join( "\x1f", $request->normalizeUnicode( explode( "\x1f", $rawValue ) ) );
+                               } else {
+                                       $this->dieUsage(
+                                               "U+001F multi-value separation may only be used for multi-valued parameters.",
+                                               'badvalue_notmultivalue'
+                                       );
+                               }
+                       }
+
+                       // Check for NFC normalization, and warn
+                       if ( $rawValue !== $value ) {
+                               $this->handleParamNormalization( $paramName, $value, $rawValue );
+                       }
                }
 
                if ( isset( $value ) && ( $multi || is_array( $type ) ) ) {
@@ -1112,6 +1170,40 @@ abstract class ApiBase extends ContextSource {
                return $value;
        }
 
+       /**
+        * Handle when a parameter was Unicode-normalized
+        * @since 1.28
+        * @param string $paramName Unprefixed parameter name
+        * @param string $value Input that will be used.
+        * @param string $rawValue Input before normalization.
+        */
+       protected function handleParamNormalization( $paramName, $value, $rawValue ) {
+               $encParamName = $this->encodeParamName( $paramName );
+               $this->setWarning(
+                       "The value passed for '$encParamName' contains invalid or non-normalized data. "
+                       . 'Textual data should be valid, NFC-normalized Unicode without '
+                       . 'C0 control characters other than HT (\\t), LF (\\n), and CR (\\r).'
+               );
+       }
+
+       /**
+        * Split a multi-valued parameter string, like explode()
+        * @since 1.28
+        * @param string $value
+        * @param int $limit
+        * @return string[]
+        */
+       protected function explodeMultiValue( $value, $limit ) {
+               if ( substr( $value, 0, 1 ) === "\x1f" ) {
+                       $sep = "\x1f";
+                       $value = substr( $value, 1 );
+               } else {
+                       $sep = '|';
+               }
+
+               return explode( $sep, $value, $limit );
+       }
+
        /**
         * Return an array of values that were given in a 'a|b|c' notation,
         * after it optionally validates them against the list allowed values.
@@ -1126,18 +1218,19 @@ abstract class ApiBase extends ContextSource {
         * @return string|string[] (allowMultiple ? an_array_of_values : a_single_value)
         */
        protected function parseMultiValue( $valueName, $value, $allowMultiple, $allowedValues ) {
-               if ( trim( $value ) === '' && $allowMultiple ) {
+               if ( ( trim( $value ) === '' || trim( $value ) === "\x1f" ) && $allowMultiple ) {
                        return [];
                }
 
                // This is a bit awkward, but we want to avoid calling canApiHighLimits()
                // because it unstubs $wgUser
-               $valuesList = explode( '|', $value, self::LIMIT_SML2 + 1 );
+               $valuesList = $this->explodeMultiValue( $value, self::LIMIT_SML2 + 1 );
                $sizeLimit = count( $valuesList ) > self::LIMIT_SML1 && $this->mMainModule->canApiHighLimits()
                        ? self::LIMIT_SML2
                        : self::LIMIT_SML1;
 
                if ( self::truncateArray( $valuesList, $sizeLimit ) ) {
+                       $this->logFeatureUsage( "too-many-$valueName-for-{$this->getModulePath()}" );
                        $this->setWarning( "Too many values supplied for parameter '$valueName': " .
                                "the limit is $sizeLimit" );
                }
@@ -2144,7 +2237,7 @@ abstract class ApiBase extends ContextSource {
        /**
         * Return the error message related to a certain array
         * @param array|string|MessageSpecifier $error Element of a getUserPermissionsErrors()-style array
-        * @return array('code' => code, 'info' => info)
+        * @return [ 'code' => code, 'info' => info ]
         */
        public function parseMsg( $error ) {
                // Check whether someone passed the whole array, instead of one element as
@@ -2197,7 +2290,7 @@ abstract class ApiBase extends ContextSource {
         * analysis.
         * @param string $feature Feature being used.
         */
-       protected function logFeatureUsage( $feature ) {
+       public function logFeatureUsage( $feature ) {
                $request = $this->getRequest();
                $s = '"' . addslashes( $feature ) . '"' .
                        ' "' . wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) . '"' .
@@ -2458,6 +2551,7 @@ abstract class ApiBase extends ContextSource {
 
                // Build map of extension directories to extension info
                if ( self::$extensionInfo === null ) {
+                       $extDir = $this->getConfig()->get( 'ExtensionDirectory' );
                        self::$extensionInfo = [
                                realpath( __DIR__ ) ?: __DIR__ => [
                                        'path' => $IP,
@@ -2465,6 +2559,7 @@ abstract class ApiBase extends ContextSource {
                                        'license-name' => 'GPL-2.0+',
                                ],
                                realpath( "$IP/extensions" ) ?: "$IP/extensions" => null,
+                               realpath( $extDir ) ?: $extDir => null,
                        ];
                        $keep = [
                                'path' => null,
index 5271996..407ae71 100644 (file)
@@ -87,6 +87,7 @@ class ApiCSPReport extends ApiBase {
                $reportOnly = $this->getParameter( 'reportonly' );
                $userAgent = $this->getRequest()->getHeader( 'user-agent' );
                $source = $this->getParameter( 'source' );
+               $falsePositives = $this->getConfig()->get( 'CSPFalsePositiveUrls' );
 
                $flags = [];
                if ( $source !== 'internal' ) {
@@ -95,6 +96,16 @@ class ApiCSPReport extends ApiBase {
                if ( $reportOnly ) {
                        $flags[] = 'report-only';
                }
+
+               if (
+                       ( isset( $report['blocked-uri'] ) &&
+                       isset( $falsePositives[$report['blocked-uri']] ) )
+                       || ( isset( $report['source-file'] ) &&
+                       isset( $falsePositives[$report['source-file']] ) )
+               ) {
+                       // Report caused by Ad-Ware
+                       $flags[] = 'false-positive';
+               }
                return $flags;
        }
 
index cffccb1..cbb1524 100644 (file)
@@ -21,7 +21,6 @@
  */
 
 use MediaWiki\Auth\AuthManager;
-use MediaWiki\Auth\AuthenticationRequest;
 use MediaWiki\Auth\AuthenticationResponse;
 use MediaWiki\Auth\CreateFromLoginAuthenticationRequest;
 
@@ -73,6 +72,7 @@ class ApiClientLogin extends ApiBase {
                        $this->getResult()->addValue( null, 'clientlogin', $helper->formatAuthenticationResponse(
                                AuthenticationResponse::newFail( $this->msg( 'userlogin-cannot-' . AuthManager::ACTION_LOGIN ) )
                        ) );
+                       $helper->logAuthenticationResult( 'login', 'userlogin-cannot-' . AuthManager::ACTION_LOGIN );
                        return;
                }
 
@@ -100,6 +100,7 @@ class ApiClientLogin extends ApiBase {
 
                $this->getResult()->addValue( null, 'clientlogin',
                        $helper->formatAuthenticationResponse( $res ) );
+               $helper->logAuthenticationResult( 'login', $res );
        }
 
        public function isReadMode() {
index 8f1bd19..6601fb7 100644 (file)
@@ -167,7 +167,7 @@ class ApiContinuationManager {
 
        /**
         * Fetch continuation result data
-        * @return array Array( (array)$data, (bool)$batchcomplete )
+        * @return array [ (array)$data, (bool)$batchcomplete ]
         */
        public function getContinuation() {
                $data = [];
diff --git a/includes/api/ApiCreateAccount.php b/includes/api/ApiCreateAccount.php
deleted file mode 100644 (file)
index 6a48610..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-<?php
-/**
- * Created on August 7, 2012
- *
- * Copyright © 2012 Tyler Romeo <tylerromeo@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-use MediaWiki\Logger\LoggerFactory;
-
-/**
- * Unit to authenticate account registration attempts to the current wiki.
- *
- * @ingroup API
- * @deprecated since 1.27, only used when $wgDisableAuthManager is true
- */
-class ApiCreateAccount extends ApiBase {
-       public function execute() {
-               // If we're in a mode that breaks the same-origin policy, no tokens can
-               // be obtained
-               if ( $this->lacksSameOriginSecurity() ) {
-                       $this->dieUsage(
-                               'Cannot create account when the same-origin policy is not applied', 'aborted'
-                       );
-               }
-
-               // $loginForm->addNewaccountInternal will throw exceptions
-               // if wiki is read only (already handled by api), user is blocked or does not have rights.
-               // Use userCan in order to hit GlobalBlock checks (according to Special:userlogin)
-               $loginTitle = SpecialPage::getTitleFor( 'Userlogin' );
-               if ( !$loginTitle->userCan( 'createaccount', $this->getUser() ) ) {
-                       $this->dieUsage(
-                               'You do not have the right to create a new account',
-                               'permdenied-createaccount'
-                       );
-               }
-               if ( $this->getUser()->isBlockedFromCreateAccount() ) {
-                       $this->dieUsage(
-                               'You cannot create a new account because you are blocked',
-                               'blocked',
-                               0,
-                               [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $this->getUser()->getBlock() ) ]
-                       );
-               }
-
-               $params = $this->extractRequestParams();
-
-               // Make sure session is persisted
-               MediaWiki\Session\SessionManager::getGlobalSession()->persist();
-
-               if ( $params['mailpassword'] && !$params['email'] ) {
-                       $this->dieUsageMsg( 'noemail' );
-               }
-
-               if ( $params['language'] && !Language::isSupportedLanguage( $params['language'] ) ) {
-                       $this->dieUsage( 'Invalid language parameter', 'langinvalid' );
-               }
-
-               $context = new DerivativeContext( $this->getContext() );
-               $context->setRequest( new DerivativeRequest(
-                       $this->getContext()->getRequest(),
-                       [
-                               'type' => 'signup',
-                               'uselang' => $params['language'],
-                               'wpName' => $params['name'],
-                               'wpPassword' => $params['password'],
-                               'wpRetype' => $params['password'],
-                               'wpDomain' => $params['domain'],
-                               'wpEmail' => $params['email'],
-                               'wpRealName' => $params['realname'],
-                               'wpCreateaccountToken' => $params['token'],
-                               'wpCreateaccount' => $params['mailpassword'] ? null : '1',
-                               'wpCreateaccountMail' => $params['mailpassword'] ? '1' : null
-                       ]
-               ) );
-
-               $loginForm = new LoginForm();
-               $loginForm->setContext( $context );
-               Hooks::run( 'AddNewAccountApiForm', [ $this, $loginForm ] );
-               $loginForm->load();
-
-               $status = $loginForm->addNewAccountInternal();
-               LoggerFactory::getInstance( 'authmanager' )->info( 'Account creation attempt via API', [
-                       'event' => 'accountcreation',
-                       'status' => $status,
-               ] );
-               $result = [];
-               if ( $status->isGood() ) {
-                       // Success!
-                       $user = $status->getValue();
-
-                       if ( $params['language'] ) {
-                               $user->setOption( 'language', $params['language'] );
-                       }
-
-                       if ( $params['mailpassword'] ) {
-                               // If mailpassword was set, disable the password and send an email.
-                               $user->setPassword( null );
-                               $status->merge( $loginForm->mailPasswordInternal(
-                                       $user,
-                                       false,
-                                       'createaccount-title',
-                                       'createaccount-text'
-                               ) );
-                       } elseif ( $this->getConfig()->get( 'EmailAuthentication' ) &&
-                               Sanitizer::validateEmail( $user->getEmail() )
-                       ) {
-                               // Send out an email authentication message if needed
-                               $status->merge( $user->sendConfirmationMail() );
-                       }
-
-                       // Save settings (including confirmation token)
-                       $user->saveSettings();
-
-                       Hooks::run( 'AddNewAccount', [ $user, $params['mailpassword'] ] );
-
-                       if ( $params['mailpassword'] ) {
-                               $logAction = 'byemail';
-                       } elseif ( $this->getUser()->isLoggedIn() ) {
-                               $logAction = 'create2';
-                       } else {
-                               $logAction = 'create';
-                       }
-                       $user->addNewUserLogEntry( $logAction, (string)$params['reason'] );
-
-                       // Add username, id, and token to result.
-                       $result['username'] = $user->getName();
-                       $result['userid'] = $user->getId();
-                       $result['token'] = $user->getToken();
-               }
-
-               $apiResult = $this->getResult();
-
-               if ( $status->hasMessage( 'sessionfailure' ) || $status->hasMessage( 'nocookiesfornew' ) ) {
-                       // Token was incorrect, so add it to result, but don't throw an exception
-                       // since not having the correct token is part of the normal
-                       // flow of events.
-                       $result['token'] = LoginForm::getCreateaccountToken()->toString();
-                       $result['result'] = 'NeedToken';
-                       $this->setWarning( 'Fetching a token via action=createaccount is deprecated. ' .
-                               'Use action=query&meta=tokens&type=createaccount instead.' );
-                       $this->logFeatureUsage( 'action=createaccount&!token' );
-               } elseif ( !$status->isOK() ) {
-                       // There was an error. Die now.
-                       $this->dieStatus( $status );
-               } elseif ( !$status->isGood() ) {
-                       // Status is not good, but OK. This means warnings.
-                       $result['result'] = 'Warning';
-
-                       // Add any warnings to the result
-                       $warnings = $status->getErrorsByType( 'warning' );
-                       if ( $warnings ) {
-                               foreach ( $warnings as &$warning ) {
-                                       ApiResult::setIndexedTagName( $warning['params'], 'param' );
-                               }
-                               ApiResult::setIndexedTagName( $warnings, 'warning' );
-                               $result['warnings'] = $warnings;
-                       }
-               } else {
-                       // Everything was fine.
-                       $result['result'] = 'Success';
-               }
-
-               // Give extensions a chance to modify the API result data
-               Hooks::run( 'AddNewAccountApiResult', [ $this, $loginForm, &$result ] );
-
-               $apiResult->addValue( null, 'createaccount', $result );
-       }
-
-       public function mustBePosted() {
-               return true;
-       }
-
-       public function isReadMode() {
-               return false;
-       }
-
-       public function isWriteMode() {
-               return true;
-       }
-
-       public function getAllowedParams() {
-               return [
-                       'name' => [
-                               ApiBase::PARAM_TYPE => 'user',
-                               ApiBase::PARAM_REQUIRED => true
-                       ],
-                       'password' => [
-                               ApiBase::PARAM_TYPE => 'password',
-                       ],
-                       'domain' => null,
-                       'token' => [
-                               ApiBase::PARAM_TYPE => 'string',
-                               ApiBase::PARAM_REQUIRED => false, // for BC
-                               ApiBase::PARAM_HELP_MSG => [ 'api-help-param-token', 'createaccount' ],
-                       ],
-                       'email' => [
-                               ApiBase::PARAM_TYPE => 'string',
-                               ApiBase::PARAM_REQUIRED => $this->getConfig()->get( 'EmailConfirmToEdit' ),
-                       ],
-                       'realname' => null,
-                       'mailpassword' => [
-                               ApiBase::PARAM_TYPE => 'boolean',
-                               ApiBase::PARAM_DFLT => false
-                       ],
-                       'reason' => null,
-                       'language' => null
-               ];
-       }
-
-       protected function getExamplesMessages() {
-               return [
-                       'action=createaccount&name=testuser&password=test123'
-                               => 'apihelp-createaccount-example-pass',
-                       'action=createaccount&name=testmailuser&mailpassword=true&reason=MyReason'
-                               => 'apihelp-createaccount-example-mail',
-               ];
-       }
-
-       public function getHelpUrls() {
-               return 'https://www.mediawiki.org/wiki/API:Account_creation';
-       }
-}
index 00daba9..ee9150c 100644 (file)
@@ -97,6 +97,7 @@ class ApiEditPage extends ApiBase {
                } else {
                        $contentHandler = ContentHandler::getForModelID( $params['contentmodel'] );
                }
+               $contentModel = $contentHandler->getModelID();
 
                $name = $titleObj->getPrefixedDBkey();
                $model = $contentHandler->getModelID();
@@ -111,11 +112,11 @@ class ApiEditPage extends ApiBase {
                }
 
                if ( !isset( $params['contentformat'] ) || $params['contentformat'] == '' ) {
-                       $params['contentformat'] = $contentHandler->getDefaultFormat();
+                       $contentFormat = $contentHandler->getDefaultFormat();
+               } else {
+                       $contentFormat = $params['contentformat'];
                }
 
-               $contentFormat = $params['contentformat'];
-
                if ( !$contentHandler->isSupportedFormat( $contentFormat ) ) {
 
                        $this->dieUsage( "The requested format $contentFormat is not supported for content model " .
@@ -265,9 +266,21 @@ class ApiEditPage extends ApiBase {
                        if ( !$newContent ) {
                                $this->dieUsageMsg( 'undo-failure' );
                        }
-
-                       $params['text'] = $newContent->serialize( $params['contentformat'] );
-
+                       if ( empty( $params['contentmodel'] )
+                               && empty( $params['contentformat'] )
+                       ) {
+                               // If we are reverting content model, the new content model
+                               // might not support the current serialization format, in
+                               // which case go back to the old serialization format,
+                               // but only if the user hasn't specified a format/model
+                               // parameter.
+                               if ( !$newContent->isSupportedFormat( $contentFormat ) ) {
+                                       $contentFormat = $undoafterRev->getContentFormat();
+                               }
+                               // Override content model with model of undid revision.
+                               $contentModel = $newContent->getModel();
+                       }
+                       $params['text'] = $newContent->serialize( $contentFormat );
                        // If no summary was given and we only undid one rev,
                        // use an autosummary
                        if ( is_null( $params['summary'] ) &&
@@ -288,7 +301,7 @@ class ApiEditPage extends ApiBase {
                $requestArray = [
                        'wpTextbox1' => $params['text'],
                        'format' => $contentFormat,
-                       'model' => $contentHandler->getModelID(),
+                       'model' => $contentModel,
                        'wpEditToken' => $params['token'],
                        'wpIgnoreBlankSummary' => true,
                        'wpIgnoreBlankArticle' => true,
index c19926a..6d9184f 100644 (file)
@@ -202,7 +202,7 @@ class ApiErrorFormatter {
 
                        case 'raw':
                                $value += [
-                                       'message' => $msg->getKey(),
+                                       'key' => $msg->getKey(),
                                        'params' => $msg->getParams(),
                                ];
                                ApiResult::setIndexedTagName( $value['params'], 'param' );
index 48e7698..10fb182 100644 (file)
@@ -34,7 +34,7 @@
 class ApiExpandTemplates extends ApiBase {
 
        public function execute() {
-               // Cache may vary on $wgUser because ParserOptions gets data from it
+               // Cache may vary on the user because ParserOptions gets data from it
                $this->getMain()->setCacheMode( 'anon-public-user-private' );
 
                // Get parameters
index 0f0fbdc..a3bb6a2 100644 (file)
@@ -100,8 +100,10 @@ class ApiHelp extends ApiBase {
                }
 
                $out = $context->getOutput();
-               $out->addModuleStyles( 'mediawiki.hlist' );
-               $out->addModuleStyles( 'mediawiki.apihelp' );
+               $out->addModuleStyles( [
+                       'mediawiki.hlist',
+                       'mediawiki.apihelp',
+               ] );
                if ( !empty( $options['toc'] ) ) {
                        $out->addModules( 'mediawiki.toc' );
                }
index 14347d8..1017607 100644 (file)
@@ -21,7 +21,6 @@
  */
 
 use MediaWiki\Auth\AuthManager;
-use MediaWiki\Auth\AuthenticationRequest;
 use MediaWiki\Auth\AuthenticationResponse;
 
 /**
index 3572229..9bc0b3a 100644 (file)
@@ -42,9 +42,7 @@ class ApiLogin extends ApiBase {
        }
 
        protected function getDescriptionMessage() {
-               if ( $this->getConfig()->get( 'DisableAuthManager' ) ) {
-                       return 'apihelp-login-description-nonauthmanager';
-               } elseif ( $this->getConfig()->get( 'EnableBotPasswords' ) ) {
+               if ( $this->getConfig()->get( 'EnableBotPasswords' ) ) {
                        return 'apihelp-login-description';
                } else {
                        return 'apihelp-login-description-nobotpasswords';
@@ -72,6 +70,14 @@ class ApiLogin extends ApiBase {
                        return;
                }
 
+               try {
+                       $this->requirePostedParameters( [ 'password', 'token' ] );
+               } catch ( UsageException $ex ) {
+                       // Make this a warning for now, upgrade to an error in 1.29.
+                       $this->setWarning( $ex->getMessage() );
+                       $this->logFeatureUsage( 'login-params-in-query-string' );
+               }
+
                $params = $this->extractRequestParams();
 
                $result = [];
@@ -117,98 +123,69 @@ class ApiLogin extends ApiBase {
                        } else {
                                $authRes = 'Failed';
                                $message = $status->getMessage();
-                               LoggerFactory::getInstance( 'authmanager' )->info(
+                               LoggerFactory::getInstance( 'authentication' )->info(
                                        'BotPassword login failed: ' . $status->getWikiText( false, false, 'en' )
                                );
                        }
                }
 
                if ( $authRes === false ) {
-                       if ( $this->getConfig()->get( 'DisableAuthManager' ) ) {
-                               // Non-AuthManager login
-                               $context->setRequest( new DerivativeRequest(
-                                       $this->getContext()->getRequest(),
-                                       [
-                                               'wpName' => $params['name'],
-                                               'wpPassword' => $params['password'],
-                                               'wpDomain' => $params['domain'],
-                                               'wpLoginToken' => $params['token'],
-                                               'wpRemember' => ''
-                                       ]
-                               ) );
-                               $loginForm = new LoginForm();
-                               $loginForm->setContext( $context );
-                               $authRes = $loginForm->authenticateUserData();
-                               $loginType = 'LoginForm';
-
-                               switch ( $authRes ) {
-                                       case LoginForm::SUCCESS:
-                                               $authRes = 'Success';
-                                               break;
-                                       case LoginForm::NEED_TOKEN:
-                                               $authRes = 'NeedToken';
-                                               break;
-                               }
-                       } else {
-                               // Simplified AuthManager login, for backwards compatibility
-                               $manager = AuthManager::singleton();
-                               $reqs = AuthenticationRequest::loadRequestsFromSubmission(
-                                       $manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN, $this->getUser() ),
-                                       [
-                                               'username' => $params['name'],
-                                               'password' => $params['password'],
-                                               'domain' => $params['domain'],
-                                               'rememberMe' => true,
-                                       ]
-                               );
-                               $res = AuthManager::singleton()->beginAuthentication( $reqs, 'null:' );
-                               switch ( $res->status ) {
-                                       case AuthenticationResponse::PASS:
-                                               if ( $this->getConfig()->get( 'EnableBotPasswords' ) ) {
-                                                       $warn = 'Main-account login via action=login is deprecated and may stop working ' .
-                                                               'without warning.';
-                                                       $warn .= ' To continue login with action=login, see [[Special:BotPasswords]].';
-                                                       $warn .= ' To safely continue using main-account login, see action=clientlogin.';
-                                               } else {
-                                                       $warn = 'Login via action=login is deprecated and may stop working without warning.';
-                                                       $warn .= ' To safely log in, see action=clientlogin.';
-                                               }
-                                               $this->setWarning( $warn );
-                                               $authRes = 'Success';
-                                               $loginType = 'AuthManager';
-                                               break;
-
-                                       case AuthenticationResponse::FAIL:
-                                               // Hope it's not a PreAuthenticationProvider that failed...
-                                               $authRes = 'Failed';
-                                               $message = $res->message;
-                                               \MediaWiki\Logger\LoggerFactory::getInstance( 'authentication' )
-                                                       ->info( __METHOD__ . ': Authentication failed: ' . $message->plain() );
-                                               break;
-
-                                       default:
-                                               $authRes = 'Aborted';
-                                               break;
-                               }
+                       // Simplified AuthManager login, for backwards compatibility
+                       $manager = AuthManager::singleton();
+                       $reqs = AuthenticationRequest::loadRequestsFromSubmission(
+                               $manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN, $this->getUser() ),
+                               [
+                                       'username' => $params['name'],
+                                       'password' => $params['password'],
+                                       'domain' => $params['domain'],
+                                       'rememberMe' => true,
+                               ]
+                       );
+                       $res = AuthManager::singleton()->beginAuthentication( $reqs, 'null:' );
+                       switch ( $res->status ) {
+                               case AuthenticationResponse::PASS:
+                                       if ( $this->getConfig()->get( 'EnableBotPasswords' ) ) {
+                                               $warn = 'Main-account login via action=login is deprecated and may stop working ' .
+                                                       'without warning.';
+                                               $warn .= ' To continue login with action=login, see [[Special:BotPasswords]].';
+                                               $warn .= ' To safely continue using main-account login, see action=clientlogin.';
+                                       } else {
+                                               $warn = 'Login via action=login is deprecated and may stop working without warning.';
+                                               $warn .= ' To safely log in, see action=clientlogin.';
+                                       }
+                                       $this->setWarning( $warn );
+                                       $authRes = 'Success';
+                                       $loginType = 'AuthManager';
+                                       break;
+
+                               case AuthenticationResponse::FAIL:
+                                       // Hope it's not a PreAuthenticationProvider that failed...
+                                       $authRes = 'Failed';
+                                       $message = $res->message;
+                                       \MediaWiki\Logger\LoggerFactory::getInstance( 'authentication' )
+                                               ->info( __METHOD__ . ': Authentication failed: '
+                                               . $message->inLanguage( 'en' )->plain() );
+                                       break;
+
+                               default:
+                                       \MediaWiki\Logger\LoggerFactory::getInstance( 'authentication' )
+                                               ->info( __METHOD__ . ': Authentication failed due to unsupported response type: '
+                                               . $res->status, $this->getAuthenticationResponseLogData( $res ) );
+                                       $authRes = 'Aborted';
+                                       break;
                        }
                }
 
                $result['result'] = $authRes;
                switch ( $authRes ) {
                        case 'Success':
-                               if ( $this->getConfig()->get( 'DisableAuthManager' ) ) {
-                                       $user = $context->getUser();
-                                       $this->getContext()->setUser( $user );
-                                       $user->setCookies( $this->getRequest(), null, true );
-                               } else {
-                                       $user = $session->getUser();
-                               }
+                               $user = $session->getUser();
 
                                ApiQueryInfo::resetTokenCache();
 
                                // Deprecated hook
                                $injected_html = '';
-                               Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html ] );
+                               Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html, true ] );
 
                                $result['lguserid'] = intval( $user->getId() );
                                $result['lgusername'] = $user->getName();
@@ -252,65 +229,6 @@ class ApiLogin extends ApiBase {
                                }
                                break;
 
-                       // Results from LoginForm for when $wgDisableAuthManager is true
-                       case LoginForm::WRONG_TOKEN:
-                               $result['result'] = 'WrongToken';
-                               break;
-
-                       case LoginForm::NO_NAME:
-                               $result['result'] = 'NoName';
-                               break;
-
-                       case LoginForm::ILLEGAL:
-                               $result['result'] = 'Illegal';
-                               break;
-
-                       case LoginForm::WRONG_PLUGIN_PASS:
-                               $result['result'] = 'WrongPluginPass';
-                               break;
-
-                       case LoginForm::NOT_EXISTS:
-                               $result['result'] = 'NotExists';
-                               break;
-
-                       // bug 20223 - Treat a temporary password as wrong. Per SpecialUserLogin:
-                       // The e-mailed temporary password should not be used for actual logins.
-                       case LoginForm::RESET_PASS:
-                       case LoginForm::WRONG_PASS:
-                               $result['result'] = 'WrongPass';
-                               break;
-
-                       case LoginForm::EMPTY_PASS:
-                               $result['result'] = 'EmptyPass';
-                               break;
-
-                       case LoginForm::CREATE_BLOCKED:
-                               $result['result'] = 'CreateBlocked';
-                               $result['details'] = 'Your IP address is blocked from account creation';
-                               $block = $context->getUser()->getBlock();
-                               if ( $block ) {
-                                       $result = array_merge( $result, ApiQueryUserInfo::getBlockInfo( $block ) );
-                               }
-                               break;
-
-                       case LoginForm::THROTTLED:
-                               $result['result'] = 'Throttled';
-                               $result['wait'] = intval( $loginForm->mThrottleWait );
-                               break;
-
-                       case LoginForm::USER_BLOCKED:
-                               $result['result'] = 'Blocked';
-                               $block = User::newFromName( $params['name'] )->getBlock();
-                               if ( $block ) {
-                                       $result = array_merge( $result, ApiQueryUserInfo::getBlockInfo( $block ) );
-                               }
-                               break;
-
-                       case LoginForm::ABORTED:
-                               $result['result'] = 'Aborted';
-                               $result['reason'] = $loginForm->mAbortLoginErrorMsg;
-                               break;
-
                        default:
                                ApiBase::dieDebug( __METHOD__, "Unhandled case value: {$authRes}" );
                }
@@ -320,7 +238,7 @@ class ApiLogin extends ApiBase {
                if ( $loginType === 'LoginForm' && isset( LoginForm::$statusCodes[$authRes] ) ) {
                        $authRes = LoginForm::$statusCodes[$authRes];
                }
-               LoggerFactory::getInstance( 'authmanager' )->info( 'Login attempt', [
+               LoggerFactory::getInstance( 'authevents' )->info( 'Login attempt', [
                        'event' => 'login',
                        'successful' => $authRes === 'Success',
                        'loginType' => $loginType,
@@ -329,8 +247,7 @@ class ApiLogin extends ApiBase {
        }
 
        public function isDeprecated() {
-               return !$this->getConfig()->get( 'DisableAuthManager' ) &&
-                       !$this->getConfig()->get( 'EnableBotPasswords' );
+               return !$this->getConfig()->get( 'EnableBotPasswords' );
        }
 
        public function mustBePosted() {
@@ -368,4 +285,32 @@ class ApiLogin extends ApiBase {
        public function getHelpUrls() {
                return 'https://www.mediawiki.org/wiki/API:Login';
        }
+
+       /**
+        * Turns an AuthenticationResponse into a hash suitable for passing to Logger
+        * @param AuthenticationResponse $response
+        * @return array
+        */
+       protected function getAuthenticationResponseLogData( AuthenticationResponse $response ) {
+               $ret = [
+                       'status' => $response->status,
+               ];
+               if ( $response->message ) {
+                       $ret['message'] = $response->message->inLanguage( 'en' )->plain();
+               };
+               $reqs = [
+                       'neededRequests' => $response->neededRequests,
+                       'createRequest' => $response->createRequest,
+                       'linkRequest' => $response->linkRequest,
+               ];
+               foreach ( $reqs as $k => $v ) {
+                       if ( $v ) {
+                               $v = is_array( $v ) ? $v : [ $v ];
+                               $reqClasses = array_unique( array_map( 'get_class', $v ) );
+                               sort( $reqClasses );
+                               $ret[$k] = implode( ', ', $reqClasses );
+                       }
+               }
+               return $ret;
+       }
 }
index 8878729..1f3c76a 100644 (file)
@@ -25,6 +25,8 @@
  * @defgroup API API
  */
 
+use MediaWiki\Logger\LoggerFactory;
+
 /**
  * This is the main API class, used for both external and internal processing.
  * When executed, it will create the requested formatter object,
@@ -117,9 +119,9 @@ class ApiMain extends ApiBase {
        // @codingStandardsIgnoreStart String contenation on "msg" not allowed to break long line
        /**
         * List of user roles that are specifically relevant to the API.
-        * array( 'right' => array ( 'msg'    => 'Some message with a $1',
-        *                           'params' => array ( $someVarToSubst ) ),
-        *                          );
+        * [ 'right' => [ 'msg'    => 'Some message with a $1',
+        *                'params' => [ $someVarToSubst ] ],
+        * ];
         */
        private static $mRights = [
                'writeapi' => [
@@ -206,7 +208,7 @@ class ApiMain extends ApiBase {
                                        $config->get( 'CrossSiteAJAXdomainExceptions' )
                                )
                        ) ) {
-                               MediaWiki\Logger\LoggerFactory::getInstance( 'cors' )->warning(
+                               LoggerFactory::getInstance( 'cors' )->warning(
                                        'Non-whitelisted CORS request with session cookies', [
                                                'origin' => $originHeader,
                                                'cookies' => $sessionCookies,
@@ -298,6 +300,12 @@ class ApiMain extends ApiBase {
                        return true;
                }
 
+               // Anonymous CORS
+               if ( $request->getVal( 'origin' ) === '*' ) {
+                       $this->lacksSameOriginSecurity = true;
+                       return true;
+               }
+
                // Header to be used from XMLHTTPRequest when the request might
                // otherwise be used for XSS.
                if ( $request->getHeader( 'Treat-as-Untrusted' ) !== false ) {
@@ -644,31 +652,49 @@ class ApiMain extends ApiBase {
                $request = $this->getRequest();
                $response = $request->response();
 
-               // Origin: header is a space-separated list of origins, check all of them
-               $originHeader = $request->getHeader( 'Origin' );
-               if ( $originHeader === false ) {
-                       $origins = [];
+               $matchOrigin = false;
+               $allowTiming = false;
+               $varyOrigin = true;
+
+               if ( $originParam === '*' ) {
+                       // Request for anonymous CORS
+                       $matchOrigin = true;
+                       $allowOrigin = '*';
+                       $allowCredentials = 'false';
+                       $varyOrigin = false; // No need to vary
                } else {
-                       $originHeader = trim( $originHeader );
-                       $origins = preg_split( '/\s+/', $originHeader );
-               }
+                       // Non-anonymous CORS, check we allow the domain
 
-               if ( !in_array( $originParam, $origins ) ) {
-                       // origin parameter set but incorrect
-                       // Send a 403 response
-                       $response->statusHeader( 403 );
-                       $response->header( 'Cache-Control: no-cache' );
-                       echo "'origin' parameter does not match Origin header\n";
+                       // Origin: header is a space-separated list of origins, check all of them
+                       $originHeader = $request->getHeader( 'Origin' );
+                       if ( $originHeader === false ) {
+                               $origins = [];
+                       } else {
+                               $originHeader = trim( $originHeader );
+                               $origins = preg_split( '/\s+/', $originHeader );
+                       }
 
-                       return false;
-               }
+                       if ( !in_array( $originParam, $origins ) ) {
+                               // origin parameter set but incorrect
+                               // Send a 403 response
+                               $response->statusHeader( 403 );
+                               $response->header( 'Cache-Control: no-cache' );
+                               echo "'origin' parameter does not match Origin header\n";
 
-               $config = $this->getConfig();
-               $matchOrigin = count( $origins ) === 1 && self::matchOrigin(
-                       $originParam,
-                       $config->get( 'CrossSiteAJAXdomains' ),
-                       $config->get( 'CrossSiteAJAXdomainExceptions' )
-               );
+                               return false;
+                       }
+
+                       $config = $this->getConfig();
+                       $matchOrigin = count( $origins ) === 1 && self::matchOrigin(
+                               $originParam,
+                               $config->get( 'CrossSiteAJAXdomains' ),
+                               $config->get( 'CrossSiteAJAXdomainExceptions' )
+                       );
+
+                       $allowOrigin = $originHeader;
+                       $allowCredentials = 'true';
+                       $allowTiming = $originHeader;
+               }
 
                if ( $matchOrigin ) {
                        $requestedMethod = $request->getHeader( 'Access-Control-Request-Method' );
@@ -692,10 +718,12 @@ class ApiMain extends ApiBase {
                                $response->header( 'Access-Control-Allow-Methods: POST, GET' );
                        }
 
-                       $response->header( "Access-Control-Allow-Origin: $originHeader" );
-                       $response->header( 'Access-Control-Allow-Credentials: true' );
+                       $response->header( "Access-Control-Allow-Origin: $allowOrigin" );
+                       $response->header( "Access-Control-Allow-Credentials: $allowCredentials" );
                        // http://www.w3.org/TR/resource-timing/#timing-allow-origin
-                       $response->header( "Timing-Allow-Origin: $originHeader" );
+                       if ( $allowTiming !== false ) {
+                               $response->header( "Timing-Allow-Origin: $allowTiming" );
+                       }
 
                        if ( !$preflight ) {
                                $response->header(
@@ -704,7 +732,10 @@ class ApiMain extends ApiBase {
                        }
                }
 
-               $this->getOutput()->addVaryHeader( 'Origin' );
+               if ( $varyOrigin ) {
+                       $this->getOutput()->addVaryHeader( 'Origin' );
+               }
+
                return true;
        }
 
@@ -1072,18 +1103,7 @@ class ApiMain extends ApiBase {
                                $this->dieUsageMsg( [ 'missingparam', 'token' ] );
                        }
 
-                       if ( !$this->getConfig()->get( 'DebugAPI' ) &&
-                               array_key_exists(
-                                       $module->encodeParamName( 'token' ),
-                                       $this->getRequest()->getQueryValues()
-                               )
-                       ) {
-                               $this->dieUsage(
-                                       "The '{$module->encodeParamName( 'token' )}' parameter was " .
-                                               'found in the query string, but must be in the POST body',
-                                       'mustposttoken'
-                               );
-                       }
+                       $module->requirePostedParameters( [ 'token' ] );
 
                        if ( !$module->validateToken( $moduleParams['token'], $moduleParams ) ) {
                                $this->dieUsageMsg( 'sessionfailure' );
@@ -1302,9 +1322,9 @@ class ApiMain extends ApiBase {
                        }
                }
 
-               // If a majority of slaves are too lagged then disallow writes
-               $slaveCount = wfGetLB()->getServerCount() - 1;
-               if ( $numLagged >= ceil( $slaveCount / 2 ) ) {
+               // If a majority of replica DBs are too lagged then disallow writes
+               $replicaCount = wfGetLB()->getServerCount() - 1;
+               if ( $numLagged >= ceil( $replicaCount / 2 ) ) {
                        $laggedServers = implode( ', ', $laggedServers );
                        wfDebugLog(
                                'api-readonly',
@@ -1424,6 +1444,7 @@ class ApiMain extends ApiBase {
        protected function setRequestExpectations( ApiBase $module ) {
                $limits = $this->getConfig()->get( 'TrxProfilerLimits' );
                $trxProfiler = Profiler::instance()->getTransactionProfiler();
+               $trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
                if ( $this->getRequest()->hasSafeMethod() ) {
                        $trxProfiler->setExpectations( $limits['GET'], __METHOD__ );
                } elseif ( $this->getRequest()->wasPosted() && !$module->isWriteMode() ) {
index fe24c2a..42dfb71 100644 (file)
@@ -81,14 +81,14 @@ class ApiModuleManager extends ContextSource {
         *
         * @code
         *  $modules['foo'] = 'ApiFoo';
-        *  $modules['bar'] = array(
+        *  $modules['bar'] = [
         *      'class' => 'ApiBar',
         *      'factory' => function( $main, $name ) { ... }
-        *  );
-        *  $modules['xyzzy'] = array(
+        *  ];
+        *  $modules['xyzzy'] = [
         *      'class' => 'ApiXyzzy',
-        *      'factory' => array( 'XyzzyFactory', 'newApiModule' )
-        *  );
+        *      'factory' => [ 'XyzzyFactory', 'newApiModule' ]
+        *  ];
         * @endcode
         *
         * @param array $modules A map of ModuleName => ModuleSpec; The ModuleSpec
index 066aaa3..ace776c 100644 (file)
@@ -272,20 +272,7 @@ class ApiOpenSearch extends ApiBase {
                if ( $this->allowedParams !== null ) {
                        return $this->allowedParams;
                }
-               $this->allowedParams = [
-                       'search' => null,
-                       'limit' => [
-                               ApiBase::PARAM_DFLT => $this->getConfig()->get( 'OpenSearchDefaultLimit' ),
-                               ApiBase::PARAM_TYPE => 'limit',
-                               ApiBase::PARAM_MIN => 1,
-                               ApiBase::PARAM_MAX => 100,
-                               ApiBase::PARAM_MAX2 => 100
-                       ],
-                       'namespace' => [
-                               ApiBase::PARAM_DFLT => NS_MAIN,
-                               ApiBase::PARAM_TYPE => 'namespace',
-                               ApiBase::PARAM_ISMULTI => true
-                       ],
+               $this->allowedParams = $this->buildCommonApiParams( false ) + [
                        'suggest' => false,
                        'redirects' => [
                                ApiBase::PARAM_TYPE => [ 'return', 'resolve' ],
@@ -297,19 +284,21 @@ class ApiOpenSearch extends ApiBase {
                        'warningsaserror' => false,
                ];
 
-               $profileParam = $this->buildProfileApiParam( SearchEngine::COMPLETION_PROFILE_TYPE,
-                       'apihelp-query+prefixsearch-param-profile' );
-               if ( $profileParam ) {
-                       $this->allowedParams['profile'] = $profileParam;
-               }
+               // Use open search specific default limit
+               $this->allowedParams['limit'][ApiBase::PARAM_DFLT] = $this->getConfig()->get(
+                       'OpenSearchDefaultLimit'
+               );
+
                return $this->allowedParams;
        }
 
        public function getSearchProfileParams() {
-               if ( isset( $this->getAllowedParams()['profile'] ) ) {
-                       return [ SearchEngine::COMPLETION_PROFILE_TYPE => 'profile' ];
-               }
-               return [];
+               return [
+                       'profile' => [
+                               'profile-type' => SearchEngine::COMPLETION_PROFILE_TYPE,
+                               'help-message' => 'apihelp-query+prefixsearch-param-profile'
+                       ],
+               ];
        }
 
        protected function getExamplesMessages() {
index af4e536..ed229cb 100644 (file)
@@ -58,7 +58,7 @@ class ApiPageSet extends ApiBase {
        private $mGoodTitles = [];
        private $mMissingPages = []; // [ns][dbkey] => fake page_id
        private $mMissingTitles = [];
-       /** @var array [fake_page_id] => array( 'title' => $title, 'invalidreason' => $reason ) */
+       /** @var array [fake_page_id] => [ 'title' => $title, 'invalidreason' => $reason ] */
        private $mInvalidTitles = [];
        private $mMissingPageIDs = [];
        private $mRedirectTitles = [];
@@ -495,10 +495,14 @@ class ApiPageSet extends ApiBase {
         * @since 1.21
         */
        public function getNormalizedTitlesAsResult( $result = null ) {
+               global $wgContLang;
+
                $values = [];
                foreach ( $this->getNormalizedTitles() as $rawTitleStr => $titleStr ) {
+                       $encode = ( $wgContLang->normalize( $rawTitleStr ) !== $rawTitleStr );
                        $values[] = [
-                               'from' => $rawTitleStr,
+                               'fromencoded' => $encode,
+                               'from' => $encode ? rawurlencode( $rawTitleStr ) : $rawTitleStr,
                                'to' => $titleStr
                        ];
                }
@@ -777,7 +781,7 @@ class ApiPageSet extends ApiBase {
                $res = $db->select( 'page', $this->getPageTableFields(), $set,
                        __METHOD__ );
 
-               // Hack: get the ns:titles stored in array(ns => array(titles)) format
+               // Hack: get the ns:titles stored in [ ns => [ titles ] ] format
                $this->initFromQueryResult( $res, $linkBatch->data, true ); // process Titles
 
                // Resolve any found redirects
@@ -1002,7 +1006,7 @@ class ApiPageSet extends ApiBase {
                                // Get pageIDs data from the `page` table
                                $res = $db->select( 'page', $pageFlds, $set, __METHOD__ );
 
-                               // Hack: get the ns:titles stored in array(ns => array(titles)) format
+                               // Hack: get the ns:titles stored in [ns => array(titles)] format
                                $this->initFromQueryResult( $res, $linkBatch->data, true );
                        }
                }
@@ -1403,6 +1407,23 @@ class ApiPageSet extends ApiBase {
                return $result;
        }
 
+       protected function handleParamNormalization( $paramName, $value, $rawValue ) {
+               parent::handleParamNormalization( $paramName, $value, $rawValue );
+
+               if ( $paramName === 'titles' ) {
+                       // For the 'titles' parameter, we want to split it like ApiBase would
+                       // and add any changed titles to $this->mNormalizedTitles
+                       $value = $this->explodeMultiValue( $value, self::LIMIT_SML2 + 1 );
+                       $l = count( $value );
+                       $rawValue = $this->explodeMultiValue( $rawValue, $l );
+                       for ( $i = 0; $i < $l; $i++ ) {
+                               if ( $value[$i] !== $rawValue[$i] ) {
+                                       $this->mNormalizedTitles[$rawValue[$i]] = $value[$i];
+                               }
+                       }
+               }
+       }
+
        private static $generators = null;
 
        /**
index c3c9e21..caf0cd7 100644 (file)
@@ -46,7 +46,39 @@ class ApiParamInfo extends ApiBase {
                $this->context->setLanguage( $this->getMain()->getLanguage() );
 
                if ( is_array( $params['modules'] ) ) {
-                       $modules = $params['modules'];
+                       $modules = [];
+                       foreach ( $params['modules'] as $path ) {
+                               if ( $path === '*' || $path === '**' ) {
+                                       $path = "main+$path";
+                               }
+                               if ( substr( $path, -2 ) === '+*' || substr( $path, -2 ) === ' *' ) {
+                                       $submodules = true;
+                                       $path = substr( $path, 0, -2 );
+                                       $recursive = false;
+                               } elseif ( substr( $path, -3 ) === '+**' || substr( $path, -3 ) === ' **' ) {
+                                       $submodules = true;
+                                       $path = substr( $path, 0, -3 );
+                                       $recursive = true;
+                               } else {
+                                       $submodules = false;
+                               }
+
+                               if ( $submodules ) {
+                                       try {
+                                               $module = $this->getModuleFromPath( $path );
+                                       } catch ( UsageException $ex ) {
+                                               $this->setWarning( $ex->getMessage() );
+                                       }
+                                       $submodules = $this->listAllSubmodules( $module, $recursive );
+                                       if ( $submodules ) {
+                                               $modules = array_merge( $modules, $submodules );
+                                       } else {
+                                               $this->setWarning( "Module $path has no submodules" );
+                                       }
+                               } else {
+                                       $modules[] = $path;
+                               }
+                       }
                } else {
                        $modules = [];
                }
@@ -69,6 +101,8 @@ class ApiParamInfo extends ApiBase {
                        $formatModules = [];
                }
 
+               $modules = array_unique( $modules );
+
                $res = [];
 
                foreach ( $modules as $m ) {
@@ -121,6 +155,29 @@ class ApiParamInfo extends ApiBase {
                $result->addValue( null, $this->getModuleName(), $res );
        }
 
+       /**
+        * List all submodules of a module
+        * @param ApiBase $module
+        * @param boolean $recursive
+        * @return string[]
+        */
+       private function listAllSubmodules( ApiBase $module, $recursive ) {
+               $manager = $module->getModuleManager();
+               if ( $manager ) {
+                       $paths = [];
+                       $names = $manager->getNames();
+                       sort( $names );
+                       foreach ( $names as $name ) {
+                               $submodule = $manager->getModule( $name );
+                               $paths[] = $submodule->getModulePath();
+                               if ( $recursive && $submodule->getModuleManager() ) {
+                                       $paths = array_merge( $paths, $this->listAllSubmodules( $submodule, $recursive ) );
+                               }
+                       }
+               }
+               return $paths;
+       }
+
        /**
         * @param array $res Result array
         * @param string $key Result key
@@ -162,6 +219,7 @@ class ApiParamInfo extends ApiBase {
                                                'key' => $m->getKey(),
                                                'params' => $m->getParams(),
                                        ];
+                                       ApiResult::setIndexedTagName( $a['params'], 'param' );
                                        if ( $m instanceof ApiHelpParamValueMessage ) {
                                                $a['forvalue'] = $m->getParamValue();
                                        }
@@ -448,8 +506,10 @@ class ApiParamInfo extends ApiBase {
 
        protected function getExamplesMessages() {
                return [
-                       'action=paraminfo&modules=parse|phpfm|query+allpages|query+siteinfo'
+                       'action=paraminfo&modules=parse|phpfm|query%2Ballpages|query%2Bsiteinfo'
                                => 'apihelp-paraminfo-example-1',
+                       'action=paraminfo&modules=query%2B*'
+                               => 'apihelp-paraminfo-example-2',
                ];
        }
 
index f96acf3..83b5d93 100644 (file)
@@ -36,6 +36,12 @@ class ApiParse extends ApiBase {
        /** @var Content $pstContent */
        private $pstContent = null;
 
+       private function checkReadPermissions( Title $title ) {
+               if ( !$title->userCan( 'read', $this->getUser() ) ) {
+                       $this->dieUsage( "You don't have permission to view this page", 'permissiondenied' );
+               }
+       }
+
        public function execute() {
                // The data is hot but user-dependent, like page views, so we set vary cookies
                $this->getMain()->setCacheMode( 'anon-public-user-private' );
@@ -102,6 +108,8 @@ class ApiParse extends ApiBase {
                                if ( !$rev ) {
                                        $this->dieUsage( "There is no revision ID $oldid", 'missingrev' );
                                }
+
+                               $this->checkReadPermissions( $rev->getTitle() );
                                if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
                                        $this->dieUsage( "You don't have permission to view deleted revisions", 'permissiondenied' );
                                }
@@ -161,6 +169,8 @@ class ApiParse extends ApiBase {
                                if ( !$titleObj || !$titleObj->exists() ) {
                                        $this->dieUsage( "The page you specified doesn't exist", 'missingtitle' );
                                }
+
+                               $this->checkReadPermissions( $titleObj );
                                $wgTitle = $titleObj;
 
                                if ( isset( $prop['revid'] ) ) {
@@ -345,34 +355,32 @@ class ApiParse extends ApiBase {
                                $titleObj->getPrefixedText();
                }
 
-               if ( isset( $prop['headitems'] ) || isset( $prop['headhtml'] ) ) {
-                       $context = $this->getContext();
-                       $context->setTitle( $titleObj );
-                       $context->getOutput()->addParserOutputMetadata( $p_result );
-
-                       if ( isset( $prop['headitems'] ) ) {
-                               $headItems = $this->formatHeadItems( $p_result->getHeadItems() );
-
-                               $css = $this->formatCss( $context->getOutput()->buildCssLinksArray() );
+               if ( isset( $prop['headitems'] ) ) {
+                       $result_array['headitems'] = $this->formatHeadItems( $p_result->getHeadItems() );
+                       $this->logFeatureUsage( 'action=parse&prop=headitems' );
+                       $this->setWarning( 'headitems is deprecated since MediaWiki 1.28. '
+                               . 'Use prop=headhtml when creating new HTML documents, or '
+                               . 'prop=modules|jsconfigvars when updating a document client-side.' );
+               }
 
-                               $scripts = [ $context->getOutput()->getHeadScripts() ];
+               if ( isset( $prop['headhtml'] ) ) {
+                       $context = new DerivativeContext( $this->getContext() );
+                       $context->setTitle( $titleObj );
+                       $context->setWikiPage( $pageObj );
 
-                               $result_array['headitems'] = array_merge( $headItems, $css, $scripts );
-                       }
+                       // We need an OutputPage tied to $context, not to the
+                       // RequestContext at the root of the stack.
+                       $output = new OutputPage( $context );
+                       $output->addParserOutputMetadata( $p_result );
 
-                       if ( isset( $prop['headhtml'] ) ) {
-                               $result_array['headhtml'] = $context->getOutput()->headElement( $context->getSkin() );
-                               $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'headhtml';
-                       }
+                       $result_array['headhtml'] = $output->headElement( $context->getSkin() );
+                       $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'headhtml';
                }
 
                if ( isset( $prop['modules'] ) ) {
                        $result_array['modules'] = array_values( array_unique( $p_result->getModules() ) );
                        $result_array['modulescripts'] = array_values( array_unique( $p_result->getModuleScripts() ) );
                        $result_array['modulestyles'] = array_values( array_unique( $p_result->getModuleStyles() ) );
-                       // To be removed in 1.27
-                       $result_array['modulemessages'] = [];
-                       $this->setWarning( 'modulemessages is deprecated since MediaWiki 1.26' );
                }
 
                if ( isset( $prop['jsconfigvars'] ) ) {
@@ -456,7 +464,6 @@ class ApiParse extends ApiBase {
                        'indicators' => 'ind',
                        'modulescripts' => 'm',
                        'modulestyles' => 'm',
-                       'modulemessages' => 'm',
                        'properties' => 'pp',
                        'limitreportdata' => 'lr',
                ];
@@ -704,18 +711,6 @@ class ApiParse extends ApiBase {
                return $result;
        }
 
-       private function formatCss( $css ) {
-               $result = [];
-               foreach ( $css as $file => $link ) {
-                       $entry = [];
-                       $entry['file'] = $file;
-                       ApiResult::setContentValue( $entry, 'link', $link );
-                       $result[] = $entry;
-               }
-
-               return $result;
-       }
-
        private function formatLimitReportData( $limitReportData ) {
                $result = [];
 
index 64bb9ba..f671103 100644 (file)
@@ -68,35 +68,39 @@ class ApiPurge extends ApiBase {
 
                                        # Parse content; note that HTML generation is only needed if we want to cache the result.
                                        $content = $page->getContent( Revision::RAW );
-                                       $enableParserCache = $this->getConfig()->get( 'EnableParserCache' );
-                                       $p_result = $content->getParserOutput(
-                                               $title,
-                                               $page->getLatest(),
-                                               $popts,
-                                               $enableParserCache
-                                       );
-
-                                       # Logging to better see expensive usage patterns
-                                       if ( $forceRecursiveLinkUpdate ) {
-                                               LoggerFactory::getInstance( 'RecursiveLinkPurge' )->info(
-                                                       "Recursive link purge enqueued for {title}",
-                                                       [
-                                                               'user' => $this->getUser()->getName(),
-                                                               'title' => $title->getPrefixedText()
-                                                       ]
+                                       if ( $content ) {
+                                               $enableParserCache = $this->getConfig()->get( 'EnableParserCache' );
+                                               $p_result = $content->getParserOutput(
+                                                       $title,
+                                                       $page->getLatest(),
+                                                       $popts,
+                                                       $enableParserCache
                                                );
-                                       }
-
-                                       # Update the links tables
-                                       $updates = $content->getSecondaryDataUpdates(
-                                               $title, null, $forceRecursiveLinkUpdate, $p_result );
-                                       DataUpdate::runUpdates( $updates );
-
-                                       $r['linkupdate'] = true;
 
-                                       if ( $enableParserCache ) {
-                                               $pcache = ParserCache::singleton();
-                                               $pcache->save( $p_result, $page, $popts );
+                                               # Logging to better see expensive usage patterns
+                                               if ( $forceRecursiveLinkUpdate ) {
+                                                       LoggerFactory::getInstance( 'RecursiveLinkPurge' )->info(
+                                                               "Recursive link purge enqueued for {title}",
+                                                               [
+                                                                       'user' => $this->getUser()->getName(),
+                                                                       'title' => $title->getPrefixedText()
+                                                               ]
+                                                       );
+                                               }
+
+                                               # Update the links tables
+                                               $updates = $content->getSecondaryDataUpdates(
+                                                       $title, null, $forceRecursiveLinkUpdate, $p_result );
+                                               foreach ( $updates as $update ) {
+                                                       DeferredUpdates::addUpdate( $update, DeferredUpdates::PRESEND );
+                                               }
+
+                                               $r['linkupdate'] = true;
+
+                                               if ( $enableParserCache ) {
+                                                       $pcache = ParserCache::singleton();
+                                                       $pcache->save( $p_result, $page, $popts );
+                                               }
                                        }
                                } else {
                                        $error = $this->parseMsg( [ 'actionthrottledtext' ] );
index ed4d373..5eb86ab 100644 (file)
@@ -443,16 +443,13 @@ class ApiQuery extends ApiBase {
                }
 
                $exporter = new WikiExporter( $this->getDB() );
-               // WikiExporter writes to stdout, so catch its
-               // output with an ob
-               ob_start();
+               $sink = new DumpStringOutput;
+               $exporter->setOutputSink( $sink );
                $exporter->openStream();
                foreach ( $exportTitles as $title ) {
                        $exporter->pageByTitle( $title );
                }
                $exporter->closeStream();
-               $exportxml = ob_get_contents();
-               ob_end_clean();
 
                // Don't check the size of exported stuff
                // It's not continuable, so it would cause more
@@ -460,10 +457,10 @@ class ApiQuery extends ApiBase {
                if ( $this->mParams['exportnowrap'] ) {
                        $result->reset();
                        // Raw formatter will handle this
-                       $result->addValue( null, 'text', $exportxml, ApiResult::NO_SIZE_CHECK );
+                       $result->addValue( null, 'text', $sink, ApiResult::NO_SIZE_CHECK );
                        $result->addValue( null, 'mime', 'text/xml', ApiResult::NO_SIZE_CHECK );
                } else {
-                       $result->addValue( 'query', 'export', $exportxml, ApiResult::NO_SIZE_CHECK );
+                       $result->addValue( 'query', 'export', $sink, ApiResult::NO_SIZE_CHECK );
                        $result->addValue( 'query', ApiResult::META_BC_SUBELEMENTS, [ 'export' ] );
                }
        }
index a559683..3073a95 100644 (file)
@@ -55,6 +55,14 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
 
                $result = $this->getResult();
 
+               // If the user wants no namespaces, they get no pages.
+               if ( $params['namespace'] === [] ) {
+                       if ( $resultPageSet === null ) {
+                               $result->addValue( 'query', $this->getModuleName(), [] );
+                       }
+                       return;
+               }
+
                // This module operates in two modes:
                // 'user': List deleted revs by a certain user
                // 'all': List all deleted revs in NS
@@ -153,10 +161,10 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                if ( $mode == 'all' ) {
                        if ( $params['namespace'] !== null ) {
                                $namespaces = $params['namespace'];
-                               $this->addWhereFld( 'ar_namespace', $namespaces );
                        } else {
                                $namespaces = MWNamespace::getValidNamespaces();
                        }
+                       $this->addWhereFld( 'ar_namespace', $namespaces );
 
                        // For from/to/prefix, we have to consider the potential
                        // transformations of the title in all specified namespaces.
@@ -447,7 +455,7 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
                return [
                        'action=query&list=alldeletedrevisions&adruser=Example&adrlimit=50'
                                => 'apihelp-query+alldeletedrevisions-example-user',
-                       'action=query&list=alldeletedrevisions&adrdir=newer&adrlimit=50'
+                       'action=query&list=alldeletedrevisions&adrdir=newer&adrnamespace=0&adrlimit=50'
                                => 'apihelp-query+alldeletedrevisions-example-ns-main',
                ];
        }
index 1d250e9..661ec5a 100644 (file)
@@ -117,12 +117,12 @@ class ApiQueryAuthManagerInfo extends ApiQueryBase {
        protected function getExamplesMessages() {
                return [
                        'action=query&meta=authmanagerinfo&amirequestsfor=' . urlencode( AuthManager::ACTION_LOGIN )
-                               => 'apihelp-query+filerepoinfo-example-login',
+                               => 'apihelp-query+authmanagerinfo-example-login',
                        'action=query&meta=authmanagerinfo&amirequestsfor=' . urlencode( AuthManager::ACTION_LOGIN ) .
                                '&amimergerequestfields=1'
-                               => 'apihelp-query+filerepoinfo-example-login-merged',
+                               => 'apihelp-query+authmanagerinfo-example-login-merged',
                        'action=query&meta=authmanagerinfo&amisecuritysensitiveoperation=foo'
-                               => 'apihelp-query+filerepoinfo-example-securitysensitiveoperation',
+                               => 'apihelp-query+authmanagerinfo-example-securitysensitiveoperation',
                ];
        }
 
index 3810e90..236fb9e 100644 (file)
@@ -53,6 +53,7 @@ class ApiQueryBacklinksprop extends ApiQueryGeneratorBase {
                        'code' => 'lh',
                        'prefix' => 'pl',
                        'linktable' => 'pagelinks',
+                       'indexes' => [ 'pl_namespace', 'pl_backlinks_namespace' ],
                        'from_namespace' => true,
                        'showredirects' => true,
                ],
@@ -60,6 +61,7 @@ class ApiQueryBacklinksprop extends ApiQueryGeneratorBase {
                        'code' => 'ti',
                        'prefix' => 'tl',
                        'linktable' => 'templatelinks',
+                       'indexes' => [ 'tl_namespace', 'tl_backlinks_namespace' ],
                        'from_namespace' => true,
                        'showredirects' => true,
                ],
@@ -67,6 +69,7 @@ class ApiQueryBacklinksprop extends ApiQueryGeneratorBase {
                        'code' => 'fu',
                        'prefix' => 'il',
                        'linktable' => 'imagelinks',
+                       'indexes' => [ 'il_to', 'il_backlinks_namespace' ],
                        'from_namespace' => true,
                        'to_namespace' => NS_FILE,
                        'exampletitle' => 'File:Example.jpg',
@@ -249,6 +252,18 @@ class ApiQueryBacklinksprop extends ApiQueryGeneratorBase {
                // Override any ORDER BY from above with what we calculated earlier.
                $this->addOption( 'ORDER BY', array_keys( $sortby ) );
 
+               // MySQL's optimizer chokes if we have too many values in "$bl_title IN
+               // (...)" and chooses the wrong index, so specify the correct index to
+               // use for the query. See T139056 for details.
+               if ( !empty( $settings['indexes'] ) ) {
+                       list( $idxNoFromNS, $idxWithFromNS ) = $settings['indexes'];
+                       if ( $params['namespace'] !== null && !empty( $settings['from_namespace'] ) ) {
+                               $this->addOption( 'USE INDEX', [ $settings['linktable'] => $idxWithFromNS ] );
+                       } else {
+                               $this->addOption( 'USE INDEX', [ $settings['linktable'] => $idxNoFromNS ] );
+                       }
+               }
+
                $this->addOption( 'LIMIT', $params['limit'] + 1 );
 
                $res = $this->select( __METHOD__ );
index 318af58..b35eec2 100644 (file)
@@ -176,10 +176,9 @@ abstract class ApiQueryBase extends ApiBase {
        /**
         * Add a set of JOIN conditions to the internal array
         *
-        * JOIN conditions are formatted as array( tablename => array(jointype,
-        * conditions) e.g. array('page' => array('LEFT JOIN',
-        * 'page_id=rev_page')) . conditions may be a string or an
-        * addWhere()-style array
+        * JOIN conditions are formatted as [ tablename => [ jointype, conditions ] ]
+        * e.g. [ 'page' => [ 'LEFT JOIN', 'page_id=rev_page' ] ].
+        * Conditions may be a string or an addWhere()-style array.
         * @param array $join_conds JOIN conditions
         */
        protected function addJoinConds( $join_conds ) {
@@ -219,12 +218,12 @@ abstract class ApiQueryBase extends ApiBase {
 
        /**
         * Add a set of WHERE clauses to the internal array.
-        * Clauses can be formatted as 'foo=bar' or array('foo' => 'bar'),
+        * Clauses can be formatted as 'foo=bar' or [ 'foo' => 'bar' ],
         * the latter only works if the value is a constant (i.e. not another field)
         *
         * If $value is an empty array, this function does nothing.
         *
-        * For example, array('foo=bar', 'baz' => 3, 'bla' => 'foo') translates
+        * For example, [ 'foo=bar', 'baz' => 3, 'bla' => 'foo' ] translates
         * to "foo=bar AND baz='3' AND bla='foo'"
         * @param string|array $value
         */
@@ -341,13 +340,13 @@ abstract class ApiQueryBase extends ApiBase {
         * @param string $method Function the query should be attributed to.
         *  You should usually use __METHOD__ here
         * @param array $extraQuery Query data to add but not store in the object
-        *  Format is array(
+        *  Format is [
         *    'tables' => ...,
         *    'fields' => ...,
         *    'where' => ...,
         *    'options' => ...,
         *    'join_conds' => ...
-        *  )
+        *  ]
         * @return ResultWrapper
         */
        protected function select( $method, $extraQuery = [] ) {
index 8b8bd01..5d7c664 100644 (file)
@@ -173,10 +173,8 @@ class ApiQueryBlocks extends ApiQueryBase {
                        $this->addWhereFld( 'ipb_deleted', 0 );
                }
 
-               // Purge expired entries on one in every 10 queries
-               if ( !mt_rand( 0, 10 ) ) {
-                       Block::purgeExpired();
-               }
+               # Filter out expired rows
+               $this->addWhere( 'ipb_expiry > ' . $db->addQuotes( $db->timestamp() ) );
 
                $res = $this->select( __METHOD__ );
 
index ae93bb1..02b7883 100644 (file)
@@ -96,7 +96,7 @@ class ApiQueryDuplicateFiles extends ApiQueryGeneratorBase {
                }
 
                // find all files with the hashes, result format is:
-               // array( hash => array( dup1, dup2 ), hash1 => ... )
+               // [ hash => [ dup1, dup2 ], hash1 => ... ]
                $filesToFindBySha1s = array_unique( array_values( $sha1s ) );
                if ( $params['localonly'] ) {
                        $filesBySha1s = RepoGroup::singleton()->getLocalRepo()->findBySha1s( $filesToFindBySha1s );
index f5c49ad..d287020 100644 (file)
@@ -92,7 +92,7 @@ class ApiQueryInfo extends ApiQueryBase {
         * The prototype for a token function is func($pageid, $title)
         * it should return a token or false (permission denied)
         * @deprecated since 1.24
-        * @return array Array(tokenname => function)
+        * @return array [ tokenname => function ]
         */
        protected function getTokenFunctions() {
                // Don't call the hooks twice
index 46538e0..3bf6d3f 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-use MediaWiki\MediaWikiServices;
 
 /**
  * This program is free software; you can redistribute it and/or modify
@@ -106,42 +105,18 @@ class ApiQueryPrefixSearch extends ApiQueryGeneratorBase {
                if ( $this->allowedParams !== null ) {
                        return $this->allowedParams;
                }
-               $this->allowedParams = [
-                       'search' => [
-                               ApiBase::PARAM_TYPE => 'string',
-                               ApiBase::PARAM_REQUIRED => true,
-                       ],
-                       'namespace' => [
-                               ApiBase::PARAM_DFLT => NS_MAIN,
-                               ApiBase::PARAM_TYPE => 'namespace',
-                               ApiBase::PARAM_ISMULTI => true,
-                       ],
-                       'limit' => [
-                               ApiBase::PARAM_DFLT => 10,
-                               ApiBase::PARAM_TYPE => 'limit',
-                               ApiBase::PARAM_MIN => 1,
-                               // Non-standard value for compatibility with action=opensearch
-                               ApiBase::PARAM_MAX => 100,
-                               ApiBase::PARAM_MAX2 => 200,
-                       ],
-                       'offset' => [
-                               ApiBase::PARAM_DFLT => 0,
-                               ApiBase::PARAM_TYPE => 'integer',
-                       ],
-               ];
-               $profileParam = $this->buildProfileApiParam( SearchEngine::COMPLETION_PROFILE_TYPE,
-                       'apihelp-query+prefixsearch-param-profile' );
-               if ( $profileParam ) {
-                       $this->allowedParams['profile'] = $profileParam;
-               }
+               $this->allowedParams = $this->buildCommonApiParams();
+
                return $this->allowedParams;
        }
 
        public function getSearchProfileParams() {
-               if ( isset( $this->getAllowedParams()['profile'] ) ) {
-                       return [ SearchEngine::COMPLETION_PROFILE_TYPE => 'profile' ];
-               }
-               return [];
+               return [
+                       'profile' => [
+                               'profile-type' => SearchEngine::COMPLETION_PROFILE_TYPE,
+                               'help-message' => 'apihelp-query+prefixsearch-param-profile',
+                       ],
+               ];
        }
 
        protected function getExamplesMessages() {
index f0fd2f4..cc3ca60 100644 (file)
@@ -48,7 +48,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
         * The prototype for a token function is func($pageid, $title, $rc)
         * it should return a token or false (permission denied)
         * @deprecated since 1.24
-        * @return array Array(tokenname => function)
+        * @return array [ tokenname => function ]
         */
        protected function getTokenFunctions() {
                // Don't call the hooks twice
@@ -150,7 +150,6 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                 *              AND rc_timestamp < $end AND rc_namespace = $namespace
                 */
                $this->addTables( 'recentchanges' );
-               $index = [ 'recentchanges' => 'rc_timestamp' ]; // May change
                $this->addTimestampWhereRange( 'rc_timestamp', $params['dir'], $params['start'], $params['end'] );
 
                if ( !is_null( $params['continue'] ) ) {
@@ -246,7 +245,6 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
 
                if ( !is_null( $params['user'] ) ) {
                        $this->addWhereFld( 'rc_user_text', $params['user'] );
-                       $index['recentchanges'] = 'rc_user_text';
                }
 
                if ( !is_null( $params['excludeuser'] ) ) {
@@ -362,7 +360,6 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
 
                $this->token = $params['token'];
                $this->addOption( 'LIMIT', $params['limit'] + 1 );
-               $this->addOption( 'USE INDEX', $index );
 
                $count = 0;
                /* Perform the actual query. */
index 4377831..f46b5d2 100644 (file)
@@ -37,14 +37,6 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
        /** @var array list of api allowed params */
        private $allowedParams;
 
-       /**
-        * When $wgSearchType is null, $wgSearchAlternatives[0] is null. Null isn't
-        * a valid option for an array for PARAM_TYPE, so we'll use a fake name
-        * that can't possibly be a class name and describes what the null behavior
-        * does
-        */
-       const BACKEND_NULL_PARAM = 'database-backed';
-
        public function __construct( ApiQuery $query, $moduleName ) {
                parent::__construct( $query, $moduleName, 'sr' );
        }
@@ -65,10 +57,6 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
                global $wgContLang;
                $params = $this->extractRequestParams();
 
-               if ( isset( $params['backend'] ) && $params['backend'] == self::BACKEND_NULL_PARAM ) {
-                       unset( $params['backend'] );
-               }
-
                // Extract parameters
                $query = $params['search'];
                $what = $params['what'];
@@ -308,16 +296,7 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
                        return $this->allowedParams;
                }
 
-               $this->allowedParams = [
-                       'search' => [
-                               ApiBase::PARAM_TYPE => 'string',
-                               ApiBase::PARAM_REQUIRED => true
-                       ],
-                       'namespace' => [
-                               ApiBase::PARAM_DFLT => NS_MAIN,
-                               ApiBase::PARAM_TYPE => 'namespace',
-                               ApiBase::PARAM_ISMULTI => true,
-                       ],
+               $this->allowedParams = $this->buildCommonApiParams() + [
                        'what' => [
                                ApiBase::PARAM_TYPE => [
                                        'title',
@@ -354,52 +333,20 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
                                ApiBase::PARAM_ISMULTI => true,
                                ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
                        ],
-                       'offset' => [
-                               ApiBase::PARAM_DFLT => 0,
-                               ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
-                       ],
-                       'limit' => [
-                               ApiBase::PARAM_DFLT => 10,
-                               ApiBase::PARAM_TYPE => 'limit',
-                               ApiBase::PARAM_MIN => 1,
-                               ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
-                               ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
-                       ],
                        'interwiki' => false,
                        'enablerewrites' => false,
                ];
 
-               $searchConfig = MediaWikiServices::getInstance()->getSearchEngineConfig();
-               $alternatives = $searchConfig->getSearchTypes();
-               if ( count( $alternatives ) > 1 ) {
-                       if ( $alternatives[0] === null ) {
-                               $alternatives[0] = self::BACKEND_NULL_PARAM;
-                       }
-                       $this->allowedParams['backend'] = [
-                               ApiBase::PARAM_DFLT => $searchConfig->getSearchType(),
-                               ApiBase::PARAM_TYPE => $alternatives,
-                       ];
-                       // @todo: support profile selection when multiple
-                       // backends are available. The solution could be to
-                       // merge all possible profiles and let ApiBase
-                       // subclasses do the check. Making ApiHelp and ApiSandbox
-                       // comprehensive might be more difficult.
-               } else {
-                       $profileParam = $this->buildProfileApiParam( SearchEngine::FT_QUERY_INDEP_PROFILE_TYPE,
-                               'apihelp-query+search-param-qiprofile' );
-                       if ( $profileParam ) {
-                               $this->allowedParams['qiprofile'] = $profileParam;
-                       }
-               }
-
                return $this->allowedParams;
        }
 
        public function getSearchProfileParams() {
-               if ( isset( $this->getAllowedParams()['qiprofile'] ) ) {
-                       return [ SearchEngine::FT_QUERY_INDEP_PROFILE_TYPE => 'qiprofile' ];
-               }
-               return [];
+               return [
+                       'qiprofile' => [
+                               'profile-type' => SearchEngine::FT_QUERY_INDEP_PROFILE_TYPE,
+                               'help-message' => 'apihelp-query+search-param-qiprofile',
+                       ],
+               ];
        }
 
        protected function getExamplesMessages() {
index 97042af..5d32497 100644 (file)
@@ -594,7 +594,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                                        $ret['description'] = $ext['description'];
                                }
                                if ( isset( $ext['descriptionmsg'] ) ) {
-                                       // Can be a string or array( key, param1, param2, ... )
+                                       // Can be a string or [ key, param1, param2, ... ]
                                        if ( is_array( $ext['descriptionmsg'] ) ) {
                                                $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
                                                $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
index 0688970..f92a916 100644 (file)
@@ -35,7 +35,8 @@ class ApiQueryContributions extends ApiQueryBase {
                parent::__construct( $query, $moduleName, 'uc' );
        }
 
-       private $params, $prefixMode, $userprefix, $multiUserMode, $usernames, $parentLens;
+       private $params, $prefixMode, $userprefix, $multiUserMode, $idMode, $usernames, $userids,
+               $parentLens;
        private $fld_ids = false, $fld_title = false, $fld_timestamp = false,
                $fld_comment = false, $fld_parsedcomment = false, $fld_flags = false,
                $fld_patrolled = false, $fld_tags = false, $fld_size = false, $fld_sizediff = false;
@@ -56,19 +57,22 @@ class ApiQueryContributions extends ApiQueryBase {
                $this->fld_patrolled = isset( $prop['patrolled'] );
                $this->fld_tags = isset( $prop['tags'] );
 
-               // Most of this code will use the 'contributions' group DB, which can map to slaves
+               // Most of this code will use the 'contributions' group DB, which can map to replica DBs
                // with extra user based indexes or partioning by user. The additional metadata
-               // queries should use a regular slave since the lookup pattern is not all by user.
-               $dbSecondary = $this->getDB(); // any random slave
+               // queries should use a regular replica DB since the lookup pattern is not all by user.
+               $dbSecondary = $this->getDB(); // any random replica DB
 
                // TODO: if the query is going only against the revision table, should this be done?
-               $this->selectNamedDB( 'contributions', DB_SLAVE, 'contributions' );
+               $this->selectNamedDB( 'contributions', DB_REPLICA, 'contributions' );
 
+               $this->idMode = false;
                if ( isset( $this->params['userprefix'] ) ) {
                        $this->prefixMode = true;
                        $this->multiUserMode = true;
                        $this->userprefix = $this->params['userprefix'];
                } else {
+                       $anyIPs = false;
+                       $this->userids = [];
                        $this->usernames = [];
                        if ( !is_array( $this->params['user'] ) ) {
                                $this->params['user'] = [ $this->params['user'] ];
@@ -77,10 +81,32 @@ class ApiQueryContributions extends ApiQueryBase {
                                $this->dieUsage( 'User parameter may not be empty.', 'param_user' );
                        }
                        foreach ( $this->params['user'] as $u ) {
-                               $this->prepareUsername( $u );
+                               if ( is_null( $u ) || $u === '' ) {
+                                       $this->dieUsage( 'User parameter may not be empty', 'param_user' );
+                               }
+
+                               if ( User::isIP( $u ) ) {
+                                       $anyIPs = true;
+                                       $this->usernames[] = $u;
+                               } else {
+                                       $name = User::getCanonicalName( $u, 'valid' );
+                                       if ( $name === false ) {
+                                               $this->dieUsage( "User name {$u} is not valid", 'param_user' );
+                                       }
+                                       $this->usernames[] = $name;
+                               }
                        }
                        $this->prefixMode = false;
                        $this->multiUserMode = ( count( $this->params['user'] ) > 1 );
+
+                       if ( !$anyIPs ) {
+                               $dbr = $this->getDB();
+                               $res = $dbr->select( 'user', 'user_id', [ 'user_name' => $this->usernames ], __METHOD__ );
+                               foreach ( $res as $row ) {
+                                       $this->userids[] = $row->user_id;
+                               }
+                               $this->idMode = count( $this->userids ) === count( $this->usernames );
+                       }
                }
 
                $this->prepareQuery();
@@ -126,27 +152,6 @@ class ApiQueryContributions extends ApiQueryBase {
                );
        }
 
-       /**
-        * Validate the 'user' parameter and set the value to compare
-        * against `revision`.`rev_user_text`
-        *
-        * @param string $user
-        */
-       private function prepareUsername( $user ) {
-               if ( !is_null( $user ) && $user !== '' ) {
-                       $name = User::isIP( $user )
-                               ? $user
-                               : User::getCanonicalName( $user, 'valid' );
-                       if ( $name === false ) {
-                               $this->dieUsage( "User name {$user} is not valid", 'param_user' );
-                       } else {
-                               $this->usernames[] = $name;
-                       }
-               } else {
-                       $this->dieUsage( 'User parameter may not be empty', 'param_user' );
-               }
-       }
-
        /**
         * Prepares the query and returns the limit of rows requested
         */
@@ -163,7 +168,17 @@ class ApiQueryContributions extends ApiQueryBase {
                        $continue = explode( '|', $this->params['continue'] );
                        $db = $this->getDB();
                        if ( $this->multiUserMode ) {
-                               $this->dieContinueUsageIf( count( $continue ) != 3 );
+                               $this->dieContinueUsageIf( count( $continue ) != 4 );
+                               $modeFlag = array_shift( $continue );
+                               $this->dieContinueUsageIf( !in_array( $modeFlag, [ 'id', 'name' ] ) );
+                               if ( $this->idMode && $modeFlag === 'name' ) {
+                                       // The users were created since this query started, but we
+                                       // can't go back and change modes now. So just keep on with
+                                       // name mode.
+                                       $this->idMode = false;
+                               }
+                               $this->dieContinueUsageIf( ( $modeFlag === 'id' ) !== $this->idMode );
+                               $userField = $this->idMode ? 'rev_user' : 'rev_user_text';
                                $encUser = $db->addQuotes( array_shift( $continue ) );
                        } else {
                                $this->dieContinueUsageIf( count( $continue ) != 2 );
@@ -174,8 +189,8 @@ class ApiQueryContributions extends ApiQueryBase {
                        $op = ( $this->params['dir'] == 'older' ? '<' : '>' );
                        if ( $this->multiUserMode ) {
                                $this->addWhere(
-                                       "rev_user_text $op $encUser OR " .
-                                       "(rev_user_text = $encUser AND " .
+                                       "$userField $op $encUser OR " .
+                                       "($userField = $encUser AND " .
                                        "(rev_timestamp $op $encTS OR " .
                                        "(rev_timestamp = $encTS AND " .
                                        "rev_id $op= $encId)))"
@@ -206,14 +221,17 @@ class ApiQueryContributions extends ApiQueryBase {
                if ( $this->prefixMode ) {
                        $this->addWhere( 'rev_user_text' .
                                $this->getDB()->buildLike( $this->userprefix, $this->getDB()->anyString() ) );
+               } elseif ( $this->idMode ) {
+                       $this->addWhereFld( 'rev_user', $this->userids );
                } else {
                        $this->addWhereFld( 'rev_user_text', $this->usernames );
                }
                // ... and in the specified timeframe.
-               // Ensure the same sort order for rev_user_text and rev_timestamp
+               // Ensure the same sort order for rev_user/rev_user_text and rev_timestamp
                // so our query is indexed
                if ( $this->multiUserMode ) {
-                       $this->addWhereRange( 'rev_user_text', $this->params['dir'], null, null );
+                       $this->addWhereRange( $this->idMode ? 'rev_user' : 'rev_user_text',
+                               $this->params['dir'], null, null );
                }
                $this->addTimestampWhereRange( 'rev_timestamp',
                        $this->params['dir'], $this->params['start'], $this->params['end'] );
@@ -247,7 +265,6 @@ class ApiQueryContributions extends ApiQueryBase {
                        $this->addWhereIf( 'rev_parent_id = 0', isset( $show['new'] ) );
                }
                $this->addOption( 'LIMIT', $this->params['limit'] + 1 );
-               $index = [ 'revision' => 'usertext_timestamp' ];
 
                // Mandatory fields: timestamp allows request continuation
                // ns+title checks if the user has access rights for this page
@@ -320,7 +337,9 @@ class ApiQueryContributions extends ApiQueryBase {
                        $this->addWhereFld( 'ct_tag', $this->params['tag'] );
                }
 
-               $this->addOption( 'USE INDEX', $index );
+               if ( isset( $index ) ) {
+                       $this->addOption( 'USE INDEX', $index );
+               }
        }
 
        /**
@@ -430,7 +449,11 @@ class ApiQueryContributions extends ApiQueryBase {
 
        private function continueStr( $row ) {
                if ( $this->multiUserMode ) {
-                       return "$row->rev_user_text|$row->rev_timestamp|$row->rev_id";
+                       if ( $this->idMode ) {
+                               return "id|$row->rev_user|$row->rev_timestamp|$row->rev_id";
+                       } else {
+                               return "name|$row->rev_user_text|$row->rev_timestamp|$row->rev_id";
+                       }
                } else {
                        return "$row->rev_timestamp|$row->rev_id";
                }
index 5afb66f..9b45b91 100644 (file)
@@ -261,7 +261,7 @@ class ApiQueryUsers extends ApiQueryBase {
                                        }
                                } else {
                                        $data[$u]['missing'] = true;
-                                       if ( isset( $this->prop['cancreate'] ) && !$this->getConfig()->get( 'DisableAuthManager' ) ) {
+                                       if ( isset( $this->prop['cancreate'] ) ) {
                                                $status = MediaWiki\Auth\AuthManager::singleton()->canCreateAccount( $u );
                                                $data[$u]['cancreate'] = $status->isGood();
                                                if ( !$status->isGood() ) {
@@ -307,7 +307,7 @@ class ApiQueryUsers extends ApiQueryBase {
        }
 
        public function getAllowedParams() {
-               $ret = [
+               return [
                        'prop' => [
                                ApiBase::PARAM_ISMULTI => true,
                                ApiBase::PARAM_TYPE => [
@@ -320,6 +320,7 @@ class ApiQueryUsers extends ApiQueryBase {
                                        'emailable',
                                        'gender',
                                        'centralids',
+                                       'cancreate',
                                        // When adding a prop, consider whether it should be added
                                        // to self::$publicProps
                                ],
@@ -327,7 +328,6 @@ class ApiQueryUsers extends ApiQueryBase {
                        ],
                        'attachedwiki' => null,
                        'users' => [
-                               ApiBase::PARAM_TYPE => 'user',
                                ApiBase::PARAM_ISMULTI => true
                        ],
                        'token' => [
@@ -336,10 +336,6 @@ class ApiQueryUsers extends ApiQueryBase {
                                ApiBase::PARAM_ISMULTI => true
                        ],
                ];
-               if ( !$this->getConfig()->get( 'DisableAuthManager' ) ) {
-                       $ret['prop'][ApiBase::PARAM_TYPE][] = 'cancreate';
-               }
-               return $ret;
        }
 
        protected function getExamplesMessages() {
index e2599d1..c30f0cf 100644 (file)
@@ -57,7 +57,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
         * @return void
         */
        private function run( $resultPageSet = null ) {
-               $this->selectNamedDB( 'watchlist', DB_SLAVE, 'watchlist' );
+               $this->selectNamedDB( 'watchlist', DB_REPLICA, 'watchlist' );
 
                $params = $this->extractRequestParams();
 
index 64b97fe..806861e 100644 (file)
@@ -24,6 +24,8 @@
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
+
 /**
  * This query action allows clients to retrieve a list of pages
  * on the logged-in user's watchlist.
@@ -49,95 +51,78 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase {
         * @return void
         */
        private function run( $resultPageSet = null ) {
-               $this->selectNamedDB( 'watchlist', DB_SLAVE, 'watchlist' );
-
                $params = $this->extractRequestParams();
 
                $user = $this->getWatchlistUser( $params );
 
                $prop = array_flip( (array)$params['prop'] );
                $show = array_flip( (array)$params['show'] );
-               if ( isset( $show['changed'] ) && isset( $show['!changed'] ) ) {
+               if ( isset( $show[WatchedItemQueryService::FILTER_CHANGED] )
+                       && isset( $show[WatchedItemQueryService::FILTER_NOT_CHANGED] )
+               ) {
                        $this->dieUsageMsg( 'show' );
                }
 
-               $this->addTables( 'watchlist' );
-               $this->addFields( [ 'wl_namespace', 'wl_title' ] );
-               $this->addFieldsIf( 'wl_notificationtimestamp', isset( $prop['changed'] ) );
-               $this->addWhereFld( 'wl_user', $user->getId() );
-               $this->addWhereFld( 'wl_namespace', $params['namespace'] );
-               $this->addWhereIf( 'wl_notificationtimestamp IS NOT NULL', isset( $show['changed'] ) );
-               $this->addWhereIf( 'wl_notificationtimestamp IS NULL', isset( $show['!changed'] ) );
+               $options = [];
+               if ( $params['namespace'] ) {
+                       $options['namespaceIds'] = $params['namespace'];
+               }
+               if ( isset( $show[WatchedItemQueryService::FILTER_CHANGED] ) ) {
+                       $options['filter'] = WatchedItemQueryService::FILTER_CHANGED;
+               }
+               if ( isset( $show[WatchedItemQueryService::FILTER_NOT_CHANGED] ) ) {
+                       $options['filter'] = WatchedItemQueryService::FILTER_NOT_CHANGED;
+               }
 
                if ( isset( $params['continue'] ) ) {
                        $cont = explode( '|', $params['continue'] );
                        $this->dieContinueUsageIf( count( $cont ) != 2 );
                        $ns = intval( $cont[0] );
                        $this->dieContinueUsageIf( strval( $ns ) !== $cont[0] );
-                       $title = $this->getDB()->addQuotes( $cont[1] );
-                       $op = $params['dir'] == 'ascending' ? '>' : '<';
-                       $this->addWhere(
-                               "wl_namespace $op $ns OR " .
-                               "(wl_namespace = $ns AND " .
-                               "wl_title $op= $title)"
-                       );
+                       $title = $cont[1];
+                       $options['startFrom'] = new TitleValue( $ns, $title );
                }
 
                if ( isset( $params['fromtitle'] ) ) {
                        list( $ns, $title ) = $this->prefixedTitlePartToKey( $params['fromtitle'] );
-                       $title = $this->getDB()->addQuotes( $title );
-                       $op = $params['dir'] == 'ascending' ? '>' : '<';
-                       $this->addWhere(
-                               "wl_namespace $op $ns OR " .
-                               "(wl_namespace = $ns AND " .
-                               "wl_title $op= $title)"
-                       );
+                       $options['from'] = new TitleValue( $ns, $title );
                }
 
                if ( isset( $params['totitle'] ) ) {
                        list( $ns, $title ) = $this->prefixedTitlePartToKey( $params['totitle'] );
-                       $title = $this->getDB()->addQuotes( $title );
-                       $op = $params['dir'] == 'ascending' ? '<' : '>'; // Reversed from above!
-                       $this->addWhere(
-                               "wl_namespace $op $ns OR " .
-                               "(wl_namespace = $ns AND " .
-                               "wl_title $op= $title)"
-                       );
+                       $options['until'] = new TitleValue( $ns, $title );
                }
 
-               $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
-               // Don't ORDER BY wl_namespace if it's constant in the WHERE clause
-               if ( count( $params['namespace'] ) == 1 ) {
-                       $this->addOption( 'ORDER BY', 'wl_title' . $sort );
-               } else {
-                       $this->addOption( 'ORDER BY', [
-                               'wl_namespace' . $sort,
-                               'wl_title' . $sort
-                       ] );
+               $options['sort'] = WatchedItemStore::SORT_ASC;
+               if ( $params['dir'] === 'descending' ) {
+                       $options['sort'] = WatchedItemStore::SORT_DESC;
                }
-               $this->addOption( 'LIMIT', $params['limit'] + 1 );
-               $res = $this->select( __METHOD__ );
+               $options['limit'] = $params['limit'] + 1;
 
                $titles = [];
                $count = 0;
-               foreach ( $res as $row ) {
+               $items = MediaWikiServices::getInstance()->getWatchedItemQueryService()
+                       ->getWatchedItemsForUser( $user, $options );
+               foreach ( $items as $item ) {
+                       $ns = $item->getLinkTarget()->getNamespace();
+                       $dbKey = $item->getLinkTarget()->getDBkey();
                        if ( ++$count > $params['limit'] ) {
                                // We've reached the one extra which shows that there are
                                // additional pages to be had. Stop here...
-                               $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' . $row->wl_title );
+                               $this->setContinueEnumParameter( 'continue', $ns . '|' . $dbKey );
                                break;
                        }
-                       $t = Title::makeTitle( $row->wl_namespace, $row->wl_title );
+                       $t = Title::makeTitle( $ns, $dbKey );
 
                        if ( is_null( $resultPageSet ) ) {
                                $vals = [];
                                ApiQueryBase::addTitleInfo( $vals, $t );
-                               if ( isset( $prop['changed'] ) && !is_null( $row->wl_notificationtimestamp ) ) {
-                                       $vals['changed'] = wfTimestamp( TS_ISO_8601, $row->wl_notificationtimestamp );
+                               if ( isset( $prop['changed'] ) && !is_null( $item->getNotificationTimestamp() ) ) {
+                                       $vals['changed'] = wfTimestamp( TS_ISO_8601, $item->getNotificationTimestamp() );
                                }
                                $fit = $this->getResult()->addValue( $this->getModuleName(), null, $vals );
                                if ( !$fit ) {
-                                       $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' . $row->wl_title );
+                                       $this->setContinueEnumParameter( 'continue', $ns . '|' . $dbKey );
                                        break;
                                }
                        } else {
@@ -177,8 +162,8 @@ class ApiQueryWatchlistRaw extends ApiQueryGeneratorBase {
                        'show' => [
                                ApiBase::PARAM_ISMULTI => true,
                                ApiBase::PARAM_TYPE => [
-                                       'changed',
-                                       '!changed',
+                                       WatchedItemQueryService::FILTER_CHANGED,
+                                       WatchedItemQueryService::FILTER_NOT_CHANGED
                                ]
                        ],
                        'owner' => [
index 5d5c829..e308ba4 100644 (file)
@@ -20,7 +20,7 @@
 
 /**
  * This class represents the result of the API operations.
- * It simply wraps a nested array() structure, adding some functions to simplify
+ * It simply wraps a nested array structure, adding some functions to simplify
  * array's modifications. As various modules execute, they add different pieces
  * of information to this result, structuring it as it will be given to the client.
  *
@@ -217,7 +217,7 @@ class ApiResult implements ApiSerializable {
         *    set to '*'. This may be skipped by including 'no*' in the value
         *    array.
         *  - Tags listed in META_BC_SUBELEMENTS will have their values changed to
-        *    array( '*' => $value ). This may be skipped by including 'nosub' in
+        *    [ '*' => $value ]. This may be skipped by including 'nosub' in
         *    the value array.
         *  - If META_TYPE is 'BCarray', set it to 'default'
         *  - If META_TYPE is 'BCassoc', set it to 'default'
@@ -230,9 +230,9 @@ class ApiResult implements ApiSerializable {
         *    as objects.
         *  - ArmorKVP: (string) If provided, transform arrays with META_TYPE 'kvp'
         *    and 'BCkvp' into arrays of two-element arrays, something like this:
-        *      $output = array();
+        *      $output = [];
         *      foreach ( $input as $key => $value ) {
-        *          $pair = array();
+        *          $pair = [];
         *          $pair[$META_KVP_KEY_NAME ?: $ArmorKVP_value] = $key;
         *          ApiResult::setContentValue( $pair, 'value', $value );
         *          $output[] = $pair;
@@ -390,7 +390,7 @@ class ApiResult implements ApiSerializable {
         * Add value to the output data at the given path.
         *
         * Path can be an indexed array, each element specifying the branch at which to add the new
-        * value. Setting $path to array('a','b','c') is equivalent to data['a']['b']['c'] = $value.
+        * value. Setting $path to [ 'a', 'b', 'c' ] is equivalent to data['a']['b']['c'] = $value.
         * If $path is null, the value will be inserted at the data root.
         *
         * @param array|string|int|null $path
@@ -415,7 +415,7 @@ class ApiResult implements ApiSerializable {
                        if ( $this->maxSize !== false && $newsize > $this->maxSize ) {
                                /// @todo Add i18n message when replacing calls to ->setWarning()
                                $msg = new ApiRawMessage( 'This result was truncated because it would otherwise ' .
-                                       ' be larger than the limit of $1 bytes', 'truncatedresult' );
+                                       'be larger than the limit of $1 bytes', 'truncatedresult' );
                                $msg->numParams( $this->maxSize );
                                $this->errorFormatter->addWarning( 'result', $msg );
                                return false;
index 446a98c..5a2492d 100644 (file)
@@ -20,6 +20,7 @@
  */
 
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Prepare an edit in shared cache so that it can be reused on edit
@@ -39,6 +40,7 @@ class ApiStashEdit extends ApiBase {
        const ERROR_PARSE = 'error_parse';
        const ERROR_CACHE = 'error_cache';
        const ERROR_UNCACHEABLE = 'uncacheable';
+       const ERROR_BUSY = 'busy';
 
        const PRESUME_FRESH_TTL_SEC = 30;
        const MAX_CACHE_TTL = 300; // 5 minutes
@@ -51,6 +53,7 @@ class ApiStashEdit extends ApiBase {
                        $this->dieUsage( 'This interface is not supported for bots', 'botsnotsupported' );
                }
 
+               $cache = ObjectCache::getLocalClusterInstance();
                $page = $this->getTitleOrPageId( $params );
                $title = $page->getTitle();
 
@@ -60,8 +63,25 @@ class ApiStashEdit extends ApiBase {
                        $this->dieUsage( 'Unsupported content model/format', 'badmodelformat' );
                }
 
-               // Trim and fix newlines so the key SHA1's match (see RequestContext::getText())
-               $text = rtrim( str_replace( "\r\n", "\n", $params['text'] ) );
+               $text = null;
+               $textHash = null;
+               if ( strlen( $params['stashedtexthash'] ) ) {
+                       // Load from cache since the client indicates the text is the same as last stash
+                       $textHash = $params['stashedtexthash'];
+                       $textKey = $cache->makeKey( 'stashedit', 'text', $textHash );
+                       $text = $cache->get( $textKey );
+                       if ( !is_string( $text ) ) {
+                               $this->dieUsage( 'No stashed text found with the given hash', 'missingtext' );
+                       }
+               } elseif ( $params['text'] !== null ) {
+                       // Trim and fix newlines so the key SHA1's match (see WebRequest::getText())
+                       $text = rtrim( str_replace( "\r\n", "\n", $params['text'] ) );
+                       $textHash = sha1( $text );
+               } else {
+                       $this->dieUsage(
+                               'The text or stashedtexthash parameter must be given', 'missingtextparam' );
+               }
+
                $textContent = ContentHandler::makeContent(
                        $text, $title, $params['contentmodel'], $params['contentformat'] );
 
@@ -113,24 +133,25 @@ class ApiStashEdit extends ApiBase {
                // The user will abort the AJAX request by pressing "save", so ignore that
                ignore_user_abort( true );
 
-               // Use the master DB for fast blocking locks
-               $dbw = wfGetDB( DB_MASTER );
-
-               // Get a key based on the source text, format, and user preferences
-               $key = self::getStashKey( $title, $content, $user );
-               // De-duplicate requests on the same key
                if ( $user->pingLimiter( 'stashedit' ) ) {
                        $status = 'ratelimited';
-               } elseif ( $dbw->lock( $key, __METHOD__, 1 ) ) {
-                       $status = self::parseAndStash( $page, $content, $user, $params['summary'] );
-                       $dbw->unlock( $key, __METHOD__ );
                } else {
-                       $status = 'busy';
+                       $status = self::parseAndStash( $page, $content, $user, $params['summary'] );
+                       $textKey = $cache->makeKey( 'stashedit', 'text', $textHash );
+                       $cache->set( $textKey, $text, self::MAX_CACHE_TTL );
                }
 
-               $this->getStats()->increment( "editstash.cache_stores.$status" );
+               $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
+               $stats->increment( "editstash.cache_stores.$status" );
 
-               $this->getResult()->addValue( null, $this->getModuleName(), [ 'status' => $status ] );
+               $this->getResult()->addValue(
+                       null,
+                       $this->getModuleName(),
+                       [
+                               'status' => $status,
+                               'texthash' => $textHash
+                       ]
+               );
        }
 
        /**
@@ -145,17 +166,42 @@ class ApiStashEdit extends ApiBase {
                $cache = ObjectCache::getLocalClusterInstance();
                $logger = LoggerFactory::getInstance( 'StashEdit' );
 
-               $format = $content->getDefaultFormat();
-               $editInfo = $page->prepareContentForEdit( $content, null, $user, $format, false );
                $title = $page->getTitle();
+               $key = self::getStashKey( $title, self::getContentHash( $content ), $user );
 
-               if ( $editInfo && $editInfo->output ) {
-                       $key = self::getStashKey( $title, $content, $user );
+               // Use the master DB for fast blocking locks
+               $dbw = wfGetDB( DB_MASTER );
+               if ( !$dbw->lock( $key, __METHOD__, 1 ) ) {
+                       // De-duplicate requests on the same key
+                       return self::ERROR_BUSY;
+               }
+               /** @noinspection PhpUnusedLocalVariableInspection */
+               $unlocker = new ScopedCallback( function () use ( $dbw, $key ) {
+                       $dbw->unlock( $key, __METHOD__ );
+               } );
+
+               $cutoffTime = time() - self::PRESUME_FRESH_TTL_SEC;
+
+               // Reuse any freshly build matching edit stash cache
+               $editInfo = $cache->get( $key );
+               if ( $editInfo && wfTimestamp( TS_UNIX, $editInfo->timestamp ) >= $cutoffTime ) {
+                       $alreadyCached = true;
+               } else {
+                       $format = $content->getDefaultFormat();
+                       $editInfo = $page->prepareContentForEdit( $content, null, $user, $format, false );
+                       $alreadyCached = false;
+               }
 
+               if ( $editInfo && $editInfo->output ) {
                        // Let extensions add ParserOutput metadata or warm other caches
                        Hooks::run( 'ParserOutputStashForEdit',
                                [ $page, $content, $editInfo->output, $summary, $user ] );
 
+                       if ( $alreadyCached ) {
+                               $logger->debug( "Already cached parser output for key '$key' ('$title')." );
+                               return self::ERROR_NONE;
+                       }
+
                        list( $stashInfo, $ttl, $code ) = self::buildStashValue(
                                $editInfo->pstContent,
                                $editInfo->output,
@@ -205,9 +251,9 @@ class ApiStashEdit extends ApiBase {
 
                $cache = ObjectCache::getLocalClusterInstance();
                $logger = LoggerFactory::getInstance( 'StashEdit' );
-               $stats = RequestContext::getMain()->getStats();
+               $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
 
-               $key = self::getStashKey( $title, $content, $user );
+               $key = self::getStashKey( $title, self::getContentHash( $content ), $user );
                $editInfo = $cache->get( $key );
                if ( !is_object( $editInfo ) ) {
                        $start = microtime( true );
@@ -215,7 +261,7 @@ class ApiStashEdit extends ApiBase {
                        // so as to use its results and make use of the time spent parsing.
                        // Skip this logic if there no master connection in case this method
                        // is called on an HTTP GET request for some reason.
-                       $lb = wfGetLB();
+                       $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
                        $dbw = $lb->getAnyOpenConnection( $lb->getWriterIndex() );
                        if ( $dbw && $dbw->lock( $key, __METHOD__, 30 ) ) {
                                $editInfo = $cache->get( $key );
@@ -272,7 +318,7 @@ class ApiStashEdit extends ApiBase {
         * @return string|null TS_MW timestamp or null
         */
        private static function lastEditTime( User $user ) {
-               $time = wfGetDB( DB_SLAVE )->selectField(
+               $time = wfGetDB( DB_REPLICA )->selectField(
                        'recentchanges',
                        'MAX(rc_timestamp)',
                        [ 'rc_user_text' => $user->getName() ],
@@ -282,6 +328,20 @@ class ApiStashEdit extends ApiBase {
                return wfTimestampOrNull( TS_MW, $time );
        }
 
+       /**
+        * Get hash of the content, factoring in model/format
+        *
+        * @param Content $content
+        * @return string
+        */
+       private static function getContentHash( Content $content ) {
+               return sha1( implode( "\n", [
+                       $content->getModel(),
+                       $content->getDefaultFormat(),
+                       $content->serialize( $content->getDefaultFormat() )
+               ] ) );
+       }
+
        /**
         * Get the temporary prepared edit stash key for a user
         *
@@ -290,22 +350,19 @@ class ApiStashEdit extends ApiBase {
         *   - b) The parser output was made from the PST using cannonical matching options
         *
         * @param Title $title
-        * @param Content $content
+        * @param string $contentHash Result of getContentHash()
         * @param User $user User to get parser options from
         * @return string
         */
-       private static function getStashKey( Title $title, Content $content, User $user ) {
-               $hash = sha1( implode( ':', [
+       private static function getStashKey( Title $title, $contentHash, User $user ) {
+               return ObjectCache::getLocalClusterInstance()->makeKey(
+                       'prepared-edit',
+                       md5( $title->getPrefixedDBkey() ),
                        // Account for the edit model/text
-                       $content->getModel(),
-                       $content->getDefaultFormat(),
-                       sha1( $content->serialize( $content->getDefaultFormat() ) ),
+                       $contentHash,
                        // Account for user name related variables like signatures
-                       $user->getId(),
-                       md5( $user->getName() )
-               ] ) );
-
-               return wfMemcKey( 'prepared-edit', md5( $title->getPrefixedDBkey() ), $hash );
+                       md5( $user->getId() . "\n" . $user->getName() )
+               );
        }
 
        /**
@@ -313,7 +370,7 @@ class ApiStashEdit extends ApiBase {
         *
         * This makes a simple version of WikiPage::prepareContentForEdit() as stash info
         *
-        * @param Content $pstContent
+        * @param Content $pstContent Pre-Save transformed content
         * @param ParserOutput $parserOutput
         * @param string $timestamp TS_MW
         * @param User $user
@@ -355,7 +412,11 @@ class ApiStashEdit extends ApiBase {
                        ],
                        'text' => [
                                ApiBase::PARAM_TYPE => 'text',
-                               ApiBase::PARAM_REQUIRED => true
+                               ApiBase::PARAM_DFLT => null
+                       ],
+                       'stashedtexthash' => [
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_DFLT => null
                        ],
                        'summary' => [
                                ApiBase::PARAM_TYPE => 'string',
index c2d1e0d..f88c2db 100644 (file)
@@ -63,7 +63,7 @@ class ApiTag extends ApiBase {
        }
 
        protected static function validateLogId( $logid ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $result = $dbr->selectField( 'logging', 'log_id', [ 'log_id' => $logid ],
                        __METHOD__ );
                return (bool)$result;
index 15c1e39..7b44f40 100644 (file)
@@ -64,7 +64,8 @@ class ApiUpload extends ApiBase {
                                $this->dieUsage( 'No upload module set', 'nomodule' );
                        }
                } catch ( UploadStashException $e ) { // XXX: don't spam exception log
-                       $this->handleStashException( $e );
+                       list( $msg, $code ) = $this->handleStashException( get_class( $e ), $e->getMessage() );
+                       $this->dieUsage( $msg, $code );
                }
 
                // First check permission to upload
@@ -108,15 +109,19 @@ class ApiUpload extends ApiBase {
                // Get the result based on the current upload context:
                try {
                        $result = $this->getContextResult();
-                       if ( $result['result'] === 'Success' ) {
-                               $result['imageinfo'] = $this->mUpload->getImageInfo( $this->getResult() );
-                       }
                } catch ( UploadStashException $e ) { // XXX: don't spam exception log
-                       $this->handleStashException( $e );
+                       list( $msg, $code ) = $this->handleStashException( get_class( $e ), $e->getMessage() );
+                       $this->dieUsage( $msg, $code );
                }
-
                $this->getResult()->addValue( null, $this->getModuleName(), $result );
 
+               // Add 'imageinfo' in a separate addValue() call. File metadata can be unreasonably large,
+               // so otherwise when it exceeded $wgAPIMaxResultSize, no result would be returned (T143993).
+               if ( $result['result'] === 'Success' ) {
+                       $imageinfo = $this->mUpload->getImageInfo( $this->getResult() );
+                       $this->getResult()->addValue( $this->getModuleName(), 'imageinfo', $imageinfo );
+               }
+
                // Cleanup any temporary mess
                $this->mUpload->cleanupTempFile();
        }
@@ -156,20 +161,13 @@ class ApiUpload extends ApiBase {
         */
        private function getStashResult( $warnings ) {
                $result = [];
+               $result['result'] = 'Success';
+               if ( $warnings && count( $warnings ) > 0 ) {
+                       $result['warnings'] = $warnings;
+               }
                // Some uploads can request they be stashed, so as not to publish them immediately.
                // In this case, a failure to stash ought to be fatal
-               try {
-                       $result['result'] = 'Success';
-                       $result['filekey'] = $this->performStash();
-                       $result['sessionkey'] = $result['filekey']; // backwards compatibility
-                       if ( $warnings && count( $warnings ) > 0 ) {
-                               $result['warnings'] = $warnings;
-                       }
-               } catch ( UploadStashException $e ) {
-                       $this->handleStashException( $e );
-               } catch ( Exception $e ) {
-                       $this->dieUsage( $e->getMessage(), 'stashfailed' );
-               }
+               $this->performStash( 'critical', $result );
 
                return $result;
        }
@@ -185,12 +183,7 @@ class ApiUpload extends ApiBase {
                $result['warnings'] = $warnings;
                // in case the warnings can be fixed with some further user action, let's stash this upload
                // and return a key they can use to restart it
-               try {
-                       $result['filekey'] = $this->performStash();
-                       $result['sessionkey'] = $result['filekey']; // backwards compatibility
-               } catch ( Exception $e ) {
-                       $result['warnings']['stashfailed'] = $e->getMessage();
-               }
+               $this->performStash( 'optional', $result );
 
                return $result;
        }
@@ -228,14 +221,7 @@ class ApiUpload extends ApiBase {
                }
 
                if ( $this->mParams['offset'] == 0 ) {
-                       try {
-                               $filekey = $this->performStash();
-                       } catch ( UploadStashException $e ) {
-                               $this->handleStashException( $e );
-                       } catch ( Exception $e ) {
-                               // FIXME: Error handling here is wrong/different from rest of this
-                               $this->dieUsage( $e->getMessage(), 'stashfailed' );
-                       }
+                       $filekey = $this->performStash( 'critical' );
                } else {
                        $filekey = $this->mParams['filekey'];
 
@@ -257,7 +243,7 @@ class ApiUpload extends ApiBase {
                                        'offset' => $this->mUpload->getOffset(),
                                ];
 
-                               $this->dieUsage( $status->getWikiText( false, false, 'en' ), 'stashfailed', 0, $extradata );
+                               $this->dieStatusWithCode( $status, 'stashfailed', $extradata );
                        }
                }
 
@@ -288,14 +274,20 @@ class ApiUpload extends ApiBase {
                                                $filekey,
                                                [ 'result' => 'Failure', 'stage' => 'assembling', 'status' => $status ]
                                        );
-                                       $this->dieUsage( $status->getWikiText( false, false, 'en' ), 'stashfailed' );
+                                       $this->dieStatusWithCode( $status, 'stashfailed' );
+                               }
+
+                               // We can only get warnings like 'duplicate' after concatenating the chunks
+                               $warnings = $this->getApiWarnings();
+                               if ( $warnings ) {
+                                       $result['warnings'] = $warnings;
                                }
 
                                // The fully concatenated file has a new filekey. So remove
                                // the old filekey and fetch the new one.
                                UploadBase::setSessionStatus( $this->getUser(), $filekey, false );
                                $this->mUpload->stash->removeFile( $filekey );
-                               $filekey = $this->mUpload->getLocalFile()->getFileKey();
+                               $filekey = $this->mUpload->getStashFile()->getFileKey();
 
                                $result['result'] = 'Success';
                        }
@@ -320,27 +312,58 @@ class ApiUpload extends ApiBase {
        }
 
        /**
-        * Stash the file and return the file key
-        * Also re-raises exceptions with slightly more informative message strings (useful for API)
-        * @throws MWException
-        * @return string File key
+        * Stash the file and add the file key, or error information if it fails, to the data.
+        *
+        * @param string $failureMode What to do on failure to stash:
+        *   - When 'critical', use dieStatus() to produce an error response and throw an exception.
+        *     Use this when stashing the file was the primary purpose of the API request.
+        *   - When 'optional', only add a 'stashfailed' key to the data and return null.
+        *     Use this when some error happened for a non-stash upload and we're stashing the file
+        *     only to save the client the trouble of re-uploading it.
+        * @param array &$data API result to which to add the information
+        * @return string|null File key
         */
-       private function performStash() {
+       private function performStash( $failureMode, &$data = null ) {
+               $isPartial = (bool)$this->mParams['chunk'];
                try {
-                       $stashFile = $this->mUpload->stashFile( $this->getUser() );
+                       $status = $this->mUpload->tryStashFile( $this->getUser(), $isPartial );
 
-                       if ( !$stashFile ) {
-                               throw new MWException( 'Invalid stashed file' );
+                       if ( $status->isGood() && !$status->getValue() ) {
+                               // Not actually a 'good' status...
+                               $status->fatal( new ApiRawMessage( 'Invalid stashed file', 'stashfailed' ) );
                        }
-                       $fileKey = $stashFile->getFileKey();
                } catch ( Exception $e ) {
-                       $message = 'Stashing temporary file failed: ' . get_class( $e ) . ' ' . $e->getMessage();
-                       wfDebug( __METHOD__ . ' ' . $message . "\n" );
-                       $className = get_class( $e );
-                       throw new $className( $message );
+                       $debugMessage = 'Stashing temporary file failed: ' . get_class( $e ) . ' ' . $e->getMessage();
+                       wfDebug( __METHOD__ . ' ' . $debugMessage . "\n" );
+                       $status = Status::newFatal( new ApiRawMessage( $e->getMessage(), 'stashfailed' ) );
                }
 
-               return $fileKey;
+               if ( $status->isGood() ) {
+                       $stashFile = $status->getValue();
+                       $data['filekey'] = $stashFile->getFileKey();
+                       // Backwards compatibility
+                       $data['sessionkey'] = $data['filekey'];
+                       return $data['filekey'];
+               }
+
+               if ( $status->getMessage()->getKey() === 'uploadstash-exception' ) {
+                       // The exceptions thrown by upload stash code and pretty silly and UploadBase returns poor
+                       // Statuses for it. Just extract the exception details and parse them ourselves.
+                       list( $exceptionType, $message ) = $status->getMessage()->getParams();
+                       $debugMessage = 'Stashing temporary file failed: ' . $exceptionType . ' ' . $message;
+                       wfDebug( __METHOD__ . ' ' . $debugMessage . "\n" );
+                       list( $msg, $code ) = $this->handleStashException( $exceptionType, $message );
+                       $status = Status::newFatal( new ApiRawMessage( $msg, $code ) );
+               }
+
+               // Bad status
+               if ( $failureMode !== 'optional' ) {
+                       $this->dieStatus( $status );
+               } else {
+                       list( $code, $msg ) = $this->getErrorFromStatus( $status );
+                       $data['stashfailed'] = $msg;
+                       return null;
+               }
        }
 
        /**
@@ -350,25 +373,47 @@ class ApiUpload extends ApiBase {
         * @param array|string|MessageSpecifier $error Error suitable for passing to dieUsageMsg()
         * @param string $parameter Parameter that needs revising
         * @param array $data Optional extra data to pass to the user
+        * @param string $code Error code to use if the error is unknown
         * @throws UsageException
         */
-       private function dieRecoverableError( $error, $parameter, $data = [] ) {
-               try {
-                       $data['filekey'] = $this->performStash();
-                       $data['sessionkey'] = $data['filekey'];
-               } catch ( Exception $e ) {
-                       $data['stashfailed'] = $e->getMessage();
-               }
+       private function dieRecoverableError( $error, $parameter, $data = [], $code = 'unknownerror' ) {
+               $this->performStash( 'optional', $data );
                $data['invalidparameter'] = $parameter;
 
                $parsed = $this->parseMsg( $error );
                if ( isset( $parsed['data'] ) ) {
                        $data = array_merge( $data, $parsed['data'] );
                }
+               if ( $parsed['code'] === 'unknownerror' ) {
+                       $parsed['code'] = $code;
+               }
 
                $this->dieUsage( $parsed['info'], $parsed['code'], 0, $data );
        }
 
+       /**
+        * Like dieStatus(), but always uses $overrideCode for the error code, unless the code comes from
+        * IApiMessage.
+        *
+        * @param Status $status
+        * @param string $overrideCode Error code to use if there isn't one from IApiMessage
+        * @param array|null $moreExtraData
+        * @throws UsageException
+        */
+       public function dieStatusWithCode( $status, $overrideCode, $moreExtraData = null ) {
+               $extraData = null;
+               list( $code, $msg ) = $this->getErrorFromStatus( $status, $extraData );
+               $errors = $status->getErrorsByType( 'error' ) ?: $status->getErrorsByType( 'warning' );
+               if ( !( $errors[0]['message'] instanceof IApiMessage ) ) {
+                       $code = $overrideCode;
+               }
+               if ( $moreExtraData ) {
+                       $extraData = $extraData ?: [];
+                       $extraData += $moreExtraData;
+               }
+               $this->dieUsage( $msg, $code, 0, $extraData );
+       }
+
        /**
         * Select an upload module and set it to mUpload. Dies on failure. If the
         * request was a status request and not a true upload, returns false;
@@ -391,13 +436,30 @@ class ApiUpload extends ApiBase {
                        if ( !$progress ) {
                                $this->dieUsage( 'No result in status data', 'missingresult' );
                        } elseif ( !$progress['status']->isGood() ) {
-                               $this->dieUsage( $progress['status']->getWikiText( false, false, 'en' ), 'stashfailed' );
+                               $this->dieStatusWithCode( $progress['status'], 'stashfailed' );
                        }
                        if ( isset( $progress['status']->value['verification'] ) ) {
                                $this->checkVerification( $progress['status']->value['verification'] );
                        }
+                       if ( isset( $progress['status']->value['warnings'] ) ) {
+                               $warnings = $this->transformWarnings( $progress['status']->value['warnings'] );
+                               if ( $warnings ) {
+                                       $progress['warnings'] = $warnings;
+                               }
+                       }
                        unset( $progress['status'] ); // remove Status object
+                       $imageinfo = null;
+                       if ( isset( $progress['imageinfo'] ) ) {
+                               $imageinfo = $progress['imageinfo'];
+                               unset( $progress['imageinfo'] );
+                       }
+
                        $this->getResult()->addValue( null, $this->getModuleName(), $progress );
+                       // Add 'imageinfo' in a separate addValue() call. File metadata can be unreasonably large,
+                       // so otherwise when it exceeded $wgAPIMaxResultSize, no result would be returned (T143993).
+                       if ( $imageinfo ) {
+                               $this->getResult()->addValue( $this->getModuleName(), 'imageinfo', $imageinfo );
+                       }
 
                        return false;
                }
@@ -409,7 +471,7 @@ class ApiUpload extends ApiBase {
 
                if ( $this->mParams['chunk'] ) {
                        // Chunk upload
-                       $this->mUpload = new UploadFromChunks();
+                       $this->mUpload = new UploadFromChunks( $this->getUser() );
                        if ( isset( $this->mParams['filekey'] ) ) {
                                if ( $this->mParams['offset'] === 0 ) {
                                        $this->dieUsage( 'Cannot supply a filekey when offset is 0', 'badparams' );
@@ -563,12 +625,28 @@ class ApiUpload extends ApiBase {
                                $this->dieUsage( $msg, 'filetype-banned', 0, $extradata );
                                break;
                        case UploadBase::VERIFICATION_ERROR:
-                               $params = $verification['details'];
-                               $key = array_shift( $params );
-                               $msg = $this->msg( $key, $params )->inLanguage( 'en' )->useDatabase( false )->text();
-                               ApiResult::setIndexedTagName( $verification['details'], 'detail' );
-                               $this->dieUsage( "This file did not pass file verification: $msg", 'verification-error',
-                                       0, [ 'details' => $verification['details'] ] );
+                               $parsed = $this->parseMsg( $verification['details'] );
+                               $info = "This file did not pass file verification: {$parsed['info']}";
+                               if ( $verification['details'][0] instanceof IApiMessage ) {
+                                       $code = $parsed['code'];
+                               } else {
+                                       // For backwards-compatibility, all of the errors from UploadBase::verifyFile() are
+                                       // reported as 'verification-error', and the real error code is reported in 'details'.
+                                       $code = 'verification-error';
+                               }
+                               if ( $verification['details'][0] instanceof IApiMessage ) {
+                                       $msg = $verification['details'][0];
+                                       $details = array_merge( [ $msg->getKey() ], $msg->getParams() );
+                               } else {
+                                       $details = $verification['details'];
+                               }
+                               ApiResult::setIndexedTagName( $details, 'detail' );
+                               $data = [ 'details' => $details ];
+                               if ( isset( $parsed['data'] ) ) {
+                                       $data = array_merge( $data, $parsed['data'] );
+                               }
+
+                               $this->dieUsage( $info, $code, 0, $data );
                                break;
                        case UploadBase::HOOK_ABORTED:
                                if ( is_array( $verification['error'] ) ) {
@@ -626,6 +704,30 @@ class ApiUpload extends ApiBase {
                                        : $warning['file'];
                                $warnings[$warning['warning']] = $localFile->getName();
                        }
+
+                       if ( isset( $warnings['no-change'] ) ) {
+                               /** @var File $file */
+                               $file = $warnings['no-change'];
+                               unset( $warnings['no-change'] );
+
+                               $warnings['nochange'] = [
+                                       'timestamp' => wfTimestamp( TS_ISO_8601, $file->getTimestamp() )
+                               ];
+                       }
+
+                       if ( isset( $warnings['duplicate-version'] ) ) {
+                               $dupes = [];
+                               /** @var File $dupe */
+                               foreach ( $warnings['duplicate-version'] as $dupe ) {
+                                       $dupes[] = [
+                                               'timestamp' => wfTimestamp( TS_ISO_8601, $dupe->getTimestamp() )
+                                       ];
+                               }
+                               unset( $warnings['duplicate-version'] );
+
+                               ApiResult::setIndexedTagName( $dupes, 'ver' );
+                               $warnings['duplicateversions'] = $dupes;
+                       }
                }
 
                return $warnings;
@@ -633,49 +735,41 @@ class ApiUpload extends ApiBase {
 
        /**
         * Handles a stash exception, giving a useful error to the user.
-        * @param Exception $e The exception we encountered.
+        * @param string $exceptionType Class name of the exception we encountered.
+        * @param string $message Message of the exception we encountered.
+        * @return array Array of message and code, suitable for passing to dieUsage()
         */
-       protected function handleStashException( $e ) {
-               $exceptionType = get_class( $e );
-
+       protected function handleStashException( $exceptionType, $message ) {
                switch ( $exceptionType ) {
                        case 'UploadStashFileNotFoundException':
-                               $this->dieUsage(
-                                       'Could not find the file in the stash: ' . $e->getMessage(),
+                               return [
+                                       'Could not find the file in the stash: ' . $message,
                                        'stashedfilenotfound'
-                               );
-                               break;
+                               ];
                        case 'UploadStashBadPathException':
-                               $this->dieUsage(
-                                       'File key of improper format or otherwise invalid: ' . $e->getMessage(),
+                               return [
+                                       'File key of improper format or otherwise invalid: ' . $message,
                                        'stashpathinvalid'
-                               );
-                               break;
+                               ];
                        case 'UploadStashFileException':
-                               $this->dieUsage(
-                                       'Could not store upload in the stash: ' . $e->getMessage(),
+                               return [
+                                       'Could not store upload in the stash: ' . $message,
                                        'stashfilestorage'
-                               );
-                               break;
+                               ];
                        case 'UploadStashZeroLengthFileException':
-                               $this->dieUsage(
+                               return [
                                        'File is of zero length, and could not be stored in the stash: ' .
-                                               $e->getMessage(),
+                                               $message,
                                        'stashzerolength'
-                               );
-                               break;
+                               ];
                        case 'UploadStashNotLoggedInException':
-                               $this->dieUsage( 'Not logged in: ' . $e->getMessage(), 'stashnotloggedin' );
-                               break;
+                               return [ 'Not logged in: ' . $message, 'stashnotloggedin' ];
                        case 'UploadStashWrongOwnerException':
-                               $this->dieUsage( 'Wrong owner: ' . $e->getMessage(), 'stashwrongowner' );
-                               break;
+                               return [ 'Wrong owner: ' . $message, 'stashwrongowner' ];
                        case 'UploadStashNoSuchKeyException':
-                               $this->dieUsage( 'No such filekey: ' . $e->getMessage(), 'stashnosuchfilekey' );
-                               break;
+                               return [ 'No such filekey: ' . $message, 'stashnosuchfilekey' ];
                        default:
-                               $this->dieUsage( $exceptionType . ': ' . $e->getMessage(), 'stasherror' );
-                               break;
+                               return [ $exceptionType . ': ' . $message, 'stasherror' ];
                }
        }
 
@@ -692,7 +786,7 @@ class ApiUpload extends ApiBase {
                        $this->mParams['text'] = $this->mParams['comment'];
                }
 
-               /** @var $file File */
+               /** @var $file LocalFile */
                $file = $this->mUpload->getLocalFile();
 
                // For preferences mode, we want to watch if 'watchdefault' is set,
@@ -754,9 +848,14 @@ class ApiUpload extends ApiBase {
                                $this->mParams['text'], $watch, $this->getUser(), $this->mParams['tags'] );
 
                        if ( !$status->isGood() ) {
-                               $error = $status->getErrorsArray();
-                               ApiResult::setIndexedTagName( $error, 'error' );
-                               $this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error );
+                               // Is there really no better way to do this?
+                               $errors = $status->getErrorsByType( 'error' );
+                               $msg = array_merge( [ $errors[0]['message'] ], $errors[0]['params'] );
+                               $data = $status->getErrorsArray();
+                               ApiResult::setIndexedTagName( $data, 'error' );
+                               // For backwards-compatibility, we use the 'internal-error' fallback key and merge $data
+                               // into the root of the response (rather than something sane like [ 'details' => $data ]).
+                               $this->dieRecoverableError( $msg, null, $data, 'internal-error' );
                        }
                        $result['result'] = 'Success';
                }
index f09fdcb..3a7a082 100644 (file)
@@ -110,14 +110,16 @@ class ApiWatch extends ApiBase {
                        $status = UnwatchAction::doUnwatch( $title, $user );
                        $res['unwatched'] = $status->isOK();
                        if ( $status->isOK() ) {
-                               $res['message'] = $this->msg( 'removedwatchtext', $title->getPrefixedText() )
+                               $msgKey = $title->isTalkPage() ? 'removedwatchtext-talk' : 'removedwatchtext';
+                               $res['message'] = $this->msg( $msgKey, $title->getPrefixedText() )
                                        ->title( $title )->parseAsBlock();
                        }
                } else {
                        $status = WatchAction::doWatch( $title, $user );
                        $res['watched'] = $status->isOK();
                        if ( $status->isOK() ) {
-                               $res['message'] = $this->msg( 'addedwatchtext', $title->getPrefixedText() )
+                               $msgKey = $title->isTalkPage() ? 'addedwatchtext-talk' : 'addedwatchtext';
+                               $res['message'] = $this->msg( $msgKey, $title->getPrefixedText() )
                                        ->title( $title )->parseAsBlock();
                        }
                }
index 139793d..8ae1192 100644 (file)
@@ -26,26 +26,89 @@ use MediaWiki\MediaWikiServices;
  * @ingroup API
  */
 trait SearchApi {
+
+       /**
+        * When $wgSearchType is null, $wgSearchAlternatives[0] is null. Null isn't
+        * a valid option for an array for PARAM_TYPE, so we'll use a fake name
+        * that can't possibly be a class name and describes what the null behavior
+        * does
+        */
+       private static $BACKEND_NULL_PARAM = 'database-backed';
+
        /**
-        * Build the profile api param definitions.
+        * The set of api parameters that are shared between api calls that
+        * call the SearchEngine. Primarily this defines parameters that
+        * are utilized by self::buildSearchEngine().
         *
-        * @param string $profileType type of profile to customize
-        * @param string $helpMsg i18n message
-        * @param string|null $backendType SearchEngine backend type or null for default engine
-        * @return array|null the api param definition or null if profiles are
-        * not supported by the searchEngine implementation.
+        * @param bool $isScrollable True if the api offers scrolling
+        * @return array
         */
-       public function buildProfileApiParam( $profileType, $helpMsg, $backendType = null ) {
-               $searchEngine = null;
-               if ( $backendType !== null ) {
-                       $searchEngine = MediaWikiServices::getInstance()
-                               ->getSearchEngineFactory()->create( $backendType );
+       public function buildCommonApiParams( $isScrollable = true ) {
+               $params = [
+                       'search' => [
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true,
+                       ],
+                       'namespace' => [
+                               ApiBase::PARAM_DFLT => NS_MAIN,
+                               ApiBase::PARAM_TYPE => 'namespace',
+                               ApiBase::PARAM_ISMULTI => true,
+                       ],
+                       'limit' => [
+                               ApiBase::PARAM_DFLT => 10,
+                               ApiBase::PARAM_TYPE => 'limit',
+                               ApiBase::PARAM_MIN => 1,
+                               ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
+                               ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2,
+                       ],
+               ];
+               if ( $isScrollable ) {
+                       $params['offset'] = [
+                               ApiBase::PARAM_DFLT => 0,
+                               ApiBase::PARAM_TYPE => 'integer',
+                               ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
+                       ];
+               }
+
+               $searchConfig = MediaWikiServices::getInstance()->getSearchEngineConfig();
+               $alternatives = $searchConfig->getSearchTypes();
+               if ( count( $alternatives ) > 1 ) {
+                       if ( $alternatives[0] === null ) {
+                               $alternatives[0] = self::$BACKEND_NULL_PARAM;
+                       }
+                       $this->allowedParams['backend'] = [
+                               ApiBase::PARAM_DFLT => $searchConfig->getSearchType(),
+                               ApiBase::PARAM_TYPE => $alternatives,
+                       ];
+                       // @todo: support profile selection when multiple
+                       // backends are available. The solution could be to
+                       // merge all possible profiles and let ApiBase
+                       // subclasses do the check. Making ApiHelp and ApiSandbox
+                       // comprehensive might be more difficult.
                } else {
-                       $searchEngine = MediaWikiServices::getInstance()->newSearchEngine();
+                       $params += $this->buildProfileApiParam();
                }
 
-               $profiles = $searchEngine->getProfiles( $profileType );
-               if ( $profiles ) {
+               return $params;
+       }
+
+       /**
+        * Build the profile api param definitions. Makes bold assumption only one search
+        * engine is available, ensure that is true before calling.
+        *
+        * @return array array containing available additional api param definitions.
+        *  Empty if profiles are not supported by the searchEngine implementation.
+        */
+       private function buildProfileApiParam() {
+               $configs = $this->getSearchProfileParams();
+               $searchEngine = MediaWikiServices::getInstance()->newSearchEngine();
+               $params = [];
+               foreach ( $configs as $paramName => $paramConfig ) {
+                       $profiles = $searchEngine->getProfiles( $paramConfig['profile-type'] );
+                       if ( !$profiles ) {
+                               continue;
+                       }
+
                        $types = [];
                        $helpMessages = [];
                        $defaultProfile = null;
@@ -58,20 +121,23 @@ trait SearchApi {
                                        $defaultProfile = $profile['name'];
                                }
                        }
-                       return [
+
+                       $params[$paramName] = [
                                ApiBase::PARAM_TYPE => $types,
-                               ApiBase::PARAM_HELP_MSG => $helpMsg,
+                               ApiBase::PARAM_HELP_MSG => $paramConfig['help-message'],
                                ApiBase::PARAM_HELP_MSG_PER_VALUE => $helpMessages,
                                ApiBase::PARAM_DFLT => $defaultProfile,
                        ];
                }
-               return null;
+
+               return $params;
        }
 
        /**
         * Build the search engine to use.
         * If $params is provided then the following searchEngine options
         * will be set:
+        *  - backend: which search backend to use
         *  - limit: mandatory
         *  - offset: optional, if set limit will be incremented by
         *    one ( to support the continue parameter )
@@ -84,6 +150,9 @@ trait SearchApi {
        public function buildSearchEngine( array $params = null ) {
                if ( $params != null ) {
                        $type = isset( $params['backend'] ) ? $params['backend'] : null;
+                       if ( $type === self::$BACKEND_NULL_PARAM ) {
+                               $type = null;
+                       }
                        $searchEngine = MediaWikiServices::getInstance()->getSearchEngineFactory()->create( $type );
                        $limit = $params['limit'];
                        $searchEngine->setNamespaces( $params['namespace'] );
@@ -97,9 +166,15 @@ trait SearchApi {
                                $limit += 1;
                        }
                        $searchEngine->setLimitOffset( $limit, $offset );
-                       foreach ( $this->getSearchProfileParams() as $type => $param ) {
-                               if ( isset( $params[$param] ) ) {
-                                       $searchEngine->setFeatureData( $type, $params[$param] );
+
+                       // Initialize requested search profiles.
+                       $configs = $this->getSearchProfileParams();
+                       foreach ( $configs as $paramName => $paramConfig ) {
+                               if ( isset( $params[$paramName] ) ) {
+                                       $searchEngine->setFeatureData(
+                                               $paramConfig['profile-type'],
+                                               $params[$paramName]
+                                       );
                                }
                        }
                } else {
@@ -109,8 +184,8 @@ trait SearchApi {
        }
 
        /**
-        * @return string[] the list of supported search profile types. Key is
-        * the profile type and its associated value is the request param.
+        * @return array[] array of arrays mapping from parameter name to a two value map
+        *  containing 'help-message' and 'profile-type' keys.
         */
        abstract public function getSearchProfileParams();
 }
diff --git a/includes/api/i18n/azb.json b/includes/api/i18n/azb.json
new file mode 100644 (file)
index 0000000..ca09cf6
--- /dev/null
@@ -0,0 +1,15 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Ilğım"
+               ]
+       },
+       "apihelp-main-param-action": "هانسی ایش گؤرولسون.",
+       "apihelp-main-param-requestid": "بۇرادا یازیلان هر میقدار جاوابا آرتیلاجاقدیر. ایستک‌لری فرقلندیرمه‌یه ایشلنه بیلر.",
+       "apihelp-block-param-nocreate": "حساب آچماغین قاباغینی آل.",
+       "apihelp-checktoken-param-token": "تِست اۆچون توکن.",
+       "apihelp-compare-param-fromtitle": "مۆقاییسه اۆچون ایلک باشلیق.",
+       "apihelp-delete-description": "بیر صفحه‌نی سیل.",
+       "apihelp-delete-param-unwatch": "صفحه‌نی ایزله‌دیکلر لیستیندن سیل.",
+       "apihelp-edit-description": "صفحه‌لری یارادیب دَییشدیر."
+}
index 7dcc4fd..2107e89 100644 (file)
        "apihelp-parse-paramvalue-prop-displaytitle": "Вики-текстың синтаксик анализына исем ҡуя.",
        "apihelp-parse-paramvalue-prop-headitems": "<код> & ЛТ -ҡа һалыу өсөн элементтар бирә; башы & GT; биттең </ код>",
        "apihelp-parse-paramvalue-prop-headhtml": "Айырылған <код> & лт бирә; & баштары GТ; биттең </ код>.",
-       "apihelp-parse-paramvalue-prop-jsconfigvars": "Бит өсөн үҙенсәлекле  JavaScript үҙгәреүсән конфигурациялар бирә.",
+       "apihelp-parse-paramvalue-prop-jsconfigvars": "Бит өсөн үҙенсәлекле JavaScript үҙгәреүсән конфигурациялар бирә. Ҡулланыр өсөн, <code>mw.config.set()</code> ҡабыҙырға.",
        "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "JavaScriptтың JSON юлы һымаҡ үҙенсәлекле биттәренә  алышына торған конфигурация бирә.",
        "apihelp-parse-paramvalue-prop-iwlinks": "Вики-текстың синтаксик анализында интервиктарға һылтанма бирә.",
        "apihelp-parse-paramvalue-prop-wikitext": "Һығымта яҺау өсөн тәүге вики-тексты күрһәтә",
        "apihelp-protect-param-tags": "Юйҙырылғандар журналындағы яҙмаларға  мөрәжәғәт итер өсөн, билдәләрҙе үҙгәртергә.",
        "apihelp-protect-param-watchlist": "Ағымдағы ҡулланыусының теҙмәһенән битте һүҙһеҙ өҫтәргә йәки юйырға, һылтанмаларҙы файҙаланығыҙ йәки сәғәтте алмаштырмаҫҡа.",
        "apihelp-protect-example-protect": "Битте һаҡларға.",
-       "apihelp-protect-example-unprotect": "<kbd>всех</kbd> өсөн сикләүҙәр ҡуйып,биттән һаҡлауҙы алырға.",
+       "apihelp-protect-example-unprotect": "<kbd>all</kbd> сикләүҙәрен (йәғни һәр береһе эшләй ала) ҡуйып, бит һағын асырға.",
        "apihelp-protect-example-unprotect2": "Бер ниндәй сикләүҙәр ҡуймай биттән һаҡлауҙы алырға.",
        "apihelp-purge-param-forcelinkupdate": "Таблицалар бәйләнешен яңыртыу.",
        "apihelp-purge-param-forcerecursivelinkupdate": "Һылтанманы һәм таблицаны яңыртығыҙ һәм был битте шаблон итеп ҡулланған башҡа биттәр өсөн һылтанмаларҙы ла яңыртығыҙ.",
index 3b5201a..c7bfb23 100644 (file)
@@ -15,7 +15,7 @@
        "apihelp-main-param-requestid": "Любое значэньне, пададзенае тут, будзе ўключанае ў адказ. Можа быць выкарыстанае для адрозьненьня запытаў.",
        "apihelp-main-param-servedby": "Уключае ў вынік назву сэрвэра, які апрацаваў запыт.",
        "apihelp-main-param-curtimestamp": "Уключае ў вынік пазнаку актуальнага часу.",
-       "apihelp-main-param-origin": "Пры звароце да API з дапамогай міждамэннага AJAX-запыту (CORS), выстаўце парамэтру значэньне зыходнага дамэну. Ён мусіць быць уключаны ў кожны папярэдні запыт і такім чынам мусіць быць часткай URI-запыту (ня цела POST). Ён мусіць супадаць з адной з крыніц у загалоўку <code>Origin</code>, павінна быць зададзена нешта кшталту <kbd>https://en.wikipedia.org</kbd> або <kbd>https://meta.wikimedia.org</kbd>. Калі парамэтар не супадае з загалоўкам <code>Origin</code>, будзе вернуты адказ з кодам памылкі 403. Калі парамэтар супадае з загалоўкам <code>Origin</code> і крыніца знаходзіцца ў белым сьпісе, будзе выстаўлены загаловак <code>Access-Control-Allow-Origin</code>.",
+       "apihelp-main-param-origin": "Пры звароце да API з дапамогай міждамэннага AJAX-запыту (CORS), выстаўце парамэтру значэньне зыходнага дамэну. Ён мусіць быць уключаны ў кожны папярэдні запыт і такім чынам мусіць быць часткай URI-запыту (ня цела POST).\n\nДля аўтэнтыфікаваных запытаў ён мусіць супадаць з адной з крыніц у загалоўку <code>Origin</code>, павінна быць зададзена нешта кшталту <kbd>https://en.wikipedia.org</kbd> або <kbd>https://meta.wikimedia.org</kbd>. Калі парамэтар не супадае з загалоўкам <code>Origin</code>, будзе вернуты адказ з кодам памылкі 403. Калі парамэтар супадае з загалоўкам <code>Origin</code> і крыніца знаходзіцца ў белым сьпісе, будуць выстаўленыя загалоўкі <code>Access-Control-Allow-Origin</code> і <code>Access-Control-Allow-Credentials</code>.\n\nДля неаўтэнтыфікаваных запытаў выстаўце значэньне <kbd>*</kbd>. Гэта прывядзе да выстаўленьня загалоўку <code>Access-Control-Allow-Origin</code>, але <code>Access-Control-Allow-Credentials</code> будзе мець значэньне <code>false</code> і ўсе зьвесткі пра карыстальніка будуць абмежаваныя.",
        "apihelp-main-param-uselang": "Мова для выкарыстаньня ў перакладах паведамленьняў. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> з <kbd>siprop=languages</kbd> вяртае сьпіс кодаў мовы, або трэба вызначыць <kbd>user</kbd>, каб ужываць налады мовы цяперашняга карыстальніка, або вызначыць <kbd>content</kbd>, каб ужываць мову зьместу гэтай вікі.",
        "apihelp-block-description": "Блякаваньне ўдзельніка.",
        "apihelp-block-param-user": "Імя ўдзельніка, IP-адрас або IP-дыяпазон, якія вы хочаце заблякаваць.",
index 7ab62b5..4678504 100644 (file)
@@ -1,7 +1,8 @@
 {
        "@metadata": {
                "authors": [
-                       "Vodnokon4e"
+                       "Vodnokon4e",
+                       "StanProg"
                ]
        },
        "apihelp-main-param-action": "Кое действие да се извърши.",
@@ -9,6 +10,7 @@
        "apihelp-block-param-user": "Потребителско име, IP адрес или диапазон от IP адреси, които искате да блокирате.",
        "apihelp-block-param-reason": "Причина за блокиране.",
        "apihelp-block-param-nocreate": "Забрана за създаване на потребителски сметки.",
+       "apihelp-block-param-hidename": "Скрива потребителското име от дневника на блокиранията. (Изисква право <code>hideuser</code>)",
        "apihelp-createaccount-description": "Създаване на нова потребителска сметка.",
        "apihelp-createaccount-param-name": "Потребителско име.",
        "apihelp-createaccount-param-email": "Адрес на електронна поща на потребителя (незадължително).",
@@ -25,7 +27,8 @@
        "apihelp-emailuser-param-text": "Съдържание на писмото.",
        "apihelp-emailuser-param-ccme": "Изпращане на копие от това писмо до мен.",
        "apihelp-expandtemplates-param-title": "Заглавие на страница.",
-       "apihelp-feedrecentchanges-param-hideminor": "Скриване на малки редакции.",
+       "apihelp-feedcontributions-param-hideminor": "Скриване на малки промени.",
+       "apihelp-feedrecentchanges-param-hideminor": "Скриване на малки промени.",
        "apihelp-feedrecentchanges-param-hidebots": "Скриване на промени, направени от ботове.",
        "apihelp-feedrecentchanges-param-hideanons": "Скриване на промени, направени от анонимни потребители.",
        "apihelp-feedrecentchanges-param-hideliu": "Скриване на промени, направени от регистрирани потребители.",
@@ -42,5 +45,8 @@
        "apihelp-move-param-movesubpages": "Преименуване на подстраници, ако е приложимо.",
        "apihelp-move-param-noredirect": "Не създавай пренасочване.",
        "apihelp-move-param-ignorewarnings": "Пренебрегване на всякакви предупреждения.",
-       "apihelp-protect-example-protect": "Защита на страница."
+       "apihelp-protect-example-protect": "Защита на страница.",
+       "apihelp-query+langlinks-paramvalue-prop-url": "Добавя пълният URL-адрес.",
+       "apihelp-query+linkshere-paramvalue-prop-title": "Заглавие на всяка страница.",
+       "apihelp-query+watchlist-paramvalue-type-log": "Записи в дневника."
 }
index 837e3f9..987231f 100644 (file)
@@ -9,7 +9,9 @@
                        "Fitoschido"
                ]
        },
+       "apihelp-main-param-action": "Quina acció realitzar.",
        "apihelp-main-param-format": "El format de la sortida.",
+       "apihelp-main-param-curtimestamp": "Inclou la marca horària actual en el resultat.",
        "apihelp-block-description": "Bloca un usuari.",
        "apihelp-block-param-reason": "Raó del blocatge.",
        "apihelp-block-param-nocreate": "Evita la creació de comptes.",
index 8437abb..7cc2db3 100644 (file)
@@ -23,7 +23,7 @@
        "apihelp-main-param-requestid": "Libovolná zde uvedená hodnota bude zahrnuta v odpovědi. Lze použít pro rozlišení požadavků.",
        "apihelp-main-param-servedby": "Zahrnout do odpovědi název hostitele, který požadavek obsloužil.",
        "apihelp-main-param-curtimestamp": "Zahrnout do odpovědi aktuální časové razítko.",
-       "apihelp-main-param-origin": "Pokud k API přistupujete pomocí mezidoménového AJAXového požadavku (CORS), nastavte tento parametr na doménu původu. Musí být součástí všech předběžných požadavků, takže musí být součástí URI požadavku (nikoli těla POSTu). Hodnota musí přesně odpovídat jednomu z původů v hlavičce <code>Origin</code>, takže musí být nastavena na něco jako <kbd>https://en.wikipedia.org</kbd> nebo <kbd>https://meta.wikimedia.org</kbd>. Pokud parametr neodpovídá hlavičce <code>Origin</code>, bude vrácena odpověď 403. Pokud parametr odpovídá hlavičce <code>Origin</code> a tento původ je na bílé listině, bude nastavena hlavička <code>Access-Control-Allow-Origin</code>.",
+       "apihelp-main-param-origin": "Pokud k API přistupujete pomocí mezidoménového AJAXového požadavku (CORS), nastavte tento parametr na doménu původu. Musí být součástí všech předběžných požadavků, takže musí být součástí URI požadavku (nikoli těla POSTu).\n\nU autentizovaných požadavků hodnota musí přesně odpovídat jednomu z původů v hlavičce <code>Origin</code>, takže musí být nastavena na něco jako <kbd>https://en.wikipedia.org</kbd> nebo <kbd>https://meta.wikimedia.org</kbd>. Pokud parametr neodpovídá hlavičce <code>Origin</code>, bude vrácena odpověď 403. Pokud parametr odpovídá hlavičce <code>Origin</code> a tento původ je na bílé listině, budou nastaveny hlavičky <code>Access-Control-Allow-Origin</code> a <code>Access-Control-Allow-Credentials</code>.\n\nU neautentizovaných požadavků uveďte hodnotu <kbd>*</kbd>. To způsobí nastavení hlavičky <code>Access-Control-Allow-Origin</code>, ale hlavička <code>Access-Control-Allow-Credentials</code> bude <code>false</code> a budou omezena všechna data specifická pro uživatele.",
        "apihelp-main-param-uselang": "Jazyk, který se má použít pro překlad hlášení. Pomocí <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> se <kbd>siprop=languages</kbd> získáte seznam jazykových kódů nebo zadejte „<kbd>user</kbd>“ pro použití předvoleného jazyka aktuálního uživatele či „<kbd>content</kbd>“ pro použití jazyka obsahu této wiki.",
        "apihelp-block-description": "Zablokovat uživatele.",
        "apihelp-block-param-user": "Uživatelské jméno, IP adresa nebo rozsah IP adres, které chcete zablokovat.",
index ff4b499..41f1ff9 100644 (file)
@@ -29,7 +29,7 @@
        "apihelp-main-param-requestid": "Der angegebene Wert wird mit in die Antwort aufgenommen und kann zur Unterscheidung von Anfragen verwendet werden.",
        "apihelp-main-param-servedby": "Namen des bearbeitenden Hosts mit zurückgeben.",
        "apihelp-main-param-curtimestamp": "Aktuellen Zeitstempel mit zurückgeben.",
-       "apihelp-main-param-origin": "Beim Zugriff auf die API mittels Cross-Domain-AJAX-Anfrage (CORS) ist dieser Parameter auf die veranlassende Domain zu setzen. Er muss in jedem Pre-Flight-Request angegeben werden und deshalb ein Teil der Anfrage-URI sein (nicht des POST-Bodys). Er muss genau einer der Angaben im <code>Origin</code>-Header entsprechen, d.&nbsp;h. er muss auf etwas wie <kbd>https://de.wikipedia.org</kbd> oder <kbd>https://meta.wikimedia.org</kbd> gesetzt werden. Falls dieser Parameter nicht mit dem <code>Origin</code>-Header übereinstimmt, wird eine 403-Antwort zurückgegeben. Falls dieser Parameter dem <code>Origin</code>-Header entspricht und die Domain auf der Whitelist ist, wird ein <code>Access-Control-Allow-Origin</code>-Header gesetzt.",
+       "apihelp-main-param-origin": "Beim Zugriff auf die API mit einer Kreuz-Domain-AJAX-Anfrage (CORS) muss dies als entstehende Domäne festgelegt werden. Dies muss in jeder Vorfluganfrage mit eingeschlossen werden und deshalb ein Teil der Anfragen-URI sein (nicht des POST-Körpers).\n\nFür authentifizierte Anfragen muss dies exakt einem der Ursprünge im Header <code>Origin</code> entsprechen, so dass es auf etwas wie <kbd>https://de.wikipedia.org</kbd> oder <kbd>https://meta.wikimedia.org</kbd> festgelegt werden muss. Falls dieser Parameter nicht mit dem Header <code>Origin</code> übereinstimmt, wird eine 403-Antwort zurückgegeben. Falls dieser Parameter mit dem Header <code>Origin</code> übereinstimmt und der Ursprung weißgelistet ist, werden die Header <code>Access-Control-Allow-Origin</code> und <code>Access-Control-Allow-Credentials</code> festgelegt.\n\nGib für nicht authentifizierte Anfragen den Wert <kbd>*</kbd> an. Dies verursacht, dass der Header <code>Access-Control-Allow-Origin</code> festgelegt wird, aber <code>Access-Control-Allow-Credentials</code> wird <code>false</code> sein und alle benutzerspezifischen Daten werden beschränkt.",
        "apihelp-main-param-uselang": "Zu verwendende Sprache für Nachrichtenübersetzungen. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> mit <kbd>siprop=languages</kbd> gibt eine Liste der Sprachcodes zurück. Gib <kbd>user</kbd> zum Verwenden der aktuellen Benutzerspracheinstellung oder <kbd>content</kbd> an, um die Inhaltssprache des Wikis zu verwenden.",
        "apihelp-block-description": "Einen Benutzer sperren.",
        "apihelp-block-param-user": "Benutzername, IP-Adresse oder IP-Bereich, der gesperrt werden soll.",
        "apihelp-options-example-change": "Ändert die Einstellungen <kbd>skin</kbd> und <kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "Setzt alle Einstellungen zurück, dann <kbd>skin</kbd> und <kbd>nickname</kbd> festlegen.",
        "apihelp-paraminfo-description": "Ruft Informationen über API-Module ab.",
-       "apihelp-paraminfo-param-modules": "Liste von Modulnamen (Werte der <var>action</var>- und <var>format</var>-Parameters, oder <kbd>main</kbd>). Kann Untermodule mit einem <kbd>+</kbd> bestimmen.",
+       "apihelp-paraminfo-param-modules": "Liste von Modulnamen (Werte der Parameter <var>action</var> und <var>format</var> oder <kbd>main</kbd>). Kann Untermodule mit einem <kbd>+</kbd> oder alle Untermodule mit <kbd>+*</kbd> oder alle Untermodule rekursiv mit <kbd>+**</kbd> bestimmen.",
        "apihelp-paraminfo-param-helpformat": "Format der Hilfe-Zeichenfolgen.",
        "apihelp-paraminfo-param-querymodules": "Liste von Abfragemodulnamen (Werte von <var>prop</var>-, <var>meta</var>- oder <var>list</var>-Parameter). Benutze <kbd>$1modules=query+foo</kbd> anstatt <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Auch Informationen über die Hauptmodule (top-level) erhalten. Benutze <kbd>$1modules=main</kbd> stattdessen.",
        "apihelp-parse-paramvalue-prop-sections": "Gibt die Abschnitte im geparsten Wikitext zurück.",
        "apihelp-parse-paramvalue-prop-revid": "Ergänzt die Versionskennung der geparsten Seite.",
        "apihelp-parse-paramvalue-prop-displaytitle": "Ergänzt den Titel des geparsten Wikitextes.",
-       "apihelp-parse-paramvalue-prop-jsconfigvars": "Gibt die JavaScript-Konfigurationsvariablen speziell für die Seite aus.",
+       "apihelp-parse-paramvalue-prop-jsconfigvars": "Gibt die JavaScript-Konfigurationsvariablen speziell für die Seite aus. Zur Anwendung verwende <code>mw.config.set()</code>.",
        "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "Gibt die JavaScript-Konfigurationsvariablen speziell für die Seite als JSON-Zeichenfolge aus.",
        "apihelp-parse-paramvalue-prop-indicators": "Gibt das HTML der Seitenstatusindikatoren zurück, die auf der Seite verwendet werden.",
        "apihelp-parse-paramvalue-prop-iwlinks": "Gibt Interwiki-Links des geparsten Wikitextes zurück.",
        "apihelp-protect-description": "Ändert den Schutzstatus einer Seite.",
        "apihelp-protect-param-title": "Titel der Seite, die du (ent-)sperren möchtest. Kann nicht zusammen mit $1pageid verwendet werden.",
        "apihelp-protect-param-pageid": "Seitenkennung der Seite, die du (ent-)sperren möchtest. Kann nicht zusammen mit $1title verwendet werden.",
-       "apihelp-protect-param-protections": "Liste der Schutzebenen nach dem Format <kbd>Aktion=Ebene</kbd> (z.B. <kbd>edit=sysop</kbd>).\n\n<strong>HINWEIS:</strong> Wenn eine Aktion nicht angegeben wird, wird deren Schutz entfernt.",
+       "apihelp-protect-param-protections": "Listet die Schutzebenen nach dem Format <kbd>Aktion=Ebene</kbd> (z.&nbsp;B. <kbd>edit=sysop</kbd>) auf. Die Ebene <kbd>all</kbd> bedeutet, dass jeder die Aktion ausführen darf, z.&nbsp;B. keine Beschränkung.\n\n<strong>HINWEIS:</strong> Wenn eine Aktion nicht angegeben wird, wird deren Schutz entfernt.",
        "apihelp-protect-param-expiry": "Zeitstempel des Schutzablaufs. Wenn nur ein Zeitstempel übergeben wird, ist dieser für alle Seitenschutze gültig. Um eine unendliche Schutzdauer festzulegen, kannst du die Werte <kbd>infinite</kbd>, <kbd>indefinite</kbd>, <kbd>infinity</kbd> oder <kbd>never</kbd> übergeben.",
        "apihelp-protect-param-reason": "Grund für den Seitenschutz oder dessen Aufhebung.",
        "apihelp-protect-param-tags": "Auf den Seitenschutz-Logbuch-Eintrag anzuwendende Änderungsmarkierungen.",
        "apihelp-protect-param-watch": "Wenn vorhanden, fügt dieser Parameter die zu (ent-)sperrende Seite der Beobachtungsliste des aktuellen Benutzers hinzu.",
        "apihelp-protect-param-watchlist": "Die Seite bedingungslos zur Beobachtungsliste des aktuellen Benutzers hinzufügen oder von ihr entfernen, Einstellungen verwenden oder Beobachtung nicht ändern.",
        "apihelp-protect-example-protect": "Schützt eine Seite",
-       "apihelp-protect-example-unprotect": "Eine Seite entsperren, indem die Einschränkungen durch den Schutz auf <kbd>all</kbd> gestellt werden.",
+       "apihelp-protect-example-unprotect": "Entsperrt eine Seite, indem die Einschränkungen durch den Schutz auf <kbd>all</kbd> gestellt werden (z.&nbsp;B. darf jeder die Aktion ausführen).",
        "apihelp-protect-example-unprotect2": "Eine Seite entsperren, indem keine Einschränkungen übergeben werden",
        "apihelp-purge-description": "Setzt den Cache der angegebenen Seiten zurück.\n\nFalls kein Benutzer angemeldet ist, müssen POST-Anfragen genutzt werden.",
        "apihelp-purge-param-forcelinkupdate": "Aktualisiert die Linktabellen.",
        "apihelp-query+allusers-param-witheditsonly": "Listet nur Benutzer auf, die Bearbeitungen vorgenommen haben.",
        "apihelp-query+allusers-param-activeusers": "Listet nur Benutzer auf, die in den letzten $1 {{PLURAL:$1|Tag|Tagen}} aktiv waren.",
        "apihelp-query+allusers-example-Y": "Benutzer ab <kbd>Y</kbd> auflisten.",
+       "apihelp-query+authmanagerinfo-example-login": "Ruft die Anfragen ab, die beim Beginnen einer Anmeldung verwendet werden können.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "Ruft die Anfragen ab, die beim Beginnen einer Anmeldung verwendet werden können, mit zusammengeführten Formularfeldern.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "Testet, ob die Authentifizierung für die Aktion <kbd>foo</kbd> ausreichend ist.",
        "apihelp-query+backlinks-description": "Alle Seiten finden, die auf die angegebene Seite verlinken.",
        "apihelp-query+backlinks-param-title": "Zu suchender Titel. Darf nicht zusammen mit <var>$1pageid</var> benutzt werden.",
        "apihelp-query+backlinks-param-pageid": "Zu suchende Seiten-ID. Darf nicht zusammen mit <var>$1title</var> benutzt werden.",
        "apihelp-query+exturlusage-param-query": "Suchbegriff ohne Protokoll. Siehe [[Special:LinkSearch]]. Leer lassen, um alle externen Verknüpfungen aufzulisten.",
        "apihelp-query+exturlusage-param-namespace": "Die aufzulistenden Seiten-Namensräume.",
        "apihelp-query+exturlusage-param-limit": "Wie viele Seiten zurückgegeben werden sollen.",
+       "apihelp-query+exturlusage-example-simple": "Zeigt Seiten, die auf <kbd>http://www.mediawiki.org</kbd> verlinken.",
        "apihelp-query+filearchive-description": "Alle gelöschten Dateien der Reihe nach auflisten.",
        "apihelp-query+filearchive-param-from": "Der Bildertitel, bei dem die Auflistung beginnen soll.",
        "apihelp-query+filearchive-param-to": "Der Bildertitel, bei dem die Auflistung enden soll.",
        "apihelp-query+watchlist-paramvalue-prop-userid": "Ergänzt die Kennung des Benutzers, der die Bearbeitung ausgeführt hat.",
        "apihelp-query+watchlist-paramvalue-prop-comment": "Ergänzt den Kommentar der Bearbeitung.",
        "apihelp-query+watchlist-paramvalue-prop-timestamp": "Ergänzt den Zeitstempel der Bearbeitung.",
+       "apihelp-query+watchlist-paramvalue-prop-patrol": "Markiert Bearbeitungen, die kontrolliert sind.",
        "apihelp-query+watchlist-paramvalue-prop-sizes": "Ergänzt die alten und neuen Längen der Seite.",
        "apihelp-query+watchlist-paramvalue-type-new": "Seitenerstellungen.",
        "apihelp-query+watchlist-paramvalue-type-log": "Logbucheinträge.",
+       "apihelp-query+watchlistraw-description": "Ruft alle Seiten der Beobachtungsliste des aktuellen Benutzers ab.",
        "apihelp-query+watchlistraw-param-prop": "Zusätzlich zurückzugebende Eigenschaften:",
+       "apihelp-query+watchlistraw-param-fromtitle": "Titel (mit Namensraum-Präfix), bei dem die Aufzählung beginnen soll.",
+       "apihelp-query+watchlistraw-param-totitle": "Titel (mit Namensraum-Präfix), bei dem die Aufzählung enden soll.",
+       "apihelp-resetpassword-param-user": "Benutzer, der zurückgesetzt werden soll.",
+       "apihelp-revisiondelete-description": "Löscht und stellt Versionen wieder her.",
+       "apihelp-revisiondelete-param-hide": "Was für jede Version versteckt werden soll.",
+       "apihelp-revisiondelete-param-show": "Was für jede Version wieder eingeblendet werden soll.",
        "apihelp-rsd-description": "Ein RSD-Schema (Really Simple Discovery) exportieren.",
        "apihelp-rsd-example-simple": "Das RSD-Schema exportieren",
        "apihelp-setnotificationtimestamp-param-entirewatchlist": "An allen beobachteten Seiten arbeiten.",
+       "apihelp-stashedit-param-sectiontitle": "Der Titel für einen neuen Abschnitt.",
        "apihelp-stashedit-param-text": "Seiteninhalt.",
+       "apihelp-stashedit-param-stashedtexthash": "Stattdessen zu verwendende Prüfsumme des Seiteninhalts von einem vorherigen Speicher.",
+       "apihelp-stashedit-param-contentmodel": "Inhaltsmodell des neuen Inhalts.",
+       "apihelp-stashedit-param-summary": "Änderungszusammenfassung.",
        "apihelp-tag-param-reason": "Grund für die Änderung.",
+       "apihelp-tokens-param-type": "Abzufragende Tokentypen.",
+       "apihelp-tokens-example-edit": "Ruft einen Bearbeitungstoken ab (Standard).",
+       "apihelp-tokens-example-emailmove": "Ruft einen E-Mail- und Verschiebungstoken ab.",
        "apihelp-unblock-description": "Einen Benutzer freigeben.",
        "apihelp-unblock-param-id": "ID der Sperre zum Entsperren (über <kbd>list=blocks</kbd> erhalten). Darf nicht zusammen mit <var>$1user</var> verwendet werden.",
+       "apihelp-unblock-param-user": "Freizugebender Benutzername, IP-Adressbereich oder freizugebende IP-Adresse. Kann nicht zusammen mit <var>$1id</var> verwendet werden.",
        "apihelp-unblock-param-reason": "Grund für die Freigabe.",
        "apihelp-unblock-param-tags": "Auf den Benutzersperr-Logbuch-Eintrag anzuwendende Änderungsmarkierungen.",
        "apihelp-unblock-example-id": "Sperrkennung #<kbd>105</kbd> freigeben.",
        "api-help-param-type-boolean": "Typ: boolisch ([[Special:ApiHelp/main#main/datatypes|Einzelheiten]])",
        "api-help-param-type-timestamp": "Typ: {{PLURAL:$1|1=Zeitstempel|2=Liste von Zeitstempeln}} ([[Special:ApiHelp/main#main/datatypes|erlaubte Formate]])",
        "api-help-param-type-user": "Typ: {{PLURAL:$1|1=Benutzername|2=Liste von Benutzernamen}}",
-       "api-help-param-list": "{{PLURAL:$1|1=Einer der folgenden Werte|2=Werte (mit <kbd>{{!}}</kbd> trennen)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Einer der folgenden Werte|2=Werte (mit <kbd>{{!}}</kbd> trennen oder [[Special:ApiHelp/main#main/datatypes|Alternative]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Muss leer sein|Kann leer sein oder $2}}",
        "api-help-param-limit": "Nicht mehr als $1 erlaubt.",
        "api-help-param-limit2": "Nicht mehr als $1 ($2 für Bots) erlaubt.",
        "api-help-param-integer-max": "{{PLURAL:$1|1=Der Wert darf|2=Die Werte dürfen}} nicht größer sein als $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=Der Wert muss|2=Die Werte müssen}} zwischen $2 und $3 sein.",
        "api-help-param-upload": "Muss als Dateiupload mithilfe eines multipart/form-data-Formular bereitgestellt werden.",
-       "api-help-param-multi-separate": "Werte mit <kbd>|</kbd> trennen.",
+       "api-help-param-multi-separate": "Werte mit <kbd>|</kbd> trennen oder [[Special:ApiHelp/main#main/datatypes|Alternative]].",
        "api-help-param-multi-max": "Maximale Anzahl der Werte ist {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} für Bots).",
        "api-help-param-default": "Standard: $1",
        "api-help-param-default-empty": "Standard: <span class=\"apihelp-empty\">(leer)</span>",
        "api-help-permissions-granted-to": "{{PLURAL:$1|Gewährt an}}: $2",
        "api-help-right-apihighlimits": "Höhere Beschränkungen in API-Anfragen verwenden (langsame Anfragen: $1; schnelle Anfragen: $2). Die Beschränkungen für langsame Anfragen werden auch auf Mehrwertparameter angewandt.",
        "api-help-open-in-apisandbox": "<small>[in Spielwiese öffnen]</small>",
+       "api-help-authmanagerhelper-messageformat": "Zu verwendendes Format zur Rückgabe von Nachrichten.",
        "api-credits-header": "Danksagungen",
        "api-credits": "API-Entwickler:\n* Roan Kattouw (Hauptentwickler von September 2007 bis 2009)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Yuri Astrakhan (Autor, Hauptentwickler von September 2006 bis September 2007)\n* Brad Jorsch (Hauptentwickler seit 2013)\n\nBitte sende deine Kommentare, Vorschläge und Fragen an mediawiki-api@lists.wikimedia.org\noder reiche einen Fehlerbericht auf https://phabricator.wikimedia.org/ ein."
 }
index 7bd2670..0b86f10 100644 (file)
@@ -6,12 +6,17 @@
                        "Kumkumuk"
                ]
        },
+       "apihelp-main-param-action": "Performansa kamci aksiyon",
        "apihelp-block-description": "Enê karberi bloqe ke",
        "apihelp-block-param-reason": "Sebeba Bloqey",
        "apihelp-block-param-nocreate": "Hesab viraştişi bloqe ke.",
        "apihelp-checktoken-param-token": "Jetona test ke",
+       "apihelp-createaccount-description": "Yew Hesabê karberi yo newe vıraze",
        "apihelp-createaccount-param-name": "Nameyê karberi.",
+       "apihelp-createaccount-param-email": "E-postay karberi (keyfi)",
+       "apihelp-createaccount-param-realname": "Namey karberi yo raştay (keyfi)",
        "apihelp-delete-description": "Pele bestere.",
+       "apihelp-delete-example-simple": "<kbd>Main Page</kbd> besternê.",
        "apihelp-disabled-description": "Eno modul aktiv niyo.",
        "apihelp-edit-description": "Vıraze û pelan bıvurne.",
        "apihelp-edit-param-text": "Zerreki pele",
        "apihelp-emailuser-param-ccme": "Yew kopyaya nê posteyi mı rê bırışe.",
        "apihelp-expandtemplates-param-title": "Sernameyê pele.",
        "apihelp-expandtemplates-param-text": "Wikimetıni açarnê.",
+       "apihelp-expandtemplates-paramvalue-prop-wikitext": "Herabıyaye wikimetin",
+       "apihelp-feedcontributions-param-feedformat": "Formata warikerdışi",
+       "apihelp-feedcontributions-param-hideminor": "Vuryayışanê werdiyan bınımne",
+       "apihelp-feedcontributions-param-showsizediff": "Goreyê ebati ferqê versiyoni bıasne.",
        "apihelp-feedrecentchanges-param-hideminor": "Vurnayışanê qıckekan bınımne.",
        "apihelp-feedrecentchanges-param-hidebots": "Vurnayışanê botan bınımne.",
        "apihelp-feedrecentchanges-param-hideanons": "Vurnayışanê karberanê anoniman bınımne.",
        "apihelp-feedrecentchanges-param-tagfilter": "Filtrey etiketi",
        "apihelp-feedrecentchanges-example-simple": "Vurnayışê peyênan bıvin",
        "apihelp-feedrecentchanges-example-30days": "Peyni vurnayışanê 30 raco bımosne",
+       "apihelp-filerevert-param-comment": "Mışewre bar ke",
        "apihelp-login-param-name": "Nameyê karberi.",
        "apihelp-login-param-password": "Parola.",
        "apihelp-login-param-domain": "Domain (optional).",
        "apihelp-login-example-login": "Dekew.",
        "apihelp-mergehistory-description": "Verorê pela yew ke",
        "apihelp-move-description": "Yew pele bere.",
+       "apihelp-move-param-noredirect": "Hetenayış mevıraz",
+       "apihelp-options-example-reset": "Terciha pêron reset ke",
+       "apihelp-options-example-change": "Tercihanê <kbd>skin</kbd> u <kbd>hideminor</kbd> bıvurnê",
        "apihelp-parse-example-page": "Peler analiz ke",
        "apihelp-parse-example-text": "Wikimetini analiz ke",
        "apihelp-parse-example-summary": "Xulasay analiz ke",
index 24b66ab..40388f9 100644 (file)
@@ -16,7 +16,7 @@
        "apihelp-main-param-requestid": "Any value given here will be included in the response. May be used to distinguish requests.",
        "apihelp-main-param-servedby": "Include the hostname that served the request in the results.",
        "apihelp-main-param-curtimestamp": "Include the current timestamp in the result.",
-       "apihelp-main-param-origin": "When accessing the API using a cross-domain AJAX request (CORS), set this to the originating domain. This must be included in any pre-flight request, and therefore must be part of the request URI (not the POST body). This must match one of the origins in the <code>Origin</code> header exactly, so it has to be set to something like <kbd>https://en.wikipedia.org</kbd> or <kbd>https://meta.wikimedia.org</kbd>. If this parameter does not match the <code>Origin</code> header, a 403 response will be returned. If this parameter matches the <code>Origin</code> header and the origin is whitelisted, an <code>Access-Control-Allow-Origin</code> header will be set.",
+       "apihelp-main-param-origin": "When accessing the API using a cross-domain AJAX request (CORS), set this to the originating domain. This must be included in any pre-flight request, and therefore must be part of the request URI (not the POST body).\n\nFor authenticated requests, this must match one of the origins in the <code>Origin</code> header exactly, so it has to be set to something like <kbd>https://en.wikipedia.org</kbd> or <kbd>https://meta.wikimedia.org</kbd>. If this parameter does not match the <code>Origin</code> header, a 403 response will be returned. If this parameter matches the <code>Origin</code> header and the origin is whitelisted, the <code>Access-Control-Allow-Origin</code> and <code>Access-Control-Allow-Credentials</code> headers will be set.\n\nFor non-authenticated requests, specify the value <kbd>*</kbd>. This will cause the <code>Access-Control-Allow-Origin</code> header to be set, but <code>Access-Control-Allow-Credentials</code> will be <code>false</code> and all user-specific data will be restricted.",
        "apihelp-main-param-uselang": "Language to use for message translations. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> with <kbd>siprop=languages</kbd> returns a list of language codes, or specify <kbd>user</kbd> to use the current user's language preference, or specify <kbd>content</kbd> to use this wiki's content language.",
 
        "apihelp-block-description": "Block a user.",
 
        "apihelp-login-description": "Log in and get authentication cookies.\n\nThis action should only be used in combination with [[Special:BotPasswords]]; use for main-account login is deprecated and may fail without warning. To safely log in to the main account, use <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
        "apihelp-login-description-nobotpasswords": "Log in and get authentication cookies.\n\nThis action is deprecated and may fail without warning. To safely log in, use <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
-       "apihelp-login-description-nonauthmanager": "Log in and get authentication cookies.\n\nIn the event of a successful log-in, the needed cookies will be included in the HTTP response headers. In the event of a failed log-in, further attempts may be throttled to limit automated password guessing attacks.",
        "apihelp-login-param-name": "User name.",
        "apihelp-login-param-password": "Password.",
        "apihelp-login-param-domain": "Domain (optional).",
        "apihelp-options-example-complex": "Reset all preferences, then set <kbd>skin</kbd> and <kbd>nickname</kbd>.",
 
        "apihelp-paraminfo-description": "Obtain information about API modules.",
-       "apihelp-paraminfo-param-modules": "List of module names (values of the <var>action</var> and <var>format</var> parameters, or <kbd>main</kbd>). Can specify submodules with a <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "List of module names (values of the <var>action</var> and <var>format</var> parameters, or <kbd>main</kbd>). Can specify submodules with a <kbd>+</kbd>, or all submodules with <kbd>+*</kbd>, or all submodules recursively with <kbd>+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "Format of help strings.",
        "apihelp-paraminfo-param-querymodules": "List of query module names (value of <var>prop</var>, <var>meta</var> or <var>list</var> parameter). Use <kbd>$1modules=query+foo</kbd> instead of <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Get information about the main (top-level) module as well. Use <kbd>$1modules=main</kbd> instead.",
        "apihelp-paraminfo-param-pagesetmodule": "Get information about the pageset module (providing titles= and friends) as well.",
        "apihelp-paraminfo-param-formatmodules": "List of format module names (value of <var>format</var> parameter). Use <var>$1modules</var> instead.",
        "apihelp-paraminfo-example-1": "Show info for <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd>, and <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "Show info for all submodules of <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
 
        "apihelp-parse-description": "Parses content and returns parser output.\n\nSee the various prop-modules of <kbd>[[Special:ApiHelp/query|action=query]]</kbd> to get information from the current version of a page.\n\nThere are several ways to specify the text to parse:\n# Specify a page or revision, using <var>$1page</var>, <var>$1pageid</var>, or <var>$1oldid</var>.\n# Specify content explicitly, using <var>$1text</var>, <var>$1title</var>, and <var>$1contentmodel</var>.\n# Specify only a summary to parse. <var>$1prop</var> should be given an empty value.",
        "apihelp-parse-param-title": "Title of page the text belongs to. If omitted, <var>$1contentmodel</var> must be specified, and [[API]] will be used as the title.",
        "apihelp-parse-paramvalue-prop-sections": "Gives the sections in the parsed wikitext.",
        "apihelp-parse-paramvalue-prop-revid": "Adds the revision ID of the parsed page.",
        "apihelp-parse-paramvalue-prop-displaytitle": "Adds the title of the parsed wikitext.",
-       "apihelp-parse-paramvalue-prop-headitems": "Gives items to put in the <code>&lt;head&gt;</code> of the page.",
+       "apihelp-parse-paramvalue-prop-headitems": "<span class=\"apihelp-deprecated\">Deprecated.</span> Gives items to put in the <code>&lt;head&gt;</code> of the page.",
        "apihelp-parse-paramvalue-prop-headhtml": "Gives parsed <code>&lt;head&gt;</code> of the page.",
-       "apihelp-parse-paramvalue-prop-modules": "Gives the ResourceLoader modules used on the page. Either <kbd>jsconfigvars</kbd> or <kbd>encodedjsconfigvars</kbd> must be requested jointly with <kbd>modules</kbd>.",
-       "apihelp-parse-paramvalue-prop-jsconfigvars": "Gives the JavaScript configuration variables specific to the page.",
+       "apihelp-parse-paramvalue-prop-modules": "Gives the ResourceLoader modules used on the page. To load, use <code>mw.loader.using()</code>. Either <kbd>jsconfigvars</kbd> or <kbd>encodedjsconfigvars</kbd> must be requested jointly with <kbd>modules</kbd>.",
+       "apihelp-parse-paramvalue-prop-jsconfigvars": "Gives the JavaScript configuration variables specific to the page. To apply, use <code>mw.config.set()</code>.",
        "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "Gives the JavaScript configuration variables specific to the page as a JSON string.",
        "apihelp-parse-paramvalue-prop-indicators": "Gives the HTML of page status indicators used on the page.",
        "apihelp-parse-paramvalue-prop-iwlinks": "Gives interwiki links in the parsed wikitext.",
        "apihelp-protect-description": "Change the protection level of a page.",
        "apihelp-protect-param-title": "Title of the page to (un)protect. Cannot be used together with $1pageid.",
        "apihelp-protect-param-pageid": "ID of the page to (un)protect. Cannot be used together with $1title.",
-       "apihelp-protect-param-protections": "List of protection levels, formatted <kbd>action=level</kbd> (e.g. <kbd>edit=sysop</kbd>).\n\n<strong>Note:</strong> Any actions not listed will have restrictions removed.",
+       "apihelp-protect-param-protections": "List of protection levels, formatted <kbd>action=level</kbd> (e.g. <kbd>edit=sysop</kbd>). A level of <kbd>all</kbd> means everyone is allowed to take the action, i.e. no restriction.\n\n<strong>Note:</strong> Any actions not listed will have restrictions removed.",
        "apihelp-protect-param-expiry": "Expiry timestamps. If only one timestamp is set, it'll be used for all protections. Use <kbd>infinite</kbd>, <kbd>indefinite</kbd>, <kbd>infinity</kbd>, or <kbd>never</kbd>, for a never-expiring protection.",
        "apihelp-protect-param-reason": "Reason for (un)protecting.",
        "apihelp-protect-param-tags": "Change tags to apply to the entry in the protection log.",
        "apihelp-protect-param-watch": "If set, add the page being (un)protected to the current user's watchlist.",
        "apihelp-protect-param-watchlist": "Unconditionally add or remove the page from the current user's watchlist, use preferences or do not change watch.",
        "apihelp-protect-example-protect": "Protect a page.",
-       "apihelp-protect-example-unprotect": "Unprotect a page by setting restrictions to <kbd>all</kbd>.",
+       "apihelp-protect-example-unprotect": "Unprotect a page by setting restrictions to <kbd>all</kbd> (i.e. everyone is allowed to take the action).",
        "apihelp-protect-example-unprotect2": "Unprotect a page by setting no restrictions.",
 
        "apihelp-purge-description": "Purge the cache for the given titles.\n\nRequires a POST request if the user is not logged in.",
        "apihelp-query+authmanagerinfo-description": "Retrieve information about the current authentication status.",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "Test whether the user's current authentication status is sufficient for the specified security-sensitive operation.",
        "apihelp-query+authmanagerinfo-param-requestsfor": "Fetch information about the authentication requests needed for the specified authentication action.",
-       "apihelp-query+filerepoinfo-example-login": "Fetch the requests that may be used when beginning a login.",
-       "apihelp-query+filerepoinfo-example-login-merged": "Fetch the requests that may be used when beginning a login, with form fields merged.",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "Test whether authentication is sufficient for action <kbd>foo</kbd>.",
+       "apihelp-query+authmanagerinfo-example-login": "Fetch the requests that may be used when beginning a login.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "Fetch the requests that may be used when beginning a login, with form fields merged.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "Test whether authentication is sufficient for action <kbd>foo</kbd>.",
 
        "apihelp-query+backlinks-description": "Find all pages that link to the given page.",
        "apihelp-query+backlinks-param-title": "Title to search. Cannot be used together with <var>$1pageid</var>.",
        "apihelp-stashedit-param-section": "Section number. <kbd>0</kbd> for the top section, <kbd>new</kbd> for a new section.",
        "apihelp-stashedit-param-sectiontitle": "The title for a new section.",
        "apihelp-stashedit-param-text": "Page content.",
+       "apihelp-stashedit-param-stashedtexthash": "Page content hash from a prior stash to use instead.",
        "apihelp-stashedit-param-contentmodel": "Content model of the new content.",
        "apihelp-stashedit-param-contentformat": "Content serialization format used for the input text.",
        "apihelp-stashedit-param-baserevid": "Revision ID of the base revision.",
        "api-help-param-deprecated": "Deprecated.",
        "api-help-param-required": "This parameter is required.",
        "api-help-datatypes-header": "Data types",
-       "api-help-datatypes": "Some parameter types in API requests need further explanation:\n;boolean\n:Boolean parameters work like HTML checkboxes: if the parameter is specified, regardless of value, it is considered true. For a false value, omit the parameter entirely.\n;timestamp\n:Timestamps may be specified in several formats. ISO 8601 date and time is recommended. All times are in UTC, any included timezone is ignored.\n:* ISO 8601 date and time, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (punctuation and <kbd>Z</kbd> are optional)\n:* ISO 8601 date and time with (ignored) fractional seconds, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (dashes, colons, and <kbd>Z</kbd> are optional)\n:* MediaWiki format, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Generic numeric format, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (optional timezone of <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, or <kbd>-<var>##</var></kbd> is ignored)\n:* EXIF format, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*RFC 2822 format (timezone may be omitted), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 850 format (timezone may be omitted), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* C ctime format, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Seconds since 1970-01-01T00:00:00Z as a 1 to 13 digit integer (excluding <kbd>0</kbd>)\n:* The string <kbd>now</kbd>",
+       "api-help-datatypes": "Input to MediaWiki should be NFC-normalized UTF-8. MediaWiki may attempt to convert other input, but this may cause some operations (such as [[Special:ApiHelp/edit|edits]] with MD5 checks) to fail.\n\nSome parameter types in API requests need further explanation:\n;boolean\n:Boolean parameters work like HTML checkboxes: if the parameter is specified, regardless of value, it is considered true. For a false value, omit the parameter entirely.\n;timestamp\n:Timestamps may be specified in several formats. ISO 8601 date and time is recommended. All times are in UTC, any included timezone is ignored.\n:* ISO 8601 date and time, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (punctuation and <kbd>Z</kbd> are optional)\n:* ISO 8601 date and time with (ignored) fractional seconds, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (dashes, colons, and <kbd>Z</kbd> are optional)\n:* MediaWiki format, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Generic numeric format, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (optional timezone of <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, or <kbd>-<var>##</var></kbd> is ignored)\n:* EXIF format, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*RFC 2822 format (timezone may be omitted), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 850 format (timezone may be omitted), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* C ctime format, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Seconds since 1970-01-01T00:00:00Z as a 1 to 13 digit integer (excluding <kbd>0</kbd>)\n:* The string <kbd>now</kbd>\n;alternative multiple-value separator\n:Parameters that take multiple values are normally submitted with the values separated using the pipe character, e.g. <kbd>param=value1|value2</kbd> or <kbd>param=value1%7Cvalue2</kbd>. If a value must contain the pipe character, use U+001F (Unit Separator) as the separator ''and'' prefix the value with U+001F, e.g. <kbd>param=%1Fvalue1%1Fvalue2</kbd>.",
        "api-help-param-type-limit": "Type: integer or <kbd>max</kbd>",
        "api-help-param-type-integer": "Type: {{PLURAL:$1|1=integer|2=list of integers}}",
        "api-help-param-type-boolean": "Type: boolean ([[Special:ApiHelp/main#main/datatypes|details]])",
        "api-help-param-type-password": "",
        "api-help-param-type-timestamp": "Type: {{PLURAL:$1|1=timestamp|2=list of timestamps}} ([[Special:ApiHelp/main#main/datatypes|allowed formats]])",
        "api-help-param-type-user": "Type: {{PLURAL:$1|1=user name|2=list of user names}}",
-       "api-help-param-list": "{{PLURAL:$1|1=One of the following values|2=Values (separate with <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=One of the following values|2=Values (separate with <kbd>{{!}}</kbd> or [[Special:ApiHelp/main#main/datatypes|alternative]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Must be empty|Can be empty, or $2}}",
        "api-help-param-limit": "No more than $1 allowed.",
        "api-help-param-limit2": "No more than $1 ($2 for bots) allowed.",
        "api-help-param-integer-max": "The {{PLURAL:$1|1=value|2=values}} must be no greater than $3.",
        "api-help-param-integer-minmax": "The {{PLURAL:$1|1=value|2=values}} must be between $2 and $3.",
        "api-help-param-upload": "Must be posted as a file upload using multipart/form-data.",
-       "api-help-param-multi-separate": "Separate values with <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Separate values with <kbd>|</kbd> or [[Special:ApiHelp/main#main/datatypes|alternative]].",
        "api-help-param-multi-max": "Maximum number of values is {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} for bots).",
        "api-help-param-default": "Default: $1",
        "api-help-param-default-empty": "Default: <span class=\"apihelp-empty\">(empty)</span>",
index ff97cc8..e358bcb 100644 (file)
@@ -6,7 +6,7 @@
                ]
        },
        "apihelp-main-param-format": "La formo de la eligaĵo.",
-       "apihelp-block-description": "Forbari uzulon.",
+       "apihelp-block-description": "Bloki uzanton.",
        "apihelp-block-param-user": "Salutnomo, IP-adreso aŭ IP-adresa intervalo forbarota.",
        "apihelp-block-param-expiry": "Eksvalidiĝa tempo. Ĝi povas esti relativa (ekz. <kbd>5 months</kbd> aŭ <kbd>2 weeks</kbd> aŭ absoluta (ekz. <kbd>2014-09-18T12:34:56Z</kbd>). Se vi indikas <kbd>infinite</kbd> (senfine), <kbd>indefinite</kbd> (nedifinite) aŭ <kbd>never</kbd> (neniam), la forbaro neniam eksvalidiĝos.",
        "apihelp-createaccount-param-name": "Uzantnomo.",
        "apihelp-feedrecentchanges-param-hideanons": "Kaŝi redaktojn de anonimuloj.",
        "apihelp-feedrecentchanges-param-hideliu": "Kaŝi redaktojn de ensalutintaj uzantoj.",
        "apihelp-feedrecentchanges-param-hidepatrolled": "Kaŝi reviziitajn ŝanĝojn.",
-       "apihelp-feedrecentchanges-param-hidemyself": "Kaŝi redaktojn de la nun ensalutinta uzulo (= vi).",
+       "apihelp-feedrecentchanges-param-hidemyself": "Kaŝi ŝanĝojn faritajn de la nuna uzanto.",
        "apihelp-feedrecentchanges-param-hidecategorization": "Kaŝi ŝanĝojn de kategoria aneco.",
        "apihelp-feedrecentchanges-example-simple": "Montri ĵusajn ŝanĝojn.",
        "apihelp-filerevert-description": "Restarigi malnovan version de dosiero.",
        "apihelp-filerevert-param-comment": "Alŝuta komento.",
        "apihelp-login-param-name": "Uzantnomo.",
        "apihelp-login-param-password": "Pasvorto.",
-       "apihelp-login-example-login": "Ensaluti."
+       "apihelp-login-example-login": "Ensaluti.",
+       "apihelp-userrights-param-user": "Uzantnomo."
 }
index aba1d5d..7baf811 100644 (file)
@@ -21,7 +21,8 @@
                        "Mgpena",
                        "Rubentl134",
                        "2axterix2",
-                       "Dgstranz"
+                       "Dgstranz",
+                       "Copper12"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentación]]\n* [[mw:API:FAQ|Preguntas frecuentes]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista de correos]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API de anuncios]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Errores y peticiones]\n</div>\n<strong>Estado:</strong> Todas las características que se muestran en esta página debería funcionar, pero la API aún está en desarrollo activo y puede cambiar en cualquier momento. Suscríbete a [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la lista de correo de mediawiki-api-announce] para estar al día de las actualizaciones.\n\n<strong>Solicitudes erróneas:</strong> Cuando se envían solicitudes erróneas a la API, se envía un encabezado HTTP con la clave \"MediaWiki-API-Error\" y ambos valores, del encabezado y el código de error, se establecerán en el mismo valor. Para más información, véase [[mw:API:Errors_and_warnings|API: Errores y advertencias]].\n\n<strong>Pruebas:</strong> para facilitar las pruebas de solicitudes a la API, consulta [[Special:ApiSandbox]].",
@@ -34,7 +35,7 @@
        "apihelp-main-param-requestid": "Cualquier valor dado aquí se incluirá en la respuesta. Se puede utilizar para distinguir solicitudes.",
        "apihelp-main-param-servedby": "Incluir el nombre del host que ha servido la solicitud en los resultados.",
        "apihelp-main-param-curtimestamp": "Incluir la marca de tiempo actual en el resultado.",
-       "apihelp-main-param-origin": "Cuando se accede a la API usando una petición AJAX de distinto dominio (CORS), establece este valor al dominio de origen. Debe ser incluido en cualquier petición pre-vuelo, y por lo tanto debe ser parte de la URI de la petición (no del cuerpo POST). Debe coincidir exactamente con uno de los orígenes de la cabecera <code>Origin</code>, por lo que debería ser algo como <kbd>https://en.wikipedia.org</kbd> o <kbd>https://meta.wikimedia.org</kbd>. Si este parámetro no coincide con la cabecera <code>Origin</code>, se devolverá una respuesta 403.\nSi este parámetro coincide con la cabecera <code>Origin</code> y el origen está en lista blanca, se creará una cabecera <code>Access-Control-Allow-Origin</code>.",
+       "apihelp-main-param-origin": "Cuando se accede a la API usando una petición AJAX de distinto dominio (CORS), se establece este valor al dominio de origen. Debe ser incluido en cualquier petición pre-vuelo, y por lo tanto debe ser parte de la URI de la petición (no del cuerpo POST). Debe coincidir exactamente con uno de los orígenes de la cabecera <code>Origin</code>, por lo que debería ser algo como <kbd>https://en.wikipedia.org</kbd> o <kbd>https://meta.wikimedia.org</kbd>. Si este parámetro no coincide con la cabecera <code>Origin</code>, se devolverá una respuesta 403.\nSi este parámetro coincide con la cabecera <code>Origin</code> y el origen está en lista blanca, se creará una cabecera <code>Access-Control-Allow-Origin</code>.",
        "apihelp-main-param-uselang": "El idioma que se usará para las traducciones de mensajes. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> con <kbd>siprop=languages</kbd> devuelve una lista de códigos de idiomas, o especifica <kbd>user</kbd> para usar la preferencia de idioma del usuario actual, o especifica <kbd>content</kbd> para usar el idioma de contenido de este wiki.",
        "apihelp-block-description": "Bloquear a un usuario.",
        "apihelp-block-param-user": "El nombre de usuario, dirección IP o intervalo de IP que quieres bloquear.",
@@ -50,6 +51,8 @@
        "apihelp-block-param-watchuser": "Vigilar las páginas de usuario y de discusión del usuario o de la dirección IP.",
        "apihelp-block-example-ip-simple": "Bloquear la dirección IP <kbd>192.0.2.5</kbd> durante 3 días por el motivo <kbd>First strike</kbd>.",
        "apihelp-block-example-user-complex": "Bloquear al usuario <kbd>Vandal</kbd> indefinidamente con el motivo <kbd>Vandalism</kbd> y evitar que se cree nuevas cuentas o envíe correos.",
+       "apihelp-changeauthenticationdata-description": "Cambiar los datos de autentificación para el usuario actual.",
+       "apihelp-changeauthenticationdata-example-password": "Intento para cambiar la contraseña del usuario actual a <kbd>ExamplePassword</kbd>.",
        "apihelp-checktoken-description": "Comprueba la validez de una ficha desde <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>.",
        "apihelp-checktoken-param-type": "Tipo de ficha a probar.",
        "apihelp-checktoken-param-token": "Ficha a probar.",
@@ -57,6 +60,8 @@
        "apihelp-checktoken-example-simple": "Probar la validez de una ficha <kbd>csrf</kbd>.",
        "apihelp-clearhasmsg-description": "Limpia la marca <code>hasmsg</code> del usuario actual.",
        "apihelp-clearhasmsg-example-1": "Limpiar la marca <code>hasmsg</code> del usuario actual.",
+       "apihelp-clientlogin-description": "Entrar en wiki usando el flujo interactivo.",
+       "apihelp-clientlogin-example-login": "Comenzar el proceso para iniciar sesión en el wiki como usuario <kbd>Example</kbd> con la contraseña <kbd>ExamplePassword</kbd>.",
        "apihelp-compare-description": "Obtener la diferencia entre 2 páginas.\n\nSe debe pasar un número de revisión, un título de página o una ID tanto desde \"de\" hasta \"a\".",
        "apihelp-compare-param-fromtitle": "Primer título para comparar",
        "apihelp-compare-param-fromid": "ID de la primera página a comparar.",
@@ -77,6 +82,9 @@
        "apihelp-createaccount-param-language": "Código de idioma a establecer como predeterminado para el usuario (opcional, predeterminado al contenido del idioma).",
        "apihelp-createaccount-example-pass": "Crear usuario <kbd>testuser</kbd> con la contraseña <kbd>test123</kbd>.",
        "apihelp-createaccount-example-mail": "Crear usuario <kbd>testmailuser</kbd> y enviar una contraseña generada aleatoriamente.",
+       "apihelp-cspreport-description": "Utilizado por los navegadores para reportar violaciones de la política de seguridad de contenidos. Este módulo no debe usarse nunca, excepto cuando se usa automáticamente por un navegador web CSP compatible.",
+       "apihelp-cspreport-param-reportonly": "Marcar un informe de una política de vigilancia y no a una política forzada",
+       "apihelp-cspreport-param-source": "Qué generó la cabecera CSP que provocó este informe",
        "apihelp-delete-description": "Borrar una página.",
        "apihelp-delete-param-title": "Título de la página a eliminar. No se puede utilizar junto a <var>$1pageid</var>.",
        "apihelp-delete-param-pageid": "ID de la página a eliminar. No se puede utilizar junto a <var>$1title</var>.",
@@ -85,6 +93,7 @@
        "apihelp-delete-param-watch": "Añadir esta página a la lista de seguimiento del usuario actual.",
        "apihelp-delete-param-watchlist": "Incondicionalmente agregar o remover la página de la lista de seguimiento del usuario actual, usar las preferencias o no cambiar el seguimiento.",
        "apihelp-delete-param-unwatch": "Quitar la página de la lista de seguimiento del usuario actual.",
+       "apihelp-delete-param-oldimage": "El nombre de la imagen antigua es proporcionado conforme a lo dispuesto por [[Special:ApiHelp/query+imageinfo|action=query&prop=imageinfo&iiprop=archivename]].",
        "apihelp-delete-example-simple": "Borrar <kbd>Main Page</kbd>.",
        "apihelp-delete-example-reason": "Eliminar <kbd>Main Page</kbd> con el motivo <kbd>Preparing for move</kbd>.",
        "apihelp-disabled-description": "Se desactivó este módulo.",
        "apihelp-edit-param-minor": "Edición menor.",
        "apihelp-edit-param-notminor": "Edición no menor.",
        "apihelp-edit-param-bot": "Marcar esta edición como de bot.",
+       "apihelp-edit-param-basetimestamp": "Marca de tiempo de la revisión base, usada para detectar conflictos de edición. Se puede obtener mediante [[Special:ApiHelp/query+revisions|action=query&prop=revisions&rvprop=timestamp]]",
+       "apihelp-edit-param-starttimestamp": "Marca de tiempo de cuando empezó el proceso de edición, usada para detectar conflictos de edición. Se puede obtener un valor apropiado usando <var>[[Special:ApiHelp/main|curtimestamp]]</var> cuando comiences el proceso de edición (por ejemplo, al cargar el contenido de la página por editar).",
        "apihelp-edit-param-recreate": "Reemplazar los errores acerca de la página de haber sido eliminados en el ínterin.",
        "apihelp-edit-param-createonly": "No editar la página si ya existe.",
        "apihelp-edit-param-nocreate": "Producir un error si la página no existe.",
        "apihelp-edit-param-watch": "Añadir la página a la lista de seguimiento del usuario actual.",
        "apihelp-edit-param-unwatch": "Quitar la página de la lista de seguimiento del usuario actual.",
+       "apihelp-edit-param-watchlist": "Incondicionalmente añadir o eliminar la página de lista del usuario actual, utilice referencias o no cambiar el reloj.",
+       "apihelp-edit-param-md5": "El hash MD5 del parámetro $1text, o los parámetros concatenados $1prependtext y $1appendtext. Si se establece, la edición no se hará a menos que el hash sea correcto.",
        "apihelp-edit-param-prependtext": "Añadir este texto al principio de la página. Reemplaza $1text.",
        "apihelp-edit-param-appendtext": "Añadir este texto al principio de la página. Reemplaza $1text.\n\nUtiliza $1section=new para añadir una nueva sección, en lugar de este parámetro.",
        "apihelp-edit-param-undo": "Deshacer esta revisión. Reemplaza $1text, $1prependtext y $1appendtext.",
        "apihelp-expandtemplates-param-title": "Título de la página.",
        "apihelp-expandtemplates-param-text": "Sintaxis wiki que se convertirá.",
        "apihelp-expandtemplates-param-revid": "Revisión de ID, para <nowiki>{{REVISIONID}}</nowiki> y variables similares.",
+       "apihelp-expandtemplates-param-prop": "Qué elementos de información se utilizan para llegar.\n\nTenga en cuenta que si no se seleccionan los valores, el resultado contendrá el wikitexto, pero la salida será en un formato obsoleto.",
        "apihelp-expandtemplates-paramvalue-prop-wikitext": "El wikitexto expandido.",
        "apihelp-expandtemplates-paramvalue-prop-categories": "Cualesquiera categorías presentes en la entrada que no están representadas en salida de wikitexto.",
        "apihelp-expandtemplates-paramvalue-prop-properties": "Propiedades de página definidas por palabras mágicas en el wikitexto.",
+       "apihelp-expandtemplates-paramvalue-prop-volatile": "Si la salida es volátil y no debe ser reutilizada en otro lugar dentro de la página.",
        "apihelp-expandtemplates-paramvalue-prop-ttl": "El tiempo máximo tras el cual deberían invalidarse los resultados en caché.",
        "apihelp-expandtemplates-paramvalue-prop-jsconfigvars": "Da las variables de configuración JavaScript específicas para la página.",
        "apihelp-expandtemplates-paramvalue-prop-encodedjsconfigvars": "Da las variables de configuración JavaScript específicas para la página como una cadena JSON.",
+       "apihelp-expandtemplates-paramvalue-prop-parsetree": "El árbol XML analiza el árbol de la entrada.",
        "apihelp-expandtemplates-param-includecomments": "Incluir o no los comentarios HTML en la salida.",
        "apihelp-expandtemplates-param-generatexml": "Generar un árbol de análisis XML (remplazado por $1prop=parsetree).",
        "apihelp-expandtemplates-example-simple": "Expandir el wikitexto <kbd><nowiki>{{Project:Sandbox}}</nowiki></kbd>.",
        "apihelp-feedrecentchanges-param-tagfilter": "Filtrar por etiquetas.",
        "apihelp-feedrecentchanges-param-target": "Mostrar solo los cambios en las páginas enlazadas en esta.",
        "apihelp-feedrecentchanges-param-showlinkedto": "Mostrar los cambios en páginas enlazadas con la página seleccionada.",
+       "apihelp-feedrecentchanges-param-categories": "Mostrar sólo cambios en las páginas en todas estas categorías.",
+       "apihelp-feedrecentchanges-param-categories_any": "Mostrar sólo cambios en las páginas en cualquiera de las categorías en lugar.",
        "apihelp-feedrecentchanges-example-simple": "Mostrar los cambios recientes.",
        "apihelp-feedrecentchanges-example-30days": "Mostrar los cambios recientes limitados a 30 días.",
        "apihelp-feedwatchlist-description": "Devuelve el canal de una lista de seguimiento.",
        "apihelp-help-param-submodules": "Incluir ayuda para submódulos del módulo con nombre.",
        "apihelp-help-param-recursivesubmodules": "Incluir ayuda para submódulos recursivamente.",
        "apihelp-help-param-helpformat": "Formato de salida de la ayuda.",
+       "apihelp-help-param-wrap": "Envolver el producto en una estructura de respuesta de la API estándar.",
        "apihelp-help-param-toc": "Incluir una tabla de contenidos en la salida HTML.",
        "apihelp-help-example-main": "Ayuda del módulo principal",
        "apihelp-help-example-submodules": "Ayuda para <kbd>action=query</kbd> y todos sus submódulos.",
        "apihelp-imagerotate-param-rotation": "Grados que rotar una imagen en sentido horario.",
        "apihelp-imagerotate-example-simple": "Rotar <kbd>File:Example.png</kbd> <kbd>90</kbd> grados.",
        "apihelp-imagerotate-example-generator": "Rotar todas las imágenes en <kbd>Category:Flip</kbd> <kbd>180</kbd> grados.",
+       "apihelp-import-description": "Importar una página desde otra wiki, o desde un archivo XML.\n\nTenga en cuenta que el HTTP POST debe hacerse como una carga de archivos (es decir, el uso de multipart/form-data) al enviar un archivo para el parámetro <var>xml</var>.",
        "apihelp-import-param-summary": "Resumen de importación de entrada del registro.",
        "apihelp-import-param-xml": "Se cargó el archivo XML.",
        "apihelp-import-param-interwikisource": "Para importaciones interwiki: wiki desde la que importar.",
        "apihelp-import-param-namespace": "Importar a este espacio de nombres. No puede usarse simultáneamente con <var>$1rootpage</var>.",
        "apihelp-import-param-rootpage": "Importar como subpágina de esta página. No puede usarse simultáneamente con <var>$1namespace</var>.",
        "apihelp-import-example-import": "Importar [[meta:Help:ParserFunctions]] al espacio de nombres 100 con todo el historial.",
-       "apihelp-login-description": "Iniciar sesión y obtener cookies de autenticación.\n\nSi inicias sesión sin problemas, las cookies necesarias se incluirán en los encabezados de respuesta HTTP. Si se produce algún error al iniciar sesión y este persiste, se puede regular para evitar los ataques masivos automatizados para adivinar contraseñas.",
+       "apihelp-linkaccount-description": "Vincular una cuenta de un proveedor de terceros para el usuario actual.",
+       "apihelp-linkaccount-example-link": "Iniciar el proceso de vincular a una cuenta de <kbd>Ejemplo</kbd>.",
+       "apihelp-login-description": "Iniciar sesión y obtener las cookies de autenticación.\n\nEsta acción solo se debe utilizar en combinación con [[Special:BotPasswords]]; para la cuenta de inicio de sesión no se utiliza y puede fallar sin previo aviso. Para iniciar la sesión de forma segura a la cuenta principal, utilice <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
+       "apihelp-login-description-nobotpasswords": "Iniciar sesión y obtener las cookies de autenticación.\n\nEsta acción esta obsoleta y puede fallar sin previo aviso. Para conectarse de forma segura, utilice <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
        "apihelp-login-param-name": "Nombre de usuario.",
        "apihelp-login-param-password": "Contraseña.",
        "apihelp-login-param-domain": "Dominio (opcional).",
        "apihelp-managetags-param-operation": "Qué operación realizar:\n;create: Crear una nueva etiqueta de cambio de uso manual.\n;delete: Eliminar una etiqueta de cambio de la base de datos, eliminando la etiqueta de todas las revisiones, cambios en entradas recientes y registros en los que se ha utilizado.\n;activate: Activar una etiqueta de cambio, permitiendo a los usuarios aplicarla manualmente.\n;deactivate: Desactivar una etiqueta de cambio, evitando que los usuarios la apliquen manualmente.",
        "apihelp-managetags-param-tag": "Etiqueta para crear, eliminar, activar o desactivar. Para crear una etiqueta, esta debe no existir. Para eliminarla, debe existir. Para activarla, debe existir y no estar en uso por ninguna extensión. Para desactivarla, debe estar activada y definida manualmente.",
        "apihelp-managetags-param-reason": "Un motivo opcional para crear, eliminar, activar o desactivar la etiqueta.",
+       "apihelp-managetags-param-ignorewarnings": "Ya sea para ignorar las advertencias que se emiten durante la operación.",
        "apihelp-managetags-example-create": "Crear una etiqueta llamada <kbd>spam</kbd> con el motivo <kbd>For use in edit patrolling</kbd>",
        "apihelp-managetags-example-delete": "Eliminar la etiqueta <kbd>vandlaism</kbd> con el motivo <kbd>Misspelt</kbd>",
        "apihelp-managetags-example-activate": "Activar una etiqueta llamada <kbd>spam</kbd> con el motivo <kbd>For use in edit patrolling</kbd>",
        "apihelp-managetags-example-deactivate": "Desactivar una etiqueta llamada <kbd>spam</kbd> con el motivo <kbd>No longer required</kbd>",
        "apihelp-mergehistory-description": "Fusionar historiales de páginas.",
+       "apihelp-mergehistory-param-from": "El título de la página desde la que se combinará la historia. No se puede utilizar junto con <var>$1fromid</var>.",
+       "apihelp-mergehistory-param-fromid": "Page ID de la página desde la que se combinara el historial. No se puede utilizar junto con <var>$1from</var>.",
+       "apihelp-mergehistory-param-to": "El título de la página desde la que se combinara el historial. No se puede utilizar junto con <var>$1toid</var>.",
+       "apihelp-mergehistory-param-toid": "Page ID de la página desde la que se combinara el historial. No se puede utilizar junto con <var>$1to</var>.",
+       "apihelp-mergehistory-param-timestamp": "La marca de tiempo de las revisiones se moverá del historial de la página de origen al historial de la página de destino. Si se omite, todo el historial de la página de la página de origen se fusionará en la página de destino.",
        "apihelp-mergehistory-param-reason": "Motivo para la fusión del historial.",
+       "apihelp-mergehistory-example-merge": "Combinar todo el historial de <kbd>Oldpage</kbd> en <kbd>Newpage</kbd>.",
+       "apihelp-mergehistory-example-merge-timestamp": "Combinar las revisiones de <kbd>Oldpage</kbd> hasta el <kbd>2015-12-31T04:37:41Z</kbd> en <kbd>Newpage</kbd>.",
        "apihelp-move-description": "Trasladar una página.",
        "apihelp-move-param-from": "Título de la página a renombrar. No se puede utilizar con <var>$1fromid</var>.",
        "apihelp-move-param-fromid": "ID de la página a renombrar. No se puede utilizar con <var>$1from</var>.",
        "apihelp-move-param-noredirect": "No crear una redirección.",
        "apihelp-move-param-watch": "Añadir la página y su redirección a la lista de seguimiento del usuario actual.",
        "apihelp-move-param-unwatch": "Eliminar la página y la redirección de la lista de seguimiento del usuario.",
+       "apihelp-move-param-watchlist": "Incondicionalmente puede añadir o eliminar la página de lista del usuario actual, utilizar referencias o no cambiar el reloj.",
        "apihelp-move-param-ignorewarnings": "Ignorar cualquier aviso.",
        "apihelp-move-example-move": "Trasladar <kbd>Badtitle</kbd> a <kbd>Goodtitle</kbd> sin dejar una redirección.",
        "apihelp-opensearch-description": "Buscar en el wiki mediante el protocolo OpenSearch.",
        "apihelp-paraminfo-param-modules": "Lista de los nombres de los módulos (valores de los parámetros <var>action</var> y <var>format</var> o <kbd>main</kbd>). Se pueden especificar los submódulos con un <kbd>+</kbd>.",
        "apihelp-paraminfo-param-helpformat": "Formato de las cadenas de ayuda.",
        "apihelp-paraminfo-param-querymodules": "Lista de los nombres de los módulos de consulta (valor de los parámetros <var>prop</var>, <var>meta</var> or <var>list</var>). Utiliza <kbd>$1modules=query+foo</kbd> en vez de <kbd>$1querymodules=foo</kbd>.",
+       "apihelp-paraminfo-param-mainmodule": "Obtener también información sobre el módulo principal (primer nivel). Utilizar <kbd>$1modules=main</kbd> en su lugar.",
+       "apihelp-paraminfo-param-pagesetmodule": "Obtener también información sobre el módulo PageSet (Proporcionar títulos= y amigos).",
        "apihelp-paraminfo-param-formatmodules": "Lista de los nombres del formato de los módulos (valor del parámetro <var>format</var>). Utiliza <var>$1modules</var> en su lugar.",
        "apihelp-paraminfo-example-1": "Mostrar información para <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd> y <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
        "apihelp-parse-param-title": "Título de la página a la que pertenece el texto. Si se omite se debe especificar <var>$1contentmodel</var> y se debe utilizar el [[API]] como título.",
        "apihelp-parse-paramvalue-prop-properties": "Da varias propiedades definidas en el wikitexto analizado.",
        "apihelp-parse-paramvalue-prop-limitreportdata": "Da el informe del límite de forma estructurada. No da datos si <var>$1disablelimitreport</var> está establecido.",
        "apihelp-parse-paramvalue-prop-limitreporthtml": "Da la versión HTML del informe del límite. No da datos si <var>$1disablelimitreport</var> está establecido.",
+       "apihelp-parse-paramvalue-prop-parsetree": "El árbol de análisis sintáctico XML del contenido de la revisión (requiere modelo de contenido <code>$1</code>)",
+       "apihelp-parse-param-pst": "Guardar previamente los cambios antes de transformar la entrada antes de analizarla. Sólo es válido cuando se utiliza con el texto.",
+       "apihelp-parse-param-onlypst": "Guardar previamente los cambios antes de transformar (PST) en la entrada. Devuelve el mismo wikitexto, después de que un PST se ha aplicado. Sólo es válido cuando se utiliza con <var>$1text</var>.",
        "apihelp-parse-param-effectivelanglinks": "Incluye enlaces de idiomas proporcionados por las extensiones (para utilizar con <kbd>$1prop=langlinks</kbd>).",
+       "apihelp-parse-param-disablelimitreport": "Omitir el informe de límite (\"NewPP limit report\") desde la salida del analizador.",
        "apihelp-parse-param-disablepp": "Usa <var>$1disablelimitreport</var> en su lugar.",
+       "apihelp-parse-param-disableeditsection": "Omitir los enlaces de edición de sección de la salida del analizador.",
+       "apihelp-parse-param-disabletidy": "No ejecute la limpieza HTML (por ejemplo ordenada) en la salida del analizador.",
+       "apihelp-parse-param-generatexml": "Generar árbol de análisis sintáctico XML (requiere modelo de contenido <code>$1</code>; sustituido por <kbd>$2prop=parsetree</kbd>).",
        "apihelp-parse-param-preview": "Analizar en modo de vista previa.",
        "apihelp-parse-param-sectionpreview": "Analizar sección en modo de vista previa (también activa el modo de vista previa).",
        "apihelp-parse-param-disabletoc": "Omitir la tabla de contenidos en la salida.",
+       "apihelp-parse-param-contentformat": "Formato de serialización de contenido utilizado para la introducción de texto. Sólo es válido cuando se utiliza con $1text.",
        "apihelp-parse-example-page": "Analizar una página.",
        "apihelp-parse-example-text": "Analizar wikitexto.",
        "apihelp-parse-example-texttitle": "Analizar wikitexto, especificando el título de la página.",
        "apihelp-protect-param-pageid": "ID de la página a (des)proteger. No se puede utilizar con $1title.",
        "apihelp-protect-param-protections": "Lista de los niveles de protección, con formato <kbd>action=level</kbd> (por ejemplo: <kbd>edit=sysop</kbd>).\n\n<strong>Nota:</strong> Cualquier acción no mencionada tendrá las restricciones eliminadas.",
        "apihelp-protect-param-reason": "Motivo de la (des)protección.",
+       "apihelp-protect-param-tags": "Cambiar las etiquetas para aplicar a la entrada en el registro de protección.",
        "apihelp-protect-param-cascade": "Activar la protección en cascada (o sea, proteger plantillas e imágenes transcluidas usadas en esta página). Se ignorará si ninguno de los niveles de protección dados son compatibles con la función de cascada.",
        "apihelp-protect-example-protect": "Proteger una página",
        "apihelp-protect-example-unprotect": "Desproteger una página estableciendo la restricción a <kbd>all</kbd>.",
        "apihelp-query+allfileusages-example-unique-generator": "Recupera los títulos de todos los archivos y marca los faltantes.",
        "apihelp-query+allfileusages-example-generator": "Recupera las páginas que contienen los archivos.",
        "apihelp-query+allimages-description": "Enumerar todas las imágenes secuencialmente.",
+       "apihelp-query+allimages-param-sort": "Propiedad por la que realizar la ordenación.",
+       "apihelp-query+allimages-param-dir": "La dirección en la que se listará.",
        "apihelp-query+allimages-param-from": "El título de la imagen para comenzar la enumeración. Solo puede utilizarse con $1sort=name.",
        "apihelp-query+allimages-param-to": "El título de la imagen para detener la enumeración. Solo puede utilizarse con $1sort=name.",
        "apihelp-query+allimages-param-start": "El sello de tiempo para comenzar la enumeración. Solo puede utilizarse con $1sort=timestamp.",
        "apihelp-query+alllinks-paramvalue-prop-title": "Añade el título del enlace.",
        "apihelp-query+alllinks-param-namespace": "El espacio de nombres que enumerar.",
        "apihelp-query+alllinks-param-limit": "Cuántos elementos en total se devolverán.",
+       "apihelp-query+alllinks-param-dir": "La dirección en la que se listará.",
+       "apihelp-query+alllinks-example-unique": "Lista de títulos vinculados únicamente.",
        "apihelp-query+alllinks-example-unique-generator": "Obtiene todos los títulos enlazados, marcando los que falten.",
+       "apihelp-query+alllinks-example-generator": "Obtiene páginas que contienen los enlaces.",
+       "apihelp-query+allmessages-description": "Devolver los mensajes de este sitio.",
+       "apihelp-query+allmessages-param-messages": "Qué mensajes mostrar. <kbd>*</kbd> (predeterminado) significa todos los mensajes.",
        "apihelp-query+allmessages-param-prop": "Qué propiedades se obtendrán.",
+       "apihelp-query+allmessages-param-enableparser": "Establecer para habilitar el analizador, se preprocesará el wikitexto del mensaje (sustitución de palabras mágicas, uso de plantillas, etc.).",
+       "apihelp-query+allmessages-param-nocontent": "Si se establece, no incluya el contenido de los mensajes en la salida.",
+       "apihelp-query+allmessages-param-args": "Los argumentos que se sustituyen en el mensaje.",
        "apihelp-query+allmessages-param-filter": "Devolver solo mensajes con nombres que contengan esta cadena.",
        "apihelp-query+allmessages-param-customised": "Devolver solo mensajes en este estado de personalización.",
        "apihelp-query+allmessages-param-lang": "Devolver mensajes en este idioma.",
        "apihelp-query+allpages-param-maxsize": "Limitar a páginas con este número máximo de bytes.",
        "apihelp-query+allpages-param-prtype": "Limitar a páginas protegidas.",
        "apihelp-query+allpages-param-limit": "Cuántas páginas en total se devolverán.",
+       "apihelp-query+allpages-param-dir": "La dirección en la que se listará.",
+       "apihelp-query+allpages-param-filterlanglinks": "Filtrar en función de si una página tiene langlinks. Tenga en cuenta que esto no puede considerar langlinks agregados por extensiones.",
        "apihelp-query+allpages-example-B": "Mostrar una lista de páginas que empiecen con la letra <kbd>B</kbd>.",
        "apihelp-query+allpages-example-generator": "Mostrar información acerca de 4 páginas que empiecen por la letra <kbd>T</kbd>.",
        "apihelp-query+allpages-example-generator-revisions": "Mostrar el contenido de las 2 primeras páginas que no redirijan y empiecen por <kbd>Re</kbd>.",
+       "apihelp-query+allredirects-description": "Obtener la lista de todas las redirecciones a un espacio de nombres.",
+       "apihelp-query+allredirects-param-from": "El título de la redirección para iniciar la enumeración.",
+       "apihelp-query+allredirects-param-to": "El título de la redirección para detener la enumeración.",
        "apihelp-query+allredirects-param-prefix": "Buscar todas las páginas de destino que empiecen con este valor.",
        "apihelp-query+allredirects-param-prop": "Qué piezas de información incluir:",
        "apihelp-query+allredirects-paramvalue-prop-title": "Añade el título de la redirección.",
+       "apihelp-query+allredirects-param-namespace": "El espacio de nombres a enumerar.",
        "apihelp-query+allredirects-param-limit": "Cuántos elementos se devolverán.",
+       "apihelp-query+allredirects-param-dir": "La dirección en la que se listará.",
+       "apihelp-query+allredirects-example-unique": "La lista de páginas de destino.",
+       "apihelp-query+allredirects-example-unique-generator": "Obtiene todas las páginas de destino, marcando los que faltan.",
+       "apihelp-query+allredirects-example-generator": "Obtiene páginas que contienen las redirecciones.",
        "apihelp-query+allrevisions-description": "Listar todas las revisiones.",
+       "apihelp-query+allrevisions-param-start": "La marca de tiempo para iniciar la enumeración.",
+       "apihelp-query+allrevisions-param-end": "La marca de tiempo para detener la enumeración.",
        "apihelp-query+allrevisions-param-user": "Listar solo las revisiones de este usuario.",
        "apihelp-query+allrevisions-param-excludeuser": "No listar las revisiones de este usuario.",
        "apihelp-query+allrevisions-param-namespace": "Listar solo las páginas en este espacio de nombres.",
        "apihelp-query+allrevisions-example-user": "Listar las últimas 50 contribuciones del usuario <kbd>Example</kbd>.",
        "apihelp-query+allrevisions-example-ns-main": "Listar las primeras 50 revisiones en el espacio de nombres principal.",
+       "apihelp-query+mystashedfiles-description": "Obtener una lista de archivos en la corriente de carga de usuarios.",
+       "apihelp-query+mystashedfiles-param-prop": "Propiedades a buscar para los archivos.",
+       "apihelp-query+mystashedfiles-paramvalue-prop-size": "Buscar el tamaño del archivo y las dimensiones de la imagen.",
+       "apihelp-query+mystashedfiles-paramvalue-prop-type": "Obtener el tipo MIME y tipo multimedia del archivo.",
        "apihelp-query+mystashedfiles-param-limit": "Cuántos archivos obtener.",
+       "apihelp-query+alltransclusions-param-from": "El título de la transclusión por la que empezar la enumeración.",
+       "apihelp-query+alltransclusions-param-to": "El título de la transclusión por la que terminar la enumeración.",
        "apihelp-query+alltransclusions-param-prefix": "Buscar todos los títulos transcluidos que comiencen con este valor.",
        "apihelp-query+alltransclusions-param-prop": "Qué piezas de información incluir:",
        "apihelp-query+alltransclusions-paramvalue-prop-title": "Añade el título de la transclusión.",
+       "apihelp-query+alltransclusions-param-limit": "Número de elementos que se desea obtener.",
        "apihelp-query+alltransclusions-example-unique": "Listar títulos transcluidos de forma única.",
        "apihelp-query+alltransclusions-example-unique-generator": "Obtiene todos los títulos transcluidos, marcando los que faltan.",
+       "apihelp-query+alltransclusions-example-generator": "Obtiene las páginas que contienen las transclusiones.",
        "apihelp-query+allusers-description": "Enumerar todos los usuarios registrados.",
        "apihelp-query+allusers-param-prefix": "Buscar todos los usuarios que empiecen con este valor.",
        "apihelp-query+allusers-param-group": "Incluir solo usuarios en los grupos dados.",
        "apihelp-query+allusers-paramvalue-prop-blockinfo": "Añade información sobre un bloque actual al usuario.",
        "apihelp-query+allusers-paramvalue-prop-groups": "Lista los grupos a los que el usuario pertenece. Esto utiliza más recursos del servidor y puede devolver menos resultados que el límite.",
        "apihelp-query+allusers-paramvalue-prop-rights": "Lista los permisos que tiene el usuario.",
+       "apihelp-query+allusers-paramvalue-prop-editcount": "Añade el número de ediciones del usuario.",
+       "apihelp-query+allusers-paramvalue-prop-registration": "Añade la marca de tiempo del momento en que el usuario se registró, si está disponible (puede estar en blanco).",
        "apihelp-query+allusers-param-limit": "Cuántos nombres de usuario se devolverán.",
        "apihelp-query+allusers-param-activeusers": "Solo listar usuarios activos en {{PLURAL:$1|el último día|los $1 últimos días}}.",
        "apihelp-query+allusers-example-Y": "Listar usuarios que empiecen por <kbd>Y</kbd>.",
-       "apihelp-query+filerepoinfo-example-login": "Captura de las solicitudes que puede ser utilizadas al comienzo de inicio de sesión.",
+       "apihelp-query+authmanagerinfo-example-login": "Captura de las solicitudes que puede ser utilizadas al comienzo de inicio de sesión.",
+       "apihelp-query+backlinks-description": "Encuentra todas las páginas que enlazan a la página dada.",
        "apihelp-query+backlinks-param-pageid": "Identificador de página que buscar. No puede usarse junto con <var>$1title</var>",
        "apihelp-query+backlinks-param-filterredir": "Cómo filtrar redirecciones. Si se establece a <kbd>nonredirects</kbd> cuando está activo <var>$1redirect</var>, esto sólo se aplica al segundo nivel.",
        "apihelp-query+backlinks-param-limit": "Cuántas páginas en total se devolverán. Si está activo <var>$1redirect</var>, el límite aplica a cada nivel por separado (lo que significa que se pueden devolver hasta 2 * <var>$1limit</var> resultados).",
        "apihelp-query+blocks-paramvalue-prop-reason": "Añade la razón dada para el bloqueo.",
        "apihelp-query+blocks-example-simple": "Listar bloques.",
        "apihelp-query+categories-param-prop": "Qué propiedades adicionales obtener para cada categoría:",
+       "apihelp-query+categories-paramvalue-prop-timestamp": "Añade la marca de tiempo del momento en que se añadió la categoría.",
        "apihelp-query+categories-param-show": "Qué tipo de categorías mostrar.",
        "apihelp-query+categories-param-limit": "Cuántas categorías se devolverán.",
        "apihelp-query+categories-example-generator": "Obtener información acerca de todas las categorías utilizadas en la página <kbd>Albert Einstein</kbd>.",
        "apihelp-query+categoryinfo-description": "Devuelve información acerca de las categorías dadas.",
        "apihelp-query+categoryinfo-example-simple": "Obtener información acerca de <kbd>Category:Foo</kbd> y <kbd>Category:Bar</kbd>",
+       "apihelp-query+categorymembers-description": "Lista todas las páginas en una categoría dada.",
        "apihelp-query+categorymembers-param-prop": "Qué piezas de información incluir:",
        "apihelp-query+categorymembers-paramvalue-prop-ids": "Añade el identificador de página.",
        "apihelp-query+categorymembers-paramvalue-prop-title": "Agrega el título y el identificador del espacio de nombres de la página.",
        "apihelp-query+categorymembers-paramvalue-prop-type": "Añade el tipo en el que se categorizó la página (<samp>page</samp>, <samp>subcat</samp> or <samp>file</samp>).",
+       "apihelp-query+categorymembers-paramvalue-prop-timestamp": "Añade la marca de tiempo del momento en que se incluyó la página.",
+       "apihelp-query+categorymembers-param-sort": "Propiedad por la que realizar la ordenación.",
+       "apihelp-query+categorymembers-param-dir": "Dirección en la que desea ordenar.",
        "apihelp-query+categorymembers-param-startsortkey": "Utilizar $1starthexsortkey en su lugar.",
        "apihelp-query+categorymembers-param-endsortkey": "Utilizar $1endhexsortkey en su lugar.",
        "apihelp-query+categorymembers-example-simple": "Obtener las primeras 10 páginas en <kbd>Category:Physics</kbd>.",
        "apihelp-query+filearchive-param-to": "El título de imagen para detener la enumeración.",
        "apihelp-query+filearchive-param-prefix": "Buscar todos los títulos de las imágenes que comiencen con este valor.",
        "apihelp-query+filearchive-param-prop": "Qué información de imagen se obtendrá:",
+       "apihelp-query+filearchive-paramvalue-prop-timestamp": "Añade la marca de tiempo de la versión subida.",
+       "apihelp-query+filearchive-paramvalue-prop-user": "Agrega el usuario que subió la versión de la imagen.",
        "apihelp-query+filearchive-paramvalue-prop-size": "Agrega el tamaño de la imagen en bytes y la altura, la anchura y el número de páginas (si es aplicable).",
        "apihelp-query+filearchive-paramvalue-prop-dimensions": "Alias del tamaño.",
        "apihelp-query+filearchive-paramvalue-prop-description": "Añade la descripción de la versión de la imagen.",
        "apihelp-query+imageinfo-param-extmetadatafilter": "Si se especifica y no vacío, sólo estas claves serán devueltos por $1prop=extmetadata.",
        "apihelp-query+imageinfo-param-urlparam": "Un controlador específico de la cadena de parámetro. Por ejemplo, los archivos Pdf pueden utilizar <kbd>page15-100px</kbd>. <var>$1urlwidth</var> debe ser utilizado y debe ser consistente con <var>$1urlparam</var>.",
        "apihelp-query+imageinfo-param-localonly": "Buscar solo archivos en el repositorio local.",
+       "apihelp-query+imageinfo-example-simple": "Obtener información sobre la versión actual de [[:File:Albert Einstein Head.jpg]].",
+       "apihelp-query+imageinfo-example-dated": "Obtener información sobre las versiones de [[:File:Test.jpg]] a partir de 2008.",
        "apihelp-query+images-description": "Devuelve todos los archivos contenidos en las páginas dadas.",
        "apihelp-query+images-param-limit": "Cuántos archivos se devolverán.",
        "apihelp-query+images-example-simple": "Obtener una lista de los archivos usados en la [[Main Page|Portada]].",
        "apihelp-query+info-example-protection": "Obtén información general y protección acerca de la página <kbd>Main Page</kbd>.",
        "apihelp-query+iwbacklinks-param-limit": "Cuántas páginas se devolverán.",
        "apihelp-query+iwbacklinks-param-prop": "Qué propiedades se obtendrán:",
+       "apihelp-query+iwbacklinks-paramvalue-prop-iwtitle": "Añade el título del interwiki.",
        "apihelp-query+iwbacklinks-example-simple": "Obtener las páginas enlazadas a [[wikibooks:Test]]",
+       "apihelp-query+iwlinks-description": "Devuelve todos los enlaces interwiki de las páginas dadas.",
        "apihelp-query+iwlinks-param-prop": "Qué propiedades adicionales obtener para cada enlace interlingüe:",
        "apihelp-query+iwlinks-paramvalue-prop-url": "Añade el URL completo.",
+       "apihelp-query+iwlinks-param-limit": "Cuántos enlaces interwiki se desea devolver.",
+       "apihelp-query+iwlinks-param-prefix": "Devolver únicamente enlaces interwiki con este prefijo.",
        "apihelp-query+langbacklinks-param-lang": "Idioma del enlace de idioma.",
        "apihelp-query+langbacklinks-param-limit": "Cuántas páginas en total se devolverán.",
        "apihelp-query+langbacklinks-param-prop": "Qué propiedades se obtendrán:",
        "apihelp-query+langbacklinks-paramvalue-prop-lltitle": "Añade el título del enlace de idioma.",
        "apihelp-query+langbacklinks-example-simple": "Obtener las páginas enlazadas a [[:fr:Test]]",
        "apihelp-query+langbacklinks-example-generator": "Obtener información acerca de las páginas enlazadas a [[:fr:Test]].",
+       "apihelp-query+langlinks-param-url": "Obtener la URL completa o no (no se puede usar con <var>$1prop</var>).",
        "apihelp-query+langlinks-param-prop": "Qué propiedades adicionales obtener para cada enlace interlingüe:",
        "apihelp-query+langlinks-paramvalue-prop-url": "Añade el URL completo.",
        "apihelp-query+langlinks-paramvalue-prop-autonym": "Añade el nombre del idioma nativo.",
        "apihelp-query+recentchanges-param-excludeuser": "No listar cambios de este usuario.",
        "apihelp-query+recentchanges-param-tag": "Listar solo los cambios con esta etiqueta.",
        "apihelp-query+recentchanges-param-prop": "Incluir piezas adicionales de información:",
+       "apihelp-query+recentchanges-paramvalue-prop-comment": "Añade el comentario de la edición.",
        "apihelp-query+recentchanges-paramvalue-prop-parsedcomment": "Añade el comentario analizado para la edición.",
        "apihelp-query+recentchanges-paramvalue-prop-flags": "Añade marcas para la edición.",
+       "apihelp-query+recentchanges-paramvalue-prop-timestamp": "Añade la marca de tiempo de la edición.",
+       "apihelp-query+recentchanges-paramvalue-prop-title": "Añade el título de página de la edición.",
+       "apihelp-query+recentchanges-paramvalue-prop-ids": "Añade los códigos ID de la página, de los cambios recientes y de las revisiones antigua y nueva.",
+       "apihelp-query+recentchanges-paramvalue-prop-sizes": "Añade la longitud antigua y la longitud nueva de la página en bytes.",
+       "apihelp-query+recentchanges-paramvalue-prop-redirect": "Etiqueta la edición si la página es una redirección.",
        "apihelp-query+recentchanges-paramvalue-prop-patrolled": "Etiqueta ediciones verificables como verificadas o no verificadas.",
        "apihelp-query+recentchanges-param-token": "Usa <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd> en su lugar.",
        "apihelp-query+recentchanges-param-limit": "Cuántos cambios en total se devolverán.",
        "apihelp-query+redirects-paramvalue-prop-pageid": "Identificador de página de cada redirección.",
        "apihelp-query+redirects-paramvalue-prop-title": "Título de cada redirección.",
        "apihelp-query+redirects-paramvalue-prop-fragment": "Fragmento de cada redirección, si los hubiere.",
+       "apihelp-query+redirects-param-namespace": "Incluir solo páginas de estos espacios de nombres.",
        "apihelp-query+redirects-param-limit": "Cuántas redirecciones se devolverán.",
        "apihelp-query+redirects-example-simple": "Mostrar una lista de las redirecciones a la [[Main Page|Portada]]",
+       "apihelp-query+redirects-example-generator": "Obtener información sobre todas las redirecciones a la [[Main Page|Portada]].",
+       "apihelp-query+revisions-param-end": "Enumerar hasta esta marca de tiempo.",
+       "apihelp-query+revisions-param-user": "Incluir solo las revisiones realizadas por el usuario.",
+       "apihelp-query+revisions-param-excludeuser": "Excluir las revisiones realizadas por el usuario.",
        "apihelp-query+revisions-example-last5": "Mostrar las últimas 5 revisiones de la <kbd>Main Page</kbd>.",
        "apihelp-query+revisions+base-param-prop": "Las propiedades que se obtendrán para cada revisión:",
        "apihelp-query+revisions+base-paramvalue-prop-ids": "El identificador de la revisión.",
index 079eeb2..5f09064 100644 (file)
@@ -29,6 +29,8 @@
        "apihelp-expandtemplates-paramvalue-prop-wikitext": "Wikitestu zabaldua.",
        "apihelp-feedcontributions-param-year": "Urtetik aurrera (eta lehenagotik)",
        "apihelp-feedcontributions-param-month": "Hilabetetik aurrera (eta lehenagotik)",
+       "apihelp-feedcontributions-param-newonly": "Orrialde sorkuntza direnak soilik erakutsi",
+       "apihelp-feedcontributions-param-hideminor": "Aldaketa txikiak ezkutatu",
        "apihelp-feedrecentchanges-param-hideminor": "Ezkutatu aldaketa txikiak.",
        "apihelp-feedrecentchanges-param-hidebots": "Ezkutatu botek egindako aldaketak.",
        "apihelp-feedrecentchanges-param-hideanons": "Ezkutatu erabiltzaile anonimoek egindako aldaketak.",
index a429583..2ae81d4 100644 (file)
                        "Macofe",
                        "Huji",
                        "Ladsgroup",
-                       "Freshman404"
+                       "Freshman404",
+                       "Alifakoor"
                ]
        },
        "apihelp-main-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 اعلانات رابط برنامه‌نویسی کاربردی]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R ایرادها و درخواست‌ها]\n</div>\n\n<strong>وضعیت:</strong> تمام ویژگی‌هایی که در این صفحه نمایش یافته‌اند باید کار بکنند، ولی رابط برنامه‌نویسی کاربردی کماکان در حال توسعه است، و ممکن است در هر زمان تغییر بکند. به عضویت [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ فهرست پست الکترونیکی mediawiki-api-announce] در بیایید تا از تغییرات باخبر شوید.\n\n<strong>درخواست‌های معیوب:</strong> وقتی درخواست‌های معیوب به رابط برنامه‌نویسی کاربردی فرستاده شوند، یک سرایند اچ‌تی‌تی‌پی با کلید «MediaWiki-API-Erorr» فرستاده می‌شود و بعد هم مقدار سرایند و هم کد خطای بازگردانده شده  هر دو به یک مقدار نسبت داده می‌شوند. برای اطلاعات بیشتر [[mw:API:Errors_and_warnings|API: Errors and warnings]] را ببینید.\n\n<strong>آزمایش:</strong> برای انجام درخواست‌های API آزمایشی [[Special:ApiSandbox]] را ببینید.",
        "apihelp-main-param-action": "کدام عملیات را انجام دهد.",
        "apihelp-main-param-format": "فرمت خروجی.",
+       "apihelp-main-param-smaxage": "تنظيم  <code>s-maxage</code> سرآیند کنترل حافضهٔ نهان HTTP بر اين تعداد ثانيه.",
+       "apihelp-main-param-maxage": "تنظيم  <code>s-maxage</code> سرآیند کنترل حافضهٔ نهان HTTP بر اين تعداد ثانيه.\nخطاها هيچگاه در حافظهٔ نهان قرار نمی‌گيرند.",
+       "apihelp-main-param-requestid": "هر مقداری که در اینجا وارد شود در پاسخ گنجانده می‌شود. ممکن است برای تمايز بين درخواست‌ها بکار رود.",
+       "apihelp-main-param-servedby": "نام ميزبانی که درخواست را سرويس داده در نتايج گنجانده شود.",
        "apihelp-main-param-curtimestamp": "برچسب زمان کنونی را در نتیجه قرار دهید.",
        "apihelp-block-description": "بستن کاربر",
        "apihelp-block-param-user": "نام کاربری، آدرس آی پی یا محدوده آی پی موردنظر شما برای بستن.",
        "apihelp-block-param-reblock": "اگر کاربر پیش از این مسدود شده‌است، مسدود موجود را بازنویسی کن.",
        "apihelp-block-param-watchuser": "صفحه‌های کاربر و بحث کاربر نشانی آی‌پی یا کاربر را پی‌گیری کنید.",
        "apihelp-block-example-ip-simple": "آی‌پی <kbd>۱۹۲٫۰٫۲٫۵</kbd> را برای سه روز همراه دلیل <kbd>برخورد اول</kbd> ببندید",
+       "apihelp-block-example-user-complex": "بستن کاربر<kbd>خرابکار</kbd> به شکل نامحدود به علت <kbd>خرابکاری</kbd>، همچنين جلوگيری از ايجاد حساب جديد و ارسال ايميل.",
+       "apihelp-changeauthenticationdata-description": "تغيير اطلاعات احراز هويت برای کاربر فعلی",
+       "apihelp-changeauthenticationdata-example-password": "تلاش برای تغيير گذرواژه فعلی کاربر به <kbd>نمونهٔ گذرواژه</kbd>.",
+       "apihelp-checktoken-param-type": "نوع توکنی که دارد آزمایش می‌شود.",
        "apihelp-checktoken-param-token": "توکن برای تست",
        "apihelp-checktoken-param-maxtokenage": "حداکثر عمر توکن به ثانیه.",
        "apihelp-checktoken-example-simple": "تست اعتبار یک توکن <kbd>csrf</kbd>",
        "apihelp-clearhasmsg-description": "پرچم <code>hasmsg</code> را برای کاربر جاری پاک کن.",
        "apihelp-clearhasmsg-example-1": "پاک‌کردن پرچم <code>hasmsg</code> برای کاربر جاری",
+       "apihelp-clientlogin-example-login": "شروع فرآیند ورود به ويکی به عنوان کاربر <kbd>نمونه</kbd> با گذرواژهٔ <kbd>نمونهٔ گذرواژه</kbd>",
        "apihelp-compare-description": "تفاوت بین ۲ صفحه را بیابید.\n\nشما باید یک شماره بازبینی، یک عنوان صفحه، یا یک شناسه صفحه برای هر دو «از» و «به» مشخص کنید.",
        "apihelp-compare-param-fromtitle": "عنوان اول برای مقایسه.",
        "apihelp-compare-param-fromid": "شناسه صفحه اول برای مقایسه.",
        "apihelp-query+allpages-param-minsize": "محدودکردن به صفحه‌هایی که همراه دست کم این تعداد بایت است.",
        "apihelp-query+allpages-param-limit": "میزان کل صفحه‌ها برای بازگرداندن.",
        "apihelp-query+allredirects-param-limit": "تعداد آیتم‌ها برای بازگرداندن.",
+       "apihelp-query+allrevisions-description": "فهرست همه نسخه‌ها",
+       "apihelp-query+mystashedfiles-param-limit": "تعداد پرونده‌هایی که باید بگیرد.",
+       "apihelp-query+allusers-param-dir": "جهتی که باید مرتب شود.",
+       "apihelp-query+allusers-paramvalue-prop-rights": "فهرست دسترسی‌هایی که کاربر دارد.",
+       "apihelp-query+allusers-paramvalue-prop-editcount": "شمار ويرایش کاربر را می‌افزايد",
+       "apihelp-query+allusers-paramvalue-prop-registration": "زمان ثبت نام کاربر را در صورت وجود می‌افزايد (ممکن است خالی باشد)",
+       "apihelp-query+allusers-param-limit": "تعداد کل نام‌های کاربری برای بازگرداندن.",
+       "apihelp-query+allusers-param-witheditsonly": "فقط کاربرانی را که ويرایش داشته اند ليست کن",
+       "apihelp-query+allusers-param-activeusers": "فقط کاربرانی را ليست کن که در $1 روز گذشته فعاليت داشته‌اند",
+       "apihelp-query+authmanagerinfo-description": "بازیابی اطلاعات در مورد وضعيت فعلی احراز هويت",
        "apihelp-query+backlinks-example-simple": "نمایش پیوندها به <kbd>Main page</kbd>.",
        "apihelp-query+blocks-example-simple": "فهرست بسته‌شده‌ها",
        "apihelp-query+categories-param-show": "کدام نوع رده‌ها نمایش داده‌شود.",
        "apihelp-query+categories-param-limit": "چه میزان رده بازگردانده شود.",
        "apihelp-query+categories-param-categories": "فقط این رده‌ها فهرست شود. کاربردی برای بررسی وجود یک صفحهٔ مشخص در یک ردهٔ مشخص.",
        "apihelp-query+categorymembers-description": "فهرست‌کردن همهٔ صفحه‌ها در یک ردهٔ مشخص‌شده.",
+       "apihelp-query+categorymembers-paramvalue-prop-ids": "افزودن شناسه صفحه",
        "apihelp-query+categorymembers-param-sort": "خصوصیت برای مرتب‌سازی",
        "apihelp-query+categorymembers-param-dir": "جهت مرتب شدن",
        "apihelp-query+categorymembers-param-startsortkey": "جایش از $1starthexsortkey استفاده کنید.",
+       "apihelp-query+deletedrevs-param-from": "شروع فهرست کردن مواردی که این عنوان را دارند.",
+       "apihelp-query+deletedrevs-param-to": "خاتمه فهرست کردن مواردی که این عنوان را دارند.",
+       "apihelp-query+deletedrevs-param-namespace": "فقط صفحات ین فضای نام را فهرست کن.",
+       "apihelp-query+deletedrevs-param-limit": "حداکثر تعداد بازنگری‌هايي که فهرست شوند.",
+       "apihelp-query+fileusage-paramvalue-prop-title": "عنوان هر صفحه.",
+       "apihelp-query+fileusage-param-limit": "تعدادی که باید بازگردانده شود.",
+       "apihelp-query+imageinfo-paramvalue-prop-dimensions": "نام مستعار برای size",
+       "apihelp-query+imageinfo-paramvalue-prop-sha1": "افزودن  SHA-1 hash برای پرونده",
+       "apihelp-query+imageinfo-paramvalue-prop-mime": "افزودن نوع MIME برای پرونده",
+       "apihelp-query+imageinfo-param-end": "زمان توقف فهرست کردن.",
        "apihelp-query+imageinfo-param-urlheight": "مشابه $1urlwidth.",
+       "apihelp-query+images-param-limit": "تعداد پرونده‌هایی که باید بازگرداند.",
        "apihelp-query+info-description": "دریافت اطلاعات سادهٔ صفحه.",
        "apihelp-query+iwbacklinks-param-prefix": "پیشوند میان‌ویکی.",
        "apihelp-query+iwbacklinks-param-title": "پیوند میان‌ویکی برای جستجو. باید همراه <var>$1blprefix</var> استفاده شود.",
        "apihelp-query+iwbacklinks-param-limit": "تعداد صفحه‌ها برای بازگرداندن.",
+       "apihelp-query+iwbacklinks-param-prop": "خصوصیتی که باید گرفته شود.",
+       "apihelp-query+iwlinks-paramvalue-prop-url": "افزودن نشانی اینترنتی کامل.",
+       "apihelp-query+langbacklinks-param-prop": "خصوصیتی که باید گرفته شود:",
+       "apihelp-query+langlinks-paramvalue-prop-url": "افزودن نشانی اینترنتی کامل.",
+       "apihelp-query+links-param-limit": "تعداد پیوندهایی که باید بازگرداند.",
+       "apihelp-query+linkshere-param-prop": "خصوصیتی که باید گرفته شود:",
+       "apihelp-query+linkshere-paramvalue-prop-pageid": "شناسه صفحه هر صفحه.",
+       "apihelp-query+linkshere-paramvalue-prop-title": "عنوان هر صفحه.",
+       "apihelp-query+linkshere-paramvalue-prop-redirect": "اگر صفحه تغییر مسیر بود برچسب بزن.",
+       "apihelp-query+linkshere-param-namespace": "فقط صفحات این فضای نام را فهرست کن.",
        "apihelp-query+linkshere-param-limit": "تعداد برای بازگرداندن.",
        "apihelp-query+logevents-description": "دریافت رویدادها از سیاهه‌ها.",
+       "apihelp-query+logevents-param-prop": "خصوصیتی که باید گرفته شود.",
+       "apihelp-query+logevents-paramvalue-prop-ids": "افزودن شناسه سیاهه رویداد.",
+       "apihelp-query+pageswithprop-paramvalue-prop-ids": "افزودن شناسه صفحه",
+       "apihelp-query+pageswithprop-param-dir": "جهت مرتب شدن",
        "apihelp-query+prefixsearch-param-search": "جستجوی رشته",
        "apihelp-query+prefixsearch-param-namespace": "فضاهای نامی برای جستجو",
        "apihelp-query+prefixsearch-param-limit": "حداکثر تعداد نتایج برای بازگرداندن.",
        "apihelp-query+random-example-generator": "بازگرداندن اطلاعات صفحه دربارهٔ دو صفحهٔ تصادفی از فضای نام اصلی",
        "apihelp-query+recentchanges-param-start": "برچسب زمانی برای آغاز شمارش از.",
        "apihelp-query+recentchanges-param-end": "برچسب زمانی برای پایان شمارش.",
+       "apihelp-query+recentchanges-paramvalue-prop-flags": "افزودن برچسب برای ویرایش.",
+       "apihelp-query+recentchanges-paramvalue-prop-timestamp": "افزودن زمان ویرایش.",
+       "apihelp-query+redirects-paramvalue-prop-title": "عنوان هر تغییرمسیر.",
        "apihelp-query+redirects-param-limit": "تعداد تغییرمسیرها برای بازگرداندن.",
+       "apihelp-query+revisions+base-paramvalue-prop-content": "متن نسخه ویرایش.",
+       "apihelp-query+revisions+base-paramvalue-prop-tags": "برچسب برای نسخه‌های ویرایش.",
+       "apihelp-query+siteinfo-param-prop": "اطلاعاتی که باید گرفته‌شود:",
+       "apihelp-query+siteinfo-paramvalue-prop-statistics": "بازرگرداندن آمار سایت.",
+       "apihelp-query+siteinfo-example-simple": "دریافت اطلاعات سایت.",
+       "apihelp-query+tags-description": "فهرست تغییرات برچسب‌ها.",
+       "apihelp-query+tags-param-limit": "حداکثر تعداد برچسب‌ها برای فهرست شدن.",
+       "apihelp-query+tags-param-prop": "خصوصیتی که باید گرفته شود:",
+       "apihelp-query+tags-paramvalue-prop-name": "افزودن نام برچسب.",
+       "apihelp-query+transcludedin-paramvalue-prop-title": "عنوان هر صفحه.",
+       "apihelp-query+watchlist-paramvalue-type-log": "مدخل‌های سیاهه.",
+       "apihelp-stashedit-param-text": "محتوای صفحه.",
+       "apihelp-stashedit-param-contentmodel": "مدل محتوایی محتوای جدید",
+       "apihelp-stashedit-param-summary": "خلاصه تغییرات.",
+       "apihelp-tag-param-reason": "دلیل تغییر.",
+       "apihelp-unblock-description": "بازکردن کاربر.",
+       "apihelp-undelete-param-reason": "دلیل احیا.",
+       "apihelp-upload-param-filename": "نام پرونده مقصد.",
        "apihelp-upload-param-ignorewarnings": "چشم‌پوشی از همهٔ هشدارها.",
+       "apihelp-upload-param-file": "محتوی پرونده.",
+       "apihelp-upload-param-url": "نشانی اینترنتی برای دریافت فایل.",
        "apihelp-userrights-param-user": "نام کاربری.",
+       "apihelp-userrights-param-userid": "شناسه کاربر.",
+       "apihelp-userrights-param-reason": "دلیل تغییر.",
+       "apihelp-none-description": "بیرون‌ریزی هیچ.",
+       "api-format-title": "نتیجه ای‌پی‌آی مدیاویکی",
+       "api-help-main-header": "پودمان اصلی",
+       "api-help-source": "منبع: $1",
        "api-help-param-deprecated": "توصیه.",
+       "api-help-param-limit": "بيش از $1 مجاز نيست",
+       "api-help-param-limit2": "بيش از $1 (برای ربات‌ها $2) مجاز نيست",
+       "api-help-param-default": "پیش‌فرض: $1",
        "api-credits-header": "اعتبار"
 }
index a10c486..ef9d7d1 100644 (file)
@@ -25,7 +25,9 @@
                        "Lbayle",
                        "Verdy p",
                        "Yasten",
-                       "Trial"
+                       "Trial",
+                       "Pols12",
+                       "The RedBurn"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentation]]\n* [[mw: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 en-tête HTTP sera renvoyé avec la clé « MediaWiki-API-Error ». La valeur de cet en-tête et le code d’erreur renvoyé prendront la même valeur. Pour plus d’information, voyez [[mw: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-main-param-maxlag": "La latence maximale peut être utilisée quand MédiaWiki est installé sur un cluster de base de données répliqué. Pour éviter des actions provoquant un supplément de latence de réplication de site, ce paramètre peut faire attendre le client jusqu’à ce que la latence de réplication soit inférieure à une valeur spécifiée. En cas de latence excessive, le code d’erreur <samp>maxlag</samp> est renvoyé avec un message tel que <samp>Attente de $host : $lag secondes de délai</samp>.<br />Voyez [[mw:Manual:Maxlag_parameter|Manuel: Maxlag parameter]] pour plus d’information.",
        "apihelp-main-param-smaxage": "Fixer l’entête HTTP de contrôle de cache <code>s-maxage</code> à ce nombre de secondes. Les erreurs ne sont jamais mises en cache.",
        "apihelp-main-param-maxage": "Fixer l’entête HTTP de contrôle de cache <code>max-age</code> à ce nombre de secondes. Les erreurs ne sont jamais mises en cache.",
-       "apihelp-main-param-assert": "Vérifier si l’utilisateur est connecté si positionné à <kbd>user</kbd>, ou a le droit utilisateur robot si positionné à <kbd>bot</kbd>.",
+       "apihelp-main-param-assert": "Vérifier si l’utilisateur est connecté si positionné à <kbd>user</kbd>, ou s'il a le droit d'un utilisateur robot si positionné à <kbd>bot</kbd>.",
        "apihelp-main-param-requestid": "Toute valeur fournie ici sera incluse dans la réponse. Peut être utilisé pour distinguer des demandes.",
        "apihelp-main-param-servedby": "Inclure le nom d’hôte qui a renvoyé la requête dans les résultats.",
        "apihelp-main-param-curtimestamp": "Inclure l’horodatage actuel dans le résultat.",
-       "apihelp-main-param-origin": "En accédant à l’API en utilisant une requête AJAX inter-domaines (CORS), mettre le domaine d’origine dans ce paramètre. Il doit être inclus dans toute requête de pre-flight, et doit donc faire partie de l’URI de la requête (pas du corps du POST). Il doit correspondre exactement à une des origines dans l’entête <code>Origin</code> header, donc il doit être fixé avec quelque chose comme <kbd>https://en.wikipedia.org</kbd> ou <kbd>https://meta.wikimedia.org</kbd>. Si ce paramètre ne correspond pas à l’entête <code>Origin</code>, une réponse 403 sera renvoyée. Si ce paramètre correspond à l’entête <code>Origin</code> et que l’origine est en liste blanche, un entête <code>Access-Control-Allow-Origin</code> sera positionné.",
+       "apihelp-main-param-origin": "En accédant à l’API en utilisant une requête AJAX inter-domaines (CORS), mettre le domaine d’origine dans ce paramètre. Il doit être inclus dans toute requête de pre-flight, et doit donc faire partie de l’URI de la requête (pas du corps du POST).\n\nPour les requêtes authentifiées, il doit correspondre exactement à une des origines dans l’entête <code>Origin</code> header, donc il doit être fixé avec quelque chose comme <kbd>https://en.wikipedia.org</kbd> ou <kbd>https://meta.wikimedia.org</kbd>. Si ce paramètre ne correspond pas à l’entête <code>Origin</code>, une réponse 403 sera renvoyée. Si ce paramètre correspond à l’entête <code>Origin</code> et que l’origine est en liste blanche, des entêtes <code>Access-Control-Allow-Origin</code> et <code>Access-Control-Allow-Credentials</code> seront positionnés.\n\nPour les requêtes non authentifiées, spécifiez la valeur <kbd>*</kbd>. Cela positionnera l’entête <code>Access-Control-Allow-Origin</code>, mais <code>Access-Control-Allow-Credentials</code> vaudra <code>false</code> et toutes les données spécifiques à l’utilisateur seront filtrées.",
        "apihelp-main-param-uselang": "Langue à utiliser pour les traductions de message. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> avec <kbd>siprop=languages</kbd> renvoie une liste de codes de langue, ou en spécifiant <kbd>user</kbd> pour utiliser la préférence de langue de l’utilisateur actuel, ou en spécifiant <kbd>content</kbd> pour utiliser le langage du contenu de ce wiki.",
        "apihelp-block-description": "Bloquer un utilisateur.",
        "apihelp-block-param-user": "Nom d’utilisateur, adresse IP ou plage d’adresses IP que vous voulez bloquer.",
        "apihelp-edit-param-sectiontitle": "Le titre pour une nouvelle section.",
        "apihelp-edit-param-text": "Contenu de la page.",
        "apihelp-edit-param-summary": "Modifier le résumé. Également le titre de la section quand $1section=new et $1sectiontitle n’est pas défini.",
-       "apihelp-edit-param-tags": "Modifier les balises à appliquer à la révision.",
+       "apihelp-edit-param-tags": "Modifier les balises à appliquer à la version.",
        "apihelp-edit-param-minor": "Modification mineure.",
        "apihelp-edit-param-notminor": "Modification non mineure.",
        "apihelp-edit-param-bot": "Marquer cette modification comme robot.",
        "apihelp-linkaccount-example-link": "Commencer le processus de liaison d’un compte depuis <kbd>Exemple</kbd>.",
        "apihelp-login-description": "Se connecter et obtenir les cookies d’authentification.\n\nCette action ne devrait être utilisée qu’en lien avec [[Special:BotPasswords]] ; l’utiliser pour la connexion du compte principal est obsolète et peut échouer sans avertissement. Pour se connecter sans problème au compte principal, utiliser <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
        "apihelp-login-description-nobotpasswords": "Se connecter et obtenir les cookies d’authentification.\n\nCette action est obsolète et peut échouer sans prévenir. Pour se connecter sans problème, utiliser <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
-       "apihelp-login-description-nonauthmanager": "Se connecter et obtenir les cookies d’authentification.\n\nDans le cas d’une connexion réussie, les cookies nécessaires seront inclus dans les entêtes HTTP de la réponse. Dans le cas d’une connexion en échec, des tentatives ultérieures pourront être limitées pour éviter les attaques automatiques pour deviner les mots de passe.",
        "apihelp-login-param-name": "Nom d’utilisateur.",
        "apihelp-login-param-password": "Mot de passe.",
        "apihelp-login-param-domain": "Domaine (facultatif).",
        "apihelp-options-example-change": "Modifier les préférences <kbd>skin</kbd> et <kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "Réinitialiser toutes les préférences, puis définir <kbd>skin</kbd> et <kbd>nickname</kbd>.",
        "apihelp-paraminfo-description": "Obtenir des informations sur les modules de l’API.",
-       "apihelp-paraminfo-param-modules": "Liste des noms de module (valeurs des paramètres <var>action</var> et <var>format</var>, ou <kbd>main</kbd>). Peut spécifier des sous-modules avec un <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "Liste des noms de module (valeurs des paramètres <var>action</var> et <var>format</var>, ou <kbd>main</kbd>). Peut spécifier des sous-modules avec un <kbd>+</kbd>, ou tous les sous-modules avec <kbd>+*</kbd>, ou tous les sous-modules récursivement avec <kbd>+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "Format des chaînes d’aide.",
        "apihelp-paraminfo-param-querymodules": "Liste des noms de module de requêtage (valeur des paramètres <var>prop</var>, <var>meta</var> ou <var>list</var>=). Utiliser <kbd>$1modules=query+foo</kbd> au lieu de <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Obtenir aussi des informations sur le module principal (niveau supérieur). Utiliser plutôt <kbd>$1modules=main</kbd>.",
        "apihelp-paraminfo-param-pagesetmodule": "Obtenir aussi des informations sur le module pageset (en fournissant titles= et ses amis).",
        "apihelp-paraminfo-param-formatmodules": "Liste des noms de module de mise en forme (valeur du paramètre <var>format</var>). Utiliser plutôt <var>$1modules</var>.",
        "apihelp-paraminfo-example-1": "Afficher les informations pour <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd> et <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "Afficher les informations pour tous les sous-modules de <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
        "apihelp-parse-description": "Analyse le contenu et renvoie le résultat de l’analyseur.\n\nVoyez les différents modules prop de <kbd>[[Special:ApiHelp/query|action=query]]</kbd> pour avoir de l’information sur la version actuelle d’une page.\n\nIl y a plusieurs moyens de spécifier le texte à analyser :\n# Spécifier une page ou une révision, en utilisant <var>$1page</var>, <var>$1pageid</var> ou <var>$1oldid</var>.\n# Spécifier explicitement un contenu, en utilisant <var>$1text</var>, <var>$1title</var> et <var>$1contentmodel</var>\n# Spécifier uniquement un résumé à analyser. <var>$1prop</var> doit recevoir une valeur vide.",
        "apihelp-parse-param-title": "Titre de la page à laquelle appartient le texte. Si omis, <var>$1contentmodel</var> doit être spécifié, et [[API]] sera utilisé comme titre.",
        "apihelp-parse-param-text": "Texte à analyser. utiliser <var>$1title</var> ou <var>$1contentmodel</var> pour contrôler le modèle de contenu.",
        "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": "Fournit les éléments à mettre dans le <code>&lt;head&gt;</code> de la page.",
+       "apihelp-parse-paramvalue-prop-headitems": "<span class=\"apihelp-deprecated\">Obsolète.</span> 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. 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.",
+       "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-parse-paramvalue-prop-encodedjsconfigvars": "Fournit les variables de configuration JavaScript spécifiques à la page comme chaîne JSON.",
        "apihelp-parse-paramvalue-prop-indicators": "Fournit le HTML des indicateurs d’état de page utilisés sur la page.",
        "apihelp-parse-paramvalue-prop-iwlinks": "Fournit les liens interwikis dans le wikitexte analysé.",
        "apihelp-protect-description": "Modifier le niveau de protection d’une page.",
        "apihelp-protect-param-title": "Titre de la page à (dé)protéger. Impossible à utiliser avec $1pageid.",
        "apihelp-protect-param-pageid": "ID de la page à (dé)protéger. Impossible à utiliser avec $1title.",
-       "apihelp-protect-param-protections": "Liste des niveaux de protection, au format <kbd>action=niveau</kbd> (par ex. <kbd>edit=sysop</kbd>).\n\n<strong>NOTE :<strong> Toutes les actions non listées auront leur restrictions supprimées.",
+       "apihelp-protect-param-protections": "Liste des niveaux de protection, au format <kbd>action=niveau</kbd> (par exemple  <kbd>edit=sysop</kbd>). Un niveau de <kbd>tout</kbd>, indique que tout le monde est autorisé à faire l'action, c'est à dire aucune restriction.\n\n<strong>NOTE :<strong> Toutes les actions non listées auront leur restrictions supprimées.",
        "apihelp-protect-param-expiry": "Horodatages d’expiration. Si un seul horodatage est fourni, il sera utilisé pour toutes les protections. Utiliser <kbd>infinite</kbd>, <kbd>indefinite</kbd>, <kbd>infinity</kbd> ou <kbd>never</kbd> pour une protection sans expiration.",
        "apihelp-protect-param-reason": "Motif de (dé)protection.",
        "apihelp-protect-param-tags": "Modifier les balises à appliquer à l’entrée dans le journal de protection.",
-       "apihelp-protect-param-cascade": "Activer la protection en cascade (c’est-à-dire protéger les modèles transclus et les images utilisées dans cette page). Ignoré si aucun des niveaux de protection fournis ne supporte la mise en cascade.",
+       "apihelp-protect-param-cascade": "Activer la protection en cascade (c’est-à-dire protéger les modèles transclus et les images utilisées dans cette page). Ignoré si aucun des niveaux de protection fournis ne prend en charge la mise en cascade.",
        "apihelp-protect-param-watch": "Si activé, ajouter la page (dé)protégée à la liste de suivi de l'utilisateur actuel.",
        "apihelp-protect-param-watchlist": "Ajouter ou supprimer sans condition la page de la liste de suivi de l'utilisateur actuel, utiliser les préférences ou ne pas modifier le suivi.",
        "apihelp-protect-example-protect": "Protéger une page",
-       "apihelp-protect-example-unprotect": "Enlever la protection d’une page en mettant les restrictions à <kbd>all</kbd>.",
+       "apihelp-protect-example-unprotect": "Enlever la protection d’une page en mettant les restrictions à <kbd>all</kbd> (c'est à dire tout le monde est autorisé à faire l'action).",
        "apihelp-protect-example-unprotect2": "Enlever la protection de la page en ne mettant aucune restriction",
        "apihelp-purge-description": "Vider le cache des titres fournis.\n\nNécessite une requête POST si l’utilisateur n’est pas connecté.",
        "apihelp-purge-param-forcelinkupdate": "Mettre à jour les tables de liens.",
        "apihelp-query+authmanagerinfo-description": "Récupérer les informations concernant l’état d’authentification actuel.",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "Tester si l’état d’authentification actuel de l’utilisateur est suffisant pour l’opération spécifiée comme sensible du point de vue sécurité.",
        "apihelp-query+authmanagerinfo-param-requestsfor": "Récupérer les informations sur les requêtes d’authentification nécessaires pour l’action d’authentification spécifiée.",
-       "apihelp-query+filerepoinfo-example-login": "Récupérer les requêtes qui peuvent être utilisées en commençant une connexion.",
-       "apihelp-query+filerepoinfo-example-login-merged": "Récupérer les requêtes qui peuvent être utilisées au début de la connexion, avec les champs de formulaire intégrés.",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "Tester si l’authentification est suffisante pour l’action <kbd>foo</kbd>.",
+       "apihelp-query+authmanagerinfo-example-login": "Récupérer les requêtes qui peuvent être utilisées en commençant une connexion.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "Récupérer les requêtes qui peuvent être utilisées au début de la connexion, avec les champs de formulaire intégrés.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "Tester si l’authentification est suffisante pour l’action <kbd>foo</kbd>.",
        "apihelp-query+backlinks-description": "Trouver toutes les pages qui ont un lien vers la page donnée.",
        "apihelp-query+backlinks-param-title": "Titre à rechercher. Impossible à utiliser avec <var>$1pageid</var>.",
        "apihelp-query+backlinks-param-pageid": "ID de la page à chercher. Impossible à utiliser avec <var>$1title</var>.",
        "apihelp-query+search-paramvalue-prop-sectiontitle": "Ajoute le titre de la section correspondante.",
        "apihelp-query+search-paramvalue-prop-categorysnippet": "Ajoute un extrait analysé de la catégorie correspondante.",
        "apihelp-query+search-paramvalue-prop-isfilematch": "Ajoute un booléen indiquant si la recherche correspond au contenu du fichier.",
-       "apihelp-query+search-paramvalue-prop-score": "<span class=\"apihelp-deprecated\">Obsolète et ignoré.</span>",
+       "apihelp-query+search-paramvalue-prop-score": "<span class=\"apihelp-deprecated\">Désuet et ignoré.</span>",
        "apihelp-query+search-paramvalue-prop-hasrelated": "<span class=\"apihelp-deprecated\">Obsolète et ignoré.</span>",
        "apihelp-query+search-param-limit": "Combien de pages renvoyer au total.",
        "apihelp-query+search-param-interwiki": "Inclure les résultats interwiki dans la recherche, s’ils sont disponibles.",
        "apihelp-query+siteinfo-paramvalue-prop-fileextensions": "Renvoie la liste des extensions de fichier (types de fichier) autorisées au téléchargement.",
        "apihelp-query+siteinfo-paramvalue-prop-rightsinfo": "Renvoie l’information sur les droits du wiki (sa licence), si elle est disponible.",
        "apihelp-query+siteinfo-paramvalue-prop-restrictions": "Renvoie l’information sur les types de restriction disponibles (protection).",
-       "apihelp-query+siteinfo-paramvalue-prop-languages": "Renvoie une liste des langues que supporte MédiaWiki (éventuellement localisé en utilisant <var>$1inlanguagecode</var>).",
+       "apihelp-query+siteinfo-paramvalue-prop-languages": "Renvoie une liste des langues que MédiaWiki prend en charge (éventuellement localisée en utilisant <var>$1inlanguagecode</var>).",
        "apihelp-query+siteinfo-paramvalue-prop-skins": "Renvoie une liste de tous les habillages activés (éventuellement localisé en utilisant <var>$1inlanguagecode</var>, sinon dans la langue du contenu).",
        "apihelp-query+siteinfo-paramvalue-prop-extensiontags": "Renvoie une liste des balises d’extension de l’analyseur.",
        "apihelp-query+siteinfo-paramvalue-prop-functionhooks": "Renvoie une liste des accroches de fonction de l’analyseur.",
        "apihelp-stashedit-param-section": "Numéro de section. <kbd>0</kbd> pour la section du haut, <kbd>new</kbd> pour une nouvelle section.",
        "apihelp-stashedit-param-sectiontitle": "Le titre pour une nouvelle section.",
        "apihelp-stashedit-param-text": "Contenu de la page.",
+       "apihelp-stashedit-param-stashedtexthash": "Empreinte du contenu de la page venant d’une réserve préalable à utiliser à la place.",
        "apihelp-stashedit-param-contentmodel": "Modèle de contenu du nouveau contenu.",
        "apihelp-stashedit-param-contentformat": "Format de sérialisation de contenu utilisé pour le texte saisi.",
        "apihelp-stashedit-param-baserevid": "ID de révision de la révision de base.",
        "api-pageset-param-generator": "Obtenir la liste des pages sur lesquelles travailler en exécutant le module de recherche spécifié.\n\n<strong>NOTE :<strong> les noms de paramètre du générateur doivent être préfixés avec un « g », voir les exemples.",
        "api-pageset-param-redirects-generator": "Résoudre automatiquement les redirections dans <var>$1titles</var>, <var>$1pageids</var> et <var>$1revids</var>, et dans les pages renvoyées par <var>$1generator</var>.",
        "api-pageset-param-redirects-nogenerator": "Résoudre automatiquement les redirections dans <var>$1titles</var>, <var>$1pageids</var> et <var>$1revids</var>.",
-       "api-pageset-param-converttitles": "Convertir les titres dans d’autres variantes si nécessaire. Fonctionne uniquement si la langue de contenu du wiki supporte la conversion en variantes. Les langues qui supportent la conversion en variante incluent $1.",
+       "api-pageset-param-converttitles": "Convertir les titres dans d’autres variantes si nécessaire. Fonctionne uniquement si la langue de contenu du wiki prend en charge la conversion en variantes. Les langues qui prennent en charge la conversion en variante incluent $1.",
        "api-help-title": "Aide de l’API de MediaWiki",
        "api-help-lead": "Ceci est une page d’aide de l’API de MediaWiki générée automatiquement.\n\nDocumentation et exemples : https://www.mediawiki.org/wiki/API",
        "api-help-main-header": "Module principal",
        "api-help-param-deprecated": "Obsolète.",
        "api-help-param-required": "Ce paramètre est obligatoire.",
        "api-help-datatypes-header": "Type de données",
-       "api-help-datatypes": "Certains types de paramètre dans les requêtes de l’API nécessitent plus d’explication :\n;boolean\n:Les paramètres booléens fonctionnent comme des cases à cocher HTML : si le paramètre est spécifié, quelle que soit sa valeur, il est considéré comme vrai. Pour une valeur fausse, enlever complètement le paramètre.\n;timestamp\n:Les horodatages peuvent être spécifiés sous différentes formes. Date et heure ISO 8601 est recommandé. Toutes les heures sont en UTC, tout fuseau horaire inclus est ignoré.\n:* Date et heure ISO 8601, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (la ponctuation et <kbd>Z</kbd> sont facultatifs)\n:* Date et heure ISO 8601 avec fractions de seconde (ignorées), <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (tirets, deux-points et <kbd>Z</kbd> sont facultatifs)\n:* Format MédiaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Format numérique générique, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (fuseau horaire facultatif en <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, ou <kbd>-<var>##</var></kbd> sont ignorés)\n:* Format EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*Format RFC 2822 (le fuseau horaire est facultatif), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Format RFC 850 (le fuseau horaire est facultatif), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Format ctime C, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Secondes depuis 1970-01-01T00:00:00Z sous forme d’entier de 1 à 13 chiffres (sans <kbd>0</kbd>)\n:* La chaîne <kbd>now</kbd>",
+       "api-help-datatypes": "Les entrées dans MédiaWiki doivent être en UTF-8 à la norme NFC. MédiaWiki peut tenter de convertir d’autres types d’entrée, mais cela peut faire échouer certaines opérations (comme les [[Special:ApiHelp/edit|modifications]] avec contrôles MD5) to fail.\n\nCertains types de paramètre dans les requêtes de l’API nécessitent plus d’explication :\n;boolean\n:Les paramètres booléens fonctionnent comme des cases à cocher HTML : si le paramètre est spécifié, quelle que soit sa valeur, il est considéré comme vrai. Pour une valeur fausse, enlever complètement le paramètre.\n;timestamp\n:Les horodatages peuvent être spécifiés sous différentes formes. Date et heure ISO 8601 est recommandé. Toutes les heures sont en UTC, tout fuseau horaire inclus est ignoré.\n:* Date et heure ISO 8601, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (la ponctuation et <kbd>Z</kbd> sont facultatifs)\n:* Date et heure ISO 8601 avec fractions de seconde (ignorées), <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (tirets, deux-points et <kbd>Z</kbd> sont facultatifs)\n:* Format MédiaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Format numérique générique, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (fuseau horaire facultatif en <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, ou <kbd>-<var>##</var></kbd> sont ignorés)\n:* Format EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*Format RFC 2822 (le fuseau horaire est facultatif), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Format RFC 850 (le fuseau horaire est facultatif), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Format ctime C, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Secondes depuis 1970-01-01T00:00:00Z sous forme d’entier de 1 à 13 chiffres (sans <kbd>0</kbd>)\n:* La chaîne <kbd>now</kbd>",
        "api-help-param-type-limit": "Type : entier ou <kbd>max</kbd>",
        "api-help-param-type-integer": "Type : {{PLURAL:$1|1=entier|2=liste d’entiers}}",
        "api-help-param-type-boolean": "Type : booléen ([[Special:ApiHelp/main#main/datatypes|détails]])",
        "api-help-param-type-timestamp": "Type : {{PLURAL:$1|1=horodatage|2=liste d’horodatages}} ([[Special:ApiHelp/main#main/datatypes|formats autorisés]])",
        "api-help-param-type-user": "Type : {{PLURAL:$1|1=nom d’utilisateur|2=liste de noms d’utilisateur}}",
-       "api-help-param-list": "{{PLURAL:$1|1=Une des valeurs suivantes|2=Valeurs (séparées par <kbd>{{!}}</kbd>)}} : $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Une des valeurs suivantes|2=Valeurs (séparées par <kbd>{{!}}</kbd> ou [[Special:ApiHelp/main#main/datatypes|autre]])}} : $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Doit être vide|Peut être vide, ou $2}}",
        "api-help-param-limit": "Pas plus de $1 autorisé.",
        "api-help-param-limit2": "Pas plus de $1 autorisé ($2 pour les robots).",
-       "api-help-param-integer-min": "{{PLURAL:$1|1=La valeur doit être inférieure|2=Les valeurs doivent être inférieures}} à $2.",
+       "api-help-param-integer-min": "{{PLURAL:$1|1=La valeur ne doit pas être inférieure|2=Les valeurs ne doivent pas être inférieures}} à $2.",
        "api-help-param-integer-max": "{{PLURAL:$1|1=La valeur ne doit pas être supérieure|2=Les valeurs ne doivent pas être supérieures}} à $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=La valeur doit|2=Les valeurs doivent}} être entre $2 et $3.",
        "api-help-param-upload": "Doit être envoyé comme un fichier importé utilisant multipart/form-data.",
-       "api-help-param-multi-separate": "Valeurs séparées par <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Valeurs séparées par <kbd>|</kbd> ou [[Special:ApiHelp/main#main/datatypes|autre]].",
        "api-help-param-multi-max": "Le nombre maximal de valeurs est {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} pour les robots).",
        "api-help-param-default": "Par défaut : $1",
        "api-help-param-default-empty": "Par défaut : <span class=\"apihelp-empty\">(vide)</span>",
index 8506a78..79e36cf 100644 (file)
@@ -23,7 +23,7 @@
        "apihelp-main-param-requestid": "Calquera valor dado aquí será incluído na resposta. Pode usarse para distingir peticións.",
        "apihelp-main-param-servedby": "Inclúa o nome do servidor que servía a solicitude nos resultados.",
        "apihelp-main-param-curtimestamp": "Incluir a marca de tempo actual no resultado.",
-       "apihelp-main-param-origin": "Cando se accede á API usando unha petición AJAX entre-dominios (CORS), inicializar o parámetro co dominio orixe. Isto debe incluírse en calquera petición pre-flight, e polo tanto debe ser parte da petición URI (non do corpo POST). Debe coincidir exactamente cunha das orixes na cabeceira <code>Origin</code>, polo que ten que ser fixado a algo como <kbd>https://en.wikipedia.org</kbd> ou <kbd>https://meta.wikimedia.org</kbd>. Se este parámetro non coincide coa cabeceira <code>Origin</code>, devolverase unha resposta 403. Se este parámetro coincide coa cabeceira <code>Origin</code> e a orixe está na lista branca, porase unha cabeceira <code>Access-Control-Allow-Origin</code>.",
+       "apihelp-main-param-origin": "Cando se accede á API usando unha petición AJAX entre-dominios (CORS), inicializar o parámetro co dominio orixe. Isto debe incluírse en calquera petición pre-flight, e polo tanto debe ser parte da petición URI (non do corpo POST). Para peticións autenticadas, isto debe coincidir exactamente cunha das orixes na cabeceira <code>Origin</code>, polo que ten que ser fixado a algo como <kbd>https://en.wikipedia.org</kbd> ou <kbd>https://meta.wikimedia.org</kbd>. Se este parámetro non coincide coa cabeceira <code>Origin</code>, devolverase unha resposta 403. Se este parámetro coincide coa cabeceira <code>Origin</code> e a orixe está na lista branca, as cabeceiras <code>Access-Control-Allow-Origin</code> e <code>Access-Control-Allow-Credentials</code> serán fixadas.\n\nPara peticións non autenticadas, especifique o valor <kbd>*</kbd>. Isto fará que se fixe a cabeceira <code>Access-Control-Allow-Origin</code>, pero <code>Access-Control-Allow-Credentials</code> será <code>false</code> e todos os datos específicos do usuario serán ocultados.",
        "apihelp-main-param-uselang": "Linga a usar para a tradución de mensaxes. <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> con <kbd>siprop=languages</kbd> devolve unha lista de códigos de lingua, ou especificando <kbd>user</kbd> coa preferencia de lingua do usuario actual, ou especificando <kbd>content</kbd> para usar a lingua do contido desta wiki.",
        "apihelp-block-description": "Bloquear un usuario.",
        "apihelp-block-param-user": "Nome de usuario, dirección ou rango de IPs que quere bloquear.",
@@ -60,6 +60,7 @@
        "apihelp-compare-param-torev": "Segunda revisión a comparar.",
        "apihelp-compare-example-1": "Mostrar diferencias entre a revisión 1 e a 2",
        "apihelp-createaccount-description": "Crear unha nova conta de usuario.",
+       "apihelp-createaccount-param-preservestate": "SE <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> devolve o valor \"certo\" para  <samp>hasprimarypreservedstate</samp>, as consultas marcadas como <samp>primary-required</samp> deben ser omitidas. Se devolve un valor non baleiro para <samp>preservedusername</samp>, ese nome de usuario debe usarse para o parámetro <var>username</var>.",
        "apihelp-createaccount-example-create": "Comezar o proceso de crear un usuario <kbd>Exemplo</kbd> con contrasinal <kbd>ExemploContrasinal</kbd>.",
        "apihelp-createaccount-param-name": "Nome de usuario.",
        "apihelp-createaccount-param-password": "Contrasinal (ignorado se <var>$1mailpassword</var> está activo)",
@@ -72,6 +73,9 @@
        "apihelp-createaccount-param-language": "Código de lingua para usar como defecto polo usuario (de xeito opcional, usarase a lingua por defecto)",
        "apihelp-createaccount-example-pass": "Crear usuario <kbd>testuser</kbd> con contrasinal <kbd>test123</kbd>.",
        "apihelp-createaccount-example-mail": "Crear usuario <kbd>testmailuser</kbd>\"testmailuser\" e enviar por correo electrónico un contrasinal xenerado de forma aleatoria.",
+       "apihelp-cspreport-description": "Usado polos navegadores para informar de violacións da política de confidencialidade de contido. Este módulo non debe se usado nunca, excepto cando é usado automaticamente por un navegador web compatible con CSP.",
+       "apihelp-cspreport-param-reportonly": "Marcar un informe dunha política de vixiancia e non unha política esixida",
+       "apihelp-cspreport-param-source": "Que xerou a cabeceira CSP que lanzou este informe",
        "apihelp-delete-description": "Borrar a páxina.",
        "apihelp-delete-param-title": "Título da páxina a eliminar. Non pode usarse xunto con <var>$1pageid</var>.",
        "apihelp-delete-param-pageid": "Identificador da páxina a eliminar. Non pode usarse xunto con <var>$1title</var>.",
        "apihelp-import-example-import": "Importar [[meta:Help:ParserFunctions]] ó espazo de nomes 100 con todo o historial.",
        "apihelp-linkaccount-description": "Vincular unha conta dun provedor externo ó usuario actual.",
        "apihelp-linkaccount-example-link": "Comezar o proceso de vincular a unha conta de <kbd>Exemplo</kbd>.",
-       "apihelp-login-description": "No caso dunha conexión correcta, as cookies necesarias incluiranse nas cabeceiras HTTP de resposta. No caso dunha conexión fallida, os intentos posteriores poden ser reducidos para limitar ataques automaticos de roubo de contrasinais.",
+       "apihelp-login-description": "Iniciar sesión e obter as cookies de autenticación.\n\nEsta acción só debe utilizarse en combinación con [[Special:BotPasswords]]; para a cuenta de inicio de sesión non se utiliza e pode fallar sen previo aviso. Para iniciar a sesión de forma segura na conta principal, utilice <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
        "apihelp-login-description-nobotpasswords": "Conectarse e obter as cookies de autenticación. \n\nEsta acción está obsoleta e pode fallar sen avisar. Para conectarse sen problema use <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
-       "apihelp-login-description-nonauthmanager": "Conectarse e obter as cookies de autenticación. \n\nNo caso dunha conexión correcta, as cookies necesarias incluiranse nas cabeceiras HTTP de resposta. No caso dunha conexión fallida, os intentos posteriores poden ser reducidos para limitar ataques automaticos de roubo de contrasinais.",
        "apihelp-login-param-name": "Nome de usuario.",
        "apihelp-login-param-password": "Contrasinal",
        "apihelp-login-param-domain": "Dominio (opcional).",
        "apihelp-options-example-change": "Cambiar as preferencias <kbd>skin</kbd> and <kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "Restaurar todas as preferencias, logo fixar <kbd>skin</kbd> e <kbd>nickname</kbd>.",
        "apihelp-paraminfo-description": "Obter información sobre módulos API.",
-       "apihelp-paraminfo-param-modules": "Lista de nomes de módulos (valores dos parámetros <var>acción</var e <var>formato</var>, ou <kbd>principal</kbd>). Pode especificar submódulos con <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "Lista de nomes de módulos (valores dos parámetros <var>acción</var e <var>formato</var>, ou <kbd>principal</kbd>). Pode especificar submódulos con <kbd>+</kbd>, ou tódolos submódulos con <kbd>+*</kbd>, ou tódolos submódulos recursivamente con <kbd>+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "Formato das cadeas de axuda.",
        "apihelp-paraminfo-param-querymodules": "Lista dos nomes de módulos de consulta (valores dos parámetros <var>prop</var>, <var>meta</var> ou <var>list</var>). Use <kbd>$1modules=query+foo</kbd> no canto de <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Obter información sobre o módulo principal (nivel superior). No canto use <kbd>$1modules=main</kbd>.",
        "apihelp-paraminfo-param-pagesetmodule": "Obter información sobre o módulo pageset (proporcionando títulos= e amigos).",
        "apihelp-paraminfo-param-formatmodules": "Lista dos nomes de módulo de formato (valores do parámetro <var>formato</var>). No canto use <var>$1modules</var>.",
        "apihelp-paraminfo-example-1": "Amosar información para <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd>, e <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "Mostrar a información para tódolos submódulos de <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
        "apihelp-parse-description": "Analiza o contido e devolve o resultado do analizador.\n\nVexa varios módulos propostos de <kbd>[[Special:ApiHelp/query|action=query]]</kbd> para obter información sobre a versión actual dunha páxina.\n\nHai varias formas de especificar o texto a analizar:\n# Especificar unha páxina ou revisión, usando <var>$1page</var>, <var>$1pageid</var>, ou <var>$1oldid</var>.\n# Especificando contido explícitamente, usando <var>$1text</var>, <var>$1title</var>, and <var>$1contentmodel</var>.\n# Especificando só un resumo a analizar. <var>$1prop</var> debe ter un valor baleiro.",
        "apihelp-parse-param-title": "Título da páxina á que pertence o texto. Se non se indica, debe especificarse <var>$1contentmodel</var>, e [[API]] usarase como o título.",
        "apihelp-parse-param-text": "Texto a analizar. Use <var>$1title</var> ou <var>$1contentmodel</var> para controlar o modelo de contido.",
        "apihelp-parse-paramvalue-prop-sections": "Devolve as seccións do texto wiki analizado.",
        "apihelp-parse-paramvalue-prop-revid": "Engade o identificador de edición do texto wiki analizado.",
        "apihelp-parse-paramvalue-prop-displaytitle": "Engade o título do texto wiki analizado.",
-       "apihelp-parse-paramvalue-prop-headitems": "Devolve os elementos a poñer na <code>&lt;cabeceira&gt;</code> da páxina.",
+       "apihelp-parse-paramvalue-prop-headitems": "<span class=\"apihelp-deprecated\">Obsoleto.</span> Devolve os elementos a poñer na <code>&lt;cabeceira&gt;</code> da páxina.",
        "apihelp-parse-paramvalue-prop-headhtml": "Devolve <code>&lt;cabeceira&gt;</code> analizada da páxina.",
-       "apihelp-parse-paramvalue-prop-modules": "Devolve os módulos ResourceLoader usados na páxina. <kbd>jsconfigvars</kbd> ou <kbd>encodedjsconfigvars</kbd> deben ser solicitados xunto con <kbd>modules</kbd>.",
-       "apihelp-parse-paramvalue-prop-jsconfigvars": "Devolve as variables específicas de configuración JavaScript da páxina.",
+       "apihelp-parse-paramvalue-prop-modules": "Devolve os módulos ResourceLoader usados na páxina. Para cargar, use <code>mw.loader.using()</code>. <kbd>jsconfigvars</kbd> ou <kbd>encodedjsconfigvars</kbd> deben ser solicitados xunto con <kbd>modules</kbd>.",
+       "apihelp-parse-paramvalue-prop-jsconfigvars": "Devolve as variables específicas de configuración JavaScript da páxina. Para aplicalo, use <code>mw.config.set()</code>.",
        "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "Devolve as variables específicas de configuración JavaScript da páxina como unha cadea de texto JSON.",
        "apihelp-parse-paramvalue-prop-indicators": "Devolve o HTML dos indicadores de estado de páxina usados na páxina.",
        "apihelp-parse-paramvalue-prop-iwlinks": "Devolve as ligazóns interwiki do texto wiki analizado.",
        "apihelp-protect-description": "Cambiar o nivel de protección dunha páxina.",
        "apihelp-protect-param-title": "Título da páxina que quere (des)protexer. Non pode usarse xunto con $1pageid.",
        "apihelp-protect-param-pageid": "Identificador da páxina que quere (des)protexer. Non pode usarse xunto con $1title.",
-       "apihelp-protect-param-protections": "Lista dos niveis de protección, con formato <kbd>action=level</kbd> (p.ex. <kbd>edit=sysop</kbd>).\n\n<strong>Nota:</strong> Todas as accións que non estean listadas terán restriccións para ser eliminadas.",
+       "apihelp-protect-param-protections": "Lista dos niveis de protección, con formato <kbd>action=level</kbd> (p.ex. <kbd>edit=sysop</kbd>). Un nivel de <kbd>all</kbd> quere dicir que todo o mundo ten permiso para realizar a acción, sen restricións.\n\n<strong>Nota:</strong> Todas as accións que non estean listadas terán restriccións para ser eliminadas.",
        "apihelp-protect-param-expiry": "Selos de tempo de caducidade. Se só se indica un selo de tempo, usarase para todas as proteccións. Use <kbd>infinite</kbd>, <kbd>indefinite</kbd>, <kbd>infinity</kbd>, ou <kbd>never</kbd>, para unha protección sen caducidade.",
        "apihelp-protect-param-reason": "Razón para (des)protexer.",
        "apihelp-protect-param-tags": "Cambiar as etiquetas a aplicar na entrada do rexistro de protección.",
        "apihelp-protect-param-watch": "Se se define este parámetro, engadir a páxina que se (des)protexe á lista de vixilancia do usuario actual.",
        "apihelp-protect-param-watchlist": "Engadir ou eliminar sen condicións a páxina da lista de vixiancia do usuario actual, use as preferencias ou non cambie a vixiancia.",
        "apihelp-protect-example-protect": "Protexer unha páxina",
-       "apihelp-protect-example-unprotect": "Desprotexer unha páxina poñendo as restricións a <kbd>all</kbd>.",
+       "apihelp-protect-example-unprotect": "Desprotexer unha páxina poñendo as restricións a <kbd>all</kbd>. (isto quere dicir que todo o mundo pode realizar a acción).",
        "apihelp-protect-example-unprotect2": "Desprotexer unha páxina quitando as restricións.",
        "apihelp-purge-description": "Borrar a caché para os títulos indicados.\n\nPrecisa dunha petición POST se o usuario non está conectado.",
        "apihelp-purge-param-forcelinkupdate": "Actualizar as táboas de ligazóns.",
        "apihelp-query+authmanagerinfo-description": "Recuperar información sobre o estado de autenticación actual.",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "Comprobar se o estado de autenticación actual do usuario é abondo para a operación especificada como sensible dende o punto de vista da seguridade.",
        "apihelp-query+authmanagerinfo-param-requestsfor": "Recuperar a información sobre as peticións de autenticación necesarias para a acción de autenticación especificada.",
-       "apihelp-query+filerepoinfo-example-login": "Recuperar as peticións que poden ser usadas ó comezo dunha conexión.",
-       "apihelp-query+filerepoinfo-example-login-merged": "Recuperar as peticións que poden ser usadas ó comezo dunha conexión, xunto cos campos de formulario integrados.",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "Probar se a autenticación é abondo para a acción <kbd>foo</kbd>.",
+       "apihelp-query+authmanagerinfo-example-login": "Recuperar as peticións que poden ser usadas ó comezo dunha conexión.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "Recuperar as peticións que poden ser usadas ó comezo dunha conexión, xunto cos campos de formulario integrados.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "Probar se a autenticación é abondo para a acción <kbd>foo</kbd>.",
        "apihelp-query+backlinks-description": "Atopar todas as páxinas que ligan coa páxina dada.",
        "apihelp-query+backlinks-param-title": "Título a buscar. Non pode usarse xunto con <var>$1pageid</var>.",
        "apihelp-query+backlinks-param-pageid": "Identificador de páxina a buscar. Non pode usarse xunto con <var>$1title</var>.",
        "apihelp-stashedit-param-section": "Número de selección. O <kbd>0</kbd> é para a sección superior, <kbd>novo</kbd> para unha sección nova.",
        "apihelp-stashedit-param-sectiontitle": "Título para unha nova sección.",
        "apihelp-stashedit-param-text": "Contido da páxina.",
+       "apihelp-stashedit-param-stashedtexthash": "Función hash do contido da páxina dunha reserva anterior para ser usada.",
        "apihelp-stashedit-param-contentmodel": "Modelo de contido para o novo contido.",
        "apihelp-stashedit-param-contentformat": "Formato de serialización de contido utilizado para o texto de entrada.",
        "apihelp-stashedit-param-baserevid": "Identificador da revisión da revisión de base.",
        "api-help-param-deprecated": "Obsoleto.",
        "api-help-param-required": "Este parámetro é obrigatorio.",
        "api-help-datatypes-header": "Tipos de datos",
-       "api-help-datatypes": "Algúns tipos de parámetros nas solicitudes de API necesitan máis explicación:\n;boolean\n:Os parámetros booleanos traballan como caixas de verificación HTML: se o parámetro se especifica, independentemente do seu valor, considérase verdadeiro. Para un valor falso, omíta o parámetro completo.\n;timestamp\n:Os selos de tempo poden especificarse en varios formatos. Recoméndase o ISO 8601 coa data e a hora. Todas as horas están en UTC, a inclusión da zona horaria é ignorada.\n:* ISO 8601 con data e hora, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (signos de puntuación e <kbd>Z</kbd> son opcionais)\n:* ISO 8601 data e hora (omítense) fraccións de segundo, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (guións, dous puntos e, <kbd>Z</kbd> son opcionais)\n:* Formato MediaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Formato numérico xenérico, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (opcional na zona horaria <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, o <kbd>-<var>##</var></kbd> omítese)\n:* Formato EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*Formato RFC 2822 (a zona horaria pódese omitir), <kbd><var>Mon</var>, <var>15</var> <var>Xan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formato RFC 850 (a zona horaria pódese omitir), <kbd><var>luns</var>, <var>15</var>-<var>xaneiro</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formato C ctime, <kbd><var>luns</var> <var>xaneiro</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>de 2001</var></kbd>\n:* Segundos desde 1970-01-01T00:00:00Z como de 1 a 13, díxitos enteiros (excluíndo o <kbd>0</kbd>)\n:* O texto <kbd>now</kbd> (agora)",
+       "api-help-datatypes": "A entrada a MediaWiki debe ser normalizada NFC UTF-8. MediaWiki puede intentar converter outras entradas, pero isto pode provocar que algunhas operacións (como as [[Special:ApiHelp/edit|edición]] con comprobación MD5) fallen.\n\nAlgúns tipos de parámetros nas solicitudes de API necesitan máis explicación:\n;boolean\n:Os parámetros booleanos traballan como caixas de verificación HTML: se o parámetro se especifica, independentemente do seu valor, considérase verdadeiro. Para un valor falso, omíta o parámetro completo.\n;timestamp\n:Os selos de tempo poden especificarse en varios formatos. Recoméndase o ISO 8601 coa data e a hora. Todas as horas están en UTC, a inclusión da zona horaria é ignorada.\n:* ISO 8601 con data e hora, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (signos de puntuación e <kbd>Z</kbd> son opcionais)\n:* ISO 8601 data e hora (omítense) fraccións de segundo, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (guións, dous puntos e, <kbd>Z</kbd> son opcionais)\n:* Formato MediaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Formato numérico xenérico, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (opcional na zona horaria <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, o <kbd>-<var>##</var></kbd> omítese)\n:* Formato EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*Formato RFC 2822 (a zona horaria pódese omitir), <kbd><var>Mon</var>, <var>15</var> <var>Xan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formato RFC 850 (a zona horaria pódese omitir), <kbd><var>luns</var>, <var>15</var>-<var>xaneiro</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formato C ctime, <kbd><var>luns</var> <var>xaneiro</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>de 2001</var></kbd>\n:* Segundos desde 1970-01-01T00:00:00Z como de 1 a 13, díxitos enteiros (excluíndo o <kbd>0</kbd>)\n:* O texto <kbd>now</kbd> (agora)",
        "api-help-param-type-limit": "Tipo: enteiro ou <kbd>max</kbd>",
        "api-help-param-type-integer": "Tipo: {{PLURAL:$1|1=enteiro|2=lista de enteiros}}",
        "api-help-param-type-boolean": "Tipo: booleano ([[Special:ApiHelp/main#main/datatypes|detalles]])",
        "api-help-param-type-timestamp": "Tipo: {{PLURAL:$1|1=selo de tempo|2=lista de selos de tempo}} ([[Special:ApiHelp/main#main/datatypes|formatos permitidos]])",
        "api-help-param-type-user": "Tipo: {{PLURAL:$1|1=nome de usuario|2=lista de nomes de usuarios}}",
-       "api-help-param-list": "{{PLURAL:$1|1=Un valor dos seguintes valores|2=Valores (separados con <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Un valor dos seguintes valores|2=Valores (separados con <kbd>{{!}}</kbd> ou [[Special:ApiHelp/main#main/datatypes|outros]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Debe ser baleiro|Pode ser baleiro, ou $2}}",
        "api-help-param-limit": "Non se permiten máis de $1.",
        "api-help-param-limit2": "Non se permiten máis de $1 ($2 para bots).",
        "api-help-param-integer-max": "{{PLURAL:$1|1=O valor debe ser menor |2=Os valores deben ser menores}} que $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=O valor debe estar entre $2 e $3 |2=Os valores deben estar entre $2 e $3}}.",
        "api-help-param-upload": "Debe ser enviado como un ficheiro importado usando multipart/form-data.",
-       "api-help-param-multi-separate": "Separe os valores con <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Separe os valores con <kbd>|</kbd> ou [[Special:ApiHelp/main#main/datatypes|outros]].",
        "api-help-param-multi-max": "O número máximo de valores é {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} para os bots).",
        "api-help-param-default": "Por defecto: $1",
        "api-help-param-default-empty": "Por defecto: <span class=\"apihelp-empty\">(baleiro)</span>",
        "api-help-permissions-granted-to": "{{PLURAL:$1|Concedida a|Concedidas a}}: $2",
        "api-help-right-apihighlimits": "Usar os valores superiores das consultas da API (consultas lentas: $1; consultas rápidas: $2). Os límites para as consultas lentas tamén se aplican ós parámetros multivaluados.",
        "api-help-open-in-apisandbox": "<small>[abrir en zona de probas]</small>",
+       "api-help-authmanager-general-usage": "O procedemento xeral para usar este módulo é:\n# Buscar os campos dispoñibles dende <kbd>[[Special:ApiHelp/query+authmanagerinfo|\n\naction=query&meta=authmanagerinfo]]</kbd> con <kbd>amirequestsfor=$4</kbd>, e un identificador <kbd>$5</kbd> de <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>.\n# Presentar os campos ó usuario, e obter o seu envío.\n# Enviar a este módulo, proporcionando <var>$1returnurl</var> e calquera campo relevante.\n# Comprobar o <samp>status</samp> na resposta.\n#* Se vostede recibe <samp>PASS</samp> ou <samp>FAIL</samp>, a acción rematou. A operación foi correcta ou non se fixo.\n#* Se vostede recibe <samp>UI</samp>, presenta os novos campos ó usuario e obtén o seu envío. Logo son enviados a este módulo con <var>$1continue</var> e o conxunto de campos relevantes, e repite o paso 4.\n#* Se vostede recibe <samp>REDIRECT</samp>, dirixe ó usuario a <samp>redirecttarget</samp> e espera pola resposta a <var>$1returnurl</var>. Logo envíaa a este módulo con <var>$1continue</var> e calquera campo pasado á URL de volta, e repite o paso 4.\n#* Se recibe <samp>RESTART</samp>, isto significa que a autenticación funcionou pero que non temos unha conta de usuario ligada. Debe tratar isto igual que <samp>UI</samp> ou como <samp>FAIL</samp>.",
        "api-help-authmanagerhelper-requests": "Só usar estas peticións de autenticación, co <samp>id</samp> devolto por <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> con <kbd>amirequestsfor=$1</kbd> ou dunha resposta previa deste módulo.",
        "api-help-authmanagerhelper-request": "Usar esta petición de autenticación, co <samp>id</samp> devolto por <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> con <kbd>amirequestsfor=$1</kbd>.",
        "api-help-authmanagerhelper-messageformat": "Formato a usar para devolver as mensaxes.",
        "api-help-authmanagerhelper-mergerequestfields": "Fusionar os campos de información para todas as peticións de autenticación nunha táboa.",
        "api-help-authmanagerhelper-preservestate": "Conservar o estado dun intento previo de conexión fallida, se é posible.",
+       "api-help-authmanagerhelper-returnurl": "Devolve o URL para os fluxos de autenticación de terceiros, que debe ser absoluto. Este ou <var>$1continue</var> é obrigatorio.\n\nLogo da recepción dunha resposta <samp>REDIRECT</samp>, vostede normalmente abrirá un navegador web ou un visor web para ver a URL <samp>redirecttarget</samp> especificada para un fluxo de autenticación de terceiros. Cando isto se complete, a aplicación de terceiros enviará ó navegador web ou visor web a esta URL. Vostede debe eliminar calquera consulta ou parámetros POST da URL e pasalos como unha consulta <var>$1continue</var> a este módulo API.",
        "api-help-authmanagerhelper-continue": "Esta petición é unha continucación despois dun resposta precedente <samp>UI</samp> ou <samp>REDIRECT</samp>. Esta ou <var>$1returnurl</var> é requirida.",
+       "api-help-authmanagerhelper-additional-params": "Este módulo acepta parámetros adicionais dependendo das consultas de autenticación dispoñibles. Use <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> con <kbd>amirequestsfor=$1</kbd> (ou unha resposta previa deste módulo, se aplicable) para determinar as consultas dispoñibles e os campos que usan.",
        "api-credits-header": "Créditos",
        "api-credits": "Desenvolvedores da API:\n* Roan Kattouw (desenvolvedor principal, set. 2007-2009)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Yuri Astrakhan (creador e desenvolvedor principal, set. 2006-sep. 2007)\n* Brad Jorsch (desenvolvedor principal, 2013-actualidade)\n\nEnvía comentarios, suxerencias e preguntas a mediawiki-api@lists.wikimedia.org\nou informa dun erro en https://phabricator.wikimedia.org/."
 }
index 2a39745..dd0d07b 100644 (file)
@@ -24,7 +24,7 @@
        "apihelp-main-param-requestid": "כל ערך שיינתן כאן ייכלל בתשובה. אפשר להשתמש בזה כדי להבדיל בין בקשות.",
        "apihelp-main-param-servedby": "לכלול את שם המארח ששירת את הבקשה בתוצאות.",
        "apihelp-main-param-curtimestamp": "הכללת חותם־הזמן הנוכחי בתוצאה.",
-       "apihelp-main-param-origin": "בעת גישה ל־API עם בקשת AJAX חוצה מתחמים (CORS), יש להציב כאן את המתחם שהבקשה יוצאת ממנו. זה היה להיות כלול בכל בקשה מקדימה, ולכן הוא חייב להיות חלק מה־URI של הבקשה (לא גוף ה־POST). זה חייב להיות תואם במדויק לאחד המקורות בכותרת <code>Origin</code>, כך שזה צריך להיות מוגדר למשהו כמו <kbd>https://en.wikipedia.org</kbd> או <kbd>https://meta.wikimedia.org</kbd>. אם הפרמטר הזה אינו תואם לכותרת <code>Origin</code>, תוחזר תשובת 403. אם הפרמטר הזה תורם לכותרת <code>Origin</code> והמקור נמצא ברשימה הלבנה, תוגדר כותרת <code>Access-Control-Allow-Origin</code>.",
+       "apihelp-main-param-origin": "בעת גישה ל־API עם בקשת AJAX חוצה מתחמים (CORS), יש להציב כאן את המתחם שהבקשה יוצאת ממנו. זה היה להיות כלול בכל בקשה מקדימה, ולכן הוא חייב להיות חלק מה־URI של הבקשה (לא גוף ה־POST).\n\nעבור בקשות מאומתות, זה חייב להיות תואם במדויק לאחד המקורות בכותרת <code>Origin</code>, כך שזה צריך להיות מוגדר למשהו כמו <kbd>https://en.wikipedia.org</kbd> או <kbd>https://meta.wikimedia.org</kbd>. אם הפרמטר הזה אינו תואם לכותרת <code>Origin</code>, תוחזר תשובת 403. אם הפרמטר הזה תורם לכותרת <code>Origin</code> והמקור נמצא ברשימה הלבנה, תוגדרנה הכותרות <code>Access-Control-Allow-Origin</code> ו־<code>Access-Control-Allow-Credentials</code>.\n\nעבור בקשות בלתי־מאומתות, יש לציין את הערך <kbd>*</kbd>. זה יגרום לכותרת להיות <code>Access-Control-Allow-Origin</code>, אבל <code>Access-Control-Allow-Credentials</code> תהיה <code>false</code> וכל הנתונים הייחודיים למשתמש יהיו מוגבלים.",
        "apihelp-main-param-uselang": "באיזו שפה להשתמש לתרגומי הודעות. הקריאה <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> עם <kbd>siprop=languages</kbd> מחזירה רשימת קודים. ציון <kbd>user</kbd> כדי להשתמש בהעדפת השפה של המשתמש הנוכחי, וציון <kbd>content</kbd> להשתמש בקוד השפה של הוויקי הזה.",
        "apihelp-block-description": "חסימת משתמש.",
        "apihelp-block-param-user": "שם משתמש, כתובת IP, או טווח IP שהנך רוצה לחסום.",
@@ -95,7 +95,7 @@
        "apihelp-edit-param-section": "מספר הפסקה <kbd>0</kbd> לפסקה העליונה, <kbd>new</kbd> לפסקה חדשה.",
        "apihelp-edit-param-sectiontitle": "הכותרת לפסקה החדשה.",
        "apihelp-edit-param-text": "תוכן הדף.",
-       "apihelp-edit-param-summary": "תקצ×\99ר ×¢×¨×\99×\9b×\94. ×\92×\9d ×\92ותרת פסקה כש־$1section=new ו־$1sectiontitle אינו מוגדר.",
+       "apihelp-edit-param-summary": "תקצ×\99ר ×¢×¨×\99×\9b×\94. ×\92×\9d ×\9bותרת פסקה כש־$1section=new ו־$1sectiontitle אינו מוגדר.",
        "apihelp-edit-param-tags": "אילו תגי שינוי להחיל על הגרסה.",
        "apihelp-edit-param-minor": "עריכה משנית.",
        "apihelp-edit-param-notminor": "שינוי לא משני.",
        "apihelp-linkaccount-example-link": "תחילת תהליך הקישור לחשבון מ־<kbd>Example</kbd>.",
        "apihelp-login-description": "להיכנס ולקבל עוגיות אימות.\n\nהפעולה הזאת צריכה לשמש רק בשילוב [[Special:BotPasswords]]; שימוש לכניסה לחשבון ראשי מיושן ועשוי להיכשל ללא אזהרה. כדי להיכנס בבטחה לחשבון הראשי, יש להשתמש ב־<kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
        "apihelp-login-description-nobotpasswords": "להיכנס ולקבל עוגיות אימות.\n\nהפעולה הזאת מיושנת ועשויה להיכשל ללא אזהרה. כדי להיכנס בבטחה, יש להשתמש ב־<kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
-       "apihelp-login-description-nonauthmanager": "להיכנס ולקבל עוגיות אימות.\n\nבמקרה של כניסה מוצלחת, העוגיות המקוננות תיכללנה בכותרות תשובות ה־HTTP. במקרה של כניסה כושלת, הניסיונות הבאים יוגבלו למספר ניסויי ניחוש הססמה האוטומטיים.",
        "apihelp-login-param-name": "שם משתמש.",
        "apihelp-login-param-password": "ססמה.",
        "apihelp-login-param-domain": "שם מתחם (רשות).",
        "apihelp-options-example-change": "לשנות את ההעדפות <kbd>skin</kbd> ו־<kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "לאתחל את כל ההעדפות ואז להגדיר את <kbd>skin</kbd> ואת <kbd>nickname</kbd>.",
        "apihelp-paraminfo-description": "קבלת מידע על יחידות של API.",
-       "apihelp-paraminfo-param-modules": "רשימה של שמות יחידות (ערכים של הפרמטרים <var>action</var> ו־<var>format</var>, או <kbd>main</kbd>). אפשר להגדיר תת־יחידות עם <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "רשימה של שמות יחידות (ערכים של הפרמטרים <var>action</var> ו־<var>format</var>, או <kbd>main</kbd>). אפשר להגדיר תת־יחידות עם <kbd>+</kbd>, או כל התת־מודולים עם <kbd dir=\"ltr\">+*</kbd>, או כל התת־מודולים באופן רקורסיבי עם <kbd dir=\"ltr\">+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "תסדיר מחרוזות העזרה.",
        "apihelp-paraminfo-param-querymodules": "רשימת שמות יחידות query (ערך של הפרמטר <var>prop</var>‏, <var>meta</var> או <var>list</var>). יש להשתמש ב־<kbd>$1modules=query+foo</kbd> במקום <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "קבלת מידע עם היחידה הראשית (העליונה). יש להשתמש ב־<kbd>$1modules=main</kbd> במקום זה.",
        "apihelp-paraminfo-param-pagesetmodule": "קבלת מידע גם על יחידת pageset (שמספק את titles=‎ וידידיו).",
        "apihelp-paraminfo-param-formatmodules": "רשימת שמות תסדירים (ערכים של הפרמטר <var>format</var>). יש להשתמש ב־<var>$1modules</var> במקום זה.",
        "apihelp-paraminfo-example-1": "הצגת מידע עבור <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>‏, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>‏, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd>‏, ו־<kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "הצגת מידע עבור כל התת־מודולים של <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
        "apihelp-parse-description": "מפענח את התוכן ומחזיר פלט מפענח.\n\nר' את יחידת ה־prop השיונות של <kbd>[[Special:ApiHelp/query|action=query]]</kbd> כדי לקבל מידע על הגרסה הנוכחית של הדף.\n\nיש מספר דרכים לציין טקסט לפענוח:\n# ציון דף או גרסה באמצעות <var>$1page</var>‏, <var>$1pageid</var>, או <var>$1oldid</var>.\n# ציון התוכן במפורש, באמצעות <var>$1text</var>‏, <var>$1title</var>, ו־<var>$1contentmodel</var>.\n# ציון רק של  התקציר לפענוח. ל־<var>$1prop</var> צריך לתת ערך ריק.",
        "apihelp-parse-param-title": "שם הדף שהטקסט שייך אליו. אם זה מושמט, יש לציין את <var>$1contentmodel</var>, ו־[[API]] ישמש ככותרת.",
        "apihelp-parse-param-text": "הטקסט לפענוח. יש להשתמש ב־<var>$1title</var> או ב־<var>$1contentmodel</var>.",
        "apihelp-parse-paramvalue-prop-sections": "מתן הפסקאות בקוד הוויקי המפוענח.",
        "apihelp-parse-paramvalue-prop-revid": "הוספת מזהה הגרסה של הדף המפוענח.",
        "apihelp-parse-paramvalue-prop-displaytitle": "הוספת הכותרת של קוד הוויקי המפוענח.",
-       "apihelp-parse-paramvalue-prop-headitems": "נותן פריטים לשים ב־<code>&lt;head&gt;</code> של הדף.",
+       "apihelp-parse-paramvalue-prop-headitems": "<span class=\"apihelp-deprecated\">לא בשימוש.</span> נותן פריטים לשים ב־<code>&lt;head&gt;</code> של הדף.",
        "apihelp-parse-paramvalue-prop-headhtml": "נותן את ה־<code>&lt;head&gt;</code> המפוענח של הדף.",
-       "apihelp-parse-paramvalue-prop-modules": "×\9eצ×\9f ×\99×\97×\99×\93×\95ת ResourceLoader ×©×\9eש×\9eש×\95ת ×\91×\93×£. יש לבקש את <kbd>jsconfigvars</kbd> או את <kbd>encodedjsconfigvars</kbd> יחד עם <kbd>modules</kbd>.",
-       "apihelp-parse-paramvalue-prop-jsconfigvars": "נותן משתני הגדרות של JavaScript שייחודיים לדף הזה.",
+       "apihelp-parse-paramvalue-prop-modules": "×\9eת×\9f ×\99×\97×\99×\93×\95ת ResourceLoader ×©×\9eש×\9eש×\95ת ×\91×\93×£. ×\9b×\93×\99 ×\9c×\98×¢×\95×\9f, ×\99ש ×\9c×\94שת×\9eש ×\91<code dir=\"ltr\">mw.loader.using()</code>. יש לבקש את <kbd>jsconfigvars</kbd> או את <kbd>encodedjsconfigvars</kbd> יחד עם <kbd>modules</kbd>.",
+       "apihelp-parse-paramvalue-prop-jsconfigvars": "נותן משתני הגדרות של JavaScript שייחודיים לדף הזה. כדי להחיל, יש להשתמש ב<code dir=\"ltr\">mw.config.set()</code>.",
        "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "נותן משתני הגדרות של JavaScript שייחודיים לדף הזה בתור מחרוזת JSON.",
        "apihelp-parse-paramvalue-prop-indicators": "נותן את ה־HTML של מחווני מצב דף שמשמשים בדף.",
        "apihelp-parse-paramvalue-prop-iwlinks": "מתן קישורי בינוויקי בקוד הוויקי המפוענח.",
        "apihelp-protect-description": "לשנות את רמת ההגנה של דף.",
        "apihelp-protect-param-title": "כותרת הדף להגנה או הסרת הגנה. לא ניתן להשתמש בזה יחד עם $1pageid.",
        "apihelp-protect-param-pageid": "מזהה הדף להגנה או הסרת הגנה. לא ניתן להשתמש בזה יחד עם $1title.",
-       "apihelp-protect-param-protections": "רש×\99×\9eת ×¨×\9e×\95ת ×\94×\9b× ×\94, ×\91תס×\93×\99ר <kbd>action=level</kbd> (×\9c×\9eש×\9c <kbd>edit=sysop</kbd>).",
+       "apihelp-protect-param-protections": "רש×\99×\9eת ×¨×\9e×\95ת ×\94×\92× ×\94, ×\91תס×\93×\99ר <kbd>action=level</kbd> (×\9c×\9eש×\9c <kbd>edit=sysop</kbd>). ×¨×\9eת <kbd>all</kbd> ×¤×\99ר×\95ש×\94 ×©×\9b×\95×\9c×\9d ×\9e×\95רש×\99×\9d ×\9c×\91צע ×\90ת ×\94פע×\95×\9c×\94, ×\9b×\9c×\95×\9eר ×\90×\99×\9f ×\94×\92× ×\94.\n\n<strong>×\94ער×\94:</strong> ×\94×\94×\92×\91×\9c×\95ת ×\99×\95סר×\95 ×\9e×\9b×\9c ×\94פע×\95×\9c×\95ת ×©×\9c×\90 ×\9bת×\95×\91×\95ת ×\91רש×\99×\9e×\94.",
        "apihelp-protect-param-expiry": "חותמי־זמן של תפוגה. אם הוגדר רק חותם־זמן אחד, הוא ישמש לכל ההגנות. יש להשתמש ב־<kbd>infinite</kbd>‏, <kbd>indefinite</kbd>‏, <kbd>infinity</kbd>, או <kbd>never</kbd> להגנה שלא פגה לעולם.",
        "apihelp-protect-param-reason": "סיבה להגנה או הסרת הגנה.",
        "apihelp-protect-param-tags": "תגי שינוי שיחולו על העיול ביומן ההגנה.",
        "apihelp-protect-param-watch": "אם זה מוגדר, הוספת הדף שהגנה נוספת אליו או מוסרת ממנו לרשימת המעקב של המשתמש הנוכחי.",
        "apihelp-protect-param-watchlist": "הוספה או הסרה של הדף ללא תנאי מרשימת המעקב של המשתמש הנוכחי, להשתמש בהעדפות או לא לשנות את המעקב.",
        "apihelp-protect-example-protect": "הגנה על דף.",
-       "apihelp-protect-example-unprotect": "להסיר את ההגנה מהדף על־ידי הגדרת מגבלות על <kbd>all</kbd>.",
+       "apihelp-protect-example-unprotect": "להסיר את ההגנה מהדף על־ידי הגדרת מגבלות על <kbd>all</kbd> (למשל: כולם מורשים לבצע את הפעולה).",
        "apihelp-protect-example-unprotect2": "הסרת הגנה מדף על־ידי הגדרה של אפס הגבלות.",
        "apihelp-purge-description": "ניקוי המטמון לכותרות שניתנו.\n\nדורש בקשת POST אם המשתמש לא נכנס לחשבון.",
        "apihelp-purge-param-forcelinkupdate": "עדכון טבלאות הקישורים.",
        "apihelp-query+authmanagerinfo-description": "אחזור מידע אודות מצב האימות הנוכחי.",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "בדיקה האם מצב האימות הנוכחי של המשתמש מספיק בשביל הפעולה הרגישה מבחינת אבטחה שצוינה.",
        "apihelp-query+authmanagerinfo-param-requestsfor": "אחזור מידע על בקשות האימות הדרושות לפעולת האימות המבוקשת.",
-       "apihelp-query+filerepoinfo-example-login": "אחזור הבקשות שיכולות לשמש לתחילת הכניסה.",
-       "apihelp-query+filerepoinfo-example-login-merged": "אחזור הבקשות שיכולות לשמש לתחילת הכניסה, עם שדות טופס ממוזגים.",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "בדיקה האם האימות מספיק בשביל הפעולה <kbd>foo</kbd>.",
+       "apihelp-query+authmanagerinfo-example-login": "אחזור הבקשות שיכולות לשמש לתחילת הכניסה.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "אחזור הבקשות שיכולות לשמש לתחילת הכניסה, עם שדות טופס ממוזגים.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "בדיקה האם האימות מספיק בשביל הפעולה <kbd>foo</kbd>.",
        "apihelp-query+backlinks-description": "מציאת כל הדפים שמקשרים לדף הנתון.",
        "apihelp-query+backlinks-param-title": "איזו כותרת לחפש. לא ניתן להשתמש בזה יחד עם <var>$1pageid</var>.",
        "apihelp-query+backlinks-param-pageid": "מזהה דף לחיפוש. לא ניתן להשתמש בזה יחד עם <var>$1title</var>.",
        "apihelp-stashedit-param-section": "מספר הפסקה. <kbd>0</kbd> עבור הפסקה הראשונה, <kbd>new</kbd> עבור פסקה חדשה.",
        "apihelp-stashedit-param-sectiontitle": "כותרת הפסקה החדשה.",
        "apihelp-stashedit-param-text": "תוכן הדף.",
+       "apihelp-stashedit-param-stashedtexthash": "גיבוב של תוכן דף מסליק קודם שישמש במקום זה.",
        "apihelp-stashedit-param-contentmodel": "מודל התוכן של התוכן החדש.",
        "apihelp-stashedit-param-contentformat": "תסדיר הסדרת תוכן עבור טקסט הקלט.",
        "apihelp-stashedit-param-baserevid": "מזהה גסה של גרסת הבסיס.",
        "api-help-param-deprecated": "מיושן.",
        "api-help-param-required": "פרמטר זה נדרש.",
        "api-help-datatypes-header": "סוגי נתונים",
-       "api-help-datatypes": "×\97×\9cק ×\9eס×\95×\92×\99 ×\94פר×\9e×\98ר×\99×\9d ×\91×\91קש×\95ת API ×\93×\95רש×\99×\9d ×\94ס×\91ר × ×\95סף:\n;×\91×\95×\9c×\99×\90× ×\99 (boolean)\n:פר×\9e×\98ר×\99×\9d ×\91×\95×\9c×\99×\90× ×\99×\99×\9d ×¢×\95×\91×\93×\99×\9d ×\9b×\9e×\95 ×ª×\99×\91×\95ת ×¡×\99×\9e×\95×\9f ×©×\9c HTML: ×\90×\9d ×\94פר×\9e×\98ר ×¦×\95×\99×\9f, ×\91×\9c×\99 ×§×©×¨ ×\9cער×\9a ×©×\9c×\95, ×\94×\95×\90 ×\90×\9eת (true). ×\91ש×\91×\99×\9c ×¢×¨×\9a ×©×§×¨ (false), ×\99ש ×\9c×\94ש×\9e×\99×\98 ×\90ת ×\94פר×\9e×\98ר ×\9c×\92×\9eר×\99.\n;×\97×\95ת×\9dÖ¾×\96×\9e×\9f (timestamp)\n:×\90פשר ×\9c×\9bת×\95×\91 ×\97×\95ת×\9e×\99Ö¾×\96×\9e×\9f ×\91×\9eספר ×ª×¡×\93×\99ר×\99×\9d. ×ª×\90ר×\99×\9a ×\95שע×\94 ×\9cפ×\99 ISO 8601 ×\94×\95×\90 ×\94×\93×\91ר ×\94×\9e×\95×\9e×\9cת. ×\9b×\9c ×\94×\96×\9e× ×\99×\9d ×\9eצ×\95×\99× ×\99×\9d ×\91Ö¾ UTC, ×\9c×\90 ×ª×\94×\99×\94 ×\94שפע×\94 ×\9cש×\95×\9d ×\90×\96×\95ר ×\96×\9e×\9f ×©×\99צ×\95×\99×\9f.\n:* ×ª×\90ר×\99×\9a ×\95שע×\94 ×\9cפ×\99 ISO 8601â\80\8f, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (×\9c×\90 ×\97×\95×\91×\94 ×\9c×\9bת×\95×\91 ×¤×\99ס×\95ק ×\95Ö¾<kbd>Z</kbd>)\n:* ×ª×\90ר×\99×\9a ×\95שע×\94 ×\9cפ×\99 ISO 8601 ×¢×\9d ×\97×\9cק×\99 ×©× ×\99×\99×\94 (ש×\9c×\90 ×ª×\94×\99×\94 ×\9c×\94×\9d ×©×\95×\9d ×\94שפע×\94), <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (×\9c×\90 ×\97×\95×\91×\94 ×\9c×\9bת×\95×\91 ×§×\95×\95×\99×\9d ×\9eפר×\99×\93×\99×\9d, × ×§×\95×\93ת×\99×\99×\9d ×\95Ö¾<kbd>Z</kbd>)\n:* ×ª×¡×\93×\99ר MediaWikiâ\80\8f, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* ×ª×¡×\93×\99ר ×\9eספר×\99 ×\9b×\9c×\9c×\99, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (×\9c×\90×\96×\95ר ×\96×\9e×\9f ×\90×\95פצ×\99×\95× ×\9c×\99 ×©×\9c <kbd>GMT</kbd>â\80\8f, <kbd dir=\"ltr\">+<var>##</var></kbd>, ×\90×\95 <kbd dir=\"ltr\">-<var>##</var></kbd> ×\90×\99×\9f ×\94שפע×\94)\n:* ×ª×¡×\93×\99ר EXIFâ\80\8f, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* ×ª×¡×\93×\99ר RFC 2822 (×\90פשר ×\9c×\94ש×\9e×\99×\98 ×\90ת ×\90×\96×\95ר ×\94×\96×\9e×\9f), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* ×ª×¡×\93×\99ר RFC 850 (×\90פשר ×\9c×\94ש×\9e×\99×\98 ×\90ת ×\90×\96×\95ר ×\94×\96×\9e×\9f), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* ×ª×¡×\93×\99ר C ctimeâ\80\8f, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* ×©× ×\99×\95ת ×\9e×\90×\96 1970-01-01T00:00:00Z ×\91ת×\95ר ×\9eספר ×©×\9c×\9a ×\91×\99×\9f 1 ×\9cÖ¾13 (×\9c×\90 ×\9b×\95×\9c×\9c <kbd>0</kbd>)\n:* ×\94×\9e×\97ר×\95×\96ת <kbd>now</kbd>",
+       "api-help-datatypes": "ק×\9c×\98 ×\9c×\9e×\93×\99×\94Ö¾×\95×\99ק×\99 ×¦×¨×\99×\9a ×\9c×\94×\99×\95ת ×\91ק×\99×\93×\95×\93 UTF-8 ×\9e× ×\95ר×\9e×\9c ×\91Ö¾NFC. ×\9e×\93×\99×\94Ö¾×\95×\99ק×\99 ×\99×\9b×\95×\9c×\94 ×\9cנס×\95ת ×\9c×\94×\9e×\99ר ×§×\9c×\98 ×\90×\97ר, ×\90×\91×\9c ×\96×\94 ×¢×\9c×\95×\9c ×\9c×\92ר×\95×\9d ×\9cפע×\95×\9c×\95ת ×\9eס×\95×\99×\9e×\95ת (×\9b×\92×\95×\9f [[Special:ApiHelp/edit|ער×\99×\9b×\95ת]] ×¢×\9d ×\91×\93×\99ק×\95ת MD5) ×\9c×\94×\99×\9bש×\9c.\n\n×\97×\9cק ×\9eס×\95×\92×\99 ×\94פר×\9e×\98ר×\99×\9d ×\91×\91קש×\95ת API ×\93×\95רש×\99×\9d ×\94ס×\91ר × ×\95סף:\n;×\91×\95×\9c×\99×\90× ×\99 (boolean)\n:פר×\9e×\98ר×\99×\9d ×\91×\95×\9c×\99×\90× ×\99×\99×\9d ×¢×\95×\91×\93×\99×\9d ×\9b×\9e×\95 ×ª×\99×\91×\95ת ×¡×\99×\9e×\95×\9f ×©×\9c HTML: ×\90×\9d ×\94פר×\9e×\98ר ×¦×\95×\99×\9f, ×\91×\9c×\99 ×§×©×¨ ×\9cער×\9a ×©×\9c×\95, ×\94×\95×\90 ×\90×\9eת (true). ×\91ש×\91×\99×\9c ×¢×¨×\9a ×©×§×¨ (false), ×\99ש ×\9c×\94ש×\9e×\99×\98 ×\90ת ×\94פר×\9e×\98ר ×\9c×\92×\9eר×\99.\n;×\97×\95ת×\9dÖ¾×\96×\9e×\9f (timestamp)\n:×\90פשר ×\9c×\9bת×\95×\91 ×\97×\95ת×\9e×\99Ö¾×\96×\9e×\9f ×\91×\9eספר ×ª×¡×\93×\99ר×\99×\9d. ×ª×\90ר×\99×\9a ×\95שע×\94 ×\9cפ×\99 ISO 8601 ×\94×\95×\90 ×\94×\93×\91ר ×\94×\9e×\95×\9e×\9cת. ×\9b×\9c ×\94×\96×\9e× ×\99×\9d ×\9eצ×\95×\99× ×\99×\9d ×\91Ö¾ UTC, ×\9c×\90 ×ª×\94×\99×\94 ×\94שפע×\94 ×\9cש×\95×\9d ×\90×\96×\95ר ×\96×\9e×\9f ×©×\99צ×\95×\99×\9f.\n:* ×ª×\90ר×\99×\9a ×\95שע×\94 ×\9cפ×\99 ISO 8601â\80\8f, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (×\9c×\90 ×\97×\95×\91×\94 ×\9c×\9bת×\95×\91 ×¤×\99ס×\95ק ×\95Ö¾<kbd>Z</kbd>)\n:* ×ª×\90ר×\99×\9a ×\95שע×\94 ×\9cפ×\99 ISO 8601 ×¢×\9d ×\97×\9cק×\99 ×©× ×\99×\99×\94 (ש×\9c×\90 ×ª×\94×\99×\94 ×\9c×\94×\9d ×©×\95×\9d ×\94שפע×\94), <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (×\9c×\90 ×\97×\95×\91×\94 ×\9c×\9bת×\95×\91 ×§×\95×\95×\99×\9d ×\9eפר×\99×\93×\99×\9d, × ×§×\95×\93ת×\99×\99×\9d ×\95Ö¾<kbd>Z</kbd>)\n:* ×ª×¡×\93×\99ר MediaWikiâ\80\8f, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* ×ª×¡×\93×\99ר ×\9eספר×\99 ×\9b×\9c×\9c×\99, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (×\9c×\90×\96×\95ר ×\96×\9e×\9f ×\90×\95פצ×\99×\95× ×\9c×\99 ×©×\9c <kbd>GMT</kbd>â\80\8f, <kbd dir=\"ltr\">+<var>##</var></kbd>, ×\90×\95 <kbd dir=\"ltr\">-<var>##</var></kbd> ×\90×\99×\9f ×\94שפע×\94)\n:* ×ª×¡×\93×\99ר EXIFâ\80\8f, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* ×ª×¡×\93×\99ר RFC 2822 (×\90פשר ×\9c×\94ש×\9e×\99×\98 ×\90ת ×\90×\96×\95ר ×\94×\96×\9e×\9f), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* ×ª×¡×\93×\99ר RFC 850 (×\90פשר ×\9c×\94ש×\9e×\99×\98 ×\90ת ×\90×\96×\95ר ×\94×\96×\9e×\9f), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* ×ª×¡×\93×\99ר C ctimeâ\80\8f, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* ×©× ×\99×\95ת ×\9e×\90×\96 1970-01-01T00:00:00Z ×\91ת×\95ר ×\9eספר ×©×\9c×\9a ×\91×\99×\9f 1 ×\9cÖ¾13 (×\9c×\90 ×\9b×\95×\9c×\9c <kbd>0</kbd>)\n:* ×\94×\9e×\97ר×\95×\96ת <kbd>now</kbd>\n;×\9eפר×\99×\93 ×¢×¨×\9b×\99×\9d ×\9eר×\95×\91×\99×\9d ×\97×\9c×\95פ×\99\n:פר×\9e×\98ר×\99×\9d ×©×\9c×\95ק×\97×\99×\9d ×¢×¨×\9b×\99×\9d ×\9eר×\95×\91×\99×\9d ×\91×\93ר×\9aÖ¾×\9b×\9c×\9c × ×©×\9c×\97×\99×\9d ×¢×\9d ×\94ער×\9b×\99×\9d ×\9e×\95פר×\93×\99×\9d ×\91×\90×\9eצע×\95ת ×ª×\95 ×\9eק×\9c, ×\9c×\9eש×\9c <kbd>param=value1|value2</kbd> ×\90×\95 <kbd>param=value1%7Cvalue2</kbd>. ×\90×\9d ×\94ער×\9a ×¦×¨×\99×\9a ×\9c×\94×\9b×\99×\9c ×\90ת ×ª×\95 ×\94×\9eק×\9c, ×\99ש ×\9c×\94שת×\9eש ×\91Ö¾U+001F (×\9eפר×\99×\93 ×\99×\97×\99×\93×\95ת) ×\91ת×\95ר ×\94×\9eפר×\99×\93 ''×\95×\92×\9d'' ×\9c×\94×\95ס×\99×£ ×\9cת×\97×\99×\9cת ×\94ער×\9a U+001F, ×\9c×\9eש×\9c <kbd>param=%1Fvalue1%1Fvalue2</kbd>.",
        "api-help-param-type-limit": "סוג: מספר שלם או <kbd>max</kbd>",
        "api-help-param-type-integer": "סוג: {{PLURAL:$1|1=מספר שלם|2=רשימת מספרים שלמים}}",
        "api-help-param-type-boolean": "סוג: בוליאני ([[Special:ApiHelp/main#main/datatypes|פרטים]])",
        "api-help-param-type-timestamp": "סוג: {{PLURAL:$1|חותם־זמן|רשימת חותמי־זמן}} ([[Special:ApiHelp/main#main/datatypes|תסדירים מורשים]])",
        "api-help-param-type-user": "סוג: {{PLURAL:$1|1=שם משתמש|2=רשימת שמות משתמשים}}",
-       "api-help-param-list": "{{PLURAL:$1|1=אחד מהערכים הבאים|2=ערכים (מופרדים באמצעות \"<kbd>{{!}}</kbd>\")}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=אחד מהערכים הבאים|2=ערכים (מופרדים באמצעות \"<kbd>{{!}}</kbd>\" או or [[Special:ApiHelp/main#main/datatypes|תו חלופי]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=חייב להיות ריק|יכול להיות ריק או $2}}",
        "api-help-param-limit": "מספר הפרמטרים לא יכול להיות גדול מ־$1.",
        "api-help-param-limit2": "המספר המרבי המותר הוא $1 (עבור בוטים – $2).",
        "api-help-param-integer-max": "ה{{PLURAL:$1|1=ערך לא יכול להיות גדול|2=ערכים לא יכולים להיות גדולים}} מ־$3.",
        "api-help-param-integer-minmax": "ה{{PLURAL:$1|1=ערך חייב|2=ערכים חייבים}} להיות בין $2 ל־$3.",
        "api-help-param-upload": "חייב להישלח (posted) בתור העלאת קובץ באמצעות multipart/form-data.",
-       "api-help-param-multi-separate": "הפרדה בין ערכים נעשית באמצעות <kbd>|</kbd>",
+       "api-help-param-multi-separate": "הפרדה בין ערכים נעשית באמצעות <kbd>|</kbd> או [[Special:ApiHelp/main#main/datatypes|תו חלופי]].",
        "api-help-param-multi-max": "מספר הערכים המרבי הוא {{PLURAL:$1|$1}} (עבור בוטים – {{PLURAL:$2|$2}}).",
        "api-help-param-default": "ברירת מחדל: $1",
        "api-help-param-default-empty": "ברירת מחדל: <span class=\"apihelp-empty\">(ריק)</span>",
index 7334fab..e0920f6 100644 (file)
@@ -6,7 +6,8 @@
                        "Tacsipacsi",
                        "ViDam",
                        "Macofe",
-                       "Wolf Rex"
+                       "Wolf Rex",
+                       "Dj"
                ]
        },
        "apihelp-main-param-action": "Milyen műveletet hajtson végre.",
@@ -42,6 +43,7 @@
        "apihelp-feedrecentchanges-param-hidepatrolled": "Ellenőrzött változtatások elrejtése.",
        "apihelp-login-param-name": "Szerkesztőnév.",
        "apihelp-login-param-password": "Jelszó.",
+       "apihelp-login-param-domain": "Tartomány (opcionális)",
        "apihelp-login-example-login": "Bejelentkezés.",
        "apihelp-logout-example-logout": "Aktuális felhasználó kijelentkeztetése.",
        "apihelp-mergehistory-description": "Laptörténetek egyesítése",
index 6a2f836..9786543 100644 (file)
        "apihelp-linkaccount-example-link": "Avvia il processo di collegamento ad un'utenza da <kbd>Example</kbd>.",
        "apihelp-login-description": "Accedi e ottieni i cookie di autenticazione.\n\nQuesta azione deve essere usata esclusivamente in combinazione con [[Special:BotPasswords]]; utilizzarla per l'accesso all'account principale è deprecato e può fallire senza preavviso. Per accedere in modo sicuro all'utenza principale, usa <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
        "apihelp-login-description-nobotpasswords": "Accedi e ottieni i cookies di autenticazione.\n\nQuesta azione è deprecata e può fallire senza preavviso. Per accedere in modo sicuro, usa [[Special:ApiHelp/clientlogin|action=clientlogin]].",
-       "apihelp-login-description-nonauthmanager": "Accedi e ottieni i cookie di autenticazione.\n\nIn caso di accesso riuscito, i cookies necessari saranno inclusi nella intestazioni di risposta HTTP. In caso di accesso fallito, ulteriori tentativi potrebbero essere limitati, in modo da contenere gli attacchi automatizzati per indovinare le password.",
        "apihelp-login-param-name": "Nome utente.",
        "apihelp-login-param-password": "Password.",
        "apihelp-login-param-domain": "Dominio (opzionale).",
        "apihelp-protect-param-pageid": "ID della pagina da (s)proteggere. Non può essere usato insieme con $1title.",
        "apihelp-protect-param-tags": "Modifica etichette da applicare all'elemento del registro delle protezioni.",
        "apihelp-protect-example-protect": "Proteggi una pagina.",
-       "apihelp-protect-example-unprotect": "Sproteggi una pagina impostando restrizione su <kbd>all</kbd>.",
+       "apihelp-protect-example-unprotect": "Sproteggi una pagina impostando restrizione su <kbd>all</kbd> (cioè a tutti è consentito intraprendere l'azione).",
        "apihelp-protect-example-unprotect2": "Sproteggi una pagina impostando nessuna restrizione.",
        "apihelp-purge-param-forcelinkupdate": "Aggiorna la tabella dei collegamenti.",
        "apihelp-purge-param-forcerecursivelinkupdate": "Aggiorna la tabella dei collegamenti per questa pagina, e per ogni pagina che usa questa pagina come template.",
        "apihelp-query+authmanagerinfo-description": "Recupera informazioni circa l'attuale stato di autenticazione.",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "Verifica se lo stato di autenticazione dell'utente attuale è sufficiente per la specifica operazione sensibile alla sicurezza.",
        "apihelp-query+authmanagerinfo-param-requestsfor": "Recupera informazioni circa le richieste di autenticazione necessarie per la specifica azione di autenticazione.",
-       "apihelp-query+filerepoinfo-example-login": "Recupera le richieste che possono essere utilizzate quando si inizia l'accesso.",
-       "apihelp-query+filerepoinfo-example-login-merged": "Recupera le richieste che possono essere utilizzate quando si inizia l'accesso, con i campi del modulo uniti.",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "Verificare se l'autenticazione è sufficiente per l'azione <kbd>foo</kbd>.",
+       "apihelp-query+authmanagerinfo-example-login": "Recupera le richieste che possono essere utilizzate quando si inizia l'accesso.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "Recupera le richieste che possono essere utilizzate quando si inizia l'accesso, con i campi del modulo uniti.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "Verificare se l'autenticazione è sufficiente per l'azione <kbd>foo</kbd>.",
        "apihelp-query+backlinks-description": "Trova tutte le pagine che puntano a quella specificata.",
        "apihelp-query+backlinks-param-namespace": "Il namespace da elencare.",
        "apihelp-query+backlinks-param-dir": "La direzione in cui elencare.",
        "api-help-param-type-boolean": "Tipo: booleano ([[Special:ApiHelp/main#main/datatypes|dettagli]])",
        "api-help-param-type-timestamp": "Tipo: {{PLURAL:$1|1=timestamp|2=elenco di timestamp}} ([[Special:ApiHelp/main#main/datatypes|formati consentiti]])",
        "api-help-param-type-user": "Tipo: {{PLURAL:$1|1=nome utente|2=elenco di nomi utente}}",
-       "api-help-param-list": "{{PLURAL:$1|1=Uno dei seguenti valori|2=Valori (separati da <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Uno dei seguenti valori|2=Valori (separati da <kbd>{{!}}</kbd> o [[Special:ApiHelp/main#main/datatypes|alternativa]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Deve essere vuoto|Può essere vuoto, o $2}}",
        "api-help-param-limit": "Non più di $1 consentito.",
        "api-help-param-limit2": "Non più di $1 ($2 per bot) consentito.",
        "api-help-param-integer-min": "{{PLURAL:$1|1=Il valore non deve essere inferiore|2=I valori non devono essere inferiori}} a $2.",
        "api-help-param-integer-max": "{{PLURAL:$1|1=Il valore non deve essere superiore|2=I valori non devono essere superiori}} a $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=Il valore deve essere compreso|2=I valori devono essere compresi}} tra $2 e $3.",
-       "api-help-param-multi-separate": "Separa i valori con <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Separa i valori con <kbd>|</kbd> o [[Special:ApiHelp/main#main/datatypes|alternativa]].",
        "api-help-param-multi-max": "Il numero massimo di valori è {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} per i bot).",
        "api-help-param-default": "Predefinito: $1",
        "api-help-param-default-empty": "Predefinito: <span class=\"apihelp-empty\">(vuoto)</span>",
        "api-help-examples": "{{PLURAL:$1|Esempio|Esempi}}:",
        "api-help-permissions": "{{PLURAL:$1|Permesso|Permessi}}:",
        "api-help-open-in-apisandbox": "<small>[apri in una sandbox]</small>",
-       "api-help-authmanager-general-usage": "La procedura generale per usare questo modulo Ã©:\n# Ottenere i campi disponibili da <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> con <kbd>amirequestsfor=$4</kbd>, e un token <kbd>$5</kbd> da <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>.\n# Mostra i campi all'utente e ottieni i dati che invia.\n# Esegui un post a questo modulo, fornendo <var>$1returnurl</var> e ogni campo rilevante.\n# Controlla <samp>status</samp> nella response.\n#* Se hai ricevuto <samp>PASS</samp> o <samp>FAIL</samp>, hai finito. L'operazione nel primo caso è andata a buon fine, nel secondo no.\n#* Se hai ricevuto <samp>UI</samp>, mostra i nuovi campi all'utente e ottieni i dati che invia. Esegui un post a questo modulo con <var>$1continue</var> e i campi rilevanti settati, quindi ripeti il punto 4.\n#* Se hai ricevuto <samp>REDIRECT</samp>, dirigi l'utente a <samp>redirecttarget</samp> e aspetta che ritorni a <var>$1returnurl</var>. A quel punto esegui un post a questo modulo con <var>$1continue</var> e ogni campo passato all'URL di ritorno, e ripeti il punto 4.\n#* Se hai ricevuto <samp>RESTART</samp>, vuol dire che l'autenticazione ha funzionato ma non abbiamo un account collegato. Potresti considerare questo caso come <samp>UI</samp> o come <samp>FAIL</samp>.",
+       "api-help-authmanager-general-usage": "La procedura generale per usare questo modulo Ã¨:\n# Ottenere i campi disponibili da <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> con <kbd>amirequestsfor=$4</kbd>, e un token <kbd>$5</kbd> da <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>.\n# Mostra i campi all'utente e ottieni i dati che invia.\n# Esegui un post a questo modulo, fornendo <var>$1returnurl</var> e ogni campo rilevante.\n# Controlla <samp>status</samp> nella response.\n#* Se hai ricevuto <samp>PASS</samp> o <samp>FAIL</samp>, hai finito. L'operazione nel primo caso è andata a buon fine, nel secondo no.\n#* Se hai ricevuto <samp>UI</samp>, mostra i nuovi campi all'utente e ottieni i dati che invia. Esegui un post a questo modulo con <var>$1continue</var> e i campi rilevanti settati, quindi ripeti il punto 4.\n#* Se hai ricevuto <samp>REDIRECT</samp>, dirigi l'utente a <samp>redirecttarget</samp> e aspetta che ritorni a <var>$1returnurl</var>. A quel punto esegui un post a questo modulo con <var>$1continue</var> e ogni campo passato all'URL di ritorno, e ripeti il punto 4.\n#* Se hai ricevuto <samp>RESTART</samp>, vuol dire che l'autenticazione ha funzionato ma non abbiamo un account collegato. Potresti considerare questo caso come <samp>UI</samp> o come <samp>FAIL</samp>.",
        "api-help-authmanagerhelper-messageformat": "Formato da utilizzare per per la restituzione dei messaggi.",
        "api-help-authmanagerhelper-preservestate": "Conserva lo stato da un precedente tentativo di accesso non riuscito, se possibile.",
        "api-help-authmanagerhelper-returnurl": "URL di ritorno per i flussi di autenticazione di terze parti, deve essere assoluto. E' necessario fornirlo, oppure va fornito <var>$1continue</var>.\n\nAlla ricezione di una risposta <samp>REDIRECT</samp>, in genere si apre un browser o una vista web all'URL specificato <samp>redirecttarget</samp> per un flusso di autenticazione di terze parti. Quando questo è completato, la terza parte invierà il browser o la vista web a questo URL. Dovresti estrarre qualsiasi parametro POST o della richiesta dall'URL e passarli come un request <var>$1continue</var> a questo modulo API.",
index e0eef1c..f3beff6 100644 (file)
@@ -8,7 +8,8 @@
                        "Mfuji",
                        "Otokoume",
                        "Sujiniku",
-                       "Macofe"
+                       "Macofe",
+                       "Suchichi02"
                ]
        },
        "apihelp-main-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/ the mediawiki-api-announce メーリングリスト]に参加してください。\n\n<strong>誤ったリクエスト:</strong> 誤ったリクエストが API に送られた場合、\"MediaWiki-API-Error\" HTTP ヘッダーが送信され、そのヘッダーの値と送り返されるエラーコードは同じ値にセットされます。より詳しい情報は [[mw:API:Errors_and_warnings|API: Errors and warnings]] を参照してください。\n\n<strong>テスト:</strong> API のリクエストのテストは、[[Special:ApiSandbox]]で簡単に行えます。",
        "apihelp-block-param-watchuser": "その利用者またはIPアドレスの利用者ページとトークページをウォッチします。",
        "apihelp-block-example-ip-simple": "IPアドレス <kbd>192.0.2.5</kbd> を <kbd>First strike<kbd> という理由で3日ブロックする",
        "apihelp-block-example-user-complex": "利用者 <kbd>Vandal</kbd> を <kbd>Vandalism</kbd> という理由で無期限ブロックし、新たなアカウント作成とメールの送信を禁止する。",
+       "apihelp-changeauthenticationdata-example-password": "現在の利用者のパスワードを <kbd>ExamplePassword</kbd> に変更する。",
        "apihelp-checktoken-description": "<kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd> のトークンの妥当性を確認します。",
        "apihelp-checktoken-param-type": "調べるトークンの種類。",
        "apihelp-checktoken-param-token": "調べるトークン。",
        "apihelp-checktoken-example-simple": "<kbd>csrf</kbd> トークンの妥当性を調べる。",
        "apihelp-clearhasmsg-description": "現在の利用者の <code>hasmsg</code> フラグを消去します。",
        "apihelp-clearhasmsg-example-1": "現在の利用者の <code>hasmsg</code> フラグを消去する。",
+       "apihelp-clientlogin-example-login": "利用者 <kbd>Example</kbd> としてのログイン処理をパスワード <kbd>ExamplePassword</kbd> で開始する",
        "apihelp-compare-description": "2つの版間の差分を取得します。\n\n\"from\" と \"to\" の両方の版番号、ページ名、もしくはページIDを渡す必要があります。",
        "apihelp-compare-param-fromtitle": "比較する1つ目のページ名。",
        "apihelp-compare-param-fromid": "比較する1つ目のページID。",
        "apihelp-feedcontributions-param-deletedonly": "削除された投稿記録のみ表示します。",
        "apihelp-feedcontributions-param-toponly": "最新版の編集のみ表示します。",
        "apihelp-feedcontributions-param-newonly": "ページ作成を伴う編集のみを表示します。",
+       "apihelp-feedcontributions-param-hideminor": "細部の編集を非表示",
        "apihelp-feedcontributions-param-showsizediff": "版間のサイズの増減を表示する。",
        "apihelp-feedcontributions-example-simple": "利用者 <kbd>Example</kbd> の投稿記録を取得する。",
        "apihelp-feedrecentchanges-description": "最近の更新フィードを返します。",
        "apihelp-query+backlinks-param-pageid": "検索するページID。<var>$1title</var>とは同時に使用できません。",
        "apihelp-query+backlinks-param-namespace": "列挙する名前空間。",
        "apihelp-query+backlinks-param-dir": "一覧表示する方向。",
+       "apihelp-query+backlinks-param-limit": "返すページの総数。<var>$1redirect</var> を有効化した場合は、各レベルに対し個別にlimitが適用されます (つまり、最大で 2 * <var>$1limit</var> 件の結果が返されます)。",
        "apihelp-query+backlinks-example-simple": "<kbd>Main page</kbd> へのリンクを表示する。",
        "apihelp-query+backlinks-example-generator": "<kbd>Main page</kbd> にリンクしているページの情報を取得する。",
        "apihelp-query+blocks-description": "ブロックされた利用者とIPアドレスを一覧表示します。",
        "apihelp-query+deletedrevs-param-end": "列挙の終点となるタイムスタンプ。",
        "apihelp-query+deletedrevs-param-from": "列挙の始点となるページ名。",
        "apihelp-query+deletedrevs-param-to": "列挙の終点となるページ名。",
+       "apihelp-query+deletedrevs-param-tag": "このタグが付与された版のみを一覧表示する。",
+       "apihelp-query+deletedrevs-param-user": "この利用者による版のみを一覧表示する。",
        "apihelp-query+deletedrevs-param-excludeuser": "この利用者による版を一覧表示しない。",
        "apihelp-query+deletedrevs-param-namespace": "この名前空間に含まれるページのみを一覧表示します。",
        "apihelp-query+deletedrevs-param-limit": "一覧表示する版の最大量。",
        "apihelp-query+info-description": "ページの基本的な情報を取得します。",
        "apihelp-query+info-param-prop": "追加で取得するプロパティ:",
        "apihelp-query+info-paramvalue-prop-protection": "それぞれのページの保護レベルを一覧表示する。",
+       "apihelp-query+info-param-token": "代わりに [[Special:ApiHelp/query+tokens|action=query&meta=tokens]] を使用してください。",
        "apihelp-query+info-example-simple": "<kbd>Main Page</kbd> に関する情報を取得する。",
        "apihelp-query+iwbacklinks-param-prefix": "インターウィキ接頭辞。",
        "apihelp-query+iwbacklinks-param-title": "検索するウィキ間リンク。<var>$1blprefix</var>と同時に使用しなければなりません。",
        "apihelp-query+watchlist-param-prop": "追加で取得するプロパティ:",
        "apihelp-query+watchlist-paramvalue-prop-ids": "版IDとページIDを追加します。",
        "apihelp-query+watchlist-paramvalue-prop-title": "ページ名を追加します。",
+       "apihelp-query+watchlist-paramvalue-prop-flags": "編集のフラグを追加します。",
        "apihelp-query+watchlist-paramvalue-prop-comment": "編集のコメントを追加します。",
        "apihelp-query+watchlist-paramvalue-prop-parsedcomment": "編集の構文解析されたコメントを追加します。",
        "apihelp-query+watchlist-paramvalue-prop-timestamp": "編集のタイムスタンプを追加します。",
        "api-help-param-deprecated": "廃止予定です。",
        "api-help-param-required": "このパラメーターは必須です。",
        "api-help-datatypes-header": "データ型",
-       "api-help-param-list": "{{PLURAL:$1|1=値 (次の値のいずれか1つ)|2=値 (<kbd>{{!}}</kbd>で区切る)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=å\80¤ (次ã\81®å\80¤ã\81®ã\81\84ã\81\9aã\82\8cã\81\8b\81¤)|2=å\80¤ (<kbd>{{!}}</kbd>ã\82\82ã\81\97ã\81\8fã\81¯[[Special:ApiHelp/main#main/datatypes|å\88¥ã\81®æ\96\87å­\97å\88\97]]ã\81§å\8cºå\88\87ã\82\8b)}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=空欄にしてください|空欄にするか、または $2}}",
        "api-help-param-integer-min": "{{PLURAL:$1|値}}は $2 以上にしてください。",
        "api-help-param-integer-max": "{{PLURAL:$1|値}}は $3 以下にしてください。",
        "api-help-param-integer-minmax": "{{PLURAL:$1|値}}は $2 以上 $3 以下にしてください。",
        "api-help-param-upload": "multipart/form-data 形式でファイルをアップロードしてください。",
-       "api-help-param-multi-separate": "複数の値は <kbd>|</kbd> で区切ってください。",
+       "api-help-param-multi-separate": "è¤\87æ\95°ã\81®å\80¤ã\81¯ <kbd>|</kbd> ã\82\82ã\81\97ã\81\8fã\81¯[[Special:ApiHelp/main#main/datatypes|代ã\82\8fã\82\8aã\81®æ\96\87å­\97]]ã\81§å\8cºå\88\87ã\81£ã\81¦ã\81\8fã\81 ã\81\95ã\81\84ã\80\82",
        "api-help-param-multi-max": "値の最大値は {{PLURAL:$1|$1}} (ボットの場合は {{PLURAL:$2|$2}}) です。",
        "api-help-param-default": "既定値: $1",
        "api-help-param-default-empty": "既定値: <span class=\"apihelp-empty\">(空)</span>",
index fc9370b..5ae6c87 100644 (file)
@@ -41,7 +41,7 @@
        "apihelp-block-example-ip-simple": "IP <kbd>192.0.2.5</kbd>에 대해 <kbd>First strike</kbd>라는 이유로 3일 간 차단하기",
        "apihelp-block-example-user-complex": "사용자 <kbd>Vandal</kbd>을 <kbd>Vandalism</kbd>이라는 이유로 무기한 차단하며 계정 생성 및 이메일 발송을 막기",
        "apihelp-changeauthenticationdata-description": "현재 사용자의 인증 데이터를 변경합니다.",
-       "apihelp-changeauthenticationdata-example-password": "í\98\84ì\9e¬ ì\82¬ì\9a©ì\9e\90ì\9d\98 ë¹\84ë°\80ë²\88í\98¸ë¥¼ <kbd>ExamplePassword</kbd>ë¡\9c ë³\80ê²½í\95\98는 것을 시도합니다.",
+       "apihelp-changeauthenticationdata-example-password": "í\98\84ì\9e¬ ì\82¬ì\9a©ì\9e\90ì\9d\98 ë¹\84ë°\80ë²\88í\98¸ë¥¼ <kbd>ExamplePassword</kbd>ë¡\9c ë°\94꾸는 것을 시도합니다.",
        "apihelp-checktoken-description": "<kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>의 토큰의 유효성을 확인합니다.",
        "apihelp-checktoken-param-type": "테스트되는 토큰의 종류.",
        "apihelp-checktoken-param-token": "테스트할 토큰",
        "apihelp-emailuser-param-subject": "제목 헤더.",
        "apihelp-emailuser-param-text": "메일 본문.",
        "apihelp-emailuser-param-ccme": "자신에게 메일의 복사본을 보냅니다.",
-       "apihelp-emailuser-example-email": "<kbd>WikiSysop</kbd> 사용자에게 텍스트 <kbd>콘텐츠</kbd>로 이메일을 보냅니다.",
+       "apihelp-emailuser-example-email": "<kbd>WikiSysop</kbd> 사용자에게 텍스트 <kbd>Content</kbd>로 이메일을 보냅니다.",
        "apihelp-expandtemplates-description": "모든 틀을 위키텍스트로 확장.",
        "apihelp-expandtemplates-param-title": "문서 제목",
        "apihelp-expandtemplates-param-text": "변환할 위키텍스트.",
        "apihelp-paraminfo-description": "API 모듈의 정보를 가져옵니다.",
        "apihelp-paraminfo-param-helpformat": "도움말 문자열 포맷.",
        "apihelp-parse-param-summary": "구문 분석할 요약입니다.",
+       "apihelp-parse-paramvalue-prop-text": "위키텍스트의 구문 분석된 텍스트를 제공합니다.",
+       "apihelp-parse-paramvalue-prop-langlinks": "구문 분석된 위키텍스트의 언어 링크를 제공합니다.",
+       "apihelp-parse-paramvalue-prop-categories": "구문 분석된 위키텍스트의 분류를 제공합니다.",
        "apihelp-parse-paramvalue-prop-categorieshtml": "분류의 HTML 버전을 제공합니다.",
        "apihelp-parse-paramvalue-prop-links": "구문 분석된 위키텍스트의 내부 링크를 제공합니다.",
        "apihelp-parse-paramvalue-prop-templates": "구문 분석된 위키텍스트의 틀을 제공합니다.",
        "apihelp-parse-paramvalue-prop-sections": "구문 분석된 위키텍스트의 문단을 제공합니다.",
        "apihelp-parse-paramvalue-prop-revid": "구문 분석된 페이지의 판 ID를 추가합니다.",
        "apihelp-parse-paramvalue-prop-displaytitle": "구문 분석된 위키텍스트의 제목을 추가합니다.",
+       "apihelp-parse-paramvalue-prop-headitems": "<span class=\"apihelp-deprecated\">사용되지 않습니다.</span> 문서의 <code>&lt;head&gt;</code> 안에 넣을 항목을 제공합니다.",
+       "apihelp-parse-paramvalue-prop-headhtml": "문서의 구문 분석된 <code>&lt;head&gt;</code>를 제공합니다.",
+       "apihelp-parse-paramvalue-prop-modules": "문서에 사용되는 ResourceLoader 모듈을 제공합니다. 불러오려면, <code>mw.loader.using()</code>을 사용하세요. <kbd>jsconfigvars</kbd> 또는 <kbd>encodedjsconfigvars</kbd>는 <kbd>modules</kbd>와 함께 요청해야 합니다.",
+       "apihelp-parse-paramvalue-prop-jsconfigvars": "문서에 특화된 자바스크립트 구성 변수를 제공합니다. 적용하려면 <code>mw.config.set()</code>을 사용하세요.",
        "apihelp-parse-paramvalue-prop-iwlinks": "구문 분석된 위키텍스트의 인터위키 링크를 제공합니다.",
        "apihelp-parse-paramvalue-prop-wikitext": "구문 분석된 위키텍스트 원문을 제공합니다.",
        "apihelp-parse-paramvalue-prop-properties": "구문 분석된 위키텍스트에 정의된 다양한 속성을 제공합니다.",
        "apihelp-query+alldeletedrevisions-param-excludeuser": "이 사용자에 대한 판을 나열하지 않습니다.",
        "apihelp-query+alldeletedrevisions-param-namespace": "이 이름공간의 문서만 나열합니다.",
        "apihelp-query+allfileusages-paramvalue-prop-title": "파일의 제목을 추가합니다.",
+       "apihelp-query+allfileusages-param-limit": "반환할 총 항목 수입니다.",
        "apihelp-query+allfileusages-example-unique": "고유한 파일 제목을 나열합니다.",
        "apihelp-query+allfileusages-example-unique-generator": "모든 파일 제목을 가져오되, 존재하지 않는 항목을 표시합니다.",
        "apihelp-query+allfileusages-example-generator": "파일을 포함하는 문서를 가져옵니다.",
        "apihelp-query+allusers-description": "등록된 모든 사용자를 열거합니다.",
        "apihelp-query+allusers-param-dir": "정렬 방향.",
        "apihelp-query+allusers-param-prop": "포함할 정보:",
+       "apihelp-query+allusers-paramvalue-prop-editcount": "사용자의 편집 수를 추가합니다.",
        "apihelp-query+allusers-param-witheditsonly": "편집을 한 사용자만 나열합니다.",
        "apihelp-query+allusers-example-Y": "<kbd>Y</kbd>로 시작하는 사용자를 나열합니다.",
        "apihelp-query+authmanagerinfo-description": "현재의 인증 상태에 대한 정보를 검색합니다.",
        "apihelp-query+blocks-param-ids": "나열할 차단 ID 목록 (선택 사항).",
        "apihelp-query+blocks-param-users": "검색할 사용자 목록 (선택 사항).",
        "apihelp-query+blocks-param-prop": "얻고자 하는 속성:",
+       "apihelp-query+blocks-paramvalue-prop-id": "블록의 ID를 추가합니다.",
        "apihelp-query+blocks-paramvalue-prop-user": "차단된 사용자의 사용자 이름을 추가합니다.",
        "apihelp-query+blocks-paramvalue-prop-userid": "차단된 사용자의 사용자 ID를 추가합니다.",
        "apihelp-query+blocks-paramvalue-prop-by": "차단을 수행하는 사용자의 사용자 이름을 추가합니다.",
        "apihelp-query+exturlusage-param-namespace": "열거할 문서 이름공간.",
        "apihelp-query+exturlusage-param-limit": "반환할 문서 수.",
        "apihelp-query+filearchive-description": "삭제된 모든 파일을 순서대로 열거합니다.",
+       "apihelp-query+filearchive-paramvalue-prop-sha1": "그림에 대한 SHA-1 해시를 추가합니다.",
+       "apihelp-query+filearchive-paramvalue-prop-user": "그림 판을 올린 사용자를 추가합니다.",
+       "apihelp-query+filearchive-paramvalue-prop-description": "그림 판의 설명을 추가합니다.",
        "apihelp-query+filearchive-paramvalue-prop-mime": "그림의 MIME를 추가합니다.",
        "apihelp-query+filearchive-paramvalue-prop-mediatype": "그림의 미디어 유형을 추가합니다.",
        "apihelp-query+filearchive-paramvalue-prop-metadata": "그림의 버전에 대한 Exif 메타데이터를 나열합니다.",
        "apihelp-query+fileusage-param-namespace": "이 이름공간의 문서만 포함합니다.",
        "apihelp-query+fileusage-param-limit": "반환할 항목 수.",
        "apihelp-query+fileusage-param-show": "이 기준을 충족하는 항목만 표시합니다:\n;redirect:넘겨주기만 표시합니다.\n;!redirect:넘겨주기가 아닌 항목만 표시합니다.",
+       "apihelp-query+imageinfo-paramvalue-prop-timestamp": "업로드된 판에 대한 타임스탬프를 추가합니다.",
+       "apihelp-query+imageinfo-paramvalue-prop-sha1": "파일에 대한 SHA-1 해시를 추가합니다.",
        "apihelp-query+imageinfo-paramvalue-prop-mediatype": "파일의 미디어 유형을 추가합니다.",
        "apihelp-query+imageinfo-param-urlheight": "$1urlwidth와 유사합니다.",
        "apihelp-query+imageinfo-example-simple": "[[:File:Albert Einstein Head.jpg]]의 현재 판에 대한 정보를 가져옵니다.",
        "apihelp-query+linkshere-param-namespace": "이 이름공간의 문서만 포함합니다.",
        "apihelp-query+linkshere-param-limit": "반환할 항목 수.",
        "apihelp-query+linkshere-param-show": "이 기준을 충족하는 항목만 표시합니다:\n;redirect:넘겨주기만 표시합니다.\n;!redirect:넘겨주기가 아닌 항목만 표시합니다.",
+       "apihelp-query+logevents-paramvalue-prop-ids": "로그 이벤트의 ID를 추가합니다.",
+       "apihelp-query+logevents-paramvalue-prop-type": "로그 이벤트의 유형을 추가합니다.",
        "apihelp-query+pagepropnames-param-limit": "반환할 이름의 최대 수.",
        "apihelp-query+pageswithprop-param-prop": "포함할 정보:",
        "apihelp-query+pageswithprop-paramvalue-prop-ids": "페이지 ID를 추가합니다.",
        "apihelp-query+recentchanges-param-prop": "추가 정보를 포함합니다:",
        "apihelp-query+recentchanges-paramvalue-prop-user": "편집에 임할 사용자를 추가하고 IP인 경우 태그합니다.",
        "apihelp-query+recentchanges-paramvalue-prop-userid": "편집에 임할 사용자를 추가합니다.",
+       "apihelp-query+recentchanges-paramvalue-prop-flags": "편집에 대한 플래그를 추가합니다.",
        "apihelp-query+revisions+base-paramvalue-prop-size": "판의 길이. (바이트)",
        "apihelp-query+revisions+base-paramvalue-prop-sha1": "판의 SHA-1 (base 16).",
        "apihelp-query+revisions+base-paramvalue-prop-contentmodel": "판의 콘텐츠 모델 ID.",
        "apihelp-query+siteinfo-example-simple": "사이트 정보를 가져옵니다.",
        "apihelp-query+siteinfo-example-interwiki": "로컬 인터위키 접두사 목록을 가져옵니다.",
        "apihelp-query+tags-param-limit": "나열할 태그의 최대 수.",
+       "apihelp-query+tags-paramvalue-prop-name": "태그의 이름을 추가합니다.",
        "apihelp-query+tags-paramvalue-prop-description": "태그의 설명을 추가합니다.",
        "apihelp-query+tags-paramvalue-prop-hitcount": "판의 수와 이 판을 가진 로그 엔트리를 추가합니다.",
        "apihelp-query+templates-param-limit": "반환할 틀 수.",
        "apihelp-query+watchlist-param-excludeuser": "이 사용자의 변경 사항을 나열하지 않습니다.",
        "apihelp-query+watchlist-paramvalue-prop-ids": "판 ID와 페이지 ID를 추가합니다.",
        "apihelp-query+watchlist-paramvalue-prop-title": "문서의 제목을 추가합니다.",
+       "apihelp-query+watchlist-paramvalue-prop-flags": "편집에 대한 플래그를 추가합니다.",
        "apihelp-query+watchlist-paramvalue-prop-loginfo": "적절한 곳에 로그 정보를 추가합니다.",
        "apihelp-removeauthenticationdata-description": "현재 사용자의 인증 데이터를 제거합니다.",
        "apihelp-revisiondelete-description": "판을 삭제하거나 되살립니다.",
        "api-help-license-noname": "라이선스: [[$1|링크 참조]]",
        "api-help-license-unknown": "라이선스: <span class=\"apihelp-unknown\">알 수 없음</span>",
        "api-help-parameters": "{{PLURAL:$1|변수}}:",
-       "api-help-param-deprecated": "사용 중지됨.",
+       "api-help-param-deprecated": "사용되지 않습니다.",
        "api-help-param-required": "이 변수는 필수 입력 사항입니다.",
        "api-help-datatypes-header": "데이터 유형",
-       "api-help-datatypes": "API 요청 내 몇몇 매개변수형에 대해 더 자세히 설명해보겠습니다:\n;boolean\n:Boolean 매개변수들은 HTML 체크박스처럼 동작합니다: 만약 매개변수가 지정되었다면, 값에 상관없이 참의 값으로 여겨집니다. 거짓값은 매개변수 전체를 생략하세요.\n;timestamp\n:타임스탬프들은 여러 형식으로 표현될 수 있으나 ISO 8601 날짜와 시간이 추천됩니다. 모든 시간은 UTC이어야 하며, 포함된 시간대는 모두 무시됩니다.\n:* ISO 8601 날짜와 시간, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (구두점과 <kbd>Z</kbd>는 선택입니다.)\n:* ISO 8601 날짜와 시간과 (무시되는) 소수 초, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (대시, 콜론과 <kbd>Z</kbd>는 선택입니다.)\n:* 미디어위키 형식, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* 일반적인 수 형식 <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (<kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, 또는 <kbd>-<var>##</var></kbd>와 같은 선택적 시간대는 무시됩니다)\n:*RFC 2822 형식 (시간대는 생략될 수 있음), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 850 형식 (시간대는 생략될 수 있음), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* C ctime 형식, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* 1부터 13자리까지의 숫자로 표현된 1970-01-01T00:00:00Z 부터 흐른 시간(초) (<kbd>0</kbd>을 제외)\n:* 문자열 <kbd>now</kbd>",
+       "api-help-datatypes": "API 요청 내 몇몇 매개변수형에 대해 더 자세히 설명해보겠습니다:\n;boolean\n:Boolean 매개변수들은 HTML 체크박스처럼 동작합니다: 만약 매개변수가 지정되었다면, 값에 상관없이 참의 값으로 여겨집니다. 거짓값은 매개변수 전체를 생략하세요.\n;timestamp\n:타임스탬프들은 여러 형식으로 표현될 수 있으나 ISO 8601 날짜와 시간이 추천됩니다. 모든 시간은 UTC이어야 하며, 포함된 시간대는 모두 무시됩니다.\n:* ISO 8601 날짜와 시간, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (구두점과 <kbd>Z</kbd>는 선택입니다.)\n:* ISO 8601 날짜와 시간과 (무시되는) 소수 초, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (대시, 콜론과 <kbd>Z</kbd>는 선택입니다.)\n:* 미디어위키 형식, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* 일반적인 수 형식 <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (<kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, 또는 <kbd>-<var>##</var></kbd>와 같은 선택적 시간대는 무시됩니다)\n:*RFC 2822 형식 (시간대는 생략될 수 있음), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 850 형식 (시간대는 생략될 수 있음), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* C ctime 형식, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* 1부터 13자리까지의 숫자로 표현된 1970-01-01T00:00:00Z부터 흐른 시간(초) (<kbd>0</kbd>을 제외)\n:* 문자열 <kbd>now</kbd>",
        "api-help-param-type-limit": "유형: 정수 또는 <kbd>max</kbd>",
        "api-help-param-type-integer": "유형: {{PLURAL:$1|1=정수|2=정수 목록}}",
        "api-help-param-type-boolean": "유형: 부울 ([[Special:ApiHelp/main#main/datatypes|자세한 정보]])",
-       "api-help-param-list": "{{PLURAL:$1|1=하나의 값|2=값 (\"{{!}}\"로 구분)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=다음 값 중 하나|2=값 (<kbd>{{!}}</kbd>로 구분)}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=비어 있어야 함|비어 있을 수 있거나 $2}}",
        "api-help-param-limit": "$1 초과는 허용되지 않습니다.",
        "api-help-param-limit2": "$1 초과는 허용되지 않습니다. (봇의 경우 $2)",
        "api-help-permissions-granted-to": "{{PLURAL:$1|다음 그룹에 부여됨}}: $2",
        "api-help-open-in-apisandbox": "<small>[연습장에서 열기]</small>",
        "api-help-authmanagerhelper-messageformat": "반환 메시지에 사용할 형식.",
-       "api-credits": "API 개발자:\n* Roan Kattouw (선임 개발자, 2007년 9월–2009년)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Yuri Astrakhan (초기 개발자, 선임 개발자 2006년 9월~2007년 9월)\n* Brad Jorsch (선임 개발자 2013년–현재)\n\n당신의 의견이나 제안, 질문은 mediawiki-api@lists.wikimedia.org 로 보내주시거나,\nhttps://phabricator.wikimedia.org/ 에 버그 신고를 해 주시기 바랍니다.."
+       "api-credits": "API 개발자:\n* Roan Kattouw (선임 개발자, 2007년 9월–2009년)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Yuri Astrakhan (초기 개발자, 선임 개발자 2006년 9월~2007년 9월)\n* Brad Jorsch (선임 개발자 2013년–현재)\n\n당신의 의견이나 제안, 질문은 mediawiki-api@lists.wikimedia.org 로 보내주시거나,\nhttps://phabricator.wikimedia.org/ 에 버그 보고를 해 주시기 바랍니다."
 }
index 2376c7b..7db9665 100644 (file)
        "apihelp-imagerotate-param-rotation": "Öm wi vill Jrahd sulle de Bellder noh de Uhr drieh wääde?",
        "apihelp-imagerotate-example-simple": "Drieh de <kbd>Dattei:Beijschpell.png</kbd> öm <kbd>90</kbd> Jrahd.",
        "apihelp-imagerotate-example-generator": "Drieh alle Bellder en dä <kbd>Saachjropp:Ömdriehje</kbd> öm <kbd>180</kbd> Jrahd.",
-       "apihelp-import-param-summary": "Zersammefaßong för der Empohrt.",
+       "apihelp-import-param-summary": "Zersammefaßong för der Empohrt för et Logbohch.",
        "apihelp-import-param-xml": "Donn en Dattei em <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Extensible Markup Language\">XML</i>-Fommaht huhjahde.",
        "apihelp-import-param-interwikisource": "För et Empottehre us enem andere Wikki: Dat Wikki vun woh der Empohrt kumme sull.",
        "apihelp-import-param-interwikipage": "För et Empottehre us enem andere Wikki: De Sigg zom Empottehre.",
        "apihelp-managetags-example-delete": "Schmiiß de Makkehrong mem Nahme „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">vandlaism</kbd>“ fott mem Jrond „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Misspelt</kbd>“.",
        "apihelp-managetags-example-activate": "Donn en Makkehrong aktevehre mem Nahme „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">spam</kbd>“ mem Jrond „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">For use in edit patrolling</kbd>“.",
        "apihelp-managetags-example-deactivate": "Donn en Makkehrong mem Nahme „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">spam</kbd>“ nit mieh aktihv maache, mem Jrond „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">For use in edit patrolling</kbd>“.",
+       "apihelp-mergehistory-description": "Väsjohne fun Sigge zosamme lähje.",
+       "apihelp-mergehistory-param-from": "De Övverschreff vun dä Sigg, vun däh de verjange Väsjohne zesamme jelaat wähde sulle. Kam_mer nit zesamme met <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1fromid</var> bruche.",
+       "apihelp-mergehistory-param-fromid": "De Kännong vun dä Sigg, vun däh de verjange Väsjohne zesamme jelaat wähde sulle. Kam_mer nit zesamme met <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1fromid</var> bruche.",
+       "apihelp-mergehistory-param-to": "De Övverschreff vun dä Sigg, wohen de verjange Väsjohne zesamme jelaat wähde sulle. Kam_mer nit zesamme met <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1toid</var> bruche.",
+       "apihelp-mergehistory-param-toid": "De Kännong vun dä Sigg, wohen de verjange Väsjohne zesamme jelaat wähde sulle. Kam_mer nit zesamme met <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1toid</var> bruche.",
        "apihelp-mergehistory-param-reason": "Der Jrond för et Zesammelähje vun dä älldere Väsjohne.",
        "apihelp-mergehistory-example-merge": "Donn de jannze älldere Väsjohne vun dä Sigg „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Oldpage</kbd>“ met dä Sigg „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Newpage</kbd>“ zesammelähje.",
        "apihelp-mergehistory-example-merge-timestamp": "Donn de älldere Väsjohne vun dä Sigg „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Oldpage</kbd>“ bes zom <kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">2015-12-31T04:37:41Z</kbd> met dä Sigg „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Newpage</kbd>“ zesammelähje.",
        "apihelp-parse-paramvalue-prop-headitems": "Jitt de Saacher för enn der <code>&lt;head&gt;</code> vun dä Sigg ze donn.",
        "apihelp-parse-paramvalue-prop-modules": "Jitt dem <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Delivery system in MediaWiki for the optimized run-time loading and managing of modules\">ResourceLoader</i> sing Moduhle uß, di en dä Sigg jebruch wähde. Äntwehder „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">jsconfigvars</kbd>“ udder „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">encodedjsconfigvars</kbd>“ moß mer met „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">modules</kbd>“ zesamme aanforrdere.",
        "apihelp-parse-paramvalue-prop-jsconfigvars": "Livvert de Varrejahble vun dä Ennschtällonge vum JavaSkrep, di äxtra för heh di Sigg enjeschtallt sin.",
+       "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "Livvert de Varrejahble vun dä Ennschtällonge vum JavaSkrep, di äxtra för heh di Sigg enjeschtallt sin als ene Täx em <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"JavaScript Object Notation\">JSON</i>-Fommaht.",
        "apihelp-parse-paramvalue-prop-iwlinks": "Jitt de Engewikkilengks em jepahßde Wikkitäx uß.",
        "apihelp-parse-paramvalue-prop-wikitext": "Jitt de der ojinahl Wikkitäx us, dä jepahß woode es.",
        "apihelp-parse-paramvalue-prop-properties": "Jitt devärse Eijeschafte uß, di em jepahßde Wikkitäx faßjelaat woode sen.",
        "apihelp-parse-param-section": "Donn blohß der Ennhalld vun däm Affschnett met dä Nommer paase.\n\nWann „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">new</kbd>“ enjejovve es, donn dä Täx <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1text</var> un de Övverschreff <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1sectiontitle</var> paase, wi wänn_enne neuje Affschnett en dä Sigg derbei köhm.\n\nDä Parramehter „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">new</kbd>“ es blohß zohjelohße, wann och <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">text</var> aanjejovve es.",
        "apihelp-parse-param-sectiontitle": "De Övverschreff för dä neuje Afschnet, wann <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">section</var> = <kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">new</kbd> es.\n\nAnders wi beim Beärbeide vun dä Sigg weed dä Parramehter nit dorsch de <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">summary</var> ußjetuusch, wann hä fottjelohße udder läddesch es.",
        "apihelp-parse-param-disablelimitreport": "Jiff keine Bereesch vum Vüürbereijde zom Paase (der „<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\">NewPP limit report</i>“) mem Paaser singe Dahte zosamme uß.",
+       "apihelp-parse-param-disablepp": "Nämm <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1disablelimitreport</var> schtatt dämm.",
        "apihelp-parse-param-disableeditsection": "Donn de Lenks för Affschnedde ze änndere en de Ußjahbe vum Paaser eruß lohße.",
+       "apihelp-parse-param-disabletidy": "Donn et <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"HyperText Markup Language\">HTML</i> vun dä Ußjahbe nit oprühme, för e Beijschpell met <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"correct and cleans up HTML and XML documents fixing markup errors and upgrading legacy code\">tidy</i>.",
        "apihelp-parse-param-disabletoc": "Donn et Ennhaldsverzeijscheneß en de Ußjahbe vottlohße.",
        "apihelp-parse-example-page": "Donn en Sigg pahse.",
        "apihelp-parse-example-text": "Donn Wikkitäx pahse.",
        "apihelp-protect-param-reason": "Der Jrond för et Schöze udder Freijävve.",
        "apihelp-protect-param-tags": "Donn de Makehronge aanpaße, dat se för dä Enndraach em Logbohch vum Sigge Schöze jehühre.",
        "apihelp-protect-param-cascade": "Donn en Schotz-Kaskahd zohlohße, alsu ene Schoz för ennjeföhschte Schablohne un upjerohfe Bellder vun dä Sigg. Deiht nix, wann keine von dä aanjejovve Zoote Schoz en Kaskahd zohlöht.",
+       "apihelp-protect-param-watchlist": "Donn di Sigg ohne Bedengonge op däm aktoälle Metmaacher sing Oppaßleß udder nemm se druß fott, donn de Enschtällonge nämme, udder donn de Oppaßleß jaa nit verändere.",
        "apihelp-protect-example-protect": "Donn en Sigg schöze.",
+       "apihelp-protect-example-unprotect": "Donn en Sigg nit mih schöze un doh för saz de Beschrängkonge op <kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">all</kbd>“. <!--  https://phabricator.wikimedia.org/T141367 -->",
+       "apihelp-protect-example-unprotect2": "Donn dä Schoz för en Sigg ophävve, un doh för kein Beschrängkonge säze.",
        "apihelp-purge-param-forcelinkupdate": "Bräng de Tabälle met de lengks obb ene neue Schtand.",
        "apihelp-purge-example-simple": "Donn fö de Sigge „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Main Page</kbd>“ un „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">API</kbd>“ de zweschejeschpeijscherte Väsjohn fottschmiiße.",
        "apihelp-purge-example-generator": "Donn fö de eezte zehn Sigge em Schtanndadd_Appachtemang de zweschejeschpeijscherte Väsjohn fottschmiiße.",
        "apihelp-query+allredirects-param-to": "De Övverschreff vun dä Sigg, woh et Zälle ophühre sull.",
        "apihelp-query+allredirects-param-prefix": "Söhk not Sigge, di esu aanfange.",
        "apihelp-query+allredirects-param-unique": "Zeijsch blohß de ongerscheidlijje Zihl_Sigg. Kam_mer nit zesamme met „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1prop=ids|fragment|interwiki</code>“ bruche. Wam_mer et als ene  Jenerahtor bruche deiht, kritt mer Zihlsiggge anschtatt vun Quällesigge.",
+       "apihelp-query+allredirects-param-prop": "Wat för en Aanjahbe ennschlehße:",
+       "apihelp-query+allredirects-paramvalue-prop-ids": "Deiht de Kännonge vun dä Ömleijdongssigg derbei. Kam_mer nit zersamme met „< var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1unique</var>“ bruche.",
        "apihelp-query+allredirects-paramvalue-prop-title": "Deiht dä Ömleijdong ehr Övverschreff derbei.",
        "apihelp-query+allredirects-param-namespace": "Dat Appachtemang zom opzälle.",
        "apihelp-query+allredirects-param-limit": "Wi vill sulle överhoup aanjezeisch wääde?",
        "apihelp-query+allrevisions-param-generatetitles": "Wann als ene  Jenerahtor enjesaz, brängk dat Övverschreffte un kein Kännonge vun Väsjohne.",
        "apihelp-query+allrevisions-example-user": "Donn de läzde fuffzisch Beijdrähsch vum Metmaacher „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Example</kbd>“ opleßte.",
        "apihelp-query+allrevisions-example-ns-main": "Donn de eezde fuffzisch Väsjohne em Houp-Appachemang opleßte.",
+       "apihelp-query+mystashedfiles-description": "Holl en Leß vun dem aktoälle Metmaacher singe upload stash.",
        "apihelp-query+mystashedfiles-param-prop": "Wat för en Aanjahbe holle för di Datteije.",
        "apihelp-query+mystashedfiles-param-limit": "Wi vill Datteije holle?",
        "apihelp-query+alltransclusions-param-from": "De Övverschreff vun dä ennjeföhschte Sigg, woh de Leß medd aanfange sull.",
        "apihelp-query+allusers-param-group": "Donn blohß Metmaacher uß dä aanjejovve Jroppe enschlehße.",
        "apihelp-query+allusers-param-excludegroup": "Donn keine Metmaacher uß dä aanjejovve Jroppe enschlehße.",
        "apihelp-query+allusers-param-prop": "Wat för en Aanjahbe med enzschlehße:",
+       "apihelp-query+allusers-paramvalue-prop-implicitgroups": "Donn alle Jroppe opleste, woh dä Metmaacher automattesch dren es.",
        "apihelp-query+allusers-paramvalue-prop-rights": "De Rääschde vn däm Memaacher.",
        "apihelp-query+allusers-paramvalue-prop-editcount": "Donn de Aanzahl Änderonge derbei, di dä Metmaacher em Wikki jemaat hät.",
+       "apihelp-query+allusers-paramvalue-prop-registration": "Wann aanjejovve, deihd dat heh et Dattom un de Zigg derbei, wann dä Metmaacher sesch aanjemälld hät, wann müjjelech. Dat kann läddesch blihve.",
        "apihelp-query+allusers-param-limit": "Wi vill Nahme Metmaacher sulle mer krijje?",
        "apihelp-query+allusers-param-witheditsonly": "Blohß Metmahcher, di och ens jät verändert han.",
        "apihelp-query+allusers-param-activeusers": "Donn blohß Metmaacher opleßte, di  {{PLURAL:$1|der läzde Daach|en de läzde $1 Dääsch|keine läzde Daach}} aktihf wohre.",
        "apihelp-query+iwbacklinks-example-simple": "Holl Sigge, di op „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">[[wikibooks:Test]]</code>“ verlengke.",
        "apihelp-query+iwbacklinks-example-generator": "Holl Ennfommazjuhne övver Sigge, di op „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">[[wikibooks:Test]]</code>“ verlengke.",
        "apihelp-query+iwlinks-description": "Jiff alle Engerwikki_Lengks vun de aanjejovve Sigge uß.",
+       "apihelp-query+iwlinks-paramvalue-prop-url": "Deiht dä kumplätte <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Uniform Ressource Locator\">URL</i> derbei.",
        "apihelp-query+iwlinks-param-limit": "Wi vill Engerwikki_Lengks zem ußjävve?",
        "apihelp-query+iwlinks-param-prefix": "Jiff blohß de Engerwikki_Lengks uß, di dermet aanfange.",
+       "apihelp-query+iwlinks-param-title": "Dä Engerwiki Lengk för dernoh ze söhke. Moß met <var>$1prefix</var> zesamme jebruch wähde.",
        "apihelp-query+iwlinks-param-dir": "En wälsche Reihjefollsch opleßte.",
        "apihelp-query+langbacklinks-param-lang": "Schprohch för dä Schprohche_Lengk.",
        "apihelp-query+langbacklinks-param-title": "Der Schprohche_Lengk för noh ze söhke. Moß zersamme met <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1lang</code> jebruch wähde.",
        "apihelp-query+langbacklinks-param-limit": "Wi vill Sigge ensjesammp zem ußjävve?",
+       "apihelp-query+langbacklinks-param-prop": "Wat för en Prijoretähte holle:",
+       "apihelp-query+langbacklinks-paramvalue-prop-lllang": "Deiht de Kännong för de Schprohch för dä Schprohchelengk derbei.",
+       "apihelp-query+langbacklinks-paramvalue-prop-lltitle": "Deiht de Övverschreff för dä Schprohchelengk derbei.",
        "apihelp-query+langbacklinks-param-dir": "En wälsche Reihjefollsch opleßte.",
        "apihelp-query+langbacklinks-example-simple": "Holl Sigge, di op „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">[[:fr:Test]]</code>“ verlengke.",
        "apihelp-query+langbacklinks-example-generator": "Holl Ennfommazjuhne övver Sigge, di op „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">[[:fr:Test]]</code>“ verlengke.",
        "apihelp-query+langlinks-description": "Jiff alle Schprohche_Lengks vun de aanjejovve Sigge uß.",
        "apihelp-query+langlinks-param-limit": "Wi vill Schprohche_Lengks holle?",
+       "apihelp-query+langlinks-paramvalue-prop-url": "Deiht dä kumplätte <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Uniform Ressource Locator\">URL</i> derbei.",
+       "apihelp-query+langlinks-paramvalue-prop-autonym": "Deiht dä Nahme vun de Moterschprohch derbei.",
        "apihelp-query+langlinks-param-lang": "Donn blohß de Schprohche_Lengks met däm aanjejovve Schprohche_Köözel.",
        "apihelp-query+langlinks-param-dir": "En wälsche Reihjefollsch opleßte.",
        "apihelp-query+links-description": "Jiff alle Lengks vun de aanjejovve Sigge uß.",
+       "apihelp-query+links-param-namespace": "Zeijsch blohß de Lengks en dä Appachtemangs.",
        "apihelp-query+links-param-limit": "Wi vill Lengks ußjävve?",
        "apihelp-query+links-param-titles": "Donn blohß e Lengks of heh di Övverschreffte opleßte. Dadd es johd, öm eruß ze fenge ovv en en beschtemmpte Sigg op ene beschtemmpte Övverschreff verlengk es.",
        "apihelp-query+links-param-dir": "En wälsche Reihjefollsch opleßte.",
+       "apihelp-query+links-example-simple": "Holl de Lengks vun dä Sigg <kbd>Main Page</kbd>",
        "apihelp-query+linkshere-description": "Fengk alle Sigge, di op de aanjejovve Sigge lengke.",
        "apihelp-query+linkshere-param-prop": "Wat för en Eijeschaffte holle:",
        "apihelp-query+linkshere-paramvalue-prop-pageid": "Page ID of each page.",
        "apihelp-query+linkshere-example-simple": "Holl en Leß vun Sigge, di op de Sigg „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">[[Main Page]]</code>“ lengke donn.",
        "apihelp-query+linkshere-example-generator": "Holl Ennfommazjuhne övver Sigge, di op de Sigg „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">[[Main Page]]</code>“ lengke.",
        "apihelp-query+logevents-description": "Holl Enndrähsch us de Logböhscher.",
+       "apihelp-query+logevents-param-prop": "Wat för en Eijeschaffte holle:",
        "apihelp-query+logevents-param-type": "Söhk blohß heh di Zood Enndrähsch us de Logböhscher.",
        "apihelp-query+logevents-param-start": "Et Dattom un de Zigg vun woh aff opjezallt wähde sull.",
        "apihelp-query+logevents-param-end": "Dattum un Uhrzigg, bes wann opzälle.",
        "apihelp-query+pageprops-description": "Jitt devärse Eijeschafte uß, di em Ennhald vun dä Sigg faßjelaat wohde sen.",
        "apihelp-query+pageprops-example-simple": "Holl de Eijeschaffte för di Sigge „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Main Page</kbd>“ un „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">MediaWiki</kbd>“.",
        "apihelp-query+pageswithprop-description": "Donn alle Sigge met bechtemmpte Sigge_Eijeschaff opleßte.",
+       "apihelp-query+pageswithprop-param-prop": "Wat för en Aanjahbe ennschlehße:",
        "apihelp-query+pageswithprop-paramvalue-prop-ids": "Deiht de Kännong vun de Sigge derbei.",
+       "apihelp-query+pageswithprop-paramvalue-prop-title": "Donn de Övverschrevv un de Kännong för di Sigg derbei.",
        "apihelp-query+pageswithprop-paramvalue-prop-value": "Deiht der Wäät för de Eijeschaff vun dä Sigg derbei.",
        "apihelp-query+pageswithprop-param-limit": "De jrüüßte Zahl Sigge för ußzejävve.",
        "apihelp-query+pageswithprop-param-dir": "En wälsche Reihjefollsch opleßte.",
        "apihelp-query+recentchanges-description": "Donn de  neußte Änderonge opleßte.",
        "apihelp-query+recentchanges-param-start": "Et Dattom un de Zigg vun woh aff opjezallt wähde sull.",
        "apihelp-query+recentchanges-param-end": "Dattum un Uhrzigg, bes wann opzälle.",
+       "apihelp-query+recentchanges-param-namespace": "Donn de Änderonge blohß us de aanjejovve Appachtemans nämme.",
        "apihelp-query+recentchanges-param-user": "Donn blohß Änderonge vun heh däm Metmaacher opleßte.",
        "apihelp-query+recentchanges-param-excludeuser": "Donn kein Änderonge vun heh däm Metmaacher opleßte.",
        "apihelp-query+recentchanges-param-tag": "Donn blohß Änderonge met heh dä Makkehrong opleßte.",
+       "apihelp-query+recentchanges-param-prop": "Donn zohsäzlejje Aanjahbe ennschlehße:",
+       "apihelp-query+recentchanges-paramvalue-prop-flags": "Deihd de Makkehronge vun dä Änderong derbei.",
+       "apihelp-query+recentchanges-paramvalue-prop-timestamp": "Deihd et Dattom un de Uhrzigg vun dä Änderong derbei.",
+       "apihelp-query+recentchanges-paramvalue-prop-title": "Deihd de neuje Övverschreff noh dä Änderong derbei.",
        "apihelp-query+recentchanges-paramvalue-prop-tags": "Donn de Makkehronge för dä Enndraach opleßte.",
        "apihelp-query+recentchanges-paramvalue-prop-sha1": "Donn de Pröhvsom för di Enndrähsch oplesßte, di met enne Väsjohn zesamme hange.",
        "apihelp-query+recentchanges-param-token": "Nemm „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>“ schtatt dämm.",
        "apihelp-query+revisions+base-paramvalue-prop-userid": "Däm Metmaacher sing Kännong, dä di Väsjohn aanjelaat hät.",
        "apihelp-query+revisions+base-paramvalue-prop-size": "Der Ömvang en <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Bytes</i> vun dä Väsjohn.",
        "apihelp-query+revisions+base-paramvalue-prop-sha1": "De <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"secure hash algorithm\">SHA-1 (base 16)</i> Prööfsomm vun dä Väsjohn.",
-       "apihelp-query+revisions+base-paramvalue-prop-comment": "De Aanmärkong vum Metmaacher för di äsjohn.",
+       "apihelp-query+revisions+base-paramvalue-prop-comment": "De Aanmärkong vum Metmaacher för di Väsjohn.",
+       "apihelp-query+revisions+base-paramvalue-prop-parsedcomment": "De jepaaste Aanmärkong vum Metmaacher för di Väsjohn.",
        "apihelp-query+revisions+base-paramvalue-prop-content": "Der Täx vun dä Väsjohn.",
        "apihelp-query+revisions+base-paramvalue-prop-tags": "Makkehronge vun dä Väsjohn.",
        "apihelp-query+revisions+base-param-limit": "Wi vill Väsjohne sulle ußjejovve wähde?",
        "apihelp-query+search-param-what": "Wat för en Aat ze Söhke?",
        "apihelp-query+search-param-info": "Wat för en Metta_Dahte ußzejävve.",
        "apihelp-query+search-param-prop": "Wat för en Eijeschaffte holle:",
+       "apihelp-query+search-paramvalue-prop-wordcount": "Deiht de Aanzahl Wööter en dä Sigg derbeij.",
+       "apihelp-query+search-paramvalue-prop-timestamp": "Deihd et Dattum un de Uhrzigg derbei, wann di Sigg et läz veränndert wohd.",
+       "apihelp-query+search-paramvalue-prop-redirecttitle": "Deiht dä zopaß Ömleijdong ehr Övverschreff derbei.",
        "apihelp-query+search-param-limit": "Wi vill Sigge ensjesamp ußjävve?",
        "apihelp-query+search-param-interwiki": "Donn de Engerwiki Lengks met ußjävve beim Söhke, wann_er doh sin.",
        "apihelp-query+search-example-simple": "Söhk noh „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">meaning</kbd>“.",
        "apihelp-query+search-example-text": "Söhk en Täxte noh „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">meaning</kbd>“.",
        "apihelp-query+search-example-generator": "Holl anjahbe övver di Sigge, di jefonge wähde beim söhke noh \n„<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">meaning</kbd>“",
        "apihelp-query+siteinfo-description": "Jiff alljemeine Ennfommazjuhne övver heh di ẞaid_uß.",
+       "apihelp-query+siteinfo-param-prop": "Wat för en Ennfommazjuhne holle:",
+       "apihelp-query+siteinfo-paramvalue-prop-general": "Alljemeine Aanjabe zom Süßtehm.",
+       "apihelp-query+siteinfo-paramvalue-prop-statistics": "Jivv Schtatistike vum Wikki uß.",
+       "apihelp-query+siteinfo-paramvalue-prop-variables": "Jid en Leß med de verföhschbahre Kännonge us.",
        "apihelp-query+siteinfo-param-filteriw": "Donn blohß de Enndrähsch för heh et Wikki udder blohß de Enndrähsch för ußerhallef en di Leß.",
        "apihelp-query+siteinfo-param-showalldb": "Donn alle ẞööver för de Dahtebangke opleßte, nit blohß di am mihßte hengerher sin.",
        "apihelp-query+siteinfo-param-numberingroup": "Donn de Aanzahl Metmaacher en de Jroppe vun Metmaacher opleßte.",
        "apihelp-query+usercontribs-paramvalue-prop-ids": "Donn de Kännong för jehde Sigg un jehe Väsjohn derbei.",
        "apihelp-query+usercontribs-paramvalue-prop-title": "Donn de Övverschrevv un de Kännong för et Appachtemang derbei.",
        "apihelp-query+usercontribs-paramvalue-prop-timestamp": "Deihd et Dattom un de Uhrzigg vun dä Änderong derbei.",
+       "apihelp-query+usercontribs-paramvalue-prop-comment": "Deihd de Zosammefaßong vun dä Änderong derbei.",
+       "apihelp-query+usercontribs-paramvalue-prop-parsedcomment": "Deihd de jepaaste Zosammefaßong vun dä Änderong derbei.",
        "apihelp-query+usercontribs-paramvalue-prop-size": "Deihd de neuje Jrühße noh dä Änderong derbei.",
        "apihelp-query+usercontribs-paramvalue-prop-sizediff": "Deihd de Änderong vun dä Jrühße vun dä Änderong derbei.",
        "apihelp-query+usercontribs-paramvalue-prop-flags": "Deihd de Makkehronge vun dä Änderong derbei.",
+       "apihelp-query+usercontribs-paramvalue-prop-patrolled": "Nohjelohrte Änderonge makkehre.",
        "apihelp-query+usercontribs-paramvalue-prop-tags": "Donn de Makkehronge vun dä Änderong opleßte.",
        "apihelp-query+usercontribs-param-tag": "Donn blohß Väsjohne met heh dä Makehrong opleßte.",
        "apihelp-query+usercontribs-param-toponly": "Bloß Änderonge aanzeije, woh de neußte Väsjohn beij eruß kohm.",
        "apihelp-query+userinfo-example-data": "Holl zohsäzlejje Aanjahbe övver dä aktoälle Metmaacher.",
        "apihelp-query+users-description": "Holl Aanjahbe övver en Leß vun Metmaacher.",
        "apihelp-query+users-param-prop": "Wat för en Aanjahbe med enzschlehße:",
+       "apihelp-query+users-paramvalue-prop-groups": "Donn alle Jroppe opleßte, woh all de Metmaacher dren sin.",
+       "apihelp-query+users-paramvalue-prop-implicitgroups": "Donn alle Jroppe opleßte, woh ene Metmaacher aotomattesch dren es.",
        "apihelp-query+users-paramvalue-prop-rights": "Donn alle Rääschte opleßte, di alle Metmaacher han.",
        "apihelp-query+users-paramvalue-prop-editcount": "Donn däm Metmaacher sing Aanzahl Ännderonge derbeij.",
        "apihelp-query+users-paramvalue-prop-registration": "Donn et Dattom vun dämm Metmaacher singe eetze Aanmäldong derbei.",
        "apihelp-query+watchlist-paramvalue-prop-flags": "Deihd de Makkehronge vun dä Änderong derbei.",
        "apihelp-query+watchlist-paramvalue-prop-user": "Deiht dä Metmaacher derbei, dä di Änderong jemaat hät.",
        "apihelp-query+watchlist-paramvalue-prop-userid": "Deiht de kännong vn äm Metmaacher derbei, dä di Änderong jemaat hät.",
+       "apihelp-query+watchlist-paramvalue-prop-comment": "Deihd de Zosammefaßong vun dä Änderong derbei.",
+       "apihelp-query+watchlist-paramvalue-prop-parsedcomment": "Deihd de jepaaste Zosammefaßong vun dä Änderong derbei.",
        "apihelp-query+watchlist-paramvalue-prop-timestamp": "Deihd et Dattom un de Uhrzigg vun dä Änderong derbei.",
+       "apihelp-query+watchlist-paramvalue-prop-patrol": "Makkehrt de nohjelohrte Ännderonge.",
+       "apihelp-query+watchlist-paramvalue-prop-sizes": "Deiht de vörrijje un de neuje Läng vun dä Sigg derbei.",
        "apihelp-query+watchlist-param-type": "Wat för en Änderonge aanzeije:",
        "apihelp-query+watchlist-paramvalue-type-edit": "Jewöhnlejje Ännderonge aan Sigge.",
        "apihelp-query+watchlist-paramvalue-type-external": "Änderonge vun Ußerhallef.",
        "apihelp-query+watchlistraw-param-namespace": "Donn blohß Sigge en heh däm Appachtemang opleßte.",
        "apihelp-query+watchlistraw-param-limit": "Wi vell Äjehbneße ennsjesammp pro Oprohv ußjejovve wähde sulle.",
        "apihelp-query+watchlistraw-param-prop": "Wat för en zohsäzlejje Eijeschaffte holle:",
+       "apihelp-query+watchlistraw-param-dir": "En wälsche Reihjefollsch opleßte.",
        "apihelp-query+watchlistraw-example-simple": "Donn alle Sigge uß dem aktälle Metmaacher sing Oppaßleß opleßte.",
        "apihelp-removeauthenticationdata-example-simple": "Versöhk dem aktoäle Metmaacher sing Dahte för <kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">FooAuthenticationRequest</kbd> fott ze nämme.",
        "apihelp-resetpassword-example-email": "Schegg en <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> mem Passwod neu säze aan alle Matmaacher met dä Addräß <kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">user@example.com</kbd>.",
        "apihelp-tag-example-log": "Donn de Makkehrong „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">spam</kbd>“ vun dämm Enndrahch met dä Kännong „<kbd>123</kbd>“ em Logbohch fott nämme un als Jrond draaach „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Wrongly applied</kbd>“ enn.",
        "apihelp-unblock-description": "Don en Schpärr för ene Metmaacher ophävve.",
        "apihelp-unblock-param-reason": "Der Jrond för de Schpärr opzehävve.",
+       "apihelp-unblock-param-tags": "Donn de Makehronge änndere, di för dä Enndraach em Logbohch vum Schpärre jesaz wähde sulle.",
        "apihelp-undelete-param-title": "De Övverschreff vun dä Sigg zom zerök holle.",
        "apihelp-undelete-param-reason": "Der Jrond för et Zerök holle.",
        "apihelp-undelete-param-tags": "Donn de Makehronge aanpaße, dat se för dä Enndraach em Logbohch vum Sigge fott Schmihße jehühre.",
        "api-help-param-default": "Schtandatt: $1",
        "api-help-param-default-empty": "Schtandatt: <span class=\"apihelp-empty\">(läddesch)</span>",
        "api-help-param-disabled-in-miser-mode": "Dadd es wäje em [[mw:Manual:$wgMiserMode|miser mode]] affjeschalldt.",
-       "api-help-param-limited-in-miser-mode": "<strong>opjepaß:</strong> Weil der  [[mw:Manual:$wgMiserMode|miser mode]] enjeschalld es, künne heh winnijer wi <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1limit</var> Äjehpneße ußjejejovve wähde, vör em Wigger_Mache. En Jränzfäll künne et Noll sin.",
+       "api-help-param-limited-in-miser-mode": "<strong>Opjepaß:</strong> Weil der [[mw:Manual:$wgMiserMode|miser mode]] enjeschalld es, künne heh winnijer wi <var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1limit</var> Äjehpneße ußjejejovve wähde, vör em Wigger_Mache. En Jränzfäll künne et Noll sin.",
        "api-help-param-direction": "En wälsche Reihjefollsch opleßte:\n;<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">newer</code>:De Ählsde et eez. Opjepaß: „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1start</code>“ moß fröhjer sin wi „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1end</code>“.\n;<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">older</code>:De Neuste et eez, der Schtanndatt. Opjepaß: „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1start</code>“ moß schpääder sin wi „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1end</code>“.",
        "api-help-param-continue": "Wann mih ze holle es, nemm dat för wigger ze maache.",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(nix drövver bikannt)</span>",
index 12ec506..be09192 100644 (file)
@@ -21,6 +21,9 @@
        "apihelp-emailuser-param-target": "Bikarhênerê ku e-name jê rê bê şandin.",
        "apihelp-expandtemplates-param-title": "Sernavê rûpelê.",
        "apihelp-feedcontributions-param-deletedonly": "Tenê beşdariyên jêbirî nîşan bide.",
+       "apihelp-feedcontributions-param-hideminor": "Guherandinên biçûk veşêre.",
+       "apihelp-feedrecentchanges-param-hideminor": "Guherandinên biçûk veşêre.",
+       "apihelp-feedrecentchanges-param-hidebots": "Guherandinên botan veşêre.",
        "apihelp-feedrecentchanges-example-simple": "Guherandinên dawî nîşan bide.",
        "apihelp-feedrecentchanges-example-30days": "Guherandinên dawî yên 30 rojan nîşan bide",
        "apihelp-filerevert-param-comment": "Şîroveyê bar bike.",
@@ -35,5 +38,7 @@
        "apihelp-parse-example-summary": "Kurteyekê analîz bike",
        "apihelp-protect-description": "Asta parastinê ya rûpelekê biguherîne.",
        "apihelp-protect-example-protect": "Rûpelekê biparêze.",
-       "apihelp-tag-param-reason": "Sedemê bo guherandinê."
+       "apihelp-query+alllinks-paramvalue-prop-title": "Sernavê girêdanê lê zêde dike.",
+       "apihelp-tag-param-reason": "Sedemê bo guherandinê.",
+       "api-help-parameters": "{{PLURAL:$1|Parametre}}:"
 }
diff --git a/includes/api/i18n/lij.json b/includes/api/i18n/lij.json
new file mode 100644 (file)
index 0000000..8b07da9
--- /dev/null
@@ -0,0 +1,174 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Giromin Cangiaxo"
+               ]
+       },
+       "apihelp-main-param-action": "Açion da compî.",
+       "apihelp-main-param-format": "Formato de l'output.",
+       "apihelp-main-param-assert": "Veifica che l'utente o l'agge effetoòu l'accesso se s'è impostou <kbd>user</kbd>, ò ch'o l'agge i permissi di bot se s'è impostou <kbd>bot</kbd>.",
+       "apihelp-main-param-requestid": "Tutti i valoî fornii saian incruxi inta risposta. Porieivan ese doeuviæ pe distingue e receste.",
+       "apihelp-main-param-servedby": "Inciodi into risultou o nomme de l'host ch'o l'ha servio a recesta.",
+       "apihelp-main-param-curtimestamp": "Inciodi into risultou o timestamp attoâ.",
+       "apihelp-block-description": "Blocca un utente.",
+       "apihelp-block-param-user": "Nomme utente, adresso IP o range di IP da bloccâ.",
+       "apihelp-block-param-expiry": "Tempo de scadença. O poeu ese relativo (presempio, <kbd>5 months</kbd> o <kbd>2 weeks</kbd>) ò assoluo (presempio <kbd>2014-09-18T12:34:56Z</kbd>). Se impostou a <kbd>infinite</kbd>, <kbd>indefinite</kbd> ò <kbd>never</kbd>, o blòcco o no descaziâ mai.",
+       "apihelp-block-param-reason": "Raxon do blòcco.",
+       "apihelp-block-param-anononly": "Blocca solo che i utenti non registræ (saiv'a dî disattiva i contributi anonnimi da questo adresso IP).",
+       "apihelp-block-param-nocreate": "Impedisci a creaçion de utençe.",
+       "apihelp-block-param-autoblock": "Blocca aotomaticamente l'urtimo adreçço IP doeuviou da l'utente e i succescivi co-i quæ tentan l'accesso",
+       "apihelp-block-param-hidename": "Ascondi o nomme utente da-o registro di blocchi (Ghe voeu i permissi de <code>hideuser</code>).",
+       "apihelp-block-param-reblock": "Se l'utente o l'è za bloccou, sorvescrive o blocco existente.",
+       "apihelp-block-param-watchuser": "Oserva a paggina utente e e paggine de discuscion utente de l'utente ò de l'adresso IP.",
+       "apihelp-block-example-ip-simple": "Blocca l'adresso IP <kbd>192.0.2.5</kbd> pe trei giorni con motivaçion <kbd>First strike</kbd>.",
+       "apihelp-block-example-user-complex": "Blocca l'utente <kbd>Vandal</kbd> a tempo indeterminou con motivaçion <kbd>Vandalism</kbd>, e impediscighe a creaçion de noeuve utençe e l'invio de e-mail.",
+       "apihelp-changeauthenticationdata-description": "Modificâ i dæti d'aotenticaçion pe l'utente corente.",
+       "apihelp-changeauthenticationdata-example-password": "Tentativo de modificâ a password de l'utente corente a <kbd>ExamplePassword</kbd>.",
+       "apihelp-checktoken-description": "Veifica a validitæ de 'n token da <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>.",
+       "apihelp-checktoken-param-type": "Tipo de token in corso de test.",
+       "apihelp-checktoken-param-token": "Token da testâ.",
+       "apihelp-checktoken-param-maxtokenage": "Mascima etæ consentia pe-o token, in segondi.",
+       "apihelp-checktoken-example-simple": "Veifica a validitæ de 'n token <kbd>csrf</kbd>.",
+       "apihelp-clearhasmsg-description": "Scassa o flag <code>hasmsg</code> pe l'utente corente.",
+       "apihelp-clearhasmsg-example-1": "Scassa o flag <code>hasmsg</code> pe l'utente corente.",
+       "apihelp-clientlogin-description": "Accedi a-o wiki doeuviando o flusso interattivo.",
+       "apihelp-clientlogin-example-login": "Avvia o processo d'accesso a-a wiki comme utente <kbd>Example</kbd> con password <kbd>ExamplePassword</kbd>.",
+       "apihelp-clientlogin-example-login2": "Continnoa l'accesso doppo una risposta de l'<samp>UI</samp> pe l'aotenticaçion a doî fattoî, fornindo un <var>OATHToken</var> de <kbd>987654</kbd>.",
+       "apihelp-compare-description": "Otegni e differençe tra 2 paggine.\n\nUn nummero de revixon, o tittolo de 'na paggina, ò un ID de paggina o dev'ese indicou segge pe-o \"da\" che pe-o \"a\".",
+       "apihelp-compare-param-fromtitle": "Primmo tittolo da confrontâ.",
+       "apihelp-compare-param-fromid": "Primo ID de paggina da confrontâ.",
+       "apihelp-compare-param-fromrev": "Primma revixon da confrontâ.",
+       "apihelp-compare-param-totitle": "Segondo tittolo da confrontâ.",
+       "apihelp-compare-param-toid": "Segondo ID de paggina da confrontâ.",
+       "apihelp-compare-param-torev": "Segonda revixon da confrontâ.",
+       "apihelp-compare-example-1": "Crea un diff tra revixon 1 e revixon 2.",
+       "apihelp-createaccount-description": "Crea una noeuva utença.",
+       "apihelp-createaccount-param-preservestate": "Se <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> o l'ha restituto true pe <samp>hasprimarypreservedstate</samp>, e receste contrssegnæ comme <samp>primary-required</samp> dovieivan ese omisse. Se invece o l'ha restituio un valô non voeuo pe <samp>preservedusername</samp>, quello nomme utente o dev'ese doeuviou pe-o parammetro <var>username</var>.",
+       "apihelp-createaccount-example-create": "Avvia o processo de creaçion d'utente <kbd>Example</kbd> con password <kbd>ExamplePassword</kbd>.",
+       "apihelp-createaccount-param-name": "Nomme utente",
+       "apihelp-createaccount-param-password": "Password (a saiâ ignorâ se l'è impostou <var>$1mailpassword</var>).",
+       "apihelp-createaccount-param-domain": "Dominnio pe l'aotenticaçion esterna (opçionâ).",
+       "apihelp-createaccount-param-email": "Adresso Email de l'utente (opçionâ).",
+       "apihelp-createaccount-param-realname": "Nomme veo de l'utente (opçionâ).",
+       "apihelp-createaccount-param-mailpassword": "Se impostou insce 'n qualonque valô, una password random (caxoâ) a saiâ inviâ a l'utente.",
+       "apihelp-createaccount-param-reason": "Raxon facortativa da creaçion de l'utença da insei inti registri.",
+       "apihelp-createaccount-param-language": "Codiçe de lengua da impostâ comme predefinia pe l'utente (opçionâ, pe difetto a l'è a lengua do contegnuo).",
+       "apihelp-createaccount-example-pass": "Crea l'utente <kbd>testuser</kbd> con password <kbd>test123</kbd>.",
+       "apihelp-createaccount-example-mail": "Crea l'utente <kbd>testmailuser</kbd> e mandighe via e-mail una password generâ abrettio.",
+       "apihelp-delete-description": "Scassa 'na paggina",
+       "apihelp-delete-param-title": "Tittolo da paggina che se dexîa eliminâ. O no poeu vese doeuviou insemme a <var>$1pageid</var>.",
+       "apihelp-delete-param-pageid": "ID de paggina da paggina da scassâ. O no poeu vese doeuviou insemme con <var>$1title</var>.",
+       "apihelp-delete-param-reason": "Raxon da scassatua. S'a no saiâ indicâ, saiâ doeuviou 'na raxon generâ aotomaticamente.",
+       "apihelp-delete-param-watch": "O l'azonze a paggina a-a lista di oservæ speciali de l'utente corente.",
+       "apihelp-delete-param-unwatch": "O rimoeuve a pagina da-a lista di oservæ speciali de l'utente corente.",
+       "apihelp-delete-param-oldimage": "O nomme da vegia inmaggine da scassâ, comme fornia da [[Special:ApiHelp/query+imageinfo|action=query&prop=imageinfo&iiprop=archivename]].",
+       "apihelp-delete-example-simple": "Scassa <kbd>Main Page</kbd>.",
+       "apihelp-delete-example-reason": "Scassa a <kbd>Main Page</kbd> con motivaçion <kbd>Preparing for move</kbd>.",
+       "apihelp-disabled-description": "Questo modulo o l'è stæto disabilitou.",
+       "apihelp-edit-description": "Crea e modifica paggine.",
+       "apihelp-edit-param-title": "Tittolo da paggina da modificâ. O no poeu vese doeuviou insemme a <var>$1pageid</var>.",
+       "apihelp-edit-param-pageid": "ID de paggina da paggina da modificâ. O no poeu ese doeuviou insemme a <var>$1title</var>.",
+       "apihelp-edit-param-section": "Nummero de seçion. <kbd>0</kbd> pe-a seçion de d'ato, <kbd>new</kbd> pe 'na noeuva seçion.",
+       "apihelp-edit-param-sectiontitle": "O tittolo pe 'na noeuva seçion.",
+       "apihelp-edit-param-text": "Contegnuo da paggina.",
+       "apihelp-edit-param-summary": "Ogetto da modiffica. E ascì tittolo da seçion se $1sezione=new e $1sectiontitle o no l'è impostou.",
+       "apihelp-edit-param-tags": "Cangia i tag da apricâ a-a revixon.",
+       "apihelp-edit-param-minor": "Cangiamento menô.",
+       "apihelp-edit-param-notminor": "Cangiamento non-menô.",
+       "apihelp-edit-param-bot": "Marca sta modiffica comme bot.",
+       "apihelp-edit-param-createonly": "No modificâ a paggina s'a l'existe za.",
+       "apihelp-edit-param-nocreate": "O genera un errô se a paggina a no l'existe.",
+       "apihelp-edit-param-watch": "O l'azonze a paggina a-a lista di oservæ speciali de l'utente corente.",
+       "apihelp-edit-param-unwatch": "O rimoeuve a pagina da-a lista di oservæ speciali de l'utente corente.",
+       "apihelp-edit-param-redirect": "Resciorvi aotomaticamente i rimandi.",
+       "apihelp-edit-param-contentmodel": "Modello de contegnuo di noeuvi contegnui.",
+       "apihelp-edit-param-token": "O token o dev'ese delongo inviou comme urtimo parammetro, ò aomeno doppo o parametro $1text.",
+       "apihelp-edit-example-edit": "Modiffica 'na paggina.",
+       "apihelp-edit-example-prepend": "Antepon-i <kbd>_&#95;NOTOC_&#95;</kbd> a 'na paggina.",
+       "apihelp-emailuser-description": "Manda 'n'e-mail a 'n utente.",
+       "apihelp-emailuser-param-target": "Utente a chi inviâ l'e-mail.",
+       "apihelp-emailuser-param-subject": "Ogetto de l'e-mail.",
+       "apihelp-emailuser-param-text": "Testo de l'e-mail.",
+       "apihelp-emailuser-param-ccme": "Mandime una copia de questa mail.",
+       "apihelp-emailuser-example-email": "Manda un'e-mail a l'utente <kbd>WikiSysop</kbd> co-o testo <kbd>Content</kbd>.",
+       "apihelp-expandtemplates-description": "Espandi tutti i template into wikitesto.",
+       "apihelp-expandtemplates-param-title": "Tittolo da paggina.",
+       "apihelp-expandtemplates-param-text": "Wikitesto da convertî.",
+       "apihelp-expandtemplates-param-prop": "Quæ informaçion otegnî.\n\nNotta che se no l'è seleçionou arcun valô, o risultou o contegniâ o codiçe wiki, ma l'output o saiâ inte 'n formato obsoleto.",
+       "apihelp-expandtemplates-paramvalue-prop-wikitext": "O wikitext espanso.",
+       "apihelp-expandtemplates-paramvalue-prop-properties": "Propietæ da paggina definie da-e paole magiche esteise into wikitesto.",
+       "apihelp-expandtemplates-paramvalue-prop-volatile": "Se l'output o segge volatile e o no 'agge da ese riadoeuviou atr'onde a l'interno da paggina.",
+       "apihelp-expandtemplates-paramvalue-prop-ttl": "O tempo mascimo doppo o quæ e memoizaçioin tempoannie (cache) do risultou dovieivan ese invalidæ.",
+       "apihelp-feedcontributions-param-feedformat": "O formato do feed.",
+       "apihelp-feedrecentchanges-example-simple": "Mostra i urtime modiffiche.",
+       "apihelp-feedrecentchanges-example-30days": "Mostra e modifiche di urtimi 30 giorni.",
+       "apihelp-feedwatchlist-param-feedformat": "O formato do feed.",
+       "apihelp-feedwatchlist-param-hours": "Elenca e paggine modificæ inte quest'urtime oe.",
+       "apihelp-feedwatchlist-param-linktosections": "Collega direttamente a-e seçioin modificæ, se poscibbile.",
+       "apihelp-feedwatchlist-example-all6hrs": "Mostra tutte e modiffiche a-e pagine oservæ inti urtime 6 oe.",
+       "apihelp-filerevert-description": "Ripristina un file a 'na verscion precedente.",
+       "apihelp-filerevert-param-filename": "Nomme do file de destinaçion, sença o prefisso 'File:'.",
+       "apihelp-filerevert-param-comment": "Commento in sciô caregamento.",
+       "apihelp-filerevert-param-archivename": "Nomme de l'archivvio da verscion da ripristinâ.",
+       "apihelp-filerevert-example-revert": "Ripristina <kbd>Wiki.png</kbd> a-a verscion do <kbd>2011-03-05T15:27:40Z</kbd>.",
+       "apihelp-help-description": "Mostra a guidda pe-i modduli speçificæ.",
+       "apihelp-help-param-toc": "Inciodi un endexo inte l'output HTML.",
+       "apihelp-help-example-main": "Agiutto pe-o moddulo prinçipâ.",
+       "apihelp-help-example-submodules": "Agiutto pe <kbd>action=query</kbd> e tutti i so sotto-modduli.",
+       "apihelp-help-example-recursive": "Tutti i agiutti inte 'na paggina.",
+       "apihelp-help-example-help": "Agiutto pe-o moddulo d'agiutto mæximo.",
+       "apihelp-imagerotate-description": "Roeua un-a o ciù inmaggine.",
+       "apihelp-imagerotate-param-rotation": "Graddi de rotaçion de l'inmaggine in senso oaio.",
+       "apihelp-imagerotate-example-simple": "Roeua <kbd>File:Example.png</kbd> de <kbd>90</kbd> graddi.",
+       "apihelp-imagerotate-example-generator": "Roeua tutte e inmaggine in <kbd>Category:Flip</kbd> de <kbd>180</kbd> graddi.",
+       "apihelp-import-param-summary": "Ogetto into registro d'importaçion.",
+       "apihelp-import-param-xml": "File XML caregou.",
+       "apihelp-import-param-interwikisource": "Pe-e importaçioin interwiki: wiki da-e quæ importâ.",
+       "apihelp-import-param-interwikipage": "Pe-e importaçioin interwiki: paggina da importâ.",
+       "apihelp-import-param-fullhistory": "Pe-e importaçioin interwiki: importa l'intrega cronologia, non solo a verscion attoale.",
+       "apihelp-import-param-templates": "Pe-e importaçioin interwiki: importa tutti i template incioxi ascì.",
+       "apihelp-import-param-namespace": "Importa inte questo namespace. O no poeu ese doeuviou insemme a <var>$1rootpage</var>.",
+       "apihelp-import-param-rootpage": "Importa comme sottopaggina de questa paggina. O no poeu ese doeuviou insemme a <var>$1namespace</var>.",
+       "apihelp-import-example-import": "Importa [[meta:Help:ParserFunctions]] into namespace 100 con cronologia completa.",
+       "apihelp-linkaccount-description": "Colegamento de 'n'utença de 'n provider de terçe parte a l'utente corente.",
+       "apihelp-linkaccount-example-link": "Avvia o processo de collegamento a 'n'utença da <kbd>Example</kbd>.",
+       "apihelp-login-description": "Accedi e otegni i cookie d'aotenticaçion.\n\nQuest'açion dev'ese doeuviâ escluxivamente in combinaçion con [[Special:BotPasswords]]; doeuviâla pe l'accesso a l'account prinçipâ o l'è deprecou e o poeu fallî sença preaviso. Pe acedere in moddo seguo a l'utença prinçipâ, doeuvia <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
+       "apihelp-login-description-nobotpasswords": "Accedi e otegni i cookies d'aotenticaçion.\n\nQuest'açion a l'è deprecâ e a poeu fallî sença preaviso. Pe acede in moddo seguo, doeuvia [[Special:ApiHelp/clientlogin|action=clientlogin]].",
+       "apihelp-login-param-name": "Nomme utente.",
+       "apihelp-login-param-password": "Password.",
+       "apihelp-login-param-domain": "Dominnio (opçionâ).",
+       "apihelp-login-example-gettoken": "Recuppera un token de login.",
+       "apihelp-login-example-login": "Intra",
+       "apihelp-logout-description": "Sciorti e scassa i dæti da sescion.",
+       "apihelp-logout-example-logout": "Disconnetti l'utente attoale.",
+       "apihelp-mergehistory-description": "O l'unisce e cronologie de paggine.",
+       "apihelp-mergehistory-param-from": "O tittolo da paggina da-a quæ a cronologia a saiâ unia. O no poeu ese doeuviou insemme a <var>$1fromid</var>.",
+       "apihelp-mergehistory-param-fromid": "L'ID da paggina da-a quæ a cronologia a saiâ unia. O no poeu ese doeuviou insemme a <var>$1from</var>.",
+       "apihelp-mergehistory-param-to": "O tittolo da paggina inta quæ a cronologia a saiâ unia. O no poeu ese doeuviou insemme a <var>$1toid</var>.",
+       "apihelp-mergehistory-param-toid": "L'ID da paggina inta quæ a cronologia a saiâ unia. O no poeu ese doeuviou insemme a <var>$1fromid</var>.",
+       "apihelp-mergehistory-param-timestamp": "O timestamp scin a-o quæle verscioin saian mesciæ da-a cronologia da paggina d'origine a quella da paggina de destinaçion. Se omisso, l'intrega cronologia da paggina d'origine a saiâ unia inta paggina de destinaçion.",
+       "apihelp-mergehistory-param-reason": "Raxon pe l'union da cronologia.",
+       "apihelp-mergehistory-example-merge": "Unisci l'intrega cronologia de <kbd>Oldpage</kbd> inte <kbd>Newpage</kbd>.",
+       "apihelp-mergehistory-example-merge-timestamp": "Unisci e verscioin da paggina <kbd>Oldpage</kbd> scin a <kbd>2015-12-31T04:37:41Z</kbd> inte <kbd>Newpage</kbd>.",
+       "apihelp-move-description": "Mescia 'na paggina",
+       "apihelp-move-param-from": "Tittolo da paggina da rinominâ. O no poeu vese doeuviou insemme a <var>$1pageid</var>.",
+       "apihelp-move-param-fromid": "ID de paggina da paggina da rinominâ. O no poeu ese doeuviou insemme a <var>$1title</var>.",
+       "apihelp-move-param-to": "Tittolo a-o quæ mesciâ a paggina.",
+       "apihelp-move-param-reason": "Raxon da rinommina.",
+       "apihelp-move-param-movetalk": "Rinommina a paggina de discuscion, s'a l'existe.",
+       "apihelp-move-param-movesubpages": "Rinommina e sottopaggine, se applicabile.",
+       "apihelp-move-param-noredirect": "No creâ un rinvio.",
+       "apihelp-move-param-watch": "Azonze a paggina e o redirect a-i oservæ speciali de l'utente attoale.",
+       "apihelp-move-param-unwatch": "Rimoeuvi a paggina e o redirect da-i oservæ speciali de l'utente attoale.",
+       "apihelp-move-param-ignorewarnings": "Ignora i messaggi d'avvertimento do scistema",
+       "apihelp-move-example-move": "Mescia <kbd>Badtitle</kbd> a <kbd>Goodtitle</kbd> sença lasciâ de redirect.",
+       "apihelp-opensearch-param-search": "Stringa de çerchia.",
+       "apihelp-opensearch-param-limit": "Nummero mascimo di risultæ da restituî.",
+       "apihelp-opensearch-param-suggest": "No stanni a fâ ninte se <var>[[mw:Manual:$wgEnableOpenSearchSuggest|$wgEnableOpenSearchSuggest]]</var> o l'è faso.",
+       "apihelp-opensearch-param-format": "O formato de l'output.",
+       "apihelp-opensearch-example-te": "Troeuva e paggine che començan con <kbd>Te</kbd>.",
+       "apihelp-options-example-reset": "Reimposta tutte e preferençe.",
+       "apihelp-paraminfo-description": "Otegni de informaçioin in scî modduli API.",
+       "apihelp-paraminfo-param-helpformat": "Formato de stringhe d'agiutto.",
+       "apihelp-parse-param-summary": "Ogetto da analizâ."
+}
index da676a1..938c098 100644 (file)
 {
        "@metadata": {
                "authors": [
-                       "Zygimantus"
+                       "Zygimantus",
+                       "Eitvys200"
                ]
        },
+       "apihelp-main-param-action": "Kurį veiksmą atlikti.",
+       "apihelp-main-param-curtimestamp": "Prie rezultato pridėti dabartinę laiko žymę.",
+       "apihelp-block-description": "Blokuoti vartotoją.",
+       "apihelp-block-param-reason": "Blokavimo priežastis.",
+       "apihelp-block-param-nocreate": "Neleisti kurti paskyrų.",
        "apihelp-createaccount-param-name": "Naudotojo vardas.",
        "apihelp-createaccount-param-realname": "Vardas (nebūtina).",
        "apihelp-delete-description": "Ištrinti puslapį.",
+       "apihelp-delete-example-simple": "Ištrinti <kbd>Main Page</kbd>.",
+       "apihelp-delete-example-reason": "Ištrinti <kbd>Main Page</kbd> su priežastimi <kbd>Preparing for move</kbd>.",
+       "apihelp-disabled-description": "Šis modulis buvo išjungtas.",
+       "apihelp-edit-description": "Kurti ir redaguoti puslapius.",
+       "apihelp-edit-param-sectiontitle": "Naujo skyriaus pavadinimas.",
        "apihelp-edit-param-text": "Puslapio turinys.",
+       "apihelp-edit-param-minor": "Smulkus pakeitimas.",
+       "apihelp-edit-param-notminor": "Nesmulkus pakeitimas.",
+       "apihelp-edit-param-createonly": "Neredaguoti puslapio jei jis jau egzistuoja.",
+       "apihelp-edit-param-watch": "Pridėti puslapį į dabartinio vartotojo stebimųjų sąrašą.",
+       "apihelp-edit-param-unwatch": "Pašalinti puslapį iš dabartinio vartotojo stebimųjų sąrašo.",
+       "apihelp-edit-param-redirect": "Automatiškai išspręsti peradresavimus.",
+       "apihelp-edit-example-edit": "Redaguoti puslapį.",
        "apihelp-emailuser-description": "Siųsti el. laišką naudotojui.",
+       "apihelp-emailuser-param-target": "El. laiško gavėjas.",
        "apihelp-expandtemplates-param-title": "Puslapio pavadinimas.",
+       "apihelp-feedcontributions-param-feedformat": "Srauto formatas.",
+       "apihelp-feedcontributions-param-year": "Nuo metų (ir anksčiau).",
+       "apihelp-feedcontributions-param-month": "Nuo mėnesio (ir anksčiau).",
+       "apihelp-feedcontributions-param-tagfilter": "Filtruoti įnašus, kurie turi šias žymes.",
+       "apihelp-feedcontributions-param-deletedonly": "Rodyti tik ištrintus įnašus.",
+       "apihelp-feedcontributions-param-hideminor": "Slėpti nedidelius pakeitimus.",
+       "apihelp-feedrecentchanges-param-feedformat": "Srauto formatas.",
+       "apihelp-feedrecentchanges-param-from": "Rodyti pakeitimus nuo tada.",
+       "apihelp-feedrecentchanges-param-hideminor": "Slėpti smulkius pakeitimus.",
+       "apihelp-feedrecentchanges-param-hidebots": "Slėpti robotų pakeitimus.",
+       "apihelp-feedrecentchanges-param-hideanons": "Slėpti vartotojų anonimų pakeitimus.",
+       "apihelp-feedrecentchanges-param-hideliu": "Slėpti užsiregistravusių vartotojų pakeitimus.",
+       "apihelp-feedrecentchanges-param-hidemyself": "Slėpti pakeitimus, atliktus dabartinio vartotojo.",
+       "apihelp-feedrecentchanges-param-tagfilter": "Filtruoti pagal žymę.",
+       "apihelp-feedrecentchanges-param-target": "Rodyti tik keitimus puslapiuose, pasiekiamuose iš šio puslapio.",
        "apihelp-feedrecentchanges-example-simple": "Parodyti naujausius keitimus.",
+       "apihelp-feedwatchlist-param-feedformat": "Srauto formatas.",
+       "apihelp-filerevert-param-comment": "Įkėlimo komentaras.",
+       "apihelp-help-example-recursive": "Visa pagalba viename puslapyje.",
+       "apihelp-help-example-help": "Pačio pagalbos modulio pagalba.",
+       "apihelp-imagerotate-description": "Pasukti viena ar daugiau paveikslėlių.",
+       "apihelp-imagerotate-param-rotation": "Kiek laipsnių pasukti paveikslėlį pagal laikrodžio rodyklę.",
+       "apihelp-imagerotate-example-generator": "Pasukti visus paveikslėlius <kbd>Category:Flip</kbd> <kbd>180</kbd> laipsnių.",
+       "apihelp-import-param-xml": "XML failas įkeltas.",
+       "apihelp-login-param-name": "Vartotojo vardas.",
+       "apihelp-login-param-password": "Slaptažodis.",
+       "apihelp-login-param-domain": "Domenas (neprivaloma).",
+       "apihelp-login-example-login": "Prisijungti.",
+       "apihelp-logout-description": "Atsijungti ir išvalyti sesijos duomenis.",
+       "apihelp-logout-example-logout": "Atjungti dabartinė vartotoją.",
+       "apihelp-managetags-example-delete": "Ištrinti <kbd>vandlaism</kbd> žymę su priežastimi <kbd>Misspelt</kbd>",
+       "apihelp-managetags-example-activate": "Aktyvuoti žymę pavadinimu <kbd>spam</kbd> su priežastimi <kbd>For use in edit patrolling</kbd>",
+       "apihelp-managetags-example-deactivate": "Išjungti žymę pavadinimu <kbd>spam</kbd> su priežastimi <kbd>No longer required</kbd>",
+       "apihelp-mergehistory-description": "Sujungti puslapio istorijas.",
+       "apihelp-mergehistory-param-reason": "Istorijos sujungimo priežastis.",
+       "apihelp-mergehistory-example-merge": "Sujungti visą <kbd>Oldpage</kbd> istoriją į <kbd>Newpage</kbd>.",
+       "apihelp-move-description": "Perkelti puslapį.",
+       "apihelp-move-param-to": "Pavadinimas, į kuri pervadinamas puslapis.",
+       "apihelp-move-param-reason": "Pervadinimo priežastis.",
+       "apihelp-move-param-movetalk": "Pervadinti aptarimo puslapį, jei jis egzistuoja.",
+       "apihelp-move-param-noredirect": "Nekurti nukreipimo.",
+       "apihelp-move-param-watch": "Pridėti puslapį ir nukreipimą į dabartinio vartotojo stebimųjų sąrašą.",
+       "apihelp-move-param-unwatch": "Pašalinti puslapį ir nukreipimą iš dabartinio vartotojo stebimųjų sąrašo.",
+       "apihelp-move-param-ignorewarnings": "Ignuoruoti bet kokius įspėjimus.",
+       "apihelp-move-example-move": "Perkelti <kbd>Badtitle</kbd> į <kbd>Goodtitle</kbd> nepaliekant nukreipimo.",
+       "apihelp-opensearch-description": "Ieškoti viki naudojant OpenSearch protokolą.",
+       "apihelp-opensearch-param-limit": "Maksimalus grąžinamas rezultatų skaičius.",
+       "apihelp-opensearch-example-te": "Rasti puslapius prasidedančius su <kbd>Te</kbd>.",
+       "apihelp-options-example-reset": "Nustatyti visus pageidavimus iš naujo.",
+       "apihelp-options-example-change": "Keisti <kbd>skin</kbd> ir <kbd>hideminor</kbd> pageidavimus.",
+       "apihelp-options-example-complex": "Nustatyti visus pageidavimus iš naujo, tada nustatyti <kbd>skin</kbd> ir <kbd>nickname</kbd>.",
+       "apihelp-paraminfo-description": "Gauti informaciją apie API modulius.",
+       "apihelp-protect-example-protect": "Apsaugoti puslapį.",
+       "apihelp-query+allcategories-param-dir": "Rūšiavimo kryptis.",
+       "apihelp-query+allcategories-param-min": "Gražinti tik kategorijas, kuriuose yra bent tiek narių.",
+       "apihelp-query+allcategories-param-max": "Gražinti tik kategorijas, kuriuose yra iki tiek narių.",
+       "apihelp-query+allcategories-param-limit": "Kiek kategorijų gražinti.",
+       "apihelp-query+allcategories-paramvalue-prop-size": "Prideda puslapių kategorijoje skaičių.",
        "apihelp-query+alldeletedrevisions-example-user": "Sąrašas paskutinių 50 ištrintų indėlių pagal vartotoją\n<kbd>Pavyzdys</kbd>.",
+       "apihelp-query+alllinks-paramvalue-prop-title": "Prideda nuorodos pavadinimą.",
+       "apihelp-query+alllinks-param-limit": "Kiek objektų iš viso gražinti.",
+       "apihelp-query+allmessages-param-lang": "Gražinti pranešimus šia kalba.",
        "apihelp-query+allrevisions-param-namespace": "Rodyti puslapius tik šioje vardų srityje.",
        "apihelp-query+backlinks-example-simple": "Rodyti nuorodas <kbd>Pagrindinis puslapis</kbd>.",
+       "apihelp-query+blocks-paramvalue-prop-id": "Prideda bloko ID.",
+       "apihelp-query+blocks-paramvalue-prop-user": "Prideda užblokuoto vartotojo vardą.",
+       "apihelp-query+blocks-paramvalue-prop-userid": "Prideda užblokuoto vartotojo ID.",
+       "apihelp-query+blocks-paramvalue-prop-by": "Prideda užblokuoto vartotojo vardą.",
+       "apihelp-query+blocks-paramvalue-prop-byid": "Prideda užblokuoto vartotojo ID.",
+       "apihelp-query+blocks-paramvalue-prop-timestamp": "Prideda blokavimo laiko žymę.",
+       "apihelp-query+blocks-paramvalue-prop-expiry": "Prideda blokavimo pabaigos laiko žymes.",
+       "apihelp-query+blocks-paramvalue-prop-reason": "Prideda blokavimo priežastį.",
+       "apihelp-query+blocks-paramvalue-prop-range": "Prideda blokavimo paveiktų IP adresų diapazoną.",
        "apihelp-query+watchlist-paramvalue-type-external": "Išoriniai keitimai.",
        "apihelp-query+watchlist-paramvalue-type-new": "Puslapio sukūrimai.",
        "apihelp-stashedit-param-title": "Puslapio pavadinimas buvo redaguotas.",
index d88e0ec..03cb2b5 100644 (file)
@@ -99,5 +99,6 @@
        "apihelp-query+watchlist-paramvalue-type-categorize": "वर्ग सदस्यता बदलते.",
        "apihelp-stashedit-param-title": "पानाच्या मथळ्याचे संपादन होत आहे.",
        "apihelp-stashedit-param-sectiontitle": "नविन विभागाचा मथळा",
-       "apihelp-stashedit-param-summary": "सारांश बदला."
+       "apihelp-stashedit-param-summary": "सारांश बदला.",
+       "api-help-examples": "{{PLURAL:$1|उदाहरण|उदाहरणे}}:"
 }
index 32e227d..6e85779 100644 (file)
        "apihelp-expandtemplates-param-title": "Títol de la pagina.",
        "apihelp-expandtemplates-param-text": "Wikitèxte de convertir.",
        "apihelp-expandtemplates-paramvalue-prop-wikitext": "Lo wikitèxte desvolopat.",
+       "apihelp-feedcontributions-description": "Renvia lo fial de las contribucions d’un utilizaire.",
        "apihelp-feedcontributions-param-feedformat": "Lo format del flux.",
        "apihelp-feedcontributions-param-year": "A partir de l’annada (e mai recent) :",
        "apihelp-feedcontributions-param-month": "A partir del mes (e mai recent) :",
+       "apihelp-feedcontributions-param-tagfilter": "Filtrar las contribucions qu'an aquestas balisas.",
+       "apihelp-feedcontributions-param-deletedonly": "Afichar solament las contribucions suprimidas.",
+       "apihelp-feedcontributions-param-hideminor": "Amagar los cambiaments mendres.",
+       "apihelp-feedcontributions-param-showsizediff": "Afichar la diferéncia de talha entre las revisions.",
        "apihelp-feedrecentchanges-param-feedformat": "Lo format del flux.",
+       "apihelp-feedrecentchanges-param-hideminor": "Amagar las modificacions menoras.",
        "apihelp-feedrecentchanges-param-tagfilter": "Filtrar per balisa.",
        "apihelp-filerevert-param-comment": "Telecargar lo comentari.",
        "apihelp-filerevert-param-archivename": "Nom d’archiu de la revision de restablir.",
-       "apihelp-import-param-summary": "Importar lo resumit.",
+       "apihelp-import-param-summary": "Resumit de l’importacion de l’entrada de jornal.",
        "apihelp-import-param-xml": "Fichièr XML telecargat.",
        "apihelp-login-param-name": "Nom d'utilizaire.",
        "apihelp-login-param-password": "Senhal.",
@@ -83,6 +89,7 @@
        "api-help-license-unknown": "Licéncia : <span class=\"apihelp-unknown\">desconeguda</span>",
        "api-help-parameters": "{{PLURAL:$1|Paramètre|Paramètres}} :",
        "api-help-param-deprecated": "Obsolèt.",
+       "api-help-param-required": "Aqueste paramètre es obligatòri.",
        "api-help-datatypes-header": "Tipe de donadas",
        "api-help-param-default": "Per defaut : $1",
        "api-credits-header": "Mercejaments"
index 96fe70e..6023be0 100644 (file)
        "apihelp-protect-param-reason": "Powód zabezpieczania/odbezpieczania.",
        "apihelp-protect-param-cascade": "Włącz ochronę kaskadową (chronione są wszystkie osadzone szablony i obrazki na tej stronie). Ignorowane, jeśli żaden z danych poziomów ochrony nie wspiera kaskadowania.",
        "apihelp-protect-example-protect": "Zabezpiecz stronę",
-       "apihelp-protect-example-unprotect": "Odbezpiecz stronę ustawiając ograniczenia na <kbd>all</kbd>.",
+       "apihelp-protect-example-unprotect": "Odbezpiecz stronę ustawiając ograniczenia na <kbd>all</kbd> (czyli każdy może wykonać działanie).",
        "apihelp-protect-example-unprotect2": "Odbezpiecz stronę ustawiając brak ograniczeń.",
        "apihelp-purge-param-forcelinkupdate": "Uaktualnij tabele linków.",
        "apihelp-purge-example-generator": "Przeczyść pierwsze 10 stron w przestrzeni głównej.",
index 89fa617..c2545fc 100644 (file)
@@ -45,6 +45,7 @@
        "apihelp-query+watchlist-paramvalue-prop-title": "د يو مخ سرليک ورگډوي.",
        "apihelp-query+watchlist-paramvalue-type-new": "مخ جوړونې.",
        "apihelp-stashedit-param-sectiontitle": "د يوې نوې برخې سرليک.",
+       "apihelp-stashedit-param-summary": "د بدلون لنډيز.",
        "apihelp-tag-param-reason": "د بدلون سبب.",
        "apihelp-unblock-param-reason": "د بنديز ليرې کولو سبب.",
        "apihelp-undelete-param-reason": "د بيازېرملو سبب.",
index 1b90a0d..40d5786 100644 (file)
@@ -98,6 +98,9 @@
        "api-help-lead": "Esta é uma página de documentação API do MediaWiki gerada automaticamente.\n\nDocumentação e exemplos: https://www.mediawiki.org/wiki/API",
        "api-help-main-header": "Módulo principal",
        "api-help-flag-deprecated": "Este módulo está obsoleto.",
+       "api-help-flag-readrights": "Este módulo requer direitos de leitura.",
+       "api-help-flag-writerights": "Este módulo requer direitos de leitura.",
+       "api-help-flag-mustbeposted": "Este módulo aceita somente solicitações POST.",
        "api-help-source": "Fonte: $1",
        "api-help-license": "Licença: [[$1|$2]]",
        "api-help-license-noname": "Licença: [[$1|Ver ligação]]",
        "api-help-param-deprecated": "Obsoleto.",
        "api-help-param-required": "Este parâmetro é obrigatório.",
        "api-help-datatypes-header": "Tipo de dados",
+       "api-help-datatypes": "Alguns tipos de parâmetro na API necessitam de mais explicações:\n;boolean\n:Os parâmetros booleanos funcionam como as caixas de seleção HTML: se o parâmetro for especificado, independentemente do valor, é considerado verdadeiro. Para um valor falso, omitir o parâmetro completo.\n;timestamp\n:Timestamps podem ser especificados em vários formatos. Formato de data e hora ISO 8601 é recomendado. Todos os horários estão em UTC, qualquer inclusão de fuso horário é ignorado.\n:* Data e hora ISO 8601, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (pontuação e <kbd>Z</kbd> são opcionais)\n:* Data e hora ISO 8601 com segundos fracionários (ignorado), <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (traços, dois pontos e <kbd>Z</kbd> são opcionais)\n:* Formato do MediaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Formato numérico genérico, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (fuso horário opcional do <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd>, or <kbd>-<var>##</var></kbd> são ignorados)\n:* Formato EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:*Formato RFC 2822 (o fuso horário pode ser omitido), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formato RFC 850 (o fuso horário pode ser omitido), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Formato C ctime, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Segundos desde 1970-01-01T00:00:00Z como um inteiro de 1 a 13 dígitos (excluindo <kbd>0</kbd>)\n:* A string <kbd>now</kbd>",
        "api-help-param-type-limit": "Tipo: inteiro ou <kbd>max</kbd>",
        "api-help-param-type-boolean": "Tipo: boolean ([[Special:ApiHelp/main#main/datatypes|detalhes]])",
        "api-help-param-type-user": "Tipo: {{PLURAL:$1|1=nome de utilizador|2=lista de nomes de utilizadores}}",
        "api-help-param-list": "{{PLURAL:$1|1=Um dos seguintes valores|2=Valores (separar com <kbd>{{!}}</kbd>)}}: $2",
        "api-help-param-multi-separate": "Separe os valores com <kbd>|</kbd>.",
+       "api-help-param-multi-max": "O número máximo de valores é {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} para robôs).",
        "api-help-param-default": "Padrão: $1",
        "api-help-param-default-empty": "Padrão: <span class=\"apihelp-empty\">(vazio)</span>",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(sem descrição)</span>",
index 6c47aa4..caa89b5 100644 (file)
        "apihelp-linkaccount-example-link": "{{doc-apihelp-example|linkaccount}}",
        "apihelp-login-description": "{{doc-apihelp-description|login|info=This message is used when <code>$wgEnableBotPasswords</code> is true.|seealso=* {{msg-mw|apihelp-login-description-nobotpasswords}}}}",
        "apihelp-login-description-nobotpasswords": "{{doc-apihelp-description|login|info=This message is used when <code>$wgEnableBotPasswords</code> is false.|seealso=* {{msg-mw|apihelp-login-description}}}}",
-       "apihelp-login-description-nonauthmanager": "{{doc-apihelp-description|login|info=This message is used when <code>$wgDisableAuthManager</code> is true.|seealso=* {{msg-mw|apihelp-login-description}}}}",
        "apihelp-login-param-name": "{{doc-apihelp-param|login|name}}\n{{Identical|Username}}",
        "apihelp-login-param-password": "{{doc-apihelp-param|login|password}}\n{{Identical|Password}}",
        "apihelp-login-param-domain": "{{doc-apihelp-param|login|domain}}",
        "apihelp-paraminfo-param-pagesetmodule": "{{doc-apihelp-param|paraminfo|pagesetmodule}}",
        "apihelp-paraminfo-param-formatmodules": "{{doc-apihelp-param|paraminfo|formatmodules}}",
        "apihelp-paraminfo-example-1": "{{doc-apihelp-example|paraminfo}}",
+       "apihelp-paraminfo-example-2": "{{doc-apihelp-example|paraminfo}}",
        "apihelp-parse-description": "{{doc-apihelp-description|parse}}",
        "apihelp-parse-param-title": "{{doc-apihelp-param|parse|title}}",
        "apihelp-parse-param-text": "{{doc-apihelp-param|parse|text}}",
        "apihelp-query+authmanagerinfo-description": "{{doc-apihelp-description|query+authmanagerinfo}}",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "{{doc-apihelp-param|query+authmanagerinfo|securitysensitiveoperation}}",
        "apihelp-query+authmanagerinfo-param-requestsfor": "{{doc-apihelp-param|query+authmanagerinfo|requestsfor}}",
-       "apihelp-query+filerepoinfo-example-login": "{{doc-apihelp-example|query+filerepoinfo}}",
-       "apihelp-query+filerepoinfo-example-login-merged": "{{doc-apihelp-example|query+filerepoinfo}}",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "{{doc-apihelp-example|query+filerepoinfo}}",
+       "apihelp-query+authmanagerinfo-example-login": "{{doc-apihelp-example|query+authmanagerinfo}}",
+       "apihelp-query+authmanagerinfo-example-login-merged": "{{doc-apihelp-example|query+authmanagerinfo}}",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "{{doc-apihelp-example|query+authmanagerinfo}}",
        "apihelp-query+backlinks-description": "{{doc-apihelp-description|query+backlinks}}",
        "apihelp-query+backlinks-param-title": "{{doc-apihelp-param|query+backlinks|title}}",
        "apihelp-query+backlinks-param-pageid": "{{doc-apihelp-param|query+backlinks|pageid}}",
        "apihelp-stashedit-param-section": "{{doc-apihelp-param|stashedit|section}}",
        "apihelp-stashedit-param-sectiontitle": "{{doc-apihelp-param|stashedit|sectiontitle}}",
        "apihelp-stashedit-param-text": "{{doc-apihelp-param|stashedit|text}}",
+       "apihelp-stashedit-param-stashedtexthash": "{{doc-apihelp-param|stashedit|stashedtexthash}}",
        "apihelp-stashedit-param-contentmodel": "{{doc-apihelp-param|stashedit|contentmodel}}",
        "apihelp-stashedit-param-contentformat": "{{doc-apihelp-param|stashedit|contentformat}}",
        "apihelp-stashedit-param-baserevid": "{{doc-apihelp-param|stashedit|baserevid}}",
index dd537f6..e1cb982 100644 (file)
@@ -28,7 +28,7 @@
        "apihelp-main-param-requestid": "Любое заданное здесь значение будет включено в ответ. Может быть использовано для различения запросов.",
        "apihelp-main-param-servedby": "Включить в результаты имя хоста, обработавшего запрос.",
        "apihelp-main-param-curtimestamp": "Включить в результаты временную метку.",
-       "apihelp-main-param-origin": "При обращении к API, используя кросс-доменный AJAX-запрос (CORS), задайте параметру значение исходного домена. Он должен быть включён в любой предварительный запрос и таким образом должен быть частью URI-запроса (не тела POST). Он должен точно соответствовать одному из источников в заголовке <code>Origin<code>, так что он должен быть задан наподобие <kbd>https://ru.wikipedia.org</kbd> или <kbd>https://meta.wikimedia.org</kbd>. Если параметр не соответствует заголовку <code>Origin<code>, будет возвращён ответ с кодом ошибки 403. Если параметр соответствует заголовку <code>Origin</code>, и источник находится в белом списке, будет установлен заголовок <code>Access-Control-Allow-Origin</code>.",
+       "apihelp-main-param-origin": "При обращении к API, используя кросс-доменный AJAX-запрос (CORS), задайте параметру значение исходного домена. Он должен быть включён в любой предварительный запрос и таким образом должен быть частью URI-запроса (не тела POST).\n\nДля аутентифицированных запросов он должен точно соответствовать одному из источников в заголовке <code>Origin<code>, так что он должен быть задан наподобие <kbd>https://ru.wikipedia.org</kbd> или <kbd>https://meta.wikimedia.org</kbd>. Если параметр не соответствует заголовку <code>Origin<code>, будет возвращён ответ с кодом ошибки 403. Если параметр соответствует заголовку <code>Origin</code>, и источник находится в белом списке, будут установлены заголовки <code>Access-Control-Allow-Origin</code> и <code>Access-Control-Allow-Credentials</code>.\n\nДля неаутентифицированных запросов укажите значение <kbd>*</kbd>. Это приведёт к установке заголовка <code>Access-Control-Allow-Origin</code> заголовка должен быть установлен, но <code>Access-Control-Allow-Credentials</code> примет значение <code>false</code> и все пользовательские данные будут ограничены.",
        "apihelp-block-description": "Блокировка участника.",
        "apihelp-block-param-user": "Имя участника, IP-адрес или диапазон IP-адресов, которые вы хотите заблокировать.",
        "apihelp-block-param-reason": "Причина блокировки.",
        "apihelp-feedcontributions-param-deletedonly": "Показать только удалённые правки.",
        "apihelp-feedcontributions-param-toponly": "Показать только правки, являющиеся последними версиями.",
        "apihelp-feedcontributions-param-newonly": "Показывать только правки, являющиеся созданием страниц.",
+       "apihelp-feedcontributions-param-hideminor": "Скрыть малые правки.",
        "apihelp-feedcontributions-param-showsizediff": "Показать размер различия между версиями.",
        "apihelp-feedcontributions-example-simple": "Показать вклад участника <kbd>Example</kbd>.",
        "apihelp-feedrecentchanges-param-invert": "Все пространства имён, кроме выбранного.",
        "apihelp-imagerotate-param-rotation": "На сколько градусов по часовой стрелке повернуть изображение.",
        "apihelp-imagerotate-example-simple": "Повернуть <kbd>File:Example.png</kbd> на <kbd>90</kbd> градусов.",
        "apihelp-imagerotate-example-generator": "Повернуть все изображения в <kbd>Category:Flip</kbd> на <kbd>180</kbd> градусов.",
-       "apihelp-import-param-summary": "Ð\98мпоÑ\80Ñ\82 Ð¸Ñ\82ога",
+       "apihelp-import-param-summary": "Ð\9eпиÑ\81ание Ð·Ð°Ð¿Ð¸Ñ\81и Ð¶Ñ\83Ñ\80нала Ð¸Ð¼Ð¿Ð¾Ñ\80Ñ\82а.",
        "apihelp-import-param-xml": "Загруженный XML-файл.",
        "apihelp-import-example-import": "Импортировать [[meta:Help:ParserFunctions]] с полной историей в пространство имён 100.",
        "apihelp-login-param-name": "Имя участника.",
        "apihelp-protect-param-title": "Бит атамаһы. $1pageid менән бергә ҡулланылмай.",
        "apihelp-protect-param-reason": "(ООН) һағы сәбәптәре.",
        "apihelp-protect-example-protect": "Защитить страницу.",
-       "apihelp-protect-example-unprotect": "<kbd>всех</kbd> өсөн сикләүҙәр ҡуйып,биттән һаҡлауҙы алырға.",
+       "apihelp-protect-example-unprotect": "Снять защиту страницы, установив ограничения <kbd>all</kbd> (т. е. любой желающий может принять меры).",
        "apihelp-protect-example-unprotect2": "Бер ниндәй сикләүҙәр ҡуймай биттән һаҡлауҙы алырға.",
        "apihelp-purge-param-forcelinkupdate": "Обновление связей таблиц.",
        "apihelp-purge-param-forcerecursivelinkupdate": "Һылтанманы һәм таблицаны яңыртығыҙ һәм был битте шаблон итеп ҡулланған башҡа биттәр өсөн һылтанмаларҙы ла яңыртығыҙ.",
        "apihelp-query+allimages-param-maxsize": "Бар һүрәттәр лимиты (байттарҙа).",
        "apihelp-query+allimages-param-limit": "Кире ҡайтыу өсөн образдар һаны.",
        "apihelp-query+allimages-example-B": "<kbd>Б</kbd> хәрефенән башланған файлдар исемлеген күрһәтергә.",
-       "apihelp-query+allimages-example-recent": "Ð\9aүпÑ\82Ó\99н Ñ\82үгел Ñ\82ейÓ\99лгÓ\99н Ò»Ó\99м [[Ñ\81пеÑ\86иалÑ\8cнÑ\8bе:NewFiles]] Ð¾Ò¡Ñ\88аÑ\88 Ñ\84айлдаÑ\80 Ð¸Ñ\81емлеген ÐºÒ¯Ñ\80Ò»Ó\99Ñ\82еÑ\80гÓ\99.",
+       "apihelp-query+allimages-example-recent": "Ð\9fоказаÑ\82Ñ\8c Ñ\81пиÑ\81ок Ð½ÐµÐ´Ð°Ð²Ð½Ð¾ Ð·Ð°Ð³Ñ\80Ñ\83женнÑ\8bÑ\85 Ñ\84айлов, Ð°Ð½Ð°Ð»Ð¾Ð³Ð¸Ñ\87но [[Special:NewFiles]].",
        "apihelp-query+allimages-example-generator": "<kbd>Т</kbd> хәрефенән башланған 4 файл хаҡында мәғлүмәтте  күрһәтергә.",
        "apihelp-query+alllinks-description": "Бирелгән исемдәр арауығына йүнәлткән барлыҡ һылтанмаларҙы һанап сығырға.",
        "apihelp-query+alllinks-param-from": "Һанауҙы башлау өсөн һылтанма атамаһы.",
        "api-help-param-type-boolean": "Тип: двоичный ([[Special:ApiHelp/main#main/datatypes|details]])",
        "api-help-param-type-timestamp": "Тип: {{PLURAL:$1|1=timestamp|2=list of timestamps}} ([[Special:ApiHelp/main#main/datatypes|allowed formats]])",
        "api-help-param-type-user": "Тип: {{PLURAL:$1|1=user name|2=list of user names}}",
-       "api-help-param-list": "{{PLURAL:$1|1=Одно из следующих значений|2=Значения (разделённые <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Одно из следующих значений|2=Значения (разделённые с помощью <kbd>{{!}}</kbd> или [[Special:ApiHelp/main#main/datatypes|альтернативного варианта]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Должен быть пустым|может быть пустым, или $2}}",
        "api-help-param-limit": "Не более чем $1 разрешено.",
        "api-help-param-limit2": "Разрешено не более чем $1 ($2 для ботов).",
        "api-help-param-integer-min": "{{PLURAL:$1|1=value|2=values}} должен быть не меньше чем $2.",
        "api-help-param-integer-max": "{{PLURAL:$1|1=value|2=values}} должен быть не больше чем $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=value|2=values}} должен быть между $2 и $3.",
-       "api-help-param-multi-separate": "Разделяйте значения с помощью <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Разделяйте значения с помощью <kbd>|</kbd> или [[Special:ApiHelp/main#main/datatypes|альтернативного варианта]].",
        "api-help-param-multi-max": "Максимальное количество значений должно быть {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} для ботов).",
        "api-help-param-default": "По умолчанию: $1",
        "api-help-param-default-empty": "По умолчанию: <span class=\"apihelp-empty\">(пусто)</span>",
index 3205248..35389b0 100644 (file)
        "api-help-parameters": "{{PLURAL:$1|Parameter|Parametrar}}:",
        "api-help-param-deprecated": "Föråldrad.",
        "api-help-param-required": "Denna parameter är obligatorisk.",
-       "api-help-param-list": "{{PLURAL:$1|1=Ett av följande värden|2=Värden (separerade med <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Ett av följande värden|2=Värden (separerade med <kbd>{{!}}</kbd> eller [[Special:ApiHelp/main#main/datatypes|alternativ]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Måste vara tom|Kan vara tom, eller $2}}",
        "api-help-param-limit": "Inte mer än $1 tillåts.",
-       "api-help-param-limit2": "Inte mer än $1 ($2 för robotar) tillåts."
+       "api-help-param-limit2": "Inte mer än $1 ($2 för robotar) tillåts.",
+       "api-help-param-multi-separate": "Separera värden med <kbd>|</kbd> eller [[Special:ApiHelp/main#main/datatypes|alternativ]]."
 }
index d99945d..1a079df 100644 (file)
@@ -24,7 +24,7 @@
        "apihelp-main-param-requestid": "Будь-яке значення, вказане тут, буде включене у відповідь. Може використовуватися, щоб відрізняти запити.",
        "apihelp-main-param-servedby": "Включити в результати ім'я хоста, який обробив запит.",
        "apihelp-main-param-curtimestamp": "Включити в результат поточну мітку часу.",
-       "apihelp-main-param-origin": "При доступі до API з використанням крос-доменного AJAX-запиту (CORS), задайте параметру значення вихідного домена. Він має бути включений у будь-який попередній запит і таким чином мусить бути частиною запиту URI (не тіла POST). Він повинен точно співпадати з одним з виходів у заголовку <code>Origin</code>, тобто бути заданим чимось на зразок <kbd>https://uk.wikipedia.org</kbd> або <kbd>https://meta.wikimedia.org</kbd>. Якщо цей параметр не співпадає з заголовком <code>Origin</code>, повернеться помилка 403. Якщо цей параметр співпадає з заголовком <code>Origin</code> і вихід знаходиться у білому списку, буде встановлено заголовок <code>Access-Control-Allow-Origin</code>.",
+       "apihelp-main-param-origin": "При доступі до API з використанням крос-доменного AJAX-запиту (CORS), задайте параметру значення вихідного домена. Він має бути включений у будь-який попередній запит і таким чином мусить бути частиною запиту URI (не тіла POST). \n\nДля автентифікованих запитів він повинен точно співпадати з одним з виходів у заголовку <code>Origin</code>, тобто бути заданим чимось на зразок <kbd>https://uk.wikipedia.org</kbd> або <kbd>https://meta.wikimedia.org</kbd>. Якщо цей параметр не співпадає з заголовком <code>Origin</code>, повернеться помилка 403. Якщо цей параметр співпадає з заголовком <code>Origin</code> і вихід знаходиться у білому списку, буде встановлено заголовки <code>Access-Control-Allow-Origin</code> і <code>Access-Control-Allow-Credentials</code>.\n\nДля неавтентифікованих запитів укажіть значення <kbd>*</kbd>. Це дасть встановлення заголовка <code>Access-Control-Allow-Origin</code>, але <code>Access-Control-Allow-Credentials</code> буде <code>false</code> і всі дані про користувача будуть заборонені.",
        "apihelp-main-param-uselang": "Мова, що використовується для перекладу повідомлень. Список кодів можна видати на <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd> з <kbd>siprop=languages</kbd> або вказати <kbd>user</kbd> на використання поточного налаштування мови користувача, або вказати <kbd>content</kbd> на використання мови вмісту цієї вікі.",
        "apihelp-block-description": "Заблокувати користувача.",
        "apihelp-block-param-user": "Ім'я користувача, IP-адреса або діапазон IP-адрес для блокування.",
@@ -74,6 +74,9 @@
        "apihelp-createaccount-param-language": "Код мови для встановлення за замовчуванням для користувача (необов'язково, за замовчуванням — мова вмісту).",
        "apihelp-createaccount-example-pass": "Створити користувача <kbd>testuser</kbd> з паролем <kbd>test123</kbd>.",
        "apihelp-createaccount-example-mail": "Створити користувача <kbd>testmailuser</kbd> і надіслати на електронну пошту випадково-згенерований пароль.",
+       "apihelp-cspreport-description": "Використовується браузерами для повідомлення порушень Правил безпеки контенту (Content Security Policy). Цей модуль не повинен використовуватися, окрім випадків автоматичного використання веб-браузером для CSP-скарги.",
+       "apihelp-cspreport-param-reportonly": "Позначити як доповідь із моніторингової політики, не примусової політики",
+       "apihelp-cspreport-param-source": "Що згенерувало CSP-заголовок, який запустив цю доповідь",
        "apihelp-delete-description": "Вилучити сторінку.",
        "apihelp-delete-param-title": "Назва сторінки для вилучення. Не можна використати разом з <var>$1pageid</var>.",
        "apihelp-delete-param-pageid": "ID-сторінки на вилучення. Не можна використати разом з <var>$1title</var>.",
        "apihelp-linkaccount-example-link": "Почати процес пов'язування з обліковм записом з <kbd>Example</kbd>.",
        "apihelp-login-description": "Увійти в систему й отримати куки автентифікації.\n\nЦю дію треба використовувати лише в комбінації з [[Special:BotPasswords]]; використання для входу в основний обліковий запис застаріле і може ламатися без попередження. Щоб безпечно увійти в основний обліковий запис, використовуйте <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
        "apihelp-login-description-nobotpasswords": "Увійти й отримати куки автентифікації.\n\nЦя дія застаріла і може ламатися без попередження. Щоб безпечно входити в систему, використовуйте <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
-       "apihelp-login-description-nonauthmanager": "Увійти й отримати куки автентифікації.\n\nУ випадку успішного входження в систему будуть включені в заголовки відповіді HTTP. У випадку невдалого входження подальші спроби можуть бути урізані, щоб обмежити атаки автоматичного вгадування пароля.",
        "apihelp-login-param-name": "Ім'я користувача.",
        "apihelp-login-param-password": "Пароль.",
        "apihelp-login-param-domain": "Домен (необов'язково).",
        "apihelp-options-example-change": "Змінити налаштування <kbd>skin</kbd> та <kbd>hideminor</kbd>.",
        "apihelp-options-example-complex": "Скинути всі налаштування, потім встановити <kbd>skin</kbd> та <kbd>nickname</kbd>.",
        "apihelp-paraminfo-description": "Отримати інформацію про модулі API.",
-       "apihelp-paraminfo-param-modules": "Список назв модулів (значення параметрів <var>action</var> і <var>format</var> або <kbd>main</kbd>). Можна вказати підмодулі через <kbd>+</kbd>.",
+       "apihelp-paraminfo-param-modules": "Список назв модулів (значення параметрів <var>action</var> і <var>format</var> або <kbd>main</kbd>). Можна вказати підмодулі через <kbd>+</kbd>, усі підмодулі через <kbd>+*</kbd> або усі підмодулі рекурсивно через <kbd>+**</kbd>.",
        "apihelp-paraminfo-param-helpformat": "Формат рядків довідки.",
        "apihelp-paraminfo-param-querymodules": "Список назв модулів запитів (значення параметра <var>prop</var>, <var>meta</var> або <var>list</var>). Використати <kbd>$1modules=query+foo</kbd> замість <kbd>$1querymodules=foo</kbd>.",
        "apihelp-paraminfo-param-mainmodule": "Отримати інформацію також про основний модуль (топ-рівень). Використати натомість <kbd>$1modules=main</kbd>.",
        "apihelp-paraminfo-param-pagesetmodule": "Отримати також інформацію про модуль pageset (з вказанням titles= і рідних).",
        "apihelp-paraminfo-param-formatmodules": "Список назв модулів форматування (значення параметра <var>format</var>). Використати натомість <var>$1modules</var>.",
        "apihelp-paraminfo-example-1": "Показати інформацію для <kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>, <kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>, <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd> та <kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>.",
+       "apihelp-paraminfo-example-2": "Показати інформацію про всі підмодулі <kbd>[[Special:ApiHelp/query|action=query]]</kbd>.",
        "apihelp-parse-description": "Аналізує вміст і видає парсер виходу.\n\nДив. різні prop-модулі <kbd>[[Special:ApiHelp/query|action=query]]</kbd>, щоб отримати інформацію з поточної версії сторінки.\n\nЄ декілька способів вказати текст для аналізу:\n# Вказати сторінку або версію, використавши <var>$1page</var>, <var>$1pageid</var> або <var>$1oldid</var>.\n# Вказати безпосередньо, використавши <var>$1text</var>, <var>$1title</var> і <var>$1contentmodel</var>.\n# Вказати лише підсумок аналізу. <var>$1prop</var> повинен мати порожнє значення.",
        "apihelp-parse-param-title": "Назва сторінки, якій належить текст. Якщо пропущена, має бути вказано <var>$1contentmodel</var>, а як назву буде вжито [[API]].",
        "apihelp-parse-param-text": "Текст для аналізу. Використати <var>$1title</var> або <var>$1contentmodel</var> для контролю моделі вмісту.",
        "apihelp-parse-paramvalue-prop-sections": "Дає розділи в аналізованому вікітексті.",
        "apihelp-parse-paramvalue-prop-revid": "Додає ідентифікатор версії аналізованої сторінки.",
        "apihelp-parse-paramvalue-prop-displaytitle": "Додає заголовок аналізованого вікітексту.",
-       "apihelp-parse-paramvalue-prop-headitems": "Дає елементи для вставки в <code>&lt;head&gt;</code> сторінки.",
+       "apihelp-parse-paramvalue-prop-headitems": "<span class=\"apihelp-deprecated\">Застаріло.</span> Дає елементи для вставки в <code>&lt;head&gt;</code> сторінки.",
        "apihelp-parse-paramvalue-prop-headhtml": "Дає проаналізований <code>&lt;head&gt;</code> сторінки.",
-       "apihelp-parse-paramvalue-prop-modules": "Дає модулі ResourceLoader, використані на сторінці. Чи <kbd>jsconfigvars</kbd>, чи <kbd>encodedjsconfigvars</kbd> має бути запитано разом з <kbd>modules</kbd>.",
-       "apihelp-parse-paramvalue-prop-jsconfigvars": "Дає змінні конфігурації JavaScript, притаманні для сторінки.",
+       "apihelp-parse-paramvalue-prop-modules": "Ð\94аÑ\94 Ð¼Ð¾Ð´Ñ\83лÑ\96 ResourceLoader, Ð²Ð¸ÐºÐ¾Ñ\80иÑ\81Ñ\82анÑ\96 Ð½Ð° Ñ\81Ñ\82оÑ\80Ñ\96нÑ\86Ñ\96. Ð©Ð¾Ð± Ð·Ð°Ð²Ð°Ð½Ñ\82ажиÑ\82и, Ð²Ð¸ÐºÐ¾Ñ\80иÑ\81Ñ\82овÑ\83йÑ\82е <code>mw.loader.using()</code>. Ð§Ð¸ <kbd>jsconfigvars</kbd>, Ñ\87и <kbd>encodedjsconfigvars</kbd> Ð¼Ð°Ñ\94 Ð±Ñ\83Ñ\82и Ð·Ð°Ð¿Ð¸Ñ\82ано Ñ\80азом Ð· <kbd>modules</kbd>.",
+       "apihelp-parse-paramvalue-prop-jsconfigvars": "Дає змінні конфігурації JavaScript, притаманні для сторінки. Щоб застосувати, використайте <code>mw.config.set()</code>.",
        "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "Дає змінні конфігурації JavaScript, притаманні для сторінки, як рядок JSON.",
        "apihelp-parse-paramvalue-prop-indicators": "Дає HTML індикаторів стану сторінки, використаних на сторінці.",
        "apihelp-parse-paramvalue-prop-iwlinks": "Дає інтервікі-посилання в аналізованому вікітексті.",
        "apihelp-protect-description": "Змінити рівень захисту сторінки.",
        "apihelp-protect-param-title": "Заголовок сторінки для (зняття) захисту. Не може використовуватися разом із $1pageid.",
        "apihelp-protect-param-pageid": "ID сторінки для (зняття) захисту. Не може використовуватися разом з $1title.",
-       "apihelp-protect-param-protections": "Список рівнів захисту у форматі <kbd>action=level</kbd> (напр., <kbd>edit=sysop</kbd>).\n\n<strong>Примітка:</strong> Обмеження на дії, яких нема в списку, буде знято.",
+       "apihelp-protect-param-protections": "Список рівнів захисту у форматі <kbd>action=level</kbd> (напр., <kbd>edit=sysop</kbd>). Рівень <kbd>all</kbd> означає, що будь-хто може робити дію, тобто обмежень немає.\n\n<strong>Примітка:</strong> Обмеження на дії, яких нема в списку, буде знято.",
        "apihelp-protect-param-expiry": "Часові мітки закінчення. Якщо встановлена лише одна мітка, її буде використано для усіх захистів. Для безстрокового захисту використовуйте <kbd>infinite</kbd>, <kbd>indefinite</kbd>, <kbd>infinity</kbd> або <kbd>never</kbd>.",
        "apihelp-protect-param-reason": "Причина для (зняття) захисту.",
        "apihelp-protect-param-tags": "Змінити теги, що мають бути застосовані до запису в журналі захисту.",
        "apihelp-protect-param-watch": "Якщо вказано, додати сторінку, де додається/знімається захист, до списку спостереження поточного користувача.",
        "apihelp-protect-param-watchlist": "Беззастережно додати або вилучити сторінку зі списку спостереження поточного користувача, використати налаштування або не змінювати спостереження.",
        "apihelp-protect-example-protect": "Захистити сторінку.",
-       "apihelp-protect-example-unprotect": "Зняти захист зі сторінки, встановивши обмеження для <kbd>all</kbd>.",
+       "apihelp-protect-example-unprotect": "Зняти захист зі сторінки, встановивши обмеження для <kbd>all</kbd> (тобто будь-хто зможе робити дії).",
        "apihelp-protect-example-unprotect2": "Зняти захист з сторінки, встановивши відсутність обмежень.",
        "apihelp-purge-description": "Очистити кеш для вказаних заголовків.\n\nВимагає запиту POST, якщо користувач не ввійшов у систему.",
        "apihelp-purge-param-forcelinkupdate": "Оновити таблиці посилань.",
        "apihelp-query+authmanagerinfo-description": "Отримати інформацію про поточний стан автентифікації.",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "Перевірити, чи поточний стан автентифікації користувача є достатнім для даної конфіденційної операції.",
        "apihelp-query+authmanagerinfo-param-requestsfor": "Отримати інформацію про запити автентифікації, потрібні для даної дії автентифікації.",
-       "apihelp-query+filerepoinfo-example-login": "Вибірка запитів, що можуть бути використані при початку входу.",
-       "apihelp-query+filerepoinfo-example-login-merged": "Отримати запити, які можуть бути використані при початку входу, з об'єднаними полями форми.",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "Перевірити чи автентифікація є достатньою для дії <kbd>foo</kbd>.",
+       "apihelp-query+authmanagerinfo-example-login": "Вибірка запитів, що можуть бути використані при початку входу.",
+       "apihelp-query+authmanagerinfo-example-login-merged": "Отримати запити, які можуть бути використані при початку входу, з об'єднаними полями форми.",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "Перевірити чи автентифікація є достатньою для дії <kbd>foo</kbd>.",
        "apihelp-query+backlinks-description": "Знайти усі сторінки, що посилаються на подану сторінку.",
        "apihelp-query+backlinks-param-title": "Назва для пошуку. Не можна використати разом з <var>$1pageid</var>.",
        "apihelp-query+backlinks-param-pageid": "ID сторінки для пошуку. Не можна використати разом з <var>$1title</var>.",
        "apihelp-stashedit-param-section": "Номер розділу. <kbd>0</kbd> для вступного розділу, <kbd>new</kbd> для нового розділу.",
        "apihelp-stashedit-param-sectiontitle": "Назва нового розділу.",
        "apihelp-stashedit-param-text": "Вміст сторінки.",
+       "apihelp-stashedit-param-stashedtexthash": "Хеш вмісту сторінки з попереднього сховку, який треба використати натомість.",
        "apihelp-stashedit-param-contentmodel": "Модель вмісту нового вмісту.",
        "apihelp-stashedit-param-contentformat": "Формат серіалізації вмісту, використовуваний для введеного тексту.",
        "apihelp-stashedit-param-baserevid": "Ідентифікатор базової версії.",
        "apihelp-undelete-example-page": "Відновити сторінку <kbd>Main Page</kbd>.",
        "apihelp-undelete-example-revisions": "Відновити дві версії сторінки <kbd>Main Page</kbd>.",
        "apihelp-unlinkaccount-description": "Вилучити пов'язаний обліковий запис третьої сторони з поточного користувача.",
+       "apihelp-unlinkaccount-example-simple": "Здійснити спробу вилучити посилання поточного користувача для провайдера, асоційованого з <kbd>FooAuthenticationRequest</kbd>.",
        "apihelp-upload-description": "Завантажити файл, або отримати статус завантажень у процесі.\n\nДоступні декілька методів:\n* Завантажити вміст файлу напряму, використовуючи параметр <var>$1file</var>.\n* Завантажити файл шматками, використовуючи параметри <var>$1filesize</var>, <var>$1chunk</var>, та <var>$1offset</var>.\n* Змусити сервер Медіавікі отримати файл за URL, використовуючи параметр <var>$1url</var>.\n* Завершити раніше розпочате завантаження, яке не вдалось через попередження, використовуючи параметр <var>$1filekey</var>.\nЗауважте, що HTTP POST повинен бути здійснений як завантаження файлу (наприклад, використовуючи <code>multipart/form-data</code>)",
        "apihelp-upload-param-filename": "Цільова назва файлу.",
        "apihelp-upload-param-comment": "Коментар завантаження. Також використовується як початковий текст сторінок для нових файлів, якщо <var>$1text</var> не вказано.",
        "api-help-param-deprecated": "Застарілий.",
        "api-help-param-required": "Цей параметр є обов'язковим.",
        "api-help-datatypes-header": "Типи даних",
-       "api-help-datatypes": "Ð\94еÑ\8fкÑ\96 Ñ\82ипи Ð¿Ð°Ñ\80амеÑ\82Ñ\80Ñ\96в Ñ\83 Ð·Ð°Ð¿Ð¸Ñ\82аÑ\85 API Ð¿Ð¾Ñ\82Ñ\80ебÑ\83Ñ\8eÑ\82Ñ\8c Ñ\88иÑ\80Ñ\88ого Ð¿Ð¾Ñ\8fÑ\81неннÑ\8f:\n;boolean\n:Ð\9bогÑ\96Ñ\87нÑ\96 Ð¿Ð°Ñ\80амеÑ\82Ñ\80и Ð¿Ñ\80аÑ\86Ñ\8eÑ\8eÑ\82Ñ\8c Ñ\8fк Ð³Ð°Ð»Ð¾Ñ\87ки HTML: Ñ\8fкÑ\89о Ð¿Ð°Ñ\80амеÑ\82Ñ\80 Ð²ÐºÐ°Ð·Ð°Ð½Ð¾, Ð½Ðµ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾ Ð²Ñ\96д Ð·Ð½Ð°Ñ\87еннÑ\8f, Ð²Ñ\96н Ð²Ð²Ð°Ð¶Ð°Ñ\94Ñ\82Ñ\8cÑ\81Ñ\8f Ñ\96Ñ\81Ñ\82инним. Ð©Ð¾Ð± Ð·Ð½Ð°Ñ\87еннÑ\8f Ð±Ñ\83ло Ñ\85ибним, Ð¿Ñ\80опÑ\83Ñ\81Ñ\82Ñ\96Ñ\82Ñ\8c Ð¿Ð°Ñ\80амеÑ\82Ñ\80 Ð·Ð¾Ð²Ñ\81Ñ\96м.\n;timestamp\n:ЧаÑ\81овÑ\96 Ð¼Ñ\96Ñ\82ки Ð¼Ð¾Ð¶Ñ\83Ñ\82Ñ\8c Ð±Ñ\83Ñ\82и Ð²ÐºÐ°Ð·Ð°Ð½Ñ\96 Ñ\83 ÐºÑ\96лÑ\8cкоÑ\85 Ñ\84оÑ\80маÑ\82аÑ\85. Ð ÐµÐºÐ¾Ð¼ÐµÐ½Ð´Ñ\83Ñ\94Ñ\82Ñ\8cÑ\81Ñ\8f Ñ\87аÑ\81 Ñ\96 Ð´Ð°Ñ\82а Ð² ISO 8601. Ð£Ñ\81Ñ\96 Ð·Ð½Ð°Ñ\87еннÑ\8f Ñ\87аÑ\81Ñ\83 Ð² UTC, Ð±Ñ\83дÑ\8c\8fкÑ\96 Ñ\87аÑ\81овÑ\96 Ð¿Ð¾Ñ\8fÑ\81и Ñ\96гноÑ\80Ñ\83Ñ\8eÑ\82Ñ\8cÑ\81Ñ\8f.\n:* Ð\94аÑ\82а Ñ\96 Ñ\87аÑ\81 ISO 8601, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (пÑ\83нкÑ\82Ñ\83аÑ\86Ñ\96Ñ\8f Ñ\96 <kbd>Z</kbd> Ð½ÐµÐ¾Ð±Ð¾Ð²'Ñ\8fзоквÑ\96)\n:* Ð\94аÑ\82а Ñ\96 Ñ\87аÑ\81 ISO 8601 Ð· (Ñ\96гноÑ\80ованими) Ñ\87аÑ\81Ñ\82ками Ñ\81екÑ\83нди, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (деÑ\84Ñ\96Ñ\81и, Ð´Ð²Ð¾ÐºÑ\80апки Ñ\82а <kbd>Z</kbd> Ð½ÐµÐ¾Ð±Ð¾Ð²'Ñ\8fзковÑ\96)\n:* Ð¤Ð¾Ñ\80маÑ\82 MediaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Ð\97агалÑ\8cний Ñ\87иÑ\81ловий Ñ\84оÑ\80маÑ\82, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (необов'Ñ\8fзковий Ñ\87аÑ\81овий Ð¿Ð¾Ñ\8fÑ\81 <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd> Ð°Ð±Ð¾ <kbd>-<var>##</var></kbd> Ñ\96гноÑ\80Ñ\83Ñ\94Ñ\82Ñ\8cÑ\81Ñ\8f)\n:* Ð¤Ð¾Ñ\80маÑ\82 EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Ð¤Ð¾Ñ\80маÑ\82 RFC 2822 (Ñ\87аÑ\81овий Ð¿Ð¾Ñ\8fÑ\81 Ð¼Ð¾Ð¶Ðµ Ð±Ñ\83Ñ\82и Ð¾Ð¿Ñ\83Ñ\89ений), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Ð¤Ð¾Ñ\80маÑ\82 RFC 850 (Ñ\87аÑ\81овий Ð¿Ð¾Ñ\8fÑ\81 Ð¼Ð¾Ð¶Ðµ Ð±Ñ\83Ñ\82и Ð¾Ð¿Ñ\83Ñ\89ений), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Ð¤Ð¾Ñ\80маÑ\82 C ctime, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Ð¡ÐµÐºÑ\83нди Ð²Ñ\96д 1970-01-01T00:00:00Z Ñ\83 Ð²Ð¸Ð³Ð»Ñ\8fдÑ\96 Ñ\86Ñ\96лого Ñ\87иÑ\81ла Ð²Ñ\96д 1 Ð´Ð¾ 13 Ñ\86иÑ\84Ñ\80 (без <kbd>0</kbd>)\n:* Ð Ñ\8fдок <kbd>now</kbd>",
+       "api-help-datatypes": "Ð\92Ñ\85Ñ\96днÑ\96 Ð´Ð°Ð½Ñ\96 Ñ\83 MediaWiki Ð¼Ð°Ñ\8eÑ\82Ñ\8c Ð±Ñ\83Ñ\82и Ð² NFC-ноÑ\80малÑ\96зованомÑ\83 UTF-8. MediaWiki Ð¼Ð¾Ð¶Ðµ Ñ\81пÑ\80обÑ\83ваÑ\82и ÐºÐ¾Ð½Ð²ÐµÑ\80Ñ\82Ñ\83ваÑ\82и Ð²Ñ\85Ñ\96днÑ\96 Ð´Ð°Ð½Ñ\96 Ñ\96нÑ\88ого Ð²Ð¸Ð³Ð»Ñ\8fдÑ\83, Ð°Ð»Ðµ Ð²Ñ\96д Ñ\86Ñ\8cого Ð¼Ð¾Ð¶Ñ\83Ñ\82Ñ\8c Ð¿Ð¾Ñ\81Ñ\82Ñ\80аждаÑ\82и Ð´ÐµÑ\8fкÑ\96 Ð¾Ð¿ÐµÑ\80аÑ\86Ñ\96Ñ\97 (Ñ\8fк [[Special:ApiHelp/edit|Ñ\80едагÑ\83ваннÑ\8f]] Ð· Ð¿ÐµÑ\80евÑ\96Ñ\80коÑ\8e MD5).\n\nÐ\94еÑ\8fкÑ\96 Ñ\82ипи Ð¿Ð°Ñ\80амеÑ\82Ñ\80Ñ\96в Ñ\83 Ð·Ð°Ð¿Ð¸Ñ\82аÑ\85 API Ð¿Ð¾Ñ\82Ñ\80ебÑ\83Ñ\8eÑ\82Ñ\8c Ñ\88иÑ\80Ñ\88ого Ð¿Ð¾Ñ\8fÑ\81неннÑ\8f:\n;boolean\n:Ð\9bогÑ\96Ñ\87нÑ\96 Ð¿Ð°Ñ\80амеÑ\82Ñ\80и Ð¿Ñ\80аÑ\86Ñ\8eÑ\8eÑ\82Ñ\8c Ñ\8fк Ð³Ð°Ð»Ð¾Ñ\87ки HTML: Ñ\8fкÑ\89о Ð¿Ð°Ñ\80амеÑ\82Ñ\80 Ð²ÐºÐ°Ð·Ð°Ð½Ð¾, Ð½Ðµ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾ Ð²Ñ\96д Ð·Ð½Ð°Ñ\87еннÑ\8f, Ð²Ñ\96н Ð²Ð²Ð°Ð¶Ð°Ñ\94Ñ\82Ñ\8cÑ\81Ñ\8f Ñ\96Ñ\81Ñ\82инним. Ð©Ð¾Ð± Ð·Ð½Ð°Ñ\87еннÑ\8f Ð±Ñ\83ло Ñ\85ибним, Ð¿Ñ\80опÑ\83Ñ\81Ñ\82Ñ\96Ñ\82Ñ\8c Ð¿Ð°Ñ\80амеÑ\82Ñ\80 Ð·Ð¾Ð²Ñ\81Ñ\96м.\n;timestamp\n:ЧаÑ\81овÑ\96 Ð¼Ñ\96Ñ\82ки Ð¼Ð¾Ð¶Ñ\83Ñ\82Ñ\8c Ð±Ñ\83Ñ\82и Ð²ÐºÐ°Ð·Ð°Ð½Ñ\96 Ñ\83 ÐºÑ\96лÑ\8cкоÑ\85 Ñ\84оÑ\80маÑ\82аÑ\85. Ð ÐµÐºÐ¾Ð¼ÐµÐ½Ð´Ñ\83Ñ\94Ñ\82Ñ\8cÑ\81Ñ\8f Ñ\87аÑ\81 Ñ\96 Ð´Ð°Ñ\82а Ð² ISO 8601. Ð£Ñ\81Ñ\96 Ð·Ð½Ð°Ñ\87еннÑ\8f Ñ\87аÑ\81Ñ\83 Ð² UTC, Ð±Ñ\83дÑ\8c\8fкÑ\96 Ñ\87аÑ\81овÑ\96 Ð¿Ð¾Ñ\8fÑ\81и Ñ\96гноÑ\80Ñ\83Ñ\8eÑ\82Ñ\8cÑ\81Ñ\8f.\n:* Ð\94аÑ\82а Ñ\96 Ñ\87аÑ\81 ISO 8601, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd> (пÑ\83нкÑ\82Ñ\83аÑ\86Ñ\96Ñ\8f Ñ\96 <kbd>Z</kbd> Ð½ÐµÐ¾Ð±Ð¾Ð²'Ñ\8fзоквÑ\96)\n:* Ð\94аÑ\82а Ñ\96 Ñ\87аÑ\81 ISO 8601 Ð· (Ñ\96гноÑ\80ованими) Ñ\87аÑ\81Ñ\82ками Ñ\81екÑ\83нди, <kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd> (деÑ\84Ñ\96Ñ\81и, Ð´Ð²Ð¾ÐºÑ\80апки Ñ\82а <kbd>Z</kbd> Ð½ÐµÐ¾Ð±Ð¾Ð²'Ñ\8fзковÑ\96)\n:* Ð¤Ð¾Ñ\80маÑ\82 MediaWiki, <kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* Ð\97агалÑ\8cний Ñ\87иÑ\81ловий Ñ\84оÑ\80маÑ\82, <kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd> (необов'Ñ\8fзковий Ñ\87аÑ\81овий Ð¿Ð¾Ñ\8fÑ\81 <kbd>GMT</kbd>, <kbd>+<var>##</var></kbd> Ð°Ð±Ð¾ <kbd>-<var>##</var></kbd> Ñ\96гноÑ\80Ñ\83Ñ\94Ñ\82Ñ\8cÑ\81Ñ\8f)\n:* Ð¤Ð¾Ñ\80маÑ\82 EXIF, <kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Ð¤Ð¾Ñ\80маÑ\82 RFC 2822 (Ñ\87аÑ\81овий Ð¿Ð¾Ñ\8fÑ\81 Ð¼Ð¾Ð¶Ðµ Ð±Ñ\83Ñ\82и Ð¾Ð¿Ñ\83Ñ\89ений), <kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Ð¤Ð¾Ñ\80маÑ\82 RFC 850 (Ñ\87аÑ\81овий Ð¿Ð¾Ñ\8fÑ\81 Ð¼Ð¾Ð¶Ðµ Ð±Ñ\83Ñ\82и Ð¾Ð¿Ñ\83Ñ\89ений), <kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* Ð¤Ð¾Ñ\80маÑ\82 C ctime, <kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* Ð¡ÐµÐºÑ\83нди Ð²Ñ\96д 1970-01-01T00:00:00Z Ñ\83 Ð²Ð¸Ð³Ð»Ñ\8fдÑ\96 Ñ\86Ñ\96лого Ñ\87иÑ\81ла Ð²Ñ\96д 1 Ð´Ð¾ 13 Ñ\86иÑ\84Ñ\80 (без <kbd>0</kbd>)\n:* Ð Ñ\8fдок <kbd>now</kbd>\n;алÑ\8cÑ\82еÑ\80наÑ\82ивний Ñ\80оздÑ\96лÑ\8cник Ð±Ð°Ð³Ð°Ñ\82Ñ\8cоÑ\85 Ð·Ð½Ð°Ñ\87енÑ\8c\n:Ð\9fаÑ\80амеÑ\82Ñ\80и, Ñ\89о Ð¿Ñ\80иймаÑ\8eÑ\82Ñ\8c Ð±Ð°Ð³Ð°Ñ\82о Ð·Ð½Ð°Ñ\87енÑ\8c, Ð·Ð°Ð·Ð²Ð¸Ñ\87ай Ð¿Ð¾Ð´Ð°Ñ\8eÑ\82Ñ\8cÑ\81Ñ\8f Ð·Ñ\96 Ð·Ð½Ð°Ñ\87еннÑ\8fми, Ñ\80оздÑ\96леними Ð²ÐµÑ\80Ñ\82икалÑ\8cноÑ\8e Ñ\80иÑ\81коÑ\8e, Ð½Ð°Ð¿Ñ\80иклад, <kbd>param=value1|value2</kbd> Ð°Ð±Ð¾ <kbd>param=value1%7Cvalue2</kbd>. Ð¯ÐºÑ\89о Ð·Ð½Ð°Ñ\87еннÑ\8f Ð¿Ð¾Ð²Ð¸Ð½Ð½Ðµ Ð¼Ñ\96Ñ\81Ñ\82иÑ\82и Ð²ÐµÑ\80Ñ\82икалÑ\8cнÑ\83 Ñ\80иÑ\81кÑ\83, Ð²Ð¸ÐºÐ¾Ñ\80иÑ\81Ñ\82овÑ\83йÑ\82е Ñ\8fк Ñ\80оздÑ\96лÑ\8cник U+001F (Ñ\80оздÑ\96лÑ\8cник Ð¾Ð´Ð¸Ð½Ð¸Ñ\86Ñ\8c) ''Ñ\82а'' Ð¿Ð¾Ñ\81Ñ\82авÑ\82е U+001F Ð¿ÐµÑ\80ед Ð·Ð½Ð°Ñ\87еннÑ\8fм, Ð½Ð°Ð¿Ñ\80иклад, <kbd>param=%1Fvalue1%1Fvalue2</kbd>.",
        "api-help-param-type-limit": "Тип: ціле число або <kbd>max</kbd>",
        "api-help-param-type-integer": "Тип: {{PLURAL:$1|1=ціле число|2=список цілих чисел}}",
        "api-help-param-type-boolean": "Тип: логічний ([[Special:ApiHelp/main#main/datatypes|деталі]])",
        "api-help-param-type-timestamp": "Тип: {{PLURAL:$1|1=часова мітка|2=список часових міток}} ([[Special:ApiHelp/main#main/datatypes|дозволені формати]])",
        "api-help-param-type-user": "Тип: {{PLURAL:$1|1=ім'я користувача|2=список імен користувачів}}",
-       "api-help-param-list": "{{PLURAL:$1|1=Одне з наступних значень|2=Значення (розділені через <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list": "{{PLURAL:$1|1=Одне з наступних значень|2=Значення (розділені через <kbd>{{!}}</kbd> або [[Special:ApiHelp/main#main/datatypes|альтернативу]])}}: $2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Повинно бути пустим|Може бути пустим або $2}}",
        "api-help-param-limit": "Дозволено не більше $1.",
        "api-help-param-limit2": "Дозволено не більше $1 ($2 для ботів).",
        "api-help-param-integer-max": "{{PLURAL:$1|1=Значення має бути|2=Значення мають бути}} не більше $3.",
        "api-help-param-integer-minmax": "{{PLURAL:$1|1=Значення має бути|2=Значення мають бути}} між $2 і $3.",
        "api-help-param-upload": "Повинно бути надіслано у формі надсилання файлу використовуючи multipart/form-data.",
-       "api-help-param-multi-separate": "Розділіть значення з допомогою <kbd>|</kbd>.",
+       "api-help-param-multi-separate": "Розділіть значення з допомогою <kbd>|</kbd> або [[Special:ApiHelp/main#main/datatypes|альтернативу]].",
        "api-help-param-multi-max": "Максимальна кількість значень — {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} для ботів).",
        "api-help-param-default": "За замовчуванням: $1",
        "api-help-param-default-empty": "За замовчуванням: <span class=\"apihelp-empty\">(пусто)</span>",
        "api-help-permissions-granted-to": "{{PLURAL:$1|Надано|Надані}}: $2",
        "api-help-right-apihighlimits": "Використовувати вищі ліміти у запитах API (повільні запити: $1; швидкі запити: $2). Ліміти для повільних запитів також застосовуються до багатозначних параметрів.",
        "api-help-open-in-apisandbox": "<small>[відкрити в пісочниці]</small>",
+       "api-help-authmanager-general-usage": "Загальна процедура використання цього модуля така:\n# Отримайте доступні поля зі <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> за допомогою <kbd>amirequestsfor=$4</kbd>, а також токен  <kbd>$5</kbd> зі <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>.\n# Передайте ці поля користувачеві, і отримайте інформацію, якою він їх заповнить.\n# Напишіть до цього модуля, заповнивши <var>$1returnurl</var> та всі релевантні поля.\n# Перевірте <samp>status</samp> у відповіді.\n#* Якщо Ви отримали <samp>PASS</samp> або <samp>FAIL</samp>, роботу завершено. Операція або була успішною, або провалилася.\n#* Якщо Ви отримали <samp>UI</samp>, надішліть нові поля користувачеві й отримайте інформацію, якою він їх заповнить. Далі напишіть до цього модуля із <var>$1continue</var> та заповніть всі реелевантні поля, після чого повторіть крок 4.\n#* Якщо Ви отримали <samp>REDIRECT</samp>, направте користувача до <samp>redirecttarget</samp> і дочекайтеся повернення до <var>$1returnurl</var>. Тоді напишіть до цього модуля із <var>$1continue</var>, та з усіма полями, що були передані до повернутої URL-адреси, після чого повторіть крок 4.\n#* Якщо Ви отримали <samp>RESTART</samp>, це означає, що автентифікація спрацювала, але ми не маємо пов'язаного облікового запису користувача. Ви можете розцінити це як <samp>UI</samp>, або як <samp>FAIL</samp>.",
+       "api-help-authmanagerhelper-requests": "Використовувати ці автентифікаційні запити через <samp>id</samp>, що повертається з <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> за допомогою <kbd>amirequestsfor=$1</kbd>, або з попередньої відповіді з цього модуля.",
+       "api-help-authmanagerhelper-request": "Використовувати цей автентифікаційний запит через <samp>id</samp>, що повертається з <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> за допомогою <kbd>amirequestsfor=$1</kbd>.",
        "api-help-authmanagerhelper-messageformat": "Формат до використання для повернення повідомлень.",
        "api-help-authmanagerhelper-mergerequestfields": "Об'єднати інформацію всіх автентифікаційних запитів у один масив.",
        "api-help-authmanagerhelper-preservestate": "Зберегти статус з попередньої спроби входу, що не вдалась, якщо можливо.",
+       "api-help-authmanagerhelper-returnurl": "URL-адреса повернення для сторонніх автентифікаційних потоків повинна бути абсолютною. Обов'язковим є це, або <var>$1continue</var>. \n\nПри отриманні відповіді <samp>REDIRECT</samp>, найбільш звичною Вашою дією буде відкриття браузерного чи іншого веб-перегляду вказаного URL-посилання для стороннього потоку автентифікації. Коли ця операція буде завершена, стороння програма перенаправить веб-переглядач на цю URL-адресу. Ви повинні видобувати будь-які параметри запитів або POST-параметри із цієї URL-адреси, і передавати їх як запит <var>$1continue</var> до цього модуля API.",
+       "api-help-authmanagerhelper-continue": "Цей запит є продовженням після попередньої відповіді <samp>UI</samp> або <samp>REDIRECT</samp>. Або це, або <var>$1returnurl</var> є обов'язковим.",
+       "api-help-authmanagerhelper-additional-params": "Цей модуль приймає додаткові параметри, залежно від доступних автентифікаційних запитів. Використовуйте <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> за допомогою <kbd>amirequestsfor=$1</kbd> (або попередню відповідь від цього модуля, якщо це застосовно), аби визначити доступні запити та поля, які вони використовують.",
        "api-credits-header": "Автор(и)",
        "api-credits": "Розробники API:\n* Roan Kattouw (головний розробник вер. 2007–2009)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Yuri Astrakhan (творець, головний розробник вер. 2006 – вер. 2007)\n* Brad Jorsch (головний розробник 2013 – тепер)\n\nБудь ласка, надсилайте свої коментарі, пропозиції та запитання на mediawiki-api@lists.wikimedia.org\nабо зафайліть звіт про баґ на https://phabricator.wikimedia.org/."
 }
index 34bb4c2..573748d 100644 (file)
@@ -20,7 +20,8 @@
                        "Hzy980512",
                        "PhiLiP",
                        "Arthur2e5",
-                       "損齋"
+                       "損齋",
+                       "Myy730"
                ]
        },
        "apihelp-main-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收到错误请求时,HTTP header将会返回一个包含\"MediaWiki-API-Error\"的值,随后header的值与error code将会送回并设置为相同的值。详细信息请参阅[[mw:API:Errors_and_warnings|API: 错误与警告]]。\n\n<strong>测试中:</strong>测试API请求的易用性,请参见[[Special:ApiSandbox]]。",
@@ -33,7 +34,7 @@
        "apihelp-main-param-requestid": "任何在此提供的值将包含在响应中。可能可以用以区别请求。",
        "apihelp-main-param-servedby": "包含保存结果请求的主机名。",
        "apihelp-main-param-curtimestamp": "在结果中包括当前时间戳。",
-       "apihelp-main-param-origin": "当通过跨域名AJAX请求(CORS)访问API时,设置此作为起始域名。这必须包括在任何pre-flight请求中,并因此必须是请求的URI的一部分(而不是POST正文)。这必须匹配<code>Origin</code>中的一个起点:从头到底,因此它已经设置为像<kbd>https://zh.wikipedia.org</kbd>或<kbd>https://meta.wikimedia.org</kbd>的东西。如果此参数不匹配<code>Origin</code>页顶,就返回403错误响应。如果此参数匹配<code>Origin</code>页顶并且起点被白名单,将设置一个<code>Access-Control-Allow-Origin</code>开头。",
+       "apihelp-main-param-origin": "当通过跨域名AJAX请求(CORS)访问API时,设置此作为起始域名。这必须包括在任何pre-flight请求中,并因此必须是请求的URI的一部分(而不是POST正文)。\n\n对于已验证的请求,这必须正确匹配<code>Origin</code>标头中的原点之一,因此它已经设置为像<kbd>https://zh.wikipedia.org</kbd>或<kbd>https://meta.wikimedia.org</kbd>的东西。如果此参数不匹配<code>Origin</code>页顶,就返回403错误响应。如果此参数匹配<code>Origin</code>页顶并且起点被白名单,将设置<code>Access-Control-Allow-Origin</code>和<code>Access-Control-Allow-Credentials</code>开头。\n\n对于未验证的请求,会指定值<kbd>*</kbd>。这将导致<code>Access-Control-Allow-Origin</code>标头被设置,但<code>Access-Control-Allow-Credentials</code>将为<code>false</code>,且所有用户特定数据将受限制。",
        "apihelp-main-param-uselang": "用于消息翻译的语言。<kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>与<kbd>siprop=languages</kbd>可返回语言代码列表,或指定<kbd>user</kbd>以使用当前用户的语言设置,或指定<kbd>content</kbd>以使用此wiki的内容语言。",
        "apihelp-block-description": "封禁一位用户。",
        "apihelp-block-param-user": "您要封禁的用户、IP地址或IP地址段。",
@@ -83,6 +84,9 @@
        "apihelp-createaccount-param-language": "要为用户设置为默认值的语言代码(可选,默认为内容语言)。",
        "apihelp-createaccount-example-pass": "创建用户<kbd>testuser</kbd>和密码<kbd>test123</kbd>。",
        "apihelp-createaccount-example-mail": "创建用户<kbd>testmailuser</kbd>并电邮发送一个随机生成的密码。",
+       "apihelp-cspreport-description": "由浏览器使用以报告违反内容安全方针的内容。此模块应永不使用,除了在被CSP兼容的浏览器自动使用时。",
+       "apihelp-cspreport-param-reportonly": "标记作为来自监视方针的报告,而不是执行方针的报告",
+       "apihelp-cspreport-param-source": "生成引发此报告的CSP标头的事物",
        "apihelp-delete-description": "删除一个页面。",
        "apihelp-delete-param-title": "要删除的页面标题。不能与<var>$1pageid</var>一起使用。",
        "apihelp-delete-param-pageid": "要删除的页面的页面 ID。不能与<var>$1title</var>一起使用。",
        "apihelp-linkaccount-example-link": "开始从<kbd>Example</kbd>链接至账户的过程。",
        "apihelp-login-description": "登录并获取身份验证Cookie。\n\n此操作只应与[[Special:BotPasswords]]一起使用;用于主账户登录的方式已弃用,并可能在没有警告的情况下失败。要安全登录主账户,请使用<kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>。",
        "apihelp-login-description-nobotpasswords": "登录并获取身份验证Cookie。\n\n此操作已弃用,并可能在没有警告的情况下失败。要安全登录,请使用<kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>。",
-       "apihelp-login-description-nonauthmanager": "登录并获取身份验证Cookie。\n\n在成功登录的情况下,所需的Cookie将包含在HTTP响应头中。在登录失败的情况下,进一步的尝试可能会被自动密码猜解攻击的限制所遏制。",
        "apihelp-login-param-name": "用户名。",
        "apihelp-login-param-password": "密码。",
        "apihelp-login-param-domain": "域名(可选)。",
        "apihelp-options-example-change": "更改<kbd>skin</kbd>和<kbd>hideminor</kbd>设置。",
        "apihelp-options-example-complex": "重置所有设置,然后设置<kbd>skin</kbd>和<kbd>nickname</kbd>。",
        "apihelp-paraminfo-description": "获得关于API模块的信息。",
-       "apihelp-paraminfo-param-modules": "模块名称(<var>action</var>和<var>format</var>参数值,或<kbd>main</kbd>)的列表。可通过<kbd>+</kbd>指定子模块。",
+       "apihelp-paraminfo-param-modules": "模块名称(<var>action</var>和<var>format</var>参数值,或<kbd>main</kbd>)的列表。可通过<kbd>+</kbd>指定子模块,或通过<kbd>+*</kbd>指定所有子模块,或通过<kbd>+**</kbd>指定所有递归子模块。",
        "apihelp-paraminfo-param-helpformat": "帮助字符串的格式。",
        "apihelp-paraminfo-param-querymodules": "查询模块名称(<var>prop</var>、<var>meta</var>或<var>list</var>参数值)的列表。使用<kbd>$1modules=query+foo</kbd>而不是<kbd>$1querymodules=foo</kbd>。",
        "apihelp-paraminfo-param-mainmodule": "获取有关主要(最高级)模块的信息。也可使用<kbd>$1modules=main</kbd>。",
        "apihelp-paraminfo-param-pagesetmodule": "获取有关页面设置模块(提供titles=和朋友)的信息。",
        "apihelp-paraminfo-param-formatmodules": "格式模块名称(<var>format</var>参数的值)的列表。也可使用<var>$1modules</var>。",
        "apihelp-paraminfo-example-1": "显示<kbd>[[Special:ApiHelp/parse|action=parse]]</kbd>、<kbd>[[Special:ApiHelp/jsonfm|format=jsonfm]]</kbd>、<kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd>和<kbd>[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]</kbd>的信息。",
+       "apihelp-paraminfo-example-2": "显示<kbd>[[Special:ApiHelp/query|action=query]]</kbd>的所有子模块的信息。",
        "apihelp-parse-description": "解析内容并返回解析器输出。\n\n参见<kbd>[[Special:ApiHelp/query|action=query]]</kbd>的各种prop-module以从页面的当前版本获得信息。\n\n这里有几种方法可以指定解析的文本:\n# 指定一个页面或修订,使用<var>$1page</var>、<var>$1pageid</var>或<var>$1oldid</var>。\n# 明确指定内容,使用<var>$1text</var>、<var>$1title</var>和<var>$1contentmodel</var>。\n# 只指定一段摘要解析。<var>$1prop</var>应提供一个空值。",
        "apihelp-parse-param-title": "文本属于的页面标题。如果省略,<var>$1contentmodel</var>就必须被指定,且[[API]]将作为标题使用。",
        "apihelp-parse-param-text": "要解析的文本。使用<var>$1title</var>或<var>$1contentmodel</var>以控制内容模型。",
        "apihelp-parse-paramvalue-prop-sections": "在被解析的wiki文本中提供段落。",
        "apihelp-parse-paramvalue-prop-revid": "添加被解析页面的修订ID。",
        "apihelp-parse-paramvalue-prop-displaytitle": "为被解析的wiki文本添加标题。",
-       "apihelp-parse-paramvalue-prop-headitems": "提供项目以插入至页面的<code>&lt;head&gt;</code>。",
+       "apihelp-parse-paramvalue-prop-headitems": "<span class=\"apihelp-deprecated\">已弃用。</span>提供项目以插入至页面的<code>&lt;head&gt;</code>。",
        "apihelp-parse-paramvalue-prop-headhtml": "提供页面的被解析<code>&lt;head&gt;</code>。",
-       "apihelp-parse-paramvalue-prop-modules": "提供在页面中使用的ResourceLoader模块。无论<kbd>jsconfigvars</kbd>还是<kbd>encodedjsconfigvars</kbd>都必须与<kbd>modules</kbd>共同被请求。",
-       "apihelp-parse-paramvalue-prop-jsconfigvars": "针对页面提供JavaScript配置变量。",
+       "apihelp-parse-paramvalue-prop-modules": "提供在页面中使用的ResourceLoader模块。要加载,请使用<code>mw.loader.using()</code>。无论<kbd>jsconfigvars</kbd>还是<kbd>encodedjsconfigvars</kbd>都必须与<kbd>modules</kbd>共同被请求。",
+       "apihelp-parse-paramvalue-prop-jsconfigvars": "针对页面提供JavaScript配置变量。要应用,请使用<code>mw.config.set()</code>。",
        "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "针对页面提供JavaScript配置变量为一个JSON字符串。",
        "apihelp-parse-paramvalue-prop-indicators": "提供页面上使用的页面状态指示器的HTML。",
        "apihelp-parse-paramvalue-prop-iwlinks": "在被解析的wiki文本中提供跨wiki链接。",
        "apihelp-protect-description": "更改页面的保护等级。",
        "apihelp-protect-param-title": "要(解除)保护的页面标题。不能与$1pageid一起使用。",
        "apihelp-protect-param-pageid": "要(解除)保护的页面ID。不能与$1title一起使用。",
-       "apihelp-protect-param-protections": "保护等级列表,格式:<kbd>action=level</kbd>(例如<kbd>edit=sysop</kbd>)。\n\n<strong>注意:</strong>未列出的操作将移除限制。",
+       "apihelp-protect-param-protections": "保护等级列表,格式:<kbd>action=level</kbd>(例如<kbd>edit=sysop</kbd>)。等级<kbd>all</kbd>意味着任何人都可以执行操作,也就是说没有限制。\n\n<strong>注意:</strong>未列出的操作将移除限制。",
        "apihelp-protect-param-expiry": "到期时间戳。如果只有一个时间戳被设置,它将被用于所有保护。使用<kbd>infinite</kbd>、<kbd>indefinite</kbd>、<kbd>infinity</kbd>或<kbd>never</kbd>用于永不过期的保护。",
        "apihelp-protect-param-reason": "(解除)保护的原因。",
        "apihelp-protect-param-tags": "要在保护日志中应用到实体的更改标签。",
        "apihelp-protect-param-watch": "如果设置,就加入已开始(解除)保护的页面至当前用户的监视列表。",
        "apihelp-protect-param-watchlist": "无条件地将页面加入至当前用户的监视列表或将其移除,使用设置或不更改监视。",
        "apihelp-protect-example-protect": "保护一个页面。",
-       "apihelp-protect-example-unprotect": "通过设置限制为<kbd>all</kbd>解除保护一个页面。",
+       "apihelp-protect-example-unprotect": "通过设置限制为<kbd>all</kbd>解除保护一个页面(就是说任何人都可以执行操作)。",
        "apihelp-protect-example-unprotect2": "通过设置没有限制解除保护一个页面。",
        "apihelp-purge-description": "为指定标题刷新缓存。\n\n如果用户尚未登录的话,就需要POST请求。",
        "apihelp-purge-param-forcelinkupdate": "更新链接表。",
        "apihelp-query+authmanagerinfo-description": "检索有关当前身份验证状态的信息。",
        "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "测试用户当前的身份验证状态是否足够用于指定的安全敏感操作。",
        "apihelp-query+authmanagerinfo-param-requestsfor": "取得指定身份验证操作所需的有关身份验证请求的信息。",
-       "apihelp-query+filerepoinfo-example-login": "检索当开始登录时可能使用的请求。",
-       "apihelp-query+filerepoinfo-example-login-merged": "检索当开始登录时可能使用的请求,并合并表单字段。",
-       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "测试身份验证对操作<kbd>foo</kbd>是否足够。",
+       "apihelp-query+authmanagerinfo-example-login": "检索当开始登录时可能使用的请求。",
+       "apihelp-query+authmanagerinfo-example-login-merged": "检索当开始登录时可能使用的请求,并合并表单字段。",
+       "apihelp-query+authmanagerinfo-example-securitysensitiveoperation": "测试身份验证对操作<kbd>foo</kbd>是否足够。",
        "apihelp-query+backlinks-description": "查找所有链接至指定页面的页面。",
        "apihelp-query+backlinks-param-title": "要搜索的标题。不能与<var>$1pageid</var>一起使用。",
        "apihelp-query+backlinks-param-pageid": "要搜索的页面ID。不能与<var>$1title</var>一起使用。",
        "apihelp-query+random-param-filterredir": "如何过滤重定向。",
        "apihelp-query+random-example-simple": "从主名字空间返回两个随机页面。",
        "apihelp-query+random-example-generator": "返回有关来自主名字空间的两个随机页面的页面信息。",
-       "apihelp-query+recentchanges-description": "举最近更改。",
+       "apihelp-query+recentchanges-description": "举最近更改。",
        "apihelp-query+recentchanges-param-start": "枚举的起始时间戳。",
        "apihelp-query+recentchanges-param-end": "枚举的结束时间戳。",
        "apihelp-query+recentchanges-param-namespace": "过滤更改为仅限这些名字空间。",
        "apihelp-query+recentchanges-paramvalue-prop-ids": "添加页面ID、最近更改ID和新旧修订的ID。",
        "apihelp-query+recentchanges-paramvalue-prop-sizes": "添加新旧页面长度(字节)。",
        "apihelp-query+recentchanges-paramvalue-prop-redirect": "如果页面是重定向的话,标记编辑。",
-       "apihelp-query+recentchanges-paramvalue-prop-patrolled": "Tags patrollable edits as being patrolled or unpatrolled.",
-       "apihelp-query+recentchanges-paramvalue-prop-loginfo": "Adds log information (log ID, log type, etc) to log entries.",
+       "apihelp-query+recentchanges-paramvalue-prop-patrolled": "将可巡查编辑标记为已巡查或未巡查。",
+       "apihelp-query+recentchanges-paramvalue-prop-loginfo": "添加日志信息(日志ID、日志类型等)至日志记录。",
        "apihelp-query+recentchanges-paramvalue-prop-tags": "列举条目的标签。",
        "apihelp-query+recentchanges-paramvalue-prop-sha1": "Adds the content checksum for entries associated with a revision.",
        "apihelp-query+recentchanges-param-token": "请改用<kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>。",
        "apihelp-query+search-paramvalue-prop-snippet": "Adds a parsed snippet of the page.",
        "apihelp-query+search-paramvalue-prop-titlesnippet": "Adds a parsed snippet of the page title.",
        "apihelp-query+search-paramvalue-prop-redirectsnippet": "添加被解析的重定向标题的片段。",
-       "apihelp-query+search-paramvalue-prop-redirecttitle": "Adds the title of the matching redirect.",
+       "apihelp-query+search-paramvalue-prop-redirecttitle": "添加匹配的重定向的标题。",
        "apihelp-query+search-paramvalue-prop-sectionsnippet": "Adds a parsed snippet of the matching section title.",
        "apihelp-query+search-paramvalue-prop-sectiontitle": "Adds the title of the matching section.",
        "apihelp-query+search-paramvalue-prop-categorysnippet": "Adds a parsed snippet of the matching category.",
-       "apihelp-query+search-paramvalue-prop-isfilematch": "Adds a boolean indicating if the search matched file content.",
+       "apihelp-query+search-paramvalue-prop-isfilematch": "添加布尔值,表明搜索是否匹配文件内容。",
        "apihelp-query+search-paramvalue-prop-score": "<span class=\"apihelp-deprecated\">已弃用并已忽略。</span>",
        "apihelp-query+search-paramvalue-prop-hasrelated": "<span class=\"apihelp-deprecated\">已弃用并已忽略。</span>",
        "apihelp-query+search-param-limit": "返回的总计页面数。",
        "apihelp-query+search-param-enablerewrites": "启用内部查询重写。一些搜索后端可以重写查询到它认为会给出更好结果的地方,例如纠正拼写错误。",
        "apihelp-query+search-example-simple": "搜索<kbd>meaning</kbd>。",
        "apihelp-query+search-example-text": "搜索文本<kbd>meaning</kbd>。",
-       "apihelp-query+search-example-generator": "è\8e·å¾\97有关搜索<kbd>meaning</kbd>返回页面的页面信息。",
+       "apihelp-query+search-example-generator": "è\8e·å\8f\96有关搜索<kbd>meaning</kbd>返回页面的页面信息。",
        "apihelp-query+siteinfo-description": "返回有关网站的一般信息。",
        "apihelp-query+siteinfo-param-prop": "要获取的信息:",
        "apihelp-query+siteinfo-paramvalue-prop-general": "全部系统信息。",
        "apihelp-stashedit-param-section": "段落数。<kbd>0</kbd>用于首段,<kbd>new</kbd>用于新的段落。",
        "apihelp-stashedit-param-sectiontitle": "新段落的标题。",
        "apihelp-stashedit-param-text": "页面内容。",
+       "apihelp-stashedit-param-stashedtexthash": "要使用的来自先前暂存处的页面内容哈希。",
        "apihelp-stashedit-param-contentmodel": "新内容的内容模型。",
        "apihelp-stashedit-param-contentformat": "用于输入文本的内容序列化格式。",
        "apihelp-stashedit-param-baserevid": "基础修订的修订ID。",
        "api-help-param-deprecated": "已弃用。",
        "api-help-param-required": "这个参数是必须的。",
        "api-help-datatypes-header": "数据类型",
-       "api-help-datatypes": "一些在API请求中的参数类型需要更进一步解释:\n;boolean\n:布尔参数就像HTML复选框一样工作:如果指定参数,无论何值都被认为是真。如果要假值,则可完全忽略参数。\n;timestamp\n:时间戳可被指定为很多格式。推荐使用ISO 8601日期和时间标准。所有时间为UTC时间,包含的任何时区会被忽略。\n:* ISO 8601日期和时间,<kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd>(标点和<kbd>Z</kbd>是可选项)\n:* 带小数秒(会被忽略)的ISO 8601日期和时间,<kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd>(破折号、括号和<kbd>Z</kbd>是可选的)\n:* MediaWiki格式,<kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* 一般数字格式,<kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>(<kbd>GMT</kbd>、<kbd>+<var>##</var></kbd>或<kbd>-<var>##</var></kbd>的可选时区会被忽略)\n:* EXIF格式,<kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 2822格式(时区可能会被省略),<kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 850格式(时区可能会被省略),<kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* C ctime格式,<kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* 秒数是从1970-01-01T00:00:00Z开始,作为1到13位数的整数(除了<kbd>0</kbd>)\n:* 字符串<kbd>now</kbd>",
+       "api-help-datatypes": "至MediaWiki的输入应为NFC标准化的UTF-8。MediaWiki可以尝试转换其他输入,但这可能导致一些操作失败(例如[[Special:ApiHelp/edit|edits]]与MD5校验)。\n\n一些在API请求中的参数类型需要更进一步解释:\n;boolean\n:布尔参数就像HTML复选框一样工作:如果指定参数,无论何值都被认为是真。如果要假值,则可完全忽略参数。\n;timestamp\n:时间戳可被指定为很多格式。推荐使用ISO 8601日期和时间标准。所有时间为UTC时间,包含的任何时区会被忽略。\n:* ISO 8601日期和时间,<kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>Z</kbd>(标点和<kbd>Z</kbd>是可选项)\n:* 带小数秒(会被忽略)的ISO 8601日期和时间,<kbd><var>2001</var>-<var>01</var>-<var>15</var>T<var>14</var>:<var>56</var>:<var>00</var>.<var>00001</var>Z</kbd>(破折号、括号和<kbd>Z</kbd>是可选的)\n:* MediaWiki格式,<kbd><var>2001</var><var>01</var><var>15</var><var>14</var><var>56</var><var>00</var></kbd>\n:* 一般数字格式,<kbd><var>2001</var>-<var>01</var>-<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>(<kbd>GMT</kbd>、<kbd>+<var>##</var></kbd>或<kbd>-<var>##</var></kbd>的可选时区会被忽略)\n:* EXIF格式,<kbd><var>2001</var>:<var>01</var>:<var>15</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 2822格式(时区可能会被省略),<kbd><var>Mon</var>, <var>15</var> <var>Jan</var> <var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* RFC 850格式(时区可能会被省略),<kbd><var>Monday</var>, <var>15</var>-<var>Jan</var>-<var>2001</var> <var>14</var>:<var>56</var>:<var>00</var></kbd>\n:* C ctime格式,<kbd><var>Mon</var> <var>Jan</var> <var>15</var> <var>14</var>:<var>56</var>:<var>00</var> <var>2001</var></kbd>\n:* 秒数是从1970-01-01T00:00:00Z开始,作为1到13位数的整数(除了<kbd>0</kbd>)\n:* 字符串<kbd>now</kbd>\n;替代多值分隔符\n:使用多个值的参数通常会与管道符号分隔的值一起提交,例如<kbd>param=value1|value2</kbd>或<kbd>param=value1%7Cvalue2</kbd>。如果值必须包含管道符号,使用U+001F(单位分隔符)作为分隔符,''并''在值前加前缀U+001F,例如<kbd>param=%1Fvalue1%1Fvalue2</kbd>。",
        "api-help-param-type-limit": "类型:整数或<kbd>max</kbd>",
        "api-help-param-type-integer": "类型:{{PLURAL:$1|1=整数|2=整数列表}}",
        "api-help-param-type-boolean": "类型:布尔值([[Special:ApiHelp/main#main/datatypes|详细信息]])",
        "api-help-param-type-timestamp": "类型:{{PLURAL:$1|1=时间戳|2=时间戳列表}}([[Special:ApiHelp/main#main/datatypes|允许格式]])",
        "api-help-param-type-user": "类型:{{PLURAL:$1|1=用户名|2=用户名列表}}",
-       "api-help-param-list": "{{PLURAL:$1|1=以下值中的一个|2=值(以<kbd>{{!}}</kbd>分隔)}}:$2",
+       "api-help-param-list": "{{PLURAL:$1|1=以下值中的一个|2=值(以<kbd>{{!}}</kbd>或[[Special:ApiHelp/main#main/datatypes|替代物]]分隔)}}:$2",
        "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=必须为空|可以为空,或$2}}",
        "api-help-param-limit": "不允许超过$1。",
        "api-help-param-limit2": "不允许超过$1个(对于机器人则是$2个)。",
        "api-help-param-integer-max": "{{PLURAL:$1|值}}必须不大于$3。",
        "api-help-param-integer-minmax": "{{PLURAL:$1|值}}必须介于$2和$3之间。",
        "api-help-param-upload": "必须被公布为使用multipart/form-data的一次文件上传。",
-       "api-help-param-multi-separate": "通过“<kbd>|</kbd>”隔开各值。",
+       "api-help-param-multi-separate": "通过<kbd>|</kbd>或[[Special:ApiHelp/main#main/datatypes|替代物]]隔开各值。",
        "api-help-param-multi-max": "值的最高数字是{{PLURAL:$1|$1}}(对于机器人则是{{PLURAL:$2|$2}})。",
        "api-help-param-default": "默认:$1",
        "api-help-param-default-empty": "默认:<span class=\"apihelp-empty\">(空)</span>",
        "api-help-permissions-granted-to": "{{PLURAL:$1|授予}}:$2",
        "api-help-right-apihighlimits": "在API查询中使用更高的上限(慢查询:$1;快查询:$2)。慢查询的限制也适用于多值参数。",
        "api-help-open-in-apisandbox": "<small>[在沙盒中打开]</small>",
-       "api-help-authmanager-general-usage": "使用此模块的一般程序是:\n# 通过<kbd>amirequestsfor=$4</kbd>取得来自<kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd>的可用字段,和来自<kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>的<kbd>$5</kbd>令牌。\n# Present the fields to the user, and obtain their submission.\n# Post to this module, supplying <var>$1returnurl</var> and any relevant fields.\n# Check the <samp>status</samp> in the response.\n#* If you received <samp>PASS</samp> or <samp>FAIL</samp>, you're done. The operation either succeeded or it didn't.\n#* If you received <samp>UI</samp>, present the new fields to the user and obtain their submission. Then post to this module with <var>$1continue</var> and the relevant fields set, and repeat step 4.\n#* If you received <samp>REDIRECT</samp>, direct the user to the <samp>redirecttarget</samp> and wait for the return to <var>$1returnurl</var>. Then post to this module with <var>$1continue</var> and any fields passed to the return URL, and repeat step 4.\n#* If you received <samp>RESTART</samp>, that means the authentication worked but we don't have a linked user account. You might treat this as <samp>UI</samp> or as <samp>FAIL</samp>.",
+       "api-help-authmanager-general-usage": "使用此模块的一般程序是:\n# 通过<kbd>amirequestsfor=$4</kbd>取得来自<kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd>的可用字段,和来自<kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>的<kbd>$5</kbd>令牌。\n# 向用户显示字段,并获得其提交内容。\n# 发送至此模块,提供<var>$1returnurl</var>及任何相关字段。\n# 在响应中检查<samp>status</samp>。\n#* 如果您收到了<samp>PASS</samp>或<samp>FAIL</samp>,您已经完成。The operation either succeeded or it didn't.\n#* 如果您收到了<samp>UI</samp>,present the new fields to the user and obtain their submission. Then post to this module with <var>$1continue</var> and the relevant fields set, and repeat step 4.\n#* 如果您收到了<samp>REDIRECT</samp>,direct the user to the <samp>redirecttarget</samp> and wait for the return to <var>$1returnurl</var>. Then post to this module with <var>$1continue</var> and any fields passed to the return URL, and repeat step 4.\n#* 如果您收到了<samp>RESTART</samp>,that means the authentication worked but we don't have a linked user account. You might treat this as <samp>UI</samp> or as <samp>FAIL</samp>.",
        "api-help-authmanagerhelper-request": "使用此身份验证请求,通过返回自<kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd>的<samp>id</samp>与<kbd>amirequestsfor=$1</kbd>。",
        "api-help-authmanagerhelper-messageformat": "返回消息使用的格式。",
        "api-help-authmanagerhelper-mergerequestfields": "合并用于所有身份验证请求的字段信息至一个数组中。",
+       "api-help-authmanagerhelper-preservestate": "从之前失败的登录尝试中保持状态,如果可能。",
+       "api-help-authmanagerhelper-continue": "此请求是在早先的<samp>UI</samp>或<samp>REDIRECT</samp>响应之后的附加请求。必需此值或<var>$1returnurl</var>。",
        "api-help-authmanagerhelper-additional-params": "此模块允许额外参数,取决于可用的身份验证请求。使用<kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd>与<kbd>amirequestsfor=$1</kbd>(或之前来自此模块的相应,如果可以)以决定可用请求及其使用的字段。",
        "api-credits-header": "制作人员",
        "api-credits": "API 开发人员:\n* Yuri Astrakhan(创建者,2006年9月~2007年9月的开发组领导)\n* Roan Kattouw(2007年9月~2009年的开发组领导)\n* Victor Vasiliev\n* Bryan Tong Minh\n* Sam Reed\n* Brad Jorsch(2013年至今的开发组领导)\n\n请将您的评论、建议和问题发送至mediawiki-api@lists.wikimedia.org,或提交错误请求至https://phabricator.wikimedia.org/。"
index 6db5f2c..89a22f8 100644 (file)
@@ -37,8 +37,46 @@ use WebRequest;
  * In the future, it may also serve as the entry point to the authorization
  * system.
  *
+ * If you are looking at this because you are working on an extension that creates its own
+ * login or signup page, then 1) you really shouldn't do that, 2) if you feel you absolutely
+ * have to, subclass AuthManagerSpecialPage or build it on the client side using the clientlogin
+ * or the createaccount API. Trying to call this class directly will very likely end up in
+ * security vulnerabilities or broken UX in edge cases.
+ *
+ * If you are working on an extension that needs to integrate with the authentication system
+ * (e.g. by providing a new login method, or doing extra permission checks), you'll probably
+ * need to write an AuthenticationProvider.
+ *
+ * If you want to create a "reserved" user programmatically, User::newSystemUser() might be what
+ * you are looking for. If you want to change user data, use User::changeAuthenticationData().
+ * Code that is related to some SessionProvider or PrimaryAuthenticationProvider can
+ * create a (non-reserved) user by calling AuthManager::autoCreateUser(); it is then the provider's
+ * responsibility to ensure that the user can authenticate somehow (see especially
+ * PrimaryAuthenticationProvider::autoCreatedAccount()).
+ * If you are writing code that is not associated with such a provider and needs to create accounts
+ * programmatically for real users, you should rethink your architecture. There is no good way to
+ * do that as such code has no knowledge of what authentication methods are enabled on the wiki and
+ * cannot provide any means for users to access the accounts it would create.
+ *
+ * The two main control flows when using this class are as follows:
+ * * Login, user creation or account linking code will call getAuthenticationRequests(), populate
+ *   the requests with data (by using them to build a HTMLForm and have the user fill it, or by
+ *   exposing a form specification via the API, so that the client can build it), and pass them to
+ *   the appropriate begin* method. That will return either a success/failure response, or more
+ *   requests to fill (either by building a form or by redirecting the user to some external
+ *   provider which will send the data back), in which case they need to be submitted to the
+ *   appropriate continue* method and that step has to be repeated until the response is a success
+ *   or failure response. AuthManager will use the session to maintain internal state during the
+ *   process.
+ * * Code doing an authentication data change will call getAuthenticationRequests(), select
+ *   a single request, populate it, and pass it to allowsAuthenticationDataChange() and then
+ *   changeAuthenticationData(). If the data change is user-initiated, the whole process needs
+ *   to be preceded by a call to securitySensitiveOperationStatus() and aborted if that returns
+ *   a non-OK status.
+ *
  * @ingroup Auth
  * @since 1.27
+ * @see https://www.mediawiki.org/wiki/Manual:SessionManager_and_AuthManager
  */
 class AuthManager implements LoggerAwareInterface {
        /** Log in with an existing (not necessarily local) user */
@@ -105,12 +143,6 @@ class AuthManager implements LoggerAwareInterface {
         * @return AuthManager
         */
        public static function singleton() {
-               global $wgDisableAuthManager;
-
-               if ( $wgDisableAuthManager ) {
-                       throw new \BadMethodCallException( '$wgDisableAuthManager is set' );
-               }
-
                if ( self::$instance === null ) {
                        self::$instance = new self(
                                \RequestContext::getMain()->getRequest(),
@@ -743,7 +775,10 @@ class AuthManager implements LoggerAwareInterface {
        /**
         * Determine whether a username can authenticate
         *
-        * @param string $username
+        * This is mainly for internal purposes and only takes authentication data into account,
+        * not things like blocks that can change without the authentication system being aware.
+        *
+        * @param string $username MediaWiki username
         * @return bool
         */
        public function userCanAuthenticate( $username ) {
@@ -838,6 +873,9 @@ class AuthManager implements LoggerAwareInterface {
         * If $req was returned for AuthManager::ACTION_REMOVE, using $req should
         * no longer result in a successful login.
         *
+        * This method should only be called if allowsAuthenticationDataChange( $req, true )
+        * returned success.
+        *
         * @param AuthenticationRequest $req
         */
        public function changeAuthenticationData( AuthenticationRequest $req ) {
@@ -877,7 +915,7 @@ class AuthManager implements LoggerAwareInterface {
 
        /**
         * Determine whether a particular account can be created
-        * @param string $username
+        * @param string $username MediaWiki username
         * @param array $options
         *  - flags: (int) Bitfield of User:READ_* constants, default User::READ_NORMAL
         *  - creating: (bool) For internal use only. Never specify this.
@@ -1480,6 +1518,13 @@ class AuthManager implements LoggerAwareInterface {
 
        /**
         * Auto-create an account, and log into that account
+        *
+        * PrimaryAuthenticationProviders can invoke this method by returning a PASS from
+        * beginPrimaryAuthentication/continuePrimaryAuthentication with the username of a
+        * non-existing user. SessionProviders can invoke it by returning a SessionInfo with
+        * the username of a non-existing user from provideSessionInfo(). Calling this method
+        * explicitly (e.g. from a maintenance script) is also fine.
+        *
         * @param User $user User to auto-create
         * @param string $source What caused the auto-creation? This must be the ID
         *  of a PrimaryAuthenticationProvider or the constant self::AUTOCREATE_SOURCE_SESSION.
@@ -1495,7 +1540,7 @@ class AuthManager implements LoggerAwareInterface {
 
                $username = $user->getName();
 
-               // Try the local user from the slave DB
+               // Try the local user from the replica DB
                $localId = User::idFromName( $username );
                $flags = User::READ_NORMAL;
 
@@ -1611,9 +1656,6 @@ class AuthManager implements LoggerAwareInterface {
                        }
                }
 
-               // Ignore warnings about master connections/writes...hard to avoid here
-               \Profiler::instance()->getTransactionProfiler()->resetExpectations();
-
                $backoffKey = wfMemcKey( 'AuthManager', 'autocreate-failed', md5( $username ) );
                if ( $cache->get( $backoffKey ) ) {
                        $this->logger->debug( __METHOD__ . ': {username} denied by prior creation attempt failures', [
@@ -1631,17 +1673,18 @@ class AuthManager implements LoggerAwareInterface {
                        'from' => $from,
                ] );
 
+               // Ignore warnings about master connections/writes...hard to avoid here
+               $trxProfiler = \Profiler::instance()->getTransactionProfiler();
+               $trxProfiler->setSilenced( true );
                try {
                        $status = $user->addToDatabase();
                        if ( !$status->isOk() ) {
-                               // double-check for a race condition (T70012)
-                               $localId = User::idFromName( $username, User::READ_LATEST );
-                               if ( $localId ) {
+                               // Double-check for a race condition (T70012). We make use of the fact that when
+                               // addToDatabase fails due to the user already existing, the user object gets loaded.
+                               if ( $user->getId() ) {
                                        $this->logger->info( __METHOD__ . ': {username} already exists locally (race)', [
                                                'username' => $username,
                                        ] );
-                                       $user->setId( $localId );
-                                       $user->loadFromId( User::READ_LATEST );
                                        if ( $login ) {
                                                $this->setSessionDataForUser( $user );
                                        }
@@ -1658,6 +1701,7 @@ class AuthManager implements LoggerAwareInterface {
                                return $status;
                        }
                } catch ( \Exception $ex ) {
+                       $trxProfiler->setSilenced( false );
                        $this->logger->error( __METHOD__ . ': {username} failed with exception {exception}', [
                                'username' => $username,
                                'exception' => $ex,
@@ -1679,9 +1723,10 @@ class AuthManager implements LoggerAwareInterface {
 
                // Update user count
                \DeferredUpdates::addUpdate( new \SiteStatsUpdate( 0, 0, 0, 0, 1 ) );
-
                // Watch user's userpage and talk page
-               $user->addWatch( $user->getUserPage(), User::IGNORE_USER_RIGHTS );
+               \DeferredUpdates::addCallableUpdate( function () use ( $user ) {
+                       $user->addWatch( $user->getUserPage(), User::IGNORE_USER_RIGHTS );
+               } );
 
                // Log the creation
                if ( $this->config->get( 'NewUserLog' ) ) {
@@ -1692,9 +1737,11 @@ class AuthManager implements LoggerAwareInterface {
                        $logEntry->setParameters( [
                                '4::userid' => $user->getId(),
                        ] );
-                       $logid = $logEntry->insert();
+                       $logEntry->insert();
                }
 
+               $trxProfiler->setSilenced( false );
+
                if ( $login ) {
                        $this->setSessionDataForUser( $user );
                }
@@ -2024,37 +2071,26 @@ class AuthManager implements LoggerAwareInterface {
 
                // Query them and merge results
                $reqs = [];
-               $allPrimaryRequired = null;
                foreach ( $providers as $provider ) {
                        $isPrimary = $provider instanceof PrimaryAuthenticationProvider;
-                       $thisRequired = [];
                        foreach ( $provider->getAuthenticationRequests( $providerAction, $options ) as $req ) {
                                $id = $req->getUniqueId();
 
-                               // If it's from a Primary, mark it as "primary-required" but
-                               // track it for later.
+                               // If a required request if from a Primary, mark it as "primary-required" instead
                                if ( $isPrimary ) {
                                        if ( $req->required ) {
-                                               $thisRequired[$id] = true;
                                                $req->required = AuthenticationRequest::PRIMARY_REQUIRED;
                                        }
                                }
 
-                               if ( !isset( $reqs[$id] ) || $req->required === AuthenticationRequest::REQUIRED ) {
+                               if (
+                                       !isset( $reqs[$id] )
+                                       || $req->required === AuthenticationRequest::REQUIRED
+                                       || $reqs[$id] === AuthenticationRequest::OPTIONAL
+                               ) {
                                        $reqs[$id] = $req;
                                }
                        }
-
-                       // Track which requests are required by all primaries
-                       if ( $isPrimary ) {
-                               $allPrimaryRequired = $allPrimaryRequired === null
-                                       ? $thisRequired
-                                       : array_intersect_key( $allPrimaryRequired, $thisRequired );
-                       }
-               }
-               // Any requests that were required by all primaries are required.
-               foreach ( (array)$allPrimaryRequired as $id => $dummy ) {
-                       $reqs[$id]->required = AuthenticationRequest::REQUIRED;
                }
 
                // AuthManager has its own req for some actions
@@ -2323,6 +2359,7 @@ class AuthManager implements LoggerAwareInterface {
        }
 
        /**
+        * Log the user in
         * @param User $user
         * @param bool|null $remember
         */
@@ -2387,6 +2424,7 @@ class AuthManager implements LoggerAwareInterface {
 
        /**
         * Reset the internal caching for unit testing
+        * @protected Unit tests only
         */
        public static function resetCache() {
                if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
index 8d85b44..8845858 100644 (file)
@@ -161,35 +161,13 @@ class AuthManagerAuthPlugin extends \AuthPlugin {
        }
 
        public function addUser( $user, $password, $email = '', $realname = '' ) {
-               global $wgUser;
-
-               $data = [
-                       'username' => $user->getName(),
-                       'password' => $password,
-                       'retype' => $password,
-                       'email' => $email,
-                       'realname' => $realname,
-               ];
-               if ( $this->domain !== null && $this->domain !== '' ) {
-                       $data['domain'] = $this->domain;
-               }
-               $reqs = AuthManager::singleton()->getAuthenticationRequests( AuthManager::ACTION_CREATE );
-               $reqs = AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
-
-               $res = AuthManager::singleton()->beginAccountCreation( $wgUser, $reqs, 'null:' );
-               switch ( $res->status ) {
-                       case AuthenticationResponse::PASS:
-                               return true;
-                       case AuthenticationResponse::FAIL:
-                               // Hope it's not a PreAuthenticationProvider that failed...
-                               $msg = $res->message instanceof \Message ? $res->message : new \Message( $res->message );
-                               $this->logger->info( __METHOD__ . ': Authentication failed: ' . $msg->plain() );
-                               return false;
-                       default:
-                               throw new \BadMethodCallException(
-                                       'AuthManager does not support such simplified account creation'
-                               );
-               }
+               throw new \BadMethodCallException(
+                       'Creation of users via AuthPlugin is not supported with '
+                       . 'AuthManager. Generally, user creation should be left to either '
+                       . 'Special:CreateAccount, auto-creation when triggered by a '
+                       . 'SessionProvider or PrimaryAuthenticationProvider, or '
+                       . 'User::newSystemUser().'
+               );
        }
 
        public function strict() {
index 4db0a84..11f3e22 100644 (file)
@@ -28,6 +28,11 @@ use Psr\Log\LoggerAwareInterface;
 
 /**
  * An AuthenticationProvider is used by AuthManager when authenticating users.
+ *
+ * This interface should not be implemented directly; use one of its children.
+ *
+ * Authentication providers can be registered via $wgAuthManagerAutoConfig.
+ *
  * @ingroup Auth
  * @since 1.27
  */
@@ -83,9 +88,9 @@ interface AuthenticationProvider extends LoggerAwareInterface {
         *    - ACTION_LINK: The local user being linked to.
         *    - ACTION_CHANGE: The user having data changed.
         *    - ACTION_REMOVE: The user having data removed.
-        *    This does not need to be copied into the returned requests, you only
-        *    need to pay attention to it if the set of requests differs based on
-        *    the user.
+        *    If you leave the username property of the returned requests empty, this
+        *    will automatically be copied there (except for ACTION_CREATE where it
+        *    wouldn't really make sense).
         * @return AuthenticationRequest[]
         */
        public function getAuthenticationRequests( $action, array $options );
index ff4d52e..ff4569b 100644 (file)
@@ -29,7 +29,7 @@ use Message;
  * This is a value object for authentication requests.
  *
  * An AuthenticationRequest represents a set of form fields that are needed on
- * and provided from the login, account creation, or password change forms.
+ * and provided from a login, account creation, password change or similar form.
  *
  * @ingroup Auth
  * @since 1.27
@@ -39,11 +39,15 @@ abstract class AuthenticationRequest {
        /** Indicates that the request is not required for authentication to proceed. */
        const OPTIONAL = 0;
 
-       /** Indicates that the request is required for authentication to proceed. */
+       /** Indicates that the request is required for authentication to proceed.
+        * This will only be used for UI purposes; it is the authentication providers'
+        * responsibility to verify that all required requests are present.
+        */
        const REQUIRED = 1;
 
        /** Indicates that the request is required by a primary authentication
-        * provdier, but other primary authentication providers do not require it. */
+        * provider. Since the user can choose which primary to authenticate with,
+        * the request might or might not end up being actually required. */
        const PRIMARY_REQUIRED = 2;
 
        /** @var string|null The AuthManager::ACTION_* constant this request was
@@ -59,7 +63,8 @@ abstract class AuthenticationRequest {
        /** @var string|null Return-to URL, in case of redirect */
        public $returnToUrl = null;
 
-       /** @var string|null Username. May not be used by all subclasses. */
+       /** @var string|null Username. See AuthenticationProvider::getAuthenticationRequests()
+        * for details of what this means and how it behaves. */
        public $username = null;
 
        /**
@@ -101,6 +106,13 @@ abstract class AuthenticationRequest {
         *  - label: (Message) Text suitable for a label in an HTML form
         *  - help: (Message) Text suitable as a description of what the field is
         *  - optional: (bool) If set and truthy, the field may be left empty
+        *  - sensitive: (bool) If set and truthy, the field is considered sensitive. Code using the
+        *      request should avoid exposing the value of the field.
+        *
+        * All AuthenticationRequests are populated from the same data, so most of the time you'll
+        * want to prefix fields names with something unique to the extension/provider (although
+        * in some cases sharing the field with other requests is the right thing to do, e.g. for
+        * a 'password' field).
         *
         * @return array As above
         */
@@ -123,10 +135,13 @@ abstract class AuthenticationRequest {
        /**
         * Initialize form submitted form data.
         *
-        * Should always return false if self::getFieldInfo() returns an empty
-        * array.
+        * The default behavior is to to check for each key of self::getFieldInfo()
+        * in the submitted data, and copy the value - after type-appropriate transformations -
+        * to $this->$key. Most subclasses won't need to override this; if you do override it,
+        * make sure to always return false if self::getFieldInfo() returns an empty array.
         *
-        * @param array $data Submitted data as an associative array
+        * @param array $data Submitted data as an associative array (keys will correspond
+        *   to getFieldInfo())
         * @return bool Whether the request data was successfully loaded
         */
        public function loadFromSubmission( array $data ) {
@@ -247,7 +262,7 @@ abstract class AuthenticationRequest {
         *
         * Only considers requests that have a "username" field.
         *
-        * @param AuthenticationRequest[] $requests
+        * @param AuthenticationRequest[] $reqs
         * @return string|null
         * @throws \UnexpectedValueException If multiple different usernames are present.
         */
@@ -314,6 +329,8 @@ abstract class AuthenticationRequest {
                                        $options['optional'] = !empty( $options['optional'] );
                                }
 
+                               $options['sensitive'] = !empty( $options['sensitive'] );
+
                                if ( !array_key_exists( $name, $merged ) ) {
                                        $merged[$name] = $options;
                                } elseif ( $merged[$name]['type'] !== $options['type'] ) {
@@ -332,6 +349,7 @@ abstract class AuthenticationRequest {
                                        }
 
                                        $merged[$name]['optional'] = $merged[$name]['optional'] && $options['optional'];
+                                       $merged[$name]['sensitive'] = $merged[$name]['sensitive'] || $options['sensitive'];
 
                                        // No way to merge 'value', 'image', 'help', or 'label', so just use
                                        // the value from the first request.
index 5048cf8..0339e45 100644 (file)
@@ -27,6 +27,10 @@ use Message;
 
 /**
  * This is a value object to hold authentication response data
+ *
+ * An AuthenticationResponse represents both the status of the authentication
+ * (success, failure, in progress) and it its state (what data is needed to continue).
+ *
  * @ingroup Auth
  * @since 1.27
  */
@@ -39,7 +43,8 @@ class AuthenticationResponse {
 
        /** Indicates that third-party authentication succeeded but no user exists.
         * Either treat this like a UI response or pass $this->createRequest to
-        * AuthManager::beginCreateAccount().
+        * AuthManager::beginCreateAccount(). For use by AuthManager only (providers
+        * should just return a PASS with no username).
         */
        const RESTART = 'RESTART';
 
@@ -67,7 +72,9 @@ class AuthenticationResponse {
 
        /**
         * @var AuthenticationRequest[] Needed AuthenticationRequests to continue
-        * after a UI or REDIRECT response
+        * after a UI or REDIRECT response. This plays the same role when continuing
+        * authentication as AuthManager::getAuthenticationRequests() does when
+        * beginning it.
         */
        public $neededRequests = [];
 
@@ -84,35 +91,42 @@ class AuthenticationResponse {
         * @var AuthenticationRequest|null
         *
         * Returned with a PrimaryAuthenticationProvider login FAIL or a PASS with
-        * no username, this holds a request that should result in a PASS when
+        * no username, this can be set to a request that should result in a PASS when
         * passed to that provider's PrimaryAuthenticationProvider::beginPrimaryAccountCreation().
+        * The client will be able to send that back for expedited account creation where only
+        * the username needs to be filled.
         *
         * Returned with an AuthManager login FAIL or RESTART, this holds a
         * CreateFromLoginAuthenticationRequest that may be passed to
         * AuthManager::beginCreateAccount(), possibly in place of any
         * "primary-required" requests. It may also be passed to
-        * AuthManager::beginAuthentication() to preserve state.
+        * AuthManager::beginAuthentication() to preserve the list of
+        * accounts which can be linked after success (see $linkRequest).
         */
        public $createRequest = null;
 
        /**
-        * @var AuthenticationRequest|null Returned with a PrimaryAuthenticationProvider
-        *  login PASS with no username, this holds a request to pass to
-        *  AuthManager::changeAuthenticationData() to link the account once the
-        *  local user has been determined.
+        * @var AuthenticationRequest|null When returned with a PrimaryAuthenticationProvider
+        *  login PASS with no username, the request this holds will be passed to
+        *  AuthManager::changeAuthenticationData() once the local user has been determined and the
+        *  user has confirmed the account ownership (by reviewing the information given by
+        *  $linkRequest->describeCredentials()). The provider should handle that
+        *  changeAuthenticationData() call by doing the actual linking.
         */
        public $linkRequest = null;
 
        /**
         * @var AuthenticationRequest|null Returned with an AuthManager account
         *  creation PASS, this holds a request to pass to AuthManager::beginAuthentication()
-        *  to immediately log into the created account.
+        *  to immediately log into the created account. All provider methods except
+        *   postAuthentication will be skipped.
         */
        public $loginRequest = null;
 
        /**
         * @param string|null $username Local username
         * @return AuthenticationResponse
+        * @see AuthenticationResponse::PASS
         */
        public static function newPass( $username = null ) {
                $ret = new AuthenticationResponse;
@@ -124,6 +138,7 @@ class AuthenticationResponse {
        /**
         * @param Message $msg
         * @return AuthenticationResponse
+        * @see AuthenticationResponse::FAIL
         */
        public static function newFail( Message $msg ) {
                $ret = new AuthenticationResponse;
@@ -135,6 +150,7 @@ class AuthenticationResponse {
        /**
         * @param Message $msg
         * @return AuthenticationResponse
+        * @see AuthenticationResponse::RESTART
         */
        public static function newRestart( Message $msg ) {
                $ret = new AuthenticationResponse;
@@ -145,6 +161,7 @@ class AuthenticationResponse {
 
        /**
         * @return AuthenticationResponse
+        * @see AuthenticationResponse::ABSTAIN
         */
        public static function newAbstain() {
                $ret = new AuthenticationResponse;
@@ -156,6 +173,7 @@ class AuthenticationResponse {
         * @param AuthenticationRequest[] $reqs AuthenticationRequests needed to continue
         * @param Message $msg
         * @return AuthenticationResponse
+        * @see AuthenticationResponse::UI
         */
        public static function newUI( array $reqs, Message $msg ) {
                if ( !$reqs ) {
@@ -174,6 +192,7 @@ class AuthenticationResponse {
         * @param string $redirectTarget URL
         * @param mixed $redirectApiData Data suitable for adding to an ApiResult
         * @return AuthenticationResponse
+        * @see AuthenticationResponse::REDIRECT
         */
        public static function newRedirect( array $reqs, $redirectTarget, $redirectApiData = null ) {
                if ( !$reqs ) {
index 54ccdf4..f7a7ec1 100644 (file)
@@ -23,7 +23,6 @@ namespace MediaWiki\Auth;
 
 use Config;
 use StatusValue;
-use User;
 
 /**
  * Check if the user is blocked, and prevent authentication if so.
index c632e3c..a82f018 100644 (file)
@@ -3,7 +3,6 @@
 namespace MediaWiki\Auth;
 
 use Config;
-use StatusValue;
 
 /**
  * Handles email notification / email address confirmation for account creation.
@@ -51,15 +50,16 @@ class EmailNotificationSecondaryAuthenticationProvider
                        && $user->getEmail()
                        && !$this->manager->getAuthenticationSessionData( 'no-email' )
                ) {
-                       $status = $user->sendConfirmationMail();
-                       $user->saveSettings();
-                       if ( $status->isGood() ) {
-                               // TODO show 'confirmemail_oncreate' success message
-                       } else {
-                               // TODO show 'confirmemail_sendfailed' error message
-                               $this->logger->warning( 'Could not send confirmation email: ' .
-                                       $status->getWikiText( false, false, 'en' ) );
-                       }
+                       // TODO show 'confirmemail_oncreate'/'confirmemail_sendfailed' message
+                       wfGetDB( DB_MASTER )->onTransactionIdle( function () use ( $user ) {
+                               $user = $user->getInstanceForUpdate();
+                               $status = $user->sendConfirmationMail();
+                               $user->saveSettings();
+                               if ( !$status->isGood() ) {
+                                       $this->logger->warning( 'Could not send confirmation email: ' .
+                                               $status->getWikiText( false, false, 'en' ) );
+                               }
+                       } );
                }
 
                return AuthenticationResponse::newPass();
index 5f5ef79..bbc6e8d 100644 (file)
@@ -110,7 +110,7 @@ class LocalPasswordPrimaryAuthenticationProvider
                }
 
                $status = $this->checkPasswordValidity( $username, $req->password );
-               if ( !$status->isOk() ) {
+               if ( !$status->isOK() ) {
                        // Fatal, can't log in
                        return AuthenticationResponse::newFail( $status->getMessage() );
                }
index 187c29a..8550f3e 100644 (file)
@@ -53,6 +53,7 @@ class PasswordAuthenticationRequest extends AuthenticationRequest {
                                'type' => 'password',
                                'label' => wfMessage( $passwordLabel ),
                                'help' => wfMessage( 'authmanager-password-help' ),
+                               'sensitive' => true,
                        ],
                ];
 
@@ -68,6 +69,7 @@ class PasswordAuthenticationRequest extends AuthenticationRequest {
                                'type' => 'password',
                                'label' => wfMessage( $retypeLabel ),
                                'help' => wfMessage( 'authmanager-retype-help' ),
+                               'sensitive' => true,
                        ];
                }
 
index 13fae6e..8590cbd 100644 (file)
@@ -27,16 +27,19 @@ use StatusValue;
 use User;
 
 /**
- * A pre-authentication provider is a check that must pass for authentication
- * to proceed.
+ * A pre-authentication provider can prevent authentication early on.
  *
  * A PreAuthenticationProvider is used to supply arbitrary checks to be
  * performed before the PrimaryAuthenticationProviders are consulted during the
- * login process. Possible uses include checking that a per-IP throttle has not
- * been reached or that a captcha has been solved.
+ * login / account creation / account linking process. Possible uses include
+ * checking that a per-IP throttle has not been reached or that a captcha has been solved.
+ *
+ * This interface also provides callbacks that are invoked after login / account creation
+ * / account linking succeeded or failed.
  *
  * @ingroup Auth
  * @since 1.27
+ * @see https://www.mediawiki.org/wiki/Manual:SessionManager_and_AuthManager
  */
 interface PreAuthenticationProvider extends AuthenticationProvider {
 
@@ -52,10 +55,17 @@ interface PreAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-login callback
+        *
+        * This will be called at the end of a login attempt. It will not be called for unfinished
+        * login attempts that fail by the session timing out.
+        *
+        * @note Under certain circumstances, this can be called even when testForAuthentication
+        *   was not; see AuthenticationRequest::$loginRequest.
         * @param User|null $user User that was attempted to be logged in, if known.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAuthentication( $user, AuthenticationResponse $response );
 
@@ -97,12 +107,18 @@ interface PreAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-creation callback
+        *
+        * This will be called at the end of an account creation attempt. It will not be called if
+        * the account creation process results in a session timeout (possibly after a successful
+        * user creation, while a secondary provider is waiting for a response).
+        *
         * @param User $user User that was attempted to be created.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param User $creator User doing the creation. This may become a
         *   "UserValue" in the future, or User may be refactored into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAccountCreation( $user, $creator, AuthenticationResponse $response );
 
@@ -118,10 +134,14 @@ interface PreAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-link callback
+        *
+        * This will be called at the end of an account linking attempt.
+        *
         * @param User $user User that was attempted to be linked.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAccountLink( $user, AuthenticationResponse $response );
 
index c44c8fc..4033613 100644 (file)
@@ -27,27 +27,50 @@ use StatusValue;
 use User;
 
 /**
- * A primary authentication provider determines which user is trying to log in.
+ * A primary authentication provider is responsible for associating the submitted
+ * authentication data with a MediaWiki account.
  *
- * A PrimaryAuthenticationProvider is used as part of presenting a login form
- * to authenticate a user. In particular, the PrimaryAuthenticationProvider
- * takes form data and determines the authenticated user (if any) corresponds
- * to that form data. It might do this on the basis of a username and password
- * in that data, or by interacting with an external authentication service
- * (e.g. using OpenID), or by some other mechanism.
+ * When multiple primary authentication providers are configured for a site, they
+ * act as alternatives; the first one that recognizes the data will handle it,
+ * and further primary providers are not called (although they all get a chance
+ * to prevent actions).
  *
- * A PrimaryAuthenticationProvider would not be appropriate for something like
+ * For login, the PrimaryAuthenticationProvider takes form data and determines
+ * which authenticated user (if any) corresponds to that form data. It might
+ * do this on the basis of a username and password in that data, or by
+ * interacting with an external authentication service (e.g. using OpenID),
+ * or by some other mechanism.
+ *
+ * (A PrimaryAuthenticationProvider would not be appropriate for something like
  * HTTP authentication, OAuth, or SSL client certificates where each HTTP
  * request contains all the information needed to identify the user. In that
- * case you'll want to be looking at a \\MediaWiki\\Session\\SessionProvider
- * instead.
+ * case you'll want to be looking at a \MediaWiki\Session\SessionProvider
+ * instead.)
+ *
+ * For account creation, the PrimaryAuthenticationProvider takes form data and
+ * stores some authentication details which will allow it to verify a login by
+ * that user in the future. This might for example involve saving it in the
+ * database in a table that can be joined to the user table, or sending it to
+ * some external service for account creation, or authenticating the user with
+ * some remote service and then recording that the remote identity is linked to
+ * the local account.
+ * The creation of the local user (i.e. calling User::addToDatabase()) is handled
+ * by AuthManager once the primary authentication provider returns a PASS
+ * from begin/continueAccountCreation; do not try to do it yourself.
+ *
+ * For account linking, the PrimaryAuthenticationProvider verifies the user's
+ * identity at some external service (typically by redirecting the user and
+ * asking the external service to verify) and then records which local account
+ * is linked to which remote accounts. It should keep track of this and be able
+ * to enumerate linked accounts via getAuthenticationRequests(ACTION_REMOVE).
  *
  * This interface also provides methods for changing authentication data such
- * as passwords and for creating new users who can later be authenticated with
- * this provider.
+ * as passwords, and callbacks that are invoked after login / account creation
+ * / account linking succeeded or failed.
  *
  * @ingroup Auth
  * @since 1.27
+ * @see https://www.mediawiki.org/wiki/Manual:SessionManager_and_AuthManager
  */
 interface PrimaryAuthenticationProvider extends AuthenticationProvider {
        /** Provider can create accounts */
@@ -57,6 +80,14 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
        /** Provider cannot create or link to accounts */
        const TYPE_NONE = 'none';
 
+       /**
+        * {@inheritdoc}
+        *
+        * Of the requests returned by this method, exactly one should have
+        * {@link AuthenticationRequest::$required} set to REQUIRED.
+        */
+       public function getAuthenticationRequests( $action, array $options );
+
        /**
         * Start an authentication flow
         *
@@ -85,16 +116,25 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-login callback
+        *
+        * This will be called at the end of any login attempt, regardless of whether this provider was
+        * the one that handled it. It will not be called for unfinished login attempts that fail by
+        * the session timing out.
+        *
         * @param User|null $user User that was attempted to be logged in, if known.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAuthentication( $user, AuthenticationResponse $response );
 
        /**
         * Test whether the named user exists
-        * @param string $username
+        *
+        * Single-sign-on providers can use this to reserve a username for autocreation.
+        *
+        * @param string $username MediaWiki username
         * @param int $flags Bitfield of User:READ_* constants
         * @return bool
         */
@@ -102,7 +142,11 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Test whether the named user can authenticate with this provider
-        * @param string $username
+        *
+        * Should return true if the provider has any data for this user which can be used to
+        * authenticate it, even if the user is temporarily prevented from authentication somehow.
+        *
+        * @param string $username MediaWiki username
         * @return bool
         */
        public function testUserCanAuthenticate( $username );
@@ -176,6 +220,10 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
         * If $req was returned for AuthManager::ACTION_REMOVE, the corresponding
         * credentials should no longer result in a successful login.
         *
+        * It can be assumed that providerAllowsAuthenticationDataChange with $checkData === true
+        * was called before this, and passed. This method should never fail (other than throwing an
+        * exception).
+        *
         * @param AuthenticationRequest $req
         */
        public function providerChangeAuthenticationData( AuthenticationRequest $req );
@@ -241,7 +289,8 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
         * Post-creation callback
         *
         * Called after the user is added to the database, before secondary
-        * authentication providers are run.
+        * authentication providers are run. Only called if this provider was the one that issued
+        * a PASS.
         *
         * @param User $user User being created (has been added to the database now).
         *   This may become a "UserValue" in the future, or User may be refactored
@@ -258,7 +307,10 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
        /**
         * Post-creation callback
         *
-        * Called when the account creation process ends.
+        * This will be called at the end of any account creation attempt, regardless of whether this
+        * provider was the one that handled it. It will not be called if the account creation process
+        * results in a session timeout (possibly after a successful user creation, while a secondary
+        * provider is waiting for a response).
         *
         * @param User $user User that was attempted to be created.
         *   This may become a "UserValue" in the future, or User may be refactored
@@ -266,6 +318,7 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
         * @param User $creator User doing the creation. This may become a
         *   "UserValue" in the future, or User may be refactored into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAccountCreation( $user, $creator, AuthenticationResponse $response );
 
@@ -332,10 +385,15 @@ interface PrimaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-link callback
+        *
+        * This will be called at the end of any account linking attempt, regardless of whether this
+        * provider was the one that handled it.
+        *
         * @param User $user User that was attempted to be linked.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAccountLink( $user, AuthenticationResponse $response );
 
index 1ccc9c6..c55e65d 100644 (file)
@@ -27,16 +27,27 @@ use StatusValue;
 use User;
 
 /**
- * A secondary authentication provider performs additional authentication steps
- * after a PrimaryAuthenticationProvider has done its thing.
+ * A secondary provider mostly acts when the submitted authentication data has
+ * already been associated to a MediaWiki user account.
  *
- * A SecondaryAuthenticationProvider is used to perform arbitrary checks on an
- * authentication request after the user itself has been authenticated. For
- * example, it might implement a password reset, request the second factor for
- * two-factor auth, or prevent the login if the account is blocked.
+ * For login, a secondary provider performs additional authentication steps
+ * after a PrimaryAuthenticationProvider has identified which MediaWiki user is
+ * trying to log in. For example, it might implement a password reset, request
+ * the second factor for two-factor auth, or prevent the login if the account is blocked.
+ *
+ * For account creation, a secondary provider performs optional extra steps after
+ * a PrimaryAuthenticationProvider has created the user; for example, it can collect
+ * further user information such as a biography.
+ *
+ * (For account linking, secondary providers are not involved.)
+ *
+ * This interface also provides methods for changing authentication data such
+ * as a second-factor token, and callbacks that are invoked after login / account creation
+ * succeeded or failed.
  *
  * @ingroup Auth
  * @since 1.27
+ * @see https://www.mediawiki.org/wiki/Manual:SessionManager_and_AuthManager
  */
 interface SecondaryAuthenticationProvider extends AuthenticationProvider {
 
@@ -75,10 +86,15 @@ interface SecondaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-login callback
+        *
+        * This will be called at the end of a login attempt. It will not be called for unfinished
+        * login attempts that fail by the session timing out.
+        *
         * @param User|null $user User that was attempted to be logged in, if known.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAuthentication( $user, AuthenticationResponse $response );
 
@@ -129,6 +145,10 @@ interface SecondaryAuthenticationProvider extends AuthenticationProvider {
         * If $req was returned for AuthManager::ACTION_REMOVE, the corresponding
         * credentials should no longer result in a successful login.
         *
+        * It can be assumed that providerAllowsAuthenticationDataChange with $checkData === true
+        * was called before this, and passed. This method should never fail (other than throwing an
+        * exception).
+        *
         * @param AuthenticationRequest $req
         */
        public function providerChangeAuthenticationData( AuthenticationRequest $req );
@@ -151,6 +171,12 @@ interface SecondaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Start an account creation flow
+        *
+        * @note There is no guarantee this will be called in a successful account
+        *   creation process as the user can just abandon the process at any time
+        *   after the primary provider has issued a PASS and still have a valid
+        *   account. Be prepared to handle any database inconsistencies that result
+        *   from this or continueSecondaryAccountCreation() not being called.
         * @param User $user User being created (has been added to the database).
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
@@ -167,6 +193,7 @@ interface SecondaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Continue an authentication flow
+        *
         * @param User $user User being created (has been added to the database).
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
@@ -183,12 +210,18 @@ interface SecondaryAuthenticationProvider extends AuthenticationProvider {
 
        /**
         * Post-creation callback
+        *
+        * This will be called at the end of an account creation attempt. It will not be called if
+        * the account creation process results in a session timeout (possibly after a successful
+        * user creation, while a secondary provider is waiting for a response).
+        *
         * @param User $user User that was attempted to be created.
         *   This may become a "UserValue" in the future, or User may be refactored
         *   into such.
         * @param User $creator User doing the creation. This may become a
         *   "UserValue" in the future, or User may be refactored into such.
         * @param AuthenticationResponse $response Authentication response that will be returned
+        *   (PASS or FAIL)
         */
        public function postAccountCreation( $user, $creator, AuthenticationResponse $response );
 
index 46cbab5..f5571c7 100644 (file)
@@ -126,8 +126,8 @@ class TemporaryPasswordPrimaryAuthenticationProvider
                        return AuthenticationResponse::newAbstain();
                }
 
-               $dbw = wfGetDB( DB_MASTER );
-               $row = $dbw->selectRow(
+               $dbr = wfGetDB( DB_REPLICA );
+               $row = $dbr->selectRow(
                        'user',
                        [
                                'user_id', 'user_newpassword', 'user_newpass_time',
@@ -165,8 +165,8 @@ class TemporaryPasswordPrimaryAuthenticationProvider
                        return false;
                }
 
-               $dbw = wfGetDB( DB_MASTER );
-               $row = $dbw->selectRow(
+               $dbr = wfGetDB( DB_REPLICA );
+               $row = $dbr->selectRow(
                        'user',
                        [ 'user_newpassword', 'user_newpass_time' ],
                        [ 'user_name' => $username ],
@@ -303,7 +303,11 @@ class TemporaryPasswordPrimaryAuthenticationProvider
                );
 
                if ( $sendMail ) {
-                       $this->sendPasswordResetEmail( $req );
+                       // Send email after DB commit
+                       $dbw->onTransactionIdle( function () use ( $req ) {
+                               /** @var TemporaryPasswordAuthenticationRequest $req */
+                               $this->sendPasswordResetEmail( $req );
+                       } );
                }
        }
 
@@ -370,7 +374,10 @@ class TemporaryPasswordPrimaryAuthenticationProvider
                $this->providerChangeAuthenticationData( $req );
 
                if ( $mailpassword ) {
-                       $this->sendNewAccountEmail( $user, $creator, $req->password );
+                       // Send email after DB commit
+                       wfGetDB( DB_MASTER )->onTransactionIdle( function () use ( $user, $creator, $req ) {
+                               $this->sendNewAccountEmail( $user, $creator, $req->password );
+                       } );
                }
 
                return $mailpassword ? 'byemail' : null;
index 5b14a3b..f47c606 100644 (file)
 namespace MediaWiki\Auth;
 
 use BagOStuff;
-use Config;
 use MediaWiki\Logger\LoggerFactory;
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerInterface;
 use Psr\Log\LogLevel;
-use Psr\Log\NullLogger;
-use User;
 
 /**
  * A helper class for throttling authentication attempts.
index 361fe23..9dfabfd 100644 (file)
@@ -48,7 +48,7 @@ class BacklinkCache {
         *  > (string) links table name
         *   > (int) batch size
         *    > 'numRows' : Number of rows for this link table
-        *    > 'batches' : array( $start, $end )
+        *    > 'batches' : [ $start, $end ]
         *
         * @see BacklinkCache::partitionResult()
         *
@@ -137,13 +137,13 @@ class BacklinkCache {
        }
 
        /**
-        * Get the slave connection to the database
+        * Get the replica DB connection to the database
         * When non existing, will initialize the connection.
         * @return DatabaseBase
         */
        protected function getDB() {
                if ( !isset( $this->db ) ) {
-                       $this->db = wfGetDB( DB_SLAVE );
+                       $this->db = wfGetDB( DB_REPLICA );
                }
 
                return $this->db;
index 80f04ce..a34d235 100644 (file)
@@ -157,7 +157,7 @@ class GenderCache {
                        return;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $table = [ 'user', 'user_properties' ];
                $fields = [ 'user_name', 'up_value' ];
                $conds = [ 'user_name' => $usersToCheck ];
index bb78aa0..1bab0f5 100644 (file)
  */
 class HTMLFileCache extends FileCacheBase {
        /**
-        * Construct an ObjectFileCache from a Title and an action
+        * Construct an HTMLFileCache object from a Title and an action
+        *
+        * @deprecated since 1.24, instantiate this class directly
         * @param Title|string $title Title object or prefixed DB key string
         * @param string $action
         * @throws MWException
         * @return HTMLFileCache
-        *
-        * @deprecated Since 1.24, instantiate this class directly
         */
        public static function newFromTitle( $title, $action ) {
                return new self( $title, $action );
@@ -127,7 +127,7 @@ class HTMLFileCache extends FileCacheBase {
                $ulang = $context->getLanguage();
 
                // Check that there are no other sources of variation
-               if ( $user->getId() || $user->getNewtalk() || $ulang->equals( $wgContLang ) ) {
+               if ( $user->getId() || $user->getNewtalk() || !$ulang->equals( $wgContLang ) ) {
                        return false;
                }
                // Allow extensions to disable caching
index ec37dd6..8a4d061 100644 (file)
@@ -40,7 +40,11 @@ class LinkBatch {
         */
        protected $caller;
 
-       function __construct( $arr = [] ) {
+       /**
+        * LinkBatch constructor.
+        * @param LinkTarget[] $arr Initial items to be added to the batch
+        */
+       public function __construct( $arr = [] ) {
                foreach ( $arr as $item ) {
                        $this->addObj( $item );
                }
@@ -188,7 +192,7 @@ class LinkBatch {
                }
 
                // This is similar to LinkHolderArray::replaceInternal
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $table = 'page';
                $fields = array_merge(
                        LinkCache::getSelectFields(),
index 3fd29f3..23cc26d 100644 (file)
@@ -29,19 +29,17 @@ use MediaWiki\MediaWikiServices;
  * @ingroup Cache
  */
 class LinkCache {
-       /**
-        * @var HashBagOStuff
-        */
+       /** @var HashBagOStuff */
        private $mGoodLinks;
-       /**
-        * @var HashBagOStuff
-        */
+       /** @var HashBagOStuff */
        private $mBadLinks;
+       /** @var WANObjectCache */
+       private $wanCache;
+
+       /** @var bool */
        private $mForUpdate = false;
 
-       /**
-        * @var TitleFormatter
-        */
+       /** @var TitleFormatter */
        private $titleFormatter;
 
        /**
@@ -50,9 +48,10 @@ class LinkCache {
         */
        const MAX_SIZE = 10000;
 
-       public function __construct( TitleFormatter $titleFormatter ) {
+       public function __construct( TitleFormatter $titleFormatter, WANObjectCache $cache ) {
                $this->mGoodLinks = new HashBagOStuff( [ 'maxKeys' => self::MAX_SIZE ] );
                $this->mBadLinks = new HashBagOStuff( [ 'maxKeys' => self::MAX_SIZE ] );
+               $this->wanCache = $cache;
                $this->titleFormatter = $titleFormatter;
        }
 
@@ -244,15 +243,31 @@ class LinkCache {
                        return 0;
                }
 
-               // Some fields heavily used for linking...
-               $db = $this->mForUpdate ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE );
+               // Cache template/file pages as they are less often viewed but heavily used
+               if ( $this->mForUpdate ) {
+                       $row = $this->fetchPageRow( wfGetDB( DB_MASTER ), $nt );
+               } elseif ( $this->isCacheable( $nt ) ) {
+                       // These pages are often transcluded heavily, so cache them
+                       $cache = $this->wanCache;
+                       $row = $cache->getWithSetCallback(
+                               $cache->makeKey( 'page', $nt->getNamespace(), sha1( $nt->getDBkey() ) ),
+                               $cache::TTL_DAY,
+                               function ( $curValue, &$ttl, array &$setOpts ) use ( $cache, $nt ) {
+                                       $dbr = wfGetDB( DB_REPLICA );
+                                       $setOpts += Database::getCacheSetOptions( $dbr );
 
-               $row = $db->selectRow( 'page', self::getSelectFields(),
-                       [ 'page_namespace' => $nt->getNamespace(), 'page_title' => $nt->getDBkey() ],
-                       __METHOD__
-               );
+                                       $row = $this->fetchPageRow( $dbr, $nt );
+                                       $mtime = $row ? wfTimestamp( TS_UNIX, $row->page_touched ) : false;
+                                       $ttl = $cache->adaptiveTTL( $mtime, $ttl );
 
-               if ( $row !== false ) {
+                                       return $row;
+                               }
+                       );
+               } else {
+                       $row = $this->fetchPageRow( wfGetDB( DB_REPLICA ), $nt );
+               }
+
+               if ( $row ) {
                        $this->addGoodLinkObjFromRow( $nt, $row );
                        $id = intval( $row->page_id );
                } else {
@@ -263,6 +278,39 @@ class LinkCache {
                return $id;
        }
 
+       private function isCacheable( LinkTarget $title ) {
+               return ( $title->inNamespace( NS_TEMPLATE ) || $title->inNamespace( NS_FILE ) );
+       }
+
+       private function fetchPageRow( IDatabase $db, LinkTarget $nt ) {
+               $fields = self::getSelectFields();
+               if ( $this->isCacheable( $nt ) ) {
+                       $fields[] = 'page_touched';
+               }
+
+               return $db->selectRow(
+                       'page',
+                       $fields,
+                       [ 'page_namespace' => $nt->getNamespace(), 'page_title' => $nt->getDBkey() ],
+                       __METHOD__
+               );
+       }
+
+       /**
+        * Purge the link cache for a title
+        *
+        * @param LinkTarget $title
+        * @since 1.28
+        */
+       public function invalidateTitle( LinkTarget $title ) {
+               if ( $this->isCacheable( $title ) ) {
+                       $cache = ObjectCache::getMainWANInstance();
+                       $cache->delete(
+                               $cache->makeKey( 'page', $title->getNamespace(), sha1( $title->getDBkey() ) )
+                       );
+               }
+       }
+
        /**
         * Clears cache
         */
index 279898c..90ad241 100644 (file)
@@ -165,7 +165,7 @@ class MessageBlobStore implements LoggerAwareInterface {
                $cache->set( $cacheKey, $blob,
                        // Add part of a day to TTL to avoid all modules expiring at once
                        $cache::TTL_WEEK + mt_rand( 0, $cache::TTL_DAY ),
-                       Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) )
+                       Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) )
                );
                return $blob;
        }
@@ -179,7 +179,7 @@ class MessageBlobStore implements LoggerAwareInterface {
        public function updateMessage( $key ) {
                $moduleNames = $this->getResourceLoader()->getModulesByMessage( $key );
                foreach ( $moduleNames as $moduleName ) {
-                       // Uses a holdoff to account for database slave lag (for MessageCache)
+                       // Uses a holdoff to account for database replica DB lag (for MessageCache)
                        $this->wanCache->touchCheckKey( $this->wanCache->makeKey( __CLASS__, $moduleName ) );
                }
        }
index 62fab5f..d254d3d 100644 (file)
@@ -302,7 +302,7 @@ class MessageCache {
                                                $where[] = 'global cache is expired';
                                                $staleCache = $cache;
                                        } elseif ( $hashVolatile ) {
-                                               # DB results are slave lag prone until the holdoff TTL passes.
+                                               # DB results are replica DB lag prone until the holdoff TTL passes.
                                                # By then, updates should be reflected in loadFromDBWithLock().
                                                # One thread renerates the cache while others use old values.
                                                $where[] = 'global cache is expired/volatile';
@@ -442,7 +442,7 @@ class MessageCache {
        function loadFromDB( $code, $mode = null ) {
                global $wgMaxMsgCacheEntrySize, $wgLanguageCode, $wgAdaptiveMessageCache;
 
-               $dbr = wfGetDB( ( $mode == self::FOR_UPDATE ) ? DB_MASTER : DB_SLAVE );
+               $dbr = wfGetDB( ( $mode == self::FOR_UPDATE ) ? DB_MASTER : DB_REPLICA );
 
                $cache = [];
 
@@ -564,7 +564,7 @@ class MessageCache {
                }
 
                // Mark this cache as definitely "latest" (non-volatile) so
-               // load() calls do try to refresh the cache with slave data
+               // load() calls do try to refresh the cache with replica DB data
                $this->mCache[$code]['LATEST'] = time();
 
                // Update caches if the lock was acquired
diff --git a/includes/cache/ObjectFileCache.php b/includes/cache/ObjectFileCache.php
deleted file mode 100644 (file)
index c7ef044..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-/**
- * Object cache in the file system.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Cache
- */
-
-/**
- * Object cache in the file system.
- *
- * @ingroup Cache
- */
-class ObjectFileCache extends FileCacheBase {
-       /**
-        * Construct an ObjectFileCache from a key and a type
-        * @param string $key
-        * @param string $type
-        * @return ObjectFileCache
-        */
-       public static function newFromKey( $key, $type ) {
-               $cache = new self();
-
-               $cache->mKey = (string)$key;
-               $cache->mType = (string)$type;
-
-               return $cache;
-       }
-
-       /**
-        * Get the base file cache directory
-        * @return string
-        */
-       protected function cacheDirectory() {
-               return $this->baseCacheDirectory() . '/object';
-       }
-}
index 48d0398..016306a 100644 (file)
@@ -100,7 +100,7 @@ class UserCache {
 
                // Lookup basic info for users not yet loaded...
                if ( count( $usersToQuery ) ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $table = [ 'user' ];
                        $conds = [ 'user_id' => $usersToQuery ];
                        $fields = [ 'user_name', 'user_real_name', 'user_registration', 'user_id' ];
index c350178..e7e2d10 100644 (file)
@@ -39,7 +39,7 @@ class LCStoreDB implements LCStore {
                if ( $this->writesDone && $this->dbw ) {
                        $db = $this->dbw; // see the changes in finishWrite()
                } else {
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                }
 
                $value = $db->selectField(
index 64d8139..515ab05 100644 (file)
@@ -164,7 +164,7 @@ class CategoryMembershipChange {
                /**
                 * T109700 - Default bot flag to true when there is no corresponding RC entry
                 * This means all changes caused by parser functions & Lua on reparse are marked as bot
-                * Also in the case no RC entry could be found due to slave lag
+                * Also in the case no RC entry could be found due to replica DB lag
                 */
                $bot = 1;
                $lastRevId = 0;
index 9948040..77038ed 100644 (file)
@@ -236,6 +236,9 @@ class ChangesList extends ContextSource {
 
        /**
         * Show formatted char difference
+        *
+        * Needs the css module 'mediawiki.special.changeslist' to style output
+        *
         * @param int $old Number of bytes
         * @param int $new Number of bytes
         * @param IContextSource $context
index 4a0f566..d3a414b 100644 (file)
@@ -358,8 +358,7 @@ class EnhancedChangesList extends ChangesList {
        protected function getLineData( array $block, RCCacheEntry $rcObj, array $queryParams = [] ) {
                $RCShowChangedSize = $this->getConfig()->get( 'RCShowChangedSize' );
 
-               # Classes to apply -- TODO implement
-               $classes = [];
+               $classes = [ 'mw-enhanced-rc' ];
                $type = $rcObj->mAttribs['rc_type'];
                $data = [];
                $lineParams = [];
@@ -367,7 +366,7 @@ class EnhancedChangesList extends ChangesList {
                if ( $rcObj->watched
                        && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
                ) {
-                       $lineParams['classes'] = [ 'mw-enhanced-watched' ];
+                       $classes = [ 'mw-enhanced-watched' ];
                }
 
                $separator = ' <span class="mw-changeslist-separator">. .</span> ';
@@ -462,6 +461,8 @@ class EnhancedChangesList extends ChangesList {
                        unset( $data['timestampLink'] );
                }
 
+               $lineParams['classes'] = array_values( $classes );
+
                // everything else: makes it easier for extensions to add or remove data
                $lineParams['data'] = array_values( $data );
 
index 159cfd9..a5d1fc5 100644 (file)
@@ -180,7 +180,7 @@ class RecentChange {
        public static function newFromConds(
                $conds,
                $fname = __METHOD__,
-               $dbType = DB_SLAVE
+               $dbType = DB_REPLICA
        ) {
                $db = wfGetDB( $dbType );
                $row = $db->selectRow( 'recentchanges', self::selectFields(), $conds, $fname );
@@ -285,6 +285,17 @@ class RecentChange {
                        $this->mAttribs['rc_ip'] = '';
                }
 
+               # Strict mode fixups (not-NULL fields)
+               foreach ( [ 'minor', 'bot', 'new', 'patrolled', 'deleted' ] as $field ) {
+                       $this->mAttribs["rc_$field"] = (int)$this->mAttribs["rc_$field"];
+               }
+               # ...more fixups (NULL fields)
+               foreach ( [ 'old_len', 'new_len' ] as $field ) {
+                       $this->mAttribs["rc_$field"] = isset( $this->mAttribs["rc_$field"] )
+                               ? (int)$this->mAttribs["rc_$field"]
+                               : null;
+               }
+
                # If our database is strict about IP addresses, use NULL instead of an empty string
                if ( $dbw->strictIPs() && $this->mAttribs['rc_ip'] == '' ) {
                        unset( $this->mAttribs['rc_ip'] );
@@ -589,16 +600,20 @@ class RecentChange {
                        'pageStatus' => 'changed'
                ];
 
-               DeferredUpdates::addCallableUpdate( function() use ( $rc, $tags ) {
-                       $rc->save();
-                       if ( $rc->mAttribs['rc_patrolled'] ) {
-                               PatrolLog::record( $rc, true, $rc->getPerformer() );
-                       }
-                       if ( count( $tags ) ) {
-                               ChangeTags::addTags( $tags, $rc->mAttribs['rc_id'],
-                                       $rc->mAttribs['rc_this_oldid'], null, null );
-                       }
-               } );
+               DeferredUpdates::addCallableUpdate(
+                       function () use ( $rc, $tags ) {
+                               $rc->save();
+                               if ( $rc->mAttribs['rc_patrolled'] ) {
+                                       PatrolLog::record( $rc, true, $rc->getPerformer() );
+                               }
+                               if ( count( $tags ) ) {
+                                       ChangeTags::addTags( $tags, $rc->mAttribs['rc_id'],
+                                               $rc->mAttribs['rc_this_oldid'], null, null );
+                               }
+                       },
+                       DeferredUpdates::POSTSEND,
+                       wfGetDB( DB_MASTER )
+               );
 
                return $rc;
        }
@@ -661,16 +676,20 @@ class RecentChange {
                        'pageStatus' => 'created'
                ];
 
-               DeferredUpdates::addCallableUpdate( function() use ( $rc, $tags ) {
-                       $rc->save();
-                       if ( $rc->mAttribs['rc_patrolled'] ) {
-                               PatrolLog::record( $rc, true, $rc->getPerformer() );
-                       }
-                       if ( count( $tags ) ) {
-                               ChangeTags::addTags( $tags, $rc->mAttribs['rc_id'],
-                                       $rc->mAttribs['rc_this_oldid'], null, null );
-                       }
-               } );
+               DeferredUpdates::addCallableUpdate(
+                       function () use ( $rc, $tags ) {
+                               $rc->save();
+                               if ( $rc->mAttribs['rc_patrolled'] ) {
+                                       PatrolLog::record( $rc, true, $rc->getPerformer() );
+                               }
+                               if ( count( $tags ) ) {
+                                       ChangeTags::addTags( $tags, $rc->mAttribs['rc_id'],
+                                               $rc->mAttribs['rc_this_oldid'], null, null );
+                               }
+                       },
+                       DeferredUpdates::POSTSEND,
+                       wfGetDB( DB_MASTER )
+               );
 
                return $rc;
        }
@@ -768,7 +787,7 @@ class RecentChange {
                        'rc_comment' => $logComment,
                        'rc_this_oldid' => $revId,
                        'rc_last_oldid' => 0,
-                       'rc_bot' => $user->isAllowed( 'bot' ) ? $wgRequest->getBool( 'bot', true ) : 0,
+                       'rc_bot' => $user->isAllowed( 'bot' ) ? (int)$wgRequest->getBool( 'bot', true ) : 0,
                        'rc_ip' => self::checkIPAddress( $ip ),
                        'rc_patrolled' => $markPatrolled ? 1 : 0,
                        'rc_new' => 0, # obsolete
index a2945af..c8c5073 100644 (file)
@@ -174,7 +174,7 @@ class ChangeTags {
 
                // Might as well look for rcids and so on.
                if ( !$rc_id ) {
-                       // Info might be out of date, somewhat fractionally, on slave.
+                       // Info might be out of date, somewhat fractionally, on replica DB.
                        // LogEntry/LogPage and WikiPage match rev/log/rc timestamps,
                        // so use that relation to avoid full table scans.
                        if ( $log_id ) {
@@ -201,7 +201,7 @@ class ChangeTags {
                                );
                        }
                } elseif ( !$log_id && !$rev_id ) {
-                       // Info might be out of date, somewhat fractionally, on slave.
+                       // Info might be out of date, somewhat fractionally, on replica DB.
                        $log_id = $dbw->selectField(
                                'recentchanges',
                                'rc_logid',
@@ -313,7 +313,7 @@ class ChangeTags {
                $tagsToAdd = array_diff( $tagsToAdd, $tagsToRemove );
 
                // Update the summary row.
-               // $prevTags can be out of date on slaves, especially when addTags is called consecutively,
+               // $prevTags can be out of date on replica DBs, especially when addTags is called consecutively,
                // causing loss of tags added recently in tag_summary table.
                $prevTags = $dbw->selectField( 'tag_summary', 'ts_tags', $tsConds, __METHOD__ );
                $prevTags = $prevTags ? $prevTags : '';
@@ -633,7 +633,7 @@ class ChangeTags {
                        throw new MWException( 'Unable to determine appropriate JOIN condition for tagging.' );
                }
 
-               $fields['ts_tags'] = wfGetDB( DB_SLAVE )->buildGroupConcatField(
+               $fields['ts_tags'] = wfGetDB( DB_REPLICA )->buildGroupConcatField(
                        ',', 'change_tag', 'ct_tag', $join_cond
                );
 
@@ -1136,7 +1136,7 @@ class ChangeTags {
                        wfMemcKey( 'active-tags' ),
                        WANObjectCache::TTL_MINUTE * 5,
                        function ( $oldValue, &$ttl, array &$setOpts ) {
-                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) );
+                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
 
                                // Ask extensions which tags they consider active
                                $extensionActive = [];
@@ -1181,7 +1181,7 @@ class ChangeTags {
                        wfMemcKey( 'valid-tags-db' ),
                        WANObjectCache::TTL_MINUTE * 5,
                        function ( $oldValue, &$ttl, array &$setOpts ) use ( $fname ) {
-                               $dbr = wfGetDB( DB_SLAVE );
+                               $dbr = wfGetDB( DB_REPLICA );
 
                                $setOpts += Database::getCacheSetOptions( $dbr );
 
@@ -1211,7 +1211,7 @@ class ChangeTags {
                        wfMemcKey( 'valid-tags-hook' ),
                        WANObjectCache::TTL_MINUTE * 5,
                        function ( $oldValue, &$ttl, array &$setOpts ) {
-                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) );
+                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
 
                                $tags = [];
                                Hooks::run( 'ListDefinedTags', [ &$tags ] );
@@ -1266,7 +1266,7 @@ class ChangeTags {
                        wfMemcKey( 'change-tag-statistics' ),
                        WANObjectCache::TTL_MINUTE * 5,
                        function ( $oldValue, &$ttl, array &$setOpts ) use ( $fname ) {
-                               $dbr = wfGetDB( DB_SLAVE, 'vslow' );
+                               $dbr = wfGetDB( DB_REPLICA, 'vslow' );
 
                                $setOpts += Database::getCacheSetOptions( $dbr );
 
index 9fb0660..881c8c2 100644 (file)
@@ -49,10 +49,14 @@ abstract class Collation {
                switch ( $collationName ) {
                        case 'uppercase':
                                return new UppercaseCollation;
+                       case 'numeric':
+                               return new NumericUppercaseCollation;
                        case 'identity':
                                return new IdentityCollation;
                        case 'uca-default':
                                return new IcuCollation( 'root' );
+                       case 'uca-default-u-kn':
+                               return new IcuCollation( 'root-u-kn' );
                        case 'xx-uca-ckb':
                                return new CollationCkb;
                        case 'xx-uca-et':
index 27f917b..530fc76 100644 (file)
@@ -36,6 +36,9 @@ class IcuCollation extends Collation {
        /** @var Language */
        protected $digitTransformLanguage;
 
+       /** @var boolean */
+       private $useNumericCollation = false;
+
        /** @var array */
        private $firstLetterData;
 
@@ -93,7 +96,14 @@ class IcuCollation extends Collation {
                'be-tarask' => [ "Ё" ],
                'cy' => [ "Ch", "Dd", "Ff", "Ng", "Ll", "Ph", "Rh", "Th" ],
                'en' => [],
-               'fa' => [ "آ", "ء", "ه" ],
+               // RTL, let's put each letter on a new line
+               'fa' => [
+                       "آ",
+                       "ء",
+                       "ه",
+                       "ا",
+                       "و"
+               ],
                'fi' => [ "Å", "Ä", "Ö" ],
                'fr' => [],
                'hu' => [ "Cs", "Dz", "Dzs", "Gy", "Ly", "Ny", "Ö", "Sz", "Ty", "Ü", "Zs" ],
@@ -140,7 +150,7 @@ class IcuCollation extends Collation {
                'la' => [],
                'lb' => [],
                'lt' => [ "Č", "Š", "Ž" ],
-               'mk' => [],
+               'mk' => [ "Ѓ", "Ќ" ],
                'mo' => [ "Ă", "Â", "Î", "Ş", "Ţ" ],
                'mt' => [ "Ċ", "Ġ", "Għ", "Ħ", "Ż" ],
                'nl' => [],
@@ -190,6 +200,15 @@ class IcuCollation extends Collation {
 
                $this->primaryCollator = Collator::create( $locale );
                $this->primaryCollator->setStrength( Collator::PRIMARY );
+
+               // If the special suffix for numeric collation is present, turn on numeric collation.
+               if ( substr( $locale, -5, 5 ) === '-u-kn' ) {
+                       $this->useNumericCollation = true;
+                       // Strip off the special suffix so it doesn't trip up fetchFirstLetterData().
+                       $this->locale = substr( $this->locale, 0, -5 );
+                       $this->mainCollator->setAttribute( Collator::NUMERIC_COLLATION, Collator::ON );
+                       $this->primaryCollator->setAttribute( Collator::NUMERIC_COLLATION, Collator::ON );
+               }
        }
 
        public function getSortKey( $string ) {
@@ -206,8 +225,9 @@ class IcuCollation extends Collation {
                        return '';
                }
 
-               // Check for CJK
                $firstChar = mb_substr( $string, 0, 1, 'UTF-8' );
+
+               // If the first character is a CJK character, just return that character.
                if ( ord( $firstChar ) > 0x7f && self::isCjk( UtfNormal\Utils::utf8ToCodepoint( $firstChar ) ) ) {
                        return $firstChar;
                }
@@ -225,7 +245,19 @@ class IcuCollation extends Collation {
                        // Before the first letter
                        return '';
                }
-               return $this->getLetterByIndex( $min );
+
+               $sortLetter = $this->getLetterByIndex( $min );
+
+               if ( $this->useNumericCollation ) {
+                       // If the sort letter is a number, return '0–9' (or localized equivalent).
+                       // ASCII value of 0 is 48. ASCII value of 9 is 57.
+                       // Note that this also applies to non-Arabic numerals since they are
+                       // mapped to Arabic numeral sort letters. For example, ২ sorts as 2.
+                       if ( ord( $sortLetter ) >= 48 && ord( $sortLetter ) <= 57 ) {
+                               $sortLetter = wfMessage( 'category-header-numerals' )->numParams( 0, 9 )->text();
+                       }
+               }
+               return $sortLetter;
        }
 
        /**
@@ -401,6 +433,7 @@ class IcuCollation extends Collation {
        }
 
        /**
+        * Test if a code point is a CJK (Chinese, Japanese, Korean) character
         * @since 1.16.3
         */
        public static function isCjk( $codepoint ) {
@@ -444,6 +477,13 @@ class IcuCollation extends Collation {
                $versionPrefix = substr( $icuVersion, 0, 3 );
                // Source: http://site.icu-project.org/download
                $map = [
+                       '57.' => '8.0',
+                       '56.' => '8.0',
+                       '55.' => '7.0',
+                       '54.' => '7.0',
+                       '53.' => '6.3',
+                       '52.' => '6.3',
+                       '51.' => '6.2',
                        '50.' => '6.2',
                        '49.' => '6.1',
                        '4.8' => '6.0',
diff --git a/includes/collation/NumericUppercaseCollation.php b/includes/collation/NumericUppercaseCollation.php
new file mode 100644 (file)
index 0000000..4bf2f73
--- /dev/null
@@ -0,0 +1,58 @@
+<?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
+ */
+
+/**
+ * Collation that orders text with numbers "naturally", so that 'Foo 1' < 'Foo 2' < 'Foo 12'.
+ *
+ * Note that this only works in terms of sequences of digits, and the behavior for decimal fractions
+ * or pretty-formatted numbers may be unexpected.
+ *
+ * @since 1.28
+ */
+class NumericUppercaseCollation extends UppercaseCollation {
+       public function getSortKey( $string ) {
+               $sortkey = parent::getSortKey( $string );
+
+               // For each sequence of digits, insert the digit '0' and then the length of the sequence
+               // (encoded in two bytes) before it. That's all folks, it sorts correctly now! The '0' ensures
+               // correct position (where digits would normally sort), then the length will be compared putting
+               // shorter numbers before longer ones; if identical, then the characters will be compared, which
+               // generates the correct results for numbers of equal length.
+               $sortkey = preg_replace_callback( '/\d+/', function ( $matches ) {
+                       $len = strlen( $matches[0] );
+                       // This allows sequences of up to 65536 numeric characters to be handled correctly. One byte
+                       // would allow only for 256, which doesn't feel future-proof.
+                       $prefix = chr( floor( $len / 256 ) ) . chr( $len % 256 );
+                       return '0' . $prefix . $matches[0];
+               }, $sortkey );
+
+               return $sortkey;
+       }
+
+       public function getFirstLetter( $string ) {
+               if ( preg_match( '/^\d/', $string ) ) {
+                       // Note that we pass 0 and 9 as normal params, not numParams(). This only works for 0-9
+                       // and not localised digits, so we don't want them to be converted.
+                       return wfMessage( 'category-header-numerals' )->params( 0, 9 )->text();
+               } else {
+                       return parent::getFirstLetter( $string );
+               }
+       }
+}
index 694b633..20893bb 100644 (file)
@@ -63,4 +63,5 @@ abstract class CodeContentHandler extends TextContentHandler {
        protected function getContentClass() {
                throw new MWException( 'Subclass must override' );
        }
+
 }
index e225fb7..4e50c8e 100644 (file)
@@ -1,4 +1,7 @@
 <?php
+
+use MediaWiki\Search\ParserOutputSearchDataExtractor;
+
 /**
  * Base class for content handling.
  *
@@ -450,10 +453,6 @@ abstract class ContentHandler {
        public function __construct( $modelId, $formats ) {
                $this->mModelID = $modelId;
                $this->mSupportedFormats = $formats;
-
-               $this->mModelName = preg_replace( '/(Content)?Handler$/', '', get_class( $this ) );
-               $this->mModelName = preg_replace( '/[_\\\\]/', '', $this->mModelName );
-               $this->mModelName = strtolower( $this->mModelName );
        }
 
        /**
@@ -897,7 +896,7 @@ abstract class ContentHandler {
         * have it / want it.
         */
        public function getAutoDeleteReason( Title $title, &$hasHistory ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Get the last revision
                $rev = Revision::newFromTitle( $title );
@@ -1015,9 +1014,21 @@ abstract class ContentHandler {
                        return false; // no content to undo
                }
 
-               $this->checkModelID( $cur_content->getModel() );
-               $this->checkModelID( $undo_content->getModel() );
-               $this->checkModelID( $undoafter_content->getModel() );
+               try {
+                       $this->checkModelID( $cur_content->getModel() );
+                       $this->checkModelID( $undo_content->getModel() );
+                       if ( $current->getId() !== $undo->getId() ) {
+                               // If we are undoing the most recent revision,
+                               // its ok to revert content model changes. However
+                               // if we are undoing a revision in the middle, then
+                               // doing that will be confusing.
+                               $this->checkModelID( $undoafter_content->getModel() );
+                       }
+               } catch ( MWException $e ) {
+                       // If the revisions have different content models
+                       // just return false
+                       return false;
+               }
 
                if ( $cur_content->equals( $undo_content ) ) {
                        // No use doing a merge if it's just a straight revert.
@@ -1156,62 +1167,19 @@ abstract class ContentHandler {
         *
         * @param string $event Event name
         * @param array $args Parameters passed to hook functions
-        * @param bool $warn Whether to log a warning.
-        *                    Default to self::$enableDeprecationWarnings.
-        *                    May be set to false for testing.
+        * @param string|null $deprecatedVersion Emit a deprecation notice
+        *   when the hook is run for the provided version
         *
         * @return bool True if no handler aborted the hook
-        *
-        * @see ContentHandler::$enableDeprecationWarnings
         */
        public static function runLegacyHooks( $event, $args = [],
-               $warn = null
+               $deprecatedVersion = null
        ) {
 
-               if ( $warn === null ) {
-                       $warn = self::$enableDeprecationWarnings;
-               }
-
                if ( !Hooks::isRegistered( $event ) ) {
                        return true; // nothing to do here
                }
 
-               if ( $warn ) {
-                       // Log information about which handlers are registered for the legacy hook,
-                       // so we can find and fix them.
-
-                       $handlers = Hooks::getHandlers( $event );
-                       $handlerInfo = [];
-
-                       MediaWiki\suppressWarnings();
-
-                       foreach ( $handlers as $handler ) {
-                               if ( is_array( $handler ) ) {
-                                       if ( is_object( $handler[0] ) ) {
-                                               $info = get_class( $handler[0] );
-                                       } else {
-                                               $info = $handler[0];
-                                       }
-
-                                       if ( isset( $handler[1] ) ) {
-                                               $info .= '::' . $handler[1];
-                                       }
-                               } elseif ( is_object( $handler ) ) {
-                                       $info = get_class( $handler[0] );
-                                       $info .= '::on' . $event;
-                               } else {
-                                       $info = $handler;
-                               }
-
-                               $handlerInfo[] = $info;
-                       }
-
-                       MediaWiki\restoreWarnings();
-
-                       wfWarn( "Using obsolete hook $event via ContentHandler::runLegacyHooks()! Handlers: " .
-                               implode( ', ', $handlerInfo ), 2 );
-               }
-
                // convert Content objects to text
                $contentObjects = [];
                $contentTexts = [];
@@ -1229,7 +1197,7 @@ abstract class ContentHandler {
                }
 
                // call the hook functions
-               $ok = Hooks::run( $event, $args );
+               $ok = Hooks::run( $event, $args, $deprecatedVersion );
 
                // see if the hook changed the text
                foreach ( $contentTexts as $k => $orig ) {
@@ -1248,4 +1216,117 @@ abstract class ContentHandler {
 
                return $ok;
        }
+
+       /**
+        * Get fields definition for search index
+        *
+        * @todo Expose title, redirect, namespace, text, source_text, text_bytes
+        *       field mappings here. (see T142670 and T143409)
+        *
+        * @param SearchEngine $engine
+        * @return SearchIndexField[] List of fields this content handler can provide.
+        * @since 1.28
+        */
+       public function getFieldsForSearchIndex( SearchEngine $engine ) {
+               $fields['category'] = $engine->makeSearchFieldMapping(
+                       'category',
+                       SearchIndexField::INDEX_TYPE_TEXT
+               );
+
+               $fields['category']->setFlag( SearchIndexField::FLAG_CASEFOLD );
+
+               $fields['external_link'] = $engine->makeSearchFieldMapping(
+                       'external_link',
+                       SearchIndexField::INDEX_TYPE_KEYWORD
+               );
+
+               $fields['outgoing_link'] = $engine->makeSearchFieldMapping(
+                       'outgoing_link',
+                       SearchIndexField::INDEX_TYPE_KEYWORD
+               );
+
+               $fields['template'] = $engine->makeSearchFieldMapping(
+                       'template',
+                       SearchIndexField::INDEX_TYPE_KEYWORD
+               );
+
+               $fields['template']->setFlag( SearchIndexField::FLAG_CASEFOLD );
+
+               return $fields;
+       }
+
+       /**
+        * Add new field definition to array.
+        * @param SearchIndexField[] $fields
+        * @param SearchEngine       $engine
+        * @param string             $name
+        * @param int                $type
+        * @return SearchIndexField[] new field defs
+        * @since 1.28
+        */
+       protected function addSearchField( &$fields, SearchEngine $engine, $name, $type ) {
+               $fields[$name] = $engine->makeSearchFieldMapping( $name, $type );
+               return $fields;
+       }
+
+       /**
+        * Return fields to be indexed by search engine
+        * as representation of this document.
+        * Overriding class should call parent function or take care of calling
+        * the SearchDataForIndex hook.
+        * @param WikiPage     $page Page to index
+        * @param ParserOutput $output
+        * @param SearchEngine $engine Search engine for which we are indexing
+        * @return array Map of name=>value for fields
+        * @since 1.28
+        */
+       public function getDataForSearchIndex( WikiPage $page, ParserOutput $output,
+                                              SearchEngine $engine ) {
+               $fieldData = [];
+               $content = $page->getContent();
+
+               if ( $content ) {
+                       $searchDataExtractor = new ParserOutputSearchDataExtractor();
+
+                       $fieldData['category'] = $searchDataExtractor->getCategories( $output );
+                       $fieldData['external_link'] = $searchDataExtractor->getExternalLinks( $output );
+                       $fieldData['outgoing_link'] = $searchDataExtractor->getOutgoingLinks( $output );
+                       $fieldData['template'] = $searchDataExtractor->getTemplates( $output );
+
+                       $text = $content->getTextForSearchIndex();
+
+                       $fieldData['text'] = $text;
+                       $fieldData['source_text'] = $text;
+                       $fieldData['text_bytes'] = $content->getSize();
+               }
+
+               Hooks::run( 'SearchDataForIndex', [ &$fieldData, $this, $page, $output, $engine ] );
+               return $fieldData;
+       }
+
+       /**
+        * Produce page output suitable for indexing.
+        *
+        * Specific content handlers may override it if they need different content handling.
+        *
+        * @param WikiPage    $page
+        * @param ParserCache $cache
+        * @return ParserOutput
+        */
+       public function getParserOutputForIndexing( WikiPage $page, ParserCache $cache = null ) {
+               $parserOptions = $page->makeParserOptions( 'canonical' );
+               $revId = $page->getRevision()->getId();
+               if ( $cache ) {
+                       $parserOutput = $cache->get( $page, $parserOptions );
+               }
+               if ( empty( $parserOutput ) ) {
+                       $parserOutput =
+                               $page->getContent()->getParserOutput( $page->getTitle(), $revId, $parserOptions );
+                       if ( $cache ) {
+                               $cache->save( $parserOutput, $page, $parserOptions );
+                       }
+               }
+               return $parserOutput;
+       }
+
 }
index 845e39c..9c11035 100644 (file)
@@ -37,7 +37,7 @@ class CssContentHandler extends CodeContentHandler {
        }
 
        protected function getContentClass() {
-               return 'CssContent';
+               return CssContent::class;
        }
 
        public function supportsRedirects() {
index b082b32..9abad3e 100644 (file)
@@ -39,7 +39,7 @@ class JavaScriptContentHandler extends CodeContentHandler {
         * @return string
         */
        protected function getContentClass() {
-               return 'JavaScriptContent';
+               return JavaScriptContent::class;
        }
 
        public function supportsRedirects() {
index 40d9277..14c8182 100644 (file)
@@ -86,7 +86,7 @@ class JsonContent extends TextContent {
                        return $this;
                }
 
-               return new static( $this->beautifyJSON() );
+               return new static( self::normalizeLineEndings( $this->beautifyJSON() ) );
        }
 
        /**
index f97eaa4..edb21f6 100644 (file)
@@ -37,6 +37,11 @@ class JsonContentHandler extends CodeContentHandler {
         * @return string
         */
        protected function getContentClass() {
-               return 'JsonContent';
+               return JsonContent::class;
+       }
+
+       public function makeEmptyContent() {
+               $class = $this->getContentClass();
+               return new $class( '{}' );
        }
 }
index 225522e..7bb4def 100644 (file)
@@ -147,9 +147,28 @@ class TextContent extends AbstractContent {
                }
        }
 
+       /**
+        * Do a "\r\n" -> "\n" and "\r" -> "\n" transformation
+        * as well as trim trailing whitespace
+        *
+        * This was formerly part of Parser::preSaveTransform, but
+        * for non-wikitext content models they probably still want
+        * to normalize line endings without all of the other PST
+        * changes.
+        *
+        * @since 1.28
+        * @param $text
+        * @return string
+        */
+       public static function normalizeLineEndings( $text ) {
+               return str_replace( [ "\r\n", "\r" ], "\n", rtrim( $text ) );
+       }
+
        /**
         * Returns a Content object with pre-save transformations applied.
-        * This implementation just trims trailing whitespace.
+        *
+        * At a minimum, subclasses should make sure to call TextContent::normalizeLineEndings()
+        * either directly or part of Parser::preSaveTransform().
         *
         * @param Title $title
         * @param User $user
@@ -159,7 +178,7 @@ class TextContent extends AbstractContent {
         */
        public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
                $text = $this->getNativeData();
-               $pst = rtrim( $text );
+               $pst = self::normalizeLineEndings( $text );
 
                return ( $text === $pst ) ? $this : new static( $pst, $this->getModel() );
        }
index ad40cd9..09cdcee 100644 (file)
@@ -31,8 +31,7 @@
 class TextContentHandler extends ContentHandler {
 
        // @codingStandardsIgnoreStart bug 57585
-       public function __construct( $modelId = CONTENT_MODEL_TEXT,
-               $formats = [ CONTENT_FORMAT_TEXT ] ) {
+       public function __construct( $modelId = CONTENT_MODEL_TEXT, $formats = [ CONTENT_FORMAT_TEXT ] ) {
                parent::__construct( $modelId, $formats );
        }
        // @codingStandardsIgnoreEnd
@@ -41,7 +40,7 @@ class TextContentHandler extends ContentHandler {
         * Returns the content's text as-is.
         *
         * @param Content $content
-        * @param string $format The serialization format to check
+        * @param string  $format The serialization format to check
         *
         * @return mixed
         */
@@ -102,7 +101,7 @@ class TextContentHandler extends ContentHandler {
         * @return string
         */
        protected function getContentClass() {
-               return 'TextContent';
+               return TextContent::class;
        }
 
        /**
@@ -143,4 +142,20 @@ class TextContentHandler extends ContentHandler {
                return true;
        }
 
+       public function getFieldsForSearchIndex( SearchEngine $engine ) {
+               $fields = parent::getFieldsForSearchIndex( $engine );
+               $fields['language'] =
+                       $engine->makeSearchFieldMapping( 'language', SearchIndexField::INDEX_TYPE_KEYWORD );
+
+               return $fields;
+       }
+
+       public function getDataForSearchIndex( WikiPage $page, ParserOutput $output,
+                                              SearchEngine $engine ) {
+               $fields = parent::getDataForSearchIndex( $page, $output, $engine );
+               $fields['language'] =
+                       $this->getPageLanguage( $page->getTitle(), $page->getContent() )->getCode();
+               return $fields;
+       }
+
 }
diff --git a/includes/content/WikiTextStructure.php b/includes/content/WikiTextStructure.php
new file mode 100644 (file)
index 0000000..55c4ad5
--- /dev/null
@@ -0,0 +1,244 @@
+<?php
+
+use HtmlFormatter\HtmlFormatter;
+use MediaWiki\Logger\LoggerFactory;
+
+/**
+ * Class allowing to explore structure of parsed wikitext.
+ */
+class WikiTextStructure {
+       /**
+        * @var string
+        */
+       private $openingText;
+       /**
+        * @var string
+        */
+       private $allText;
+       /**
+        * @var string[]
+        */
+       private $auxText = [];
+       /**
+        * @var ParserOutput
+        */
+       private $parserOutput;
+
+       /**
+        * @var string[] selectors to elements that are excluded entirely from search
+        */
+       private $excludedElementSelectors = [
+               'audio', 'video',       // "it looks like you don't have javascript enabled..."
+                                       // do not need to index
+               'sup.reference',        // The [1] for references
+               '.mw-cite-backlink',    // The ↑ next to references in the references section
+               'h1', 'h2', 'h3',       // Headings are already indexed in their own field.
+               'h5', 'h6', 'h4',
+               '.autocollapse',        // Collapsed fields are hidden by default so we don't want them
+                                                               // showing up.
+       ];
+
+       /**
+        * @var string[] selectors to elements that are considered auxiliary to article text for search
+        */
+       private $auxiliaryElementSelectors = [
+               '.thumbcaption',        // Thumbnail captions aren't really part of the text proper
+               'table',                // Neither are tables
+               '.rellink',             // Common style for "See also:".
+               '.dablink',             // Common style for calling out helpful links at the top
+                                                               // of the article.
+               '.searchaux',           // New class users can use to mark stuff as auxiliary to searches.
+       ];
+
+       /**
+        * WikiTextStructure constructor.
+        * @param ParserOutput $parserOutput
+        */
+       public function __construct( ParserOutput $parserOutput ) {
+               $this->parserOutput = $parserOutput;
+       }
+
+       /**
+        * Get headings on the page.
+        * @return string[]
+        * First strip out things that look like references.  We can't use HTML filtering because
+        * the references come back as <sup> tags without a class.  To keep from breaking stuff like
+        *  ==Applicability of the strict mass–energy equivalence formula, ''E'' = ''mc''<sup>2</sup>==
+        * we don't remove the whole <sup> tag.  We also don't want to strip the <sup> tag and remove
+        * everything that looks like [2] because, I dunno, maybe there is a band named Word [2] Foo
+        * or something.  Whatever.  So we only strip things that look like <sup> tags wrapping a
+        * reference.  And since the data looks like:
+        *      Reference in heading <sup>&#91;1&#93;</sup><sup>&#91;2&#93;</sup>
+        * we can not really use HtmlFormatter as we have no suitable selector.
+        */
+       public function headings() {
+               $headings = [];
+               $ignoredHeadings = $this->getIgnoredHeadings();
+               foreach ( $this->parserOutput->getSections() as $heading ) {
+                       $heading = $heading[ 'line' ];
+
+                       // Some wikis wrap the brackets in a span:
+                       // http://en.wikipedia.org/wiki/MediaWiki:Cite_reference_link
+                       $heading = preg_replace( '/<\/?span>/', '', $heading );
+                       // Normalize [] so the following regexp would work.
+                       $heading = preg_replace( [ '/&#91;/', '/&#93;/' ], [ '[', ']' ], $heading );
+                       $heading = preg_replace( '/<sup>\s*\[\s*\d+\s*\]\s*<\/sup>/is', '', $heading );
+
+                       // Strip tags from the heading or else we'll display them (escaped) in search results
+                       $heading = trim( Sanitizer::stripAllTags( $heading ) );
+
+                       // Note that we don't take the level of the heading into account - all headings are equal.
+                       // Except the ones we ignore.
+                       if ( !in_array( $heading, $ignoredHeadings ) ) {
+                               $headings[] = $heading;
+                       }
+               }
+               return $headings;
+       }
+
+       /**
+        * Parse a message content into an array. This function is generally used to
+        * parse settings stored as i18n messages (see search-ignored-headings).
+        *
+        * @param string $message
+        * @return string[]
+        */
+       public static function parseSettingsInMessage( $message ) {
+               $lines = explode( "\n", $message );
+               $lines = preg_replace( '/#.*$/', '', $lines ); // Remove comments
+               $lines = array_map( 'trim', $lines );          // Remove extra spaces
+               $lines = array_filter( $lines );               // Remove empty lines
+               return $lines;
+       }
+
+       /**
+        * Get list of heading to ignore.
+        * @return string[]
+        */
+       private function getIgnoredHeadings() {
+               static $ignoredHeadings = null;
+               if ( $ignoredHeadings === null ) {
+                       $ignoredHeadings = [];
+                       $source = wfMessage( 'search-ignored-headings' )->inContentLanguage();
+                       if ( $source->isBlank() ) {
+                               // Try old version too, just in case
+                               $source = wfMessage( 'cirrussearch-ignored-headings' )->inContentLanguage();
+                       }
+                       if ( !$source->isDisabled() ) {
+                               $lines = self::parseSettingsInMessage( $source->plain() );
+                               $ignoredHeadings = $lines;               // Now we just have headings!
+                       }
+               }
+               return $ignoredHeadings;
+       }
+
+       /**
+        * Extract parts of the text - opening, main and auxiliary.
+        */
+       private function extractWikitextParts() {
+               if ( !is_null( $this->allText ) ) {
+                       return;
+               }
+               $this->parserOutput->setEditSectionTokens( false );
+               $this->parserOutput->setTOCEnabled( false );
+               $text = $this->parserOutput->getText();
+               if ( strlen( $text ) == 0 ) {
+                       $this->allText = "";
+                       // empty text - nothing to seek here
+                       return;
+               }
+               $opening = null;
+
+               $this->openingText = $this->extractHeadingBeforeFirstHeading( $text );
+
+               // Add extra spacing around break tags so text crammed together like<br>this
+               // doesn't make one word.
+               $text = str_replace( '<br', "\n<br", $text );
+
+               $formatter = new HtmlFormatter( $text );
+
+               // Strip elements from the page that we never want in the search text.
+               $formatter->remove( $this->excludedElementSelectors );
+               $formatter->filterContent();
+
+               // Strip elements from the page that are auxiliary text.  These will still be
+               // searched but matches will be ranked lower and non-auxiliary matches will be
+               // preferred in highlighting.
+               $formatter->remove( $this->auxiliaryElementSelectors );
+               $auxiliaryElements = $formatter->filterContent();
+               $this->allText = trim( Sanitizer::stripAllTags( $formatter->getText() ) );
+               foreach ( $auxiliaryElements as $auxiliaryElement ) {
+                       $this->auxText[] =
+                               trim( Sanitizer::stripAllTags( $formatter->getText( $auxiliaryElement ) ) );
+               }
+       }
+
+       /**
+        * Get text before first heading.
+        * @param string $text
+        * @return string|null
+        */
+       private function extractHeadingBeforeFirstHeading( $text ) {
+               $matches = [];
+               if ( !preg_match( '/<h[123456]>/', $text, $matches, PREG_OFFSET_CAPTURE ) ) {
+                       // There isn't a first heading so we interpret this as the article
+                       // being entirely without heading.
+                       return null;
+               }
+               $text = substr( $text, 0, $matches[ 0 ][ 1 ] );
+               if ( !$text ) {
+                       // There isn't any text before the first heading so we declare there isn't
+                       // a first heading.
+                       return null;
+               }
+
+               $formatter = new HtmlFormatter( $text );
+               $formatter->remove( $this->excludedElementSelectors );
+               $formatter->remove( $this->auxiliaryElementSelectors );
+               $formatter->filterContent();
+               $text = trim( Sanitizer::stripAllTags( $formatter->getText() ) );
+
+               if ( !$text ) {
+                       // There isn't any text after filtering before the first heading so we declare
+                       // that there isn't a first heading.
+                       return null;
+               }
+
+               return $text;
+       }
+
+       /**
+        * Get opening text
+        * @return string
+        */
+       public function getOpeningText() {
+               $this->extractWikitextParts();
+               return $this->openingText;
+       }
+
+       /**
+        * Get main text
+        * @return string
+        */
+       public function getMainText() {
+               $this->extractWikitextParts();
+               return $this->allText;
+       }
+
+       /**
+        * Get auxiliary text
+        * @return string[]
+        */
+       public function getAuxiliaryText() {
+               $this->extractWikitextParts();
+               return $this->auxText;
+       }
+
+       /**
+        * Get the defaultsort property
+        * @return string|null
+        */
+       public function getDefaultSort() {
+               return $this->parserOutput->getProperty( 'defaultsort' );
+       }
+}
index a63819d..9296728 100644 (file)
@@ -138,7 +138,6 @@ class WikitextContent extends TextContent {
 
                $text = $this->getNativeData();
                $pst = $wgParser->preSaveTransform( $text, $title, $user, $popts );
-               rtrim( $pst );
 
                return ( $text === $pst ) ? $this : new static( $pst );
        }
index 0701a0f..978ac44 100644 (file)
@@ -35,7 +35,7 @@ class WikitextContentHandler extends TextContentHandler {
        }
 
        protected function getContentClass() {
-               return 'WikitextContent';
+               return WikitextContent::class;
        }
 
        /**
@@ -108,4 +108,68 @@ class WikitextContentHandler extends TextContentHandler {
                return true;
        }
 
+       public function getFieldsForSearchIndex( SearchEngine $engine ) {
+               $fields = parent::getFieldsForSearchIndex( $engine );
+
+               $fields['heading'] =
+                       $engine->makeSearchFieldMapping( 'heading', SearchIndexField::INDEX_TYPE_TEXT );
+               $fields['heading']->setFlag( SearchIndexField::FLAG_SCORING );
+
+               $fields['auxiliary_text'] =
+                       $engine->makeSearchFieldMapping( 'auxiliary_text', SearchIndexField::INDEX_TYPE_TEXT );
+
+               $fields['opening_text'] =
+                       $engine->makeSearchFieldMapping( 'opening_text', SearchIndexField::INDEX_TYPE_TEXT );
+               $fields['opening_text']->setFlag( SearchIndexField::FLAG_SCORING |
+                                                 SearchIndexField::FLAG_NO_HIGHLIGHT );
+
+               // FIXME: this really belongs in separate file handler but files
+               // do not have separate handler. Sadness.
+               $fields['file_text'] =
+                       $engine->makeSearchFieldMapping( 'file_text', SearchIndexField::INDEX_TYPE_TEXT );
+
+               return $fields;
+       }
+
+       /**
+        * Extract text of the file
+        * TODO: probably should go to file handler?
+        * @param Title $title
+        * @return string|null
+        */
+       protected function getFileText( Title $title ) {
+               $file = wfLocalFile( $title );
+               if ( $file && $file->exists() ) {
+                       $handler = $file->getHandler();
+                       if ( !$handler ) {
+                               return null;
+                       }
+                       return $handler->getEntireText( $file );
+               }
+
+               return null;
+       }
+
+       public function getDataForSearchIndex( WikiPage $page, ParserOutput $parserOutput,
+                                              SearchEngine $engine ) {
+               $fields = parent::getDataForSearchIndex( $page, $parserOutput, $engine );
+
+               $structure = new WikiTextStructure( $parserOutput );
+               $fields['heading'] = $structure->headings();
+               // text fields
+               $fields['opening_text'] = $structure->getOpeningText();
+               $fields['text'] = $structure->getMainText(); // overwrites one from ContentHandler
+               $fields['auxiliary_text'] = $structure->getAuxiliaryText();
+               $fields['defaultsort'] = $structure->getDefaultSort();
+
+               $title = $page->getTitle();
+               if ( NS_FILE == $title->getNamespace() ) {
+                       $fileText = $this->getFileText( $title );
+                       if ( $fileText ) {
+                               $fields['file_text'] = $fileText;
+                       }
+               }
+               return $fields;
+       }
+
 }
index b98174b..cc63446 100644 (file)
@@ -26,7 +26,7 @@
  *
  * @since 1.26
  */
-class DBAccessObjectUtils {
+class DBAccessObjectUtils implements IDBAccessObject {
        /**
         * @param integer $bitfield
         * @param integer $flags IDBAccessObject::READ_* constant
@@ -37,23 +37,45 @@ class DBAccessObjectUtils {
        }
 
        /**
-        * Get an appropriate DB index and options for a query
+        * Get an appropriate DB index, options, and fallback DB index for a query
         *
-        * @param integer $bitfield
-        * @return array (DB_MASTER/DB_SLAVE, SELECT options array)
+        * The fallback DB index and options are to be used if the entity is not found
+        * with the initial DB index, typically querying the master DB to avoid lag
+        *
+        * @param integer $bitfield Bitfield of IDBAccessObject::READ_* constants
+        * @return array List of DB indexes and options in this order:
+        *   - DB_MASTER or DB_REPLICA constant for the initial query
+        *   - SELECT options array for the initial query
+        *   - DB_MASTER constant for the fallback query; null if no fallback should happen
+        *   - SELECT options array for the fallback query; empty if no fallback should happen
         */
        public static function getDBOptions( $bitfield ) {
-               $index = self::hasFlags( $bitfield, IDBAccessObject::READ_LATEST )
-                       ? DB_MASTER
-                       : DB_SLAVE;
+               if ( self::hasFlags( $bitfield, self::READ_LATEST_IMMUTABLE ) ) {
+                       $index = DB_REPLICA; // override READ_LATEST if set
+                       $fallbackIndex = DB_MASTER;
+               } elseif ( self::hasFlags( $bitfield, self::READ_LATEST ) ) {
+                       $index = DB_MASTER;
+                       $fallbackIndex = null;
+               } else {
+                       $index = DB_REPLICA;
+                       $fallbackIndex = null;
+               }
+
+               $lockingOptions = [];
+               if ( self::hasFlags( $bitfield, self::READ_EXCLUSIVE ) ) {
+                       $lockingOptions[] = 'FOR UPDATE';
+               } elseif ( self::hasFlags( $bitfield, self::READ_LOCKING ) ) {
+                       $lockingOptions[] = 'LOCK IN SHARE MODE';
+               }
 
-               $options = [];
-               if ( self::hasFlags( $bitfield, IDBAccessObject::READ_EXCLUSIVE ) ) {
-                       $options[] = 'FOR UPDATE';
-               } elseif ( self::hasFlags( $bitfield, IDBAccessObject::READ_LOCKING ) ) {
-                       $options[] = 'LOCK IN SHARE MODE';
+               if ( $fallbackIndex !== null ) {
+                       $options = []; // locks on DB_REPLICA make no sense
+                       $fallbackOptions = $lockingOptions;
+               } else {
+                       $options = $lockingOptions;
+                       $fallbackOptions = []; // no fallback
                }
 
-               return [ $index, $options ];
+               return [ $index, $options, $fallbackIndex, $fallbackOptions ];
        }
 }
index 3690735..5acf3ae 100644 (file)
  * though certain objects may assume READ_LATEST for common use case or legacy reasons.
  *
  * There are four types of reads:
- *   - READ_NORMAL    : Potentially cached read of data (e.g. from a slave or stale replica)
+ *   - READ_NORMAL    : Potentially cached read of data (e.g. from a replica DB or stale replica)
  *   - READ_LATEST    : Up-to-date read as of transaction start (e.g. from master or a quorum read)
  *   - READ_LOCKING   : Up-to-date read as of now, that locks (shared) the records
  *   - READ_EXCLUSIVE : Up-to-date read as of now, that locks (exclusive) the records
  * All record locks persist for the duration of the transaction.
  *
+ * A special constant READ_LATEST_IMMUTABLE can be used for fetching append-only data. Such
+ * data is either (a) on a replica DB and up-to-date or (b) not yet there, but on the master/quorum.
+ * Because the data is append-only, it can never be stale on a replica DB if present.
+ *
  * Callers should use READ_NORMAL (or pass in no flags) unless the read determines a write.
  * In theory, such cases may require READ_LOCKING, though to avoid contention, READ_LATEST is
  * often good enough. If UPDATE race condition checks are required on a row and expensive code
  * must run after the row is fetched to determine the UPDATE, it may help to do something like:
- *   - a) Read the current row
- *   - b) Determine the new row (expensive, so we don't want to hold locks now)
- *   - c) Re-read the current row with READ_LOCKING; if it changed then bail out
- *   - d) otherwise, do the updates
+ *   - a) Start transaction
+ *   - b) Read the current row with READ_LATEST
+ *   - c) Determine the new row (expensive, so we don't want to hold locks now)
+ *   - d) Re-read the current row with READ_LOCKING; if it changed then bail out
+ *   - e) otherwise, do the updates
+ *   - f) Commit transaction
  *
  * @since 1.20
  */
 interface IDBAccessObject {
-       // Constants for object loading bitfield flags (higher => higher QoS)
-       const READ_LATEST = 1; // read from the master
+       /** Constants for object loading bitfield flags (higher => higher QoS) */
+       /** @var integer Read from a replica DB/non-quorum */
+       const READ_NORMAL = 0;
+       /** @var integer Read from the master/quorum */
+       const READ_LATEST = 1;
+       /* @var integer Read from the master/quorum and lock out other writers */
        const READ_LOCKING = 3; // READ_LATEST (1) and "LOCK IN SHARE MODE" (2)
+       /** @var integer Read from the master/quorum and lock out other writers and locking readers */
        const READ_EXCLUSIVE = 7; // READ_LOCKING (3) and "FOR UPDATE" (4)
 
-       // Convenience constant for callers to explicitly request slave data
-       const READ_NORMAL = 0; // read from the slave
+       /** @var integer Read from a replica DB or without a quorum, using the master/quorum on miss */
+       const READ_LATEST_IMMUTABLE = 8;
 
        // Convenience constant for tracking how data was loaded (higher => higher QoS)
        const READ_NONE = -1; // not loaded yet (or the object was cleared)
index cc35999..39c9957 100644 (file)
@@ -31,8 +31,8 @@ class ChronologyProtector {
 
        /** @var string Storage key name */
        protected $key;
-       /** @var array Map of (ip: <IP>, agent: <user-agent>) */
-       protected $client;
+       /** @var string Hash of client parameters */
+       protected $clientId;
        /** @var bool Whether to no-op all method calls */
        protected $enabled = true;
        /** @var bool Whether to check and wait on positions */
@@ -44,6 +44,8 @@ class ChronologyProtector {
        protected $startupPositions = [];
        /** @var DBMasterPos[] Map of (DB master name => position) */
        protected $shutdownPositions = [];
+       /** @var float[] Map of (DB master name => 1) */
+       protected $shutdownTouchDBs = [];
 
        /**
         * @param BagOStuff $store
@@ -52,11 +54,8 @@ class ChronologyProtector {
         */
        public function __construct( BagOStuff $store, array $client ) {
                $this->store = $store;
-               $this->client = $client;
-               $this->key = $store->makeGlobalKey(
-                       'ChronologyProtector',
-                       md5( $client['ip'] . "\n" . $client['agent'] )
-               );
+               $this->clientId = md5( $client['ip'] . "\n" . $client['agent'] );
+               $this->key = $store->makeGlobalKey( __CLASS__, $this->clientId );
        }
 
        /**
@@ -79,9 +78,9 @@ class ChronologyProtector {
         * Initialise a LoadBalancer to give it appropriate chronology protection.
         *
         * If the stash has a previous master position recorded, this will try to
-        * make sure that the next query to a slave of that master will see changes up
+        * make sure that the next query to a replica DB of that master will see changes up
         * to that position by delaying execution. The delay may timeout and allow stale
-        * data if no non-lagged slaves are available.
+        * data if no non-lagged replica DBs are available.
         *
         * @param LoadBalancer $lb
         * @return void
@@ -95,10 +94,8 @@ class ChronologyProtector {
 
                $masterName = $lb->getServerName( $lb->getWriterIndex() );
                if ( !empty( $this->startupPositions[$masterName] ) ) {
-                       $info = $lb->parentInfo();
                        $pos = $this->startupPositions[$masterName];
-                       wfDebugLog( 'replication', __METHOD__ .
-                               ": LB '" . $info['id'] . "' waiting for master pos $pos\n" );
+                       wfDebugLog( 'replication', __METHOD__ . ": LB for '$masterName' set to pos $pos\n" );
                        $lb->waitFor( $pos );
                }
        }
@@ -111,35 +108,47 @@ class ChronologyProtector {
         * @return void
         */
        public function shutdownLB( LoadBalancer $lb ) {
-               if ( !$this->enabled || $lb->getServerCount() <= 1 ) {
-                       return; // non-replicated setup or disabled
+               if ( !$this->enabled ) {
+                       return; // not enabled
+               } elseif ( !$lb->hasOrMadeRecentMasterChanges( INF ) ) {
+                       // Only save the position if writes have been done on the connection
+                       return;
                }
 
-               $info = $lb->parentInfo();
                $masterName = $lb->getServerName( $lb->getWriterIndex() );
-
-               // Only save the position if writes have been done on the connection
-               $db = $lb->getAnyOpenConnection( $lb->getWriterIndex() );
-               if ( !$db || !$db->doneWrites() ) {
-                       wfDebugLog( 'replication', __METHOD__ . ": LB {$info['id']}, no writes done\n" );
-
-                       return; // nothing to do
+               if ( $lb->getServerCount() > 1 ) {
+                       $pos = $lb->getMasterPos();
+                       wfDebugLog( 'replication', __METHOD__ . ": LB for '$masterName' has pos $pos\n" );
+                       $this->shutdownPositions[$masterName] = $pos;
+               } else {
+                       wfDebugLog( 'replication', __METHOD__ . ": DB '$masterName' touched\n" );
                }
-
-               $pos = $db->getMasterPos();
-               wfDebugLog( 'replication', __METHOD__ . ": LB {$info['id']} has master pos $pos\n" );
-               $this->shutdownPositions[$masterName] = $pos;
+               $this->shutdownTouchDBs[$masterName] = 1;
        }
 
        /**
         * Notify the ChronologyProtector that the LBFactory is done calling shutdownLB() for now.
         * May commit chronology data to persistent storage.
         *
-        * @return array Empty on success; returns the (db name => position) map on failure
+        * @return DBMasterPos[] Empty on success; returns the (db name => position) map on failure
         */
        public function shutdown() {
-               if ( !$this->enabled || !count( $this->shutdownPositions ) ) {
-                       return true; // nothing to save
+               if ( !$this->enabled ) {
+                       return [];
+               }
+
+               // Some callers might want to know if a user recently touched a DB.
+               // These writes do not need to block on all datacenters receiving them.
+               foreach ( $this->shutdownTouchDBs as $dbName => $unused ) {
+                       $this->store->set(
+                               $this->getTouchedKey( $this->store, $dbName ),
+                               microtime( true ),
+                               BagOStuff::TTL_DAY
+                       );
+               }
+
+               if ( !count( $this->shutdownPositions ) ) {
+                       return []; // nothing to save
                }
 
                wfDebugLog( 'replication',
@@ -147,31 +156,20 @@ class ChronologyProtector {
                        implode( ', ', array_keys( $this->shutdownPositions ) ) . "\n"
                );
 
-               $shutdownPositions = $this->shutdownPositions;
-               $ok = $this->store->merge(
-                       $this->key,
-                       function ( $store, $key, $curValue ) use ( $shutdownPositions ) {
-                               /** @var $curPositions DBMasterPos[] */
-                               if ( $curValue === false ) {
-                                       $curPositions = $shutdownPositions;
-                               } else {
-                                       $curPositions = $curValue['positions'];
-                                       // Use the newest positions for each DB master
-                                       foreach ( $shutdownPositions as $db => $pos ) {
-                                               if ( !isset( $curPositions[$db] )
-                                                       || $pos->asOfTime() > $curPositions[$db]->asOfTime()
-                                               ) {
-                                                       $curPositions[$db] = $pos;
-                                               }
-                                       }
-                               }
-
-                               return [ 'positions' => $curPositions ];
-                       },
-                       BagOStuff::TTL_MINUTE,
-                       10,
-                       BagOStuff::WRITE_SYNC // visible in all datacenters
-               );
+               // CP-protected writes should overwhemingly go to the master datacenter, so get DC-local
+               // lock to merge the values. Use a DC-local get() and a synchronous all-DC set(). This
+               // makes it possible for the BagOStuff class to write in parallel to all DCs with one RTT.
+               if ( $this->store->lock( $this->key, 3 ) ) {
+                       $ok = $this->store->set(
+                               $this->key,
+                               self::mergePositions( $this->store->get( $this->key ), $this->shutdownPositions ),
+                               BagOStuff::TTL_MINUTE,
+                               BagOStuff::WRITE_SYNC
+                       );
+                       $this->store->unlock( $this->key );
+               } else {
+                       $ok = false;
+               }
 
                if ( !$ok ) {
                        // Raced out too many times or stash is down
@@ -186,6 +184,24 @@ class ChronologyProtector {
                return [];
        }
 
+       /**
+        * @param string $dbName DB master name (e.g. "db1052")
+        * @return float|bool UNIX timestamp when client last touched the DB; false if not on record
+        * @since 1.28
+        */
+       public function getTouched( $dbName ) {
+               return $this->store->get( $this->getTouchedKey( $this->store, $dbName ) );
+       }
+
+       /**
+        * @param BagOStuff $store
+        * @param string $dbName
+        * @return string
+        */
+       private function getTouchedKey( BagOStuff $store, $dbName ) {
+               return $store->makeGlobalKey( __CLASS__, 'mtime', $this->clientId, $dbName );
+       }
+
        /**
         * Load in previous master positions for the client
         */
@@ -198,12 +214,34 @@ class ChronologyProtector {
                if ( $this->wait ) {
                        $data = $this->store->get( $this->key );
                        $this->startupPositions = $data ? $data['positions'] : [];
-
                        wfDebugLog( 'replication', __METHOD__ . ": key is {$this->key} (read)\n" );
                } else {
                        $this->startupPositions = [];
-
                        wfDebugLog( 'replication', __METHOD__ . ": key is {$this->key} (unread)\n" );
                }
        }
+
+       /**
+        * @param array|bool $curValue
+        * @param DBMasterPos[] $shutdownPositions
+        * @return array
+        */
+       private static function mergePositions( $curValue, array $shutdownPositions ) {
+               /** @var $curPositions DBMasterPos[] */
+               if ( $curValue === false ) {
+                       $curPositions = $shutdownPositions;
+               } else {
+                       $curPositions = $curValue['positions'];
+                       // Use the newest positions for each DB master
+                       foreach ( $shutdownPositions as $db => $pos ) {
+                               if ( !isset( $curPositions[$db] )
+                                       || $pos->asOfTime() > $curPositions[$db]->asOfTime()
+                               ) {
+                                       $curPositions[$db] = $pos;
+                               }
+                       }
+               }
+
+               return [ 'positions' => $curPositions ];
+       }
 }
index 3cd09e2..577c98d 100644 (file)
@@ -129,25 +129,11 @@ class CloneDatabase {
         */
        public static function changePrefix( $prefix ) {
                global $wgDBprefix;
-               wfGetLBFactory()->forEachLB( [ 'CloneDatabase', 'changeLBPrefix' ], [ $prefix ] );
+               wfGetLBFactory()->forEachLB( function( $lb ) use ( $prefix ) {
+                       $lb->forEachOpenConnection( function ( $db ) use ( $prefix ) {
+                               $db->tablePrefix( $prefix );
+                       } );
+               } );
                $wgDBprefix = $prefix;
        }
-
-       /**
-        * @param LoadBalancer $lb
-        * @param string $prefix
-        * @return void
-        */
-       public static function changeLBPrefix( $lb, $prefix ) {
-               $lb->forEachOpenConnection( [ 'CloneDatabase', 'changeDBPrefix' ], [ $prefix ] );
-       }
-
-       /**
-        * @param DatabaseBase $db
-        * @param string $prefix
-        * @return void
-        */
-       public static function changeDBPrefix( $db, $prefix ) {
-               $db->tablePrefix( $prefix );
-       }
 }
index af5f8f9..9997f2c 100644 (file)
@@ -55,6 +55,10 @@ class DBConnRef implements IDatabase {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
+       public function explicitTrxActive() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
        public function tablePrefix( $prefix = null ) {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
@@ -99,7 +103,7 @@ class DBConnRef implements IDatabase {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
-       public function pendingWriteQueryDuration() {
+       public function pendingWriteQueryDuration( $type = self::ESTIMATE_TOTAL ) {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
@@ -111,11 +115,15 @@ class DBConnRef implements IDatabase {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
-       public function setFlag( $flag ) {
+       public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
-       public function clearFlag( $flag ) {
+       public function restoreFlags( $state = self::RESTORE_PRIOR ) {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
@@ -417,11 +425,23 @@ class DBConnRef implements IDatabase {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
-       public function onTransactionIdle( $callback ) {
+       public function serverIsReadOnly() {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
-       public function onTransactionPreCommitOrIdle( $callback ) {
+       public function onTransactionResolution( callable $callback ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function onTransactionIdle( callable $callback ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function onTransactionPreCommitOrIdle( callable $callback ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
+       public function setTransactionListener( $name, callable $callback = null ) {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
@@ -437,7 +457,7 @@ class DBConnRef implements IDatabase {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
-       public function begin( $fname = __METHOD__ ) {
+       public function begin( $fname = __METHOD__, $mode = IDatabase::TRANSACTION_EXPLICIT ) {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
@@ -449,6 +469,10 @@ class DBConnRef implements IDatabase {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
+       public function flushSnapshot( $fname = __METHOD__ ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
        public function listTables( $prefix = null, $fname = __METHOD__ ) {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
@@ -461,8 +485,10 @@ class DBConnRef implements IDatabase {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
-       public function ping() {
-               return $this->__call( __FUNCTION__, func_get_args() );
+       public function ping( &$rtt = null ) {
+               return func_num_args()
+                       ? $this->__call( __FUNCTION__, [ &$rtt ] )
+                       : $this->__call( __FUNCTION__, [] ); // method cares about null vs missing
        }
 
        public function getLag() {
index 68df9cf..0a1774d 100644 (file)
 abstract class DatabaseBase implements IDatabase {
        /** Number of times to re-try an operation in case of deadlock */
        const DEADLOCK_TRIES = 4;
-
        /** Minimum time to wait before retry, in microseconds */
        const DEADLOCK_DELAY_MIN = 500000;
-
        /** Maximum time to wait before retry */
        const DEADLOCK_DELAY_MAX = 1500000;
 
+       /** How long before it is worth doing a dummy query to test the connection */
+       const PING_TTL = 1.0;
+       const PING_QUERY = 'SELECT 1 AS ping';
+
+       const TINY_WRITE_SEC = .010;
+       const SLOW_WRITE_SEC = .500;
+       const SMALL_WRITE_ROWS = 100;
+
+       /** @var string SQL query */
        protected $mLastQuery = '';
+       /** @var bool */
        protected $mDoneWrites = false;
+       /** @var string|bool */
        protected $mPHPError = false;
-
-       protected $mServer, $mUser, $mPassword, $mDBname;
+       /** @var string */
+       protected $mServer;
+       /** @var string */
+       protected $mUser;
+       /** @var string */
+       protected $mPassword;
+       /** @var string */
+       protected $mDBname;
+       /** @var bool */
+       protected $cliMode;
 
        /** @var BagOStuff APC cache */
        protected $srvCache;
 
        /** @var resource Database connection */
        protected $mConn = null;
+       /** @var bool */
        protected $mOpened = false;
 
-       /** @var callable[] */
+       /** @var array[] List of (callable, method name) */
        protected $mTrxIdleCallbacks = [];
-       /** @var callable[] */
+       /** @var array[] List of (callable, method name) */
        protected $mTrxPreCommitCallbacks = [];
-
+       /** @var array[] List of (callable, method name) */
+       protected $mTrxEndCallbacks = [];
+       /** @var array[] Map of (name => (callable, method name)) */
+       protected $mTrxRecurringCallbacks = [];
+       /** @var bool Whether to suppress triggering of transaction end callbacks */
+       protected $mTrxEndCallbacksSuppressed = false;
+
+       /** @var string */
        protected $mTablePrefix;
+       /** @var string */
        protected $mSchema;
+       /** @var integer */
        protected $mFlags;
+       /** @var bool */
        protected $mForeign;
+       /** @var array */
        protected $mLBInfo = [];
+       /** @var bool|null */
        protected $mDefaultBigSelects = null;
+       /** @var array|bool */
        protected $mSchemaVars = false;
        /** @var array */
        protected $mSessionVars = [];
-
+       /** @var array|null */
        protected $preparedArgs;
-
+       /** @var string|bool|null Stashed value of html_errors INI setting */
        protected $htmlErrors;
-
+       /** @var string */
        protected $delimiter = ';';
 
        /**
@@ -80,7 +111,6 @@ abstract class DatabaseBase implements IDatabase {
         * @var int
         */
        protected $mTrxLevel = 0;
-
        /**
         * Either a short hexidecimal string if a transaction is active or ""
         *
@@ -88,7 +118,6 @@ abstract class DatabaseBase implements IDatabase {
         * @see DatabaseBase::mTrxLevel
         */
        protected $mTrxShortId = '';
-
        /**
         * The UNIX time that the transaction started. Callers can assume that if
         * snapshot isolation is used, then the data is *at least* up to date to that
@@ -98,10 +127,8 @@ abstract class DatabaseBase implements IDatabase {
         * @see DatabaseBase::mTrxLevel
         */
        private $mTrxTimestamp = null;
-
        /** @var float Lag estimate at the time of BEGIN */
-       private $mTrxSlaveLag = null;
-
+       private $mTrxReplicaLag = null;
        /**
         * Remembers the function name given for starting the most recent transaction via begin().
         * Used to provide additional context for error reporting.
@@ -110,7 +137,6 @@ abstract class DatabaseBase implements IDatabase {
         * @see DatabaseBase::mTrxLevel
         */
        private $mTrxFname = null;
-
        /**
         * Record if possible write queries were done in the last transaction started
         *
@@ -118,7 +144,6 @@ abstract class DatabaseBase implements IDatabase {
         * @see DatabaseBase::mTrxLevel
         */
        private $mTrxDoneWrites = false;
-
        /**
         * Record if the current transaction was started implicitly due to DBO_TRX being set.
         *
@@ -126,34 +151,44 @@ abstract class DatabaseBase implements IDatabase {
         * @see DatabaseBase::mTrxLevel
         */
        private $mTrxAutomatic = false;
-
        /**
         * Array of levels of atomicity within transactions
         *
         * @var array
         */
        private $mTrxAtomicLevels = [];
-
        /**
         * Record if the current transaction was started implicitly by DatabaseBase::startAtomic
         *
         * @var bool
         */
        private $mTrxAutomaticAtomic = false;
-
        /**
         * Track the write query callers of the current transaction
         *
         * @var string[]
         */
        private $mTrxWriteCallers = [];
-
        /**
-        * Track the seconds spent in write queries for the current transaction
-        *
-        * @var float
+        * @var float Seconds spent in write queries for the current transaction
         */
        private $mTrxWriteDuration = 0.0;
+       /**
+        * @var integer Number of write queries for the current transaction
+        */
+       private $mTrxWriteQueryCount = 0;
+       /**
+        * @var float Like mTrxWriteQueryCount but excludes lock-bound, easy to replicate, queries
+        */
+       private $mTrxWriteAdjDuration = 0.0;
+       /**
+        * @var integer Number of write queries counted in mTrxWriteAdjDuration
+        */
+       private $mTrxWriteAdjQueryCount = 0;
+       /**
+        * @var float RTT time estimate
+        */
+       private $mRTTEstimate = 0.0;
 
        /** @var array Map of (name => 1) for locks obtained via lock() */
        private $mNamedLocksHeld = [];
@@ -173,6 +208,14 @@ abstract class DatabaseBase implements IDatabase {
         */
        protected $allViews = null;
 
+       /** @var float UNIX timestamp */
+       protected $lastPing = 0.0;
+
+       /** @var int[] Prior mFlags values */
+       private $priorFlags = [];
+
+       /** @var Profiler */
+       protected $profiler;
        /** @var TransactionProfiler */
        protected $trxProfiler;
 
@@ -292,10 +335,6 @@ abstract class DatabaseBase implements IDatabase {
         * @return TransactionProfiler
         */
        protected function getTransactionProfiler() {
-               if ( !$this->trxProfiler ) {
-                       $this->trxProfiler = new TransactionProfiler();
-               }
-
                return $this->trxProfiler;
        }
 
@@ -393,8 +432,26 @@ abstract class DatabaseBase implements IDatabase {
                );
        }
 
-       public function pendingWriteQueryDuration() {
-               return $this->mTrxLevel ? $this->mTrxWriteDuration : false;
+       public function pendingWriteQueryDuration( $type = self::ESTIMATE_TOTAL ) {
+               if ( !$this->mTrxLevel ) {
+                       return false;
+               } elseif ( !$this->mTrxDoneWrites ) {
+                       return 0.0;
+               }
+
+               switch ( $type ) {
+                       case self::ESTIMATE_DB_APPLY:
+                               $this->ping( $rtt );
+                               $rttAdjTotal = $this->mTrxWriteAdjQueryCount * $rtt;
+                               $applyTime = max( $this->mTrxWriteAdjDuration - $rttAdjTotal, 0 );
+                               // For omitted queries, make them count as something at least
+                               $omitted = $this->mTrxWriteQueryCount - $this->mTrxWriteAdjQueryCount;
+                               $applyTime += self::TINY_WRITE_SEC * $omitted;
+
+                               return $applyTime;
+                       default: // everything
+                               return $this->mTrxWriteDuration;
+               }
        }
 
        public function pendingWriteCallers() {
@@ -405,14 +462,33 @@ abstract class DatabaseBase implements IDatabase {
                return $this->mOpened;
        }
 
-       public function setFlag( $flag ) {
+       public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
+               if ( $remember === self::REMEMBER_PRIOR ) {
+                       array_push( $this->priorFlags, $this->mFlags );
+               }
                $this->mFlags |= $flag;
        }
 
-       public function clearFlag( $flag ) {
+       public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) {
+               if ( $remember === self::REMEMBER_PRIOR ) {
+                       array_push( $this->priorFlags, $this->mFlags );
+               }
                $this->mFlags &= ~$flag;
        }
 
+       public function restoreFlags( $state = self::RESTORE_PRIOR ) {
+               if ( !$this->priorFlags ) {
+                       return;
+               }
+
+               if ( $state === self::RESTORE_INITIAL ) {
+                       $this->mFlags = reset( $this->priorFlags );
+                       $this->priorFlags = [];
+               } else {
+                       $this->mFlags = array_pop( $this->priorFlags );
+               }
+       }
+
        public function getFlag( $flag ) {
                return !!( $this->mFlags & $flag );
        }
@@ -496,7 +572,7 @@ abstract class DatabaseBase implements IDatabase {
         * @param array $params Parameters passed from DatabaseBase::factory()
         */
        function __construct( array $params ) {
-               global $wgDBprefix, $wgDBmwschema, $wgCommandLineMode;
+               global $wgDBprefix, $wgDBmwschema;
 
                $this->srvCache = ObjectCache::getLocalServerInstance( 'hash' );
 
@@ -509,9 +585,13 @@ abstract class DatabaseBase implements IDatabase {
                $schema = $params['schema'];
                $foreign = $params['foreign'];
 
+               $this->cliMode = isset( $params['cliMode'] )
+                       ? $params['cliMode']
+                       : ( PHP_SAPI === 'cli' );
+
                $this->mFlags = $flags;
                if ( $this->mFlags & DBO_DEFAULT ) {
-                       if ( $wgCommandLineMode ) {
+                       if ( $this->cliMode ) {
                                $this->mFlags &= ~DBO_TRX;
                        } else {
                                $this->mFlags |= DBO_TRX;
@@ -536,13 +616,17 @@ abstract class DatabaseBase implements IDatabase {
 
                $this->mForeign = $foreign;
 
-               if ( isset( $params['trxProfiler'] ) ) {
-                       $this->trxProfiler = $params['trxProfiler']; // override
-               }
+               $this->profiler = isset( $params['profiler'] )
+                       ? $params['profiler']
+                       : Profiler::instance(); // @TODO: remove global state
+               $this->trxProfiler = isset( $params['trxProfiler'] )
+                       ? $params['trxProfiler']
+                       : new TransactionProfiler();
 
                if ( $user ) {
                        $this->open( $server, $user, $password, $dbName );
                }
+
        }
 
        /**
@@ -578,6 +662,8 @@ abstract class DatabaseBase implements IDatabase {
         * @return DatabaseBase|null DatabaseBase subclass or null
         */
        final public static function factory( $dbType, $p = [] ) {
+               global $wgCommandLineMode;
+
                $canonicalDBTypes = [
                        'mysql' => [ 'mysqli', 'mysql' ],
                        'postgres' => [],
@@ -635,6 +721,7 @@ abstract class DatabaseBase implements IDatabase {
                                $p['schema'] = isset( $defaultSchemas[$dbType] ) ? $defaultSchemas[$dbType] : null;
                        }
                        $p['foreign'] = isset( $p['foreign'] ) ? $p['foreign'] : false;
+                       $p['cliMode'] = $wgCommandLineMode;
 
                        return new $class( $p );
                } else {
@@ -692,9 +779,6 @@ abstract class DatabaseBase implements IDatabase {
        }
 
        public function close() {
-               if ( count( $this->mTrxIdleCallbacks ) ) { // sanity
-                       throw new MWException( "Transaction idle callbacks still pending." );
-               }
                if ( $this->mConn ) {
                        if ( $this->trxLevel() ) {
                                if ( !$this->mTrxAutomatic ) {
@@ -702,11 +786,13 @@ abstract class DatabaseBase implements IDatabase {
                                                " performing implicit commit before closing connection!" );
                                }
 
-                               $this->commit( __METHOD__, 'flush' );
+                               $this->commit( __METHOD__, self::FLUSHING_INTERNAL );
                        }
 
                        $closed = $this->closeConnection();
                        $this->mConn = false;
+               } elseif ( $this->mTrxIdleCallbacks || $this->mTrxEndCallbacks ) { // sanity
+                       throw new MWException( "Transaction callbacks still pending." );
                } else {
                        $closed = true;
                }
@@ -760,7 +846,16 @@ abstract class DatabaseBase implements IDatabase {
         * @return bool
         */
        protected function isWriteQuery( $sql ) {
-               return !preg_match( '/^(?:SELECT|BEGIN|ROLLBACK|COMMIT|SET|SHOW|EXPLAIN|\(SELECT)\b/i', $sql );
+               return !preg_match(
+                       '/^(?:SELECT|BEGIN|ROLLBACK|COMMIT|SET|SHOW|EXPLAIN|\(SELECT)\b/i', $sql );
+       }
+
+       /**
+        * @param $sql
+        * @return string|null
+        */
+       protected function getQueryVerb( $sql ) {
+               return preg_match( '/^\s*([a-z]+)/i', $sql, $m ) ? strtoupper( $m[1] ) : null;
        }
 
        /**
@@ -773,17 +868,18 @@ abstract class DatabaseBase implements IDatabase {
         * @return bool
         */
        protected function isTransactableQuery( $sql ) {
-               $verb = substr( $sql, 0, strcspn( $sql, " \t\r\n" ) );
-               return !in_array( $verb, [ 'BEGIN', 'COMMIT', 'ROLLBACK', 'SHOW', 'SET' ] );
+               $verb = $this->getQueryVerb( $sql );
+               return !in_array( $verb, [ 'BEGIN', 'COMMIT', 'ROLLBACK', 'SHOW', 'SET' ], true );
        }
 
        public function query( $sql, $fname = __METHOD__, $tempIgnore = false ) {
                global $wgUser;
 
+               $priorWritesPending = $this->writesOrCallbacksPending();
                $this->mLastQuery = $sql;
 
-               $isWriteQuery = $this->isWriteQuery( $sql );
-               if ( $isWriteQuery ) {
+               $isWrite = $this->isWriteQuery( $sql );
+               if ( $isWrite ) {
                        $reason = $this->getReadOnlyReason();
                        if ( $reason !== false ) {
                                throw new DBReadOnlyError( $this, "Database is read-only: $reason" );
@@ -807,89 +903,51 @@ abstract class DatabaseBase implements IDatabase {
                // Or, for one-word queries (like "BEGIN" or COMMIT") add it to the end (bug 42598)
                $commentedSql = preg_replace( '/\s|$/', " /* $fname $userName */ ", $sql, 1 );
 
-               if ( !$this->mTrxLevel && $this->getFlag( DBO_TRX ) && $this->isTransactableQuery( $sql ) ) {
-                       $this->begin( __METHOD__ . " ($fname)" );
+               # Start implicit transactions that wrap the request if DBO_TRX is enabled
+               if ( !$this->mTrxLevel && $this->getFlag( DBO_TRX )
+                       && $this->isTransactableQuery( $sql )
+               ) {
+                       $this->begin( __METHOD__ . " ($fname)", self::TRANSACTION_INTERNAL );
                        $this->mTrxAutomatic = true;
                }
 
                # Keep track of whether the transaction has write queries pending
-               if ( $this->mTrxLevel && !$this->mTrxDoneWrites && $isWriteQuery ) {
+               if ( $this->mTrxLevel && !$this->mTrxDoneWrites && $isWrite ) {
                        $this->mTrxDoneWrites = true;
                        $this->getTransactionProfiler()->transactionWritingIn(
                                $this->mServer, $this->mDBname, $this->mTrxShortId );
                }
 
-               $isMaster = !is_null( $this->getLBInfo( 'master' ) );
-               # generalizeSQL will probably cut down the query to reasonable
-               # logging size most of the time. The substr is really just a sanity check.
-               if ( $isMaster ) {
-                       $queryProf = 'query-m: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
-                       $totalProf = 'DatabaseBase::query-master';
-               } else {
-                       $queryProf = 'query: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
-                       $totalProf = 'DatabaseBase::query';
-               }
-               # Include query transaction state
-               $queryProf .= $this->mTrxShortId ? " [TRX#{$this->mTrxShortId}]" : "";
-
-               $profiler = Profiler::instance();
-               if ( !$profiler instanceof ProfilerStub ) {
-                       $totalProfSection = $profiler->scopedProfileIn( $totalProf );
-                       $queryProfSection = $profiler->scopedProfileIn( $queryProf );
-               }
-
                if ( $this->debug() ) {
                        wfDebugLog( 'queries', sprintf( "%s: %s", $this->mDBname, $commentedSql ) );
                }
 
-               $queryId = MWDebug::query( $sql, $fname, $isMaster );
-
                # Avoid fatals if close() was called
                $this->assertOpen();
 
-               # Do the query and handle errors
-               $startTime = microtime( true );
-               $ret = $this->doQuery( $commentedSql );
-               $queryRuntime = microtime( true ) - $startTime;
-               # Log the query time and feed it into the DB trx profiler
-               $this->getTransactionProfiler()->recordQueryCompletion(
-                       $queryProf, $startTime, $isWriteQuery, $this->affectedRows() );
-
-               MWDebug::queryTime( $queryId );
+               # Send the query to the server
+               $ret = $this->doProfiledQuery( $sql, $commentedSql, $isWrite, $fname );
 
                # Try reconnecting if the connection was lost
                if ( false === $ret && $this->wasErrorReissuable() ) {
-                       # Transaction is gone; this can mean lost writes or REPEATABLE-READ snapshots
-                       $hadTrx = $this->mTrxLevel;
-                       # T127428: for non-write transactions, a disconnect and a COMMIT are similar:
-                       # neither changed data and in both cases any read snapshots are reset anyway.
-                       $isNoopCommit = ( !$this->writesOrCallbacksPending() && $sql === 'COMMIT' );
-                       # Update state tracking to reflect transaction loss
-                       $this->mTrxLevel = 0;
-                       $this->mTrxIdleCallbacks = []; // bug 65263
-                       $this->mTrxPreCommitCallbacks = []; // bug 65263
-                       wfDebug( "Connection lost, reconnecting...\n" );
-                       # Stash the last error values since ping() might clear them
+                       $recoverable = $this->canRecoverFromDisconnect( $sql, $priorWritesPending );
+                       # Stash the last error values before anything might clear them
                        $lastError = $this->lastError();
                        $lastErrno = $this->lastErrno();
-                       if ( $this->ping() ) {
+                       # Update state tracking to reflect transaction loss due to disconnection
+                       $this->handleTransactionLoss();
+                       wfDebug( "Connection lost, reconnecting...\n" );
+                       if ( $this->reconnect() ) {
                                wfDebug( "Reconnected\n" );
-                               $server = $this->getServer();
-                               $msg = __METHOD__ . ": lost connection to $server; reconnected";
+                               $msg = __METHOD__ . ": lost connection to {$this->getServer()}; reconnected";
                                wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
 
-                               if ( ( $hadTrx && !$isNoopCommit ) || $this->mNamedLocksHeld ) {
-                                       # Leave $ret as false and let an error be reported.
-                                       # Callers may catch the exception and continue to use the DB.
-                                       $this->reportQueryError( $lastError, $lastErrno, $sql, $fname, $tempIgnore );
+                               if ( !$recoverable ) {
+                                       # Callers may catch the exception and continue to use the DB
+                                       $this->reportQueryError( $lastError, $lastErrno, $sql, $fname );
                                } else {
-                                       # Should be safe to silently retry (no trx/callbacks/locks)
-                                       $startTime = microtime( true );
-                                       $ret = $this->doQuery( $commentedSql );
-                                       $queryRuntime = microtime( true ) - $startTime;
-                                       # Log the query time and feed it into the DB trx profiler
-                                       $this->getTransactionProfiler()->recordQueryCompletion(
-                                               $queryProf, $startTime, $isWriteQuery, $this->affectedRows() );
+                                       # Should be safe to silently retry the query
+                                       $ret = $this->doProfiledQuery( $sql, $commentedSql, $isWrite, $fname );
                                }
                        } else {
                                wfDebug( "Failed\n" );
@@ -897,22 +955,132 @@ abstract class DatabaseBase implements IDatabase {
                }
 
                if ( false === $ret ) {
+                       # Deadlocks cause the entire transaction to abort, not just the statement.
+                       # http://dev.mysql.com/doc/refman/5.7/en/innodb-error-handling.html
+                       # https://www.postgresql.org/docs/9.1/static/explicit-locking.html
+                       if ( $this->wasDeadlock() ) {
+                               if ( $this->explicitTrxActive() || $priorWritesPending ) {
+                                       $tempIgnore = false; // not recoverable
+                               }
+                               # Update state tracking to reflect transaction loss
+                               $this->handleTransactionLoss();
+                       }
+
                        $this->reportQueryError(
                                $this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore );
                }
 
                $res = $this->resultObject( $ret );
 
-               // Destroy profile sections in the opposite order to their creation
-               ScopedCallback::consume( $queryProfSection );
-               ScopedCallback::consume( $totalProfSection );
+               return $res;
+       }
 
-               if ( $isWriteQuery && $this->mTrxLevel ) {
-                       $this->mTrxWriteDuration += $queryRuntime;
-                       $this->mTrxWriteCallers[] = $fname;
+       private function doProfiledQuery( $sql, $commentedSql, $isWrite, $fname ) {
+               $isMaster = !is_null( $this->getLBInfo( 'master' ) );
+               # generalizeSQL() will probably cut down the query to reasonable
+               # logging size most of the time. The substr is really just a sanity check.
+               if ( $isMaster ) {
+                       $queryProf = 'query-m: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
+               } else {
+                       $queryProf = 'query: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 );
                }
 
-               return $res;
+               # Include query transaction state
+               $queryProf .= $this->mTrxShortId ? " [TRX#{$this->mTrxShortId}]" : "";
+
+               $startTime = microtime( true );
+               $this->profiler->profileIn( $queryProf );
+               $ret = $this->doQuery( $commentedSql );
+               $this->profiler->profileOut( $queryProf );
+               $queryRuntime = max( microtime( true ) - $startTime, 0.0 );
+
+               unset( $queryProfSection ); // profile out (if set)
+
+               if ( $ret !== false ) {
+                       $this->lastPing = $startTime;
+                       if ( $isWrite && $this->mTrxLevel ) {
+                               $this->updateTrxWriteQueryTime( $sql, $queryRuntime );
+                               $this->mTrxWriteCallers[] = $fname;
+                       }
+               }
+
+               if ( $sql === self::PING_QUERY ) {
+                       $this->mRTTEstimate = $queryRuntime;
+               }
+
+               $this->getTransactionProfiler()->recordQueryCompletion(
+                       $queryProf, $startTime, $isWrite, $this->affectedRows()
+               );
+               MWDebug::query( $sql, $fname, $isMaster, $queryRuntime );
+
+               return $ret;
+       }
+
+       /**
+        * Update the estimated run-time of a query, not counting large row lock times
+        *
+        * LoadBalancer can be set to rollback transactions that will create huge replication
+        * lag. It bases this estimate off of pendingWriteQueryDuration(). Certain simple
+        * queries, like inserting a row can take a long time due to row locking. This method
+        * uses some simple heuristics to discount those cases.
+        *
+        * @param string $sql A SQL write query
+        * @param float $runtime Total runtime, including RTT
+        */
+       private function updateTrxWriteQueryTime( $sql, $runtime ) {
+               // Whether this is indicative of replica DB runtime (except for RBR or ws_repl)
+               $indicativeOfReplicaRuntime = true;
+               if ( $runtime > self::SLOW_WRITE_SEC ) {
+                       $verb = $this->getQueryVerb( $sql );
+                       // insert(), upsert(), replace() are fast unless bulky in size or blocked on locks
+                       if ( $verb === 'INSERT' ) {
+                               $indicativeOfReplicaRuntime = $this->affectedRows() > self::SMALL_WRITE_ROWS;
+                       } elseif ( $verb === 'REPLACE' ) {
+                               $indicativeOfReplicaRuntime = $this->affectedRows() > self::SMALL_WRITE_ROWS / 2;
+                       }
+               }
+
+               $this->mTrxWriteDuration += $runtime;
+               $this->mTrxWriteQueryCount += 1;
+               if ( $indicativeOfReplicaRuntime ) {
+                       $this->mTrxWriteAdjDuration += $runtime;
+                       $this->mTrxWriteAdjQueryCount += 1;
+               }
+       }
+
+       private function canRecoverFromDisconnect( $sql, $priorWritesPending ) {
+               # Transaction dropped; this can mean lost writes, or REPEATABLE-READ snapshots.
+               # Dropped connections also mean that named locks are automatically released.
+               # Only allow error suppression in autocommit mode or when the lost transaction
+               # didn't matter anyway (aside from DBO_TRX snapshot loss).
+               if ( $this->mNamedLocksHeld ) {
+                       return false; // possible critical section violation
+               } elseif ( $sql === 'COMMIT' ) {
+                       return !$priorWritesPending; // nothing written anyway? (T127428)
+               } elseif ( $sql === 'ROLLBACK' ) {
+                       return true; // transaction lost...which is also what was requested :)
+               } elseif ( $this->explicitTrxActive() ) {
+                       return false; // don't drop atomocity
+               } elseif ( $priorWritesPending ) {
+                       return false; // prior writes lost from implicit transaction
+               }
+
+               return true;
+       }
+
+       private function handleTransactionLoss() {
+               $this->mTrxLevel = 0;
+               $this->mTrxIdleCallbacks = []; // bug 65263
+               $this->mTrxPreCommitCallbacks = []; // bug 65263
+               try {
+                       // Handle callbacks in mTrxEndCallbacks
+                       $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
+                       $this->runTransactionListenerCallbacks( self::TRIGGER_ROLLBACK );
+                       return null;
+               } catch ( Exception $e ) {
+                       // Already logged; move on...
+                       return $e;
+               }
        }
 
        public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
@@ -1650,6 +1818,15 @@ abstract class DatabaseBase implements IDatabase {
                return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
        }
 
+       /**
+        * @param string $field Field or column to cast
+        * @return string
+        * @since 1.28
+        */
+       public function buildStringCast( $field ) {
+               return $field;
+       }
+
        public function selectDB( $db ) {
                # Stub. Shouldn't cause serious problems if it's not overridden, but
                # if your database engine supports a concept similar to MySQL's
@@ -1828,7 +2005,7 @@ abstract class DatabaseBase implements IDatabase {
        /**
         * Gets an array of aliased table names
         *
-        * @param array $tables Array( [alias] => table )
+        * @param array $tables [ [alias] => table ]
         * @return string[] See tableNameWithAlias()
         */
        public function tableNamesWithAlias( $tables ) {
@@ -1862,7 +2039,7 @@ abstract class DatabaseBase implements IDatabase {
        /**
         * Gets an array of aliased field names
         *
-        * @param array $fields Array( [alias] => field )
+        * @param array $fields [ [alias] => field ]
         * @return string[] See fieldNameWithAlias()
         */
        public function fieldNamesWithAlias( $fields ) {
@@ -2197,7 +2374,7 @@ abstract class DatabaseBase implements IDatabase {
 
                $useTrx = !$this->mTrxLevel;
                if ( $useTrx ) {
-                       $this->begin( $fname );
+                       $this->begin( $fname, self::TRANSACTION_INTERNAL );
                }
                try {
                        # Update any existing conflicting row(s)
@@ -2210,12 +2387,12 @@ abstract class DatabaseBase implements IDatabase {
                        $ok = $this->insert( $table, $rows, $fname, [ 'IGNORE' ] ) && $ok;
                } catch ( Exception $e ) {
                        if ( $useTrx ) {
-                               $this->rollback( $fname );
+                               $this->rollback( $fname, self::FLUSHING_INTERNAL );
                        }
                        throw $e;
                }
                if ( $useTrx ) {
-                       $this->commit( $fname );
+                       $this->commit( $fname, self::FLUSHING_INTERNAL );
                }
 
                return $ok;
@@ -2294,7 +2471,46 @@ abstract class DatabaseBase implements IDatabase {
                return $this->query( $sql, $fname );
        }
 
-       public function insertSelect( $destTable, $srcTable, $varMap, $conds,
+       public function insertSelect(
+               $destTable, $srcTable, $varMap, $conds,
+               $fname = __METHOD__, $insertOptions = [], $selectOptions = []
+       ) {
+               if ( $this->cliMode ) {
+                       // For massive migrations with downtime, we don't want to select everything
+                       // into memory and OOM, so do all this native on the server side if possible.
+                       return $this->nativeInsertSelect(
+                               $destTable,
+                               $srcTable,
+                               $varMap,
+                               $conds,
+                               $fname,
+                               $insertOptions,
+                               $selectOptions
+                       );
+               }
+
+               // For web requests, do a locking SELECT and then INSERT. This puts the SELECT burden
+               // on only the master (without needing row-based-replication). It also makes it easy to
+               // know how big the INSERT is going to be.
+               $fields = [];
+               foreach ( $varMap as $dstColumn => $sourceColumnOrSql ) {
+                       $fields[] = $this->fieldNameWithAlias( $sourceColumnOrSql, $dstColumn );
+               }
+               $selectOptions[] = 'FOR UPDATE';
+               $res = $this->select( $srcTable, implode( ',', $fields ), $conds, $fname, $selectOptions );
+               if ( !$res ) {
+                       return false;
+               }
+
+               $rows = [];
+               foreach ( $res as $row ) {
+                       $rows[] = (array)$row;
+               }
+
+               return $this->insert( $destTable, $rows, $fname, $insertOptions );
+       }
+
+       public function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds,
                $fname = __METHOD__,
                $insertOptions = [], $selectOptions = []
        ) {
@@ -2424,14 +2640,19 @@ abstract class DatabaseBase implements IDatabase {
         * queries. If a deadlock occurs during the processing, the transaction
         * will be rolled back and the callback function will be called again.
         *
+        * Avoid using this method outside of Job or Maintenance classes.
+        *
         * Usage:
         *   $dbw->deadlockLoop( callback, ... );
         *
         * Extra arguments are passed through to the specified callback function.
+        * This method requires that no transactions are already active to avoid
+        * causing premature commits or exceptions.
         *
         * Returns whatever the callback function returned on its successful,
         * iteration, or false on error, for example if the retry limit was
         * reached.
+        *
         * @return mixed
         * @throws DBUnexpectedError
         * @throws Exception
@@ -2487,94 +2708,175 @@ abstract class DatabaseBase implements IDatabase {
                return false;
        }
 
-       final public function onTransactionIdle( $callback ) {
+       public function serverIsReadOnly() {
+               return false;
+       }
+
+       final public function onTransactionResolution( callable $callback ) {
+               if ( !$this->mTrxLevel ) {
+                       throw new DBUnexpectedError( $this, "No transaction is active." );
+               }
+               $this->mTrxEndCallbacks[] = [ $callback, wfGetCaller() ];
+       }
+
+       final public function onTransactionIdle( callable $callback ) {
                $this->mTrxIdleCallbacks[] = [ $callback, wfGetCaller() ];
                if ( !$this->mTrxLevel ) {
-                       $this->runOnTransactionIdleCallbacks();
+                       $this->runOnTransactionIdleCallbacks( self::TRIGGER_IDLE );
                }
        }
 
-       final public function onTransactionPreCommitOrIdle( $callback ) {
+       final public function onTransactionPreCommitOrIdle( callable $callback ) {
                if ( $this->mTrxLevel ) {
                        $this->mTrxPreCommitCallbacks[] = [ $callback, wfGetCaller() ];
                } else {
-                       $this->onTransactionIdle( $callback ); // this will trigger immediately
+                       // If no transaction is active, then make one for this callback
+                       $this->startAtomic( __METHOD__ );
+                       try {
+                               call_user_func( $callback );
+                               $this->endAtomic( __METHOD__ );
+                       } catch ( Exception $e ) {
+                               $this->rollback( __METHOD__, self::FLUSHING_INTERNAL );
+                               throw $e;
+                       }
+               }
+       }
+
+       final public function setTransactionListener( $name, callable $callback = null ) {
+               if ( $callback ) {
+                       $this->mTrxRecurringCallbacks[$name] = [ $callback, wfGetCaller() ];
+               } else {
+                       unset( $this->mTrxRecurringCallbacks[$name] );
                }
        }
 
        /**
-        * Actually any "on transaction idle" callbacks.
+        * Whether to disable running of post-COMMIT/ROLLBACK callbacks
         *
+        * This method should not be used outside of Database/LoadBalancer
+        *
+        * @param bool $suppress
+        * @since 1.28
+        */
+       final public function setTrxEndCallbackSuppression( $suppress ) {
+               $this->mTrxEndCallbacksSuppressed = $suppress;
+       }
+
+       /**
+        * Actually run and consume any "on transaction idle/resolution" callbacks.
+        *
+        * This method should not be used outside of Database/LoadBalancer
+        *
+        * @param integer $trigger IDatabase::TRIGGER_* constant
         * @since 1.20
+        * @throws Exception
         */
-       protected function runOnTransactionIdleCallbacks() {
-               $autoTrx = $this->getFlag( DBO_TRX ); // automatic begin() enabled?
+       public function runOnTransactionIdleCallbacks( $trigger ) {
+               if ( $this->mTrxEndCallbacksSuppressed ) {
+                       return;
+               }
 
-               $e = $ePrior = null; // last exception
+               $autoTrx = $this->getFlag( DBO_TRX ); // automatic begin() enabled?
+               /** @var Exception $e */
+               $e = null; // first exception
                do { // callbacks may add callbacks :)
-                       $callbacks = $this->mTrxIdleCallbacks;
-                       $this->mTrxIdleCallbacks = []; // recursion guard
+                       $callbacks = array_merge(
+                               $this->mTrxIdleCallbacks,
+                               $this->mTrxEndCallbacks // include "transaction resolution" callbacks
+                       );
+                       $this->mTrxIdleCallbacks = []; // consumed (and recursion guard)
+                       $this->mTrxEndCallbacks = []; // consumed (recursion guard)
                        foreach ( $callbacks as $callback ) {
                                try {
                                        list( $phpCallback ) = $callback;
                                        $this->clearFlag( DBO_TRX ); // make each query its own transaction
-                                       call_user_func( $phpCallback );
+                                       call_user_func_array( $phpCallback, [ $trigger ] );
                                        if ( $autoTrx ) {
                                                $this->setFlag( DBO_TRX ); // restore automatic begin()
                                        } else {
                                                $this->clearFlag( DBO_TRX ); // restore auto-commit
                                        }
-                               } catch ( Exception $e ) {
-                                       if ( $ePrior ) {
-                                               MWExceptionHandler::logException( $ePrior );
-                                       }
-                                       $ePrior = $e;
+                               } catch ( Exception $ex ) {
+                                       MWExceptionHandler::logException( $ex );
+                                       $e = $e ?: $ex;
                                        // Some callbacks may use startAtomic/endAtomic, so make sure
                                        // their transactions are ended so other callbacks don't fail
                                        if ( $this->trxLevel() ) {
-                                               $this->rollback( __METHOD__ );
+                                               $this->rollback( __METHOD__, self::FLUSHING_INTERNAL );
                                        }
                                }
                        }
                } while ( count( $this->mTrxIdleCallbacks ) );
 
                if ( $e instanceof Exception ) {
-                       throw $e; // re-throw any last exception
+                       throw $e; // re-throw any first exception
                }
        }
 
        /**
-        * Actually any "on transaction pre-commit" callbacks.
+        * Actually run and consume any "on transaction pre-commit" callbacks.
+        *
+        * This method should not be used outside of Database/LoadBalancer
         *
         * @since 1.22
+        * @throws Exception
         */
-       protected function runOnTransactionPreCommitCallbacks() {
-               $e = $ePrior = null; // last exception
+       public function runOnTransactionPreCommitCallbacks() {
+               $e = null; // first exception
                do { // callbacks may add callbacks :)
                        $callbacks = $this->mTrxPreCommitCallbacks;
-                       $this->mTrxPreCommitCallbacks = []; // recursion guard
+                       $this->mTrxPreCommitCallbacks = []; // consumed (and recursion guard)
                        foreach ( $callbacks as $callback ) {
                                try {
                                        list( $phpCallback ) = $callback;
                                        call_user_func( $phpCallback );
-                               } catch ( Exception $e ) {
-                                       if ( $ePrior ) {
-                                               MWExceptionHandler::logException( $ePrior );
-                                       }
-                                       $ePrior = $e;
+                               } catch ( Exception $ex ) {
+                                       MWExceptionHandler::logException( $ex );
+                                       $e = $e ?: $ex;
                                }
                        }
                } while ( count( $this->mTrxPreCommitCallbacks ) );
 
                if ( $e instanceof Exception ) {
-                       throw $e; // re-throw any last exception
+                       throw $e; // re-throw any first exception
+               }
+       }
+
+       /**
+        * Actually run any "transaction listener" callbacks.
+        *
+        * This method should not be used outside of Database/LoadBalancer
+        *
+        * @param integer $trigger IDatabase::TRIGGER_* constant
+        * @throws Exception
+        * @since 1.20
+        */
+       public function runTransactionListenerCallbacks( $trigger ) {
+               if ( $this->mTrxEndCallbacksSuppressed ) {
+                       return;
+               }
+
+               /** @var Exception $e */
+               $e = null; // first exception
+
+               foreach ( $this->mTrxRecurringCallbacks as $callback ) {
+                       try {
+                               list( $phpCallback ) = $callback;
+                               $phpCallback( $trigger, $this );
+                       } catch ( Exception $ex ) {
+                               MWExceptionHandler::logException( $ex );
+                               $e = $e ?: $ex;
+                       }
+               }
+
+               if ( $e instanceof Exception ) {
+                       throw $e; // re-throw any first exception
                }
        }
 
        final public function startAtomic( $fname = __METHOD__ ) {
                if ( !$this->mTrxLevel ) {
-                       $this->begin( $fname );
-                       $this->mTrxAutomatic = true;
+                       $this->begin( $fname, self::TRANSACTION_INTERNAL );
                        // If DBO_TRX is set, a series of startAtomic/endAtomic pairs will result
                        // in all changes being in one transaction to keep requests transactional.
                        if ( !$this->getFlag( DBO_TRX ) ) {
@@ -2587,88 +2889,78 @@ abstract class DatabaseBase implements IDatabase {
 
        final public function endAtomic( $fname = __METHOD__ ) {
                if ( !$this->mTrxLevel ) {
-                       throw new DBUnexpectedError( $this, 'No atomic transaction is open.' );
+                       throw new DBUnexpectedError( $this, "No atomic transaction is open (got $fname)." );
                }
                if ( !$this->mTrxAtomicLevels ||
                        array_pop( $this->mTrxAtomicLevels ) !== $fname
                ) {
-                       throw new DBUnexpectedError( $this, 'Invalid atomic section ended.' );
+                       throw new DBUnexpectedError( $this, "Invalid atomic section ended (got $fname)." );
                }
 
                if ( !$this->mTrxAtomicLevels && $this->mTrxAutomaticAtomic ) {
-                       $this->commit( $fname, 'flush' );
+                       $this->commit( $fname, self::FLUSHING_INTERNAL );
                }
        }
 
        final public function doAtomicSection( $fname, callable $callback ) {
                $this->startAtomic( $fname );
                try {
-                       call_user_func_array( $callback, [ $this, $fname ] );
+                       $res = call_user_func_array( $callback, [ $this, $fname ] );
                } catch ( Exception $e ) {
-                       $this->rollback( $fname );
+                       $this->rollback( $fname, self::FLUSHING_INTERNAL );
                        throw $e;
                }
                $this->endAtomic( $fname );
+
+               return $res;
        }
 
-       final public function begin( $fname = __METHOD__ ) {
-               if ( $this->mTrxLevel ) { // implicit commit
+       final public function begin( $fname = __METHOD__, $mode = self::TRANSACTION_EXPLICIT ) {
+               // Protect against mismatched atomic section, transaction nesting, and snapshot loss
+               if ( $this->mTrxLevel ) {
                        if ( $this->mTrxAtomicLevels ) {
-                               // If the current transaction was an automatic atomic one, then we definitely have
-                               // a problem. Same if there is any unclosed atomic level.
                                $levels = implode( ', ', $this->mTrxAtomicLevels );
-                               throw new DBUnexpectedError(
-                                       $this,
-                                       "Got explicit BEGIN from $fname while atomic section(s) $levels are open."
-                               );
+                               $msg = "$fname: Got explicit BEGIN while atomic section(s) $levels are open.";
+                               throw new DBUnexpectedError( $this, $msg );
                        } elseif ( !$this->mTrxAutomatic ) {
-                               // We want to warn about inadvertently nested begin/commit pairs, but not about
-                               // auto-committing implicit transactions that were started by query() via DBO_TRX
-                               throw new DBUnexpectedError(
-                                       $this,
-                                       "$fname: Transaction already in progress (from {$this->mTrxFname}), " .
-                                               " performing implicit commit!"
-                               );
+                               $msg = "$fname: Explicit transaction already active (from {$this->mTrxFname}).";
+                               throw new DBUnexpectedError( $this, $msg );
                        } else {
-                               // The transaction was automatic and has done write operations
-                               if ( $this->mTrxDoneWrites ) {
-                                       wfDebug( "$fname: Automatic transaction with writes in progress" .
-                                               " (from {$this->mTrxFname}), performing implicit commit!\n"
-                                       );
-                               }
-                       }
-
-                       $this->runOnTransactionPreCommitCallbacks();
-                       $writeTime = $this->pendingWriteQueryDuration();
-                       $this->doCommit( $fname );
-                       if ( $this->mTrxDoneWrites ) {
-                               $this->mDoneWrites = microtime( true );
-                               $this->getTransactionProfiler()->transactionWritingOut(
-                                       $this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
+                               // @TODO: make this an exception at some point
+                               $msg = "$fname: Implicit transaction already active (from {$this->mTrxFname}).";
+                               wfLogDBError( $msg );
+                               wfWarn( $msg );
+                               return; // join the main transaction set
                        }
-                       $this->runOnTransactionIdleCallbacks();
+               } elseif ( $this->getFlag( DBO_TRX ) && $mode !== self::TRANSACTION_INTERNAL ) {
+                       // @TODO: make this an exception at some point
+                       $msg = "$fname: Implicit transaction expected (DBO_TRX set).";
+                       wfLogDBError( $msg );
+                       wfWarn( $msg );
+                       return; // let any writes be in the main transaction
                }
 
-               # Avoid fatals if close() was called
+               // Avoid fatals if close() was called
                $this->assertOpen();
 
                $this->doBegin( $fname );
                $this->mTrxTimestamp = microtime( true );
                $this->mTrxFname = $fname;
                $this->mTrxDoneWrites = false;
-               $this->mTrxAutomatic = false;
+               $this->mTrxAutomatic = ( $mode === self::TRANSACTION_INTERNAL );
                $this->mTrxAutomaticAtomic = false;
                $this->mTrxAtomicLevels = [];
-               $this->mTrxIdleCallbacks = [];
-               $this->mTrxPreCommitCallbacks = [];
                $this->mTrxShortId = wfRandomString( 12 );
                $this->mTrxWriteDuration = 0.0;
+               $this->mTrxWriteQueryCount = 0;
+               $this->mTrxWriteAdjDuration = 0.0;
+               $this->mTrxWriteAdjQueryCount = 0;
                $this->mTrxWriteCallers = [];
                // First SELECT after BEGIN will establish the snapshot in REPEATABLE-READ.
-               // Get an estimate of the slave lag before then, treating estimate staleness
+               // Get an estimate of the replica DB lag before then, treating estimate staleness
                // as lag itself just to be safe
                $status = $this->getApproximateLagStatus();
-               $this->mTrxSlaveLag = $status['lag'] + ( microtime( true ) - $status['since'] );
+               $this->mTrxReplicaLag = $status['lag'] + ( microtime( true ) - $status['since'] );
        }
 
        /**
@@ -2688,40 +2980,46 @@ abstract class DatabaseBase implements IDatabase {
                        $levels = implode( ', ', $this->mTrxAtomicLevels );
                        throw new DBUnexpectedError(
                                $this,
-                               "Got COMMIT while atomic sections $levels are still open"
+                               "$fname: Got COMMIT while atomic sections $levels are still open."
                        );
                }
 
-               if ( $flush === 'flush' ) {
+               if ( $flush === self::FLUSHING_INTERNAL || $flush === self::FLUSHING_ALL_PEERS ) {
                        if ( !$this->mTrxLevel ) {
                                return; // nothing to do
                        } elseif ( !$this->mTrxAutomatic ) {
                                throw new DBUnexpectedError(
                                        $this,
-                                       "$fname: Flushing an explicit transaction, getting out of sync!"
+                                       "$fname: Flushing an explicit transaction, getting out of sync."
                                );
                        }
                } else {
                        if ( !$this->mTrxLevel ) {
-                               wfWarn( "$fname: No transaction to commit, something got out of sync!" );
+                               wfWarn( "$fname: No transaction to commit, something got out of sync." );
                                return; // nothing to do
                        } elseif ( $this->mTrxAutomatic ) {
-                               wfWarn( "$fname: Explicit commit of implicit transaction. Something may be out of sync!" );
+                               // @TODO: make this an exception at some point
+                               $msg = "$fname: Explicit commit of implicit transaction.";
+                               wfLogDBError( $msg );
+                               wfWarn( $msg );
+                               return; // wait for the main transaction set commit round
                        }
                }
 
-               # Avoid fatals if close() was called
+               // Avoid fatals if close() was called
                $this->assertOpen();
 
                $this->runOnTransactionPreCommitCallbacks();
-               $writeTime = $this->pendingWriteQueryDuration();
+               $writeTime = $this->pendingWriteQueryDuration( self::ESTIMATE_DB_APPLY );
                $this->doCommit( $fname );
                if ( $this->mTrxDoneWrites ) {
                        $this->mDoneWrites = microtime( true );
                        $this->getTransactionProfiler()->transactionWritingOut(
                                $this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
                }
-               $this->runOnTransactionIdleCallbacks();
+
+               $this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
+               $this->runTransactionListenerCallbacks( self::TRIGGER_COMMIT );
        }
 
        /**
@@ -2738,28 +3036,36 @@ abstract class DatabaseBase implements IDatabase {
        }
 
        final public function rollback( $fname = __METHOD__, $flush = '' ) {
-               if ( $flush !== 'flush' ) {
+               if ( $flush === self::FLUSHING_INTERNAL || $flush === self::FLUSHING_ALL_PEERS ) {
                        if ( !$this->mTrxLevel ) {
-                               wfWarn( "$fname: No transaction to rollback, something got out of sync!" );
                                return; // nothing to do
                        }
                } else {
                        if ( !$this->mTrxLevel ) {
+                               wfWarn( "$fname: No transaction to rollback, something got out of sync." );
                                return; // nothing to do
+                       } elseif ( $this->getFlag( DBO_TRX ) ) {
+                               throw new DBUnexpectedError(
+                                       $this,
+                                       "$fname: Expected mass rollback of all peer databases (DBO_TRX set)."
+                               );
                        }
                }
 
-               # Avoid fatals if close() was called
+               // Avoid fatals if close() was called
                $this->assertOpen();
 
                $this->doRollback( $fname );
-               $this->mTrxIdleCallbacks = []; // cancel
-               $this->mTrxPreCommitCallbacks = []; // cancel
                $this->mTrxAtomicLevels = [];
                if ( $this->mTrxDoneWrites ) {
                        $this->getTransactionProfiler()->transactionWritingOut(
                                $this->mServer, $this->mDBname, $this->mTrxShortId );
                }
+
+               $this->mTrxIdleCallbacks = []; // clear
+               $this->mTrxPreCommitCallbacks = []; // clear
+               $this->runOnTransactionIdleCallbacks( self::TRIGGER_ROLLBACK );
+               $this->runTransactionListenerCallbacks( self::TRIGGER_ROLLBACK );
        }
 
        /**
@@ -2770,11 +3076,29 @@ abstract class DatabaseBase implements IDatabase {
         */
        protected function doRollback( $fname ) {
                if ( $this->mTrxLevel ) {
-                       $this->query( 'ROLLBACK', $fname, true );
+                       # Disconnects cause rollback anyway, so ignore those errors
+                       $ignoreErrors = true;
+                       $this->query( 'ROLLBACK', $fname, $ignoreErrors );
                        $this->mTrxLevel = 0;
                }
        }
 
+       public function flushSnapshot( $fname = __METHOD__ ) {
+               if ( $this->writesOrCallbacksPending() || $this->explicitTrxActive() ) {
+                       // This only flushes transactions to clear snapshots, not to write data
+                       throw new DBUnexpectedError(
+                               $this,
+                               "$fname: Cannot COMMIT to clear snapshot because writes are pending."
+                       );
+               }
+
+               $this->commit( $fname, self::FLUSHING_INTERNAL );
+       }
+
+       public function explicitTrxActive() {
+               return $this->mTrxLevel && ( $this->mTrxAtomicLevels || !$this->mTrxAutomatic );
+       }
+
        /**
         * Creates a new table with structure copied from existing table
         * Note that unlike most database abstraction functions, this function does not
@@ -2875,9 +3199,43 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       public function ping() {
-               # Stub. Not essential to override.
-               return true;
+       public function ping( &$rtt = null ) {
+               // Avoid hitting the server if it was hit recently
+               if ( $this->isOpen() && ( microtime( true ) - $this->lastPing ) < self::PING_TTL ) {
+                       if ( !func_num_args() || $this->mRTTEstimate > 0 ) {
+                               $rtt = $this->mRTTEstimate;
+                               return true; // don't care about $rtt
+                       }
+               }
+
+               // This will reconnect if possible or return false if not
+               $this->clearFlag( DBO_TRX, self::REMEMBER_PRIOR );
+               $ok = ( $this->query( self::PING_QUERY, __METHOD__, true ) !== false );
+               $this->restoreFlags( self::RESTORE_PRIOR );
+
+               if ( $ok ) {
+                       $rtt = $this->mRTTEstimate;
+               }
+
+               return $ok;
+       }
+
+       /**
+        * @return bool
+        */
+       protected function reconnect() {
+               $this->closeConnection();
+               $this->mOpened = false;
+               $this->mConn = false;
+               try {
+                       $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
+                       $this->lastPing = microtime( true );
+                       $ok = true;
+               } catch ( DBConnectionError $e ) {
+                       $ok = false;
+               }
+
+               return $ok;
        }
 
        public function getSessionLagStatus() {
@@ -2885,7 +3243,7 @@ abstract class DatabaseBase implements IDatabase {
        }
 
        /**
-        * Get the slave lag when the current transaction started
+        * Get the replica DB lag when the current transaction started
         *
         * This is useful when transactions might use snapshot isolation
         * (e.g. REPEATABLE-READ in innodb), so the "real" lag of that data
@@ -2897,19 +3255,19 @@ abstract class DatabaseBase implements IDatabase {
         */
        public function getTransactionLagStatus() {
                return $this->mTrxLevel
-                       ? [ 'lag' => $this->mTrxSlaveLag, 'since' => $this->trxTimestamp() ]
+                       ? [ 'lag' => $this->mTrxReplicaLag, 'since' => $this->trxTimestamp() ]
                        : null;
        }
 
        /**
-        * Get a slave lag estimate for this server
+        * Get a replica DB lag estimate for this server
         *
         * @return array ('lag': seconds or false on error, 'since': UNIX timestamp of estimate)
         * @since 1.27
         */
        public function getApproximateLagStatus() {
                return [
-                       'lag'   => $this->getLBInfo( 'slave' ) ? $this->getLag() : 0,
+                       'lag'   => $this->getLBInfo( 'replica' ) ? $this->getLag() : 0,
                        'since' => microtime( true )
                ];
        }
@@ -3215,16 +3573,32 @@ abstract class DatabaseBase implements IDatabase {
        }
 
        public function getScopedLockAndFlush( $lockKey, $fname, $timeout ) {
+               if ( $this->writesOrCallbacksPending() ) {
+                       // This only flushes transactions to clear snapshots, not to write data
+                       throw new DBUnexpectedError(
+                               $this,
+                               "$fname: Cannot COMMIT to clear snapshot because writes are pending."
+                       );
+               }
+
                if ( !$this->lock( $lockKey, $fname, $timeout ) ) {
                        return null;
                }
 
                $unlocker = new ScopedCallback( function () use ( $lockKey, $fname ) {
-                       $this->commit( __METHOD__, 'flush' );
-                       $this->unlock( $lockKey, $fname );
+                       if ( $this->trxLevel() ) {
+                               // There is a good chance an exception was thrown, causing any early return
+                               // from the caller. Let any error handler get a chance to issue rollback().
+                               // If there isn't one, let the error bubble up and trigger server-side rollback.
+                               $this->onTransactionResolution( function () use ( $lockKey, $fname ) {
+                                       $this->unlock( $lockKey, $fname );
+                               } );
+                       } else {
+                               $this->unlock( $lockKey, $fname );
+                       }
                } );
 
-               $this->commit( __METHOD__, 'flush' );
+               $this->commit( __METHOD__, self::FLUSHING_INTERNAL );
 
                return $unlocker;
        }
@@ -3333,9 +3707,14 @@ abstract class DatabaseBase implements IDatabase {
                if ( $this->mTrxLevel && $this->mTrxDoneWrites ) {
                        trigger_error( "Uncommitted DB writes (transaction from {$this->mTrxFname})." );
                }
-               if ( count( $this->mTrxIdleCallbacks ) || count( $this->mTrxPreCommitCallbacks ) ) {
+               $danglingCallbacks = array_merge(
+                       $this->mTrxIdleCallbacks,
+                       $this->mTrxPreCommitCallbacks,
+                       $this->mTrxEndCallbacks
+               );
+               if ( $danglingCallbacks ) {
                        $callers = [];
-                       foreach ( $this->mTrxIdleCallbacks as $callbackInfo ) {
+                       foreach ( $danglingCallbacks as $callbackInfo ) {
                                $callers[] = $callbackInfo[1];
                        }
                        $callers = implode( ', ', $callers );
index 4cd02b1..cfae74f 100644 (file)
@@ -470,3 +470,21 @@ class DBReadOnlyError extends DBExpectedError {
  */
 class DBTransactionError extends DBExpectedError {
 }
+
+/**
+ * Exception class for attempted DB access
+ * @ingroup Database
+ */
+class DBAccessError extends DBUnexpectedError {
+       public function __construct() {
+               parent::__construct( "Mediawiki tried to access the database via wfGetDB(). " .
+                       "This is not allowed, because database access has been disabled." );
+       }
+}
+
+/**
+ * Exception class for replica DB wait timeouts
+ * @ingroup Database
+ */
+class DBReplicationWaitError extends DBUnexpectedError {
+}
index 66c348d..f8770d2 100644 (file)
@@ -378,10 +378,10 @@ class DatabaseMssql extends Database {
         * @param mixed $conds Array or string, condition(s) for WHERE
         * @param string $fname Calling function name (use __METHOD__) for logs/profiling
         * @param array $options Associative array of options (e.g.
-        *   array('GROUP BY' => 'page_title')), see Database::makeSelectOptions
+        *   [ 'GROUP BY' => 'page_title' ]), see Database::makeSelectOptions
         *   code for list of supported stuff
         * @param array $join_conds Associative array of table join conditions
-        *   (optional) (e.g. array( 'page' => array('LEFT JOIN','page_latest=rev_id') )
+        *   (optional) (e.g. [ 'page' => [ 'LEFT JOIN','page_latest=rev_id' ] ]
         * @return mixed Database result resource (feed to Database::fetchObject
         *   or whatever), or false on failure
         * @throws DBQueryError
@@ -434,10 +434,10 @@ class DatabaseMssql extends Database {
         * @param mixed $vars Array or string, field name(s) to be retrieved
         * @param mixed $conds Array or string, condition(s) for WHERE
         * @param string $fname Calling function name (use __METHOD__) for logs/profiling
-        * @param array $options Associative array of options (e.g. array('GROUP BY' => 'page_title')),
+        * @param array $options Associative array of options (e.g. [ 'GROUP BY' => 'page_title' ]),
         *   see Database::makeSelectOptions code for list of supported stuff
         * @param array $join_conds Associative array of table join conditions (optional)
-        *    (e.g. array( 'page' => array('LEFT JOIN','page_latest=rev_id') )
+        *    (e.g. [ 'page' => [ 'LEFT JOIN','page_latest=rev_id' ] ]
         * @return string The SQL text
         */
        public function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__,
@@ -717,7 +717,7 @@ class DatabaseMssql extends Database {
 
        /**
         * INSERT SELECT wrapper
-        * $varMap must be an associative array of the form array( 'dest1' => 'source1', ...)
+        * $varMap must be an associative array of the form [ 'dest1' => 'source1', ... ]
         * Source items may be literals rather than field names, but strings should
         * be quoted with Database::addQuotes().
         * @param string $destTable
@@ -730,12 +730,12 @@ class DatabaseMssql extends Database {
         * @return null|ResultWrapper
         * @throws Exception
         */
-       public function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
+       public function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
                $insertOptions = [], $selectOptions = []
        ) {
                $this->mScrollableCursor = false;
                try {
-                       $ret = parent::insertSelect(
+                       $ret = parent::nativeInsertSelect(
                                $destTable,
                                $srcTable,
                                $varMap,
index 5b15147..87330b0 100644 (file)
@@ -201,10 +201,4 @@ class DatabaseMysql extends DatabaseMysqlBase {
 
                return mysql_real_escape_string( $s, $conn );
        }
-
-       protected function mysqlPing() {
-               $conn = $this->getBindingHandle();
-
-               return mysql_ping( $conn );
-       }
 }
index 52dee01..1b60ea1 100644 (file)
  */
 abstract class DatabaseMysqlBase extends Database {
        /** @var MysqlMasterPos */
-       protected $lastKnownSlavePos;
-       /** @var string Method to detect slave lag */
+       protected $lastKnownReplicaPos;
+       /** @var string Method to detect replica DB lag */
        protected $lagDetectionMethod;
-       /** @var array Method to detect slave lag */
+       /** @var array Method to detect replica DB lag */
        protected $lagDetectionOptions = [];
-
+       /** @var bool bool Whether to use GTID methods */
+       protected $useGTIDs = false;
+       /** @var string|null */
+       protected $sslKeyPath;
+       /** @var string|null */
+       protected $sslCertPath;
+       /** @var string|null */
+       protected $sslCAPath;
+       /** @var string[]|null */
+       protected $sslCiphers;
        /** @var string|null */
        private $serverVersion = null;
 
        /**
         * Additional $params include:
         *   - lagDetectionMethod : set to one of (Seconds_Behind_Master,pt-heartbeat).
-        *                          pt-heartbeat assumes the table is at heartbeat.heartbeat
-        *                          and uses UTC timestamps in the heartbeat.ts column.
-        *                          (https://www.percona.com/doc/percona-toolkit/2.2/pt-heartbeat.html)
+        *       pt-heartbeat assumes the table is at heartbeat.heartbeat
+        *       and uses UTC timestamps in the heartbeat.ts column.
+        *       (https://www.percona.com/doc/percona-toolkit/2.2/pt-heartbeat.html)
         *   - lagDetectionOptions : if using pt-heartbeat, this can be set to an array map to change
-        *                           the default behavior. Normally, the heartbeat row with the server
-        *                           ID of this server's master will be used. Set the "conds" field to
-        *                           override the query conditions, e.g. ['shard' => 's1'].
+        *       the default behavior. Normally, the heartbeat row with the server
+        *       ID of this server's master will be used. Set the "conds" field to
+        *       override the query conditions, e.g. ['shard' => 's1'].
+        *   - useGTIDs : use GTID methods like MASTER_GTID_WAIT() when possible.
+        *   - sslKeyPath : path to key file [default: null]
+        *   - sslCertPath : path to certificate file [default: null]
+        *   - sslCAPath : parth to certificate authority PEM files [default: null]
+        *   - sslCiphers : array list of allowable ciphers [default: null]
         * @param array $params
         */
        function __construct( array $params ) {
@@ -61,6 +75,13 @@ abstract class DatabaseMysqlBase extends Database {
                $this->lagDetectionOptions = isset( $params['lagDetectionOptions'] )
                        ? $params['lagDetectionOptions']
                        : [];
+               $this->useGTIDs = !empty( $params['useGTIDs' ] );
+               foreach ( [ 'KeyPath', 'CertPath', 'CAPath', 'Ciphers' ] as $name ) {
+                       $var = "ssl{$name}";
+                       if ( isset( $params[$var] ) ) {
+                               $this->$var = $params[$var];
+                       }
+               }
        }
 
        /**
@@ -566,14 +587,7 @@ abstract class DatabaseMysqlBase extends Database {
         * @return string
         */
        function strencode( $s ) {
-               $sQuoted = $this->mysqlRealEscapeString( $s );
-
-               if ( $sQuoted === false ) {
-                       $this->ping();
-                       $sQuoted = $this->mysqlRealEscapeString( $s );
-               }
-
-               return $sQuoted;
+               return $this->mysqlRealEscapeString( $s );
        }
 
        /**
@@ -602,33 +616,6 @@ abstract class DatabaseMysqlBase extends Database {
                return strlen( $name ) && $name[0] == '`' && substr( $name, -1, 1 ) == '`';
        }
 
-       /**
-        * @return bool
-        */
-       function ping() {
-               $ping = $this->mysqlPing();
-               if ( $ping ) {
-                       // Connection was good or lost but reconnected...
-                       // @note: mysqlnd (php 5.6+) does not support this (PHP bug 52561)
-                       return true;
-               }
-
-               // Try a full disconnect/reconnect cycle if ping() failed
-               $this->closeConnection();
-               $this->mOpened = false;
-               $this->mConn = false;
-               $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname );
-
-               return true;
-       }
-
-       /**
-        * Ping a server connection or reconnect if there is no connection
-        *
-        * @return bool
-        */
-       abstract protected function mysqlPing();
-
        function getLag() {
                if ( $this->getLagDetectionMethod() === 'pt-heartbeat' ) {
                        return $this->getLagFromPtHeartbeat();
@@ -708,7 +695,7 @@ abstract class DatabaseMysqlBase extends Database {
                $key = $cache->makeGlobalKey(
                        'mysql',
                        'master-info',
-                       // Using one key for all cluster slaves is preferable
+                       // Using one key for all cluster replica DBs is preferable
                        $this->getLBInfo( 'clusterMasterHost' ) ?: $this->getServer()
                );
 
@@ -784,17 +771,21 @@ abstract class DatabaseMysqlBase extends Database {
 
                if ( $this->getLBInfo( 'is static' ) === true ) {
                        return 0; // this is a copy of a read-only dataset with no master DB
-               } elseif ( $this->lastKnownSlavePos && $this->lastKnownSlavePos->hasReached( $pos ) ) {
+               } elseif ( $this->lastKnownReplicaPos && $this->lastKnownReplicaPos->hasReached( $pos ) ) {
                        return 0; // already reached this point for sure
                }
 
-               # Commit any open transactions
-               $this->commit( __METHOD__, 'flush' );
-
-               # Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set
-               $encFile = $this->addQuotes( $pos->file );
-               $encPos = intval( $pos->pos );
-               $res = $this->doQuery( "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)" );
+               // Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set
+               if ( $this->useGTIDs && $pos->gtids ) {
+                       // Wait on the GTID set (MariaDB only)
+                       $gtidArg = $this->addQuotes( implode( ',', $pos->gtids ) );
+                       $res = $this->doQuery( "SELECT MASTER_GTID_WAIT($gtidArg, $timeout)" );
+               } else {
+                       // Wait on the binlog coordinates
+                       $encFile = $this->addQuotes( $pos->file );
+                       $encPos = intval( $pos->pos );
+                       $res = $this->doQuery( "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)" );
+               }
 
                $row = $res ? $this->fetchRow( $res ) : false;
                if ( !$row ) {
@@ -806,16 +797,16 @@ abstract class DatabaseMysqlBase extends Database {
                if ( $status === null ) {
                        // T126436: jobs programmed to wait on master positions might be referencing binlogs
                        // with an old master hostname. Such calls make MASTER_POS_WAIT() return null. Try
-                       // to detect this and treat the slave as having reached the position; a proper master
+                       // to detect this and treat the replica DB as having reached the position; a proper master
                        // switchover already requires that the new master be caught up before the switch.
-                       $slavePos = $this->getSlavePos();
-                       if ( $slavePos && !$slavePos->channelsMatch( $pos ) ) {
-                               $this->lastKnownSlavePos = $slavePos;
+                       $replicationPos = $this->getSlavePos();
+                       if ( $replicationPos && !$replicationPos->channelsMatch( $pos ) ) {
+                               $this->lastKnownReplicaPos = $replicationPos;
                                $status = 0;
                        }
                } elseif ( $status >= 0 ) {
                        // Remember that this position was reached to save queries next time
-                       $this->lastKnownSlavePos = $pos;
+                       $this->lastKnownReplicaPos = $pos;
                }
 
                return $status;
@@ -827,15 +818,23 @@ abstract class DatabaseMysqlBase extends Database {
         * @return MySQLMasterPos|bool
         */
        function getSlavePos() {
-               $res = $this->query( 'SHOW SLAVE STATUS', 'DatabaseBase::getSlavePos' );
+               $res = $this->query( 'SHOW SLAVE STATUS', __METHOD__ );
                $row = $this->fetchObject( $res );
 
                if ( $row ) {
                        $pos = isset( $row->Exec_master_log_pos )
                                ? $row->Exec_master_log_pos
                                : $row->Exec_Master_Log_Pos;
+                       // Also fetch the last-applied GTID set (MariaDB)
+                       if ( $this->useGTIDs ) {
+                               $res = $this->query( "SHOW GLOBAL VARIABLES LIKE 'gtid_slave_pos'", __METHOD__ );
+                               $gtidRow = $this->fetchObject( $res );
+                               $gtidSet = $gtidRow ? $gtidRow->Value : '';
+                       } else {
+                               $gtidSet = '';
+                       }
 
-                       return new MySQLMasterPos( $row->Relay_Master_Log_File, $pos );
+                       return new MySQLMasterPos( $row->Relay_Master_Log_File, $pos, $gtidSet );
                } else {
                        return false;
                }
@@ -847,16 +846,32 @@ abstract class DatabaseMysqlBase extends Database {
         * @return MySQLMasterPos|bool
         */
        function getMasterPos() {
-               $res = $this->query( 'SHOW MASTER STATUS', 'DatabaseBase::getMasterPos' );
+               $res = $this->query( 'SHOW MASTER STATUS', __METHOD__ );
                $row = $this->fetchObject( $res );
 
                if ( $row ) {
-                       return new MySQLMasterPos( $row->File, $row->Position );
+                       // Also fetch the last-written GTID set (MariaDB)
+                       if ( $this->useGTIDs ) {
+                               $res = $this->query( "SHOW GLOBAL VARIABLES LIKE 'gtid_binlog_pos'", __METHOD__ );
+                               $gtidRow = $this->fetchObject( $res );
+                               $gtidSet = $gtidRow ? $gtidRow->Value : '';
+                       } else {
+                               $gtidSet = '';
+                       }
+
+                       return new MySQLMasterPos( $row->File, $row->Position, $gtidSet );
                } else {
                        return false;
                }
        }
 
+       public function serverIsReadOnly() {
+               $res = $this->query( "SHOW GLOBAL VARIABLES LIKE 'read_only'", __METHOD__ );
+               $row = $this->fetchObject( $res );
+
+               return $row ? ( strtolower( $row->Value ) === 'on' ) : false;
+       }
+
        /**
         * @param string $index
         * @return string
@@ -1159,12 +1174,6 @@ abstract class DatabaseMysqlBase extends Database {
                return $this->lastErrno() == 1205;
        }
 
-       /**
-        * Determines if the last query error was something that should be dealt
-        * with by pinging the connection and reissuing the query
-        *
-        * @return bool
-        */
        function wasErrorReissuable() {
                return $this->lastErrno() == 2013 || $this->lastErrno() == 2006;
        }
@@ -1451,20 +1460,43 @@ class MySQLField implements Field {
        }
 }
 
+/**
+ * DBMasterPos class for MySQL/MariaDB
+ *
+ * Note that master positions and sync logic here make some assumptions:
+ *  - Binlog-based usage assumes single-source replication and non-hierarchical replication.
+ *  - GTID-based usage allows getting/syncing with multi-source replication. It is assumed
+ *    that GTID sets are complete (e.g. include all domains on the server).
+ */
 class MySQLMasterPos implements DBMasterPos {
-       /** @var string */
+       /** @var string Binlog file */
        public $file;
-       /** @var int Position */
+       /** @var int Binglog file position */
        public $pos;
+       /** @var string[] GTID list */
+       public $gtids = [];
        /** @var float UNIX timestamp */
        public $asOfTime = 0.0;
 
-       function __construct( $file, $pos ) {
+       /**
+        * @param string $file Binlog file name
+        * @param integer $pos Binlog position
+        * @param string $gtid Comma separated GTID set [optional]
+        */
+       function __construct( $file, $pos, $gtid = '' ) {
                $this->file = $file;
                $this->pos = $pos;
+               $this->gtids = array_map( 'trim', explode( ',', $gtid ) );
                $this->asOfTime = microtime( true );
        }
 
+       /**
+        * @return string <binlog file>/<position>, e.g db1034-bin.000976/843431247
+        */
+       function __toString() {
+               return "{$this->file}/{$this->pos}";
+       }
+
        function asOfTime() {
                return $this->asOfTime;
        }
@@ -1474,10 +1506,29 @@ class MySQLMasterPos implements DBMasterPos {
                        throw new InvalidArgumentException( "Position not an instance of " . __CLASS__ );
                }
 
-               $thisPos = $this->getCoordinates();
-               $thatPos = $pos->getCoordinates();
+               // Prefer GTID comparisons, which work with multi-tier replication
+               $thisPosByDomain = $this->getGtidCoordinates();
+               $thatPosByDomain = $pos->getGtidCoordinates();
+               if ( $thisPosByDomain && $thatPosByDomain ) {
+                       $reached = true;
+                       // Check that this has positions GTE all of those in $pos for all domains in $pos
+                       foreach ( $thatPosByDomain as $domain => $thatPos ) {
+                               $thisPos = isset( $thisPosByDomain[$domain] ) ? $thisPosByDomain[$domain] : -1;
+                               $reached = $reached && ( $thatPos <= $thisPos );
+                       }
+
+                       return $reached;
+               }
+
+               // Fallback to the binlog file comparisons
+               $thisBinPos = $this->getBinlogCoordinates();
+               $thatBinPos = $pos->getBinlogCoordinates();
+               if ( $thisBinPos && $thatBinPos && $thisBinPos['binlog'] === $thatBinPos['binlog'] ) {
+                       return ( $thisBinPos['pos'] >= $thatBinPos['pos'] );
+               }
 
-               return ( $thisPos && $thatPos && $thisPos >= $thatPos );
+               // Comparing totally different binlogs does not make sense
+               return false;
        }
 
        function channelsMatch( DBMasterPos $pos ) {
@@ -1485,36 +1536,56 @@ class MySQLMasterPos implements DBMasterPos {
                        throw new InvalidArgumentException( "Position not an instance of " . __CLASS__ );
                }
 
-               $thisBinlog = $this->getBinlogName();
-               $thatBinlog = $pos->getBinlogName();
+               // Prefer GTID comparisons, which work with multi-tier replication
+               $thisPosDomains = array_keys( $this->getGtidCoordinates() );
+               $thatPosDomains = array_keys( $pos->getGtidCoordinates() );
+               if ( $thisPosDomains && $thatPosDomains ) {
+                       // Check that this has GTIDs for all domains in $pos
+                       return !array_diff( $thatPosDomains, $thisPosDomains );
+               }
 
-               return ( $thisBinlog !== false && $thisBinlog === $thatBinlog );
-       }
+               // Fallback to the binlog file comparisons
+               $thisBinPos = $this->getBinlogCoordinates();
+               $thatBinPos = $pos->getBinlogCoordinates();
 
-       function __toString() {
-               // e.g db1034-bin.000976/843431247
-               return "{$this->file}/{$this->pos}";
+               return ( $thisBinPos && $thatBinPos && $thisBinPos['binlog'] === $thatBinPos['binlog'] );
        }
 
        /**
-        * @return string|bool
+        * @note: this returns false for multi-source replication GTID sets
+        * @see https://mariadb.com/kb/en/mariadb/gtid
+        * @see https://dev.mysql.com/doc/refman/5.6/en/replication-gtids-concepts.html
+        * @return array Map of (domain => integer position) or false
         */
-       protected function getBinlogName() {
-               $m = [];
-               if ( preg_match( '!^(.+)\.(\d+)/(\d+)$!', (string)$this, $m ) ) {
-                       return $m[1];
+       protected function getGtidCoordinates() {
+               $gtidInfos = [];
+               foreach ( $this->gtids as $gtid ) {
+                       $m = [];
+                       // MariaDB style: <domain>-<server id>-<sequence number>
+                       if ( preg_match( '!^(\d+)-\d+-(\d+)$!', $gtid, $m ) ) {
+                               $gtidInfos[(int)$m[1]] = (int)$m[2];
+                       // MySQL style: <UUID domain>:<sequence number>
+                       } elseif ( preg_match( '!^(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}):(\d+)$!', $gtid, $m ) ) {
+                               $gtidInfos[$m[1]] = (int)$m[2];
+                       } else {
+                               $gtidInfos = [];
+                               break; // unrecognized GTID
+                       }
+
                }
 
-               return false;
+               return $gtidInfos;
        }
 
        /**
-        * @return array|bool (int, int)
+        * @see http://dev.mysql.com/doc/refman/5.7/en/show-master-status.html
+        * @see http://dev.mysql.com/doc/refman/5.7/en/show-slave-status.html
+        * @return array|bool (binlog, (integer file number, integer position)) or false
         */
-       protected function getCoordinates() {
+       protected function getBinlogCoordinates() {
                $m = [];
-               if ( preg_match( '!\.(\d+)/(\d+)$!', (string)$this, $m ) ) {
-                       return [ (int)$m[1], (int)$m[2] ];
+               if ( preg_match( '!^(.+)\.(\d+)/(\d+)$!', (string)$this, $m ) ) {
+                       return [ 'binlog' => $m[1], 'pos' => [ (int)$m[2], (int)$m[3] ] ];
                }
 
                return false;
index d45805a..e468601 100644 (file)
@@ -81,9 +81,18 @@ class DatabaseMysqli extends DatabaseMysqlBase {
                        $socket = $hostAndSocket[1];
                }
 
+               $mysqli = mysqli_init();
+
                $connFlags = 0;
                if ( $this->mFlags & DBO_SSL ) {
                        $connFlags |= MYSQLI_CLIENT_SSL;
+                       $mysqli->ssl_set(
+                               $this->sslKeyPath,
+                               $this->sslCertPath,
+                               null,
+                               $this->sslCAPath,
+                               $this->sslCiphers
+                       );
                }
                if ( $this->mFlags & DBO_COMPRESS ) {
                        $connFlags |= MYSQLI_CLIENT_COMPRESS;
@@ -92,7 +101,6 @@ class DatabaseMysqli extends DatabaseMysqlBase {
                        $realServer = 'p:' . $realServer;
                }
 
-               $mysqli = mysqli_init();
                if ( $wgDBmysql5 ) {
                        // Tell the server we're communicating with it in UTF-8.
                        // This may engage various charset conversions.
@@ -309,12 +317,6 @@ class DatabaseMysqli extends DatabaseMysqlBase {
                return $conn->real_escape_string( $s );
        }
 
-       protected function mysqlPing() {
-               $conn = $this->getBindingHandle();
-
-               return $conn->ping();
-       }
-
        /**
         * Give an id for the connection
         *
index c2bf2e7..ebeb3a6 100644 (file)
@@ -732,7 +732,7 @@ class DatabaseOracle extends Database {
                return oci_free_statement( $stmt );
        }
 
-       function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
+       function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
                $insertOptions = [], $selectOptions = []
        ) {
                $destTable = $this->tableName( $destTable );
@@ -1562,6 +1562,15 @@ class DatabaseOracle extends Database {
                return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
        }
 
+       /**
+        * @param string $field Field or column to cast
+        * @return string
+        * @since 1.28
+        */
+       public function buildStringCast( $field ) {
+               return 'CAST ( ' . $field . ' AS VARCHAR2 )';
+       }
+
        public function getSearchEngine() {
                return 'SearchOracle';
        }
index 151b444..22445c0 100644 (file)
@@ -149,7 +149,7 @@ class SavepointPostgres {
                $this->didbegin = false;
                /* If we are not in a transaction, we need to be for savepoint trickery */
                if ( !$dbw->trxLevel() ) {
-                       $dbw->begin( "FOR SAVEPOINT" );
+                       $dbw->begin( "FOR SAVEPOINT", DatabasePostgres::TRANSACTION_INTERNAL );
                        $this->didbegin = true;
                }
        }
@@ -888,7 +888,7 @@ __INDEXATTR__;
 
        /**
         * INSERT SELECT wrapper
-        * $varMap must be an associative array of the form array( 'dest1' => 'source1', ...)
+        * $varMap must be an associative array of the form [ 'dest1' => 'source1', ... ]
         * Source items may be literals rather then field names, but strings should
         * be quoted with Database::addQuotes()
         * $conds may be "*" to copy the whole table
@@ -904,7 +904,7 @@ __INDEXATTR__;
         * @param array $selectOptions
         * @return bool
         */
-       function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
+       function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
                $insertOptions = [], $selectOptions = [] ) {
                $destTable = $this->tableName( $destTable );
 
@@ -1208,7 +1208,7 @@ __INDEXATTR__;
         * @param string $desiredSchema
         */
        function determineCoreSchema( $desiredSchema ) {
-               $this->begin( __METHOD__ );
+               $this->begin( __METHOD__, self::TRANSACTION_INTERNAL );
                if ( $this->schemaExists( $desiredSchema ) ) {
                        if ( in_array( $desiredSchema, $this->getSchemas() ) ) {
                                $this->mCoreSchema = $desiredSchema;
@@ -1536,6 +1536,15 @@ SQL;
                return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
        }
 
+       /**
+        * @param string $field Field or column to cast
+        * @return string
+        * @since 1.28
+        */
+       public function buildStringCast( $field ) {
+               return $field . '::text';
+       }
+
        public function getSearchEngine() {
                return 'SearchPostgres';
        }
@@ -1580,21 +1589,21 @@ SQL;
         */
        public function lock( $lockName, $method, $timeout = 5 ) {
                $key = $this->addQuotes( $this->bigintFromLockName( $lockName ) );
-               for ( $attempts = 1; $attempts <= $timeout; ++$attempts ) {
-                       $result = $this->query(
-                               "SELECT pg_try_advisory_lock($key) AS lockstatus", $method );
-                       $row = $this->fetchObject( $result );
-                       if ( $row->lockstatus === 't' ) {
-                               parent::lock( $lockName, $method, $timeout ); // record
-                               return true;
-                       } else {
-                               sleep( 1 );
-                       }
-               }
+               $loop = new WaitConditionLoop(
+                       function () use ( $lockName, $key, $timeout, $method ) {
+                               $res = $this->query( "SELECT pg_try_advisory_lock($key) AS lockstatus", $method );
+                               $row = $this->fetchObject( $res );
+                               if ( $row->lockstatus === 't' ) {
+                                       parent::lock( $lockName, $method, $timeout ); // record
+                                       return true;
+                               }
 
-               wfDebug( __METHOD__ . " failed to acquire lock\n" );
+                               return WaitConditionLoop::CONDITION_CONTINUE;
+                       },
+                       $timeout
+               );
 
-               return false;
+               return ( $loop->invoke() === $loop::CONDITION_REACHED );
        }
 
        /**
index 9d0a0f7..e6401b3 100644 (file)
@@ -832,6 +832,15 @@ class DatabaseSqlite extends Database {
                return parent::buildLike( $params ) . "ESCAPE '\' ";
        }
 
+       /**
+        * @param string $field Field or column to cast
+        * @return string
+        * @since 1.28
+        */
+       public function buildStringCast( $field ) {
+               return 'CAST ( ' . $field . ' AS TEXT )';
+       }
+
        /**
         * @return string
         */
@@ -911,7 +920,7 @@ class DatabaseSqlite extends Database {
        public function lock( $lockName, $method, $timeout = 5 ) {
                if ( !is_dir( "{$this->dbDir}/locks" ) ) { // create dir as needed
                        if ( !is_writable( $this->dbDir ) || !mkdir( "{$this->dbDir}/locks" ) ) {
-                               throw new DBError( "Cannot create directory \"{$this->dbDir}/locks\"." );
+                               throw new DBError( $this, "Cannot create directory \"{$this->dbDir}/locks\"." );
                        }
                }
 
index b6c37ee..aeaa27f 100644 (file)
@@ -314,7 +314,7 @@ class LikeMatch {
 }
 
 /**
- * An object representing a master or slave position in a replicated setup.
+ * An object representing a master or replica DB position in a replicated setup.
  *
  * The implementation details of this opaque type are up to the database subclass.
  */
index 0a71df2..f312357 100644 (file)
  * @ingroup Database
  */
 interface IDatabase {
+       /** @var int Callback triggered immediately due to no active transaction */
+       const TRIGGER_IDLE = 1;
+       /** @var int Callback triggered by COMMIT */
+       const TRIGGER_COMMIT = 2;
+       /** @var int Callback triggered by ROLLBACK */
+       const TRIGGER_ROLLBACK = 3;
+
+       /** @var string Transaction is requested by regular caller outside of the DB layer */
+       const TRANSACTION_EXPLICIT = '';
+       /** @var string Transaction is requested internally via DBO_TRX/startAtomic() */
+       const TRANSACTION_INTERNAL = 'implicit';
+
+       /** @var string Transaction operation comes from service managing all DBs */
+       const FLUSHING_ALL_PEERS = 'flush';
+       /** @var string Transaction operation comes from the database class internally */
+       const FLUSHING_INTERNAL = 'flush';
+
+       /** @var string Do not remember the prior flags */
+       const REMEMBER_NOTHING = '';
+       /** @var string Remember the prior flags */
+       const REMEMBER_PRIOR = 'remember';
+       /** @var string Restore to the prior flag state */
+       const RESTORE_PRIOR = 'prior';
+       /** @var string Restore to the initial flag state */
+       const RESTORE_INITIAL = 'initial';
+
+       /** @var string Estimate total time (RTT, scanning, waiting on locks, applying) */
+       const ESTIMATE_TOTAL = 'total';
+       /** @var string Estimate time to apply (scanning, applying) */
+       const ESTIMATE_DB_APPLY = 'apply';
+
        /**
         * A string describing the current software version, and possibly
         * other details in a user-friendly way. Will be listed on Special:Version, etc.
@@ -86,6 +117,12 @@ interface IDatabase {
         */
        public function trxTimestamp();
 
+       /**
+        * @return bool Whether an explicit transaction or atomic sections are still open
+        * @since 1.28
+        */
+       public function explicitTrxActive();
+
        /**
         * Get/set the table prefix.
         * @param string $prefix The table prefix to set, or omitted to leave it unchanged.
@@ -168,6 +205,7 @@ interface IDatabase {
        /**
         * Returns true if there is a transaction open with possible write
         * queries or transaction pre-commit/idle callbacks waiting on it to finish.
+        * This does *not* count recurring callbacks, e.g. from setTransactionListener().
         *
         * @return bool
         */
@@ -178,10 +216,11 @@ interface IDatabase {
         *
         * High times could be due to scanning, updates, locking, and such
         *
+        * @param string $type IDatabase::ESTIMATE_* constant [default: ESTIMATE_ALL]
         * @return float|bool Returns false if not transaction is active
         * @since 1.26
         */
-       public function pendingWriteQueryDuration();
+       public function pendingWriteQueryDuration( $type = self::ESTIMATE_TOTAL );
 
        /**
         * Get the list of method names that did write queries for this transaction
@@ -207,8 +246,9 @@ interface IDatabase {
         *   - DBO_DEFAULT: automatically sets DBO_TRX if not in command line mode
         *       and removes it in command line mode
         *   - DBO_PERSISTENT: use persistant database connection
+        * @param string $remember IDatabase::REMEMBER_* constant [default: REMEMBER_NOTHING]
         */
-       public function setFlag( $flag );
+       public function setFlag( $flag, $remember = self::REMEMBER_NOTHING );
 
        /**
         * Clear a flag for this connection
@@ -220,8 +260,17 @@ interface IDatabase {
         *   - DBO_DEFAULT: automatically sets DBO_TRX if not in command line mode
         *       and removes it in command line mode
         *   - DBO_PERSISTENT: use persistant database connection
+        * @param string $remember IDatabase::REMEMBER_* constant [default: REMEMBER_NOTHING]
+        */
+       public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING );
+
+       /**
+        * Restore the flags to their prior state before the last setFlag/clearFlag call
+        *
+        * @param string $state IDatabase::RESTORE_* constant. [default: RESTORE_PRIOR]
+        * @since 1.28
         */
-       public function clearFlag( $flag );
+       public function restoreFlags( $state = self::RESTORE_PRIOR );
 
        /**
         * Returns a boolean whether the flag $flag is set for this connection
@@ -324,7 +373,7 @@ interface IDatabase {
         *
         * Example:
         * $id = $dbw->nextSequenceValue( 'page_page_id_seq' );
-        * $dbw->insert( 'page', array( 'page_id' => $id ) );
+        * $dbw->insert( 'page', [ 'page_id' => $id ] );
         * $id = $dbw->insertId();
         *
         * @return int
@@ -514,7 +563,7 @@ interface IDatabase {
         * May be either an array of table names, or a single string holding a table
         * name. If an array is given, table aliases can be specified, for example:
         *
-        *    array( 'a' => 'user' )
+        *    [ 'a' => 'user' ]
         *
         * This includes the user table in the query, with the alias "a" available
         * for use in field names (e.g. a.user_name).
@@ -532,7 +581,7 @@ interface IDatabase {
         * can be complete fragments of SQL, for direct inclusion into the SELECT
         * query. If an array is given, field aliases can be specified, for example:
         *
-        *   array( 'maxrev' => 'MAX(rev_id)' )
+        *   [ 'maxrev' => 'MAX(rev_id)' ]
         *
         * This includes an expression with the alias "maxrev" in the query.
         *
@@ -577,7 +626,7 @@ interface IDatabase {
         * including them in the array as a string value with a numeric key, for
         * example:
         *
-        *    array( 'FOR UPDATE' )
+        *    [ 'FOR UPDATE' ]
         *
         * The supported options are:
         *
@@ -639,7 +688,7 @@ interface IDatabase {
         * an SQL fragment, or an array where the string keys are equality and the
         * numeric keys are SQL fragments all AND'd together. For example:
         *
-        *    array( 'page' => array( 'LEFT JOIN', 'page_latest=rev_id' ) )
+        *    [ 'page' => [ 'LEFT JOIN', 'page_latest=rev_id' ] ]
         *
         * @return ResultWrapper|bool If the query returned no rows, a ResultWrapper
         *   with no rows in it will be returned. If there was a query error, a
@@ -852,7 +901,7 @@ interface IDatabase {
         * The keys on each level may be either integers or strings.
         *
         * @param array $data Organized as 2-d
-        *    array(baseKeyVal => array(subKeyVal => [ignored], ...), ...)
+        *    [ baseKeyVal => [ subKeyVal => [ignored], ... ], ... ]
         * @param string $baseKey Field name to match the base-level keys to (eg 'pl_namespace')
         * @param string $subKey Field name to match the sub-level keys to (eg 'pl_title')
         * @return string|bool SQL fragment, or false if no items in array
@@ -945,7 +994,7 @@ interface IDatabase {
         * Example: $dbr->buildLike( 'My_page_title/', $dbr->anyString() ) returns
         * a LIKE clause that searches for subpages of 'My page title'.
         * Alternatively:
-        *   $pattern = array( 'My_page_title/', $dbr->anyString() );
+        *   $pattern = [ 'My_page_title/', $dbr->anyString() ];
         *   $query .= $dbr->buildLike( $pattern );
         *
         * @since 1.16
@@ -1087,7 +1136,7 @@ interface IDatabase {
         *    to include in a join.
         *
         * @param array $varMap Must be an associative array of the form
-        *    array( 'dest1' => 'source1', ...). Source items may be literals
+        *    [ 'dest1' => 'source1', ... ]. Source items may be literals
         *    rather than field names, but strings should be quoted with
         *    IDatabase::addQuotes()
         *
@@ -1151,7 +1200,6 @@ interface IDatabase {
 
        /**
         * Determines how long the server has been up
-        * STUB
         *
         * @return int
         */
@@ -1159,7 +1207,6 @@ interface IDatabase {
 
        /**
         * Determines if the last failure was due to a deadlock
-        * STUB
         *
         * @return bool
         */
@@ -1167,16 +1214,14 @@ interface IDatabase {
 
        /**
         * Determines if the last failure was due to a lock timeout
-        * STUB
         *
         * @return bool
         */
        public function wasLockTimeout();
 
        /**
-        * Determines if the last query error was something that should be dealt
-        * with by pinging the connection and reissuing the query.
-        * STUB
+        * Determines if the last query error was due to a dropped connection and should
+        * be dealt with by pinging the connection and reissuing the query.
         *
         * @return bool
         */
@@ -1184,27 +1229,26 @@ interface IDatabase {
 
        /**
         * Determines if the last failure was due to the database being read-only.
-        * STUB
         *
         * @return bool
         */
        public function wasReadOnlyError();
 
        /**
-        * Wait for the slave to catch up to a given master position
+        * Wait for the replica DB to catch up to a given master position
         *
         * @param DBMasterPos $pos
         * @param int $timeout The maximum number of seconds to wait for synchronisation
-        * @return int|null Zero if the slave was past that position already,
+        * @return int|null Zero if the replica DB was past that position already,
         *   greater than zero if we waited for some period of time, less than
         *   zero if it timed out, and null on error
         */
        public function masterPosWait( DBMasterPos $pos, $timeout );
 
        /**
-        * Get the replication position of this slave
+        * Get the replication position of this replica DB
         *
-        * @return DBMasterPos|bool False if this is not a slave.
+        * @return DBMasterPos|bool False if this is not a replica DB.
         */
        public function getSlavePos();
 
@@ -1216,7 +1260,30 @@ interface IDatabase {
        public function getMasterPos();
 
        /**
-        * Run an anonymous function as soon as there is no transaction pending.
+        * @return bool Whether the DB is marked as read-only server-side
+        * @since 1.28
+        */
+       public function serverIsReadOnly();
+
+       /**
+        * Run a callback as soon as the current transaction commits or rolls back.
+        * An error is thrown if no transaction is pending. Queries in the function will run in
+        * AUTO-COMMIT mode unless there are begin() calls. Callbacks must commit any transactions
+        * that they begin.
+        *
+        * This is useful for combining cooperative locks and DB transactions.
+        *
+        * The callback takes one argument:
+        *   - How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_ROLLBACK)
+        *
+        * @param callable $callback
+        * @return mixed
+        * @since 1.28
+        */
+       public function onTransactionResolution( callable $callback );
+
+       /**
+        * Run a callback as soon as there is no transaction pending.
         * If there is a transaction and it is rolled back, then the callback is cancelled.
         * Queries in the function will run in AUTO-COMMIT mode unless there are begin() calls.
         * Callbacks must commit any transactions that they begin.
@@ -1228,15 +1295,19 @@ interface IDatabase {
         *
         * Updates will execute in the order they were enqueued.
         *
+        * The callback takes one argument:
+        *   - How the transaction ended (IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_IDLE)
+        *
         * @param callable $callback
         * @since 1.20
         */
-       public function onTransactionIdle( $callback );
+       public function onTransactionIdle( callable $callback );
 
        /**
-        * Run an anonymous function before the current transaction commits or now if there is none.
+        * Run a callback before the current transaction commits or now if there is none.
         * If there is a transaction and it is rolled back, then the callback is cancelled.
-        * Callbacks must not start nor commit any transactions.
+        * Callbacks must not start nor commit any transactions. If no transaction is active,
+        * then a transaction will wrap the callback.
         *
         * This is useful for updates that easily cause deadlocks if locks are held too long
         * but where atomicity is strongly desired for these updates and some related updates.
@@ -1246,7 +1317,24 @@ interface IDatabase {
         * @param callable $callback
         * @since 1.22
         */
-       public function onTransactionPreCommitOrIdle( $callback );
+       public function onTransactionPreCommitOrIdle( callable $callback );
+
+       /**
+        * Run a callback each time any transaction commits or rolls back
+        *
+        * The callback takes two arguments:
+        *   - IDatabase::TRIGGER_COMMIT or IDatabase::TRIGGER_ROLLBACK
+        *   - This IDatabase object
+        * Callbacks must commit any transactions that they begin.
+        *
+        * Registering a callback here will not affect writesOrCallbacks() pending
+        *
+        * @param string $name Callback name
+        * @param callable|null $callback Use null to unset a listener
+        * @return mixed
+        * @since 1.28
+        */
+       public function setTransactionListener( $name, callable $callback = null );
 
        /**
         * Begin an atomic section of statements
@@ -1308,6 +1396,7 @@ interface IDatabase {
         *
         * @param string $fname Caller name (usually __METHOD__)
         * @param callable $callback Callback that issues DB updates
+        * @return mixed $res Result of the callback (since 1.28)
         * @throws DBError
         * @throws RuntimeException
         * @throws UnexpectedValueException
@@ -1319,6 +1408,10 @@ interface IDatabase {
         * Begin a transaction. If a transaction is already in progress,
         * that transaction will be committed before the new transaction is started.
         *
+        * Only call this from code with outer transcation scope.
+        * See https://www.mediawiki.org/wiki/Database_transactions for details.
+        * Nesting of transactions is not supported.
+        *
         * Note that when the DBO_TRX flag is set (which is usually the case for web
         * requests, but not for maintenance scripts), any previous database query
         * will have started a transaction automatically.
@@ -1327,21 +1420,24 @@ interface IDatabase {
         * will cause a warning, unless the current transaction was started
         * automatically because of the DBO_TRX flag.
         *
-        * @param string $fname
+        * @param string $fname Calling function name
+        * @param string $mode A situationally valid IDatabase::TRANSACTION_* constant [optional]
         * @throws DBError
         */
-       public function begin( $fname = __METHOD__ );
+       public function begin( $fname = __METHOD__, $mode = self::TRANSACTION_EXPLICIT );
 
        /**
         * Commits a transaction previously started using begin().
         * If no transaction is in progress, a warning is issued.
         *
+        * Only call this from code with outer transcation scope.
+        * See https://www.mediawiki.org/wiki/Database_transactions for details.
         * Nesting of transactions is not supported.
         *
         * @param string $fname
-        * @param string $flush Flush flag, set to 'flush' to disable warnings about
-        *   explicitly committing implicit transactions, or calling commit when no
-        *   transaction is in progress.
+        * @param string $flush Flush flag, set to situationally valid IDatabase::FLUSHING_*
+        *   constant to disable warnings about explicitly committing implicit transactions,
+        *   or calling commit when no transaction is in progress.
         *
         *   This will trigger an exception if there is an ongoing explicit transaction.
         *
@@ -1356,18 +1452,36 @@ interface IDatabase {
         * Rollback a transaction previously started using begin().
         * If no transaction is in progress, a warning is issued.
         *
-        * No-op on non-transactional databases.
+        * Only call this from code with outer transcation scope.
+        * See https://www.mediawiki.org/wiki/Database_transactions for details.
+        * Nesting of transactions is not supported. If a serious unexpected error occurs,
+        * throwing an Exception is preferrable, using a pre-installed error handler to trigger
+        * rollback (in any case, failure to issue COMMIT will cause rollback server-side).
         *
-        * @param string $fname
-        * @param string $flush Flush flag, set to 'flush' to disable warnings about
-        *   calling rollback when no transaction is in progress. This will silently
-        *   break any ongoing explicit transaction. Only set the flush flag if you
-        *   are sure that it is safe to ignore these warnings in your context.
+        * @param string $fname Calling function name
+        * @param string $flush Flush flag, set to a situationally valid IDatabase::FLUSHING_*
+        *   constant to disable warnings about calling rollback when no transaction is in
+        *   progress. This will silently break any ongoing explicit transaction. Only set the
+        *   flush flag if you are sure that it is safe to ignore these warnings in your context.
         * @throws DBUnexpectedError
         * @since 1.23 Added $flush parameter
         */
        public function rollback( $fname = __METHOD__, $flush = '' );
 
+       /**
+        * Commit any transaction but error out if writes or callbacks are pending
+        *
+        * This is intended for clearing out REPEATABLE-READ snapshots so that callers can
+        * see a new point-in-time of the database. This is useful when one of many transaction
+        * rounds finished and significant time will pass in the script's lifetime. It is also
+        * useful to call on a replica DB after waiting on replication to catch up to the master.
+        *
+        * @param string $fname Calling function name
+        * @throws DBUnexpectedError
+        * @since 1.28
+        */
+       public function flushSnapshot( $fname = __METHOD__ );
+
        /**
         * List all tables on the database
         *
@@ -1409,12 +1523,13 @@ interface IDatabase {
        /**
         * Ping the server and try to reconnect if it there is no connection
         *
+        * @param float|null &$rtt Value to store the estimated RTT [optional]
         * @return bool Success or failure
         */
-       public function ping();
+       public function ping( &$rtt = null );
 
        /**
-        * Get slave lag. Currently supported only by MySQL.
+        * Get replica DB lag. Currently supported only by MySQL.
         *
         * Note that this function will generate a fatal error on many
         * installations. Most callers should use LoadBalancer::safeGetLag()
@@ -1425,7 +1540,7 @@ interface IDatabase {
        public function getLag();
 
        /**
-        * Get the slave lag when the current transaction started
+        * Get the replica DB lag when the current transaction started
         * or a general lag estimate if not transaction is active
         *
         * This is useful when transactions might use snapshot isolation
@@ -1527,10 +1642,14 @@ interface IDatabase {
        /**
         * Acquire a named lock, flush any transaction, and return an RAII style unlocker object
         *
+        * Only call this from outer transcation scope and when only one DB will be affected.
+        * See https://www.mediawiki.org/wiki/Database_transactions for details.
+        *
         * This is suitiable for transactions that need to be serialized using cooperative locks,
         * where each transaction can see each others' changes. Any transaction is flushed to clear
         * out stale REPEATABLE-READ snapshot data. Once the returned object falls out of PHP scope,
-        * any transaction will be committed and the lock will be released.
+        * the lock will be released unless a transaction is active. If one is active, then the lock
+        * will be released when it either commits or rolls back.
         *
         * If the lock acquisition failed, then no transaction flush happens, and null is returned.
         *
index 5b048b5..cd8dff3 100644 (file)
@@ -31,33 +31,54 @@ use MediaWiki\Logger\LoggerFactory;
  * @ingroup Database
  */
 abstract class LBFactory implements DestructibleService {
-
        /** @var ChronologyProtector */
        protected $chronProt;
-
        /** @var TransactionProfiler */
        protected $trxProfiler;
-
        /** @var LoggerInterface */
-       protected $logger;
-
+       protected $trxLogger;
+       /** @var BagOStuff */
+       protected $srvCache;
+       /** @var WANObjectCache */
+       protected $wanCache;
+
+       /** @var mixed */
+       protected $ticket;
+       /** @var string|bool String if a requested DBO_TRX transaction round is active */
+       protected $trxRoundId = false;
        /** @var string|bool Reason all LBs are read-only or false if not */
        protected $readOnlyReason = false;
+       /** @var callable[] */
+       protected $replicationWaitCallbacks = [];
 
        const SHUTDOWN_NO_CHRONPROT = 1; // don't save ChronologyProtector positions (for async code)
 
        /**
         * Construct a factory based on a configuration array (typically from $wgLBFactoryConf)
         * @param array $conf
+        * @TODO: inject objects via dependency framework
         */
        public function __construct( array $conf ) {
                if ( isset( $conf['readOnlyReason'] ) && is_string( $conf['readOnlyReason'] ) ) {
                        $this->readOnlyReason = $conf['readOnlyReason'];
                }
-
                $this->chronProt = $this->newChronologyProtector();
                $this->trxProfiler = Profiler::instance()->getTransactionProfiler();
-               $this->logger = LoggerFactory::getInstance( 'DBTransaction' );
+               // Use APC/memcached style caching, but avoids loops with CACHE_DB (T141804)
+               $cache = ObjectCache::getLocalServerInstance();
+               if ( $cache->getQoS( $cache::ATTR_EMULATION ) > $cache::QOS_EMULATION_SQL ) {
+                       $this->srvCache = $cache;
+               } else {
+                       $this->srvCache = new EmptyBagOStuff();
+               }
+               $wCache = ObjectCache::getMainWANInstance();
+               if ( $wCache->getQoS( $wCache::ATTR_EMULATION ) > $wCache::QOS_EMULATION_SQL ) {
+                       $this->wanCache = $wCache;
+               } else {
+                       $this->wanCache = WANObjectCache::newEmpty();
+               }
+               $this->trxLogger = LoggerFactory::getInstance( 'DBTransaction' );
+               $this->ticket = mt_rand();
        }
 
        /**
@@ -164,7 +185,7 @@ abstract class LBFactory implements DestructibleService {
         * @param bool|string $wiki Wiki ID, or false for the current wiki
         * @return LoadBalancer
         */
-       abstract public function &getExternalLB( $cluster, $wiki = false );
+       abstract public function getExternalLB( $cluster, $wiki = false );
 
        /**
         * Execute a function for each tracked load balancer
@@ -179,9 +200,12 @@ abstract class LBFactory implements DestructibleService {
        /**
         * Prepare all tracked load balancers for shutdown
         * @param integer $flags Supports SHUTDOWN_* flags
-        * STUB
         */
        public function shutdown( $flags = 0 ) {
+               if ( !( $flags & self::SHUTDOWN_NO_CHRONPROT ) ) {
+                       $this->shutdownChronologyProtector( $this->chronProt );
+               }
+               $this->commitMasterChanges( __METHOD__ ); // sanity
        }
 
        /**
@@ -199,20 +223,53 @@ abstract class LBFactory implements DestructibleService {
                );
        }
 
+       /**
+        * Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot
+        *
+        * @param string $fname Caller name
+        * @since 1.28
+        */
+       public function flushReplicaSnapshots( $fname = __METHOD__ ) {
+               $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
+       }
+
        /**
         * Commit on all connections. Done for two reasons:
         * 1. To commit changes to the masters.
-        * 2. To release the snapshot on all connections, master and slave.
+        * 2. To release the snapshot on all connections, master and replica DB.
         * @param string $fname Caller name
+        * @param array $options Options map:
+        *   - maxWriteDuration: abort if more than this much time was spent in write queries
         */
-       public function commitAll( $fname = __METHOD__ ) {
-               $this->logMultiDbTransaction();
-
-               $start = microtime( true );
+       public function commitAll( $fname = __METHOD__, array $options = [] ) {
+               $this->commitMasterChanges( $fname, $options );
                $this->forEachLBCallMethod( 'commitAll', [ $fname ] );
-               $timeMs = 1000 * ( microtime( true ) - $start );
+       }
 
-               RequestContext::getMain()->getStats()->timing( "db.commit-all", $timeMs );
+       /**
+        * Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
+        *
+        * The DBO_TRX setting will be reverted to the default in each of these methods:
+        *   - commitMasterChanges()
+        *   - rollbackMasterChanges()
+        *   - commitAll()
+        *
+        * This allows for custom transaction rounds from any outer transaction scope.
+        *
+        * @param string $fname
+        * @throws DBTransactionError
+        * @since 1.28
+        */
+       public function beginMasterChanges( $fname = __METHOD__ ) {
+               if ( $this->trxRoundId !== false ) {
+                       throw new DBTransactionError(
+                               null,
+                               "$fname: transaction round '{$this->trxRoundId}' already started."
+                       );
+               }
+               $this->trxRoundId = $fname;
+               // Set DBO_TRX flags on all appropriate DBs
+               $this->forEachLBCallMethod( 'beginMasterChanges', [ $fname ] );
        }
 
        /**
@@ -220,24 +277,37 @@ abstract class LBFactory implements DestructibleService {
         * @param string $fname Caller name
         * @param array $options Options map:
         *   - maxWriteDuration: abort if more than this much time was spent in write queries
+        * @throws Exception
         */
        public function commitMasterChanges( $fname = __METHOD__, array $options = [] ) {
-               $limit = isset( $options['maxWriteDuration'] ) ? $options['maxWriteDuration'] : 0;
-
-               $this->logMultiDbTransaction();
-               $this->forEachLB( function ( LoadBalancer $lb ) use ( $limit ) {
-                       $lb->forEachOpenConnection( function ( IDatabase $db ) use ( $limit ) {
-                               $time = $db->pendingWriteQueryDuration();
-                               if ( $limit > 0 && $time > $limit ) {
-                                       throw new DBTransactionError(
-                                               $db,
-                                               wfMessage( 'transaction-duration-limit-exceeded', $time, $limit )->text()
-                                       );
-                               }
-                       } );
+               if ( $this->trxRoundId !== false && $this->trxRoundId !== $fname ) {
+                       throw new DBTransactionError(
+                               null,
+                               "$fname: transaction round '{$this->trxRoundId}' still running."
+                       );
+               }
+               // Run pre-commit callbacks and suppress post-commit callbacks, aborting on failure
+               $this->forEachLBCallMethod( 'finalizeMasterChanges' );
+               $this->trxRoundId = false;
+               // Perform pre-commit checks, aborting on failure
+               $this->forEachLBCallMethod( 'approveMasterChanges', [ $options ] );
+               // Log the DBs and methods involved in multi-DB transactions
+               $this->logIfMultiDbTransaction();
+               // Actually perform the commit on all master DB connections and revert DBO_TRX
+               $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname ] );
+               // Run all post-commit callbacks
+               /** @var Exception $e */
+               $e = null; // first callback exception
+               $this->forEachLB( function ( LoadBalancer $lb ) use ( &$e ) {
+                       $ex = $lb->runMasterPostTrxCallbacks( IDatabase::TRIGGER_COMMIT );
+                       $e = $e ?: $ex;
                } );
-
+               // Commit any dangling DBO_TRX transactions from callbacks on one DB to another DB
                $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname ] );
+               // Throw any last post-commit callback error
+               if ( $e instanceof Exception ) {
+                       throw $e;
+               }
        }
 
        /**
@@ -246,13 +316,19 @@ abstract class LBFactory implements DestructibleService {
         * @since 1.23
         */
        public function rollbackMasterChanges( $fname = __METHOD__ ) {
+               $this->trxRoundId = false;
+               $this->forEachLBCallMethod( 'suppressTransactionEndCallbacks' );
                $this->forEachLBCallMethod( 'rollbackMasterChanges', [ $fname ] );
+               // Run all post-rollback callbacks
+               $this->forEachLB( function ( LoadBalancer $lb ) {
+                       $lb->runMasterPostTrxCallbacks( IDatabase::TRIGGER_ROLLBACK );
+               } );
        }
 
        /**
         * Log query info if multi DB transactions are going to be committed now
         */
-       private function logMultiDbTransaction() {
+       private function logIfMultiDbTransaction() {
                $callersByDB = [];
                $this->forEachLB( function ( LoadBalancer $lb ) use ( &$callersByDB ) {
                        $masterName = $lb->getServerName( $lb->getWriterIndex() );
@@ -268,7 +344,7 @@ abstract class LBFactory implements DestructibleService {
                        foreach ( $callersByDB as $db => $callers ) {
                                $msg .= "$db: " . implode( '; ', $callers ) . "\n";
                        }
-                       $this->logger->info( $msg );
+                       $this->trxLogger->info( $msg );
                }
        }
 
@@ -287,19 +363,28 @@ abstract class LBFactory implements DestructibleService {
        }
 
        /**
-        * Detemine if any lagged slave connection was used
-        * @since 1.27
+        * Detemine if any lagged replica DB connection was used
         * @return bool
+        * @since 1.28
         */
-       public function laggedSlaveUsed() {
+       public function laggedReplicaUsed() {
                $ret = false;
                $this->forEachLB( function ( LoadBalancer $lb ) use ( &$ret ) {
-                       $ret = $ret || $lb->laggedSlaveUsed();
+                       $ret = $ret || $lb->laggedReplicaUsed();
                } );
 
                return $ret;
        }
 
+       /**
+        * @return bool
+        * @since 1.27
+        * @deprecated Since 1.28; use laggedReplicaUsed()
+        */
+       public function laggedSlaveUsed() {
+               return $this->laggedReplicaUsed();
+       }
+
        /**
         * Determine if any master connection has pending/written changes from this request
         * @return bool
@@ -314,10 +399,10 @@ abstract class LBFactory implements DestructibleService {
        }
 
        /**
-        * Waits for the slave DBs to catch up to the current master position
+        * Waits for the replica DBs to catch up to the current master position
         *
         * Use this when updating very large numbers of rows, as in maintenance scripts,
-        * to avoid causing too much lag. Of course, this is a no-op if there are no slaves.
+        * to avoid causing too much lag. Of course, this is a no-op if there are no replica DBs.
         *
         * By default this waits on all DB clusters actually used in this request.
         * This makes sense when lag being waiting on is caused by the code that does this check.
@@ -345,6 +430,10 @@ abstract class LBFactory implements DestructibleService {
                        'ifWritesSince' => null
                ];
 
+               foreach ( $this->replicationWaitCallbacks as $callback ) {
+                       $callback();
+               }
+
                // Figure out which clusters need to be checked
                /** @var LoadBalancer[] $lbs */
                $lbs = [];
@@ -367,7 +456,7 @@ abstract class LBFactory implements DestructibleService {
                $masterPositions = array_fill( 0, count( $lbs ), false );
                foreach ( $lbs as $i => $lb ) {
                        if ( $lb->getServerCount() <= 1 ) {
-                               // Bug 27975 - Don't try to wait for slaves if there are none
+                               // Bug 27975 - Don't try to wait for replica DBs if there are none
                                // Prevents permission error when getting master position
                                continue;
                        } elseif ( $opts['ifWritesSince']
@@ -391,12 +480,90 @@ abstract class LBFactory implements DestructibleService {
 
                if ( $failed ) {
                        throw new DBReplicationWaitError(
-                               "Could not wait for slaves to catch up to " .
+                               "Could not wait for replica DBs to catch up to " .
                                implode( ', ', $failed )
                        );
                }
        }
 
+       /**
+        * Add a callback to be run in every call to waitForReplication() before waiting
+        *
+        * Callbacks must clear any transactions that they start
+        *
+        * @param string $name Callback name
+        * @param callable|null $callback Use null to unset a callback
+        * @since 1.28
+        */
+       public function setWaitForReplicationListener( $name, callable $callback = null ) {
+               if ( $callback ) {
+                       $this->replicationWaitCallbacks[$name] = $callback;
+               } else {
+                       unset( $this->replicationWaitCallbacks[$name] );
+               }
+       }
+
+       /**
+        * Get a token asserting that no transaction writes are active
+        *
+        * @param string $fname Caller name (e.g. __METHOD__)
+        * @return mixed A value to pass to commitAndWaitForReplication()
+        * @since 1.28
+        */
+       public function getEmptyTransactionTicket( $fname ) {
+               if ( $this->hasMasterChanges() ) {
+                       $this->trxLogger->error( __METHOD__ . ": $fname does not have outer scope." );
+                       return null;
+               }
+
+               return $this->ticket;
+       }
+
+       /**
+        * Convenience method for safely running commitMasterChanges()/waitForReplication()
+        *
+        * This will commit and wait unless $ticket indicates it is unsafe to do so
+        *
+        * @param string $fname Caller name (e.g. __METHOD__)
+        * @param mixed $ticket Result of getEmptyTransactionTicket()
+        * @param array $opts Options to waitForReplication()
+        * @throws DBReplicationWaitError
+        * @since 1.28
+        */
+       public function commitAndWaitForReplication( $fname, $ticket, array $opts = [] ) {
+               if ( $ticket !== $this->ticket ) {
+                       $logger = LoggerFactory::getInstance( 'DBPerformance' );
+                       $logger->error( __METHOD__ . ": cannot commit; $fname does not have outer scope." );
+                       return;
+               }
+
+               // The transaction owner and any caller with the empty transaction ticket can commit
+               // so that getEmptyTransactionTicket() callers don't risk seeing DBTransactionError.
+               if ( $this->trxRoundId !== false && $fname !== $this->trxRoundId ) {
+                       $this->trxLogger->info( "$fname: committing on behalf of {$this->trxRoundId}." );
+                       $fnameEffective = $this->trxRoundId;
+               } else {
+                       $fnameEffective = $fname;
+               }
+
+               $this->commitMasterChanges( $fnameEffective );
+               $this->waitForReplication( $opts );
+               // If a nested caller committed on behalf of $fname, start another empty $fname
+               // transaction, leaving the caller with the same empty transaction state as before.
+               if ( $fnameEffective !== $fname ) {
+                       $this->beginMasterChanges( $fnameEffective );
+               }
+       }
+
+       /**
+        * @param string $dbName DB master name (e.g. "db1052")
+        * @return float|bool UNIX timestamp when client last touched the DB or false if not recent
+        * @since 1.28
+        */
+       public function getChronologyProtectorTouched( $dbName ) {
+               return $this->chronProt->getTouched( $dbName );
+       }
+
        /**
         * Disable the ChronologyProtector for all load balancers
         *
@@ -442,7 +609,7 @@ abstract class LBFactory implements DestructibleService {
                // Write them to the stash
                $unsavedPositions = $cp->shutdown();
                // If the positions failed to write to the stash, at least wait on local datacenter
-               // slaves to catch up before responding. Even if there are several DCs, this increases
+               // replica DBs to catch up before responding. Even if there are several DCs, this increases
                // the chance that the user will see their own changes immediately afterwards. As long
                // as the sticky DC cookie applies (same domain), this is not even an issue.
                $this->forEachLB( function ( LoadBalancer $lb ) use ( $unsavedPositions ) {
@@ -453,6 +620,15 @@ abstract class LBFactory implements DestructibleService {
                } );
        }
 
+       /**
+        * @param LoadBalancer $lb
+        */
+       protected function initLoadBalancer( LoadBalancer $lb ) {
+               if ( $this->trxRoundId !== false ) {
+                       $lb->beginMasterChanges( $this->trxRoundId ); // set DBO_TRX
+               }
+       }
+
        /**
         * Close all open database connections on all open load balancers.
         * @since 1.28
@@ -462,19 +638,3 @@ abstract class LBFactory implements DestructibleService {
        }
 
 }
-
-/**
- * Exception class for attempted DB access
- */
-class DBAccessError extends MWException {
-       public function __construct() {
-               parent::__construct( "Mediawiki tried to access the database via wfGetDB(). " .
-                       "This is not allowed, because database access has been disabled." );
-       }
-}
-
-/**
- * Exception class for replica DB wait timeouts
- */
-class DBReplicationWaitError extends Exception {
-}
index 33ee250..5cd1d4b 100644 (file)
@@ -40,7 +40,7 @@ class LBFactoryFake extends LBFactory {
                throw new DBAccessError;
        }
 
-       public function &getExternalLB( $cluster, $wiki = false ) {
+       public function getExternalLB( $cluster, $wiki = false ) {
                throw new DBAccessError;
        }
 
index 3a543ac..e860840 100644 (file)
  *
  *     sectionLoads                A 2-d map. For each section, gives a map of server names to
  *                                 load ratios. For example:
- *                                 array(
- *                                     'section1' => array(
+ *                                 [
+ *                                     'section1' => [
  *                                         'db1' => 100,
  *                                         'db2' => 100
- *                                     )
- *                                 )
+ *                                     ]
+ *                                 ]
  *
  *     serverTemplate              A server info associative array as documented for $wgDBservers.
  *                                 The host, hostName and load entries will be overridden.
  *
  *     groupLoadsBySection         A 3-d map giving server load ratios for each section and group.
  *                                 For example:
- *                                 array(
- *                                     'section1' => array(
- *                                         'group1' => array(
+ *                                 [
+ *                                     'section1' => [
+ *                                         'group1' => [
  *                                             'db1' => 100,
  *                                             'db2' => 100
- *                                         )
- *                                     )
- *                                 )
+ *                                         ]
+ *                                     ]
+ *                                 ]
  *
  *     groupLoadsByDB              A 3-d map giving server load ratios by DB name.
  *
@@ -254,7 +254,6 @@ class LBFactoryMulti extends LBFactory {
                $section = $this->getSectionForWiki( $wiki );
                if ( !isset( $this->mainLBs[$section] ) ) {
                        $lb = $this->newMainLB( $wiki );
-                       $lb->parentInfo( [ 'id' => "main-$section" ] );
                        $this->chronProt->initLB( $lb );
                        $this->mainLBs[$section] = $lb;
                }
@@ -293,10 +292,9 @@ class LBFactoryMulti extends LBFactory {
         * @param bool|string $wiki Wiki ID, or false for the current wiki
         * @return LoadBalancer
         */
-       public function &getExternalLB( $cluster, $wiki = false ) {
+       public function getExternalLB( $cluster, $wiki = false ) {
                if ( !isset( $this->extLBs[$cluster] ) ) {
                        $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
-                       $this->extLBs[$cluster]->parentInfo( [ 'id' => "ext-$cluster" ] );
                        $this->chronProt->initLB( $this->extLBs[$cluster] );
                }
 
@@ -313,12 +311,18 @@ class LBFactoryMulti extends LBFactory {
         * @return LoadBalancer
         */
        private function newLoadBalancer( $template, $loads, $groupLoads, $readOnlyReason ) {
-               return new LoadBalancer( [
+               $lb = new LoadBalancer( [
                        'servers' => $this->makeServerArray( $template, $loads, $groupLoads ),
                        'loadMonitor' => $this->loadMonitorClass,
                        'readOnlyReason' => $readOnlyReason,
-                       'trxProfiler' => $this->trxProfiler
+                       'trxProfiler' => $this->trxProfiler,
+                       'srvCache' => $this->srvCache,
+                       'wanCache' => $this->wanCache
                ] );
+
+               $this->initLoadBalancer( $lb );
+
+               return $lb;
        }
 
        /**
@@ -347,7 +351,7 @@ class LBFactoryMulti extends LBFactory {
                                }
                                $master = false;
                        } else {
-                               $serverInfo['slave'] = true;
+                               $serverInfo['replica'] = true;
                        }
                        if ( isset( $this->templateOverridesByServer[$serverName] ) ) {
                                $serverInfo = $this->templateOverridesByServer[$serverName] + $serverInfo;
@@ -362,6 +366,8 @@ class LBFactoryMulti extends LBFactory {
                        }
                        $serverInfo['hostName'] = $serverName;
                        $serverInfo['load'] = $load;
+                       $serverInfo += [ 'flags' => DBO_DEFAULT ];
+
                        $servers[] = $serverInfo;
                }
 
@@ -414,11 +420,4 @@ class LBFactoryMulti extends LBFactory {
                        call_user_func_array( $callback, array_merge( [ $lb ], $params ) );
                }
        }
-
-       public function shutdown( $flags = 0 ) {
-               if ( !( $flags & self::SHUTDOWN_NO_CHRONPROT ) ) {
-                       $this->shutdownChronologyProtector( $this->chronProt );
-               }
-               $this->commitMasterChanges( __METHOD__ ); // sanity
-       }
 }
index 1b0a1f3..b6fb0d2 100644 (file)
@@ -54,8 +54,9 @@ class LBFactorySimple extends LBFactory {
                                if ( $i == 0 ) {
                                        $server['master'] = true;
                                } else {
-                                       $server['slave'] = true;
+                                       $server['replica'] = true;
                                }
+                               $server += [ 'flags' => DBO_DEFAULT ];
                        }
                } else {
                        global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype, $wgDebugDumpSql;
@@ -84,12 +85,7 @@ class LBFactorySimple extends LBFactory {
                        ] ];
                }
 
-               return new LoadBalancer( [
-                       'servers' => $servers,
-                       'loadMonitor' => $this->loadMonitorClass,
-                       'readOnlyReason' => $this->readOnlyReason,
-                       'trxProfiler' => $this->trxProfiler
-               ] );
+               return $this->newLoadBalancer( $servers );
        }
 
        /**
@@ -99,7 +95,6 @@ class LBFactorySimple extends LBFactory {
        public function getMainLB( $wiki = false ) {
                if ( !isset( $this->mainLB ) ) {
                        $this->mainLB = $this->newMainLB( $wiki );
-                       $this->mainLB->parentInfo( [ 'id' => 'main' ] );
                        $this->chronProt->initLB( $this->mainLB );
                }
 
@@ -118,12 +113,7 @@ class LBFactorySimple extends LBFactory {
                        throw new MWException( __METHOD__ . ": Unknown cluster \"$cluster\"" );
                }
 
-               return new LoadBalancer( [
-                       'servers' => $wgExternalServers[$cluster],
-                       'loadMonitor' => $this->loadMonitorClass,
-                       'readOnlyReason' => $this->readOnlyReason,
-                       'trxProfiler' => $this->trxProfiler
-               ] );
+               return $this->newLoadBalancer( $wgExternalServers[$cluster] );
        }
 
        /**
@@ -131,16 +121,30 @@ class LBFactorySimple extends LBFactory {
         * @param bool|string $wiki
         * @return array
         */
-       public function &getExternalLB( $cluster, $wiki = false ) {
+       public function getExternalLB( $cluster, $wiki = false ) {
                if ( !isset( $this->extLBs[$cluster] ) ) {
                        $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
-                       $this->extLBs[$cluster]->parentInfo( [ 'id' => "ext-$cluster" ] );
                        $this->chronProt->initLB( $this->extLBs[$cluster] );
                }
 
                return $this->extLBs[$cluster];
        }
 
+       private function newLoadBalancer( array $servers ) {
+               $lb = new LoadBalancer( [
+                       'servers' => $servers,
+                       'loadMonitor' => $this->loadMonitorClass,
+                       'readOnlyReason' => $this->readOnlyReason,
+                       'trxProfiler' => $this->trxProfiler,
+                       'srvCache' => $this->srvCache,
+                       'wanCache' => $this->wanCache
+               ] );
+
+               $this->initLoadBalancer( $lb );
+
+               return $lb;
+       }
+
        /**
         * Execute a function for each tracked load balancer
         * The callback is called with the load balancer as the first parameter,
@@ -157,11 +161,4 @@ class LBFactorySimple extends LBFactory {
                        call_user_func_array( $callback, array_merge( [ $lb ], $params ) );
                }
        }
-
-       public function shutdown( $flags = 0 ) {
-               if ( !( $flags & self::SHUTDOWN_NO_CHRONPROT ) ) {
-                       $this->shutdownChronologyProtector( $this->chronProt );
-               }
-               $this->commitMasterChanges( __METHOD__ ); // sanity
-       }
 }
index 79ca3a7..14cec0e 100644 (file)
@@ -37,7 +37,9 @@ class LBFactorySingle extends LBFactory {
 
                $this->lb = new LoadBalancerSingle( [
                        'readOnlyReason' => $this->readOnlyReason,
-                       'trxProfiler' => $this->trxProfiler
+                       'trxProfiler' => $this->trxProfiler,
+                       'srvCache' => $this->srvCache,
+                       'wanCache' => $this->wanCache
                ] + $conf );
        }
 
@@ -71,7 +73,7 @@ class LBFactorySingle extends LBFactory {
         * @param bool|string $wiki Wiki ID, or false for the current wiki
         * @return LoadBalancerSingle
         */
-       public function &getExternalLB( $cluster, $wiki = false ) {
+       public function getExternalLB( $cluster, $wiki = false ) {
                return $this->lb;
        }
 
@@ -106,7 +108,9 @@ class LoadBalancerSingle extends LoadBalancer {
                                        'load' => 1,
                                ]
                        ],
-                       'trxProfiler' => $this->trxProfiler
+                       'trxProfiler' => isset( $params['trxProfiler'] ) ? $params['trxProfiler'] : null,
+                       'srvCache' => isset( $params['srvCache'] ) ? $params['srvCache'] : null,
+                       'wanCache' => isset( $params['wanCache'] ) ? $params['wanCache'] : null
                ] );
 
                if ( isset( $params['readOnlyReason'] ) ) {
index d96c665..42044a7 100644 (file)
@@ -36,9 +36,9 @@ class LoadBalancer {
        private $mLoads;
        /** @var array[] Map of (group => server index => weight) */
        private $mGroupLoads;
-       /** @var bool Whether to disregard slave lag as a factor in slave selection */
+       /** @var bool Whether to disregard replica DB lag as a factor in replica DB selection */
        private $mAllowLagged;
-       /** @var integer Seconds to spend waiting on slave lag to resolve */
+       /** @var integer Seconds to spend waiting on replica DB lag to resolve */
        private $mWaitTimeout;
        /** @var array LBFactory information */
        private $mParentInfo;
@@ -49,33 +49,40 @@ class LoadBalancer {
        private $mLoadMonitor;
        /** @var BagOStuff */
        private $srvCache;
+       /** @var WANObjectCache */
+       private $wanCache;
+       /** @var TransactionProfiler */
+       protected $trxProfiler;
 
        /** @var bool|DatabaseBase Database connection that caused a problem */
        private $mErrorConnection;
-       /** @var integer The generic (not query grouped) slave index (of $mServers) */
+       /** @var integer The generic (not query grouped) replica DB index (of $mServers) */
        private $mReadIndex;
        /** @var bool|DBMasterPos False if not set */
        private $mWaitForPos;
-       /** @var bool Whether the generic reader fell back to a lagged slave */
-       private $laggedSlaveMode = false;
-       /** @var bool Whether the generic reader fell back to a lagged slave */
-       private $slavesDownMode = false;
+       /** @var bool Whether the generic reader fell back to a lagged replica DB */
+       private $laggedReplicaMode = false;
+       /** @var bool Whether the generic reader fell back to a lagged replica DB */
+       private $allReplicasDownMode = false;
        /** @var string The last DB selection or connection error */
        private $mLastError = 'Unknown error';
        /** @var string|bool Reason the LB is read-only or false if not */
        private $readOnlyReason = false;
        /** @var integer Total connections opened */
        private $connsOpened = 0;
-
-       /** @var TransactionProfiler */
-       protected $trxProfiler;
+       /** @var string|bool String if a requested DBO_TRX transaction round is active */
+       private $trxRoundId = false;
+       /** @var array[] Map of (name => callable) */
+       private $trxRecurringCallbacks = [];
 
        /** @var integer Warn when this many connection are held */
        const CONN_HELD_WARN_THRESHOLD = 10;
        /** @var integer Default 'max lag' when unspecified */
-       const MAX_LAG = 10;
-       /** @var integer Max time to wait for a slave to catch up (e.g. ChronologyProtector) */
+       const MAX_LAG_DEFAULT = 10;
+       /** @var integer Max time to wait for a replica DB to catch up (e.g. ChronologyProtector) */
        const POS_WAIT_TIMEOUT = 10;
+       /** @var integer Seconds to cache master server read-only status */
+       const TTL_CACHE_READONLY = 5;
 
        /**
         * @var boolean
@@ -87,6 +94,9 @@ class LoadBalancer {
         *  - servers : Required. Array of server info structures.
         *  - loadMonitor : Name of a class used to fetch server lag and load.
         *  - readOnlyReason : Reason the master DB is read-only if so [optional]
+        *  - waitTimeout : Maximum time to wait for replicas for consistency [optional]
+        *  - srvCache : BagOStuff object [optional]
+        *  - wanCache : WANObjectCache object [optional]
         * @throws MWException
         */
        public function __construct( array $params ) {
@@ -94,7 +104,9 @@ class LoadBalancer {
                        throw new MWException( __CLASS__ . ': missing servers parameter' );
                }
                $this->mServers = $params['servers'];
-               $this->mWaitTimeout = self::POS_WAIT_TIMEOUT;
+               $this->mWaitTimeout = isset( $params['waitTimeout'] )
+                       ? $params['waitTimeout']
+                       : self::POS_WAIT_TIMEOUT;
 
                $this->mReadIndex = -1;
                $this->mWriteIndex = -1;
@@ -134,8 +146,16 @@ class LoadBalancer {
                        }
                }
 
-               $this->srvCache = ObjectCache::getLocalServerInstance();
-
+               if ( isset( $params['srvCache'] ) ) {
+                       $this->srvCache = $params['srvCache'];
+               } else {
+                       $this->srvCache = new EmptyBagOStuff();
+               }
+               if ( isset( $params['wanCache'] ) ) {
+                       $this->wanCache = $params['wanCache'];
+               } else {
+                       $this->wanCache = WANObjectCache::newEmpty();
+               }
                if ( isset( $params['trxProfiler'] ) ) {
                        $this->trxProfiler = $params['trxProfiler'];
                } else {
@@ -157,34 +177,27 @@ class LoadBalancer {
                return $this->mLoadMonitor;
        }
 
-       /**
-        * Get or set arbitrary data used by the parent object, usually an LBFactory
-        * @param mixed $x
-        * @return mixed
-        */
-       public function parentInfo( $x = null ) {
-               return wfSetVar( $this->mParentInfo, $x );
-       }
-
        /**
         * @param array $loads
         * @param bool|string $wiki Wiki to get non-lagged for
         * @param int $maxLag Restrict the maximum allowed lag to this many seconds
         * @return bool|int|string
         */
-       private function getRandomNonLagged( array $loads, $wiki = false, $maxLag = self::MAX_LAG ) {
+       private function getRandomNonLagged( array $loads, $wiki = false, $maxLag = INF ) {
                $lags = $this->getLagTimes( $wiki );
 
                # Unset excessively lagged servers
                foreach ( $lags as $i => $lag ) {
                        if ( $i != 0 ) {
-                               $maxServerLag = $maxLag;
-                               if ( isset( $this->mServers[$i]['max lag'] ) ) {
-                                       $maxServerLag = min( $maxServerLag, $this->mServers[$i]['max lag'] );
-                               }
+                               # How much lag this server nominally is allowed to have
+                               $maxServerLag = isset( $this->mServers[$i]['max lag'] )
+                                       ? $this->mServers[$i]['max lag']
+                                       : self::MAX_LAG_DEFAULT; // default
+                               # Constrain that futher by $maxLag argument
+                               $maxServerLag = min( $maxServerLag, $maxLag );
 
                                $host = $this->getServerName( $i );
-                               if ( $lag === false ) {
+                               if ( $lag === false && !is_infinite( $maxServerLag ) ) {
                                        wfDebugLog( 'replication', "Server $host (#$i) is not replicating?" );
                                        unset( $loads[$i] );
                                } elseif ( $lag > $maxServerLag ) {
@@ -194,16 +207,16 @@ class LoadBalancer {
                        }
                }
 
-               # Find out if all the slaves with non-zero load are lagged
+               # Find out if all the replica DBs with non-zero load are lagged
                $sum = 0;
                foreach ( $loads as $load ) {
                        $sum += $load;
                }
                if ( $sum == 0 ) {
-                       # No appropriate DB servers except maybe the master and some slaves with zero load
+                       # No appropriate DB servers except maybe the master and some replica DBs with zero load
                        # Do NOT use the master
                        # Instead, this function will return false, triggering read-only mode,
-                       # and a lagged slave will be used instead.
+                       # and a lagged replica DB will be used instead.
                        return false;
                }
 
@@ -216,7 +229,7 @@ class LoadBalancer {
        }
 
        /**
-        * Get the index of the reader connection, which may be a slave
+        * Get the index of the reader connection, which may be a replica DB
         * This takes into account load ratios and lag times. It should
         * always return a consistent index during a given invocation
         *
@@ -263,16 +276,15 @@ class LoadBalancer {
                # Scale the configured load ratios according to the dynamic load (if the load monitor supports it)
                $this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $wiki );
 
-               $laggedSlaveMode = false;
+               $laggedReplicaMode = false;
 
                # No server found yet
                $i = false;
-               $conn = false;
                # First try quickly looking through the available servers for a server that
                # meets our criteria
                $currentLoads = $nonErrorLoads;
                while ( count( $currentLoads ) ) {
-                       if ( $this->mAllowLagged || $laggedSlaveMode ) {
+                       if ( $this->mAllowLagged || $laggedReplicaMode ) {
                                $i = ArrayUtils::pickRandom( $currentLoads );
                        } else {
                                $i = false;
@@ -289,10 +301,10 @@ class LoadBalancer {
                                        $i = $this->getRandomNonLagged( $currentLoads, $wiki );
                                }
                                if ( $i === false && count( $currentLoads ) != 0 ) {
-                                       # All slaves lagged. Switch to read-only mode
-                                       wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode" );
+                                       # All replica DBs lagged. Switch to read-only mode
+                                       wfDebugLog( 'replication', "All replica DBs lagged. Switch to read-only mode" );
                                        $i = ArrayUtils::pickRandom( $currentLoads );
-                                       $laggedSlaveMode = true;
+                                       $laggedReplicaMode = true;
                                }
                        }
 
@@ -333,18 +345,16 @@ class LoadBalancer {
                }
 
                if ( $i !== false ) {
-                       # Slave connection successful
-                       # Wait for the session master pos for a short time
+                       # Replica DB connection successful.
+                       # Wait for the session master pos for a short time.
                        if ( $this->mWaitForPos && $i > 0 ) {
-                               if ( !$this->doWait( $i ) ) {
-                                       $this->mServers[$i]['slave pos'] = $conn->getSlavePos();
-                               }
+                               $this->doWait( $i );
                        }
                        if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $group === false ) {
                                $this->mReadIndex = $i;
-                               # Record if the generic reader index is in "lagged slave" mode
-                               if ( $laggedSlaveMode ) {
-                                       $this->laggedSlaveMode = true;
+                               # Record if the generic reader index is in "lagged replica DB" mode
+                               if ( $laggedReplicaMode ) {
+                                       $this->laggedReplicaMode = true;
                                }
                        }
                        $serverName = $this->getServerName( $i );
@@ -357,7 +367,7 @@ class LoadBalancer {
 
        /**
         * Set the master wait position
-        * If a DB_SLAVE connection has been opened already, waits
+        * If a DB_REPLICA connection has been opened already, waits
         * Otherwise sets a variable telling it to wait if such a connection is opened
         * @param DBMasterPos $pos
         */
@@ -367,14 +377,13 @@ class LoadBalancer {
 
                if ( $i > 0 ) {
                        if ( !$this->doWait( $i ) ) {
-                               $this->mServers[$i]['slave pos'] = $this->getAnyOpenConnection( $i )->getSlavePos();
-                               $this->laggedSlaveMode = true;
+                               $this->laggedReplicaMode = true;
                        }
                }
        }
 
        /**
-        * Set the master wait position and wait for a "generic" slave to catch up to it
+        * Set the master wait position and wait for a "generic" replica DB to catch up to it
         *
         * This can be used a faster proxy for waitForAll()
         *
@@ -388,9 +397,9 @@ class LoadBalancer {
 
                $i = $this->mReadIndex;
                if ( $i <= 0 ) {
-                       // Pick a generic slave if there isn't one yet
+                       // Pick a generic replica DB if there isn't one yet
                        $readLoads = $this->mLoads;
-                       unset( $readLoads[$this->getWriterIndex()] ); // slaves only
+                       unset( $readLoads[$this->getWriterIndex()] ); // replica DBs only
                        $readLoads = array_filter( $readLoads ); // with non-zero load
                        $i = ArrayUtils::pickRandom( $readLoads );
                }
@@ -405,7 +414,7 @@ class LoadBalancer {
        }
 
        /**
-        * Set the master wait position and wait for ALL slaves to catch up to it
+        * Set the master wait position and wait for ALL replica DBs to catch up to it
         * @param DBMasterPos $pos
         * @param int $timeout Max seconds to wait; default is mWaitTimeout
         * @return bool Success (able to connect and no timeouts reached)
@@ -428,13 +437,13 @@ class LoadBalancer {
         * Get any open connection to a given server index, local or foreign
         * Returns false if there is no connection open
         *
-        * @param int $i
+        * @param int $i Server index
         * @return DatabaseBase|bool False on failure
         */
        public function getAnyOpenConnection( $i ) {
-               foreach ( $this->mConns as $conns ) {
-                       if ( !empty( $conns[$i] ) ) {
-                               return reset( $conns[$i] );
+               foreach ( $this->mConns as $connsByServer ) {
+                       if ( !empty( $connsByServer[$i] ) ) {
+                               return reset( $connsByServer[$i] );
                        }
                }
 
@@ -442,7 +451,7 @@ class LoadBalancer {
        }
 
        /**
-        * Wait for a given slave to catch up to the master pos stored in $this
+        * Wait for a given replica DB to catch up to the master pos stored in $this
         * @param int $index Server index
         * @param bool $open Check the server even if a new connection has to be made
         * @param int $timeout Max seconds to wait; default is mWaitTimeout
@@ -458,7 +467,7 @@ class LoadBalancer {
                $knownReachedPos = $this->srvCache->get( $key );
                if ( $knownReachedPos && $knownReachedPos->hasReached( $this->mWaitForPos ) ) {
                        wfDebugLog( 'replication', __METHOD__ .
-                               ": slave $server known to be caught up (pos >= $knownReachedPos).\n" );
+                               ": replica DB $server known to be caught up (pos >= $knownReachedPos).\n" );
                        return true;
                }
 
@@ -482,12 +491,12 @@ class LoadBalancer {
                        }
                }
 
-               wfDebugLog( 'replication', __METHOD__ . ": Waiting for slave $server to catch up...\n" );
+               wfDebugLog( 'replication', __METHOD__ . ": Waiting for replica DB $server to catch up...\n" );
                $timeout = $timeout ?: $this->mWaitTimeout;
                $result = $conn->masterPosWait( $this->mWaitForPos, $timeout );
 
                if ( $result == -1 || is_null( $result ) ) {
-                       // Timed out waiting for slave, use master instead
+                       // Timed out waiting for replica DB, use master instead
                        $msg = __METHOD__ . ": Timed out waiting on $server pos {$this->mWaitForPos}";
                        wfDebugLog( 'replication', "$msg\n" );
                        wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
@@ -548,7 +557,7 @@ class LoadBalancer {
                }
 
                # Operation-based index
-               if ( $i == DB_SLAVE ) {
+               if ( $i == DB_REPLICA ) {
                        $this->mLastError = 'Unknown error'; // reset error string
                        # Try the general server pool if $groups are unavailable.
                        $i = in_array( false, $groups, true )
@@ -556,7 +565,7 @@ class LoadBalancer {
                                : $this->getReaderIndex( false, $wiki );
                        # Couldn't find a working server in getReaderIndex()?
                        if ( $i === false ) {
-                               $this->mLastError = 'No working slave server: ' . $this->mLastError;
+                               $this->mLastError = 'No working replica DB server: ' . $this->mLastError;
 
                                return $this->reportConnectionError();
                        }
@@ -578,7 +587,7 @@ class LoadBalancer {
 
                if ( $masterOnly ) {
                        # Make master-requested DB handles inherit any read-only mode setting
-                       $conn->setLBInfo( 'readOnlyReason', $this->getReadOnlyReason( $wiki ) );
+                       $conn->setLBInfo( 'readOnlyReason', $this->getReadOnlyReason( $wiki, $conn ) );
                }
 
                return $conn;
@@ -596,11 +605,10 @@ class LoadBalancer {
                $serverIndex = $conn->getLBInfo( 'serverIndex' );
                $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
                if ( $serverIndex === null || $refCount === null ) {
-                       wfDebug( __METHOD__ . ": this connection was not opened as a foreign connection\n" );
                        /**
                         * This can happen in code like:
                         *   foreach ( $dbs as $db ) {
-                        *     $conn = $lb->getConnection( DB_SLAVE, array(), $db );
+                        *     $conn = $lb->getConnection( DB_REPLICA, [], $db );
                         *     ...
                         *     $lb->reuseConnection( $conn );
                         *   }
@@ -826,7 +834,7 @@ class LoadBalancer {
                }
 
                // Let the handle know what the cluster master is (e.g. "db1052")
-               $masterName = $this->getServerName( 0 );
+               $masterName = $this->getServerName( $this->getWriterIndex() );
                $server['clusterMasterHost'] = $masterName;
 
                // Log when many connection are made on requests
@@ -851,6 +859,15 @@ class LoadBalancer {
                );
                $db->setTransactionProfiler( $this->trxProfiler );
 
+               if ( $server['serverIndex'] === $this->getWriterIndex() ) {
+                       if ( $this->trxRoundId !== false ) {
+                               $this->applyTransactionRoundFlags( $db );
+                       }
+                       foreach ( $this->trxRecurringCallbacks as $name => $callback ) {
+                               $db->setTransactionListener( $name, $callback );
+                       }
+               }
+
                return $db;
        }
 
@@ -968,12 +985,12 @@ class LoadBalancer {
 
        /**
         * Get the current master position for chronology control purposes
-        * @return mixed
+        * @return DBMasterPos|bool Returns false if not applicable
         */
        public function getMasterPos() {
-               # If this entire request was served from a slave without opening a connection to the
-               # master (however unlikely that may be), then we can fetch the position from the slave.
-               $masterConn = $this->getAnyOpenConnection( 0 );
+               # If this entire request was served from a replica DB without opening a connection to the
+               # master (however unlikely that may be), then we can fetch the position from the replica DB.
+               $masterConn = $this->getAnyOpenConnection( $this->getWriterIndex() );
                if ( !$masterConn ) {
                        $serverCount = count( $this->mServers );
                        for ( $i = 1; $i < $serverCount; $i++ ) {
@@ -1004,14 +1021,10 @@ class LoadBalancer {
         * Close all open connections
         */
        public function closeAll() {
-               foreach ( $this->mConns as $conns2 ) {
-                       foreach ( $conns2 as $conns3 ) {
-                               /** @var DatabaseBase $conn */
-                               foreach ( $conns3 as $conn ) {
-                                       $conn->close();
-                               }
-                       }
-               }
+               $this->forEachOpenConnection( function ( DatabaseBase $conn ) {
+                       $conn->close();
+               } );
+
                $this->mConns = [
                        'local' => [],
                        'foreignFree' => [],
@@ -1022,66 +1035,237 @@ class LoadBalancer {
 
        /**
         * Close a connection
+        *
         * Using this function makes sure the LoadBalancer knows the connection is closed.
         * If you use $conn->close() directly, the load balancer won't update its state.
+        *
         * @param DatabaseBase $conn
         */
-       public function closeConnection( $conn ) {
-               $done = false;
-               foreach ( $this->mConns as $i1 => $conns2 ) {
-                       foreach ( $conns2 as $i2 => $conns3 ) {
-                               foreach ( $conns3 as $i3 => $candidateConn ) {
-                                       if ( $conn === $candidateConn ) {
-                                               $conn->close();
-                                               unset( $this->mConns[$i1][$i2][$i3] );
-                                               --$this->connsOpened;
-                                               $done = true;
-                                               break;
-                                       }
+       public function closeConnection( DatabaseBase $conn ) {
+               $serverIndex = $conn->getLBInfo( 'serverIndex' ); // second index level of mConns
+               foreach ( $this->mConns as $type => $connsByServer ) {
+                       if ( !isset( $connsByServer[$serverIndex] ) ) {
+                               continue;
+                       }
+
+                       foreach ( $connsByServer[$serverIndex] as $i => $trackedConn ) {
+                               if ( $conn === $trackedConn ) {
+                                       unset( $this->mConns[$type][$serverIndex][$i] );
+                                       --$this->connsOpened;
+                                       break 2;
                                }
                        }
                }
-               if ( !$done ) {
-                       $conn->close();
-               }
+
+               $conn->close();
        }
 
        /**
         * Commit transactions on all open connections
         * @param string $fname Caller name
+        * @throws DBExpectedError
         */
        public function commitAll( $fname = __METHOD__ ) {
-               foreach ( $this->mConns as $conns2 ) {
-                       foreach ( $conns2 as $conns3 ) {
-                               /** @var DatabaseBase[] $conns3 */
-                               foreach ( $conns3 as $conn ) {
-                                       if ( $conn->trxLevel() ) {
-                                               $conn->commit( $fname, 'flush' );
-                                       }
+               $failures = [];
+
+               $restore = ( $this->trxRoundId !== false );
+               $this->trxRoundId = false;
+               $this->forEachOpenConnection(
+                       function ( DatabaseBase $conn ) use ( $fname, $restore, &$failures ) {
+                               try {
+                                       $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
+                               } catch ( DBError $e ) {
+                                       MWExceptionHandler::logException( $e );
+                                       $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
+                               }
+                               if ( $restore && $conn->getLBInfo( 'master' ) ) {
+                                       $this->undoTransactionRoundFlags( $conn );
+                               }
+                       }
+               );
+
+               if ( $failures ) {
+                       throw new DBExpectedError(
+                               null,
+                               "Commit failed on server(s) " . implode( "\n", array_unique( $failures ) )
+                       );
+               }
+       }
+
+       /**
+        * Perform all pre-commit callbacks that remain part of the atomic transactions
+        * and disable any post-commit callbacks until runMasterPostTrxCallbacks()
+        * @since 1.28
+        */
+       public function finalizeMasterChanges() {
+               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) {
+                       // Any error should cause all DB transactions to be rolled back together
+                       $conn->setTrxEndCallbackSuppression( false );
+                       $conn->runOnTransactionPreCommitCallbacks();
+                       // Defer post-commit callbacks until COMMIT finishes for all DBs
+                       $conn->setTrxEndCallbackSuppression( true );
+               } );
+       }
+
+       /**
+        * Perform all pre-commit checks for things like replication safety
+        * @param array $options Includes:
+        *   - maxWriteDuration : max write query duration time in seconds
+        * @throws DBTransactionError
+        * @since 1.28
+        */
+       public function approveMasterChanges( array $options ) {
+               $limit = isset( $options['maxWriteDuration'] ) ? $options['maxWriteDuration'] : 0;
+               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( $limit ) {
+                       // If atomic sections or explicit transactions are still open, some caller must have
+                       // caught an exception but failed to properly rollback any changes. Detect that and
+                       // throw and error (causing rollback).
+                       if ( $conn->explicitTrxActive() ) {
+                               throw new DBTransactionError(
+                                       $conn,
+                                       "Explicit transaction still active. A caller may have caught an error."
+                               );
+                       }
+                       // Assert that the time to replicate the transaction will be sane.
+                       // If this fails, then all DB transactions will be rollback back together.
+                       $time = $conn->pendingWriteQueryDuration( $conn::ESTIMATE_DB_APPLY );
+                       if ( $limit > 0 && $time > $limit ) {
+                               throw new DBTransactionError(
+                                       $conn,
+                                       wfMessage( 'transaction-duration-limit-exceeded', $time, $limit )->text()
+                               );
+                       }
+                       // If a connection sits idle while slow queries execute on another, that connection
+                       // may end up dropped before the commit round is reached. Ping servers to detect this.
+                       if ( $conn->writesOrCallbacksPending() && !$conn->ping() ) {
+                               throw new DBTransactionError(
+                                       $conn,
+                                       "A connection to the {$conn->getDBname()} database was lost before commit."
+                               );
+                       }
+               } );
+       }
+
+       /**
+        * Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
+        *
+        * The DBO_TRX setting will be reverted to the default in each of these methods:
+        *   - commitMasterChanges()
+        *   - rollbackMasterChanges()
+        *   - commitAll()
+        * This allows for custom transaction rounds from any outer transaction scope.
+        *
+        * @param string $fname
+        * @throws DBExpectedError
+        * @since 1.28
+        */
+       public function beginMasterChanges( $fname = __METHOD__ ) {
+               if ( $this->trxRoundId !== false ) {
+                       throw new DBTransactionError(
+                               null,
+                               "$fname: Transaction round '{$this->trxRoundId}' already started."
+                       );
+               }
+               $this->trxRoundId = $fname;
+
+               $failures = [];
+               $this->forEachOpenMasterConnection(
+                       function ( DatabaseBase $conn ) use ( $fname, &$failures ) {
+                               $conn->setTrxEndCallbackSuppression( true );
+                               try {
+                                       $conn->flushSnapshot( $fname );
+                               } catch ( DBError $e ) {
+                                       MWExceptionHandler::logException( $e );
+                                       $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
                                }
+                               $conn->setTrxEndCallbackSuppression( false );
+                               $this->applyTransactionRoundFlags( $conn );
                        }
+               );
+
+               if ( $failures ) {
+                       throw new DBExpectedError(
+                               null,
+                               "$fname: Flush failed on server(s) " . implode( "\n", array_unique( $failures ) )
+                       );
                }
        }
 
        /**
-        * Issue COMMIT only on master, only if queries were done on connection
+        * Issue COMMIT on all master connections where writes where done
         * @param string $fname Caller name
+        * @throws DBExpectedError
         */
        public function commitMasterChanges( $fname = __METHOD__ ) {
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
-                                       $conn->commit( $fname, 'flush' );
+               $failures = [];
+
+               $restore = ( $this->trxRoundId !== false );
+               $this->trxRoundId = false;
+               $this->forEachOpenMasterConnection(
+                       function ( DatabaseBase $conn ) use ( $fname, $restore, &$failures ) {
+                               try {
+                                       if ( $conn->writesOrCallbacksPending() ) {
+                                               $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
+                                       } elseif ( $restore ) {
+                                               $conn->flushSnapshot( $fname );
+                                       }
+                               } catch ( DBError $e ) {
+                                       MWExceptionHandler::logException( $e );
+                                       $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
+                               }
+                               if ( $restore ) {
+                                       $this->undoTransactionRoundFlags( $conn );
                                }
                        }
+               );
+
+               if ( $failures ) {
+                       throw new DBExpectedError(
+                               null,
+                               "$fname: Commit failed on server(s) " . implode( "\n", array_unique( $failures ) )
+                       );
                }
        }
 
+       /**
+        * Issue all pending post-COMMIT/ROLLBACK callbacks
+        * @param integer $type IDatabase::TRIGGER_* constant
+        * @return Exception|null The first exception or null if there were none
+        * @since 1.28
+        */
+       public function runMasterPostTrxCallbacks( $type ) {
+               $e = null; // first exception
+               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( $type, &$e ) {
+                       $conn->setTrxEndCallbackSuppression( false );
+                       if ( $conn->writesOrCallbacksPending() ) {
+                               // This happens if onTransactionIdle() callbacks leave callbacks on *another* DB
+                               // (which finished its callbacks already). Warn and recover in this case. Let the
+                               // callbacks run in the final commitMasterChanges() in LBFactory::shutdown().
+                               wfWarn( __METHOD__ . ": did not expect writes/callbacks pending." );
+                               return;
+                       } elseif ( $conn->trxLevel() ) {
+                               // This happens for single-DB setups where DB_REPLICA uses the master DB,
+                               // thus leaving an implicit read-only transaction open at this point. It
+                               // also happens if onTransactionIdle() callbacks leave implicit transactions
+                               // open on *other* DBs (which is slightly improper). Let these COMMIT on the
+                               // next call to commitMasterChanges(), possibly in LBFactory::shutdown().
+                               return;
+                       }
+                       try {
+                               $conn->runOnTransactionIdleCallbacks( $type );
+                       } catch ( Exception $ex ) {
+                               $e = $e ?: $ex;
+                       }
+                       try {
+                               $conn->runTransactionListenerCallbacks( $type );
+                       } catch ( Exception $ex ) {
+                               $e = $e ?: $ex;
+                       }
+               } );
+
+               return $e;
+       }
+
        /**
         * Issue ROLLBACK only on master, only if queries were done on connection
         * @param string $fname Caller name
@@ -1089,32 +1273,66 @@ class LoadBalancer {
         * @since 1.23
         */
        public function rollbackMasterChanges( $fname = __METHOD__ ) {
-               $failedServers = [];
-
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
-                                       try {
-                                               $conn->rollback( $fname, 'flush' );
-                                       } catch ( DBError $e ) {
-                                               MWExceptionHandler::logException( $e );
-                                               $failedServers[] = $conn->getServer();
-                                       }
+               $restore = ( $this->trxRoundId !== false );
+               $this->trxRoundId = false;
+               $this->forEachOpenMasterConnection(
+                       function ( DatabaseBase $conn ) use ( $fname, $restore ) {
+                               if ( $conn->writesOrCallbacksPending() ) {
+                                       $conn->rollback( $fname, $conn::FLUSHING_ALL_PEERS );
+                               }
+                               if ( $restore ) {
+                                       $this->undoTransactionRoundFlags( $conn );
                                }
                        }
+               );
+       }
+
+       /**
+        * Suppress all pending post-COMMIT/ROLLBACK callbacks
+        * @return Exception|null The first exception or null if there were none
+        * @since 1.28
+        */
+       public function suppressTransactionEndCallbacks() {
+               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) {
+                       $conn->setTrxEndCallbackSuppression( true );
+               } );
+       }
+
+       /**
+        * @param DatabaseBase $conn
+        */
+       private function applyTransactionRoundFlags( DatabaseBase $conn ) {
+               if ( $conn->getFlag( DBO_DEFAULT ) ) {
+                       // DBO_TRX is controlled entirely by CLI mode presence with DBO_DEFAULT.
+                       // Force DBO_TRX even in CLI mode since a commit round is expected soon.
+                       $conn->setFlag( DBO_TRX, $conn::REMEMBER_PRIOR );
+                       // If config has explicitly requested DBO_TRX be either on or off by not
+                       // setting DBO_DEFAULT, then respect that. Forcing no transactions is useful
+                       // for things like blob stores (ExternalStore) which want auto-commit mode.
                }
+       }
 
-               if ( $failedServers ) {
-                       throw new DBExpectedError( null, "Rollback failed on server(s) " .
-                               implode( ', ', array_unique( $failedServers ) ) );
+       /**
+        * @param DatabaseBase $conn
+        */
+       private function undoTransactionRoundFlags( DatabaseBase $conn ) {
+               if ( $conn->getFlag( DBO_DEFAULT ) ) {
+                       $conn->restoreFlags( $conn::RESTORE_PRIOR );
                }
        }
 
+       /**
+        * Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot
+        *
+        * @param string $fname Caller name
+        * @since 1.28
+        */
+       public function flushReplicaSnapshots( $fname = __METHOD__ ) {
+               $this->forEachOpenReplicaConnection( function ( DatabaseBase $conn ) {
+                       $conn->flushSnapshot( __METHOD__ );
+               } );
+       }
+
        /**
         * @return bool Whether a master connection is already open
         * @since 1.24
@@ -1129,19 +1347,12 @@ class LoadBalancer {
         * @return bool
         */
        public function hasMasterChanges() {
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
-                                       return true;
-                               }
-                       }
-               }
-               return false;
+               $pending = 0;
+               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( &$pending ) {
+                       $pending |= $conn->writesOrCallbacksPending();
+               } );
+
+               return (bool)$pending;
        }
 
        /**
@@ -1151,16 +1362,10 @@ class LoadBalancer {
         */
        public function lastMasterChangeTimestamp() {
                $lastTime = false;
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               $lastTime = max( $lastTime, $conn->lastDoneWrites() );
-                       }
-               }
+               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( &$lastTime ) {
+                       $lastTime = max( $lastTime, $conn->lastDoneWrites() );
+               } );
+
                return $lastTime;
        }
 
@@ -1182,88 +1387,119 @@ class LoadBalancer {
        /**
         * Get the list of callers that have pending master changes
         *
-        * @return array
+        * @return string[] List of method names
         * @since 1.27
         */
        public function pendingMasterChangeCallers() {
                $fnames = [];
-
-               $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $conns2 ) {
-                       if ( empty( $conns2[$masterIndex] ) ) {
-                               continue;
-                       }
-                       /** @var DatabaseBase $conn */
-                       foreach ( $conns2[$masterIndex] as $conn ) {
-                               $fnames = array_merge( $fnames, $conn->pendingWriteCallers() );
-                       }
-               }
+               $this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( &$fnames ) {
+                       $fnames = array_merge( $fnames, $conn->pendingWriteCallers() );
+               } );
 
                return $fnames;
        }
 
-       /**
-        * @param mixed $value
-        * @return mixed
-        */
-       public function waitTimeout( $value = null ) {
-               return wfSetVar( $this->mWaitTimeout, $value );
-       }
-
        /**
         * @note This method will trigger a DB connection if not yet done
-        *
         * @param string|bool $wiki Wiki ID, or false for the current wiki
         * @return bool Whether the generic connection for reads is highly "lagged"
         */
-       public function getLaggedSlaveMode( $wiki = false ) {
+       public function getLaggedReplicaMode( $wiki = false ) {
                // No-op if there is only one DB (also avoids recursion)
-               if ( !$this->laggedSlaveMode && $this->getServerCount() > 1 ) {
+               if ( !$this->laggedReplicaMode && $this->getServerCount() > 1 ) {
                        try {
-                               // See if laggedSlaveMode gets set
-                               $conn = $this->getConnection( DB_SLAVE, false, $wiki );
+                               // See if laggedReplicaMode gets set
+                               $conn = $this->getConnection( DB_REPLICA, false, $wiki );
                                $this->reuseConnection( $conn );
                        } catch ( DBConnectionError $e ) {
                                // Avoid expensive re-connect attempts and failures
-                               $this->slavesDownMode = true;
-                               $this->laggedSlaveMode = true;
+                               $this->allReplicasDownMode = true;
+                               $this->laggedReplicaMode = true;
                        }
                }
 
-               return $this->laggedSlaveMode;
+               return $this->laggedReplicaMode;
+       }
+
+       /**
+        * @param bool $wiki
+        * @return bool
+        * @deprecated 1.28; use getLaggedReplicaMode()
+        */
+       public function getLaggedSlaveMode( $wiki = false ) {
+               return $this->getLaggedReplicaMode( $wiki );
        }
 
        /**
         * @note This method will never cause a new DB connection
         * @return bool Whether any generic connection used for reads was highly "lagged"
+        * @since 1.28
+        */
+       public function laggedReplicaUsed() {
+               return $this->laggedReplicaMode;
+       }
+
+       /**
+        * @return bool
         * @since 1.27
+        * @deprecated Since 1.28; use laggedReplicaUsed()
         */
        public function laggedSlaveUsed() {
-               return $this->laggedSlaveMode;
+               return $this->laggedReplicaUsed();
        }
 
        /**
         * @note This method may trigger a DB connection if not yet done
         * @param string|bool $wiki Wiki ID, or false for the current wiki
+        * @param DatabaseBase|null DB master connection; used to avoid loops [optional]
         * @return string|bool Reason the master is read-only or false if it is not
         * @since 1.27
         */
-       public function getReadOnlyReason( $wiki = false ) {
+       public function getReadOnlyReason( $wiki = false, DatabaseBase $conn = null ) {
                if ( $this->readOnlyReason !== false ) {
                        return $this->readOnlyReason;
-               } elseif ( $this->getLaggedSlaveMode( $wiki ) ) {
-                       if ( $this->slavesDownMode ) {
+               } elseif ( $this->getLaggedReplicaMode( $wiki ) ) {
+                       if ( $this->allReplicasDownMode ) {
                                return 'The database has been automatically locked ' .
-                                       'until the slave database servers become available';
+                                       'until the replica database servers become available';
                        } else {
                                return 'The database has been automatically locked ' .
-                                       'while the slave database servers catch up to the master.';
+                                       'while the replica database servers catch up to the master.';
                        }
+               } elseif ( $this->masterRunningReadOnly( $wiki, $conn ) ) {
+                       return 'The database master is running in read-only mode.';
                }
 
                return false;
        }
 
+       /**
+        * @param string $wiki Wiki ID, or false for the current wiki
+        * @param DatabaseBase|null DB master connectionl used to avoid loops [optional]
+        * @return bool
+        */
+       private function masterRunningReadOnly( $wiki, DatabaseBase $conn = null ) {
+               $cache = $this->wanCache;
+               $masterServer = $this->getServerName( $this->getWriterIndex() );
+
+               return (bool)$cache->getWithSetCallback(
+                       $cache->makeGlobalKey( __CLASS__, 'server-read-only', $masterServer ),
+                       self::TTL_CACHE_READONLY,
+                       function () use ( $wiki, $conn ) {
+                               $this->trxProfiler->setSilenced( true );
+                               try {
+                                       $dbw = $conn ?: $this->getConnection( DB_MASTER, [], $wiki );
+                                       $readOnly = (int)$dbw->serverIsReadOnly();
+                               } catch ( DBError $e ) {
+                                       $readOnly = 0;
+                               }
+                               $this->trxProfiler->setSilenced( false );
+                               return $readOnly;
+                       },
+                       [ 'pcTTL' => $cache::TTL_PROC_LONG, 'busyValue' => 0 ]
+               );
+       }
+
        /**
         * Disables/enables lag checks
         * @param null|bool $mode
@@ -1283,16 +1519,11 @@ class LoadBalancer {
         */
        public function pingAll() {
                $success = true;
-               foreach ( $this->mConns as $conns2 ) {
-                       foreach ( $conns2 as $conns3 ) {
-                               /** @var DatabaseBase[] $conns3 */
-                               foreach ( $conns3 as $conn ) {
-                                       if ( !$conn->ping() ) {
-                                               $success = false;
-                                       }
-                               }
+               $this->forEachOpenConnection( function ( DatabaseBase $conn ) use ( &$success ) {
+                       if ( !$conn->ping() ) {
+                               $success = false;
                        }
-               }
+               } );
 
                return $success;
        }
@@ -1303,9 +1534,48 @@ class LoadBalancer {
         * @param array $params
         */
        public function forEachOpenConnection( $callback, array $params = [] ) {
-               foreach ( $this->mConns as $conns2 ) {
-                       foreach ( $conns2 as $conns3 ) {
-                               foreach ( $conns3 as $conn ) {
+               foreach ( $this->mConns as $connsByServer ) {
+                       foreach ( $connsByServer as $serverConns ) {
+                               foreach ( $serverConns as $conn ) {
+                                       $mergedParams = array_merge( [ $conn ], $params );
+                                       call_user_func_array( $callback, $mergedParams );
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Call a function with each open connection object to a master
+        * @param callable $callback
+        * @param array $params
+        * @since 1.28
+        */
+       public function forEachOpenMasterConnection( $callback, array $params = [] ) {
+               $masterIndex = $this->getWriterIndex();
+               foreach ( $this->mConns as $connsByServer ) {
+                       if ( isset( $connsByServer[$masterIndex] ) ) {
+                               /** @var DatabaseBase $conn */
+                               foreach ( $connsByServer[$masterIndex] as $conn ) {
+                                       $mergedParams = array_merge( [ $conn ], $params );
+                                       call_user_func_array( $callback, $mergedParams );
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Call a function with each open replica DB connection object
+        * @param callable $callback
+        * @param array $params
+        * @since 1.28
+        */
+       public function forEachOpenReplicaConnection( $callback, array $params = [] ) {
+               foreach ( $this->mConns as $connsByServer ) {
+                       foreach ( $connsByServer as $i => $serverConns ) {
+                               if ( $i === $this->getWriterIndex() ) {
+                                       continue; // skip master
+                               }
+                               foreach ( $serverConns as $conn ) {
                                        $mergedParams = array_merge( [ $conn ], $params );
                                        call_user_func_array( $callback, $mergedParams );
                                }
@@ -1314,10 +1584,10 @@ class LoadBalancer {
        }
 
        /**
-        * Get the hostname and lag time of the most-lagged slave
+        * Get the hostname and lag time of the most-lagged replica DB
         *
         * This is useful for maintenance scripts that need to throttle their updates.
-        * May attempt to open connections to slaves on the default DB. If there is
+        * May attempt to open connections to replica DBs on the default DB. If there is
         * no lag, the maximum lag will be reported as -1.
         *
         * @param bool|string $wiki Wiki ID, or false for the default database
@@ -1386,19 +1656,19 @@ class LoadBalancer {
        }
 
        /**
-        * Wait for a slave DB to reach a specified master position
+        * Wait for a replica DB to reach a specified master position
         *
         * This will connect to the master to get an accurate position if $pos is not given
         *
-        * @param IDatabase $conn Slave DB
+        * @param IDatabase $conn Replica DB
         * @param DBMasterPos|bool $pos Master position; default: current position
         * @param integer $timeout Timeout in seconds
         * @return bool Success
         * @since 1.27
         */
        public function safeWaitForMasterPos( IDatabase $conn, $pos = false, $timeout = 10 ) {
-               if ( $this->getServerCount() == 1 || !$conn->getLBInfo( 'slave' ) ) {
-                       return true; // server is not a slave DB
+               if ( $this->getServerCount() == 1 || !$conn->getLBInfo( 'replica' ) ) {
+                       return true; // server is not a replica DB
                }
 
                $pos = $pos ?: $this->getConnection( DB_MASTER )->getMasterPos();
@@ -1428,4 +1698,25 @@ class LoadBalancer {
        public function clearLagTimeCache() {
                $this->getLoadMonitor()->clearCaches();
        }
+
+       /**
+        * Set a callback via DatabaseBase::setTransactionListener() on
+        * all current and future master connections of this load balancer
+        *
+        * @param string $name Callback name
+        * @param callable|null $callback
+        * @since 1.28
+        */
+       public function setTransactionListener( $name, callable $callback = null ) {
+               if ( $callback ) {
+                       $this->trxRecurringCallbacks[$name] = $callback;
+               } else {
+                       unset( $this->trxRecurringCallbacks[$name] );
+               }
+               $this->forEachOpenMasterConnection(
+                       function ( DatabaseBase $conn ) use ( $name, $callback ) {
+                               $conn->setTransactionListener( $name, $callback );
+                       }
+               );
+       }
 }
index d90ef8a..8c019d8 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\Logger\LegacyLogger;
+
 /**
  * New debugger system that outputs a toolbar on page view.
  *
@@ -93,7 +95,7 @@ class MWDebug {
         */
        public static function addModules( OutputPage $out ) {
                if ( self::$enabled ) {
-                       $out->addModules( 'mediawiki.debug.init' );
+                       $out->addModules( 'mediawiki.debug' );
                }
        }
 
@@ -334,6 +336,7 @@ class MWDebug {
                                if ( isset( $context['seconds_elapsed'] ) && isset( $context['memory_used'] ) ) {
                                        $prefix .= "{$context['seconds_elapsed']} {$context['memory_used']}  ";
                                }
+                               $str = LegacyLogger::interpolate( $str, $context );
                                $str = $prefix . $str;
                        }
                        self::$debug[] = rtrim( UtfNormal\Validator::cleanUp( $str ) );
@@ -347,10 +350,11 @@ class MWDebug {
         * @param string $sql
         * @param string $function
         * @param bool $isMaster
+        * @param float $runTime Query run time
         * @return int ID number of the query to pass to queryTime or -1 if the
         *  debugger is disabled
         */
-       public static function query( $sql, $function, $isMaster ) {
+       public static function query( $sql, $function, $isMaster, $runTime ) {
                if ( !self::$enabled ) {
                        return -1;
                }
@@ -384,28 +388,12 @@ class MWDebug {
                        'sql' => $sql,
                        'function' => $function,
                        'master' => (bool)$isMaster,
-                       'time' => 0.0,
-                       '_start' => microtime( true ),
+                       'time' => $runTime,
                ];
 
                return count( self::$query ) - 1;
        }
 
-       /**
-        * Calculates how long a query took.
-        *
-        * @since 1.19
-        * @param int $id
-        */
-       public static function queryTime( $id ) {
-               if ( $id === -1 || !self::$enabled ) {
-                       return;
-               }
-
-               self::$query[$id]['time'] = microtime( true ) - self::$query[$id]['_start'];
-               unset( self::$query[$id]['_start'] );
-       }
-
        /**
         * Returns a list of files included, along with their size
         *
@@ -540,12 +528,19 @@ class MWDebug {
                // see: https://github.com/facebook/hhvm/issues/2257#issuecomment-39362246
                $realMemoryUsage = wfIsHHVM();
 
+               $branch = GitInfo::currentBranch();
+               if ( GitInfo::isSHA1( $branch ) ) {
+                       // If it's a detached HEAD, the SHA1 will already be
+                       // included in the MW version, so don't show it.
+                       $branch = false;
+               }
+
                return [
                        'mwVersion' => $wgVersion,
                        'phpEngine' => wfIsHHVM() ? 'HHVM' : 'PHP',
                        'phpVersion' => wfIsHHVM() ? HHVM_VERSION : PHP_VERSION,
                        'gitRevision' => GitInfo::headSHA1(),
-                       'gitBranch' => GitInfo::currentBranch(),
+                       'gitBranch' => $branch,
                        'gitViewUrl' => GitInfo::headViewUrl(),
                        'time' => microtime( true ) - $wgRequestTime,
                        'log' => self::$log,
index a6b53ec..4cf8313 100644 (file)
@@ -25,9 +25,9 @@ namespace MediaWiki\Logger;
  *
  * Usage:
  * @code
- * $wgMWLoggerDefaultSpi = array(
+ * $wgMWLoggerDefaultSpi = [
  *   'class' => '\\MediaWiki\\Logger\\LegacySpi',
- * );
+ * ];
  * @endcode
  *
  * @see \MediaWiki\Logger\LoggerFactory
index 49ad9e6..f44ff1c 100644 (file)
@@ -39,73 +39,73 @@ use ObjectFactory;
  * global configuration variable used by LoggerFactory to construct its
  * default SPI provider:
  * @code
- * $wgMWLoggerDefaultSpi = array(
+ * $wgMWLoggerDefaultSpi = [
  *   'class' => '\\MediaWiki\\Logger\\MonologSpi',
- *   'args' => array( array(
- *       'loggers' => array(
- *           '@default' => array(
- *               'processors' => array( 'wiki', 'psr', 'pid', 'uid', 'web' ),
- *               'handlers'   => array( 'stream' ),
- *           ),
- *           'runJobs' => array(
- *               'processors' => array( 'wiki', 'psr', 'pid' ),
- *               'handlers'   => array( 'stream' ),
- *           )
- *       ),
- *       'processors' => array(
- *           'wiki' => array(
+ *   'args' => [ [
+ *       'loggers' => [
+ *           '@default' => [
+ *               'processors' => [ 'wiki', 'psr', 'pid', 'uid', 'web' ],
+ *               'handlers'   => [ 'stream' ],
+ *           ],
+ *           'runJobs' => [
+ *               'processors' => [ 'wiki', 'psr', 'pid' ],
+ *               'handlers'   => [ 'stream' ],
+ *           ]
+ *       ],
+ *       'processors' => [
+ *           'wiki' => [
  *               'class' => '\\MediaWiki\\Logger\\Monolog\\WikiProcessor',
- *           ),
- *           'psr' => array(
+ *           ],
+ *           'psr' => [
  *               'class' => '\\Monolog\\Processor\\PsrLogMessageProcessor',
- *           ),
- *           'pid' => array(
+ *           ],
+ *           'pid' => [
  *               'class' => '\\Monolog\\Processor\\ProcessIdProcessor',
- *           ),
- *           'uid' => array(
+ *           ],
+ *           'uid' => [
  *               'class' => '\\Monolog\\Processor\\UidProcessor',
- *           ),
- *           'web' => array(
+ *           ],
+ *           'web' => [
  *               'class' => '\\Monolog\\Processor\\WebProcessor',
- *           ),
- *       ),
- *       'handlers' => array(
- *           'stream' => array(
+ *           ],
+ *       ],
+ *       'handlers' => [
+ *           'stream' => [
  *               'class'     => '\\Monolog\\Handler\\StreamHandler',
- *               'args'      => array( 'path/to/your.log' ),
+ *               'args'      => [ 'path/to/your.log' ],
  *               'formatter' => 'line',
- *           ),
- *           'redis' => array(
+ *           ],
+ *           'redis' => [
  *               'class'     => '\\Monolog\\Handler\\RedisHandler',
- *               'args'      => array( function() {
+ *               'args'      => [ function() {
  *                       $redis = new Redis();
  *                       $redis->connect( '127.0.0.1', 6379 );
  *                       return $redis;
  *                   },
  *                   'logstash'
- *               ),
+ *               ],
  *               'formatter' => 'logstash',
  *               'buffer' => true,
- *           ),
- *           'udp2log' => array(
+ *           ],
+ *           'udp2log' => [
  *               'class' => '\\MediaWiki\\Logger\\Monolog\\LegacyHandler',
- *               'args' => array(
+ *               'args' => [
  *                   'udp://127.0.0.1:8420/mediawiki
- *               ),
+ *               ],
  *               'formatter' => 'line',
- *           ),
- *       ),
- *       'formatters' => array(
- *           'line' => array(
+ *           ],
+ *       ],
+ *       'formatters' => [
+ *           'line' => [
  *               'class' => '\\Monolog\\Formatter\\LineFormatter',
- *            ),
- *            'logstash' => array(
+ *            ],
+ *            'logstash' => [
  *                'class' => '\\Monolog\\Formatter\\LogstashFormatter',
- *                'args'  => array( 'mediawiki', php_uname( 'n' ), null, '', 1 ),
- *            ),
- *       ),
- *   ) ),
- * );
+ *                'args'  => [ 'mediawiki', php_uname( 'n' ), null, '', 1 ],
+ *            ],
+ *       ],
+ *   ] ],
+ * ];
  * @endcode
  *
  * @see https://github.com/Seldaek/monolog
index f92ff7d..82308d0 100644 (file)
@@ -28,9 +28,9 @@ use Psr\Log\NullLogger;
  *
  * Usage:
  *
- *     $wgMWLoggerDefaultSpi = array(
+ *     $wgMWLoggerDefaultSpi = [
  *         'class' => '\\MediaWiki\\Logger\\NullSpi',
- *     );
+ *     ];
  *
  * @see \MediaWiki\Logger\LoggerFactory
  * @since 1.25
index a9921b3..a348719 100644 (file)
@@ -4,31 +4,43 @@
  * Deferrable Update for closure/callback updates via IDatabase::doAtomicSection()
  * @since 1.27
  */
-class AtomicSectionUpdate implements DeferrableUpdate {
+class AtomicSectionUpdate implements DeferrableUpdate, DeferrableCallback {
        /** @var IDatabase */
        private $dbw;
        /** @var string */
        private $fname;
-       /** @var Closure|callable */
+       /** @var callable|null */
        private $callback;
 
        /**
         * @param IDatabase $dbw
         * @param string $fname Caller name (usually __METHOD__)
         * @param callable $callback
-        * @throws InvalidArgumentException
         * @see IDatabase::doAtomicSection()
         */
-       public function __construct( IDatabase $dbw, $fname, $callback ) {
+       public function __construct( IDatabase $dbw, $fname, callable $callback ) {
                $this->dbw = $dbw;
                $this->fname = $fname;
-               if ( !is_callable( $callback ) ) {
-                       throw new InvalidArgumentException( 'Not a valid callback/closure!' );
-               }
                $this->callback = $callback;
+
+               if ( $this->dbw->trxLevel() ) {
+                       $this->dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ] );
+               }
        }
 
        public function doUpdate() {
-               $this->dbw->doAtomicSection( $this->fname, $this->callback );
+               if ( $this->callback ) {
+                       $this->dbw->doAtomicSection( $this->fname, $this->callback );
+               }
+       }
+
+       public function cancelOnRollback( $trigger ) {
+               if ( $trigger === IDatabase::TRIGGER_ROLLBACK ) {
+                       $this->callback = null;
+               }
+       }
+
+       public function getOrigin() {
+               return $this->fname;
        }
 }
diff --git a/includes/deferred/AutoCommitUpdate.php b/includes/deferred/AutoCommitUpdate.php
new file mode 100644 (file)
index 0000000..d26cf9d
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * Deferrable Update for closure/callback updates that should use auto-commit mode
+ * @since 1.28
+ */
+class AutoCommitUpdate implements DeferrableUpdate, DeferrableCallback {
+       /** @var IDatabase */
+       private $dbw;
+       /** @var string */
+       private $fname;
+       /** @var callable|null */
+       private $callback;
+
+       /**
+        * @param IDatabase $dbw
+        * @param string $fname Caller name (usually __METHOD__)
+        * @param callable $callback Callback that takes (IDatabase, method name string)
+        */
+       public function __construct( IDatabase $dbw, $fname, callable $callback ) {
+               $this->dbw = $dbw;
+               $this->fname = $fname;
+               $this->callback = $callback;
+
+               if ( $this->dbw->trxLevel() ) {
+                       $this->dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ] );
+               }
+       }
+
+       public function doUpdate() {
+               if ( !$this->callback ) {
+                       return;
+               }
+
+               $autoTrx = $this->dbw->getFlag( DBO_TRX );
+               $this->dbw->clearFlag( DBO_TRX );
+               try {
+                       /** @var Exception $e */
+                       $e = null;
+                       call_user_func_array( $this->callback, [ $this->dbw, $this->fname ] );
+               } catch ( Exception $e ) {
+               }
+               if ( $autoTrx ) {
+                       $this->dbw->setFlag( DBO_TRX );
+               }
+               if ( $e ) {
+                       throw $e;
+               }
+       }
+
+       public function cancelOnRollback( $trigger ) {
+               if ( $trigger === IDatabase::TRIGGER_ROLLBACK ) {
+                       $this->callback = null;
+               }
+       }
+
+       public function getOrigin() {
+               return $this->fname;
+       }
+}
diff --git a/includes/deferred/CallableUpdate.php b/includes/deferred/CallableUpdate.php
deleted file mode 100644 (file)
index 4b19c20..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-/**
- * Deferrable Update for closure/callback
- */
-class MWCallableUpdate implements DeferrableUpdate {
-       /** @var Closure|callable */
-       private $callback;
-
-       /**
-        * @param callable $callback
-        * @throws InvalidArgumentException
-        */
-       public function __construct( $callback ) {
-               if ( !is_callable( $callback ) ) {
-                       throw new InvalidArgumentException( 'Not a valid callback/closure!' );
-               }
-               $this->callback = $callback;
-       }
-
-       public function doUpdate() {
-               call_user_func( $this->callback );
-       }
-}
index 4ce9e62..470086a 100644 (file)
@@ -65,7 +65,7 @@ class CdnCacheUpdate implements DeferrableUpdate, MergeableUpdate {
        /**
         * @param Title $title
         * @return CdnCacheUpdate
-        * @deprecated 1.27
+        * @deprecated since 1.27
         */
        public static function newSimplePurge( Title $title ) {
                return new CdnCacheUpdate( $title->getCdnUrls() );
index 2865461..8d26460 100644 (file)
 /**
  * Abstract base class for update jobs that do something with some secondary
  * data extracted from article.
- *
- * @note subclasses should NOT start or commit transactions in their doUpdate() method,
- *       a transaction will automatically be wrapped around the update. If need be,
- *       subclasses can override the beginTransaction() and commitTransaction() methods.
  */
 abstract class DataUpdate implements DeferrableUpdate {
-       public function __construct() {
-               // noop
-       }
-
-       /**
-        * Begin an appropriate transaction, if any.
-        * This default implementation does nothing.
-        */
-       public function beginTransaction() {
-               // noop
-       }
+       /** @var mixed Result from LBFactory::getEmptyTransactionTicket() */
+       protected $ticket;
 
-       /**
-        * Commit the transaction started via beginTransaction, if any.
-        * This default implementation does nothing.
-        */
-       public function commitTransaction() {
+       public function __construct() {
                // noop
        }
 
        /**
-        * Abort / roll back the transaction started via beginTransaction, if any.
-        * This default implementation does nothing.
+        * @param mixed $ticket Result of getEmptyTransactionTicket()
+        * @since 1.28
         */
-       public function rollbackTransaction() {
-               // noop
+       public function setTransactionTicket( $ticket ) {
+               $this->ticket = $ticket;
        }
 
        /**
         * Convenience method, calls doUpdate() on every DataUpdate in the array.
         *
-        * This methods supports transactions logic by first calling beginTransaction()
-        * on all updates in the array, then calling doUpdate() on each, and, if all goes well,
-        * then calling commitTransaction() on each update. If an error occurs,
-        * rollbackTransaction() will be called on any update object that had beginTransaction()
-        * called but not yet commitTransaction().
-        *
-        * This allows for limited transactional logic across multiple backends for storing
-        * secondary data.
-        *
         * @param DataUpdate[] $updates A list of DataUpdate instances
         * @param string $mode Use "enqueue" to use the job queue when possible [Default: run]
-        * @throws Exception|null
+        * @throws Exception
+        * @deprecated Since 1.28 Use DeferredUpdates::execute()
         */
        public static function runUpdates( array $updates, $mode = 'run' ) {
-               if ( $mode === 'enqueue' ) {
-                       // When possible, push updates as jobs instead of calling doUpdate()
-                       $updates = self::enqueueUpdates( $updates );
-               }
-
-               if ( !count( $updates ) ) {
-                       return; // nothing to do
-               }
-
-               $open_transactions = [];
-               $exception = null;
-
-               try {
-                       // begin transactions
-                       foreach ( $updates as $update ) {
-                               $update->beginTransaction();
-                               $open_transactions[] = $update;
-                       }
-
-                       // do work
-                       foreach ( $updates as $update ) {
-                               $update->doUpdate();
-                       }
-
-                       // commit transactions
-                       while ( count( $open_transactions ) > 0 ) {
-                               $trans = array_pop( $open_transactions );
-                               $trans->commitTransaction();
-                       }
-               } catch ( Exception $ex ) {
-                       $exception = $ex;
-                       wfDebug( "Caught exception, will rethrow after rollback: " .
-                               $ex->getMessage() . "\n" );
-               }
-
-               // rollback remaining transactions
-               while ( count( $open_transactions ) > 0 ) {
-                       $trans = array_pop( $open_transactions );
-                       $trans->rollbackTransaction();
-               }
-
-               if ( $exception ) {
-                       throw $exception; // rethrow after cleanup
-               }
+               DeferredUpdates::execute( $updates, $mode, DeferredUpdates::ALL );
        }
-
-       /**
-        * Enqueue jobs for every DataUpdate that support enqueueUpdate()
-        * and return the remaining DataUpdate objects (those that do not)
-        *
-        * @param DataUpdate[] $updates A list of DataUpdate instances
-        * @return DataUpdate[]
-        * @since 1.27
-        */
-       protected static function enqueueUpdates( array $updates ) {
-               $remaining = [];
-
-               foreach ( $updates as $update ) {
-                       if ( $update instanceof EnqueueableDataUpdate ) {
-                               $spec = $update->getAsJobSpecification();
-                               JobQueueGroup::singleton( $spec['wiki'] )->push( $spec['job'] );
-                       } else {
-                               $remaining[] = $update;
-                       }
-               }
-
-               return $remaining;
-       }
-}
-
-/**
- * Interface that marks a DataUpdate as enqueuable via the JobQueue
- *
- * Such updates must be representable using IJobSpecification, so that
- * they can be serialized into jobs and enqueued for later execution
- *
- * @since 1.27
- */
-interface EnqueueableDataUpdate {
-       /**
-        * @return array (wiki => wiki ID, job => IJobSpecification)
-        */
-       public function getAsJobSpecification();
 }
diff --git a/includes/deferred/DeferrableCallback.php b/includes/deferred/DeferrableCallback.php
new file mode 100644 (file)
index 0000000..2eb0d5d
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+/**
+ * Callback wrapper that has an originating method
+ *
+ * @since 1.28
+ */
+interface DeferrableCallback {
+       /**
+        * @return string Originating method name
+        */
+       function getOrigin();
+}
index 1552777..2b2b2b7 100644 (file)
@@ -19,6 +19,7 @@
  *
  * @file
  */
+use MediaWiki\MediaWikiServices;
 
 /**
  * Class for managing the deferred updates
  * In web request mode, deferred updates can be run at the end of the request, either before or
  * after the HTTP response has been sent. In either case, they run after the DB commit step. If
  * an update runs after the response is sent, it will not block clients. If sent before, it will
- * run synchronously. If such an update works via queueing, it will be more likely to complete by
- * the time the client makes their next request after this one.
+ * run synchronously. These two modes are defined via PRESEND and POSTSEND constants, the latter
+ * being the default for addUpdate() and addCallableUpdate().
  *
- * In CLI mode, updates are only deferred until the current wiki has no DB write transaction
- * active within this request.
+ * Updates that work through this system will be more likely to complete by the time the client
+ * makes their next request after this one than with the JobQueue system.
  *
- * When updates are deferred, they use a FIFO queue (one for pre-send and one for post-send).
+ * In CLI mode, updates run immediately if no DB writes are pending. Otherwise, they run when:
+ *   - a) Any waitForReplication() call if no writes are pending on any DB
+ *   - b) A commit happens on Maintenance::getDB( DB_MASTER ) if no writes are pending on any DB
+ *   - c) EnqueueableDataUpdate tasks may enqueue on commit of Maintenance::getDB( DB_MASTER )
+ *   - d) At the completion of Maintenance::execute()
+ *
+ * When updates are deferred, they go into one two FIFO "top-queues" (one for pre-send and one
+ * for post-send). Updates enqueued *during* doUpdate() of a "top" update go into the "sub-queue"
+ * for that update. After that method finishes, the sub-queue is run until drained. This continues
+ * for each top-queue job until the entire top queue is drained. This happens for the pre-send
+ * top-queue, and later on, the post-send top-queue, in execute().
  *
  * @since 1.19
  */
@@ -42,56 +53,84 @@ class DeferredUpdates {
        /** @var DeferrableUpdate[] Updates to be deferred until after request end */
        private static $postSendUpdates = [];
 
-       const ALL = 0; // all updates
+       const ALL = 0; // all updates; in web requests, use only after flushing the output buffer
        const PRESEND = 1; // for updates that should run before flushing output buffer
        const POSTSEND = 2; // for updates that should run after flushing output buffer
 
+       const BIG_QUEUE_SIZE = 100;
+
+       /** @var array|null Information about the current execute() call or null if not running */
+       private static $executeContext;
+
        /**
-        * Add an update to the deferred list
+        * Add an update to the deferred list to be run later by execute()
+        *
+        * In CLI mode, callback magic will also be used to run updates when safe
         *
         * @param DeferrableUpdate $update Some object that implements doUpdate()
-        * @param integer $type DeferredUpdates constant (PRESEND or POSTSEND) (since 1.27)
+        * @param integer $stage DeferredUpdates constant (PRESEND or POSTSEND) (since 1.27)
         */
-       public static function addUpdate( DeferrableUpdate $update, $type = self::POSTSEND ) {
-               if ( $type === self::PRESEND ) {
+       public static function addUpdate( DeferrableUpdate $update, $stage = self::POSTSEND ) {
+               global $wgCommandLineMode;
+
+               // This is a sub-DeferredUpdate, run it right after its parent update
+               if ( self::$executeContext && self::$executeContext['stage'] >= $stage ) {
+                       self::$executeContext['subqueue'][] = $update;
+                       return;
+               }
+
+               if ( $stage === self::PRESEND ) {
                        self::push( self::$preSendUpdates, $update );
                } else {
                        self::push( self::$postSendUpdates, $update );
                }
+
+               // Try to run the updates now if in CLI mode and no transaction is active.
+               // This covers scripts that don't/barely use the DB but make updates to other stores.
+               if ( $wgCommandLineMode ) {
+                       self::tryOpportunisticExecute( 'run' );
+               }
        }
 
        /**
-        * Add a callable update.  In a lot of cases, we just need a callback/closure,
+        * Add a callable update. In a lot of cases, we just need a callback/closure,
         * defining a new DeferrableUpdate object is not necessary
         *
         * @see MWCallableUpdate::__construct()
         *
         * @param callable $callable
-        * @param integer $type DeferredUpdates constant (PRESEND or POSTSEND) (since 1.27)
+        * @param integer $stage DeferredUpdates constant (PRESEND or POSTSEND) (since 1.27)
+        * @param IDatabase|null $dbw Abort if this DB is rolled back [optional] (since 1.28)
         */
-       public static function addCallableUpdate( $callable, $type = self::POSTSEND ) {
-               self::addUpdate( new MWCallableUpdate( $callable ), $type );
+       public static function addCallableUpdate(
+               $callable, $stage = self::POSTSEND, IDatabase $dbw = null
+       ) {
+               self::addUpdate( new MWCallableUpdate( $callable, wfGetCaller(), $dbw ), $stage );
        }
 
        /**
         * Do any deferred updates and clear the list
         *
         * @param string $mode Use "enqueue" to use the job queue when possible [Default: "run"]
-        * @param integer $type DeferredUpdates constant (PRESEND, POSTSEND, or ALL) (since 1.27)
+        * @param integer $stage DeferredUpdates constant (PRESEND, POSTSEND, or ALL) (since 1.27)
         */
-       public static function doUpdates( $mode = 'run', $type = self::ALL ) {
-               if ( $type === self::ALL || $type == self::PRESEND ) {
-                       self::execute( self::$preSendUpdates, $mode );
+       public static function doUpdates( $mode = 'run', $stage = self::ALL ) {
+               $stageEffective = ( $stage === self::ALL ) ? self::POSTSEND : $stage;
+
+               if ( $stage === self::ALL || $stage === self::PRESEND ) {
+                       self::execute( self::$preSendUpdates, $mode, $stageEffective );
                }
 
-               if ( $type === self::ALL || $type == self::POSTSEND ) {
-                       self::execute( self::$postSendUpdates, $mode );
+               if ( $stage === self::ALL || $stage == self::POSTSEND ) {
+                       self::execute( self::$postSendUpdates, $mode, $stageEffective );
                }
        }
 
+       /**
+        * @param DeferrableUpdate[] $queue
+        * @param DeferrableUpdate $update
+        */
        private static function push( array &$queue, DeferrableUpdate $update ) {
-               global $wgCommandLineMode;
-
                if ( $update instanceof MergeableUpdate ) {
                        $class = get_class( $update ); // fully-qualified class
                        if ( isset( $queue[$class] ) ) {
@@ -104,74 +143,165 @@ class DeferredUpdates {
                } else {
                        $queue[] = $update;
                }
-
-               // CLI scripts may forget to periodically flush these updates,
-               // so try to handle that rather than OOMing and losing them entirely.
-               // Try to run the updates as soon as there is no current wiki transaction.
-               static $waitingOnTrx = false; // de-duplicate callback
-               if ( $wgCommandLineMode && !$waitingOnTrx ) {
-                       $lb = wfGetLB();
-                       $dbw = $lb->getAnyOpenConnection( $lb->getWriterIndex() );
-                       // Do the update as soon as there is no transaction
-                       if ( $dbw && $dbw->trxLevel() ) {
-                               $waitingOnTrx = true;
-                               $dbw->onTransactionIdle( function() use ( &$waitingOnTrx ) {
-                                       DeferredUpdates::doUpdates();
-                                       $waitingOnTrx = false;
-                               } );
-                       } else {
-                               self::doUpdates();
-                       }
-               }
        }
 
-       public static function execute( array &$queue, $mode ) {
-               $stats = \MediaWiki\MediaWikiServices::getInstance()->getStatsdDataFactory();
+       /**
+        * @param DeferrableUpdate[] &$queue List of DeferrableUpdate objects
+        * @param string $mode Use "enqueue" to use the job queue when possible
+        * @param integer $stage Class constant (PRESEND, POSTSEND) (since 1.28)
+        * @throws ErrorPageError Happens on top-level calls
+        * @throws Exception Happens on second-level calls
+        */
+       public static function execute( array &$queue, $mode, $stage ) {
+               $services = MediaWikiServices::getInstance();
+               $stats = $services->getStatsdDataFactory();
+               $lbFactory = $services->getDBLoadBalancerFactory();
                $method = RequestContext::getMain()->getRequest()->getMethod();
 
-               $updates = $queue; // snapshot of queue
-               // Keep doing rounds of updates until none get enqueued
-               while ( count( $updates ) ) {
+               /** @var ErrorPageError $reportableError */
+               $reportableError = null;
+               /** @var DeferrableUpdate[] $updates Snapshot of queue */
+               $updates = $queue;
+
+               // Keep doing rounds of updates until none get enqueued...
+               while ( $updates ) {
                        $queue = []; // clear the queue
-                       /** @var DataUpdate[] $dataUpdates */
-                       $dataUpdates = [];
-                       /** @var DeferrableUpdate[] $otherUpdates */
-                       $otherUpdates = [];
-                       foreach ( $updates as $update ) {
-                               if ( $update instanceof DataUpdate ) {
-                                       $dataUpdates[] = $update;
-                               } else {
-                                       $otherUpdates[] = $update;
+
+                       if ( $mode === 'enqueue' ) {
+                               try {
+                                       // Push enqueuable updates to the job queue and get the rest
+                                       $updates = self::enqueueUpdates( $updates );
+                               } catch ( Exception $e ) {
+                                       // Let other updates have a chance to run if this failed
+                                       MWExceptionHandler::rollbackMasterChangesAndLog( $e );
                                }
-                               $stats->increment( 'deferred_updates.' . $method . '.' . get_class( $update ) );
                        }
 
-                       // Delegate DataUpdate execution to the DataUpdate class
-                       try {
-                               DataUpdate::runUpdates( $dataUpdates, $mode );
-                       } catch ( Exception $e ) {
-                               // Let the other updates occur if these had to rollback
-                               MWExceptionHandler::logException( $e );
+                       // Order will be DataUpdate followed by generic DeferrableUpdate tasks
+                       $updatesByType = [ 'data' => [], 'generic' => [] ];
+                       foreach ( $updates as $du ) {
+                               $updatesByType[$du instanceof DataUpdate ? 'data' : 'generic'][] = $du;
+                               $name = ( $du instanceof DeferrableCallback )
+                                       ? get_class( $du ) . '-' . $du->getOrigin()
+                                       : get_class( $du );
+                               $stats->increment( 'deferred_updates.' . $method . '.' . $name );
                        }
-                       // Execute the non-DataUpdate tasks
-                       foreach ( $otherUpdates as $update ) {
-                               try {
-                                       $update->doUpdate();
-                                       wfGetLBFactory()->commitMasterChanges( __METHOD__ );
-                               } catch ( Exception $e ) {
-                                       // We don't want exceptions thrown during deferred updates to
-                                       // be reported to the user since the output is already sent
-                                       if ( !$e instanceof ErrorPageError ) {
-                                               MWExceptionHandler::logException( $e );
+
+                       // Execute all remaining tasks...
+                       foreach ( $updatesByType as $updatesForType ) {
+                               foreach ( $updatesForType as $update ) {
+                                       self::$executeContext = [
+                                               'update' => $update,
+                                               'stage' => $stage,
+                                               'subqueue' => []
+                                       ];
+                                       /** @var DeferrableUpdate $update */
+                                       $guiError = self::runUpdate( $update, $lbFactory, $stage );
+                                       $reportableError = $reportableError ?: $guiError;
+                                       // Do the subqueue updates for $update until there are none
+                                       while ( self::$executeContext['subqueue'] ) {
+                                               $subUpdate = reset( self::$executeContext['subqueue'] );
+                                               $firstKey = key( self::$executeContext['subqueue'] );
+                                               unset( self::$executeContext['subqueue'][$firstKey] );
+
+                                               $guiError = self::runUpdate( $subUpdate, $lbFactory, $stage );
+                                               $reportableError = $reportableError ?: $guiError;
                                        }
-                                       // Make sure incomplete transactions are not committed and end any
-                                       // open atomic sections so that other DB updates have a chance to run
-                                       wfGetLBFactory()->rollbackMasterChanges( __METHOD__ );
+                                       self::$executeContext = null;
                                }
                        }
 
                        $updates = $queue; // new snapshot of queue (check for new entries)
                }
+
+               if ( $reportableError ) {
+                       throw $reportableError; // throw the first of any GUI errors
+               }
+       }
+
+       /**
+        * @param DeferrableUpdate $update
+        * @param LBFactory $lbFactory
+        * @param integer $stage
+        * @return ErrorPageError|null
+        */
+       private static function runUpdate( DeferrableUpdate $update, LBFactory $lbFactory, $stage ) {
+               $guiError = null;
+               try {
+                       $fnameTrxOwner = get_class( $update ) . '::doUpdate';
+                       $lbFactory->beginMasterChanges( $fnameTrxOwner );
+                       $update->doUpdate();
+                       $lbFactory->commitMasterChanges( $fnameTrxOwner );
+               } catch ( Exception $e ) {
+                       // Reporting GUI exceptions does not work post-send
+                       if ( $e instanceof ErrorPageError && $stage === self::PRESEND ) {
+                               $guiError = $e;
+                       }
+                       MWExceptionHandler::rollbackMasterChangesAndLog( $e );
+               }
+
+               return $guiError;
+       }
+
+       /**
+        * Run all deferred updates immediately if there are no DB writes active
+        *
+        * If $mode is 'run' but there are busy databates, EnqueueableDataUpdate
+        * tasks will be enqueued anyway for the sake of progress.
+        *
+        * @param string $mode Use "enqueue" to use the job queue when possible
+        * @return bool Whether updates were allowed to run
+        * @since 1.28
+        */
+       public static function tryOpportunisticExecute( $mode = 'run' ) {
+               // execute() loop is already running
+               if ( self::$executeContext ) {
+                       return false;
+               }
+
+               // Avoiding running updates without them having outer scope
+               if ( !self::getBusyDbConnections() ) {
+                       self::doUpdates( $mode );
+                       return true;
+               }
+
+               if ( self::pendingUpdatesCount() >= self::BIG_QUEUE_SIZE ) {
+                       // If we cannot run the updates with outer transaction context, try to
+                       // at least enqueue all the updates that support queueing to job queue
+                       self::$preSendUpdates = self::enqueueUpdates( self::$preSendUpdates );
+                       self::$postSendUpdates = self::enqueueUpdates( self::$postSendUpdates );
+               }
+
+               return !self::pendingUpdatesCount();
+       }
+
+       /**
+        * Enqueue a job for each EnqueueableDataUpdate item and return the other items
+        *
+        * @param DeferrableUpdate[] $updates A list of deferred update instances
+        * @return DeferrableUpdate[] Remaining updates that do not support being queued
+        */
+       private static function enqueueUpdates( array $updates ) {
+               $remaining = [];
+
+               foreach ( $updates as $update ) {
+                       if ( $update instanceof EnqueueableDataUpdate ) {
+                               $spec = $update->getAsJobSpecification();
+                               JobQueueGroup::singleton( $spec['wiki'] )->push( $spec['job'] );
+                       } else {
+                               $remaining[] = $update;
+                       }
+               }
+
+               return $remaining;
+       }
+
+       /**
+        * @return integer Number of enqueued updates
+        * @since 1.28
+        */
+       public static function pendingUpdatesCount() {
+               return count( self::$preSendUpdates ) + count( self::$postSendUpdates );
        }
 
        /**
@@ -182,4 +312,22 @@ class DeferredUpdates {
                self::$preSendUpdates = [];
                self::$postSendUpdates = [];
        }
+
+       /**
+        * @return IDatabase[] Connection where commit() cannot be called yet
+        */
+       private static function getBusyDbConnections() {
+               $connsBusy = [];
+
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $lbFactory->forEachLB( function ( LoadBalancer $lb ) use ( &$connsBusy ) {
+                       $lb->forEachOpenMasterConnection( function ( IDatabase $conn ) use ( &$connsBusy ) {
+                               if ( $conn->writesOrCallbacksPending() || $conn->explicitTrxActive() ) {
+                                       $connsBusy[] = $conn;
+                               }
+                       } );
+               } );
+
+               return $connsBusy;
+       }
 }
diff --git a/includes/deferred/EnqueueableDataUpdate.php b/includes/deferred/EnqueueableDataUpdate.php
new file mode 100644 (file)
index 0000000..ffeb740
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Interface that marks a DataUpdate as enqueuable via the JobQueue
+ *
+ * Such updates must be representable using IJobSpecification, so that
+ * they can be serialized into jobs and enqueued for later execution
+ *
+ * @since 1.27
+ */
+interface EnqueueableDataUpdate {
+       /**
+        * @return array (wiki => wiki ID, job => IJobSpecification)
+        */
+       public function getAsJobSpecification();
+}
index a7c39ca..4159166 100644 (file)
  *
  * @file
  */
+use MediaWiki\MediaWikiServices;
+
 /**
  * Update object handling the cleanup of links tables after a page was deleted.
  **/
-class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
+class LinksDeletionUpdate extends DataUpdate implements EnqueueableDataUpdate {
        /** @var WikiPage */
        protected $page;
        /** @var integer */
@@ -30,6 +32,9 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
        /** @var string */
        protected $timestamp;
 
+       /** @var IDatabase */
+       private $db;
+
        /**
         * @param WikiPage $page Page we are updating
         * @param integer|null $pageId ID of the page we are updating [optional]
@@ -37,7 +42,7 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
         * @throws MWException
         */
        function __construct( WikiPage $page, $pageId = null, $timestamp = null ) {
-               parent::__construct( false ); // no implicit transaction
+               parent::__construct();
 
                $this->page = $page;
                if ( $pageId ) {
@@ -52,20 +57,28 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
        }
 
        public function doUpdate() {
-               $config = RequestContext::getMain()->getConfig();
+               $services = MediaWikiServices::getInstance();
+               $config = $services->getMainConfig();
+               $lbFactory = $services->getDBLoadBalancerFactory();
                $batchSize = $config->get( 'UpdateRowsPerQuery' );
 
                // Page may already be deleted, so don't just getId()
                $id = $this->pageId;
-               // Make sure all links update threads see the changes of each other.
-               // This handles the case when updates have to batched into several COMMITs.
-               $scopedLock = LinksUpdate::acquirePageLock( $this->mDb, $id );
+
+               if ( $this->ticket ) {
+                       // Make sure all links update threads see the changes of each other.
+                       // This handles the case when updates have to batched into several COMMITs.
+                       $scopedLock = LinksUpdate::acquirePageLock( $this->getDB(), $id );
+               }
+
+               $title = $this->page->getTitle();
+               $dbw = $this->getDB(); // convenience
 
                // Delete restrictions for it
-               $this->mDb->delete( 'page_restrictions', [ 'pr_page' => $id ], __METHOD__ );
+               $dbw->delete( 'page_restrictions', [ 'pr_page' => $id ], __METHOD__ );
 
                // Fix category table counts
-               $cats = $this->mDb->selectFieldValues(
+               $cats = $dbw->selectFieldValues(
                        'categorylinks',
                        'cl_to',
                        [ 'cl_from' => $id ],
@@ -75,13 +88,28 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                foreach ( $catBatches as $catBatch ) {
                        $this->page->updateCategoryCounts( [], $catBatch, $id );
                        if ( count( $catBatches ) > 1 ) {
-                               $this->mDb->commit( __METHOD__, 'flush' );
-                               wfGetLBFactory()->waitForReplication( [ 'wiki' => $this->mDb->getWikiID() ] );
+                               $lbFactory->commitAndWaitForReplication(
+                                       __METHOD__, $this->ticket, [ 'wiki' => $dbw->getWikiID() ]
+                               );
+                       }
+               }
+
+               // Refresh the category table entry if it seems to have no pages. Check
+               // master for the most up-to-date cat_pages count.
+               if ( $title->getNamespace() === NS_CATEGORY ) {
+                       $row = $dbw->selectRow(
+                               'category',
+                               [ 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ],
+                               [ 'cat_title' => $title->getDBkey(), 'cat_pages <= 0' ],
+                               __METHOD__
+                       );
+                       if ( $row ) {
+                               Category::newFromRow( $row, $title )->refreshCounts();
                        }
                }
 
                // If using cascading deletes, we can skip some explicit deletes
-               if ( !$this->mDb->cascadingDeletes() ) {
+               if ( !$dbw->cascadingDeletes() ) {
                        // Delete outgoing links
                        $this->batchDeleteByPK(
                                'pagelinks',
@@ -126,15 +154,14 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                                $batchSize
                        );
                        // Delete any redirect entry or page props entries
-                       $this->mDb->delete( 'redirect', [ 'rd_from' => $id ], __METHOD__ );
-                       $this->mDb->delete( 'page_props', [ 'pp_page' => $id ], __METHOD__ );
+                       $dbw->delete( 'redirect', [ 'rd_from' => $id ], __METHOD__ );
+                       $dbw->delete( 'page_props', [ 'pp_page' => $id ], __METHOD__ );
                }
 
                // If using cleanup triggers, we can skip some manual deletes
-               if ( !$this->mDb->cleanupTriggers() ) {
-                       $title = $this->page->getTitle();
+               if ( !$dbw->cleanupTriggers() ) {
                        // Find recentchanges entries to clean up...
-                       $rcIdsForTitle = $this->mDb->selectFieldValues(
+                       $rcIdsForTitle = $dbw->selectFieldValues(
                                'recentchanges',
                                'rc_id',
                                [
@@ -142,11 +169,11 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                                        'rc_namespace' => $title->getNamespace(),
                                        'rc_title' => $title->getDBkey(),
                                        'rc_timestamp < ' .
-                                               $this->mDb->addQuotes( $this->mDb->timestamp( $this->timestamp ) )
+                                               $dbw->addQuotes( $dbw->timestamp( $this->timestamp ) )
                                ],
                                __METHOD__
                        );
-                       $rcIdsForPage = $this->mDb->selectFieldValues(
+                       $rcIdsForPage = $dbw->selectFieldValues(
                                'recentchanges',
                                'rc_id',
                                [ 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ],
@@ -156,31 +183,34 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                        // T98706: delete by PK to avoid lock contention with RC delete log insertions
                        $rcIdBatches = array_chunk( array_merge( $rcIdsForTitle, $rcIdsForPage ), $batchSize );
                        foreach ( $rcIdBatches as $rcIdBatch ) {
-                               $this->mDb->delete( 'recentchanges', [ 'rc_id' => $rcIdBatch ], __METHOD__ );
+                               $dbw->delete( 'recentchanges', [ 'rc_id' => $rcIdBatch ], __METHOD__ );
                                if ( count( $rcIdBatches ) > 1 ) {
-                                       $this->mDb->commit( __METHOD__, 'flush' );
-                                       wfGetLBFactory()->waitForReplication( [ 'wiki' => $this->mDb->getWikiID() ] );
+                                       $lbFactory->commitAndWaitForReplication(
+                                               __METHOD__, $this->ticket, [ 'wiki' => $dbw->getWikiID() ]
+                                       );
                                }
                        }
                }
 
-               $this->mDb->onTransactionIdle( function() use ( &$scopedLock ) {
-                       // Release the lock *after* the final COMMIT for correctness
-                       ScopedCallback::consume( $scopedLock );
-               } );
+               // Commit and release the lock (if set)
+               ScopedCallback::consume( $scopedLock );
        }
 
        private function batchDeleteByPK( $table, array $conds, array $pk, $bSize ) {
-               $dbw = $this->mDb; // convenience
+               $services = MediaWikiServices::getInstance();
+               $lbFactory = $services->getDBLoadBalancerFactory();
+               $dbw = $this->getDB(); // convenience
+
                $res = $dbw->select( $table, $pk, $conds, __METHOD__ );
 
                $pkDeleteConds = [];
                foreach ( $res as $row ) {
-                       $pkDeleteConds[] = $this->mDb->makeList( (array)$row, LIST_AND );
+                       $pkDeleteConds[] = $dbw->makeList( (array)$row, LIST_AND );
                        if ( count( $pkDeleteConds ) >= $bSize ) {
                                $dbw->delete( $table, $dbw->makeList( $pkDeleteConds, LIST_OR ), __METHOD__ );
-                               $dbw->commit( __METHOD__, 'flush' );
-                               wfGetLBFactory()->waitForReplication( [ 'wiki' => $dbw->getWikiID() ] );
+                               $lbFactory->commitAndWaitForReplication(
+                                       __METHOD__, $this->ticket, [ 'wiki' => $dbw->getWikiID() ]
+                               );
                                $pkDeleteConds = [];
                        }
                }
@@ -190,9 +220,17 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate
                }
        }
 
+       protected function getDB() {
+               if ( !$this->db ) {
+                       $this->db = wfGetDB( DB_MASTER );
+               }
+
+               return $this->db;
+       }
+
        public function getAsJobSpecification() {
                return [
-                       'wiki' => $this->mDb->getWikiID(),
+                       'wiki' => $this->getDB()->getWikiID(),
                        'job'  => new JobSpecification(
                                'deleteLinks',
                                [ 'pageId' => $this->pageId, 'timestamp' => $this->timestamp ],
index d4a61fa..e24a9c0 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\MediaWikiServices;
+
 /**
  * Class the manages updates of *_link tables as well as similar extension-managed tables
  *
@@ -27,7 +29,7 @@
  *
  * See docs/deferred.txt
  */
-class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
+class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate {
        // @todo make members protected, but make sure extensions don't break
 
        /** @var int Page ID of the article linked from */
@@ -79,11 +81,24 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         */
        private $linkDeletions = null;
 
+       /**
+        * @var null|array Added properties if calculated.
+        */
+       private $propertyInsertions = null;
+
+       /**
+        * @var null|array Deleted properties if calculated.
+        */
+       private $propertyDeletions = null;
+
        /**
         * @var User|null
         */
        private $user;
 
+       /** @var IDatabase */
+       private $db;
+
        /**
         * Constructor
         *
@@ -93,8 +108,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @throws MWException
         */
        function __construct( Title $title, ParserOutput $parserOutput, $recursive = true ) {
-               // Implicit transactions are disabled as they interfere with batching
-               parent::__construct( false );
+               parent::__construct();
 
                $this->mTitle = $title;
                $this->mId = $title->getArticleID( Title::GAID_FOR_UPDATE );
@@ -148,17 +162,19 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @note: this is managed by DeferredUpdates::execute(). Do not run this in a transaction.
         */
        public function doUpdate() {
-               // Make sure all links update threads see the changes of each other.
-               // This handles the case when updates have to batched into several COMMITs.
-               $scopedLock = self::acquirePageLock( $this->mDb, $this->mId );
+               if ( $this->ticket ) {
+                       // Make sure all links update threads see the changes of each other.
+                       // This handles the case when updates have to batched into several COMMITs.
+                       $scopedLock = self::acquirePageLock( $this->getDB(), $this->mId );
+               }
 
                Hooks::run( 'LinksUpdate', [ &$this ] );
                $this->doIncrementalUpdate();
 
-               // Commit and release the lock
+               // Commit and release the lock (if set)
                ScopedCallback::consume( $scopedLock );
                // Run post-commit hooks without DBO_TRX
-               $this->mDb->onTransactionIdle( function() {
+               $this->getDB()->onTransactionIdle( function() {
                        Hooks::run( 'LinksUpdateComplete', [ &$this ] );
                } );
        }
@@ -168,18 +184,16 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         *
         * @param IDatabase $dbw
         * @param integer $pageId
-        * @return ScopedCallback|null Returns null on failure
+        * @param string $why One of (job, atomicity)
+        * @return ScopedCallback
         * @throws RuntimeException
         * @since 1.27
         */
-       public static function acquirePageLock( IDatabase $dbw, $pageId ) {
-               $scopedLock = $dbw->getScopedLockAndFlush(
-                       "LinksUpdate:pageid:$pageId",
-                       __METHOD__,
-                       15
-               );
+       public static function acquirePageLock( IDatabase $dbw, $pageId, $why = 'atomicity' ) {
+               $key = "LinksUpdate:$why:pageid:$pageId";
+               $scopedLock = $dbw->getScopedLockAndFlush( $key, __METHOD__, 15 );
                if ( !$scopedLock ) {
-                       throw new RuntimeException( "Could not acquire lock on page #$pageId." );
+                       throw new RuntimeException( "Could not acquire lock '$key'." );
                }
 
                return $scopedLock;
@@ -236,12 +250,13 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
 
                # Page properties
                $existing = $this->getExistingProperties();
-               $propertiesDeletes = $this->getPropertyDeletions( $existing );
-               $this->incrTableUpdate( 'page_props', 'pp', $propertiesDeletes,
+               $this->propertyDeletions = $this->getPropertyDeletions( $existing );
+               $this->incrTableUpdate( 'page_props', 'pp', $this->propertyDeletions,
                        $this->getPropertyInsertions( $existing ) );
 
                # Invalidate the necessary pages
-               $changed = $propertiesDeletes + array_diff_assoc( $this->mProperties, $existing );
+               $this->propertyInsertions = array_diff_assoc( $this->mProperties, $existing );
+               $changed = $this->propertyDeletions + $this->propertyInsertions;
                $this->invalidateProperties( $changed );
 
                # Refresh links of all pages including this page
@@ -306,7 +321,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $cats
         */
        function invalidateCategories( $cats ) {
-               $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) );
+               PurgeJobUtils::invalidatePages( $this->getDB(), NS_CATEGORY, array_keys( $cats ) );
        }
 
        /**
@@ -325,7 +340,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $images
         */
        function invalidateImageDescriptions( $images ) {
-               $this->invalidatePages( NS_FILE, array_keys( $images ) );
+               PurgeJobUtils::invalidatePages( $this->getDB(), NS_FILE, array_keys( $images ) );
        }
 
        /**
@@ -336,7 +351,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $insertions Rows to insert
         */
        private function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
-               $bSize = RequestContext::getMain()->getConfig()->get( 'UpdateRowsPerQuery' );
+               $services = MediaWikiServices::getInstance();
+               $bSize = $services->getMainConfig()->get( 'UpdateRowsPerQuery' );
+               $factory = $services->getDBLoadBalancerFactory();
 
                if ( $table === 'page_props' ) {
                        $fromField = 'pp_page';
@@ -368,7 +385,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                        foreach ( $deletionBatches as $deletionBatch ) {
                                $deleteWheres[] = [
                                        $fromField => $this->mId,
-                                       $this->mDb->makeWhereFrom2d( $deletionBatch, $baseKey, "{$prefix}_title" )
+                                       $this->getDB()->makeWhereFrom2d( $deletionBatch, $baseKey, "{$prefix}_title" )
                                ];
                        }
                } else {
@@ -387,16 +404,18 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                }
 
                foreach ( $deleteWheres as $deleteWhere ) {
-                       $this->mDb->delete( $table, $deleteWhere, __METHOD__ );
-                       $this->mDb->commit( __METHOD__, 'flush' );
-                       wfGetLBFactory()->waitForReplication( [ 'wiki' => $this->mDb->getWikiID() ] );
+                       $this->getDB()->delete( $table, $deleteWhere, __METHOD__ );
+                       $factory->commitAndWaitForReplication(
+                               __METHOD__, $this->ticket, [ 'wiki' => $this->getDB()->getWikiID() ]
+                       );
                }
 
                $insertBatches = array_chunk( $insertions, $bSize );
                foreach ( $insertBatches as $insertBatch ) {
-                       $this->mDb->insert( $table, $insertBatch, __METHOD__, 'IGNORE' );
-                       $this->mDb->commit( __METHOD__, 'flush' );
-                       wfGetLBFactory()->waitForReplication( [ 'wiki' => $this->mDb->getWikiID() ] );
+                       $this->getDB()->insert( $table, $insertBatch, __METHOD__, 'IGNORE' );
+                       $factory->commitAndWaitForReplication(
+                               __METHOD__, $this->ticket, [ 'wiki' => $this->getDB()->getWikiID() ]
+                       );
                }
 
                if ( count( $insertions ) ) {
@@ -482,7 +501,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                foreach ( $diffs as $url => $dummy ) {
                        foreach ( wfMakeUrlIndexes( $url ) as $index ) {
                                $arr[] = [
-                                       'el_id' => $this->mDb->nextSequenceValue( 'externallinks_el_id_seq' ),
+                                       'el_id' => $this->getDB()->nextSequenceValue( 'externallinks_el_id_seq' ),
                                        'el_from' => $this->mId,
                                        'el_to' => $url,
                                        'el_index' => $index,
@@ -528,7 +547,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                                'cl_from' => $this->mId,
                                'cl_to' => $name,
                                'cl_sortkey' => $sortkey,
-                               'cl_timestamp' => $this->mDb->timestamp(),
+                               'cl_timestamp' => $this->getDB()->timestamp(),
                                'cl_sortkey_prefix' => $prefix,
                                'cl_collation' => $wgCategoryCollation,
                                'cl_type' => $type,
@@ -766,8 +785,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingLinks() {
-               $res = $this->mDb->select( 'pagelinks', [ 'pl_namespace', 'pl_title' ],
-                       [ 'pl_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'pagelinks', [ 'pl_namespace', 'pl_title' ],
+                       [ 'pl_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        if ( !isset( $arr[$row->pl_namespace] ) ) {
@@ -785,8 +804,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingTemplates() {
-               $res = $this->mDb->select( 'templatelinks', [ 'tl_namespace', 'tl_title' ],
-                       [ 'tl_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'templatelinks', [ 'tl_namespace', 'tl_title' ],
+                       [ 'tl_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        if ( !isset( $arr[$row->tl_namespace] ) ) {
@@ -804,8 +823,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingImages() {
-               $res = $this->mDb->select( 'imagelinks', [ 'il_to' ],
-                       [ 'il_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'imagelinks', [ 'il_to' ],
+                       [ 'il_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->il_to] = 1;
@@ -820,8 +839,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingExternals() {
-               $res = $this->mDb->select( 'externallinks', [ 'el_to' ],
-                       [ 'el_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'externallinks', [ 'el_to' ],
+                       [ 'el_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->el_to] = 1;
@@ -836,8 +855,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingCategories() {
-               $res = $this->mDb->select( 'categorylinks', [ 'cl_to', 'cl_sortkey_prefix' ],
-                       [ 'cl_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'categorylinks', [ 'cl_to', 'cl_sortkey_prefix' ],
+                       [ 'cl_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->cl_to] = $row->cl_sortkey_prefix;
@@ -853,8 +872,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingInterlangs() {
-               $res = $this->mDb->select( 'langlinks', [ 'll_lang', 'll_title' ],
-                       [ 'll_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'langlinks', [ 'll_lang', 'll_title' ],
+                       [ 'll_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->ll_lang] = $row->ll_title;
@@ -867,9 +886,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * Get an array of existing inline interwiki links, as a 2-D array
         * @return array (prefix => array(dbkey => 1))
         */
-       protected function getExistingInterwikis() {
-               $res = $this->mDb->select( 'iwlinks', [ 'iwl_prefix', 'iwl_title' ],
-                       [ 'iwl_from' => $this->mId ], __METHOD__, $this->mOptions );
+       private function getExistingInterwikis() {
+               $res = $this->getDB()->select( 'iwlinks', [ 'iwl_prefix', 'iwl_title' ],
+                       [ 'iwl_from' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        if ( !isset( $arr[$row->iwl_prefix] ) ) {
@@ -887,8 +906,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array Array of property names and values
         */
        private function getExistingProperties() {
-               $res = $this->mDb->select( 'page_props', [ 'pp_propname', 'pp_value' ],
-                       [ 'pp_page' => $this->mId ], __METHOD__, $this->mOptions );
+               $res = $this->getDB()->select( 'page_props', [ 'pp_propname', 'pp_value' ],
+                       [ 'pp_page' => $this->mId ], __METHOD__ );
                $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->pp_propname] = $row->pp_value;
@@ -1015,21 +1034,52 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                return $result;
        }
 
+       /**
+        * Fetch page properties added by this LinksUpdate.
+        * Only available after the update is complete.
+        * @since 1.28
+        * @return null|array
+        */
+       public function getAddedProperties() {
+               return $this->propertyInsertions;
+       }
+
+       /**
+        * Fetch page properties removed by this LinksUpdate.
+        * Only available after the update is complete.
+        * @since 1.28
+        * @return null|array
+        */
+       public function getRemovedProperties() {
+               return $this->propertyDeletions;
+       }
+
        /**
         * Update links table freshness
         */
-       protected function updateLinksTimestamp() {
+       private function updateLinksTimestamp() {
                if ( $this->mId ) {
                        // The link updates made here only reflect the freshness of the parser output
                        $timestamp = $this->mParserOutput->getCacheTime();
-                       $this->mDb->update( 'page',
-                               [ 'page_links_updated' => $this->mDb->timestamp( $timestamp ) ],
+                       $this->getDB()->update( 'page',
+                               [ 'page_links_updated' => $this->getDB()->timestamp( $timestamp ) ],
                                [ 'page_id' => $this->mId ],
                                __METHOD__
                        );
                }
        }
 
+       /**
+        * @return IDatabase
+        */
+       private function getDB() {
+               if ( !$this->db ) {
+                       $this->db = wfGetDB( DB_MASTER );
+               }
+
+               return $this->db;
+       }
+
        public function getAsJobSpecification() {
                if ( $this->user ) {
                        $userInfo = [
@@ -1047,7 +1097,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                }
 
                return [
-                       'wiki' => $this->mDb->getWikiID(),
+                       'wiki' => $this->getDB()->getWikiID(),
                        'job'  => new JobSpecification(
                                'refreshLinksPrioritized',
                                [
diff --git a/includes/deferred/MWCallableUpdate.php b/includes/deferred/MWCallableUpdate.php
new file mode 100644 (file)
index 0000000..47b162c
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * Deferrable Update for closure/callback
+ */
+class MWCallableUpdate implements DeferrableUpdate, DeferrableCallback {
+       /** @var callable|null */
+       private $callback;
+       /** @var string */
+       private $fname;
+
+       /**
+        * @param callable $callback
+        * @param string $fname Calling method
+        * @param IDatabase|null $dbw Abort if this DB is rolled back [optional] (since 1.28)
+        */
+       public function __construct( callable $callback, $fname = 'unknown', IDatabase $dbw = null ) {
+               $this->callback = $callback;
+               $this->fname = $fname;
+
+               if ( $dbw && $dbw->trxLevel() ) {
+                       $dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ] );
+               }
+       }
+
+       public function doUpdate() {
+               if ( $this->callback ) {
+                       call_user_func( $this->callback );
+               }
+       }
+
+       public function cancelOnRollback( $trigger ) {
+               if ( $trigger === IDatabase::TRIGGER_ROLLBACK ) {
+                       $this->callback = null;
+               }
+       }
+
+       public function getOrigin() {
+               return $this->fname;
+       }
+}
index 5a62185..d8bc35b 100644 (file)
@@ -74,7 +74,7 @@ class SiteStatsUpdate implements DeferrableUpdate {
                        $this->doUpdatePendingDeltas();
                } else {
                        // Need a separate transaction because this a global lock
-                       wfGetDB( DB_MASTER )->onTransactionIdle( [ $this, 'tryDBUpdateInternal' ] );
+                       DeferredUpdates::addCallableUpdate( [ $this, 'tryDBUpdateInternal' ] );
                }
        }
 
@@ -122,6 +122,9 @@ class SiteStatsUpdate implements DeferrableUpdate {
                        // Commit the updates and unlock the table
                        $dbw->unlock( $lockKey, __METHOD__ );
                }
+
+               // Invalid cache used by parser functions
+               SiteStats::unload();
        }
 
        /**
@@ -130,7 +133,7 @@ class SiteStatsUpdate implements DeferrableUpdate {
         */
        public static function cacheUpdate( $dbw ) {
                global $wgActiveUserDays;
-               $dbr = wfGetDB( DB_SLAVE, 'vslow' );
+               $dbr = wfGetDB( DB_REPLICA, 'vslow' );
                # Get non-bot users than did some recent action other than making accounts.
                # If account creation is included, the number gets inflated ~20+ fold on enwiki.
                $activeUsers = $dbr->selectField(
@@ -152,6 +155,9 @@ class SiteStatsUpdate implements DeferrableUpdate {
                        __METHOD__
                );
 
+               // Invalid cache used by parser functions
+               SiteStats::unload();
+
                return $activeUsers;
        }
 
index 9740cbe..25e8841 100644 (file)
  */
 
 /**
- * Abstract base class for update jobs that put some secondary data extracted
- * from article content into the database.
- *
- * @note subclasses should NOT start or commit transactions in their doUpdate() method,
- *       a transaction will automatically be wrapped around the update. Starting another
- *       one would break the outer transaction bracket. If need be, subclasses can override
- *       the beginTransaction() and commitTransaction() methods.
+ * @deprecated Since 1.28 Use DataUpdate directly, injecting the database
  */
 abstract class SqlDataUpdate extends DataUpdate {
        /** @var IDatabase Database connection reference */
        protected $mDb;
-
        /** @var array SELECT options to be used (array) */
        protected $mOptions = [];
 
-       /** @var bool Whether a transaction is open on this object (internal use only!) */
-       private $mHasTransaction;
-
-       /** @var bool Whether this update should be wrapped in a transaction */
-       protected $mUseTransaction;
-
-       /**
-        * Constructor
-        *
-        * @param bool $withTransaction Whether this update should be wrapped in a
-        *   transaction (default: true). A transaction is only started if no
-        *   transaction is already in progress, see beginTransaction() for details.
-        */
-       public function __construct( $withTransaction = true ) {
+       public function __construct() {
                parent::__construct();
 
                $this->mDb = wfGetLB()->getLazyConnectionRef( DB_MASTER );
-
-               $this->mWithTransaction = $withTransaction;
-               $this->mHasTransaction = false;
-       }
-
-       /**
-        * Begin a database transaction, if $withTransaction was given as true in
-        * the constructor for this SqlDataUpdate.
-        *
-        * Because nested transactions are not supported by the Database class,
-        * this implementation checks Database::trxLevel() and only opens a
-        * transaction if none is already active.
-        */
-       public function beginTransaction() {
-               if ( !$this->mWithTransaction ) {
-                       return;
-               }
-
-               // NOTE: nested transactions are not supported, only start a transaction if none is open
-               if ( $this->mDb->trxLevel() === 0 ) {
-                       $this->mDb->begin( get_class( $this ) . '::beginTransaction' );
-                       $this->mHasTransaction = true;
-               }
-       }
-
-       /**
-        * Commit the database transaction started via beginTransaction (if any).
-        */
-       public function commitTransaction() {
-               if ( $this->mHasTransaction ) {
-                       $this->mDb->commit( get_class( $this ) . '::commitTransaction' );
-                       $this->mHasTransaction = false;
-               }
-       }
-
-       /**
-        * Abort the database transaction started via beginTransaction (if any).
-        */
-       public function abortTransaction() {
-               if ( $this->mHasTransaction ) { // XXX: actually... maybe always?
-                       $this->mDb->rollback( get_class( $this ) . '::abortTransaction' );
-                       $this->mHasTransaction = false;
-               }
-       }
-
-       /**
-        * Invalidate the cache of a list of pages from a single namespace.
-        * This is intended for use by subclasses.
-        *
-        * @param int $namespace Namespace number
-        * @param array $dbkeys
-        */
-       protected function invalidatePages( $namespace, array $dbkeys ) {
-               if ( $dbkeys === [] ) {
-                       return;
-               }
-
-               $dbw = $this->mDb;
-               $dbw->onTransactionPreCommitOrIdle( function() use ( $dbw, $namespace, $dbkeys ) {
-                       /**
-                        * Determine which pages need to be updated
-                        * This is necessary to prevent the job queue from smashing the DB with
-                        * large numbers of concurrent invalidations of the same page
-                        */
-                       $now = $dbw->timestamp();
-                       $ids = $dbw->selectFieldValues( 'page',
-                               'page_id',
-                               [
-                                       'page_namespace' => $namespace,
-                                       'page_title' => $dbkeys,
-                                       'page_touched < ' . $dbw->addQuotes( $now )
-                               ],
-                               __METHOD__
-                       );
-
-                       if ( $ids === [] ) {
-                               return;
-                       }
-
-                       /**
-                        * Do the update
-                        * We still need the page_touched condition, in case the row has changed since
-                        * the non-locking select above.
-                        */
-                       $dbw->update( 'page',
-                               [ 'page_touched' => $now ],
-                               [
-                                       'page_id' => $ids,
-                                       'page_touched < ' . $dbw->addQuotes( $now )
-                               ], __METHOD__
-                       );
-               } );
        }
 }
index c5e94c5..1537535 100644 (file)
  * @ingroup DifferenceEngine
  */
 
-/**
- * Constant to indicate diff cache compatibility.
- * Bump this when changing the diff formatting in a way that
- * fixes important bugs or such to force cached diff views to
- * clear.
- */
+// Deprecated, use class constant instead
 define( 'MW_DIFF_VERSION', '1.11a' );
 
 /**
@@ -34,6 +29,13 @@ define( 'MW_DIFF_VERSION', '1.11a' );
  * @ingroup DifferenceEngine
  */
 class DifferenceEngine extends ContextSource {
+       /**
+        * Constant to indicate diff cache compatibility.
+        * Bump this when changing the diff formatting in a way that
+        * fixes important bugs or such to force cached diff views to
+        * clear.
+        */
+       const DIFF_VERSION = MW_DIFF_VERSION;
 
        /** @var int */
        public $mOldid;
@@ -178,7 +180,7 @@ class DifferenceEngine extends ContextSource {
         */
        public function deletedLink( $id ) {
                if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $row = $dbr->selectRow( 'archive', '*',
                                [ 'ar_rev_id' => $id ],
                                __METHOD__ );
@@ -526,7 +528,7 @@ class DifferenceEngine extends ContextSource {
                        RecentChange::isInRCLifespan( $this->mNewRev->getTimestamp(), 21600 )
                ) {
                        // Look for an unpatrolled change corresponding to this diff
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                        $change = RecentChange::newFromConds(
                                [
                                        'rc_timestamp' => $db->timestamp( $this->mNewRev->getTimestamp() ),
@@ -638,7 +640,7 @@ class DifferenceEngine extends ContextSource {
                                # WikiPage::getParserOutput() should not return false, but just in case
                                if ( $parserOutput ) {
                                        // Allow extensions to change parser output here
-                                       if ( !Hooks::run( 'DifferenceEngineRenderRevisionAddParserOutput', [ $this, $out, $parserOutput, $wikiPage ] ) ) {
+                                       if ( Hooks::run( 'DifferenceEngineRenderRevisionAddParserOutput', [ $this, $out, $parserOutput, $wikiPage ] ) ) {
                                                $out->addParserOutput( $parserOutput );
                                        }
                                }
@@ -693,10 +695,10 @@ class DifferenceEngine extends ContextSource {
        }
 
        /**
-        * Add style sheets and supporting JS for diff display.
+        * Add style sheets for diff display.
         */
        public function showDiffStyle() {
-               $this->getOutput()->addModuleStyles( 'mediawiki.action.history.diff' );
+               $this->getOutput()->addModuleStyles( 'mediawiki.diff.styles' );
        }
 
        /**
@@ -748,7 +750,7 @@ class DifferenceEngine extends ContextSource {
                if ( $this->mOldRev === false || ( $this->mOldRev && $this->mNewRev
                        && $this->mOldRev->getId() == $this->mNewRev->getId() )
                ) {
-                       if ( !Hooks::run( 'DifferenceEngineShowEmptyOldContent', [ $this ] ) ) {
+                       if ( Hooks::run( 'DifferenceEngineShowEmptyOldContent', [ $this ] ) ) {
                                return '';
                        }
                }
@@ -809,7 +811,7 @@ class DifferenceEngine extends ContextSource {
                        throw new MWException( 'mOldid and mNewid must be set to get diff cache key.' );
                }
 
-               return wfMemcKey( 'diff', 'version', MW_DIFF_VERSION,
+               return wfMemcKey( 'diff', 'version', self::DIFF_VERSION,
                        'oldid', $this->mOldid, 'newid', $this->mNewid );
        }
 
@@ -849,21 +851,6 @@ class DifferenceEngine extends ContextSource {
                return $this->generateTextDiffBody( $otext, $ntext );
        }
 
-       /**
-        * Generate a diff, no caching
-        *
-        * @param string $otext Old text, must be already segmented
-        * @param string $ntext New text, must be already segmented
-        *
-        * @return bool|string
-        * @deprecated since 1.21, use generateContentDiffBody() instead!
-        */
-       public function generateDiffBody( $otext, $ntext ) {
-               ContentHandler::deprecated( __METHOD__, "1.21" );
-
-               return $this->generateTextDiffBody( $otext, $ntext );
-       }
-
        /**
         * Generate a diff, no caching
         *
@@ -923,18 +910,23 @@ class DifferenceEngine extends ContextSource {
                if ( $wgExternalDiffEngine == 'wikidiff' || $wgExternalDiffEngine == 'wikidiff3' ) {
                        wfDeprecated( "\$wgExternalDiffEngine = '{$wgExternalDiffEngine}'", '1.27' );
                        $wgExternalDiffEngine = false;
+               } elseif ( $wgExternalDiffEngine == 'wikidiff2' ) {
+                       // Same as above, but with no deprecation warnings
+                       $wgExternalDiffEngine = false;
+               } elseif ( !is_string( $wgExternalDiffEngine ) && $wgExternalDiffEngine !== false ) {
+                       // And prevent people from shooting themselves in the foot...
+                       wfWarn( '$wgExternalDiffEngine is set to a non-string value, forcing it to false' );
+                       $wgExternalDiffEngine = false;
                }
 
-               if ( $wgExternalDiffEngine == 'wikidiff2' ) {
-                       if ( function_exists( 'wikidiff2_do_diff' ) ) {
-                               # 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 );
-                               $text .= $this->debug( 'wikidiff2' );
+               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 );
+                       $text .= $this->debug( 'wikidiff2' );
 
-                               return $text;
-                       }
-               } elseif ( $wgExternalDiffEngine !== false ) {
+                       return $text;
+               } elseif ( $wgExternalDiffEngine !== false && is_executable( $wgExternalDiffEngine ) ) {
                        # Diff via the shell
                        $tmpDir = wfTempDir();
                        $tempName1 = tempnam( $tmpDir, 'diff_' );
@@ -1346,7 +1338,7 @@ class DifferenceEngine extends ContextSource {
                }
 
                // Load tags information for both revisions
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                if ( $this->mOldid !== false ) {
                        $this->mOldTags = $dbr->selectField(
                                'tag_summary',
index bebd915..0a174fe 100644 (file)
@@ -91,7 +91,7 @@ class MWException extends Exception {
                                is_string( $hook ) ||
                                ( is_array( $hook ) && count( $hook ) >= 2 && is_string( $hook[0] ) )
                        ) {
-                               // 'function' or array( 'class', hook' )
+                               // 'function' or [ 'class', 'hook' ]
                                $result = call_user_func_array( $hook, $callargs );
                        } else {
                                $result = null;
index e4ff5f3..9c83d3c 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Handler class for MWExceptions
@@ -136,16 +137,16 @@ class MWExceptionHandler {
         * @param Exception|Throwable $e
         */
        public static function rollbackMasterChangesAndLog( $e ) {
-               $factory = wfGetLBFactory();
-               if ( $factory->hasMasterChanges() ) {
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               if ( $lbFactory->hasMasterChanges() ) {
                        $logger = LoggerFactory::getInstance( 'Bug56269' );
                        $logger->warning(
                                'Exception thrown with an uncommited database transaction: ' .
                                self::getLogMessage( $e ),
                                self::getLogContext( $e )
                        );
-                       $factory->rollbackMasterChanges( __METHOD__ );
                }
+               $lbFactory->rollbackMasterChanges( __METHOD__ );
        }
 
        /**
@@ -488,7 +489,11 @@ TXT;
                return "[$id] $url   $type from line $line of $file: $message";
        }
 
-       public static function getPublicLogMessage( Exception $e ) {
+       /**
+        * @param Exception|Throwable $e
+        * @return string
+        */
+       public static function getPublicLogMessage( $e ) {
                $reqId = WebRequest::getRequestId();
                $type = get_class( $e );
                return '[' . $reqId . '] '
index b7c3489..43c5b09 100644 (file)
@@ -62,7 +62,7 @@ class UserNotLoggedIn extends ErrorPageError {
         * @param string $titleMsg A message key to set the page title.
         *        Optional, default: 'exception-nologin'
         * @param array $params Parameters to wfMessage().
-        *        Optional, default: array()
+        *        Optional, default: []
         */
        public function __construct(
                $reasonMsg = 'exception-nologin-text',
diff --git a/includes/export/DumpStringOutput.php b/includes/export/DumpStringOutput.php
new file mode 100644 (file)
index 0000000..837a62d
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Stream outputter that buffers and returns data as a string.
+ *
+ * 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 Dump
+ * @since 1.28
+ */
+class DumpStringOutput extends DumpOutput {
+       private $output = '';
+
+       /**
+        * @param string $string
+        */
+       function write( $string ) {
+               $this->output .= $string;
+       }
+
+       /**
+        * Get the string containing the output.
+        *
+        * @return string
+        */
+       public function __toString() {
+               return $this->output;
+       }
+}
index b454577..2eae279 100644 (file)
@@ -112,7 +112,7 @@ class ExternalStoreDB extends ExternalStoreMedium {
        }
 
        /**
-        * Get a slave database connection for the specified cluster
+        * Get a replica DB connection for the specified cluster
         *
         * @param string $cluster Cluster name
         * @return IDatabase
@@ -130,7 +130,7 @@ class ExternalStoreDB extends ExternalStoreMedium {
                        wfDebug( "writable external store\n" );
                }
 
-               $db = $lb->getConnection( DB_SLAVE, [], $wiki );
+               $db = $lb->getConnection( DB_REPLICA, [], $wiki );
                $db->clearFlag( DBO_TRX ); // sanity
 
                return $db;
@@ -264,7 +264,7 @@ class ExternalStoreDB extends ExternalStoreMedium {
        }
 
        /**
-        * Helper function for self::batchFetchBlobs for merging master/slave results
+        * Helper function for self::batchFetchBlobs for merging master/replica DB results
         * @param array &$ret Current self::batchFetchBlobs return value
         * @param array &$ids Map from blob_id to requested itemIDs
         * @param mixed $res DB result from Database::select
index 03974f7..ed36a1f 100644 (file)
@@ -241,31 +241,31 @@ abstract class FileBackend {
         *
         * a) Create a new file in storage with the contents of a string
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'create',
         *         'dst'                 => <storage path>,
         *         'content'             => <string of new file contents>,
         *         'overwrite'           => <boolean>,
         *         'overwriteSame'       => <boolean>,
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     );
+        *     ]
         * @endcode
         *
         * b) Copy a file system file into storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'store',
         *         'src'                 => <file system path, FSFile, or TempFSFile>,
         *         'dst'                 => <storage path>,
         *         'overwrite'           => <boolean>,
         *         'overwriteSame'       => <boolean>,
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * c) Copy a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'copy',
         *         'src'                 => <storage path>,
         *         'dst'                 => <storage path>,
@@ -273,12 +273,12 @@ abstract class FileBackend {
         *         'overwriteSame'       => <boolean>,
         *         'ignoreMissingSource' => <boolean>, # since 1.21
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * d) Move a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'move',
         *         'src'                 => <storage path>,
         *         'dst'                 => <storage path>,
@@ -286,32 +286,32 @@ abstract class FileBackend {
         *         'overwriteSame'       => <boolean>,
         *         'ignoreMissingSource' => <boolean>, # since 1.21
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * e) Delete a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'delete',
         *         'src'                 => <storage path>,
         *         'ignoreMissingSource' => <boolean>
-        *     )
+        *     ]
         * @endcode
         *
         * f) Update metadata for a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'describe',
         *         'src'                 => <storage path>,
         *         'headers'             => <HTTP header name/value map>
-        *     )
+        *     ]
         * @endcode
         *
         * g) Do nothing (no-op)
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'null',
-        *     )
+        *     ]
         * @endcode
         *
         * Boolean flags for operations (operation-specific):
@@ -513,69 +513,69 @@ abstract class FileBackend {
         *
         * a) Create a new file in storage with the contents of a string
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'create',
         *         'dst'                 => <storage path>,
         *         'content'             => <string of new file contents>,
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * b) Copy a file system file into storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'store',
         *         'src'                 => <file system path, FSFile, or TempFSFile>,
         *         'dst'                 => <storage path>,
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * c) Copy a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'copy',
         *         'src'                 => <storage path>,
         *         'dst'                 => <storage path>,
         *         'ignoreMissingSource' => <boolean>, # since 1.21
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * d) Move a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'move',
         *         'src'                 => <storage path>,
         *         'dst'                 => <storage path>,
         *         'ignoreMissingSource' => <boolean>, # since 1.21
         *         'headers'             => <HTTP header name/value map> # since 1.21
-        *     )
+        *     ]
         * @endcode
         *
         * e) Delete a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'delete',
         *         'src'                 => <storage path>,
         *         'ignoreMissingSource' => <boolean>
-        *     )
+        *     ]
         * @endcode
         *
         * f) Update metadata for a file within storage
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'describe',
         *         'src'                 => <storage path>,
         *         'headers'             => <HTTP header name/value map>
-        *     )
+        *     ]
         * @endcode
         *
         * g) Do nothing (no-op)
         * @code
-        *     array(
+        *     [
         *         'op'                  => 'null',
-        *     )
+        *     ]
         * @endcode
         *
         * @par Boolean flags for operations (operation-specific):
@@ -1005,15 +1005,21 @@ abstract class FileBackend {
 
        /**
         * Stream the file at a storage path in the backend.
+        *
         * If the file does not exists, an HTTP 404 error will be given.
         * Appropriate HTTP headers (Status, Content-Type, Content-Length)
         * will be sent if streaming began, while none will be sent otherwise.
         * Implementations should flush the output buffer before sending data.
         *
         * @param array $params Parameters include:
-        *   - src     : source storage path
-        *   - headers : list of additional HTTP headers to send on success
-        *   - latest  : use the latest available data
+        *   - src      : source storage path
+        *   - headers  : list of additional HTTP headers to send if the file exists
+        *   - options  : HTTP request header map with lower case keys (since 1.28). Supports:
+        *                range             : format is "bytes=(\d*-\d*)"
+        *                if-modified-since : format is an HTTP date
+        *   - headless : only include the body (and headers from "headers") (since 1.28)
+        *   - latest   : use the latest available data
+        *   - allowOB  : preserve any output buffers (since 1.28)
         * @return Status
         */
        abstract public function streamFile( array $params );
@@ -1028,7 +1034,7 @@ abstract class FileBackend {
         *
         * Write operations should *never* be done on this file as some backends
         * may do internal tracking or may be instances of FileBackendMultiWrite.
-        * In that later case, there are copies of the file that must stay in sync.
+        * In that latter case, there are copies of the file that must stay in sync.
         * Additionally, further calls to this function may return the same file.
         *
         * @param array $params Parameters include:
index 4d9587e..bc4d81d 100644 (file)
@@ -844,30 +844,19 @@ abstract class FileBackendStore extends FileBackend {
                $ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
                $status = Status::newGood();
 
-               $info = $this->getFileStat( $params );
-               if ( !$info ) { // let StreamFile handle the 404
-                       $status->fatal( 'backend-fail-notexists', $params['src'] );
-               }
-
-               // Set output buffer and HTTP headers for stream
-               $extraHeaders = isset( $params['headers'] ) ? $params['headers'] : [];
-               $res = StreamFile::prepareForStream( $params['src'], $info, $extraHeaders );
-               if ( $res == StreamFile::NOT_MODIFIED ) {
-                       // do nothing; client cache is up to date
-               } elseif ( $res == StreamFile::READY_STREAM ) {
-                       $status = $this->doStreamFile( $params );
-                       if ( !$status->isOK() ) {
-                               // Per bug 41113, nasty things can happen if bad cache entries get
-                               // stuck in cache. It's also possible that this error can come up
-                               // with simple race conditions. Clear out the stat cache to be safe.
-                               $this->clearCache( [ $params['src'] ] );
-                               $this->deleteFileCache( $params['src'] );
-                               trigger_error( "Bad stat cache or race condition for file {$params['src']}." );
-                       }
-               } else {
+               // Always set some fields for subclass convenience
+               $params['options'] = isset( $params['options'] ) ? $params['options'] : [];
+               $params['headers'] = isset( $params['headers'] ) ? $params['headers'] : [];
+
+               // Don't stream it out as text/html if there was a PHP error
+               if ( ( empty( $params['headless'] ) || $params['headers'] ) && headers_sent() ) {
+                       print "Headers already sent, terminating.\n";
                        $status->fatal( 'backend-fail-stream', $params['src'] );
+                       return $status;
                }
 
+               $status->merge( $this->doStreamFile( $params ) );
+
                return $status;
        }
 
@@ -879,10 +868,21 @@ abstract class FileBackendStore extends FileBackend {
        protected function doStreamFile( array $params ) {
                $status = Status::newGood();
 
+               $flags = 0;
+               $flags |= !empty( $params['headless'] ) ? StreamFile::STREAM_HEADLESS : 0;
+               $flags |= !empty( $params['allowOB'] ) ? StreamFile::STREAM_ALLOW_OB : 0;
+
                $fsFile = $this->getLocalReference( $params );
-               if ( !$fsFile ) {
-                       $status->fatal( 'backend-fail-stream', $params['src'] );
-               } elseif ( !readfile( $fsFile->getPath() ) ) {
+
+               if ( $fsFile ) {
+                       $res = StreamFile::stream( $fsFile->getPath(),
+                               $params['headers'], true, $params['options'], $flags );
+               } else {
+                       $res = false;
+                       StreamFile::send404Message( $params['src'], $flags );
+               }
+
+               if ( !$res ) {
                        $status->fatal( 'backend-fail-stream', $params['src'] );
                }
 
@@ -1443,7 +1443,7 @@ abstract class FileBackendStore extends FileBackend {
        /**
         * Like resolveStoragePath() except null values are returned if
         * the container is sharded and the shard could not be determined
-        * or if the path ends with '/'. The later case is illegal for FS
+        * or if the path ends with '/'. The latter case is illegal for FS
         * backends and can confuse listings for object store backends.
         *
         * This function is used when resolving paths that must be valid
@@ -1702,8 +1702,8 @@ abstract class FileBackendStore extends FileBackend {
                if ( $path === null ) {
                        return; // invalid storage path
                }
-               $age = time() - wfTimestamp( TS_UNIX, $val['mtime'] );
-               $ttl = min( 7 * 86400, max( 300, floor( .1 * $age ) ) );
+               $mtime = wfTimestamp( TS_UNIX, $val['mtime'] );
+               $ttl = $this->memCache->adaptiveTTL( $mtime, 7 * 86400, 300, .1 );
                $key = $this->fileCacheKey( $path );
                // Set the cache unless it is currently salted.
                $this->memCache->set( $key, $val, $ttl );
index 6e32c62..e2c1ede 100644 (file)
@@ -183,21 +183,6 @@ class MemoryFileBackend extends FileBackendStore {
                return $tmpFiles;
        }
 
-       protected function doStreamFile( array $params ) {
-               $status = Status::newGood();
-
-               $src = $this->resolveHashKey( $params['src'] );
-               if ( $src === null || !isset( $this->files[$src] ) ) {
-                       $status->fatal( 'backend-fail-stream', $params['src'] );
-
-                       return $status;
-               }
-
-               print $this->files[$src]['data'];
-
-               return $status;
-       }
-
        protected function doDirectoryExists( $container, $dir, array $params ) {
                $prefix = rtrim( "$container/$dir", '/' ) . '/';
                foreach ( $this->files as $path => $data ) {
index 0f7e4b5..2adf934 100644 (file)
@@ -1045,32 +1045,62 @@ class SwiftFileBackend extends FileBackendStore {
        protected function doStreamFile( array $params ) {
                $status = Status::newGood();
 
+               $flags = !empty( $params['headless'] ) ? StreamFile::STREAM_HEADLESS : 0;
+
                list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
                if ( $srcRel === null ) {
+                       StreamFile::send404Message( $params['src'], $flags );
                        $status->fatal( 'backend-fail-invalidpath', $params['src'] );
+
+                       return $status;
                }
 
                $auth = $this->getAuthentication();
                if ( !$auth || !is_array( $this->getContainerStat( $srcCont ) ) ) {
+                       StreamFile::send404Message( $params['src'], $flags );
                        $status->fatal( 'backend-fail-stream', $params['src'] );
 
                        return $status;
                }
 
-               $handle = fopen( 'php://output', 'wb' );
+               // If "headers" is set, we only want to send them if the file is there.
+               // Do not bother checking if the file exists if headers are not set though.
+               if ( $params['headers'] && !$this->fileExists( $params ) ) {
+                       StreamFile::send404Message( $params['src'], $flags );
+                       $status->fatal( 'backend-fail-stream', $params['src'] );
 
+                       return $status;
+               }
+
+               // Send the requested additional headers
+               foreach ( $params['headers'] as $header ) {
+                       header( $header ); // aways send
+               }
+
+               if ( empty( $params['allowOB'] ) ) {
+                       // Cancel output buffering and gzipping if set
+                       wfResetOutputBuffers();
+               }
+
+               $handle = fopen( 'php://output', 'wb' );
                list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run( [
                        'method' => 'GET',
                        'url' => $this->storageUrl( $auth, $srcCont, $srcRel ),
                        'headers' => $this->authTokenHeaders( $auth )
-                               + $this->headersFromParams( $params ),
+                               + $this->headersFromParams( $params ) + $params['options'],
                        'stream' => $handle,
+                       'flags'  => [ 'relayResponseHeaders' => empty( $params['headless'] ) ]
                ] );
 
                if ( $rcode >= 200 && $rcode <= 299 ) {
                        // good
                } elseif ( $rcode === 404 ) {
                        $status->fatal( 'backend-fail-stream', $params['src'] );
+                       // Per bug 41113, nasty things can happen if bad cache entries get
+                       // stuck in cache. It's also possible that this error can come up
+                       // with simple race conditions. Clear out the stat cache to be safe.
+                       $this->clearCache( [ $params['src'] ] );
+                       $this->deleteFileCache( $params['src'] );
                } else {
                        $this->onError( $status, __METHOD__, $params, $rerr, $rcode, $rdesc );
                }
index f4410ca..cccf71a 100644 (file)
@@ -26,9 +26,8 @@
  *
  * This is meant for multi-wiki systems that may share files.
  *
- * All lock requests for a resource, identified by a hash string, will map
- * to one bucket. Each bucket maps to one or several peer DBs, each on their
- * own server, all having the filelocks.sql tables (with row-level locking).
+ * All lock requests for a resource, identified by a hash string, will map to one bucket.
+ * Each bucket maps to one or several peer DBs, each on their own server.
  * A majority of peer DBs must agree for a lock to be acquired.
  *
  * Caching is used to avoid hitting servers that are down.
@@ -37,7 +36,7 @@
  * @since 1.19
  */
 abstract class DBLockManager extends QuorumLockManager {
-       /** @var array Map of DB names to server config */
+       /** @var array[] Map of DB names to server config */
        protected $dbServers; // (DB name => server config array)
        /** @var BagOStuff */
        protected $statusCache;
@@ -46,7 +45,7 @@ abstract class DBLockManager extends QuorumLockManager {
        protected $safeDelay; // integer number of seconds
 
        protected $session = 0; // random integer
-       /** @var array Map Database connections (DB name => Database) */
+       /** @var IDatabase[] Map Database connections (DB name => Database) */
        protected $conns = [];
 
        /**
@@ -113,6 +112,8 @@ abstract class DBLockManager extends QuorumLockManager {
                return $status;
        }
 
+       abstract protected function doGetLocksOnServer( $lockSrv, array $paths, $type );
+
        protected function freeLocksOnServer( $lockSrv, array $pathsByType ) {
                return Status::newGood();
        }
@@ -143,39 +144,50 @@ abstract class DBLockManager extends QuorumLockManager {
         * @param string $lockDb
         * @return IDatabase
         * @throws DBError
+        * @throws UnexpectedValueException
         */
        protected function getConnection( $lockDb ) {
                if ( !isset( $this->conns[$lockDb] ) ) {
-                       $db = null;
                        if ( $lockDb === 'localDBMaster' ) {
-                               $lb = wfGetLBFactory()->getMainLB( $this->domain );
+                               $lb = $this->getLocalLB();
                                $db = $lb->getConnection( DB_MASTER, [], $this->domain );
+                               # Do not mess with settings if the LoadBalancer is the main singleton
+                               # to avoid clobbering the settings of handles from wfGetDB( DB_MASTER ).
+                               $init = ( wfGetLB() !== $lb );
                        } elseif ( isset( $this->dbServers[$lockDb] ) ) {
                                $config = $this->dbServers[$lockDb];
                                $db = DatabaseBase::factory( $config['type'], $config );
+                               $init = true;
+                       } else {
+                               throw new UnexpectedValueException( "No server called '$lockDb'." );
                        }
-                       if ( !$db ) {
-                               return null; // config error?
+
+                       if ( $init ) {
+                               $db->clearFlag( DBO_TRX );
+                               # If the connection drops, try to avoid letting the DB rollback
+                               # and release the locks before the file operations are finished.
+                               # This won't handle the case of DB server restarts however.
+                               $options = [];
+                               if ( $this->lockExpiry > 0 ) {
+                                       $options['connTimeout'] = $this->lockExpiry;
+                               }
+                               $db->setSessionOptions( $options );
+                               $this->initConnection( $lockDb, $db );
                        }
+
                        $this->conns[$lockDb] = $db;
-                       $this->conns[$lockDb]->clearFlag( DBO_TRX );
-                       # If the connection drops, try to avoid letting the DB rollback
-                       # and release the locks before the file operations are finished.
-                       # This won't handle the case of DB server restarts however.
-                       $options = [];
-                       if ( $this->lockExpiry > 0 ) {
-                               $options['connTimeout'] = $this->lockExpiry;
-                       }
-                       $this->conns[$lockDb]->setSessionOptions( $options );
-                       $this->initConnection( $lockDb, $this->conns[$lockDb] );
-               }
-               if ( !$this->conns[$lockDb]->trxLevel() ) {
-                       $this->conns[$lockDb]->begin( __METHOD__ ); // start transaction
                }
 
                return $this->conns[$lockDb];
        }
 
+       /**
+        * @return LoadBalancer
+        */
+       protected function getLocalLB() {
+               return wfGetLBFactory()->getMainLB( $this->domain );
+       }
+
        /**
         * Do additional initialization for new lock DB connection
         *
@@ -232,202 +244,3 @@ abstract class DBLockManager extends QuorumLockManager {
                }
        }
 }
-
-/**
- * MySQL version of DBLockManager that supports shared locks.
- * All locks are non-blocking, which avoids deadlocks.
- *
- * @ingroup LockManager
- */
-class MySqlLockManager extends DBLockManager {
-       /** @var array Mapping of lock types to the type actually used */
-       protected $lockTypeMap = [
-               self::LOCK_SH => self::LOCK_SH,
-               self::LOCK_UW => self::LOCK_SH,
-               self::LOCK_EX => self::LOCK_EX
-       ];
-
-       /**
-        * @param string $lockDb
-        * @param IDatabase $db
-        */
-       protected function initConnection( $lockDb, IDatabase $db ) {
-               # Let this transaction see lock rows from other transactions
-               $db->query( "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;" );
-       }
-
-       /**
-        * Get a connection to a lock DB and acquire locks on $paths.
-        * This does not use GET_LOCK() per http://bugs.mysql.com/bug.php?id=1118.
-        *
-        * @see DBLockManager::getLocksOnServer()
-        * @param string $lockSrv
-        * @param array $paths
-        * @param string $type
-        * @return Status
-        */
-       protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
-               $status = Status::newGood();
-
-               $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
-
-               $keys = []; // list of hash keys for the paths
-               $data = []; // list of rows to insert
-               $checkEXKeys = []; // list of hash keys that this has no EX lock on
-               # Build up values for INSERT clause
-               foreach ( $paths as $path ) {
-                       $key = $this->sha1Base36Absolute( $path );
-                       $keys[] = $key;
-                       $data[] = [ 'fls_key' => $key, 'fls_session' => $this->session ];
-                       if ( !isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
-                               $checkEXKeys[] = $key;
-                       }
-               }
-
-               # Block new writers (both EX and SH locks leave entries here)...
-               $db->insert( 'filelocks_shared', $data, __METHOD__, [ 'IGNORE' ] );
-               # Actually do the locking queries...
-               if ( $type == self::LOCK_SH ) { // reader locks
-                       $blocked = false;
-                       # Bail if there are any existing writers...
-                       if ( count( $checkEXKeys ) ) {
-                               $blocked = $db->selectField( 'filelocks_exclusive', '1',
-                                       [ 'fle_key' => $checkEXKeys ],
-                                       __METHOD__
-                               );
-                       }
-                       # Other prospective writers that haven't yet updated filelocks_exclusive
-                       # will recheck filelocks_shared after doing so and bail due to this entry.
-               } else { // writer locks
-                       $encSession = $db->addQuotes( $this->session );
-                       # Bail if there are any existing writers...
-                       # This may detect readers, but the safe check for them is below.
-                       # Note: if two writers come at the same time, both bail :)
-                       $blocked = $db->selectField( 'filelocks_shared', '1',
-                               [ 'fls_key' => $keys, "fls_session != $encSession" ],
-                               __METHOD__
-                       );
-                       if ( !$blocked ) {
-                               # Build up values for INSERT clause
-                               $data = [];
-                               foreach ( $keys as $key ) {
-                                       $data[] = [ 'fle_key' => $key ];
-                               }
-                               # Block new readers/writers...
-                               $db->insert( 'filelocks_exclusive', $data, __METHOD__ );
-                               # Bail if there are any existing readers...
-                               $blocked = $db->selectField( 'filelocks_shared', '1',
-                                       [ 'fls_key' => $keys, "fls_session != $encSession" ],
-                                       __METHOD__
-                               );
-                       }
-               }
-
-               if ( $blocked ) {
-                       foreach ( $paths as $path ) {
-                               $status->fatal( 'lockmanager-fail-acquirelock', $path );
-                       }
-               }
-
-               return $status;
-       }
-
-       /**
-        * @see QuorumLockManager::releaseAllLocks()
-        * @return Status
-        */
-       protected function releaseAllLocks() {
-               $status = Status::newGood();
-
-               foreach ( $this->conns as $lockDb => $db ) {
-                       if ( $db->trxLevel() ) { // in transaction
-                               try {
-                                       $db->rollback( __METHOD__ ); // finish transaction and kill any rows
-                               } catch ( DBError $e ) {
-                                       $status->fatal( 'lockmanager-fail-db-release', $lockDb );
-                               }
-                       }
-               }
-
-               return $status;
-       }
-}
-
-/**
- * PostgreSQL version of DBLockManager that supports shared locks.
- * All locks are non-blocking, which avoids deadlocks.
- *
- * @ingroup LockManager
- */
-class PostgreSqlLockManager extends DBLockManager {
-       /** @var array Mapping of lock types to the type actually used */
-       protected $lockTypeMap = [
-               self::LOCK_SH => self::LOCK_SH,
-               self::LOCK_UW => self::LOCK_SH,
-               self::LOCK_EX => self::LOCK_EX
-       ];
-
-       protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
-               $status = Status::newGood();
-               if ( !count( $paths ) ) {
-                       return $status; // nothing to lock
-               }
-
-               $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
-               $bigints = array_unique( array_map(
-                       function ( $key ) {
-                               return Wikimedia\base_convert( substr( $key, 0, 15 ), 16, 10 );
-                       },
-                       array_map( [ $this, 'sha1Base16Absolute' ], $paths )
-               ) );
-
-               // Try to acquire all the locks...
-               $fields = [];
-               foreach ( $bigints as $bigint ) {
-                       $fields[] = ( $type == self::LOCK_SH )
-                               ? "pg_try_advisory_lock_shared({$db->addQuotes( $bigint )}) AS K$bigint"
-                               : "pg_try_advisory_lock({$db->addQuotes( $bigint )}) AS K$bigint";
-               }
-               $res = $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
-               $row = $res->fetchRow();
-
-               if ( in_array( 'f', $row ) ) {
-                       // Release any acquired locks if some could not be acquired...
-                       $fields = [];
-                       foreach ( $row as $kbigint => $ok ) {
-                               if ( $ok === 't' ) { // locked
-                                       $bigint = substr( $kbigint, 1 ); // strip off the "K"
-                                       $fields[] = ( $type == self::LOCK_SH )
-                                               ? "pg_advisory_unlock_shared({$db->addQuotes( $bigint )})"
-                                               : "pg_advisory_unlock({$db->addQuotes( $bigint )})";
-                               }
-                       }
-                       if ( count( $fields ) ) {
-                               $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
-                       }
-                       foreach ( $paths as $path ) {
-                               $status->fatal( 'lockmanager-fail-acquirelock', $path );
-                       }
-               }
-
-               return $status;
-       }
-
-       /**
-        * @see QuorumLockManager::releaseAllLocks()
-        * @return Status
-        */
-       protected function releaseAllLocks() {
-               $status = Status::newGood();
-
-               foreach ( $this->conns as $lockDb => $db ) {
-                       try {
-                               $db->query( "SELECT pg_advisory_unlock_all()", __METHOD__ );
-                       } catch ( DBError $e ) {
-                               $status->fatal( 'lockmanager-fail-db-release', $lockDb );
-                       }
-               }
-
-               return $status;
-       }
-}
index 567a298..a3cb3b1 100644 (file)
@@ -103,17 +103,17 @@ abstract class LockManager {
         */
        final public function lockByType( array $pathsByType, $timeout = 0 ) {
                $pathsByType = $this->normalizePathsByType( $pathsByType );
-               $msleep = [ 0, 50, 100, 300, 500 ]; // retry backoff times
-               $start = microtime( true );
-               do {
-                       $status = $this->doLockByType( $pathsByType );
-                       $elapsed = microtime( true ) - $start;
-                       if ( $status->isOK() || $elapsed >= $timeout || $elapsed < 0 ) {
-                               break; // success, timeout, or clock set back
-                       }
-                       usleep( 1e3 * ( next( $msleep ) ?: 1000 ) ); // use 1 sec after enough times
-                       $elapsed = microtime( true ) - $start;
-               } while ( $elapsed < $timeout && $elapsed >= 0 );
+
+               $status = null;
+               $loop = new WaitConditionLoop(
+                       function () use ( &$status, $pathsByType ) {
+                               $status = $this->doLockByType( $pathsByType );
+
+                               return $status->isOK() ?: WaitConditionLoop::CONDITION_CONTINUE;
+                       },
+                       $timeout
+               );
+               $loop->invoke();
 
                return $status;
        }
index cb5266a..2f17e27 100644 (file)
@@ -276,6 +276,7 @@ class MemcLockManager extends QuorumLockManager {
         * @return MemcachedBagOStuff|null
         */
        protected function getCache( $lockSrv ) {
+               /** @var BagOStuff $memc */
                $memc = null;
                if ( isset( $this->bagOStuffs[$lockSrv] ) ) {
                        $memc = $this->bagOStuffs[$lockSrv];
@@ -337,20 +338,21 @@ class MemcLockManager extends QuorumLockManager {
                // Try to quickly loop to acquire the keys, but back off after a few rounds.
                // This reduces memcached spam, especially in the rare case where a server acquires
                // some lock keys and dies without releasing them. Lock keys expire after a few minutes.
-               $rounds = 0;
-               $start = microtime( true );
-               do {
-                       if ( ( ++$rounds % 4 ) == 0 ) {
-                               usleep( 1000 * 50 ); // 50 ms
-                       }
-                       foreach ( array_diff( $keys, $lockedKeys ) as $key ) {
-                               if ( $memc->add( "$key:mutex", 1, 180 ) ) { // lock record
-                                       $lockedKeys[] = $key;
-                               } else {
-                                       continue; // acquire in order
+               $loop = new WaitConditionLoop(
+                       function () use ( $memc, $keys, &$lockedKeys ) {
+                               foreach ( array_diff( $keys, $lockedKeys ) as $key ) {
+                                       if ( $memc->add( "$key:mutex", 1, 180 ) ) { // lock record
+                                               $lockedKeys[] = $key;
+                                       }
                                }
-                       }
-               } while ( count( $lockedKeys ) < count( $keys ) && ( microtime( true ) - $start ) <= 3 );
+
+                               return array_diff( $keys, $lockedKeys )
+                                       ? WaitConditionLoop::CONDITION_CONTINUE
+                                       : true;
+                       },
+                       3.0 // timeout
+               );
+               $loop->invoke();
 
                if ( count( $lockedKeys ) != count( $keys ) ) {
                        $this->releaseMutexes( $memc, $lockedKeys ); // failed; release what was locked
diff --git a/includes/filebackend/lockmanager/MySqlLockManager.php b/includes/filebackend/lockmanager/MySqlLockManager.php
new file mode 100644 (file)
index 0000000..0536091
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+/**
+ * MySQL version of DBLockManager that supports shared locks.
+ *
+ * All lock servers must have the innodb table defined in locking/filelocks.sql.
+ * All locks are non-blocking, which avoids deadlocks.
+ *
+ * @ingroup LockManager
+ */
+class MySqlLockManager extends DBLockManager {
+       /** @var array Mapping of lock types to the type actually used */
+       protected $lockTypeMap = [
+               self::LOCK_SH => self::LOCK_SH,
+               self::LOCK_UW => self::LOCK_SH,
+               self::LOCK_EX => self::LOCK_EX
+       ];
+
+       protected function getLocalLB() {
+               // Use a separate connection so releaseAllLocks() doesn't rollback the main trx
+               return wfGetLBFactory()->newMainLB( $this->domain );
+       }
+
+       protected function initConnection( $lockDb, IDatabase $db ) {
+               # Let this transaction see lock rows from other transactions
+               $db->query( "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;" );
+               # Do everything in a transaction as it all gets rolled back eventually
+               $db->startAtomic( __CLASS__ );
+       }
+
+       /**
+        * Get a connection to a lock DB and acquire locks on $paths.
+        * This does not use GET_LOCK() per http://bugs.mysql.com/bug.php?id=1118.
+        *
+        * @see DBLockManager::getLocksOnServer()
+        * @param string $lockSrv
+        * @param array $paths
+        * @param string $type
+        * @return Status
+        */
+       protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
+               $status = Status::newGood();
+
+               $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
+
+               $keys = []; // list of hash keys for the paths
+               $data = []; // list of rows to insert
+               $checkEXKeys = []; // list of hash keys that this has no EX lock on
+               # Build up values for INSERT clause
+               foreach ( $paths as $path ) {
+                       $key = $this->sha1Base36Absolute( $path );
+                       $keys[] = $key;
+                       $data[] = [ 'fls_key' => $key, 'fls_session' => $this->session ];
+                       if ( !isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
+                               $checkEXKeys[] = $key;
+                       }
+               }
+
+               # Block new writers (both EX and SH locks leave entries here)...
+               $db->insert( 'filelocks_shared', $data, __METHOD__, [ 'IGNORE' ] );
+               # Actually do the locking queries...
+               if ( $type == self::LOCK_SH ) { // reader locks
+                       $blocked = false;
+                       # Bail if there are any existing writers...
+                       if ( count( $checkEXKeys ) ) {
+                               $blocked = $db->selectField( 'filelocks_exclusive', '1',
+                                       [ 'fle_key' => $checkEXKeys ],
+                                       __METHOD__
+                               );
+                       }
+                       # Other prospective writers that haven't yet updated filelocks_exclusive
+                       # will recheck filelocks_shared after doing so and bail due to this entry.
+               } else { // writer locks
+                       $encSession = $db->addQuotes( $this->session );
+                       # Bail if there are any existing writers...
+                       # This may detect readers, but the safe check for them is below.
+                       # Note: if two writers come at the same time, both bail :)
+                       $blocked = $db->selectField( 'filelocks_shared', '1',
+                               [ 'fls_key' => $keys, "fls_session != $encSession" ],
+                               __METHOD__
+                       );
+                       if ( !$blocked ) {
+                               # Build up values for INSERT clause
+                               $data = [];
+                               foreach ( $keys as $key ) {
+                                       $data[] = [ 'fle_key' => $key ];
+                               }
+                               # Block new readers/writers...
+                               $db->insert( 'filelocks_exclusive', $data, __METHOD__ );
+                               # Bail if there are any existing readers...
+                               $blocked = $db->selectField( 'filelocks_shared', '1',
+                                       [ 'fls_key' => $keys, "fls_session != $encSession" ],
+                                       __METHOD__
+                               );
+                       }
+               }
+
+               if ( $blocked ) {
+                       foreach ( $paths as $path ) {
+                               $status->fatal( 'lockmanager-fail-acquirelock', $path );
+                       }
+               }
+
+               return $status;
+       }
+
+       /**
+        * @see QuorumLockManager::releaseAllLocks()
+        * @return Status
+        */
+       protected function releaseAllLocks() {
+               $status = Status::newGood();
+
+               foreach ( $this->conns as $lockDb => $db ) {
+                       if ( $db->trxLevel() ) { // in transaction
+                               try {
+                                       $db->rollback( __METHOD__ ); // finish transaction and kill any rows
+                               } catch ( DBError $e ) {
+                                       $status->fatal( 'lockmanager-fail-db-release', $lockDb );
+                               }
+                       }
+               }
+
+               return $status;
+       }
+}
diff --git a/includes/filebackend/lockmanager/PostgreSqlLockManager.php b/includes/filebackend/lockmanager/PostgreSqlLockManager.php
new file mode 100644 (file)
index 0000000..d55b5ae
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+/**
+ * PostgreSQL version of DBLockManager that supports shared locks.
+ * All locks are non-blocking, which avoids deadlocks.
+ *
+ * @ingroup LockManager
+ */
+class PostgreSqlLockManager extends DBLockManager {
+       /** @var array Mapping of lock types to the type actually used */
+       protected $lockTypeMap = [
+               self::LOCK_SH => self::LOCK_SH,
+               self::LOCK_UW => self::LOCK_SH,
+               self::LOCK_EX => self::LOCK_EX
+       ];
+
+       protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
+               $status = Status::newGood();
+               if ( !count( $paths ) ) {
+                       return $status; // nothing to lock
+               }
+
+               $db = $this->getConnection( $lockSrv ); // checked in isServerUp()
+               $bigints = array_unique( array_map(
+                       function ( $key ) {
+                               return Wikimedia\base_convert( substr( $key, 0, 15 ), 16, 10 );
+                       },
+                       array_map( [ $this, 'sha1Base16Absolute' ], $paths )
+               ) );
+
+               // Try to acquire all the locks...
+               $fields = [];
+               foreach ( $bigints as $bigint ) {
+                       $fields[] = ( $type == self::LOCK_SH )
+                               ? "pg_try_advisory_lock_shared({$db->addQuotes( $bigint )}) AS K$bigint"
+                               : "pg_try_advisory_lock({$db->addQuotes( $bigint )}) AS K$bigint";
+               }
+               $res = $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
+               $row = $res->fetchRow();
+
+               if ( in_array( 'f', $row ) ) {
+                       // Release any acquired locks if some could not be acquired...
+                       $fields = [];
+                       foreach ( $row as $kbigint => $ok ) {
+                               if ( $ok === 't' ) { // locked
+                                       $bigint = substr( $kbigint, 1 ); // strip off the "K"
+                                       $fields[] = ( $type == self::LOCK_SH )
+                                               ? "pg_advisory_unlock_shared({$db->addQuotes( $bigint )})"
+                                               : "pg_advisory_unlock({$db->addQuotes( $bigint )})";
+                               }
+                       }
+                       if ( count( $fields ) ) {
+                               $db->query( 'SELECT ' . implode( ', ', $fields ), __METHOD__ );
+                       }
+                       foreach ( $paths as $path ) {
+                               $status->fatal( 'lockmanager-fail-acquirelock', $path );
+                       }
+               }
+
+               return $status;
+       }
+
+       /**
+        * @see QuorumLockManager::releaseAllLocks()
+        * @return Status
+        */
+       protected function releaseAllLocks() {
+               $status = Status::newGood();
+
+               foreach ( $this->conns as $lockDb => $db ) {
+                       try {
+                               $db->query( "SELECT pg_advisory_unlock_all()", __METHOD__ );
+                       } catch ( DBError $e ) {
+                               $status->fatal( 'lockmanager-fail-db-release', $lockDb );
+                       }
+               }
+
+               return $status;
+       }
+}
index 6095aee..4121ecb 100644 (file)
@@ -81,10 +81,12 @@ class RedisLockManager extends QuorumLockManager {
        protected function getLocksOnServer( $lockSrv, array $pathsByType ) {
                $status = Status::newGood();
 
+               $pathList = call_user_func_array( 'array_merge', array_values( $pathsByType ) );
+
                $server = $this->lockServers[$lockSrv];
                $conn = $this->redisPool->getConnection( $server );
                if ( !$conn ) {
-                       foreach ( array_merge( array_values( $pathsByType ) ) as $path ) {
+                       foreach ( $pathList as $path ) {
                                $status->fatal( 'lockmanager-fail-acquirelock', $path );
                        }
 
@@ -157,7 +159,7 @@ LUA;
                }
 
                if ( $res === false ) {
-                       foreach ( array_merge( array_values( $pathsByType ) ) as $path ) {
+                       foreach ( $pathList as $path ) {
                                $status->fatal( 'lockmanager-fail-acquirelock', $path );
                        }
                } else {
@@ -172,10 +174,12 @@ LUA;
        protected function freeLocksOnServer( $lockSrv, array $pathsByType ) {
                $status = Status::newGood();
 
+               $pathList = call_user_func_array( 'array_merge', array_values( $pathsByType ) );
+
                $server = $this->lockServers[$lockSrv];
                $conn = $this->redisPool->getConnection( $server );
                if ( !$conn ) {
-                       foreach ( array_merge( array_values( $pathsByType ) ) as $path ) {
+                       foreach ( $pathList as $path ) {
                                $status->fatal( 'lockmanager-fail-releaselock', $path );
                        }
 
@@ -225,7 +229,7 @@ LUA;
                }
 
                if ( $res === false ) {
-                       foreach ( array_merge( array_values( $pathsByType ) ) as $path ) {
+                       foreach ( $pathList as $path ) {
                                $status->fatal( 'lockmanager-fail-releaselock', $path );
                        }
                } else {
index aec337e..596dbde 100644 (file)
@@ -27,7 +27,7 @@
  * @brief Proxy backend that manages file layout rewriting for FileRepo.
  *
  * LocalRepo may be configured to store files under their title names or by SHA-1.
- * This acts as a shim in the later case, providing backwards compatability for
+ * This acts as a shim in the latter case, providing backwards compatability for
  * most callers. All "public"/"deleted" zone files actually go in an "original"
  * container and are never changed.
  *
@@ -94,7 +94,7 @@ class FileBackendDBRepoWrapper extends FileBackend {
         * @return array Translated paths in same order
         */
        public function getBackendPaths( array $paths, $latest = true ) {
-               $db = $this->getDB( $latest ? DB_MASTER : DB_SLAVE );
+               $db = $this->getDB( $latest ? DB_MASTER : DB_REPLICA );
 
                // @TODO: batching
                $resolved = [];
index d7559d0..b8b1cf6 100644 (file)
@@ -482,8 +482,8 @@ class FileRepo {
         * @param array $items An array of titles, or an array of findFile() options with
         *    the "title" option giving the title. Example:
         *
-        *     $findItem = array( 'title' => $title, 'private' => true );
-        *     $findBatch = array( $findItem );
+        *     $findItem = [ 'title' => $title, 'private' => true ];
+        *     $findBatch = [ $findItem ];
         *     $repo->findFiles( $findBatch );
         *
         *    No title should appear in $items twice, as the result use titles as keys
@@ -1585,12 +1585,13 @@ class FileRepo {
         *
         * @param string $virtualUrl
         * @param array $headers Additional HTTP headers to send on success
+        * @param array $optHeaders HTTP request headers (if-modified-since, range, ...)
         * @return Status
         * @since 1.27
         */
-       public function streamFileWithStatus( $virtualUrl, $headers = [] ) {
+       public function streamFileWithStatus( $virtualUrl, $headers = [], $optHeaders = [] ) {
                $path = $this->resolveToStoragePath( $virtualUrl );
-               $params = [ 'src' => $path, 'headers' => $headers ];
+               $params = [ 'src' => $path, 'headers' => $headers, 'options' => $optHeaders ];
 
                return $this->backend->streamFile( $params );
        }
index 67080b6..538e9bc 100644 (file)
@@ -24,7 +24,7 @@
 /**
  * Generic operation result class for FileRepo-related operations
  * @ingroup FileRepo
- * @deprecated 1.25
+ * @deprecated since 1.25
  */
 class FileRepoStatus extends Status {
 }
index b48191f..645a59b 100644 (file)
@@ -28,13 +28,13 @@ use MediaWiki\Logger\LoggerFactory;
  *
  * Example config:
  *
- * $wgForeignFileRepos[] = array(
+ * $wgForeignFileRepos[] = [
  *   'class'                  => 'ForeignAPIRepo',
  *   'name'                   => 'shared',
  *   'apibase'                => 'https://en.wikipedia.org/w/api.php',
  *   'fetchDescription'       => true, // Optional
  *   'descriptionCacheExpiry' => 3600,
- * );
+ * ];
  *
  * @ingroup FileRepo
  */
@@ -63,8 +63,8 @@ class ForeignAPIRepo extends FileRepo {
        /** @var array */
        protected $mFileExists = [];
 
-       /** @var array */
-       private $mQueryCache = [];
+       /** @var string */
+       private $mApiBase;
 
        /**
         * @param array|null $info
@@ -397,7 +397,8 @@ class ForeignAPIRepo extends FileRepo {
                        }
                        /* There is a new Commons file, or existing thumbnail older than a month */
                }
-               $thumb = self::httpGet( $foreignUrl );
+
+               $thumb = self::httpGet( $foreignUrl, 'default', [], $mtime );
                if ( !$thumb ) {
                        wfDebug( __METHOD__ . " Could not download thumb\n" );
 
@@ -413,7 +414,11 @@ class ForeignAPIRepo extends FileRepo {
                        return $foreignUrl;
                }
                $knownThumbUrls[$sizekey] = $localUrl;
-               $cache->set( $key, $knownThumbUrls, $this->apiThumbCacheExpiry );
+
+               $ttl = $mtime
+                       ? $cache->adaptiveTTL( $mtime, $this->apiThumbCacheExpiry )
+                       : $this->apiThumbCacheExpiry;
+               $cache->set( $key, $knownThumbUrls, $ttl );
                wfDebug( __METHOD__ . " got local thumb $localUrl, saving to cache \n" );
 
                return $localUrl;
@@ -506,9 +511,12 @@ class ForeignAPIRepo extends FileRepo {
         * @param string $url
         * @param string $timeout
         * @param array $options
+        * @param integer|bool &$mtime Resulting Last-Modified UNIX timestamp if received
         * @return bool|string
         */
-       public static function httpGet( $url, $timeout = 'default', $options = [] ) {
+       public static function httpGet(
+               $url, $timeout = 'default', $options = [], &$mtime = false
+       ) {
                $options['timeout'] = $timeout;
                /* Http::get */
                $url = wfExpandUrl( $url, PROTO_HTTP );
@@ -524,6 +532,9 @@ class ForeignAPIRepo extends FileRepo {
                $status = $req->execute();
 
                if ( $status->isOK() ) {
+                       $mtime = wfTimestampOrNull( TS_UNIX, $req->getResponseHeader( 'Last-Modified' ) );
+                       $mtime = $mtime ?: false;
+
                        return $req->getContent();
                } else {
                        $logger = LoggerFactory::getInstance( 'http' );
@@ -531,6 +542,7 @@ class ForeignAPIRepo extends FileRepo {
                                $status->getWikiText( false, false, 'en' ),
                                [ 'caller' => 'ForeignAPIRepo::httpGet' ]
                        );
+
                        return false;
                }
        }
@@ -548,7 +560,7 @@ class ForeignAPIRepo extends FileRepo {
         * @param string $target Used in cache key creation, mostly
         * @param array $query The query parameters for the API request
         * @param int $cacheTTL Time to live for the memcached caching
-        * @return null
+        * @return string|null
         */
        public function httpGetCached( $target, $query, $cacheTTL = 3600 ) {
                if ( $this->mApiBase ) {
@@ -557,28 +569,23 @@ class ForeignAPIRepo extends FileRepo {
                        $url = $this->makeUrl( $query, 'api' );
                }
 
-               if ( !isset( $this->mQueryCache[$url] ) ) {
-                       $data = ObjectCache::getMainWANInstance()->getWithSetCallback(
-                               $this->getLocalCacheKey( get_class( $this ), $target, md5( $url ) ),
-                               $cacheTTL,
-                               function () use ( $url ) {
-                                       return ForeignAPIRepo::httpGet( $url );
+               $cache = ObjectCache::getMainWANInstance();
+               return $cache->getWithSetCallback(
+                       $this->getLocalCacheKey( get_class( $this ), $target, md5( $url ) ),
+                       $cacheTTL,
+                       function ( $curValue, &$ttl ) use ( $url, $cache ) {
+                               $html = self::httpGet( $url, 'default', [], $mtime );
+                               if ( $html !== false ) {
+                                       $ttl = $mtime ? $cache->adaptiveTTL( $mtime, $ttl ) : $ttl;
+                               } else {
+                                       $ttl = $cache->adaptiveTTL( $mtime, $ttl );
+                                       $html = null; // caches negatives
                                }
-                       );
 
-                       if ( !$data ) {
-                               return null;
-                       }
-
-                       if ( count( $this->mQueryCache ) > 100 ) {
-                               // Keep the cache from growing infinitely
-                               $this->mQueryCache = [];
-                       }
-
-                       $this->mQueryCache[$url] = $data;
-               }
-
-               return $this->mQueryCache[$url];
+                               return $html;
+                       },
+                       [ 'pcTTL' => $cache::TTL_PROC_LONG ]
+               );
        }
 
        /**
index a59ca34..f8b1ed9 100644 (file)
@@ -63,7 +63,7 @@ class ForeignDBViaLBRepo extends LocalRepo {
         * @return IDatabase
         */
        function getSlaveDB() {
-               return wfGetDB( DB_SLAVE, [], $this->wiki );
+               return wfGetDB( DB_REPLICA, [], $this->wiki );
        }
 
        /**
index eaec151..fccb755 100644 (file)
@@ -453,11 +453,11 @@ class LocalRepo extends FileRepo {
        }
 
        /**
-        * Get a connection to the slave DB
+        * Get a connection to the replica DB
         * @return DatabaseBase
         */
        function getSlaveDB() {
-               return wfGetDB( DB_SLAVE );
+               return wfGetDB( DB_REPLICA );
        }
 
        /**
@@ -469,7 +469,7 @@ class LocalRepo extends FileRepo {
        }
 
        /**
-        * Get a callback to get a DB handle given an index (DB_SLAVE/DB_MASTER)
+        * Get a callback to get a DB handle given an index (DB_REPLICA/DB_MASTER)
         * @return Closure
         */
        protected function getDBFactory() {
index 08a40eb..d515b05 100644 (file)
@@ -177,8 +177,8 @@ class RepoGroup {
         * @param array $inputItems An array of titles, or an array of findFile() options with
         *    the "title" option giving the title. Example:
         *
-        *     $findItem = array( 'title' => $title, 'private' => true );
-        *     $findBatch = array( $findItem );
+        *     $findItem = [ 'title' => $title, 'private' => true ];
+        *     $findBatch = [ $findItem ];
         *     $repo->findFiles( $findBatch );
         *
         *    No title should appear in $items twice, as the result use titles as keys
index ca1ea84..d1e683a 100644 (file)
@@ -177,7 +177,7 @@ class ArchivedFile {
 
                if ( !$this->title || $this->title->getNamespace() == NS_FILE ) {
                        $this->dataLoaded = true; // set it here, to have also true on miss
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $row = $dbr->selectRow(
                                'filearchive',
                                self::selectFields(),
index 8175b58..425a08c 100644 (file)
@@ -1028,7 +1028,7 @@ abstract class File implements IDBAccessObject {
         * @param array $params An associative array of handler-specific parameters.
         *   Typical keys are width, height and page.
         * @param int $flags A bitfield, may contain self::RENDER_NOW to force rendering
-        * @return MediaTransformOutput|bool False on failure
+        * @return ThumbnailImage|MediaTransformOutput|bool False on failure
         */
        function transform( $params, $flags = 0 ) {
                global $wgThumbnailEpoch;
@@ -1324,7 +1324,7 @@ abstract class File implements IDBAccessObject {
        /**
         * Creates a temp FS file with the same extension and the thumbnail
         * @param string $thumbPath Thumbnail path
-        * @return TempFSFile
+        * @return TempFSFile|null
         */
        protected function makeTransformTmpFile( $thumbPath ) {
                $thumbExt = FileBackend::extensionFromPath( $thumbPath );
index 066da60..d63a91b 100644 (file)
 
 use \MediaWiki\Logger\LoggerFactory;
 
-/**
- * Bump this number when serialized cache records may be incompatible.
- */
-define( 'MW_FILE_VERSION', 9 );
-
 /**
  * Class to represent a local file in the wiki's own database
  *
@@ -46,6 +41,8 @@ define( 'MW_FILE_VERSION', 9 );
  * @ingroup FileAbstraction
  */
 class LocalFile extends File {
+       const VERSION = 10; // cache version
+
        const CACHE_FIELD_MAX_LEN = 1000;
 
        /** @var bool Does the file exist on disk? (loadFromXxx) */
@@ -117,6 +114,9 @@ class LocalFile extends File {
        /** @var bool Whether the row was upgraded on load */
        private $upgraded;
 
+       /** @var bool Whether the row was scheduled to upgrade on load */
+       private $upgrading;
+
        /** @var bool True if the image row is locked */
        private $locked;
 
@@ -129,6 +129,8 @@ class LocalFile extends File {
        // @note: higher than IDBAccessObject constants
        const LOAD_ALL = 16; // integer; load all the lazy fields too (like metadata)
 
+       const ATOMIC_SECTION_LOCK = 'LocalFile::lockingTransaction';
+
        /**
         * Create a LocalFile from a title
         * Do not call this except from inside a repo class.
@@ -235,77 +237,71 @@ class LocalFile extends File {
         * @return string|bool
         */
        function getCacheKey() {
-               $hashedName = md5( $this->getName() );
-
-               return $this->repo->getSharedCacheKey( 'file', $hashedName );
+               return $this->repo->getSharedCacheKey( 'file', sha1( $this->getName() ) );
        }
 
        /**
-        * Try to load file metadata from memcached. Returns true on success.
-        * @return bool
+        * Try to load file metadata from memcached, falling back to the database
         */
        private function loadFromCache() {
                $this->dataLoaded = false;
                $this->extraDataLoaded = false;
-               $key = $this->getCacheKey();
 
+               $key = $this->getCacheKey();
                if ( !$key ) {
-                       return false;
-               }
-
-               $cache = ObjectCache::getMainWANInstance();
-               $cachedValues = $cache->get( $key );
+                       $this->loadFromDB( self::READ_NORMAL );
 
-               // Check if the key existed and belongs to this version of MediaWiki
-               if ( is_array( $cachedValues ) && $cachedValues['version'] == MW_FILE_VERSION ) {
-                       $this->fileExists = $cachedValues['fileExists'];
-                       if ( $this->fileExists ) {
-                               $this->setProps( $cachedValues );
-                       }
-                       $this->dataLoaded = true;
-                       $this->extraDataLoaded = true;
-                       foreach ( $this->getLazyCacheFields( '' ) as $field ) {
-                               $this->extraDataLoaded = $this->extraDataLoaded && isset( $cachedValues[$field] );
-                       }
+                       return;
                }
 
-               return $this->dataLoaded;
-       }
-
-       /**
-        * Save the file metadata to memcached
-        */
-       private function saveToCache() {
-               $this->load();
+               $cache = ObjectCache::getMainWANInstance();
+               $cachedValues = $cache->getWithSetCallback(
+                       $key,
+                       $cache::TTL_WEEK,
+                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $cache ) {
+                               $setOpts += Database::getCacheSetOptions( $this->repo->getSlaveDB() );
+
+                               $this->loadFromDB( self::READ_NORMAL );
+
+                               $fields = $this->getCacheFields( '' );
+                               $cacheVal['fileExists'] = $this->fileExists;
+                               if ( $this->fileExists ) {
+                                       foreach ( $fields as $field ) {
+                                               $cacheVal[$field] = $this->$field;
+                                       }
+                               }
+                               // Strip off excessive entries from the subset of fields that can become large.
+                               // If the cache value gets to large it will not fit in memcached and nothing will
+                               // get cached at all, causing master queries for any file access.
+                               foreach ( $this->getLazyCacheFields( '' ) as $field ) {
+                                       if ( isset( $cacheVal[$field] )
+                                               && strlen( $cacheVal[$field] ) > 100 * 1024
+                                       ) {
+                                               unset( $cacheVal[$field] ); // don't let the value get too big
+                                       }
+                               }
 
-               $key = $this->getCacheKey();
-               if ( !$key ) {
-                       return;
-               }
+                               if ( $this->fileExists ) {
+                                       $ttl = $cache->adaptiveTTL( wfTimestamp( TS_UNIX, $this->timestamp ), $ttl );
+                               } else {
+                                       $ttl = $cache::TTL_DAY;
+                               }
 
-               $fields = $this->getCacheFields( '' );
-               $cacheVal = [ 'version' => MW_FILE_VERSION ];
-               $cacheVal['fileExists'] = $this->fileExists;
+                               return $cacheVal;
+                       },
+                       [ 'version' => self::VERSION ]
+               );
 
+               $this->fileExists = $cachedValues['fileExists'];
                if ( $this->fileExists ) {
-                       foreach ( $fields as $field ) {
-                               $cacheVal[$field] = $this->$field;
-                       }
+                       $this->setProps( $cachedValues );
                }
 
-               // Strip off excessive entries from the subset of fields that can become large.
-               // If the cache value gets to large it will not fit in memcached and nothing will
-               // get cached at all, causing master queries for any file access.
+               $this->dataLoaded = true;
+               $this->extraDataLoaded = true;
                foreach ( $this->getLazyCacheFields( '' ) as $field ) {
-                       if ( isset( $cacheVal[$field] ) && strlen( $cacheVal[$field] ) > 100 * 1024 ) {
-                               unset( $cacheVal[$field] ); // don't let the value get too big
-                       }
+                       $this->extraDataLoaded = $this->extraDataLoaded && isset( $cachedValues[$field] );
                }
-
-               // Cache presence for 1 week and negatives for 1 day
-               $ttl = $this->fileExists ? 86400 * 7 : 86400;
-               $opts = Database::getCacheSetOptions( $this->repo->getSlaveDB() );
-               ObjectCache::getMainWANInstance()->set( $key, $cacheVal, $ttl, $opts );
        }
 
        /**
@@ -435,16 +431,18 @@ class LocalFile extends File {
        private function loadFieldsWithTimestamp( $dbr, $fname ) {
                $fieldMap = false;
 
-               $row = $dbr->selectRow( 'image', $this->getLazyCacheFields( 'img_' ),
-                       [ 'img_name' => $this->getName(), 'img_timestamp' => $this->getTimestamp() ],
-                       $fname );
+               $row = $dbr->selectRow( 'image', $this->getLazyCacheFields( 'img_' ), [
+                               'img_name' => $this->getName(),
+                               'img_timestamp' => $dbr->timestamp( $this->getTimestamp() )
+                       ], $fname );
                if ( $row ) {
                        $fieldMap = $this->unprefixRow( $row, 'img_' );
                } else {
                        # File may have been uploaded over in the meantime; check the old versions
-                       $row = $dbr->selectRow( 'oldimage', $this->getLazyCacheFields( 'oi_' ),
-                               [ 'oi_name' => $this->getName(), 'oi_timestamp' => $this->getTimestamp() ],
-                               $fname );
+                       $row = $dbr->selectRow( 'oldimage', $this->getLazyCacheFields( 'oi_' ), [
+                                       'oi_name' => $this->getName(),
+                                       'oi_timestamp' => $dbr->timestamp( $this->getTimestamp() )
+                               ], $fname );
                        if ( $row ) {
                                $fieldMap = $this->unprefixRow( $row, 'oi_' );
                        }
@@ -540,12 +538,13 @@ class LocalFile extends File {
         */
        function load( $flags = 0 ) {
                if ( !$this->dataLoaded ) {
-                       if ( ( $flags & self::READ_LATEST ) || !$this->loadFromCache() ) {
+                       if ( $flags & self::READ_LATEST ) {
                                $this->loadFromDB( $flags );
-                               $this->saveToCache();
+                       } else {
+                               $this->loadFromCache();
                        }
-                       $this->dataLoaded = true;
                }
+
                if ( ( $flags & self::LOAD_ALL ) && !$this->extraDataLoaded ) {
                        // @note: loads on name/timestamp to reduce race condition problems
                        $this->loadExtraFromDB();
@@ -557,37 +556,43 @@ class LocalFile extends File {
         */
        function maybeUpgradeRow() {
                global $wgUpdateCompatibleMetadata;
-               if ( wfReadOnly() ) {
+
+               if ( wfReadOnly() || $this->upgrading ) {
                        return;
                }
 
                $upgrade = false;
-               if ( is_null( $this->media_type ) ||
-                       $this->mime == 'image/svg'
-               ) {
+               if ( is_null( $this->media_type ) || $this->mime == 'image/svg' ) {
                        $upgrade = true;
                } else {
                        $handler = $this->getHandler();
                        if ( $handler ) {
                                $validity = $handler->isMetadataValid( $this, $this->getMetadata() );
-                               if ( $validity === MediaHandler::METADATA_BAD
-                                       || ( $validity === MediaHandler::METADATA_COMPATIBLE && $wgUpdateCompatibleMetadata )
-                               ) {
+                               if ( $validity === MediaHandler::METADATA_BAD ) {
                                        $upgrade = true;
+                               } elseif ( $validity === MediaHandler::METADATA_COMPATIBLE ) {
+                                       $upgrade = $wgUpdateCompatibleMetadata;
                                }
                        }
                }
 
                if ( $upgrade ) {
-                       try {
-                               $this->upgradeRow();
-                       } catch ( LocalFileLockError $e ) {
-                               // let the other process handle it (or do it next time)
-                       }
-                       $this->upgraded = true; // avoid rework/retries
+                       $this->upgrading = true;
+                       // Defer updates unless in auto-commit CLI mode
+                       DeferredUpdates::addCallableUpdate( function() {
+                               $this->upgrading = false; // avoid duplicate updates
+                               try {
+                                       $this->upgradeRow();
+                               } catch ( LocalFileLockError $e ) {
+                                       // let the other process handle it (or do it next time)
+                               }
+                       } );
                }
        }
 
+       /**
+        * @return bool Whether upgradeRow() ran for this object
+        */
        function getUpgraded() {
                return $this->upgraded;
        }
@@ -637,7 +642,7 @@ class LocalFile extends File {
                $this->invalidateCache();
 
                $this->unlock(); // done
-
+               $this->upgraded = true; // avoid rework/retries
        }
 
        /**
@@ -755,7 +760,7 @@ class LocalFile extends File {
 
                if ( $type == 'text' ) {
                        return $this->user_text;
-               } elseif ( $type == 'id' ) {
+               } else { // id
                        return (int)$this->user;
                }
        }
@@ -962,6 +967,33 @@ class LocalFile extends File {
                DeferredUpdates::addUpdate( new CdnCacheUpdate( $urls ), DeferredUpdates::PRESEND );
        }
 
+       /**
+        * Prerenders a configurable set of thumbnails
+        *
+        * @since 1.28
+        */
+       public function prerenderThumbnails() {
+               global $wgUploadThumbnailRenderMap;
+
+               $jobs = [];
+
+               $sizes = $wgUploadThumbnailRenderMap;
+               rsort( $sizes );
+
+               foreach ( $sizes as $size ) {
+                       if ( $this->isVectorized() || $this->getWidth() > $size ) {
+                               $jobs[] = new ThumbnailRenderJob(
+                                       $this->getTitle(),
+                                       [ 'transformParams' => [ 'width' => $size ] ]
+                               );
+                       }
+               }
+
+               if ( $jobs ) {
+                       JobQueueGroup::singleton()->lazyPush( $jobs );
+               }
+       }
+
        /**
         * Delete a list of thumbnails visible at urls
         * @param string $dir Base dir of the files.
@@ -1420,97 +1452,106 @@ class LocalFile extends File {
                # Do some cache purges after final commit so that:
                # a) Changes are more likely to be seen post-purge
                # b) They won't cause rollback of the log publish/update above
-               $that = $this;
-               $dbw->onTransactionIdle( function () use (
-                       $that, $reupload, $wikiPage, $newPageContent, $comment, $user, $logEntry, $logId, $descId, $tags
-               ) {
-                       # Update memcache after the commit
-                       $that->invalidateCache();
-
-                       $updateLogPage = false;
-                       if ( $newPageContent ) {
-                               # New file page; create the description page.
-                               # There's already a log entry, so don't make a second RC entry
-                               # CDN and file cache for the description page are purged by doEditContent.
-                               $status = $wikiPage->doEditContent(
-                                       $newPageContent,
-                                       $comment,
-                                       EDIT_NEW | EDIT_SUPPRESS_RC,
-                                       false,
-                                       $user
-                               );
-
-                               if ( isset( $status->value['revision'] ) ) {
-                                       // Associate new page revision id
-                                       $logEntry->setAssociatedRevId( $status->value['revision']->getId() );
-                               }
-                               // This relies on the resetArticleID() call in WikiPage::insertOn(),
-                               // which is triggered on $descTitle by doEditContent() above.
-                               if ( isset( $status->value['revision'] ) ) {
-                                       /** @var $rev Revision */
-                                       $rev = $status->value['revision'];
-                                       $updateLogPage = $rev->getPage();
-                               }
-                       } else {
-                               # Existing file page: invalidate description page cache
-                               $wikiPage->getTitle()->invalidateCache();
-                               $wikiPage->getTitle()->purgeSquid();
-                               # Allow the new file version to be patrolled from the page footer
-                               Article::purgePatrolFooterCache( $descId );
-                       }
+               DeferredUpdates::addUpdate(
+                       new AutoCommitUpdate(
+                               $dbw,
+                               __METHOD__,
+                               function () use (
+                                       $reupload, $wikiPage, $newPageContent, $comment, $user,
+                                       $logEntry, $logId, $descId, $tags
+                               ) {
+                                       # Update memcache after the commit
+                                       $this->invalidateCache();
+
+                                       $updateLogPage = false;
+                                       if ( $newPageContent ) {
+                                               # New file page; create the description page.
+                                               # There's already a log entry, so don't make a second RC entry
+                                               # CDN and file cache for the description page are purged by doEditContent.
+                                               $status = $wikiPage->doEditContent(
+                                                       $newPageContent,
+                                                       $comment,
+                                                       EDIT_NEW | EDIT_SUPPRESS_RC,
+                                                       false,
+                                                       $user
+                                               );
+
+                                               if ( isset( $status->value['revision'] ) ) {
+                                                       // Associate new page revision id
+                                                       $logEntry->setAssociatedRevId( $status->value['revision']->getId() );
+                                               }
+                                               // This relies on the resetArticleID() call in WikiPage::insertOn(),
+                                               // which is triggered on $descTitle by doEditContent() above.
+                                               if ( isset( $status->value['revision'] ) ) {
+                                                       /** @var $rev Revision */
+                                                       $rev = $status->value['revision'];
+                                                       $updateLogPage = $rev->getPage();
+                                               }
+                                       } else {
+                                               # Existing file page: invalidate description page cache
+                                               $wikiPage->getTitle()->invalidateCache();
+                                               $wikiPage->getTitle()->purgeSquid();
+                                               # Allow the new file version to be patrolled from the page footer
+                                               Article::purgePatrolFooterCache( $descId );
+                                       }
 
-                       # Update associated rev id. This should be done by $logEntry->insert() earlier,
-                       # but setAssociatedRevId() wasn't called at that point yet...
-                       $logParams = $logEntry->getParameters();
-                       $logParams['associated_rev_id'] = $logEntry->getAssociatedRevId();
-                       $update = [ 'log_params' => LogEntryBase::makeParamBlob( $logParams ) ];
-                       if ( $updateLogPage ) {
-                               # Also log page, in case where we just created it above
-                               $update['log_page'] = $updateLogPage;
-                       }
-                       $that->getRepo()->getMasterDB()->update(
-                               'logging',
-                               $update,
-                               [ 'log_id' => $logId ],
-                               __METHOD__
-                       );
-                       $that->getRepo()->getMasterDB()->insert(
-                               'log_search',
-                               [
-                                       'ls_field' => 'associated_rev_id',
-                                       'ls_value' => $logEntry->getAssociatedRevId(),
-                                       'ls_log_id' => $logId,
-                               ],
-                               __METHOD__
-                       );
+                                       # Update associated rev id. This should be done by $logEntry->insert() earlier,
+                                       # but setAssociatedRevId() wasn't called at that point yet...
+                                       $logParams = $logEntry->getParameters();
+                                       $logParams['associated_rev_id'] = $logEntry->getAssociatedRevId();
+                                       $update = [ 'log_params' => LogEntryBase::makeParamBlob( $logParams ) ];
+                                       if ( $updateLogPage ) {
+                                               # Also log page, in case where we just created it above
+                                               $update['log_page'] = $updateLogPage;
+                                       }
+                                       $this->getRepo()->getMasterDB()->update(
+                                               'logging',
+                                               $update,
+                                               [ 'log_id' => $logId ],
+                                               __METHOD__
+                                       );
+                                       $this->getRepo()->getMasterDB()->insert(
+                                               'log_search',
+                                               [
+                                                       'ls_field' => 'associated_rev_id',
+                                                       'ls_value' => $logEntry->getAssociatedRevId(),
+                                                       'ls_log_id' => $logId,
+                                               ],
+                                               __METHOD__
+                                       );
+
+                                       # Add change tags, if any
+                                       if ( $tags ) {
+                                               $logEntry->setTags( $tags );
+                                       }
 
-                       # Add change tags, if any
-                       if ( $tags ) {
-                               $logEntry->setTags( $tags );
-                       }
+                                       # Uploads can be patrolled
+                                       $logEntry->setIsPatrollable( true );
 
-                       # Uploads can be patrolled
-                       $logEntry->setIsPatrollable( true );
+                                       # Now that the log entry is up-to-date, make an RC entry.
+                                       $logEntry->publish( $logId );
 
-                       # Now that the log entry is up-to-date, make an RC entry.
-                       $logEntry->publish( $logId );
+                                       # Run hook for other updates (typically more cache purging)
+                                       Hooks::run( 'FileUpload', [ $this, $reupload, !$newPageContent ] );
 
-                       # Run hook for other updates (typically more cache purging)
-                       Hooks::run( 'FileUpload', [ $that, $reupload, !$newPageContent ] );
+                                       if ( $reupload ) {
+                                               # Delete old thumbnails
+                                               $this->purgeThumbnails();
+                                               # Remove the old file from the CDN cache
+                                               DeferredUpdates::addUpdate(
+                                                       new CdnCacheUpdate( [ $this->getUrl() ] ),
+                                                       DeferredUpdates::PRESEND
+                                               );
+                                       } else {
+                                               # Update backlink pages pointing to this title if created
+                                               LinksUpdate::queueRecursiveJobsForTable( $this->getTitle(), 'imagelinks' );
+                                       }
 
-                       if ( $reupload ) {
-                               # Delete old thumbnails
-                               $that->purgeThumbnails();
-                               # Remove the old file from the CDN cache
-                               DeferredUpdates::addUpdate(
-                                       new CdnCacheUpdate( [ $that->getUrl() ] ),
-                                       DeferredUpdates::PRESEND
-                               );
-                       } else {
-                               # Update backlink pages pointing to this title if created
-                               LinksUpdate::queueRecursiveJobsForTable( $that->getTitle(), 'imagelinks' );
-                       }
-               } );
+                                       $this->prerenderThumbnails();
+                               }
+                       ),
+                       DeferredUpdates::PRESEND
+               );
 
                if ( !$reupload ) {
                        # This is a new file, so update the image count
@@ -1574,7 +1615,9 @@ class LocalFile extends File {
                        $sha1 = $repo->isVirtualUrl( $srcPath )
                                ? $repo->getFileSha1( $srcPath )
                                : FSFile::getSha1Base36FromPath( $srcPath );
-                       $dst = $repo->getBackend()->getPathForSHA1( $sha1 );
+                       /** @var FileBackendDBRepoWrapper $wrapperBackend */
+                       $wrapperBackend = $repo->getBackend();
+                       $dst = $wrapperBackend->getPathForSHA1( $sha1 );
                        $status = $repo->quickImport( $src, $dst );
                        if ( $flags & File::DELETE_SOURCE ) {
                                unlink( $srcPath );
@@ -1635,16 +1678,20 @@ class LocalFile extends File {
                // Purge the source and target files...
                $oldTitleFile = wfLocalFile( $this->title );
                $newTitleFile = wfLocalFile( $target );
-               // Hack: the lock()/unlock() pair is nested in a transaction so the locking is not
-               // tied to BEGIN/COMMIT. To avoid slow purges in the transaction, move them outside.
-               $this->getRepo()->getMasterDB()->onTransactionIdle(
-                       function () use ( $oldTitleFile, $newTitleFile, $archiveNames ) {
-                               $oldTitleFile->purgeEverything();
-                               foreach ( $archiveNames as $archiveName ) {
-                                       $oldTitleFile->purgeOldThumbnails( $archiveName );
+               // To avoid slow purges in the transaction, move them outside...
+               DeferredUpdates::addUpdate(
+                       new AutoCommitUpdate(
+                               $this->getRepo()->getMasterDB(),
+                               __METHOD__,
+                               function () use ( $oldTitleFile, $newTitleFile, $archiveNames ) {
+                                       $oldTitleFile->purgeEverything();
+                                       foreach ( $archiveNames as $archiveName ) {
+                                               $oldTitleFile->purgeOldThumbnails( $archiveName );
+                                       }
+                                       $newTitleFile->purgeEverything();
                                }
-                               $newTitleFile->purgeEverything();
-                       }
+                       ),
+                       DeferredUpdates::PRESEND
                );
 
                if ( $status->isOK() ) {
@@ -1680,7 +1727,7 @@ class LocalFile extends File {
 
                $this->lock(); // begin
                $batch->addCurrent();
-               # Get old version relative paths
+               // Get old version relative paths
                $archiveNames = $batch->addOlds();
                $status = $batch->execute();
                $this->unlock(); // done
@@ -1689,16 +1736,19 @@ class LocalFile extends File {
                        DeferredUpdates::addUpdate( SiteStatsUpdate::factory( [ 'images' => -1 ] ) );
                }
 
-               // Hack: the lock()/unlock() pair is nested in a transaction so the locking is not
-               // tied to BEGIN/COMMIT. To avoid slow purges in the transaction, move them outside.
-               $that = $this;
-               $this->getRepo()->getMasterDB()->onTransactionIdle(
-                       function () use ( $that, $archiveNames ) {
-                               $that->purgeEverything();
-                               foreach ( $archiveNames as $archiveName ) {
-                                       $that->purgeOldThumbnails( $archiveName );
+               // To avoid slow purges in the transaction, move them outside...
+               DeferredUpdates::addUpdate(
+                       new AutoCommitUpdate(
+                               $this->getRepo()->getMasterDB(),
+                               __METHOD__,
+                               function () use ( $archiveNames ) {
+                                       $this->purgeEverything();
+                                       foreach ( $archiveNames as $archiveName ) {
+                                               $this->purgeOldThumbnails( $archiveName );
+                                       }
                                }
-                       }
+                       ),
+                       DeferredUpdates::PRESEND
                );
 
                // Purge the CDN
@@ -1905,71 +1955,87 @@ class LocalFile extends File {
        }
 
        /**
-        * Start a transaction and lock the image for update
-        * Increments a reference counter if the lock is already held
+        * @return Status
+        * @since 1.28
+        */
+       public function acquireFileLock() {
+               return $this->getRepo()->getBackend()->lockFiles(
+                       [ $this->getPath() ], LockManager::LOCK_EX, 10
+               );
+       }
+
+       /**
+        * @return Status
+        * @since 1.28
+        */
+       public function releaseFileLock() {
+               return $this->getRepo()->getBackend()->unlockFiles(
+                       [ $this->getPath() ], LockManager::LOCK_EX
+               );
+       }
+
+       /**
+        * Start an atomic DB section and lock the image for update
+        * or increments a reference counter if the lock is already held
+        *
+        * This method should not be used outside of LocalFile/LocalFile*Batch
+        *
         * @throws LocalFileLockError Throws an error if the lock was not acquired
         * @return bool Whether the file lock owns/spawned the DB transaction
         */
-       function lock() {
+       public function lock() {
                if ( !$this->locked ) {
                        $logger = LoggerFactory::getInstance( 'LocalFile' );
+
                        $dbw = $this->repo->getMasterDB();
-                       if ( !$dbw->trxLevel() ) {
-                               $dbw->begin( __METHOD__ );
-                               $this->lockedOwnTrx = true;
-                       }
+                       $makesTransaction = !$dbw->trxLevel();
+                       $dbw->startAtomic( self::ATOMIC_SECTION_LOCK );
                        // Bug 54736: use simple lock to handle when the file does not exist.
                        // SELECT FOR UPDATE prevents changes, not other SELECTs with FOR UPDATE.
                        // Also, that would cause contention on INSERT of similarly named rows.
-                       $backend = $this->getRepo()->getBackend();
-                       $lockPaths = [ $this->getPath() ]; // represents all versions of the file
-                       $status = $backend->lockFiles( $lockPaths, LockManager::LOCK_EX, 10 );
+                       $status = $this->acquireFileLock(); // represents all versions of the file
                        if ( !$status->isGood() ) {
-                               if ( $this->lockedOwnTrx ) {
-                                       $dbw->rollback( __METHOD__ );
-                               }
+                               $dbw->endAtomic( self::ATOMIC_SECTION_LOCK );
                                $logger->warning( "Failed to lock '{file}'", [ 'file' => $this->name ] );
 
                                throw new LocalFileLockError( $status );
                        }
-                       // Release the lock *after* commit to avoid row-level contention
-                       $this->locked++;
-                       $dbw->onTransactionIdle( function () use ( $backend, $lockPaths, $logger ) {
-                               $status = $backend->unlockFiles( $lockPaths, LockManager::LOCK_EX );
+                       // Release the lock *after* commit to avoid row-level contention.
+                       // Make sure it triggers on rollback() as well as commit() (T132921).
+                       $dbw->onTransactionResolution( function () use ( $logger ) {
+                               $status = $this->releaseFileLock();
                                if ( !$status->isGood() ) {
                                        $logger->error( "Failed to unlock '{file}'", [ 'file' => $this->name ] );
                                }
                        } );
+                       // Callers might care if the SELECT snapshot is safely fresh
+                       $this->lockedOwnTrx = $makesTransaction;
                }
 
+               $this->locked++;
+
                return $this->lockedOwnTrx;
        }
 
        /**
-        * Decrement the lock reference count. If the reference count is reduced to zero, commits
-        * the transaction and thereby releases the image lock.
+        * Decrement the lock reference count and end the atomic section if it reaches zero
+        *
+        * This method should not be used outside of LocalFile/LocalFile*Batch
+        *
+        * The commit and loc release will happen when no atomic sections are active, which
+        * may happen immediately or at some point after calling this
         */
-       function unlock() {
+       public function unlock() {
                if ( $this->locked ) {
                        --$this->locked;
-                       if ( !$this->locked && $this->lockedOwnTrx ) {
+                       if ( !$this->locked ) {
                                $dbw = $this->repo->getMasterDB();
-                               $dbw->commit( __METHOD__ );
+                               $dbw->endAtomic( self::ATOMIC_SECTION_LOCK );
                                $this->lockedOwnTrx = false;
                        }
                }
        }
 
-       /**
-        * Roll back the DB transaction and mark the image unlocked
-        */
-       function unlockAndRollback() {
-               $this->locked = false;
-               $dbw = $this->repo->getMasterDB();
-               $dbw->rollback( __METHOD__ );
-               $this->lockedOwnTrx = false;
-       }
-
        /**
         * @return Status
         */
@@ -2146,8 +2212,9 @@ class LocalFileDeleteBatch {
        }
 
        protected function doDBInserts() {
+               $now = time();
                $dbw = $this->file->repo->getMasterDB();
-               $encTimestamp = $dbw->addQuotes( $dbw->timestamp() );
+               $encTimestamp = $dbw->addQuotes( $dbw->timestamp( $now ) );
                $encUserId = $dbw->addQuotes( $this->user->getId() );
                $encReason = $dbw->addQuotes( $this->reason );
                $encGroup = $dbw->addQuotes( 'deleted' );
@@ -2169,15 +2236,15 @@ class LocalFileDeleteBatch {
                }
 
                if ( $deleteCurrent ) {
-                       $concat = $dbw->buildConcat( [ "img_sha1", $encExt ] );
-                       $where = [ 'img_name' => $this->file->getName() ];
-                       $dbw->insertSelect( 'filearchive', 'image',
+                       $dbw->insertSelect(
+                               'filearchive',
+                               'image',
                                [
                                        'fa_storage_group' => $encGroup,
                                        'fa_storage_key' => $dbw->conditional(
                                                [ 'img_sha1' => '' ],
                                                $dbw->addQuotes( '' ),
-                                               $concat
+                                               $dbw->buildConcat( [ "img_sha1", $encExt ] )
                                        ),
                                        'fa_deleted_user' => $encUserId,
                                        'fa_deleted_timestamp' => $encTimestamp,
@@ -2198,44 +2265,56 @@ class LocalFileDeleteBatch {
                                        'fa_user' => 'img_user',
                                        'fa_user_text' => 'img_user_text',
                                        'fa_timestamp' => 'img_timestamp',
-                                       'fa_sha1' => 'img_sha1',
-                               ], $where, __METHOD__ );
+                                       'fa_sha1' => 'img_sha1'
+                               ],
+                               [ 'img_name' => $this->file->getName() ],
+                               __METHOD__
+                       );
                }
 
                if ( count( $oldRels ) ) {
-                       $concat = $dbw->buildConcat( [ "oi_sha1", $encExt ] );
-                       $where = [
-                               'oi_name' => $this->file->getName(),
-                               'oi_archive_name' => array_keys( $oldRels ) ];
-                       $dbw->insertSelect( 'filearchive', 'oldimage',
+                       $res = $dbw->select(
+                               'oldimage',
+                               OldLocalFile::selectFields(),
                                [
-                                       'fa_storage_group' => $encGroup,
-                                       'fa_storage_key' => $dbw->conditional(
-                                               [ 'oi_sha1' => '' ],
-                                               $dbw->addQuotes( '' ),
-                                               $concat
-                                       ),
-                                       'fa_deleted_user' => $encUserId,
-                                       'fa_deleted_timestamp' => $encTimestamp,
-                                       'fa_deleted_reason' => $encReason,
-                                       'fa_deleted' => $this->suppress ? $bitfield : 'oi_deleted',
-
-                                       'fa_name' => 'oi_name',
-                                       'fa_archive_name' => 'oi_archive_name',
-                                       'fa_size' => 'oi_size',
-                                       'fa_width' => 'oi_width',
-                                       'fa_height' => 'oi_height',
-                                       'fa_metadata' => 'oi_metadata',
-                                       'fa_bits' => 'oi_bits',
-                                       'fa_media_type' => 'oi_media_type',
-                                       'fa_major_mime' => 'oi_major_mime',
-                                       'fa_minor_mime' => 'oi_minor_mime',
-                                       'fa_description' => 'oi_description',
-                                       'fa_user' => 'oi_user',
-                                       'fa_user_text' => 'oi_user_text',
-                                       'fa_timestamp' => 'oi_timestamp',
-                                       'fa_sha1' => 'oi_sha1',
-                               ], $where, __METHOD__ );
+                                       'oi_name' => $this->file->getName(),
+                                       'oi_archive_name' => array_keys( $oldRels )
+                               ],
+                               __METHOD__,
+                               [ 'FOR UPDATE' ]
+                       );
+                       $rowsInsert = [];
+                       foreach ( $res as $row ) {
+                               $rowsInsert[] = [
+                                       // Deletion-specific fields
+                                       'fa_storage_group' => 'deleted',
+                                       'fa_storage_key' => ( $row->oi_sha1 === '' )
+                                               ? ''
+                                               : "{$row->oi_sha1}{$dotExt}",
+                                       'fa_deleted_user' => $this->user->getId(),
+                                       'fa_deleted_timestamp' => $dbw->timestamp( $now ),
+                                       'fa_deleted_reason' => $this->reason,
+                                       // Counterpart fields
+                                       'fa_deleted' => $this->suppress ? $bitfield : $row->oi_deleted,
+                                       'fa_name' => $row->oi_name,
+                                       'fa_archive_name' => $row->oi_archive_name,
+                                       'fa_size' => $row->oi_size,
+                                       'fa_width' => $row->oi_width,
+                                       'fa_height' => $row->oi_height,
+                                       'fa_metadata' => $row->oi_metadata,
+                                       'fa_bits' => $row->oi_bits,
+                                       'fa_media_type' => $row->oi_media_type,
+                                       'fa_major_mime' => $row->oi_major_mime,
+                                       'fa_minor_mime' => $row->oi_minor_mime,
+                                       'fa_description' => $row->oi_description,
+                                       'fa_user' => $row->oi_user,
+                                       'fa_user_text' => $row->oi_user_text,
+                                       'fa_timestamp' => $row->oi_timestamp,
+                                       'fa_sha1' => $row->oi_sha1
+                               ];
+                       }
+
+                       $dbw->insert( 'filearchive', $rowsInsert, __METHOD__ );
                }
        }
 
@@ -2280,13 +2359,6 @@ class LocalFileDeleteBatch {
                        }
                }
 
-               // Lock the filearchive rows so that the files don't get deleted by a cleanup operation
-               // We acquire this lock by running the inserts now, before the file operations.
-               // This potentially has poor lock contention characteristics -- an alternative
-               // scheme would be to insert stub filearchive entries with no fa_name and commit
-               // them in a separate transaction, then run the file ops, then update the fa_name fields.
-               $this->doDBInserts();
-
                if ( !$repo->hasSha1Storage() ) {
                        // Removes non-existent file from the batch, so we don't get errors.
                        // This also handles files in the 'deleted' zone deleted via revision deletion.
@@ -2299,21 +2371,20 @@ class LocalFileDeleteBatch {
 
                        // Execute the file deletion batch
                        $status = $this->file->repo->deleteBatch( $this->deletionBatch );
-
                        if ( !$status->isGood() ) {
                                $this->status->merge( $status );
                        }
                }
 
                if ( !$this->status->isOK() ) {
-                       // Critical file deletion error
-                       // Roll back inserts, release lock and abort
-                       // TODO: delete the defunct filearchive rows if we are using a non-transactional DB
-                       $this->file->unlockAndRollback();
+                       // Critical file deletion error; abort
+                       $this->file->unlock();
 
                        return $this->status;
                }
 
+               // Copy the image/oldimage rows to filearchive
+               $this->doDBInserts();
                // Delete image/oldimage rows
                $this->doDBDeletes();
 
@@ -2417,6 +2488,7 @@ class LocalFileRestoreBatch {
         * @return FileRepoStatus
         */
        public function execute() {
+               /** @var Language */
                global $wgLang;
 
                $repo = $this->file->getRepo();
@@ -2534,8 +2606,9 @@ class LocalFileRestoreBatch {
 
                                // The live (current) version cannot be hidden!
                                if ( !$this->unsuppress && $row->fa_deleted ) {
-                                       $storeBatch[] = [ $deletedUrl, 'public', $destRel ];
-                                       $this->cleanupBatch[] = $row->fa_storage_key;
+                                       $status->fatal( 'undeleterevdel' );
+                                       $this->file->unlock();
+                                       return $status;
                                }
                        } else {
                                $archiveName = $row->fa_archive_name;
@@ -2853,33 +2926,30 @@ class LocalFileMoveBatch {
        public function execute() {
                $repo = $this->file->repo;
                $status = $repo->newGood();
+               $destFile = wfLocalFile( $this->target );
+
+               $this->file->lock(); // begin
+               $destFile->lock(); // quickly fail if destination is not available
 
                $triplets = $this->getMoveTriplets();
                $checkStatus = $this->removeNonexistentFiles( $triplets );
                if ( !$checkStatus->isGood() ) {
-                       $status->merge( $checkStatus );
+                       $destFile->unlock();
+                       $this->file->unlock();
+                       $status->merge( $checkStatus ); // couldn't talk to file backend
                        return $status;
                }
                $triplets = $checkStatus->value;
-               $destFile = wfLocalFile( $this->target );
 
-               $this->file->lock(); // begin
-               $destFile->lock(); // quickly fail if destination is not available
-               // Rename the file versions metadata in the DB.
-               // This implicitly locks the destination file, which avoids race conditions.
-               // If we moved the files from A -> C before DB updates, another process could
-               // move files from B -> C at this point, causing storeBatch() to fail and thus
-               // cleanupTarget() to trigger. It would delete the C files and cause data loss.
-               $statusDb = $this->doDBUpdates();
+               // Verify the file versions metadata in the DB.
+               $statusDb = $this->verifyDBUpdates();
                if ( !$statusDb->isGood() ) {
                        $destFile->unlock();
-                       $this->file->unlockAndRollback();
+                       $this->file->unlock();
                        $statusDb->ok = false;
 
                        return $statusDb;
                }
-               wfDebugLog( 'imagemove', "Renamed {$this->file->getName()} in database: " .
-                       "{$statusDb->successCount} successes, {$statusDb->failCount} failures" );
 
                if ( !$repo->hasSha1Storage() ) {
                        // Copy the files into their new location.
@@ -2892,7 +2962,7 @@ class LocalFileMoveBatch {
                                // Delete any files copied over (while the destination is still locked)
                                $this->cleanupTarget( $triplets );
                                $destFile->unlock();
-                               $this->file->unlockAndRollback(); // unlocks the destination
+                               $this->file->unlock();
                                wfDebugLog( 'imagemove', "Error in moving files: "
                                        . $statusMove->getWikiText( false, false, 'en' ) );
                                $statusMove->ok = false;
@@ -2902,6 +2972,12 @@ class LocalFileMoveBatch {
                        $status->merge( $statusMove );
                }
 
+               // Rename the file versions metadata in the DB.
+               $this->doDBUpdates();
+
+               wfDebugLog( 'imagemove', "Renamed {$this->file->getName()} in database: " .
+                       "{$statusDb->successCount} successes, {$statusDb->failCount} failures" );
+
                $destFile->unlock();
                $this->file->unlock(); // done
 
@@ -2914,33 +2990,62 @@ class LocalFileMoveBatch {
        }
 
        /**
-        * Do the database updates and return a new FileRepoStatus indicating how
-        * many rows where updated.
+        * Verify the database updates and return a new FileRepoStatus indicating how
+        * many rows would be updated.
         *
         * @return FileRepoStatus
         */
-       protected function doDBUpdates() {
+       protected function verifyDBUpdates() {
                $repo = $this->file->repo;
                $status = $repo->newGood();
                $dbw = $this->db;
 
-               // Update current image
-               $dbw->update(
+               $hasCurrent = $dbw->selectField(
                        'image',
-                       [ 'img_name' => $this->newName ],
+                       '1',
                        [ 'img_name' => $this->oldName ],
-                       __METHOD__
+                       __METHOD__,
+                       [ 'FOR UPDATE' ]
+               );
+               $oldRowCount = $dbw->selectField(
+                       'oldimage',
+                       'COUNT(*)',
+                       [ 'oi_name' => $this->oldName ],
+                       __METHOD__,
+                       [ 'FOR UPDATE' ]
                );
 
-               if ( $dbw->affectedRows() ) {
+               if ( $hasCurrent ) {
                        $status->successCount++;
                } else {
                        $status->failCount++;
-                       $status->fatal( 'imageinvalidfilename' );
-
-                       return $status;
                }
+               $status->successCount += $oldRowCount;
+               // Bug 34934: oldCount is based on files that actually exist.
+               // There may be more DB rows than such files, in which case $affected
+               // can be greater than $total. We use max() to avoid negatives here.
+               $status->failCount += max( 0, $this->oldCount - $oldRowCount );
+               if ( $status->failCount ) {
+                       $status->error( 'imageinvalidfilename' );
+               }
+
+               return $status;
+       }
+
+       /**
+        * Do the database updates and return a new FileRepoStatus indicating how
+        * many rows where updated.
+        */
+       protected function doDBUpdates() {
+               $dbw = $this->db;
 
+               // Update current image
+               $dbw->update(
+                       'image',
+                       [ 'img_name' => $this->newName ],
+                       [ 'img_name' => $this->oldName ],
+                       __METHOD__
+               );
                // Update old images
                $dbw->update(
                        'oldimage',
@@ -2952,19 +3057,6 @@ class LocalFileMoveBatch {
                        [ 'oi_name' => $this->oldName ],
                        __METHOD__
                );
-
-               $affected = $dbw->affectedRows();
-               $total = $this->oldCount;
-               $status->successCount += $affected;
-               // Bug 34934: $total is based on files that actually exist.
-               // There may be more DB rows than such files, in which case $affected
-               // can be greater than $total. We use max() to avoid negatives here.
-               $status->failCount += max( 0, $total - $affected );
-               if ( $status->failCount ) {
-                       $status->error( 'imageinvalidfilename' );
-               }
-
-               return $status;
        }
 
        /**
index c8a25f0..6884f65 100644 (file)
@@ -113,6 +113,7 @@ abstract class ImageGalleryBase extends ContextSource {
                                'packed' => 'PackedImageGallery',
                                'packed-hover' => 'PackedHoverImageGallery',
                                'packed-overlay' => 'PackedOverlayImageGallery',
+                               'slideshow' => 'SlideshowImageGallery',
                        ];
                        // Allow extensions to make a new gallery format.
                        Hooks::run( 'GalleryGetModes', [ &self::$modeMapping ] );
diff --git a/includes/gallery/SlideshowImageGallery.php b/includes/gallery/SlideshowImageGallery.php
new file mode 100644 (file)
index 0000000..3f0c932
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * A slideshow gallery shows one image at a time with controls to move around.
+ *
+ * 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
+ */
+
+class SlideshowImageGallery extends TraditionalImageGallery {
+       function __construct( $mode = 'traditional', IContextSource $context = null ) {
+               parent::__construct( $mode, $context );
+               // Does not support per row option.
+               $this->mPerRow = 0;
+       }
+
+       /**
+        * Add javascript adds interface elements
+        * @return array
+        */
+       protected function getModules() {
+               return [ 'mediawiki.page.gallery.slideshow' ];
+       }
+}
index 2fb2281..f6527b8 100644 (file)
@@ -189,8 +189,16 @@ class TraditionalImageGallery extends ImageGalleryBase {
                                // Preloaded into LinkCache above
                                Linker::linkKnown(
                                        $nt,
-                                       htmlspecialchars( $lang->truncate( $nt->getText(), $this->mCaptionLength ) )
-                               ) . "<br />\n" :
+                                       htmlspecialchars(
+                                               $this->mCaptionLength !== true ?
+                                                       $lang->truncate( $nt->getText(), $this->mCaptionLength ) :
+                                                       $nt->getText()
+                                       ),
+                                       [
+                                               'class' => 'galleryfilename' .
+                                                       ( $this->mCaptionLength === true ? ' galleryfilename-truncate' : '' )
+                                       ]
+                               ) . "\n" :
                                '';
 
                        $galleryText = $textlink . $text . $fileSize;
diff --git a/includes/htmlform/HTMLApiField.php b/includes/htmlform/HTMLApiField.php
deleted file mode 100644 (file)
index 24a253e..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-class HTMLApiField extends HTMLFormField {
-       public function getTableRow( $value ) {
-               return '';
-       }
-
-       public function getDiv( $value ) {
-               return $this->getTableRow( $value );
-       }
-
-       public function getRaw( $value ) {
-               return $this->getTableRow( $value );
-       }
-
-       public function getInputHTML( $value ) {
-               return '';
-       }
-
-       public function hasVisibleOutput() {
-               return false;
-       }
-}
diff --git a/includes/htmlform/HTMLAutoCompleteSelectField.php b/includes/htmlform/HTMLAutoCompleteSelectField.php
deleted file mode 100644 (file)
index 76a88d5..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-<?php
-
-/**
- * Text field for selecting a value from a large list of possible values, with
- * auto-completion and optionally with a select dropdown for selecting common
- * options.
- *
- * HTMLComboboxField implements most of the same functionality and should be
- * used instead, if possible.
- *
- * If one of 'options-messages', 'options', or 'options-message' is provided
- * and non-empty, the select dropdown will be shown. An 'other' key will be
- * appended using message 'htmlform-selectorother-other' if not already
- * present.
- *
- * Besides the parameters recognized by HTMLTextField, the following are
- * recognized:
- *   options-messages - As for HTMLSelectField
- *   options - As for HTMLSelectField
- *   options-message - As for HTMLSelectField
- *   autocomplete - Associative array mapping display text to values.
- *   autocomplete-messages - Like autocomplete, but keys are message names.
- *   require-match - Boolean, if true the value must be in the options or the
- *     autocomplete.
- *   other-message - Message to use instead of htmlform-selectorother-other for
- *      the 'other' message.
- *   other - Raw text to use for the 'other' message
- */
-class HTMLAutoCompleteSelectField extends HTMLTextField {
-       protected $autocomplete = [];
-
-       function __construct( $params ) {
-               $params += [
-                       'require-match' => false,
-               ];
-
-               parent::__construct( $params );
-
-               if ( array_key_exists( 'autocomplete-messages', $this->mParams ) ) {
-                       foreach ( $this->mParams['autocomplete-messages'] as $key => $value ) {
-                               $key = $this->msg( $key )->plain();
-                               $this->autocomplete[$key] = strval( $value );
-                       }
-               } elseif ( array_key_exists( 'autocomplete', $this->mParams ) ) {
-                       foreach ( $this->mParams['autocomplete'] as $key => $value ) {
-                               $this->autocomplete[$key] = strval( $value );
-                       }
-               }
-               if ( !is_array( $this->autocomplete ) || !$this->autocomplete ) {
-                       throw new MWException( 'HTMLAutoCompleteSelectField called without any autocompletions' );
-               }
-
-               $this->getOptions();
-               if ( $this->mOptions && !in_array( 'other', $this->mOptions, true ) ) {
-                       if ( isset( $params['other-message'] ) ) {
-                               $msg = $this->getMessage( $params['other-message'] )->text();
-                       } elseif ( isset( $params['other'] ) ) {
-                               $msg = $params['other'];
-                       } else {
-                               $msg = wfMessage( 'htmlform-selectorother-other' )->text();
-                       }
-                       $this->mOptions[$msg] = 'other';
-               }
-       }
-
-       function loadDataFromRequest( $request ) {
-               if ( $request->getCheck( $this->mName ) ) {
-                       $val = $request->getText( $this->mName . '-select', 'other' );
-
-                       if ( $val === 'other' ) {
-                               $val = $request->getText( $this->mName );
-                               if ( isset( $this->autocomplete[$val] ) ) {
-                                       $val = $this->autocomplete[$val];
-                               }
-                       }
-
-                       return $val;
-               } else {
-                       return $this->getDefault();
-               }
-       }
-
-       function validate( $value, $alldata ) {
-               $p = parent::validate( $value, $alldata );
-
-               if ( $p !== true ) {
-                       return $p;
-               }
-
-               $validOptions = HTMLFormField::flattenOptions( $this->getOptions() );
-
-               if ( in_array( strval( $value ), $validOptions, true ) ) {
-                       return true;
-               } elseif ( in_array( strval( $value ), $this->autocomplete, true ) ) {
-                       return true;
-               } elseif ( $this->mParams['require-match'] ) {
-                       return $this->msg( 'htmlform-select-badoption' )->parse();
-               }
-
-               return true;
-       }
-
-       // FIXME Ewww, this shouldn't be adding any attributes not requested in $list :(
-       public function getAttributes( array $list ) {
-               $attribs = [
-                       'type' => 'text',
-                       'data-autocomplete' => FormatJson::encode( array_keys( $this->autocomplete ) ),
-               ] + parent::getAttributes( $list );
-
-               if ( $this->getOptions() ) {
-                       $attribs['data-hide-if'] = FormatJson::encode(
-                               [ '!==', $this->mName . '-select', 'other' ]
-                       );
-               }
-
-               return $attribs;
-       }
-
-       function getInputHTML( $value ) {
-               $oldClass = $this->mClass;
-               $this->mClass = (array)$this->mClass;
-
-               $valInSelect = false;
-               $ret = '';
-
-               if ( $this->getOptions() ) {
-                       if ( $value !== false ) {
-                               $value = strval( $value );
-                               $valInSelect = in_array(
-                                       $value, HTMLFormField::flattenOptions( $this->getOptions() ), true
-                               );
-                       }
-
-                       $selected = $valInSelect ? $value : 'other';
-                       $select = new XmlSelect( $this->mName . '-select', $this->mID . '-select', $selected );
-                       $select->addOptions( $this->getOptions() );
-                       $select->setAttribute( 'class', 'mw-htmlform-select-or-other' );
-
-                       if ( !empty( $this->mParams['disabled'] ) ) {
-                               $select->setAttribute( 'disabled', 'disabled' );
-                       }
-
-                       if ( isset( $this->mParams['tabindex'] ) ) {
-                               $select->setAttribute( 'tabindex', $this->mParams['tabindex'] );
-                       }
-
-                       $ret = $select->getHTML() . "<br />\n";
-
-                       $this->mClass[] = 'mw-htmlform-hide-if';
-               }
-
-               if ( $valInSelect ) {
-                       $value = '';
-               } else {
-                       $key = array_search( strval( $value ), $this->autocomplete, true );
-                       if ( $key !== false ) {
-                               $value = $key;
-                       }
-               }
-
-               $this->mClass[] = 'mw-htmlform-autocomplete';
-               $ret .= parent::getInputHTML( $valInSelect ? '' : $value );
-               $this->mClass = $oldClass;
-
-               return $ret;
-       }
-
-       /**
-        * Get the OOUI version of this input.
-        * @param string $value
-        * @return false
-        */
-       function getInputOOUI( $value ) {
-               // To be implemented, for now override the function from HTMLTextField
-               return false;
-       }
-}
diff --git a/includes/htmlform/HTMLButtonField.php b/includes/htmlform/HTMLButtonField.php
deleted file mode 100644 (file)
index 64fe7ed..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-
-/**
- * Adds a generic button inline to the form. Does not do anything, you must add
- * click handling code in JavaScript. Use a HTMLSubmitField if you merely
- * wish to add a submit button to a form.
- *
- * Additional recognized configuration parameters include:
- * - flags: OOUI flags for the button, see OOUI\FlaggedElement
- * - buttonlabel-message: Message to use for the button display text, instead
- *   of the value from 'default'. Overrides 'buttonlabel' and 'buttonlabel-raw'.
- * - buttonlabel: Text to display for the button display text, instead
- *   of the value from 'default'. Overrides 'buttonlabel-raw'.
- * - buttonlabel-raw: HTMLto display for the button display text, instead
- *   of the value from 'default'.
- *
- * Note that the buttonlabel parameters are not supported on IE6 and IE7 due to
- * bugs in those browsers. If detected, they will be served buttons using the
- * value of 'default' as the button label.
- *
- * @since 1.22
- */
-class HTMLButtonField extends HTMLFormField {
-       protected $buttonType = 'button';
-       protected $buttonLabel = null;
-
-       /** @var array $mFlags Flags to add to OOUI Button widget */
-       protected $mFlags = [];
-
-       public function __construct( $info ) {
-               $info['nodata'] = true;
-               if ( isset( $info['flags'] ) ) {
-                       $this->mFlags = $info['flags'];
-               }
-
-               # Generate the label from a message, if possible
-               if ( isset( $info['buttonlabel-message'] ) ) {
-                       $this->buttonLabel = $this->getMessage( $info['buttonlabel-message'] )->parse();
-               } elseif ( isset( $info['buttonlabel'] ) ) {
-                       if ( $info['buttonlabel'] === '&#160;' ) {
-                               // Apparently some things set &nbsp directly and in an odd format
-                               $this->buttonLabel = '&#160;';
-                       } else {
-                               $this->buttonLabel = htmlspecialchars( $info['buttonlabel'] );
-                       }
-               } elseif ( isset( $info['buttonlabel-raw'] ) ) {
-                       $this->buttonLabel = $info['buttonlabel-raw'];
-               }
-
-               $this->setShowEmptyLabel( false );
-
-               parent::__construct( $info );
-       }
-
-       public function getInputHTML( $value ) {
-               $flags = '';
-               $prefix = 'mw-htmlform-';
-               if ( $this->mParent instanceof VFormHTMLForm ||
-                       $this->mParent->getConfig()->get( 'UseMediaWikiUIEverywhere' )
-               ) {
-                       $prefix = 'mw-ui-';
-                       // add mw-ui-button separately, so the descriptor doesn't need to set it
-                       $flags .= ' ' . $prefix . 'button';
-               }
-               foreach ( $this->mFlags as $flag ) {
-                       $flags .= ' ' . $prefix . $flag;
-               }
-               $attr = [
-                       'class' => 'mw-htmlform-submit ' . $this->mClass . $flags,
-                       'id' => $this->mID,
-                       'type' => $this->buttonType,
-                       'name' => $this->mName,
-                       'value' => $this->getDefault(),
-               ] + $this->getAttributes( [ 'disabled', 'tabindex' ] );
-
-               if ( $this->isBadIE() ) {
-                       return Html::element( 'input', $attr );
-               } else {
-                       return Html::rawElement( 'button', $attr,
-                               $this->buttonLabel ?: htmlspecialchars( $this->getDefault() ) );
-               }
-       }
-
-       /**
-        * Get the OOUI widget for this field.
-        * @param string $value
-        * @return OOUI\ButtonInputWidget
-        */
-       public function getInputOOUI( $value ) {
-               return new OOUI\ButtonInputWidget( [
-                       'name' => $this->mName,
-                       'value' => $this->getDefault(),
-                       'label' => !$this->isBadIE() && $this->buttonLabel
-                               ? new OOUI\HtmlSnippet( $this->buttonLabel )
-                               : $this->getDefault(),
-                       'type' => $this->buttonType,
-                       'classes' => [ 'mw-htmlform-submit', $this->mClass ],
-                       'id' => $this->mID,
-                       'flags' => $this->mFlags,
-                       'useInputTag' => $this->isBadIE(),
-               ] + OOUI\Element::configFromHtmlAttributes(
-                       $this->getAttributes( [ 'disabled', 'tabindex' ] )
-               ) );
-       }
-
-       protected function needsLabel() {
-               return false;
-       }
-
-       /**
-        * Button cannot be invalid
-        *
-        * @param string $value
-        * @param array $alldata
-        *
-        * @return bool
-        */
-       public function validate( $value, $alldata ) {
-               return true;
-       }
-
-       /**
-        * IE<8 has bugs with <button>, so we'll need to avoid them.
-        * @return bool Whether the request is from a bad version of IE
-        */
-       private function isBadIE() {
-               $request = $this->mParent
-                       ? $this->mParent->getRequest()
-                       : RequestContext::getMain()->getRequest();
-               return preg_match( '/MSIE [1-7]\./i', $request->getHeader( 'User-Agent' ) );
-       }
-}
diff --git a/includes/htmlform/HTMLCheckField.php b/includes/htmlform/HTMLCheckField.php
deleted file mode 100644 (file)
index 4a6b804..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-<?php
-
-/**
- * A checkbox field
- */
-class HTMLCheckField extends HTMLFormField {
-       function getInputHTML( $value ) {
-               global $wgUseMediaWikiUIEverywhere;
-
-               if ( !empty( $this->mParams['invert'] ) ) {
-                       $value = !$value;
-               }
-
-               $attr = $this->getTooltipAndAccessKey();
-               $attr['id'] = $this->mID;
-
-               $attr += $this->getAttributes( [ 'disabled', 'tabindex' ] );
-
-               if ( $this->mClass !== '' ) {
-                       $attr['class'] = $this->mClass;
-               }
-
-               $attrLabel = [ 'for' => $this->mID ];
-               if ( isset( $attr['title'] ) ) {
-                       // propagate tooltip to label
-                       $attrLabel['title'] = $attr['title'];
-               }
-
-               $chkLabel = Xml::check( $this->mName, $value, $attr ) .
-                       '&#160;' .
-                       Html::rawElement( 'label', $attrLabel, $this->mLabel );
-
-               if ( $wgUseMediaWikiUIEverywhere || $this->mParent instanceof VFormHTMLForm ) {
-                       $chkLabel = Html::rawElement(
-                               'div',
-                               [ 'class' => 'mw-ui-checkbox' ],
-                               $chkLabel
-                       );
-               }
-
-               return $chkLabel;
-       }
-
-       /**
-        * Get the OOUI version of this field.
-        * @since 1.26
-        * @param string $value
-        * @return OOUI\CheckboxInputWidget The checkbox widget.
-        */
-       public function getInputOOUI( $value ) {
-               if ( !empty( $this->mParams['invert'] ) ) {
-                       $value = !$value;
-               }
-
-               $attr = $this->getTooltipAndAccessKey();
-               $attr['id'] = $this->mID;
-               $attr['name'] = $this->mName;
-
-               $attr += OOUI\Element::configFromHtmlAttributes(
-                       $this->getAttributes( [ 'disabled', 'tabindex' ] )
-               );
-
-               if ( $this->mClass !== '' ) {
-                       $attr['classes'] = [ $this->mClass ];
-               }
-
-               $attr['selected'] = $value;
-               $attr['value'] = '1'; // Nasty hack, but needed to make this work
-
-               return new OOUI\CheckboxInputWidget( $attr );
-       }
-
-       /**
-        * For a checkbox, the label goes on the right hand side, and is
-        * added in getInputHTML(), rather than HTMLFormField::getRow()
-        *
-        * ...unless OOUI is being used, in which case we actually return
-        * the label here.
-        *
-        * @return string
-        */
-       function getLabel() {
-               if ( $this->mParent instanceof OOUIHTMLForm ) {
-                       return $this->mLabel;
-               } elseif (
-                       $this->mParent instanceof HTMLForm &&
-                       $this->mParent->getDisplayFormat() === 'div'
-               ) {
-                       return '';
-               } else {
-                       return '&#160;';
-               }
-       }
-
-       /**
-        * Get label alignment when generating field for OOUI.
-        * @return string 'left', 'right', 'top' or 'inline'
-        */
-       protected function getLabelAlignOOUI() {
-               return 'inline';
-       }
-
-       /**
-        * checkboxes don't need a label.
-        * @return bool
-        */
-       protected function needsLabel() {
-               return false;
-       }
-
-       /**
-        * @param WebRequest $request
-        *
-        * @return bool
-        */
-       function loadDataFromRequest( $request ) {
-               $invert = isset( $this->mParams['invert'] ) && $this->mParams['invert'];
-
-               // GetCheck won't work like we want for checks.
-               // Fetch the value in either one of the two following case:
-               // - we have a valid token (form got posted or GET forged by the user)
-               // - checkbox name has a value (false or true), ie is not null
-               if ( $request->getCheck( 'wpEditToken' ) || $request->getVal( $this->mName ) !== null ) {
-                       return $invert
-                               ? !$request->getBool( $this->mName )
-                               : $request->getBool( $this->mName );
-               } else {
-                       return (bool)$this->getDefault();
-               }
-       }
-}
diff --git a/includes/htmlform/HTMLCheckMatrix.php b/includes/htmlform/HTMLCheckMatrix.php
deleted file mode 100644 (file)
index 9f67233..0000000
+++ /dev/null
@@ -1,275 +0,0 @@
-<?php
-
-/**
- * A checkbox matrix
- * Operates similarly to HTMLMultiSelectField, but instead of using an array of
- * options, uses an array of rows and an array of columns to dynamically
- * construct a matrix of options. The tags used to identify a particular cell
- * are of the form "columnName-rowName"
- *
- * Options:
- *   - columns
- *     - Required list of columns in the matrix.
- *   - rows
- *     - Required list of rows in the matrix.
- *   - force-options-on
- *     - Accepts array of column-row tags to be displayed as enabled but unavailable to change
- *   - force-options-off
- *     - Accepts array of column-row tags to be displayed as disabled but unavailable to change.
- *   - tooltips
- *     - Optional array mapping row label to tooltip content
- *   - tooltip-class
- *     - Optional CSS class used on tooltip container span. Defaults to mw-icon-question.
- */
-class HTMLCheckMatrix extends HTMLFormField implements HTMLNestedFilterable {
-       static private $requiredParams = [
-               // Required by underlying HTMLFormField
-               'fieldname',
-               // Required by HTMLCheckMatrix
-               'rows',
-               'columns'
-       ];
-
-       public function __construct( $params ) {
-               $missing = array_diff( self::$requiredParams, array_keys( $params ) );
-               if ( $missing ) {
-                       throw new HTMLFormFieldRequiredOptionsException( $this, $missing );
-               }
-               parent::__construct( $params );
-       }
-
-       function validate( $value, $alldata ) {
-               $rows = $this->mParams['rows'];
-               $columns = $this->mParams['columns'];
-
-               // Make sure user-defined validation callback is run
-               $p = parent::validate( $value, $alldata );
-               if ( $p !== true ) {
-                       return $p;
-               }
-
-               // Make sure submitted value is an array
-               if ( !is_array( $value ) ) {
-                       return false;
-               }
-
-               // If all options are valid, array_intersect of the valid options
-               // and the provided options will return the provided options.
-               $validOptions = [];
-               foreach ( $rows as $rowTag ) {
-                       foreach ( $columns as $columnTag ) {
-                               $validOptions[] = $columnTag . '-' . $rowTag;
-                       }
-               }
-               $validValues = array_intersect( $value, $validOptions );
-               if ( count( $validValues ) == count( $value ) ) {
-                       return true;
-               } else {
-                       return $this->msg( 'htmlform-select-badoption' )->parse();
-               }
-       }
-
-       /**
-        * Build a table containing a matrix of checkbox options.
-        * The value of each option is a combination of the row tag and column tag.
-        * mParams['rows'] is an array with row labels as keys and row tags as values.
-        * mParams['columns'] is an array with column labels as keys and column tags as values.
-        *
-        * @param array $value Array of the options that should be checked
-        *
-        * @return string
-        */
-       function getInputHTML( $value ) {
-               $html = '';
-               $tableContents = '';
-               $rows = $this->mParams['rows'];
-               $columns = $this->mParams['columns'];
-
-               $attribs = $this->getAttributes( [ 'disabled', 'tabindex' ] );
-
-               // Build the column headers
-               $headerContents = Html::rawElement( 'td', [], '&#160;' );
-               foreach ( $columns as $columnLabel => $columnTag ) {
-                       $headerContents .= Html::rawElement( 'td', [], $columnLabel );
-               }
-               $tableContents .= Html::rawElement( 'tr', [], "\n$headerContents\n" );
-
-               $tooltipClass = 'mw-icon-question';
-               if ( isset( $this->mParams['tooltip-class'] ) ) {
-                       $tooltipClass = $this->mParams['tooltip-class'];
-               }
-
-               // Build the options matrix
-               foreach ( $rows as $rowLabel => $rowTag ) {
-                       // Append tooltip if configured
-                       if ( isset( $this->mParams['tooltips'][$rowLabel] ) ) {
-                               $tooltipAttribs = [
-                                       'class' => "mw-htmlform-tooltip $tooltipClass",
-                                       'title' => $this->mParams['tooltips'][$rowLabel],
-                               ];
-                               $rowLabel .= ' ' . Html::element( 'span', $tooltipAttribs, '' );
-                       }
-                       $rowContents = Html::rawElement( 'td', [], $rowLabel );
-                       foreach ( $columns as $columnTag ) {
-                               $thisTag = "$columnTag-$rowTag";
-                               // Construct the checkbox
-                               $thisAttribs = [
-                                       'id' => "{$this->mID}-$thisTag",
-                                       'value' => $thisTag,
-                               ];
-                               $checked = in_array( $thisTag, (array)$value, true );
-                               if ( $this->isTagForcedOff( $thisTag ) ) {
-                                       $checked = false;
-                                       $thisAttribs['disabled'] = 1;
-                               } elseif ( $this->isTagForcedOn( $thisTag ) ) {
-                                       $checked = true;
-                                       $thisAttribs['disabled'] = 1;
-                               }
-
-                               $checkbox = $this->getOneCheckbox( $checked, $attribs + $thisAttribs );
-
-                               $rowContents .= Html::rawElement(
-                                       'td',
-                                       [],
-                                       $checkbox
-                               );
-                       }
-                       $tableContents .= Html::rawElement( 'tr', [], "\n$rowContents\n" );
-               }
-
-               // Put it all in a table
-               $html .= Html::rawElement( 'table',
-                               [ 'class' => 'mw-htmlform-matrix' ],
-                               Html::rawElement( 'tbody', [], "\n$tableContents\n" ) ) . "\n";
-
-               return $html;
-       }
-
-       protected function getOneCheckbox( $checked, $attribs ) {
-               if ( $this->mParent instanceof OOUIHTMLForm ) {
-                       return new OOUI\CheckboxInputWidget( [
-                               'name' => "{$this->mName}[]",
-                               'selected' => $checked,
-                       ] + OOUI\Element::configFromHtmlAttributes(
-                               $attribs
-                       ) );
-               } else {
-                       $checkbox = Xml::check( "{$this->mName}[]", $checked, $attribs );
-                       if ( $this->mParent->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) {
-                               $checkbox = Html::openElement( 'div', [ 'class' => 'mw-ui-checkbox' ] ) .
-                                       $checkbox .
-                                       Html::element( 'label', [ 'for' => $attribs['id'] ] ) .
-                                       Html::closeElement( 'div' );
-                       }
-                       return $checkbox;
-               }
-       }
-
-       protected function isTagForcedOff( $tag ) {
-               return isset( $this->mParams['force-options-off'] )
-                       && in_array( $tag, $this->mParams['force-options-off'] );
-       }
-
-       protected function isTagForcedOn( $tag ) {
-               return isset( $this->mParams['force-options-on'] )
-                       && in_array( $tag, $this->mParams['force-options-on'] );
-       }
-
-       /**
-        * Get the complete table row for the input, including help text,
-        * labels, and whatever.
-        * We override this function since the label should always be on a separate
-        * line above the options in the case of a checkbox matrix, i.e. it's always
-        * a "vertical-label".
-        *
-        * @param string $value The value to set the input to
-        *
-        * @return string Complete HTML table row
-        */
-       function getTableRow( $value ) {
-               list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value );
-               $inputHtml = $this->getInputHTML( $value );
-               $fieldType = get_class( $this );
-               $helptext = $this->getHelpTextHtmlTable( $this->getHelpText() );
-               $cellAttributes = [ 'colspan' => 2 ];
-
-               $hideClass = '';
-               $hideAttributes = [];
-               if ( $this->mHideIf ) {
-                       $hideAttributes['data-hide-if'] = FormatJson::encode( $this->mHideIf );
-                       $hideClass = 'mw-htmlform-hide-if';
-               }
-
-               $label = $this->getLabelHtml( $cellAttributes );
-
-               $field = Html::rawElement(
-                       'td',
-                       [ 'class' => 'mw-input' ] + $cellAttributes,
-                       $inputHtml . "\n$errors"
-               );
-
-               $html = Html::rawElement( 'tr',
-                       [ 'class' => "mw-htmlform-vertical-label $hideClass" ] + $hideAttributes,
-                       $label );
-               $html .= Html::rawElement( 'tr',
-                       [ 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass $hideClass" ] +
-                               $hideAttributes,
-                       $field );
-
-               return $html . $helptext;
-       }
-
-       /**
-        * @param WebRequest $request
-        *
-        * @return array
-        */
-       function loadDataFromRequest( $request ) {
-               if ( $this->mParent->getMethod() == 'post' ) {
-                       if ( $request->wasPosted() ) {
-                               // Checkboxes are not added to the request arrays if they're not checked,
-                               // so it's perfectly possible for there not to be an entry at all
-                               return $request->getArray( $this->mName, [] );
-                       } else {
-                               // That's ok, the user has not yet submitted the form, so show the defaults
-                               return $this->getDefault();
-                       }
-               } else {
-                       // This is the impossible case: if we look at $_GET and see no data for our
-                       // field, is it because the user has not yet submitted the form, or that they
-                       // have submitted it with all the options unchecked. We will have to assume the
-                       // latter, which basically means that you can't specify 'positive' defaults
-                       // for GET forms.
-                       return $request->getArray( $this->mName, [] );
-               }
-       }
-
-       function getDefault() {
-               if ( isset( $this->mDefault ) ) {
-                       return $this->mDefault;
-               } else {
-                       return [];
-               }
-       }
-
-       function filterDataForSubmit( $data ) {
-               $columns = HTMLFormField::flattenOptions( $this->mParams['columns'] );
-               $rows = HTMLFormField::flattenOptions( $this->mParams['rows'] );
-               $res = [];
-               foreach ( $columns as $column ) {
-                       foreach ( $rows as $row ) {
-                               // Make sure option hasn't been forced
-                               $thisTag = "$column-$row";
-                               if ( $this->isTagForcedOff( $thisTag ) ) {
-                                       $res[$thisTag] = false;
-                               } elseif ( $this->isTagForcedOn( $thisTag ) ) {
-                                       $res[$thisTag] = true;
-                               } else {
-                                       $res[$thisTag] = in_array( $thisTag, $data );
-                               }
-                       }
-               }
-
-               return $res;
-       }
-}
diff --git a/includes/htmlform/HTMLComboboxField.php b/includes/htmlform/HTMLComboboxField.php
deleted file mode 100644 (file)
index 778aedb..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-
-/**
- * A combo box field.
- *
- * You can think of it as a dropdown select with the ability to add custom options,
- * or as a text field with input suggestions (autocompletion).
- *
- * When JavaScript is not supported or enabled, it uses HTML5 `<datalist>` element.
- *
- * Besides the parameters recognized by HTMLTextField, the following are
- * recognized:
- *   options-messages - As for HTMLSelectField
- *   options - As for HTMLSelectField
- *   options-message - As for HTMLSelectField
- */
-class HTMLComboboxField extends HTMLTextField {
-       // FIXME Ewww, this shouldn't be adding any attributes not requested in $list :(
-       public function getAttributes( array $list ) {
-               $attribs = [
-                       'type' => 'text',
-                       'list' => $this->mName . '-datalist',
-               ] + parent::getAttributes( $list );
-
-               return $attribs;
-       }
-
-       function getInputHTML( $value ) {
-               $datalist = new XmlSelect( false, $this->mName . '-datalist' );
-               $datalist->setTagName( 'datalist' );
-               $datalist->addOptions( $this->getOptions() );
-
-               return parent::getInputHTML( $value ) . $datalist->getHTML();
-       }
-
-       function getInputOOUI( $value ) {
-               $disabled = false;
-               $allowedParams = [ 'tabindex' ];
-               $attribs = OOUI\Element::configFromHtmlAttributes(
-                       $this->getAttributes( $allowedParams )
-               );
-
-               if ( $this->mClass !== '' ) {
-                       $attribs['classes'] = [ $this->mClass ];
-               }
-
-               if ( !empty( $this->mParams['disabled'] ) ) {
-                       $disabled = true;
-               }
-
-               return new OOUI\ComboBoxInputWidget( [
-                       'name' => $this->mName,
-                       'id' => $this->mID,
-                       'options' => $this->getOptionsOOUI(),
-                       'value' => strval( $value ),
-                       'disabled' => $disabled,
-               ] + $attribs );
-       }
-}
diff --git a/includes/htmlform/HTMLEditTools.php b/includes/htmlform/HTMLEditTools.php
deleted file mode 100644 (file)
index 1b5d1fb..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-class HTMLEditTools extends HTMLFormField {
-       public function getInputHTML( $value ) {
-               return '';
-       }
-
-       public function getTableRow( $value ) {
-               $msg = $this->formatMsg();
-
-               return
-                       '<tr><td></td><td class="mw-input">' .
-                       '<div class="mw-editTools">' .
-                       $msg->parseAsBlock() .
-                       "</div></td></tr>\n";
-       }
-
-       /**
-        * @param string $value
-        * @return string
-        * @since 1.20
-        */
-       public function getDiv( $value ) {
-               $msg = $this->formatMsg();
-
-               return '<div class="mw-editTools">' . $msg->parseAsBlock() . '</div>';
-       }
-
-       /**
-        * @param string $value
-        * @return string
-        * @since 1.20
-        */
-       public function getRaw( $value ) {
-               return $this->getDiv( $value );
-       }
-
-       protected function formatMsg() {
-               if ( empty( $this->mParams['message'] ) ) {
-                       $msg = $this->msg( 'edittools' );
-               } else {
-                       $msg = $this->getMessage( $this->mParams['message'] );
-                       if ( $msg->isDisabled() ) {
-                               $msg = $this->msg( 'edittools' );
-                       }
-               }
-               $msg->inContentLanguage();
-
-               return $msg;
-       }
-}
diff --git a/includes/htmlform/HTMLFloatField.php b/includes/htmlform/HTMLFloatField.php
deleted file mode 100644 (file)
index 2ef4978..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-/**
- * A field that will contain a numeric value
- */
-class HTMLFloatField extends HTMLTextField {
-       function getSize() {
-               return isset( $this->mParams['size'] ) ? $this->mParams['size'] : 20;
-       }
-
-       function validate( $value, $alldata ) {
-               $p = parent::validate( $value, $alldata );
-
-               if ( $p !== true ) {
-                       return $p;
-               }
-
-               $value = trim( $value );
-
-               # http://www.w3.org/TR/html5/infrastructure.html#floating-point-numbers
-               # with the addition that a leading '+' sign is ok.
-               if ( !preg_match( '/^((\+|\-)?\d+(\.\d+)?(E(\+|\-)?\d+)?)?$/i', $value ) ) {
-                       return $this->msg( 'htmlform-float-invalid' )->parseAsBlock();
-               }
-
-               # The "int" part of these message names is rather confusing.
-               # They make equal sense for all numbers.
-               if ( isset( $this->mParams['min'] ) ) {
-                       $min = $this->mParams['min'];
-
-                       if ( $min > $value ) {
-                               return $this->msg( 'htmlform-int-toolow', $min )->parseAsBlock();
-                       }
-               }
-
-               if ( isset( $this->mParams['max'] ) ) {
-                       $max = $this->mParams['max'];
-
-                       if ( $max < $value ) {
-                               return $this->msg( 'htmlform-int-toohigh', $max )->parseAsBlock();
-                       }
-               }
-
-               return true;
-       }
-}
index 8ac4cf2..3c88594 100644 (file)
  *    'help-messages'       -- array of message keys/objects. As above, each item can
  *                             be an array of msg key and then parameters.
  *                             Overwrites 'help'.
+ *    'notice'              -- message text for a message to use as a notice in the field.
+ *                             Currently used by OOUI form fields only.
+ *    'notice-messages'     -- array of message keys/objects to use for notice.
+ *                             Overrides 'notice'.
+ *    'notice-message'      -- message key or object to use as a notice.
  *    'required'            -- passed through to the object, indicating that it
  *                             is a required field.
  *    'size'                -- the length of text fields
@@ -190,6 +195,7 @@ class HTMLForm extends ContextSource {
        protected $mSubmitText;
        protected $mSubmitTooltip;
 
+       protected $mFormIdentifier;
        protected $mTitle;
        protected $mMethod = 'post';
        protected $mWasSubmitted = false;
@@ -269,14 +275,12 @@ class HTMLForm extends ContextSource {
 
                switch ( $displayFormat ) {
                        case 'vform':
-                               $reflector = new ReflectionClass( 'VFormHTMLForm' );
-                               return $reflector->newInstanceArgs( $arguments );
+                               return ObjectFactory::constructClassInstance( VFormHTMLForm::class, $arguments );
                        case 'ooui':
-                               $reflector = new ReflectionClass( 'OOUIHTMLForm' );
-                               return $reflector->newInstanceArgs( $arguments );
+                               return ObjectFactory::constructClassInstance( OOUIHTMLForm::class, $arguments );
                        default:
-                               $reflector = new ReflectionClass( 'HTMLForm' );
-                               $form = $reflector->newInstanceArgs( $arguments );
+                               /** @var HTMLForm $form */
+                               $form = ObjectFactory::constructClassInstance( HTMLForm::class, $arguments );
                                $form->setDisplayFormat( $displayFormat );
                                return $form;
                }
@@ -350,6 +354,26 @@ class HTMLForm extends ContextSource {
                $this->mFieldTree = $loadedDescriptor;
        }
 
+       /**
+        * @param string $fieldname
+        * @return bool
+        */
+       public function hasField( $fieldname ) {
+               return isset( $this->mFlatFields[$fieldname] );
+       }
+
+       /**
+        * @param string $fieldname
+        * @return HTMLFormField
+        * @throws DomainException on invalid field name
+        */
+       public function getField( $fieldname ) {
+               if ( !$this->hasField( $fieldname ) ) {
+                       throw new DomainException( __METHOD__ . ': no field named ' . $fieldname );
+               }
+               return $this->mFlatFields[$fieldname];
+       }
+
        /**
         * Set format in which to display the form
         *
@@ -480,7 +504,14 @@ class HTMLForm extends ContextSource {
                }
 
                # Load data from the request.
-               $this->loadData();
+               if (
+                       $this->mFormIdentifier === null ||
+                       $this->getRequest()->getVal( 'wpFormIdentifier' ) === $this->mFormIdentifier
+               ) {
+                       $this->loadData();
+               } else {
+                       $this->mFieldData = [];
+               }
 
                return $this;
        }
@@ -492,22 +523,29 @@ class HTMLForm extends ContextSource {
        public function tryAuthorizedSubmit() {
                $result = false;
 
-               $submit = false;
+               $identOkay = false;
+               if ( $this->mFormIdentifier === null ) {
+                       $identOkay = true;
+               } else {
+                       $identOkay = $this->getRequest()->getVal( 'wpFormIdentifier' ) === $this->mFormIdentifier;
+               }
+
+               $tokenOkay = false;
                if ( $this->getMethod() !== 'post' ) {
-                       $submit = true; // no session check needed
+                       $tokenOkay = true; // no session check needed
                } elseif ( $this->getRequest()->wasPosted() ) {
                        $editToken = $this->getRequest()->getVal( 'wpEditToken' );
                        if ( $this->getUser()->isLoggedIn() || $editToken !== null ) {
                                // Session tokens for logged-out users have no security value.
                                // However, if the user gave one, check it in order to give a nice
                                // "session expired" error instead of "permission denied" or such.
-                               $submit = $this->getUser()->matchEditToken( $editToken, $this->mTokenSalt );
+                               $tokenOkay = $this->getUser()->matchEditToken( $editToken, $this->mTokenSalt );
                        } else {
-                               $submit = true;
+                               $tokenOkay = true;
                        }
                }
 
-               if ( $submit ) {
+               if ( $tokenOkay && $identOkay ) {
                        $this->mWasSubmitted = true;
                        $result = $this->trySubmit();
                }
@@ -1042,6 +1080,12 @@ class HTMLForm extends ContextSource {
         */
        public function getHiddenFields() {
                $html = '';
+               if ( $this->mFormIdentifier !== null ) {
+                       $html .= Html::hidden(
+                               'wpFormIdentifier',
+                               $this->mFormIdentifier
+                       ) . "\n";
+               }
                if ( $this->getMethod() === 'post' ) {
                        $html .= Html::hidden(
                                'wpEditToken',
@@ -1327,6 +1371,27 @@ class HTMLForm extends ContextSource {
                return $this;
        }
 
+       /**
+        * Set an internal identifier for this form. It will be submitted as a hidden form field, allowing
+        * HTMLForm to determine whether the form was submitted (or merely viewed). Setting this serves
+        * two purposes:
+        *
+        * - If you use two or more forms on one page, it allows HTMLForm to identify which of the forms
+        *   was submitted, and not attempt to validate the other ones.
+        * - If you use checkbox or multiselect fields inside a form using the GET method, it allows
+        *   HTMLForm to distinguish between the initial page view and a form submission with all
+        *   checkboxes or select options unchecked.
+        *
+        * @since 1.28
+        * @param string $ident
+        * @return $this
+        */
+       public function setFormIdentifier( $ident ) {
+               $this->mFormIdentifier = $ident;
+
+               return $this;
+       }
+
        /**
         * Stop a default submit button being shown for this form. This implies that an
         * alternate submit method must be provided manually.
diff --git a/includes/htmlform/HTMLFormElement.php b/includes/htmlform/HTMLFormElement.php
new file mode 100644 (file)
index 0000000..089213c
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * Allows custom data specific to HTMLFormField to be set for OOjs UI forms. A matching JS widget
+ * (defined in htmlform.Element.js) picks up the extra config when constructed using OO.ui.infuse().
+ *
+ * Currently only supports passing 'hide-if' data.
+ */
+trait HTMLFormElement {
+
+       protected $hideIf = null;
+       protected $modules = null;
+
+       public function initializeHTMLFormElement( array $config = [] ) {
+               // Properties
+               $this->hideIf = isset( $config['hideIf'] ) ? $config['hideIf'] : null;
+               $this->modules = isset( $config['modules'] ) ? $config['modules'] : [];
+
+               // Initialization
+               if ( $this->hideIf ) {
+                       $this->addClasses( [ 'mw-htmlform-hide-if' ] );
+               }
+               if ( $this->modules ) {
+                       // JS code must be able to read this before infusing (before OOjs UI is even loaded),
+                       // so we put this in a separate attribute (not with the rest of the config).
+                       // And it's not needed anymore after infusing, so we don't put it in JS config at all.
+                       $this->setAttributes( [ 'data-mw-modules' => implode( ',', $this->modules ) ] );
+               }
+               $this->registerConfigCallback( function( &$config ) {
+                       if ( $this->hideIf !== null ) {
+                               $config['hideIf'] = $this->hideIf;
+                       }
+               } );
+       }
+}
+
+class HTMLFormFieldLayout extends OOUI\FieldLayout {
+       use HTMLFormElement;
+
+       public function __construct( $fieldWidget, array $config = [] ) {
+               // Parent constructor
+               parent::__construct( $fieldWidget, $config );
+               // Traits
+               $this->initializeHTMLFormElement( $config );
+       }
+
+       protected function getJavaScriptClassName() {
+               return 'mw.htmlform.FieldLayout';
+       }
+}
+
+class HTMLFormActionFieldLayout extends OOUI\ActionFieldLayout {
+       use HTMLFormElement;
+
+       public function __construct( $fieldWidget, $buttonWidget = false, array $config = [] ) {
+               // Parent constructor
+               parent::__construct( $fieldWidget, $buttonWidget, $config );
+               // Traits
+               $this->initializeHTMLFormElement( $config );
+       }
+
+       protected function getJavaScriptClassName() {
+               return 'mw.htmlform.ActionFieldLayout';
+       }
+}
index 9f5e728..8604ba2 100644 (file)
@@ -61,7 +61,7 @@ abstract class HTMLFormField {
         * @return bool
         */
        public function canDisplayErrors() {
-               return true;
+               return $this->hasVisibleOutput();
        }
 
        /**
@@ -349,6 +349,20 @@ abstract class HTMLFormField {
                $this->mShowEmptyLabels = $show;
        }
 
+       /**
+        * Can we assume that the request is an attempt to submit a HTMLForm, as opposed to an attempt to
+        * just view it? This can't normally be distinguished for e.g. checkboxes.
+        *
+        * Returns true if the request has a field for a CSRF token (wpEditToken) or a form identifier
+        * (wpFormIdentifier).
+        *
+        * @param WebRequest $request
+        * @return boolean
+        */
+       protected function isSubmitAttempt( WebRequest $request ) {
+               return $request->getCheck( 'wpEditToken' ) || $request->getCheck( 'wpFormIdentifier' );
+       }
+
        /**
         * Get the value that this input has been set to from a posted form,
         * or the input's default value if it has not been set.
@@ -441,10 +455,6 @@ abstract class HTMLFormField {
                        $this->mFilterCallback = $params['filter-callback'];
                }
 
-               if ( isset( $params['flatlist'] ) ) {
-                       $this->mClass .= ' mw-htmlform-flatlist';
-               }
-
                if ( isset( $params['hidelabel'] ) ) {
                        $this->mShowEmptyLabels = false;
                }
@@ -592,26 +602,51 @@ abstract class HTMLFormField {
                }
 
                $fieldType = get_class( $this );
-               $helpText = $this->getHelpText();
+               $help = $this->getHelpText();
                $errors = $this->getErrorsRaw( $value );
                foreach ( $errors as &$error ) {
                        $error = new OOUI\HtmlSnippet( $error );
                }
 
+               $notices = $this->getNotices();
+               foreach ( $notices as &$notice ) {
+                       $notice = new OOUI\HtmlSnippet( $notice );
+               }
+
                $config = [
                        'classes' => [ "mw-htmlform-field-$fieldType", $this->mClass ],
                        'align' => $this->getLabelAlignOOUI(),
-                       'help' => $helpText !== null ? new OOUI\HtmlSnippet( $helpText ) : null,
+                       'help' => ( $help !== null && $help !== '' ) ? new OOUI\HtmlSnippet( $help ) : null,
                        'errors' => $errors,
+                       'notices' => $notices,
                        'infusable' => $infusable,
                ];
 
+               $preloadModules = false;
+
+               if ( $infusable && $this->shouldInfuseOOUI() ) {
+                       $preloadModules = true;
+                       $config['classes'][] = 'mw-htmlform-field-autoinfuse';
+               }
+
                // the element could specify, that the label doesn't need to be added
                $label = $this->getLabel();
                if ( $label ) {
                        $config['label'] = new OOUI\HtmlSnippet( $label );
                }
 
+               if ( $this->mHideIf ) {
+                       $preloadModules = true;
+                       $config['hideIf'] = $this->mHideIf;
+               }
+
+               $config['modules'] = $this->getOOUIModules();
+
+               if ( $preloadModules ) {
+                       $this->mParent->getOutput()->addModules( 'mediawiki.htmlform.ooui' );
+                       $this->mParent->getOutput()->addModules( $this->getOOUIModules() );
+               }
+
                return $this->getFieldLayoutOOUI( $inputField, $config );
        }
 
@@ -630,9 +665,31 @@ abstract class HTMLFormField {
        protected function getFieldLayoutOOUI( $inputField, $config ) {
                if ( isset( $this->mClassWithButton ) ) {
                        $buttonWidget = $this->mClassWithButton->getInputOOUI( '' );
-                       return new OOUI\ActionFieldLayout( $inputField, $buttonWidget, $config );
+                       return new HTMLFormActionFieldLayout( $inputField, $buttonWidget, $config );
                }
-               return new OOUI\FieldLayout( $inputField, $config );
+               return new HTMLFormFieldLayout( $inputField, $config );
+       }
+
+       /**
+        * Whether the field should be automatically infused. Note that all OOjs UI HTMLForm fields are
+        * infusable (you can call OO.ui.infuse() on them), but not all are infused by default, since
+        * there is no benefit in doing it e.g. for buttons and it's a small performance hit on page load.
+        *
+        * @return bool
+        */
+       protected function shouldInfuseOOUI() {
+               // Always infuse fields with help text, since the interface for it is nicer with JS
+               return $this->getHelpText() !== null;
+       }
+
+       /**
+        * Get the list of extra ResourceLoader modules which must be loaded client-side before it's
+        * possible to infuse this field's OOjs UI widget.
+        *
+        * @return string[]
+        */
+       protected function getOOUIModules() {
+               return [];
        }
 
        /**
@@ -840,6 +897,30 @@ abstract class HTMLFormField {
                return $errors;
        }
 
+       /**
+        * Determine notices to display for the field.
+        *
+        * @since 1.28
+        * @return string[]
+        */
+       function getNotices() {
+               $notices = [];
+
+               if ( isset( $this->mParams['notice-message'] ) ) {
+                       $notices[] = $this->getMessage( $this->mParams['notice-message'] )->parse();
+               }
+
+               if ( isset( $this->mParams['notice-messages'] ) ) {
+                       foreach ( $this->mParams['notice-messages'] as $msg ) {
+                               $notices[] = $this->getMessage( $msg )->parse();
+                       }
+               } elseif ( isset( $this->mParams['notice'] ) ) {
+                       $notices[] = $this->mParams['notice'];
+               }
+
+               return $notices;
+       }
+
        /**
         * @return string HTML
         */
diff --git a/includes/htmlform/HTMLFormFieldCloner.php b/includes/htmlform/HTMLFormFieldCloner.php
deleted file mode 100644 (file)
index ec1bd84..0000000
+++ /dev/null
@@ -1,380 +0,0 @@
-<?php
-
-/**
- * A container for HTMLFormFields that allows for multiple copies of the set of
- * fields to be displayed to and entered by the user.
- *
- * Recognized parameters, besides the general ones, include:
- *   fields - HTMLFormField descriptors for the subfields this cloner manages.
- *     The format is just like for the HTMLForm. A field with key 'delete' is
- *     special: it must have type = submit and will serve to delete the group
- *     of fields.
- *   required - If specified, at least one group of fields must be submitted.
- *   format - HTMLForm display format to use when displaying the subfields:
- *     'table', 'div', or 'raw'.
- *   row-legend - If non-empty, each group of subfields will be enclosed in a
- *     fieldset. The value is the name of a message key to use as the legend.
- *   create-button-message - Message to use as the text of the button to
- *     add an additional group of fields.
- *   delete-button-message - Message to use as the text of automatically-
- *     generated 'delete' button. Ignored if 'delete' is included in 'fields'.
- *
- * In the generated HTML, the subfields will be named along the lines of
- * "clonerName[index][fieldname]", with ids "clonerId--index--fieldid". 'index'
- * may be a number or an arbitrary string, and may likely change when the page
- * is resubmitted. Cloners may be nested, resulting in field names along the
- * lines of "cloner1Name[index1][cloner2Name][index2][fieldname]" and
- * corresponding ids.
- *
- * Use of cloner may result in submissions of the page that are not submissions
- * of the HTMLForm, when non-JavaScript clients use the create or remove buttons.
- *
- * The result is an array, with values being arrays mapping subfield names to
- * their values. On non-HTMLForm-submission page loads, there may also be
- * additional (string) keys present with other types of values.
- *
- * @since 1.23
- */
-class HTMLFormFieldCloner extends HTMLFormField {
-       private static $counter = 0;
-
-       /**
-        * @var string String uniquely identifying this cloner instance and
-        * unlikely to exist otherwise in the generated HTML, while still being
-        * valid as part of an HTML id.
-        */
-       protected $uniqueId;
-
-       public function __construct( $params ) {
-               $this->uniqueId = get_class( $this ) . ++self::$counter . 'x';
-               parent::__construct( $params );
-
-               if ( empty( $this->mParams['fields'] ) || !is_array( $this->mParams['fields'] ) ) {
-                       throw new MWException( 'HTMLFormFieldCloner called without any fields' );
-               }
-
-               // Make sure the delete button, if explicitly specified, is sane
-               if ( isset( $this->mParams['fields']['delete'] ) ) {
-                       $class = 'mw-htmlform-cloner-delete-button';
-                       $info = $this->mParams['fields']['delete'] + [
-                               'cssclass' => $class
-                       ];
-                       unset( $info['name'], $info['class'] );
-
-                       if ( !isset( $info['type'] ) || $info['type'] !== 'submit' ) {
-                               throw new MWException(
-                                       'HTMLFormFieldCloner delete field, if specified, must be of type "submit"'
-                               );
-                       }
-
-                       if ( !in_array( $class, explode( ' ', $info['cssclass'] ) ) ) {
-                               $info['cssclass'] .= " $class";
-                       }
-
-                       $this->mParams['fields']['delete'] = $info;
-               }
-       }
-
-       /**
-        * Create the HTMLFormFields that go inside this element, using the
-        * specified key.
-        *
-        * @param string $key Array key under which these fields should be named
-        * @return HTMLFormField[]
-        */
-       protected function createFieldsForKey( $key ) {
-               $fields = [];
-               foreach ( $this->mParams['fields'] as $fieldname => $info ) {
-                       $name = "{$this->mName}[$key][$fieldname]";
-                       if ( isset( $info['name'] ) ) {
-                               $info['name'] = "{$this->mName}[$key][{$info['name']}]";
-                       } else {
-                               $info['name'] = $name;
-                       }
-                       if ( isset( $info['id'] ) ) {
-                               $info['id'] = Sanitizer::escapeId( "{$this->mID}--$key--{$info['id']}" );
-                       } else {
-                               $info['id'] = Sanitizer::escapeId( "{$this->mID}--$key--$fieldname" );
-                       }
-                       $field = HTMLForm::loadInputFromParameters( $name, $info, $this->mParent );
-                       $fields[$fieldname] = $field;
-               }
-               return $fields;
-       }
-
-       /**
-        * Re-key the specified values array to match the names applied by
-        * createFieldsForKey().
-        *
-        * @param string $key Array key under which these fields should be named
-        * @param array $values Values array from the request
-        * @return array
-        */
-       protected function rekeyValuesArray( $key, $values ) {
-               $data = [];
-               foreach ( $values as $fieldname => $value ) {
-                       $name = "{$this->mName}[$key][$fieldname]";
-                       $data[$name] = $value;
-               }
-               return $data;
-       }
-
-       protected function needsLabel() {
-               return false;
-       }
-
-       public function loadDataFromRequest( $request ) {
-               // It's possible that this might be posted with no fields. Detect that
-               // by looking for an edit token.
-               if ( !$request->getCheck( 'wpEditToken' ) && $request->getArray( $this->mName ) === null ) {
-                       return $this->getDefault();
-               }
-
-               $values = $request->getArray( $this->mName );
-               if ( $values === null ) {
-                       $values = [];
-               }
-
-               $ret = [];
-               foreach ( $values as $key => $value ) {
-                       if ( $key === 'create' || isset( $value['delete'] ) ) {
-                               $ret['nonjs'] = 1;
-                               continue;
-                       }
-
-                       // Add back in $request->getValues() so things that look for e.g.
-                       // wpEditToken don't fail.
-                       $data = $this->rekeyValuesArray( $key, $value ) + $request->getValues();
-
-                       $fields = $this->createFieldsForKey( $key );
-                       $subrequest = new DerivativeRequest( $request, $data, $request->wasPosted() );
-                       $row = [];
-                       foreach ( $fields as $fieldname => $field ) {
-                               if ( $field->skipLoadData( $subrequest ) ) {
-                                       continue;
-                               } elseif ( !empty( $field->mParams['disabled'] ) ) {
-                                       $row[$fieldname] = $field->getDefault();
-                               } else {
-                                       $row[$fieldname] = $field->loadDataFromRequest( $subrequest );
-                               }
-                       }
-                       $ret[] = $row;
-               }
-
-               if ( isset( $values['create'] ) ) {
-                       // Non-JS client clicked the "create" button.
-                       $fields = $this->createFieldsForKey( $this->uniqueId );
-                       $row = [];
-                       foreach ( $fields as $fieldname => $field ) {
-                               if ( !empty( $field->mParams['nodata'] ) ) {
-                                       continue;
-                               } else {
-                                       $row[$fieldname] = $field->getDefault();
-                               }
-                       }
-                       $ret[] = $row;
-               }
-
-               return $ret;
-       }
-
-       public function getDefault() {
-               $ret = parent::getDefault();
-
-               // The default default is one entry with all subfields at their
-               // defaults.
-               if ( $ret === null ) {
-                       $fields = $this->createFieldsForKey( $this->uniqueId );
-                       $row = [];
-                       foreach ( $fields as $fieldname => $field ) {
-                               if ( !empty( $field->mParams['nodata'] ) ) {
-                                       continue;
-                               } else {
-                                       $row[$fieldname] = $field->getDefault();
-                               }
-                       }
-                       $ret = [ $row ];
-               }
-
-               return $ret;
-       }
-
-       public function cancelSubmit( $values, $alldata ) {
-               if ( isset( $values['nonjs'] ) ) {
-                       return true;
-               }
-
-               foreach ( $values as $key => $value ) {
-                       $fields = $this->createFieldsForKey( $key );
-                       foreach ( $fields as $fieldname => $field ) {
-                               if ( !array_key_exists( $fieldname, $value ) ) {
-                                       continue;
-                               }
-                               if ( $field->cancelSubmit( $value[$fieldname], $alldata ) ) {
-                                       return true;
-                               }
-                       }
-               }
-
-               return parent::cancelSubmit( $values, $alldata );
-       }
-
-       public function validate( $values, $alldata ) {
-               if ( isset( $this->mParams['required'] )
-                       && $this->mParams['required'] !== false
-                       && !$values
-               ) {
-                       return $this->msg( 'htmlform-cloner-required' )->parseAsBlock();
-               }
-
-               if ( isset( $values['nonjs'] ) ) {
-                       // The submission was a non-JS create/delete click, so fail
-                       // validation in case cancelSubmit() somehow didn't already handle
-                       // it.
-                       return false;
-               }
-
-               foreach ( $values as $key => $value ) {
-                       $fields = $this->createFieldsForKey( $key );
-                       foreach ( $fields as $fieldname => $field ) {
-                               if ( !array_key_exists( $fieldname, $value ) ) {
-                                       continue;
-                               }
-                               $ok = $field->validate( $value[$fieldname], $alldata );
-                               if ( $ok !== true ) {
-                                       return false;
-                               }
-                       }
-               }
-
-               return parent::validate( $values, $alldata );
-       }
-
-       /**
-        * Get the input HTML for the specified key.
-        *
-        * @param string $key Array key under which the fields should be named
-        * @param array $values
-        * @return string
-        */
-       protected function getInputHTMLForKey( $key, $values ) {
-               $displayFormat = isset( $this->mParams['format'] )
-                       ? $this->mParams['format']
-                       : $this->mParent->getDisplayFormat();
-
-               // Conveniently, PHP method names are case-insensitive.
-               $getFieldHtmlMethod = $displayFormat == 'table' ? 'getTableRow' : ( 'get' . $displayFormat );
-
-               $html = '';
-               $hidden = '';
-               $hasLabel = false;
-
-               $fields = $this->createFieldsForKey( $key );
-               foreach ( $fields as $fieldname => $field ) {
-                       $v = array_key_exists( $fieldname, $values )
-                               ? $values[$fieldname]
-                               : $field->getDefault();
-
-                       if ( $field instanceof HTMLHiddenField ) {
-                               // HTMLHiddenField doesn't generate its own HTML
-                               list( $name, $value, $params ) = $field->getHiddenFieldData( $v );
-                               $hidden .= Html::hidden( $name, $value, $params ) . "\n";
-                       } else {
-                               $html .= $field->$getFieldHtmlMethod( $v );
-
-                               $labelValue = trim( $field->getLabel() );
-                               if ( $labelValue != '&#160;' && $labelValue !== '' ) {
-                                       $hasLabel = true;
-                               }
-                       }
-               }
-
-               if ( !isset( $fields['delete'] ) ) {
-                       $name = "{$this->mName}[$key][delete]";
-                       $label = isset( $this->mParams['delete-button-message'] )
-                               ? $this->mParams['delete-button-message']
-                               : 'htmlform-cloner-delete';
-                       $field = HTMLForm::loadInputFromParameters( $name, [
-                               'type' => 'submit',
-                               'name' => $name,
-                               'id' => Sanitizer::escapeId( "{$this->mID}--$key--delete" ),
-                               'cssclass' => 'mw-htmlform-cloner-delete-button',
-                               'default' => $this->getMessage( $label )->text(),
-                       ], $this->mParent );
-                       $v = $field->getDefault();
-
-                       if ( $displayFormat === 'table' ) {
-                               $html .= $field->$getFieldHtmlMethod( $v );
-                       } else {
-                               $html .= $field->getInputHTML( $v );
-                       }
-               }
-
-               if ( $displayFormat !== 'raw' ) {
-                       $classes = [
-                               'mw-htmlform-cloner-row',
-                       ];
-
-                       if ( !$hasLabel ) { // Avoid strange spacing when no labels exist
-                               $classes[] = 'mw-htmlform-nolabel';
-                       }
-
-                       $attribs = [
-                               'class' => implode( ' ', $classes ),
-                       ];
-
-                       if ( $displayFormat === 'table' ) {
-                               $html = Html::rawElement( 'table',
-                                       $attribs,
-                                       Html::rawElement( 'tbody', [], "\n$html\n" ) ) . "\n";
-                       } else {
-                               $html = Html::rawElement( 'div', $attribs, "\n$html\n" );
-                       }
-               }
-
-               $html .= $hidden;
-
-               if ( !empty( $this->mParams['row-legend'] ) ) {
-                       $legend = $this->msg( $this->mParams['row-legend'] )->text();
-                       $html = Xml::fieldset( $legend, $html );
-               }
-
-               return $html;
-       }
-
-       public function getInputHTML( $values ) {
-               $html = '';
-
-               foreach ( (array)$values as $key => $value ) {
-                       if ( $key === 'nonjs' ) {
-                               continue;
-                       }
-                       $html .= Html::rawElement( 'li', [ 'class' => 'mw-htmlform-cloner-li' ],
-                               $this->getInputHTMLForKey( $key, $value )
-                       );
-               }
-
-               $template = $this->getInputHTMLForKey( $this->uniqueId, null );
-               $html = Html::rawElement( 'ul', [
-                       'id' => "mw-htmlform-cloner-list-{$this->mID}",
-                       'class' => 'mw-htmlform-cloner-ul',
-                       'data-template' => $template,
-                       'data-unique-id' => $this->uniqueId,
-               ], $html );
-
-               $name = "{$this->mName}[create]";
-               $label = isset( $this->mParams['create-button-message'] )
-                       ? $this->mParams['create-button-message']
-                       : 'htmlform-cloner-create';
-               $field = HTMLForm::loadInputFromParameters( $name, [
-                       'type' => 'submit',
-                       'name' => $name,
-                       'id' => Sanitizer::escapeId( "{$this->mID}--create" ),
-                       'cssclass' => 'mw-htmlform-cloner-create-button',
-                       'default' => $this->getMessage( $label )->text(),
-               ], $this->mParent );
-               $html .= $field->getInputHTML( $field->getDefault() );
-
-               return $html;
-       }
-}
diff --git a/includes/htmlform/HTMLFormFieldWithButton.php b/includes/htmlform/HTMLFormFieldWithButton.php
deleted file mode 100644 (file)
index bcb07bd..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-/**
- * Enables HTMLFormField elements to be build with a button.
- */
-class HTMLFormFieldWithButton extends HTMLFormField {
-       /** @var string $mButtonClass CSS class for the button in this field */
-       protected $mButtonClass = '';
-
-       /** @var string|integer $mButtonId Element ID for the button in this field */
-       protected $mButtonId = '';
-
-       /** @var string $mButtonName Name the button in this field */
-       protected $mButtonName = '';
-
-       /** @var string $mButtonType Type of the button in this field (e.g. button or submit) */
-       protected $mButtonType = 'submit';
-
-       /** @var string $mButtonType Value for the button in this field */
-       protected $mButtonValue;
-
-       /** @var string $mButtonType Value for the button in this field */
-       protected $mButtonFlags = [ 'progressive' ];
-
-       public function __construct( $info ) {
-               if ( isset( $info['buttonclass'] ) ) {
-                       $this->mButtonClass = $info['buttonclass'];
-               }
-               if ( isset( $info['buttonid'] ) ) {
-                       $this->mButtonId = $info['buttonid'];
-               }
-               if ( isset( $info['buttonname'] ) ) {
-                       $this->mButtonName = $info['buttonname'];
-               }
-               if ( isset( $info['buttondefault'] ) ) {
-                       $this->mButtonValue = $info['buttondefault'];
-               }
-               if ( isset( $info['buttontype'] ) ) {
-                       $this->mButtonType = $info['buttontype'];
-               }
-               if ( isset( $info['buttonflags'] ) ) {
-                       $this->mButtonFlags = $info['buttonflags'];
-               }
-               parent::__construct( $info );
-       }
-
-       public function getInputHTML( $value ) {
-               $attr = [
-                       'class' => 'mw-htmlform-submit ' . $this->mButtonClass,
-                       'id' => $this->mButtonId,
-               ] + $this->getAttributes( [ 'disabled', 'tabindex' ] );
-
-               return Html::input( $this->mButtonName, $this->mButtonValue, $this->mButtonType, $attr );
-       }
-
-       public function getInputOOUI( $value ) {
-               return new OOUI\ButtonInputWidget( [
-                       'name' => $this->mButtonName,
-                       'value' => $this->mButtonValue,
-                       'type' => $this->mButtonType,
-                       'label' => $this->mButtonValue,
-                       'flags' => $this->mButtonFlags,
-               ] + OOUI\Element::configFromHtmlAttributes(
-                       $this->getAttributes( [ 'disabled', 'tabindex' ] )
-               ) );
-       }
-
-       /**
-        * Combines the passed element with a button.
-        * @param String $element Element to combine the button with.
-        * @return String
-        */
-       public function getElement( $element ) {
-               return $element . '&#160;' . $this->getInputHTML( '' );
-       }
-}
diff --git a/includes/htmlform/HTMLHiddenField.php b/includes/htmlform/HTMLHiddenField.php
deleted file mode 100644 (file)
index c0fce2b..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-
-class HTMLHiddenField extends HTMLFormField {
-       protected $outputAsDefault = true;
-
-       public function __construct( $params ) {
-               parent::__construct( $params );
-
-               if ( isset( $this->mParams['output-as-default'] ) ) {
-                       $this->outputAsDefault = (bool)$this->mParams['output-as-default'];
-               }
-
-               # Per HTML5 spec, hidden fields cannot be 'required'
-               # http://www.w3.org/TR/html5/forms.html#hidden-state-%28type=hidden%29
-               unset( $this->mParams['required'] );
-       }
-
-       public function getHiddenFieldData( $value ) {
-               $params = [];
-               if ( $this->mID ) {
-                       $params['id'] = $this->mID;
-               }
-
-               if ( $this->outputAsDefault ) {
-                       $value = $this->mDefault;
-               }
-
-               return [ $this->mName, $value, $params ];
-       }
-
-       public function getTableRow( $value ) {
-               list( $name, $value, $params ) = $this->getHiddenFieldData( $value );
-               $this->mParent->addHiddenField( $name, $value, $params );
-               return '';
-       }
-
-       /**
-        * @param string $value
-        * @return string
-        * @since 1.20
-        */
-       public function getDiv( $value ) {
-               return $this->getTableRow( $value );
-       }
-
-       /**
-        * @param string $value
-        * @return string
-        * @since 1.20
-        */
-       public function getRaw( $value ) {
-               return $this->getTableRow( $value );
-       }
-
-       public function getInputHTML( $value ) {
-               return '';
-       }
-
-       public function canDisplayErrors() {
-               return false;
-       }
-
-       public function hasVisibleOutput() {
-               return false;
-       }
-}
diff --git a/includes/htmlform/HTMLInfoField.php b/includes/htmlform/HTMLInfoField.php
deleted file mode 100644 (file)
index ada4fb6..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-/**
- * An information field (text blob), not a proper input.
- */
-class HTMLInfoField extends HTMLFormField {
-       public function __construct( $info ) {
-               $info['nodata'] = true;
-
-               parent::__construct( $info );
-       }
-
-       public function getInputHTML( $value ) {
-               return !empty( $this->mParams['raw'] ) ? $value : htmlspecialchars( $value );
-       }
-
-       public function getInputOOUI( $value ) {
-               if ( !empty( $this->mParams['raw'] ) ) {
-                       $value = new OOUI\HtmlSnippet( $value );
-               }
-
-               return new OOUI\LabelWidget( [
-                       'label' => $value,
-               ] );
-       }
-
-       public function getTableRow( $value ) {
-               if ( !empty( $this->mParams['rawrow'] ) ) {
-                       return $value;
-               }
-
-               return parent::getTableRow( $value );
-       }
-
-       /**
-        * @param string $value
-        * @return string
-        * @since 1.20
-        */
-       public function getDiv( $value ) {
-               if ( !empty( $this->mParams['rawrow'] ) ) {
-                       return $value;
-               }
-
-               return parent::getDiv( $value );
-       }
-
-       /**
-        * @param string $value
-        * @return string
-        * @since 1.20
-        */
-       public function getRaw( $value ) {
-               if ( !empty( $this->mParams['rawrow'] ) ) {
-                       return $value;
-               }
-
-               return parent::getRaw( $value );
-       }
-
-       protected function needsLabel() {
-               return false;
-       }
-}
diff --git a/includes/htmlform/HTMLIntField.php b/includes/htmlform/HTMLIntField.php
deleted file mode 100644 (file)
index b0148d9..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-/**
- * A field that must contain a number
- */
-class HTMLIntField extends HTMLFloatField {
-       function validate( $value, $alldata ) {
-               $p = parent::validate( $value, $alldata );
-
-               if ( $p !== true ) {
-                       return $p;
-               }
-
-               # http://www.w3.org/TR/html5/infrastructure.html#signed-integers
-               # with the addition that a leading '+' sign is ok. Note that leading zeros
-               # are fine, and will be left in the input, which is useful for things like
-               # phone numbers when you know that they are integers (the HTML5 type=tel
-               # input does not require its value to be numeric).  If you want a tidier
-               # value to, eg, save in the DB, clean it up with intval().
-               if ( !preg_match( '/^((\+|\-)?\d+)?$/', trim( $value ) ) ) {
-                       return $this->msg( 'htmlform-int-invalid' )->parseAsBlock();
-               }
-
-               return true;
-       }
-}
diff --git a/includes/htmlform/HTMLMultiSelectField.php b/includes/htmlform/HTMLMultiSelectField.php
deleted file mode 100644 (file)
index 23125bd..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-<?php
-
-/**
- * Multi-select field
- */
-class HTMLMultiSelectField extends HTMLFormField implements HTMLNestedFilterable {
-       function validate( $value, $alldata ) {
-               $p = parent::validate( $value, $alldata );
-
-               if ( $p !== true ) {
-                       return $p;
-               }
-
-               if ( !is_array( $value ) ) {
-                       return false;
-               }
-
-               # If all options are valid, array_intersect of the valid options
-               # and the provided options will return the provided options.
-               $validOptions = HTMLFormField::flattenOptions( $this->getOptions() );
-
-               $validValues = array_intersect( $value, $validOptions );
-               if ( count( $validValues ) == count( $value ) ) {
-                       return true;
-               } else {
-                       return $this->msg( 'htmlform-select-badoption' )->parse();
-               }
-       }
-
-       function getInputHTML( $value ) {
-               $value = HTMLFormField::forceToStringRecursive( $value );
-               $html = $this->formatOptions( $this->getOptions(), $value );
-
-               return $html;
-       }
-
-       function formatOptions( $options, $value ) {
-               $html = '';
-
-               $attribs = $this->getAttributes( [ 'disabled', 'tabindex' ] );
-
-               foreach ( $options as $label => $info ) {
-                       if ( is_array( $info ) ) {
-                               $html .= Html::rawElement( 'h1', [], $label ) . "\n";
-                               $html .= $this->formatOptions( $info, $value );
-                       } else {
-                               $thisAttribs = [
-                                       'id' => "{$this->mID}-$info",
-                                       'value' => $info,
-                               ];
-                               $checked = in_array( $info, $value, true );
-
-                               $checkbox = $this->getOneCheckbox( $checked, $attribs + $thisAttribs, $label );
-
-                               $html .= ' ' . Html::rawElement(
-                                       'div',
-                                       [ 'class' => 'mw-htmlform-flatlist-item' ],
-                                       $checkbox
-                               );
-                       }
-               }
-
-               return $html;
-       }
-
-       protected function getOneCheckbox( $checked, $attribs, $label ) {
-               if ( $this->mParent instanceof OOUIHTMLForm ) {
-                       throw new MWException( 'HTMLMultiSelectField#getOneCheckbox() is not supported' );
-               } else {
-                       $elementFunc = [ 'Html', $this->mOptionsLabelsNotFromMessage ? 'rawElement' : 'element' ];
-                       $checkbox =
-                               Xml::check( "{$this->mName}[]", $checked, $attribs ) .
-                               '&#160;' .
-                               call_user_func( $elementFunc,
-                                       'label',
-                                       [ 'for' => $attribs['id'] ],
-                                       $label
-                               );
-                       if ( $this->mParent->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) {
-                               $checkbox = Html::openElement( 'div', [ 'class' => 'mw-ui-checkbox' ] ) .
-                                       $checkbox .
-                                       Html::closeElement( 'div' );
-                       }
-                       return $checkbox;
-               }
-       }
-
-       /**
-        * Get the OOUI version of this field.
-        *
-        * @since 1.28
-        * @param string[] $value
-        * @return OOUI\CheckboxMultiselectInputWidget
-        */
-       public function getInputOOUI( $value ) {
-               $attr = $this->getTooltipAndAccessKey();
-               $attr['id'] = $this->mID;
-               $attr['name'] = "{$this->mName}[]";
-
-               $attr['value'] = $value;
-               $attr['options'] = $this->getOptionsOOUI();
-
-               if ( $this->mOptionsLabelsNotFromMessage ) {
-                       foreach ( $attr['options'] as &$option ) {
-                               $option['label'] = new OOUI\HtmlSnippet( $option['label'] );
-                       }
-               }
-
-               $attr += OOUI\Element::configFromHtmlAttributes(
-                       $this->getAttributes( [ 'disabled', 'tabindex' ] )
-               );
-
-               if ( $this->mClass !== '' ) {
-                       $attr['classes'] = [ $this->mClass ];
-               }
-
-               return new OOUI\CheckboxMultiselectInputWidget( $attr );
-       }
-
-       /**
-        * @param WebRequest $request
-        *
-        * @return string
-        */
-       function loadDataFromRequest( $request ) {
-               if ( $this->mParent->getMethod() == 'post' ) {
-                       if ( $request->wasPosted() ) {
-                               # Checkboxes are just not added to the request arrays if they're not checked,
-                               # so it's perfectly possible for there not to be an entry at all
-                               return $request->getArray( $this->mName, [] );
-                       } else {
-                               # That's ok, the user has not yet submitted the form, so show the defaults
-                               return $this->getDefault();
-                       }
-               } else {
-                       # This is the impossible case: if we look at $_GET and see no data for our
-                       # field, is it because the user has not yet submitted the form, or that they
-                       # have submitted it with all the options unchecked? We will have to assume the
-                       # latter, which basically means that you can't specify 'positive' defaults
-                       # for GET forms.
-                       # @todo FIXME...
-                       return $request->getArray( $this->mName, [] );
-               }
-       }
-
-       function getDefault() {
-               if ( isset( $this->mDefault ) ) {
-                       return $this->mDefault;
-               } else {
-                       return [];
-               }
-       }
-
-       function filterDataForSubmit( $data ) {
-               $data = HTMLFormField::forceToStringRecursive( $data );
-               $options = HTMLFormField::flattenOptions( $this->getOptions() );
-
-               $res = [];
-               foreach ( $options as $opt ) {
-                       $res["$opt"] = in_array( $opt, $data, true );
-               }
-
-               return $res;
-       }
-
-       protected function needsLabel() {
-               return false;
-       }
-}
diff --git a/includes/htmlform/HTMLRadioField.php b/includes/htmlform/HTMLRadioField.php
deleted file mode 100644 (file)
index e5b5e68..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-
-/**
- * Radio checkbox fields.
- */
-class HTMLRadioField extends HTMLFormField {
-       function validate( $value, $alldata ) {
-               $p = parent::validate( $value, $alldata );
-
-               if ( $p !== true ) {
-                       return $p;
-               }
-
-               if ( !is_string( $value ) && !is_int( $value ) ) {
-                       return false;
-               }
-
-               $validOptions = HTMLFormField::flattenOptions( $this->getOptions() );
-
-               if ( in_array( strval( $value ), $validOptions, true ) ) {
-                       return true;
-               } else {
-                       return $this->msg( 'htmlform-select-badoption' )->parse();
-               }
-       }
-
-       /**
-        * This returns a block of all the radio options, in one cell.
-        * @see includes/HTMLFormField#getInputHTML()
-        *
-        * @param string $value
-        *
-        * @return string
-        */
-       function getInputHTML( $value ) {
-               $html = $this->formatOptions( $this->getOptions(), strval( $value ) );
-
-               return $html;
-       }
-
-       function getInputOOUI( $value ) {
-               $options = [];
-               foreach ( $this->getOptions() as $label => $data ) {
-                       $options[] = [
-                               'data' => $data,
-                               'label' => $this->mOptionsLabelsNotFromMessage ? new OOUI\HtmlSnippet( $label ) : $label,
-                       ];
-               }
-
-               return new OOUI\RadioSelectInputWidget( [
-                       'name' => $this->mName,
-                       'id' => $this->mID,
-                       'value' => $value,
-                       'options' => $options,
-               ] + OOUI\Element::configFromHtmlAttributes(
-                       $this->getAttributes( [ 'disabled', 'tabindex' ] )
-               ) );
-       }
-
-       function formatOptions( $options, $value ) {
-               global $wgUseMediaWikiUIEverywhere;
-
-               $html = '';
-
-               $attribs = $this->getAttributes( [ 'disabled', 'tabindex' ] );
-               $elementFunc = [ 'Html', $this->mOptionsLabelsNotFromMessage ? 'rawElement' : 'element' ];
-
-               # @todo Should this produce an unordered list perhaps?
-               foreach ( $options as $label => $info ) {
-                       if ( is_array( $info ) ) {
-                               $html .= Html::rawElement( 'h1', [], $label ) . "\n";
-                               $html .= $this->formatOptions( $info, $value );
-                       } else {
-                               $id = Sanitizer::escapeId( $this->mID . "-$info" );
-                               $classes = [ 'mw-htmlform-flatlist-item' ];
-                               if ( $wgUseMediaWikiUIEverywhere || $this->mParent instanceof VFormHTMLForm ) {
-                                       $classes[] = 'mw-ui-radio';
-                               }
-                               $radio = Xml::radio( $this->mName, $info, $info === $value, $attribs + [ 'id' => $id ] );
-                               $radio .= '&#160;' . call_user_func( $elementFunc, 'label', [ 'for' => $id ], $label );
-
-                               $html .= ' ' . Html::rawElement(
-                                       'div',
-                                       [ 'class' => $classes ],
-                                       $radio
-                               );
-                       }
-               }
-
-               return $html;
-       }
-
-       protected function needsLabel() {
-               return false;
-       }
-}
diff --git a/includes/htmlform/HTMLSelectAndOtherField.php b/includes/htmlform/HTMLSelectAndOtherField.php
deleted file mode 100644 (file)
index e75c2b2..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-<?php
-
-/**
- * Double field with a dropdown list constructed from a system message in the format
- *     * Optgroup header
- *     ** <option value>
- *     * New Optgroup header
- * Plus a text field underneath for an additional reason.  The 'value' of the field is
- * "<select>: <extra reason>", or "<extra reason>" if nothing has been selected in the
- * select dropdown.
- * @todo FIXME: If made 'required', only the text field should be compulsory.
- */
-class HTMLSelectAndOtherField extends HTMLSelectField {
-       function __construct( $params ) {
-               if ( array_key_exists( 'other', $params ) ) {
-                       // Do nothing
-               } elseif ( array_key_exists( 'other-message', $params ) ) {
-                       $params['other'] = $this->getMessage( $params['other-message'] )->plain();
-               } else {
-                       $params['other'] = $this->msg( 'htmlform-selectorother-other' )->plain();
-               }
-
-               parent::__construct( $params );
-
-               if ( $this->getOptions() === null ) {
-                       // Sulk
-                       throw new MWException( 'HTMLSelectAndOtherField called without any options' );
-               }
-               if ( !in_array( 'other', $this->mOptions, true ) ) {
-                       // Have 'other' always as first element
-                       $this->mOptions = [ $params['other'] => 'other' ] + $this->mOptions;
-               }
-               $this->mFlatOptions = self::flattenOptions( $this->getOptions() );
-
-       }
-
-       function getInputHTML( $value ) {
-               $select = parent::getInputHTML( $value[1] );
-
-               $textAttribs = [
-                       'id' => $this->mID . '-other',
-                       'size' => $this->getSize(),
-                       'class' => [ 'mw-htmlform-select-and-other-field' ],
-                       'data-id-select' => $this->mID,
-               ];
-
-               if ( $this->mClass !== '' ) {
-                       $textAttribs['class'][] = $this->mClass;
-               }
-
-               $allowedParams = [
-                       'required',
-                       'autofocus',
-                       'multiple',
-                       'disabled',
-                       'tabindex',
-                       'maxlength', // gets dynamic with javascript, see mediawiki.htmlform.js
-               ];
-
-               $textAttribs += $this->getAttributes( $allowedParams );
-
-               $textbox = Html::input( $this->mName . '-other', $value[2], 'text', $textAttribs );
-
-               return "$select<br />\n$textbox";
-       }
-
-       function getInputOOUI( $value ) {
-               return false;
-       }
-
-       /**
-        * @param WebRequest $request
-        *
-        * @return array("<overall message>","<select value>","<text field value>")
-        */
-       function loadDataFromRequest( $request ) {
-               if ( $request->getCheck( $this->mName ) ) {
-                       $list = $request->getText( $this->mName );
-                       $text = $request->getText( $this->mName . '-other' );
-
-                       // Should be built the same as in mediawiki.htmlform.js
-                       if ( $list == 'other' ) {
-                               $final = $text;
-                       } elseif ( !in_array( $list, $this->mFlatOptions, true ) ) {
-                               # User has spoofed the select form to give an option which wasn't
-                               # in the original offer.  Sulk...
-                               $final = $text;
-                       } elseif ( $text == '' ) {
-                               $final = $list;
-                       } else {
-                               $final = $list . $this->msg( 'colon-separator' )->inContentLanguage()->text() . $text;
-                       }
-               } else {
-                       $final = $this->getDefault();
-
-                       $list = 'other';
-                       $text = $final;
-                       foreach ( $this->mFlatOptions as $option ) {
-                               $match = $option . $this->msg( 'colon-separator' )->inContentLanguage()->text();
-                               if ( strpos( $text, $match ) === 0 ) {
-                                       $list = $option;
-                                       $text = substr( $text, strlen( $match ) );
-                                       break;
-                               }
-                       }
-               }
-
-               return [ $final, $list, $text ];
-       }
-
-       function getSize() {
-               return isset( $this->mParams['size'] ) ? $this->mParams['size'] : 45;
-       }
-
-       function validate( $value, $alldata ) {
-               # HTMLSelectField forces $value to be one of the options in the select
-               # field, which is not useful here.  But we do want the validation further up
-               # the chain
-               $p = parent::validate( $value[1], $alldata );
-
-               if ( $p !== true ) {
-                       return $p;
-               }
-
-               if ( isset( $this->mParams['required'] )
-                       && $this->mParams['required'] !== false
-                       && $value[1] === ''
-               ) {
-                       return $this->msg( 'htmlform-required' )->parse();
-               }
-
-               return true;
-       }
-}
diff --git a/includes/htmlform/HTMLSelectField.php b/includes/htmlform/HTMLSelectField.php
deleted file mode 100644 (file)
index b6ad46c..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-/**
- * A select dropdown field.  Basically a wrapper for Xmlselect class
- */
-class HTMLSelectField extends HTMLFormField {
-       function validate( $value, $alldata ) {
-               $p = parent::validate( $value, $alldata );
-
-               if ( $p !== true ) {
-                       return $p;
-               }
-
-               $validOptions = HTMLFormField::flattenOptions( $this->getOptions() );
-
-               if ( in_array( strval( $value ), $validOptions, true ) ) {
-                       return true;
-               } else {
-                       return $this->msg( 'htmlform-select-badoption' )->parse();
-               }
-       }
-
-       function getInputHTML( $value ) {
-               $select = new XmlSelect( $this->mName, $this->mID, strval( $value ) );
-
-               if ( !empty( $this->mParams['disabled'] ) ) {
-                       $select->setAttribute( 'disabled', 'disabled' );
-               }
-
-               $allowedParams = [ 'tabindex', 'size' ];
-               $customParams = $this->getAttributes( $allowedParams );
-               foreach ( $customParams as $name => $value ) {
-                       $select->setAttribute( $name, $value );
-               }
-
-               if ( $this->mClass !== '' ) {
-                       $select->setAttribute( 'class', $this->mClass );
-               }
-
-               $select->addOptions( $this->getOptions() );
-
-               return $select->getHTML();
-       }
-
-       function getInputOOUI( $value ) {
-               $disabled = false;
-               $allowedParams = [ 'tabindex' ];
-               $attribs = OOUI\Element::configFromHtmlAttributes(
-                       $this->getAttributes( $allowedParams )
-               );
-
-               if ( $this->mClass !== '' ) {
-                       $attribs['classes'] = [ $this->mClass ];
-               }
-
-               if ( !empty( $this->mParams['disabled'] ) ) {
-                       $disabled = true;
-               }
-
-               return new OOUI\DropdownInputWidget( [
-                       'name' => $this->mName,
-                       'id' => $this->mID,
-                       'options' => $this->getOptionsOOUI(),
-                       'value' => strval( $value ),
-                       'disabled' => $disabled,
-               ] + $attribs );
-       }
-}
diff --git a/includes/htmlform/HTMLSelectLimitField.php b/includes/htmlform/HTMLSelectLimitField.php
deleted file mode 100644 (file)
index e7f1c04..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-/**
- * A limit dropdown, which accepts any valid number
- */
-class HTMLSelectLimitField extends HTMLSelectField {
-       /**
-        * Basically don't do any validation. If it's a number that's fine. Also,
-        * add it to the list if it's not there already
-        *
-        * @param string $value
-        * @param array $alldata
-        * @return bool
-        */
-       function validate( $value, $alldata ) {
-               if ( $value == '' ) {
-                       return true;
-               }
-
-               // Let folks pick an explicit limit not from our list, as long as it's a real numbr.
-               if ( !in_array( $value, $this->mParams['options'] )
-                       && $value == intval( $value )
-                       && $value > 0
-               ) {
-                       // This adds the explicitly requested limit value to the drop-down,
-                       // then makes sure it's sorted correctly so when we output the list
-                       // later, the custom option doesn't just show up last.
-                       $this->mParams['options'][$this->mParent->getLanguage()->formatNum( $value )] =
-                               intval( $value );
-                       asort( $this->mParams['options'] );
-               }
-
-               return true;
-       }
-}
diff --git a/includes/htmlform/HTMLSelectNamespace.php b/includes/htmlform/HTMLSelectNamespace.php
deleted file mode 100644 (file)
index ef21969..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-/**
- * Wrapper for Html::namespaceSelector to use in HTMLForm
- */
-class HTMLSelectNamespace extends HTMLFormField {
-       public function __construct( $params ) {
-               parent::__construct( $params );
-
-               $this->mAllValue = array_key_exists( 'all', $params )
-                       ? $params['all']
-                       : 'all';
-
-       }
-
-       function getInputHTML( $value ) {
-               return Html::namespaceSelector(
-                       [
-                               'selected' => $value,
-                               'all' => $this->mAllValue
-                       ], [
-                               'name' => $this->mName,
-                               'id' => $this->mID,
-                               'class' => 'namespaceselector',
-                       ]
-               );
-       }
-
-       public function getInputOOUI( $value ) {
-               return new MediaWiki\Widget\NamespaceInputWidget( [
-                       'value' => $value,
-                       'name' => $this->mName,
-                       'id' => $this->mID,
-                       'includeAllValue' => $this->mAllValue,
-               ] );
-       }
-}
diff --git a/includes/htmlform/HTMLSelectNamespaceWithButton.php b/includes/htmlform/HTMLSelectNamespaceWithButton.php
deleted file mode 100644 (file)
index 5225983..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-/**
- * Creates a Html::namespaceSelector input field with a button assigned to the input field.
- */
-class HTMLSelectNamespaceWithButton extends HTMLSelectNamespace {
-       /** @var HTMLFormFieldWithButton $mClassWithButton */
-       protected $mClassWithButton = null;
-
-       public function __construct( $info ) {
-               $this->mClassWithButton = new HTMLFormFieldWithButton( $info );
-               parent::__construct( $info );
-       }
-
-       public function getInputHTML( $value ) {
-               return $this->mClassWithButton->getElement( parent::getInputHTML( $value ) );
-       }
-}
diff --git a/includes/htmlform/HTMLSelectOrOtherField.php b/includes/htmlform/HTMLSelectOrOtherField.php
deleted file mode 100644 (file)
index 8f7750c..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-
-/**
- * Select dropdown field, with an additional "other" textbox.
- *
- * HTMLComboboxField implements the same functionality using a single form field
- * and should be used instead.
- */
-class HTMLSelectOrOtherField extends HTMLTextField {
-       function __construct( $params ) {
-               parent::__construct( $params );
-               $this->getOptions();
-               if ( !in_array( 'other', $this->mOptions, true ) ) {
-                       $msg =
-                               isset( $params['other'] )
-                                       ? $params['other']
-                                       : wfMessage( 'htmlform-selectorother-other' )->text();
-                       // Have 'other' always as first element
-                       $this->mOptions = [ $msg => 'other' ] + $this->mOptions;
-               }
-
-       }
-
-       function getInputHTML( $value ) {
-               $valInSelect = false;
-
-               if ( $value !== false ) {
-                       $value = strval( $value );
-                       $valInSelect = in_array(
-                               $value, HTMLFormField::flattenOptions( $this->getOptions() ), true
-                       );
-               }
-
-               $selected = $valInSelect ? $value : 'other';
-
-               $select = new XmlSelect( $this->mName, $this->mID, $selected );
-               $select->addOptions( $this->getOptions() );
-
-               $select->setAttribute( 'class', 'mw-htmlform-select-or-other' );
-
-               $tbAttribs = [ 'id' => $this->mID . '-other', 'size' => $this->getSize() ];
-
-               if ( !empty( $this->mParams['disabled'] ) ) {
-                       $select->setAttribute( 'disabled', 'disabled' );
-                       $tbAttribs['disabled'] = 'disabled';
-               }
-
-               if ( isset( $this->mParams['tabindex'] ) ) {
-                       $select->setAttribute( 'tabindex', $this->mParams['tabindex'] );
-                       $tbAttribs['tabindex'] = $this->mParams['tabindex'];
-               }
-
-               $select = $select->getHTML();
-
-               if ( isset( $this->mParams['maxlength'] ) ) {
-                       $tbAttribs['maxlength'] = $this->mParams['maxlength'];
-               }
-
-               if ( $this->mClass !== '' ) {
-                       $tbAttribs['class'] = $this->mClass;
-               }
-
-               $textbox = Html::input( $this->mName . '-other', $valInSelect ? '' : $value, 'text', $tbAttribs );
-
-               return "$select<br />\n$textbox";
-       }
-
-       function getInputOOUI( $value ) {
-               return false;
-       }
-
-       /**
-        * @param WebRequest $request
-        *
-        * @return string
-        */
-       function loadDataFromRequest( $request ) {
-               if ( $request->getCheck( $this->mName ) ) {
-                       $val = $request->getText( $this->mName );
-
-                       if ( $val === 'other' ) {
-                               $val = $request->getText( $this->mName . '-other' );
-                       }
-
-                       return $val;
-               } else {
-                       return $this->getDefault();
-               }
-       }
-}
diff --git a/includes/htmlform/HTMLSubmitField.php b/includes/htmlform/HTMLSubmitField.php
deleted file mode 100644 (file)
index cb98549..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-
-/**
- * Add a submit button inline in the form (as opposed to
- * HTMLForm::addButton(), which will add it at the end).
- */
-class HTMLSubmitField extends HTMLButtonField {
-       protected $buttonType = 'submit';
-
-       protected $mFlags = [ 'primary', 'constructive' ];
-
-       public function skipLoadData( $request ) {
-               return !$request->getCheck( $this->mName );
-       }
-
-       public function loadDataFromRequest( $request ) {
-               return $request->getCheck( $this->mName );
-       }
-}
diff --git a/includes/htmlform/HTMLTagFilter.php b/includes/htmlform/HTMLTagFilter.php
deleted file mode 100644 (file)
index 8075de5..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-/**
- * Wrapper for ChangeTags::buildTagFilterSelector to use in HTMLForm
- */
-class HTMLTagFilter extends HTMLFormField {
-       protected $tagFilter;
-
-       function getTableRow( $value ) {
-               $this->tagFilter = ChangeTags::buildTagFilterSelector( $value );
-               if ( $this->tagFilter ) {
-                       return parent::getTableRow( $value );
-               }
-               return '';
-       }
-
-       function getDiv( $value ) {
-               $this->tagFilter = ChangeTags::buildTagFilterSelector( $value );
-               if ( $this->tagFilter ) {
-                       return parent::getDiv( $value );
-               }
-               return '';
-       }
-
-       function getInputHTML( $value ) {
-               if ( $this->tagFilter ) {
-                       // we only need the select field, HTMLForm should handle the label
-                       return $this->tagFilter[1];
-               }
-               return '';
-       }
-}
diff --git a/includes/htmlform/HTMLTextAreaField.php b/includes/htmlform/HTMLTextAreaField.php
deleted file mode 100644 (file)
index 8ffff43..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-<?php
-
-class HTMLTextAreaField extends HTMLFormField {
-       const DEFAULT_COLS = 80;
-       const DEFAULT_ROWS = 25;
-
-       protected $mPlaceholder = '';
-
-       /**
-        * @param array $params
-        *   - cols, rows: textarea size
-        *   - placeholder/placeholder-message: set HTML placeholder attribute
-        *   - spellcheck: set HTML spellcheck attribute
-        */
-       public function __construct( $params ) {
-               parent::__construct( $params );
-
-               if ( isset( $params['placeholder-message'] ) ) {
-                       $this->mPlaceholder = $this->getMessage( $params['placeholder-message'] )->parse();
-               } elseif ( isset( $params['placeholder'] ) ) {
-                       $this->mPlaceholder = $params['placeholder'];
-               }
-       }
-
-       function getCols() {
-               return isset( $this->mParams['cols'] ) ? $this->mParams['cols'] : static::DEFAULT_COLS;
-       }
-
-       function getRows() {
-               return isset( $this->mParams['rows'] ) ? $this->mParams['rows'] : static::DEFAULT_ROWS;
-       }
-
-       function getSpellCheck() {
-               $val = isset( $this->mParams['spellcheck'] ) ? $this->mParams['spellcheck'] : null;
-               if ( is_bool( $val ) ) {
-                       // "spellcheck" attribute literally requires "true" or "false" to work.
-                       return $val === true ? 'true' : 'false';
-               }
-               return null;
-       }
-
-       function getInputHTML( $value ) {
-               $attribs = [
-                               'id' => $this->mID,
-                               'cols' => $this->getCols(),
-                               'rows' => $this->getRows(),
-                               'spellcheck' => $this->getSpellCheck(),
-                       ] + $this->getTooltipAndAccessKey();
-
-               if ( $this->mClass !== '' ) {
-                       $attribs['class'] = $this->mClass;
-               }
-               if ( $this->mPlaceholder !== '' ) {
-                       $attribs['placeholder'] = $this->mPlaceholder;
-               }
-
-               $allowedParams = [
-                       'tabindex',
-                       'disabled',
-                       'readonly',
-                       'required',
-                       'autofocus'
-               ];
-
-               $attribs += $this->getAttributes( $allowedParams );
-               return Html::textarea( $this->mName, $value, $attribs );
-       }
-
-       function getInputOOUI( $value ) {
-               if ( isset( $this->mParams['cols'] ) ) {
-                       throw new Exception( "OOUIHTMLForm does not support the 'cols' parameter for textareas" );
-               }
-
-               $attribs = $this->getTooltipAndAccessKey();
-
-               if ( $this->mClass !== '' ) {
-                       $attribs['classes'] = [ $this->mClass ];
-               }
-               if ( $this->mPlaceholder !== '' ) {
-                       $attribs['placeholder'] = $this->mPlaceholder;
-               }
-
-               $allowedParams = [
-                       'tabindex',
-                       'disabled',
-                       'readonly',
-                       'required',
-                       'autofocus',
-               ];
-
-               $attribs += OOUI\Element::configFromHtmlAttributes(
-                       $this->getAttributes( $allowedParams )
-               );
-
-               return new OOUI\TextInputWidget( [
-                       'id' => $this->mID,
-                       'name' => $this->mName,
-                       'multiline' => true,
-                       'value' => $value,
-                       'rows' => $this->getRows(),
-               ] + $attribs );
-       }
-}
diff --git a/includes/htmlform/HTMLTextField.php b/includes/htmlform/HTMLTextField.php
deleted file mode 100644 (file)
index 3ab7176..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-<?php
-
-class HTMLTextField extends HTMLFormField {
-       protected $mPlaceholder = '';
-
-       /**
-        * @param array $params
-        *   - type: HTML textfield type
-        *   - size: field size in characters (defaults to 45)
-        *   - placeholder/placeholder-message: set HTML placeholder attribute
-        *   - spellcheck: set HTML spellcheck attribute
-        *   - persistent: upon unsuccessful requests, retain the value (defaults to true, except
-        *     for password fields)
-        */
-       public function __construct( $params ) {
-               parent::__construct( $params );
-
-               if ( isset( $params['placeholder-message'] ) ) {
-                       $this->mPlaceholder = $this->getMessage( $params['placeholder-message'] )->parse();
-               } elseif ( isset( $params['placeholder'] ) ) {
-                       $this->mPlaceholder = $params['placeholder'];
-               }
-       }
-
-       function getSize() {
-               return isset( $this->mParams['size'] ) ? $this->mParams['size'] : 45;
-       }
-
-       function getSpellCheck() {
-               $val = isset( $this->mParams['spellcheck'] ) ? $this->mParams['spellcheck'] : null;
-               if ( is_bool( $val ) ) {
-                       // "spellcheck" attribute literally requires "true" or "false" to work.
-                       return $val === true ? 'true' : 'false';
-               }
-               return null;
-       }
-
-       public function isPersistent() {
-               if ( isset( $this->mParams['persistent'] ) ) {
-                       return $this->mParams['persistent'];
-               }
-               // don't put passwords into the HTML body, they could get cached or otherwise leaked
-               return !( isset( $this->mParams['type'] ) && $this->mParams['type'] === 'password' );
-       }
-
-       function getInputHTML( $value ) {
-               if ( !$this->isPersistent() ) {
-                       $value = '';
-               }
-
-               $attribs = [
-                               'id' => $this->mID,
-                               'name' => $this->mName,
-                               'size' => $this->getSize(),
-                               'value' => $value,
-                               'dir' => $this->mDir,
-                               'spellcheck' => $this->getSpellCheck(),
-                       ] + $this->getTooltipAndAccessKey() + $this->getDataAttribs();
-
-               if ( $this->mClass !== '' ) {
-                       $attribs['class'] = $this->mClass;
-               }
-               if ( $this->mPlaceholder !== '' ) {
-                       $attribs['placeholder'] = $this->mPlaceholder;
-               }
-
-               # @todo Enforce pattern, step, required, readonly on the server side as
-               # well
-               $allowedParams = [
-                       'type',
-                       'min',
-                       'max',
-                       'pattern',
-                       'title',
-                       'step',
-                       'list',
-                       'maxlength',
-                       'tabindex',
-                       'disabled',
-                       'required',
-                       'autofocus',
-                       'multiple',
-                       'readonly'
-               ];
-
-               $attribs += $this->getAttributes( $allowedParams );
-
-               # Extract 'type'
-               $type = $this->getType( $attribs );
-               return Html::input( $this->mName, $value, $type, $attribs );
-       }
-
-       protected function getType( &$attribs ) {
-               $type = isset( $attribs['type'] ) ? $attribs['type'] : 'text';
-               unset( $attribs['type'] );
-
-               # Implement tiny differences between some field variants
-               # here, rather than creating a new class for each one which
-               # is essentially just a clone of this one.
-               if ( isset( $this->mParams['type'] ) ) {
-                       switch ( $this->mParams['type'] ) {
-                               case 'int':
-                                       $type = 'number';
-                                       break;
-                               case 'float':
-                                       $type = 'number';
-                                       $attribs['step'] = 'any';
-                                       break;
-                               # Pass through
-                               case 'email':
-                               case 'password':
-                               case 'file':
-                               case 'url':
-                                       $type = $this->mParams['type'];
-                                       break;
-                       }
-               }
-
-               return $type;
-       }
-
-       function getInputOOUI( $value ) {
-               if ( !$this->isPersistent() ) {
-                       $value = '';
-               }
-
-               $attribs = $this->getTooltipAndAccessKey();
-
-               if ( $this->mClass !== '' ) {
-                       $attribs['classes'] = [ $this->mClass ];
-               }
-               if ( $this->mPlaceholder !== '' ) {
-                       $attribs['placeholder'] = $this->mPlaceholder;
-               }
-
-               # @todo Enforce pattern, step, required, readonly on the server side as
-               # well
-               $allowedParams = [
-                       'autofocus',
-                       'autosize',
-                       'disabled',
-                       'flags',
-                       'indicator',
-                       'maxlength',
-                       'readonly',
-                       'required',
-                       'tabindex',
-                       'type',
-               ];
-
-               $attribs += OOUI\Element::configFromHtmlAttributes(
-                       $this->getAttributes( $allowedParams )
-               );
-
-               $type = $this->getType( $attribs );
-
-               return $this->getInputWidget( [
-                       'id' => $this->mID,
-                       'name' => $this->mName,
-                       'value' => $value,
-                       'type' => $type,
-               ] + $attribs );
-       }
-
-       protected function getInputWidget( $params ) {
-               return new OOUI\TextInputWidget( $params );
-       }
-
-       /**
-        * Returns an array of data-* attributes to add to the field.
-        *
-        * @return array
-        */
-       protected function getDataAttribs() {
-               return [];
-       }
-}
diff --git a/includes/htmlform/HTMLTextFieldWithButton.php b/includes/htmlform/HTMLTextFieldWithButton.php
deleted file mode 100644 (file)
index 7c1c673..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-/**
- * Creates a text input field with a button assigned to the input field.
- */
-class HTMLTextFieldWithButton extends HTMLTextField {
-       /** @var HTMLFormFieldWithButton $mClassWithButton */
-       protected $mClassWithButton = null;
-
-       public function __construct( $info ) {
-               $this->mClassWithButton = new HTMLFormFieldWithButton( $info );
-               parent::__construct( $info );
-       }
-
-       public function getInputHTML( $value ) {
-               return $this->mClassWithButton->getElement( parent::getInputHTML( $value ) );
-       }
-}
diff --git a/includes/htmlform/HTMLTitleTextField.php b/includes/htmlform/HTMLTitleTextField.php
deleted file mode 100644 (file)
index fcf721a..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-<?php
-
-use MediaWiki\Widget\TitleInputWidget;
-
-/**
- * Implements a text input field for page titles.
- * Automatically does validation that the title is valid,
- * as well as autocompletion if using the OOUI display format.
- *
- * Note: Forms using GET requests will need to make sure the title value is not
- * an empty string.
- *
- * Optional parameters:
- * 'namespace' - Namespace the page must be in
- * 'relative' - If true and 'namespace' given, strip/add the namespace from/to the title as needed
- * 'creatable' - Whether to validate the title is creatable (not a special page)
- * 'exists' - Whether to validate that the title already exists
- *
- * @since 1.26
- */
-class HTMLTitleTextField extends HTMLTextField {
-       public function __construct( $params ) {
-               $params += [
-                       'namespace' => false,
-                       'relative' => false,
-                       'creatable' => false,
-                       'exists' => false,
-               ];
-
-               parent::__construct( $params );
-       }
-
-       public function validate( $value, $alldata ) {
-               if ( $this->mParent->getMethod() === 'get' && $value === '' ) {
-                       // If the form is a GET form and has no value, assume it hasn't been
-                       // submitted yet, and skip validation
-                       return parent::validate( $value, $alldata );
-               }
-               try {
-                       if ( !$this->mParams['relative'] ) {
-                               $title = Title::newFromTextThrow( $value );
-                       } else {
-                               // Can't use Title::makeTitleSafe(), because it doesn't throw useful exceptions
-                               global $wgContLang;
-                               $namespaceName = $wgContLang->getNsText( $this->mParams['namespace'] );
-                               $title = Title::newFromTextThrow( $namespaceName . ':' . $value );
-                       }
-               } catch ( MalformedTitleException $e ) {
-                       $msg = $this->msg( $e->getErrorMessage() );
-                       $params = $e->getErrorMessageParameters();
-                       if ( $params ) {
-                               $msg->params( $params );
-                       }
-                       return $msg->parse();
-               }
-
-               $text = $title->getPrefixedText();
-               if ( $this->mParams['namespace'] !== false &&
-                       !$title->inNamespace( $this->mParams['namespace'] )
-               ) {
-                       return $this->msg( 'htmlform-title-badnamespace', $this->mParams['namespace'], $text )->parse();
-               }
-
-               if ( $this->mParams['creatable'] && !$title->canExist() ) {
-                       return $this->msg( 'htmlform-title-not-creatable', $text )->escaped();
-               }
-
-               if ( $this->mParams['exists'] && !$title->exists() ) {
-                       return $this->msg( 'htmlform-title-not-exists', $text )->parse();
-               }
-
-               return parent::validate( $value, $alldata );
-       }
-
-       protected function getInputWidget( $params ) {
-               $this->mParent->getOutput()->addModules( 'mediawiki.widgets' );
-               if ( $this->mParams['namespace'] !== false ) {
-                       $params['namespace'] = $this->mParams['namespace'];
-               }
-               $params['relative'] = $this->mParams['relative'];
-               return new TitleInputWidget( $params );
-       }
-
-       public function getInputHtml( $value ) {
-               // add mw-searchInput class to enable search suggestions for non-OOUI, too
-               $this->mClass .= 'mw-searchInput';
-
-               // return the HTMLTextField html
-               return parent::getInputHTML( $value );
-       }
-
-       protected function getDataAttribs() {
-               return [
-                       'data-mw-searchsuggest' => FormatJson::encode( [
-                               'wrapAsLink' => false,
-                       ] ),
-               ];
-       }
-}
diff --git a/includes/htmlform/HTMLUserTextField.php b/includes/htmlform/HTMLUserTextField.php
deleted file mode 100644 (file)
index 5a7e0b9..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-use MediaWiki\Widget\UserInputWidget;
-
-/**
- * Implements a text input field for user names.
- * Automatically auto-completes if using the OOUI display format.
- *
- * FIXME: Does not work for forms that support GET requests.
- *
- * Optional parameters:
- * 'exists' - Whether to validate that the user already exists
- *
- * @since 1.26
- */
-class HTMLUserTextField extends HTMLTextField {
-       public function __construct( $params ) {
-               $params += [
-                       'exists' => false,
-                       'ipallowed' => false,
-               ];
-
-               parent::__construct( $params );
-       }
-
-       public function validate( $value, $alldata ) {
-               // check, if a user exists with the given username
-               $user = User::newFromName( $value, false );
-
-               if ( !$user ) {
-                       return $this->msg( 'htmlform-user-not-valid', $value )->parse();
-               } elseif (
-                       ( $this->mParams['exists'] && $user->getId() === 0 ) &&
-                       !( $this->mParams['ipallowed'] && User::isIP( $value ) )
-               ) {
-                       return $this->msg( 'htmlform-user-not-exists', $user->getName() )->parse();
-               }
-
-               return parent::validate( $value, $alldata );
-       }
-
-       protected function getInputWidget( $params ) {
-               $this->mParent->getOutput()->addModules( 'mediawiki.widgets.UserInputWidget' );
-
-               return new UserInputWidget( $params );
-       }
-
-       public function getInputHtml( $value ) {
-               // add the required module and css class for user suggestions in non-OOUI mode
-               $this->mParent->getOutput()->addModules( 'mediawiki.userSuggest' );
-               $this->mClass .= ' mw-autocomplete-user';
-
-               // return parent html
-               return parent::getInputHTML( $value );
-       }
-}
diff --git a/includes/htmlform/fields/HTMLApiField.php b/includes/htmlform/fields/HTMLApiField.php
new file mode 100644 (file)
index 0000000..24a253e
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+class HTMLApiField extends HTMLFormField {
+       public function getTableRow( $value ) {
+               return '';
+       }
+
+       public function getDiv( $value ) {
+               return $this->getTableRow( $value );
+       }
+
+       public function getRaw( $value ) {
+               return $this->getTableRow( $value );
+       }
+
+       public function getInputHTML( $value ) {
+               return '';
+       }
+
+       public function hasVisibleOutput() {
+               return false;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLAutoCompleteSelectField.php b/includes/htmlform/fields/HTMLAutoCompleteSelectField.php
new file mode 100644 (file)
index 0000000..76a88d5
--- /dev/null
@@ -0,0 +1,177 @@
+<?php
+
+/**
+ * Text field for selecting a value from a large list of possible values, with
+ * auto-completion and optionally with a select dropdown for selecting common
+ * options.
+ *
+ * HTMLComboboxField implements most of the same functionality and should be
+ * used instead, if possible.
+ *
+ * If one of 'options-messages', 'options', or 'options-message' is provided
+ * and non-empty, the select dropdown will be shown. An 'other' key will be
+ * appended using message 'htmlform-selectorother-other' if not already
+ * present.
+ *
+ * Besides the parameters recognized by HTMLTextField, the following are
+ * recognized:
+ *   options-messages - As for HTMLSelectField
+ *   options - As for HTMLSelectField
+ *   options-message - As for HTMLSelectField
+ *   autocomplete - Associative array mapping display text to values.
+ *   autocomplete-messages - Like autocomplete, but keys are message names.
+ *   require-match - Boolean, if true the value must be in the options or the
+ *     autocomplete.
+ *   other-message - Message to use instead of htmlform-selectorother-other for
+ *      the 'other' message.
+ *   other - Raw text to use for the 'other' message
+ */
+class HTMLAutoCompleteSelectField extends HTMLTextField {
+       protected $autocomplete = [];
+
+       function __construct( $params ) {
+               $params += [
+                       'require-match' => false,
+               ];
+
+               parent::__construct( $params );
+
+               if ( array_key_exists( 'autocomplete-messages', $this->mParams ) ) {
+                       foreach ( $this->mParams['autocomplete-messages'] as $key => $value ) {
+                               $key = $this->msg( $key )->plain();
+                               $this->autocomplete[$key] = strval( $value );
+                       }
+               } elseif ( array_key_exists( 'autocomplete', $this->mParams ) ) {
+                       foreach ( $this->mParams['autocomplete'] as $key => $value ) {
+                               $this->autocomplete[$key] = strval( $value );
+                       }
+               }
+               if ( !is_array( $this->autocomplete ) || !$this->autocomplete ) {
+                       throw new MWException( 'HTMLAutoCompleteSelectField called without any autocompletions' );
+               }
+
+               $this->getOptions();
+               if ( $this->mOptions && !in_array( 'other', $this->mOptions, true ) ) {
+                       if ( isset( $params['other-message'] ) ) {
+                               $msg = $this->getMessage( $params['other-message'] )->text();
+                       } elseif ( isset( $params['other'] ) ) {
+                               $msg = $params['other'];
+                       } else {
+                               $msg = wfMessage( 'htmlform-selectorother-other' )->text();
+                       }
+                       $this->mOptions[$msg] = 'other';
+               }
+       }
+
+       function loadDataFromRequest( $request ) {
+               if ( $request->getCheck( $this->mName ) ) {
+                       $val = $request->getText( $this->mName . '-select', 'other' );
+
+                       if ( $val === 'other' ) {
+                               $val = $request->getText( $this->mName );
+                               if ( isset( $this->autocomplete[$val] ) ) {
+                                       $val = $this->autocomplete[$val];
+                               }
+                       }
+
+                       return $val;
+               } else {
+                       return $this->getDefault();
+               }
+       }
+
+       function validate( $value, $alldata ) {
+               $p = parent::validate( $value, $alldata );
+
+               if ( $p !== true ) {
+                       return $p;
+               }
+
+               $validOptions = HTMLFormField::flattenOptions( $this->getOptions() );
+
+               if ( in_array( strval( $value ), $validOptions, true ) ) {
+                       return true;
+               } elseif ( in_array( strval( $value ), $this->autocomplete, true ) ) {
+                       return true;
+               } elseif ( $this->mParams['require-match'] ) {
+                       return $this->msg( 'htmlform-select-badoption' )->parse();
+               }
+
+               return true;
+       }
+
+       // FIXME Ewww, this shouldn't be adding any attributes not requested in $list :(
+       public function getAttributes( array $list ) {
+               $attribs = [
+                       'type' => 'text',
+                       'data-autocomplete' => FormatJson::encode( array_keys( $this->autocomplete ) ),
+               ] + parent::getAttributes( $list );
+
+               if ( $this->getOptions() ) {
+                       $attribs['data-hide-if'] = FormatJson::encode(
+                               [ '!==', $this->mName . '-select', 'other' ]
+                       );
+               }
+
+               return $attribs;
+       }
+
+       function getInputHTML( $value ) {
+               $oldClass = $this->mClass;
+               $this->mClass = (array)$this->mClass;
+
+               $valInSelect = false;
+               $ret = '';
+
+               if ( $this->getOptions() ) {
+                       if ( $value !== false ) {
+                               $value = strval( $value );
+                               $valInSelect = in_array(
+                                       $value, HTMLFormField::flattenOptions( $this->getOptions() ), true
+                               );
+                       }
+
+                       $selected = $valInSelect ? $value : 'other';
+                       $select = new XmlSelect( $this->mName . '-select', $this->mID . '-select', $selected );
+                       $select->addOptions( $this->getOptions() );
+                       $select->setAttribute( 'class', 'mw-htmlform-select-or-other' );
+
+                       if ( !empty( $this->mParams['disabled'] ) ) {
+                               $select->setAttribute( 'disabled', 'disabled' );
+                       }
+
+                       if ( isset( $this->mParams['tabindex'] ) ) {
+                               $select->setAttribute( 'tabindex', $this->mParams['tabindex'] );
+                       }
+
+                       $ret = $select->getHTML() . "<br />\n";
+
+                       $this->mClass[] = 'mw-htmlform-hide-if';
+               }
+
+               if ( $valInSelect ) {
+                       $value = '';
+               } else {
+                       $key = array_search( strval( $value ), $this->autocomplete, true );
+                       if ( $key !== false ) {
+                               $value = $key;
+                       }
+               }
+
+               $this->mClass[] = 'mw-htmlform-autocomplete';
+               $ret .= parent::getInputHTML( $valInSelect ? '' : $value );
+               $this->mClass = $oldClass;
+
+               return $ret;
+       }
+
+       /**
+        * Get the OOUI version of this input.
+        * @param string $value
+        * @return false
+        */
+       function getInputOOUI( $value ) {
+               // To be implemented, for now override the function from HTMLTextField
+               return false;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLButtonField.php b/includes/htmlform/fields/HTMLButtonField.php
new file mode 100644 (file)
index 0000000..64fe7ed
--- /dev/null
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * Adds a generic button inline to the form. Does not do anything, you must add
+ * click handling code in JavaScript. Use a HTMLSubmitField if you merely
+ * wish to add a submit button to a form.
+ *
+ * Additional recognized configuration parameters include:
+ * - flags: OOUI flags for the button, see OOUI\FlaggedElement
+ * - buttonlabel-message: Message to use for the button display text, instead
+ *   of the value from 'default'. Overrides 'buttonlabel' and 'buttonlabel-raw'.
+ * - buttonlabel: Text to display for the button display text, instead
+ *   of the value from 'default'. Overrides 'buttonlabel-raw'.
+ * - buttonlabel-raw: HTMLto display for the button display text, instead
+ *   of the value from 'default'.
+ *
+ * Note that the buttonlabel parameters are not supported on IE6 and IE7 due to
+ * bugs in those browsers. If detected, they will be served buttons using the
+ * value of 'default' as the button label.
+ *
+ * @since 1.22
+ */
+class HTMLButtonField extends HTMLFormField {
+       protected $buttonType = 'button';
+       protected $buttonLabel = null;
+
+       /** @var array $mFlags Flags to add to OOUI Button widget */
+       protected $mFlags = [];
+
+       public function __construct( $info ) {
+               $info['nodata'] = true;
+               if ( isset( $info['flags'] ) ) {
+                       $this->mFlags = $info['flags'];
+               }
+
+               # Generate the label from a message, if possible
+               if ( isset( $info['buttonlabel-message'] ) ) {
+                       $this->buttonLabel = $this->getMessage( $info['buttonlabel-message'] )->parse();
+               } elseif ( isset( $info['buttonlabel'] ) ) {
+                       if ( $info['buttonlabel'] === '&#160;' ) {
+                               // Apparently some things set &nbsp directly and in an odd format
+                               $this->buttonLabel = '&#160;';
+                       } else {
+                               $this->buttonLabel = htmlspecialchars( $info['buttonlabel'] );
+                       }
+               } elseif ( isset( $info['buttonlabel-raw'] ) ) {
+                       $this->buttonLabel = $info['buttonlabel-raw'];
+               }
+
+               $this->setShowEmptyLabel( false );
+
+               parent::__construct( $info );
+       }
+
+       public function getInputHTML( $value ) {
+               $flags = '';
+               $prefix = 'mw-htmlform-';
+               if ( $this->mParent instanceof VFormHTMLForm ||
+                       $this->mParent->getConfig()->get( 'UseMediaWikiUIEverywhere' )
+               ) {
+                       $prefix = 'mw-ui-';
+                       // add mw-ui-button separately, so the descriptor doesn't need to set it
+                       $flags .= ' ' . $prefix . 'button';
+               }
+               foreach ( $this->mFlags as $flag ) {
+                       $flags .= ' ' . $prefix . $flag;
+               }
+               $attr = [
+                       'class' => 'mw-htmlform-submit ' . $this->mClass . $flags,
+                       'id' => $this->mID,
+                       'type' => $this->buttonType,
+                       'name' => $this->mName,
+                       'value' => $this->getDefault(),
+               ] + $this->getAttributes( [ 'disabled', 'tabindex' ] );
+
+               if ( $this->isBadIE() ) {
+                       return Html::element( 'input', $attr );
+               } else {
+                       return Html::rawElement( 'button', $attr,
+                               $this->buttonLabel ?: htmlspecialchars( $this->getDefault() ) );
+               }
+       }
+
+       /**
+        * Get the OOUI widget for this field.
+        * @param string $value
+        * @return OOUI\ButtonInputWidget
+        */
+       public function getInputOOUI( $value ) {
+               return new OOUI\ButtonInputWidget( [
+                       'name' => $this->mName,
+                       'value' => $this->getDefault(),
+                       'label' => !$this->isBadIE() && $this->buttonLabel
+                               ? new OOUI\HtmlSnippet( $this->buttonLabel )
+                               : $this->getDefault(),
+                       'type' => $this->buttonType,
+                       'classes' => [ 'mw-htmlform-submit', $this->mClass ],
+                       'id' => $this->mID,
+                       'flags' => $this->mFlags,
+                       'useInputTag' => $this->isBadIE(),
+               ] + OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( [ 'disabled', 'tabindex' ] )
+               ) );
+       }
+
+       protected function needsLabel() {
+               return false;
+       }
+
+       /**
+        * Button cannot be invalid
+        *
+        * @param string $value
+        * @param array $alldata
+        *
+        * @return bool
+        */
+       public function validate( $value, $alldata ) {
+               return true;
+       }
+
+       /**
+        * IE<8 has bugs with <button>, so we'll need to avoid them.
+        * @return bool Whether the request is from a bad version of IE
+        */
+       private function isBadIE() {
+               $request = $this->mParent
+                       ? $this->mParent->getRequest()
+                       : RequestContext::getMain()->getRequest();
+               return preg_match( '/MSIE [1-7]\./i', $request->getHeader( 'User-Agent' ) );
+       }
+}
diff --git a/includes/htmlform/fields/HTMLCheckField.php b/includes/htmlform/fields/HTMLCheckField.php
new file mode 100644 (file)
index 0000000..a553839
--- /dev/null
@@ -0,0 +1,131 @@
+<?php
+
+/**
+ * A checkbox field
+ */
+class HTMLCheckField extends HTMLFormField {
+       function getInputHTML( $value ) {
+               global $wgUseMediaWikiUIEverywhere;
+
+               if ( !empty( $this->mParams['invert'] ) ) {
+                       $value = !$value;
+               }
+
+               $attr = $this->getTooltipAndAccessKey();
+               $attr['id'] = $this->mID;
+
+               $attr += $this->getAttributes( [ 'disabled', 'tabindex' ] );
+
+               if ( $this->mClass !== '' ) {
+                       $attr['class'] = $this->mClass;
+               }
+
+               $attrLabel = [ 'for' => $this->mID ];
+               if ( isset( $attr['title'] ) ) {
+                       // propagate tooltip to label
+                       $attrLabel['title'] = $attr['title'];
+               }
+
+               $chkLabel = Xml::check( $this->mName, $value, $attr ) .
+                       '&#160;' .
+                       Html::rawElement( 'label', $attrLabel, $this->mLabel );
+
+               if ( $wgUseMediaWikiUIEverywhere || $this->mParent instanceof VFormHTMLForm ) {
+                       $chkLabel = Html::rawElement(
+                               'div',
+                               [ 'class' => 'mw-ui-checkbox' ],
+                               $chkLabel
+                       );
+               }
+
+               return $chkLabel;
+       }
+
+       /**
+        * Get the OOUI version of this field.
+        * @since 1.26
+        * @param string $value
+        * @return OOUI\CheckboxInputWidget The checkbox widget.
+        */
+       public function getInputOOUI( $value ) {
+               if ( !empty( $this->mParams['invert'] ) ) {
+                       $value = !$value;
+               }
+
+               $attr = $this->getTooltipAndAccessKey();
+               $attr['id'] = $this->mID;
+               $attr['name'] = $this->mName;
+
+               $attr += OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( [ 'disabled', 'tabindex' ] )
+               );
+
+               if ( $this->mClass !== '' ) {
+                       $attr['classes'] = [ $this->mClass ];
+               }
+
+               $attr['selected'] = $value;
+               $attr['value'] = '1'; // Nasty hack, but needed to make this work
+
+               return new OOUI\CheckboxInputWidget( $attr );
+       }
+
+       /**
+        * For a checkbox, the label goes on the right hand side, and is
+        * added in getInputHTML(), rather than HTMLFormField::getRow()
+        *
+        * ...unless OOUI is being used, in which case we actually return
+        * the label here.
+        *
+        * @return string
+        */
+       function getLabel() {
+               if ( $this->mParent instanceof OOUIHTMLForm ) {
+                       return $this->mLabel;
+               } elseif (
+                       $this->mParent instanceof HTMLForm &&
+                       $this->mParent->getDisplayFormat() === 'div'
+               ) {
+                       return '';
+               } else {
+                       return '&#160;';
+               }
+       }
+
+       /**
+        * Get label alignment when generating field for OOUI.
+        * @return string 'left', 'right', 'top' or 'inline'
+        */
+       protected function getLabelAlignOOUI() {
+               return 'inline';
+       }
+
+       /**
+        * checkboxes don't need a label.
+        * @return bool
+        */
+       protected function needsLabel() {
+               return false;
+       }
+
+       /**
+        * @param WebRequest $request
+        *
+        * @return bool
+        */
+       function loadDataFromRequest( $request ) {
+               $invert = isset( $this->mParams['invert'] ) && $this->mParams['invert'];
+
+               // GetCheck won't work like we want for checks.
+               // Fetch the value in either one of the two following case:
+               // - we have a valid submit attempt (form was just submitted, or a GET URL forged by the user)
+               // - checkbox name has a value (false or true), ie is not null
+               if ( $this->isSubmitAttempt( $request ) || $request->getVal( $this->mName ) !== null ) {
+                       return $invert
+                               ? !$request->getBool( $this->mName )
+                               : $request->getBool( $this->mName );
+               } else {
+                       return (bool)$this->getDefault();
+               }
+       }
+}
diff --git a/includes/htmlform/fields/HTMLCheckMatrix.php b/includes/htmlform/fields/HTMLCheckMatrix.php
new file mode 100644 (file)
index 0000000..b324fb6
--- /dev/null
@@ -0,0 +1,266 @@
+<?php
+
+/**
+ * A checkbox matrix
+ * Operates similarly to HTMLMultiSelectField, but instead of using an array of
+ * options, uses an array of rows and an array of columns to dynamically
+ * construct a matrix of options. The tags used to identify a particular cell
+ * are of the form "columnName-rowName"
+ *
+ * Options:
+ *   - columns
+ *     - Required list of columns in the matrix.
+ *   - rows
+ *     - Required list of rows in the matrix.
+ *   - force-options-on
+ *     - Accepts array of column-row tags to be displayed as enabled but unavailable to change
+ *   - force-options-off
+ *     - Accepts array of column-row tags to be displayed as disabled but unavailable to change.
+ *   - tooltips
+ *     - Optional array mapping row label to tooltip content
+ *   - tooltip-class
+ *     - Optional CSS class used on tooltip container span. Defaults to mw-icon-question.
+ */
+class HTMLCheckMatrix extends HTMLFormField implements HTMLNestedFilterable {
+       static private $requiredParams = [
+               // Required by underlying HTMLFormField
+               'fieldname',
+               // Required by HTMLCheckMatrix
+               'rows',
+               'columns'
+       ];
+
+       public function __construct( $params ) {
+               $missing = array_diff( self::$requiredParams, array_keys( $params ) );
+               if ( $missing ) {
+                       throw new HTMLFormFieldRequiredOptionsException( $this, $missing );
+               }
+               parent::__construct( $params );
+       }
+
+       function validate( $value, $alldata ) {
+               $rows = $this->mParams['rows'];
+               $columns = $this->mParams['columns'];
+
+               // Make sure user-defined validation callback is run
+               $p = parent::validate( $value, $alldata );
+               if ( $p !== true ) {
+                       return $p;
+               }
+
+               // Make sure submitted value is an array
+               if ( !is_array( $value ) ) {
+                       return false;
+               }
+
+               // If all options are valid, array_intersect of the valid options
+               // and the provided options will return the provided options.
+               $validOptions = [];
+               foreach ( $rows as $rowTag ) {
+                       foreach ( $columns as $columnTag ) {
+                               $validOptions[] = $columnTag . '-' . $rowTag;
+                       }
+               }
+               $validValues = array_intersect( $value, $validOptions );
+               if ( count( $validValues ) == count( $value ) ) {
+                       return true;
+               } else {
+                       return $this->msg( 'htmlform-select-badoption' )->parse();
+               }
+       }
+
+       /**
+        * Build a table containing a matrix of checkbox options.
+        * The value of each option is a combination of the row tag and column tag.
+        * mParams['rows'] is an array with row labels as keys and row tags as values.
+        * mParams['columns'] is an array with column labels as keys and column tags as values.
+        *
+        * @param array $value Array of the options that should be checked
+        *
+        * @return string
+        */
+       function getInputHTML( $value ) {
+               $html = '';
+               $tableContents = '';
+               $rows = $this->mParams['rows'];
+               $columns = $this->mParams['columns'];
+
+               $attribs = $this->getAttributes( [ 'disabled', 'tabindex' ] );
+
+               // Build the column headers
+               $headerContents = Html::rawElement( 'td', [], '&#160;' );
+               foreach ( $columns as $columnLabel => $columnTag ) {
+                       $headerContents .= Html::rawElement( 'td', [], $columnLabel );
+               }
+               $tableContents .= Html::rawElement( 'tr', [], "\n$headerContents\n" );
+
+               $tooltipClass = 'mw-icon-question';
+               if ( isset( $this->mParams['tooltip-class'] ) ) {
+                       $tooltipClass = $this->mParams['tooltip-class'];
+               }
+
+               // Build the options matrix
+               foreach ( $rows as $rowLabel => $rowTag ) {
+                       // Append tooltip if configured
+                       if ( isset( $this->mParams['tooltips'][$rowLabel] ) ) {
+                               $tooltipAttribs = [
+                                       'class' => "mw-htmlform-tooltip $tooltipClass",
+                                       'title' => $this->mParams['tooltips'][$rowLabel],
+                               ];
+                               $rowLabel .= ' ' . Html::element( 'span', $tooltipAttribs, '' );
+                       }
+                       $rowContents = Html::rawElement( 'td', [], $rowLabel );
+                       foreach ( $columns as $columnTag ) {
+                               $thisTag = "$columnTag-$rowTag";
+                               // Construct the checkbox
+                               $thisAttribs = [
+                                       'id' => "{$this->mID}-$thisTag",
+                                       'value' => $thisTag,
+                               ];
+                               $checked = in_array( $thisTag, (array)$value, true );
+                               if ( $this->isTagForcedOff( $thisTag ) ) {
+                                       $checked = false;
+                                       $thisAttribs['disabled'] = 1;
+                               } elseif ( $this->isTagForcedOn( $thisTag ) ) {
+                                       $checked = true;
+                                       $thisAttribs['disabled'] = 1;
+                               }
+
+                               $checkbox = $this->getOneCheckbox( $checked, $attribs + $thisAttribs );
+
+                               $rowContents .= Html::rawElement(
+                                       'td',
+                                       [],
+                                       $checkbox
+                               );
+                       }
+                       $tableContents .= Html::rawElement( 'tr', [], "\n$rowContents\n" );
+               }
+
+               // Put it all in a table
+               $html .= Html::rawElement( 'table',
+                               [ 'class' => 'mw-htmlform-matrix' ],
+                               Html::rawElement( 'tbody', [], "\n$tableContents\n" ) ) . "\n";
+
+               return $html;
+       }
+
+       protected function getOneCheckbox( $checked, $attribs ) {
+               if ( $this->mParent instanceof OOUIHTMLForm ) {
+                       return new OOUI\CheckboxInputWidget( [
+                               'name' => "{$this->mName}[]",
+                               'selected' => $checked,
+                       ] + OOUI\Element::configFromHtmlAttributes(
+                               $attribs
+                       ) );
+               } else {
+                       $checkbox = Xml::check( "{$this->mName}[]", $checked, $attribs );
+                       if ( $this->mParent->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) {
+                               $checkbox = Html::openElement( 'div', [ 'class' => 'mw-ui-checkbox' ] ) .
+                                       $checkbox .
+                                       Html::element( 'label', [ 'for' => $attribs['id'] ] ) .
+                                       Html::closeElement( 'div' );
+                       }
+                       return $checkbox;
+               }
+       }
+
+       protected function isTagForcedOff( $tag ) {
+               return isset( $this->mParams['force-options-off'] )
+                       && in_array( $tag, $this->mParams['force-options-off'] );
+       }
+
+       protected function isTagForcedOn( $tag ) {
+               return isset( $this->mParams['force-options-on'] )
+                       && in_array( $tag, $this->mParams['force-options-on'] );
+       }
+
+       /**
+        * Get the complete table row for the input, including help text,
+        * labels, and whatever.
+        * We override this function since the label should always be on a separate
+        * line above the options in the case of a checkbox matrix, i.e. it's always
+        * a "vertical-label".
+        *
+        * @param string $value The value to set the input to
+        *
+        * @return string Complete HTML table row
+        */
+       function getTableRow( $value ) {
+               list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value );
+               $inputHtml = $this->getInputHTML( $value );
+               $fieldType = get_class( $this );
+               $helptext = $this->getHelpTextHtmlTable( $this->getHelpText() );
+               $cellAttributes = [ 'colspan' => 2 ];
+
+               $hideClass = '';
+               $hideAttributes = [];
+               if ( $this->mHideIf ) {
+                       $hideAttributes['data-hide-if'] = FormatJson::encode( $this->mHideIf );
+                       $hideClass = 'mw-htmlform-hide-if';
+               }
+
+               $label = $this->getLabelHtml( $cellAttributes );
+
+               $field = Html::rawElement(
+                       'td',
+                       [ 'class' => 'mw-input' ] + $cellAttributes,
+                       $inputHtml . "\n$errors"
+               );
+
+               $html = Html::rawElement( 'tr',
+                       [ 'class' => "mw-htmlform-vertical-label $hideClass" ] + $hideAttributes,
+                       $label );
+               $html .= Html::rawElement( 'tr',
+                       [ 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass $hideClass" ] +
+                               $hideAttributes,
+                       $field );
+
+               return $html . $helptext;
+       }
+
+       /**
+        * @param WebRequest $request
+        *
+        * @return array
+        */
+       function loadDataFromRequest( $request ) {
+               if ( $this->isSubmitAttempt( $request ) ) {
+                       // Checkboxes are just not added to the request arrays if they're not checked,
+                       // so it's perfectly possible for there not to be an entry at all
+                       return $request->getArray( $this->mName, [] );
+               } else {
+                       // That's ok, the user has not yet submitted the form, so show the defaults
+                       return $this->getDefault();
+               }
+       }
+
+       function getDefault() {
+               if ( isset( $this->mDefault ) ) {
+                       return $this->mDefault;
+               } else {
+                       return [];
+               }
+       }
+
+       function filterDataForSubmit( $data ) {
+               $columns = HTMLFormField::flattenOptions( $this->mParams['columns'] );
+               $rows = HTMLFormField::flattenOptions( $this->mParams['rows'] );
+               $res = [];
+               foreach ( $columns as $column ) {
+                       foreach ( $rows as $row ) {
+                               // Make sure option hasn't been forced
+                               $thisTag = "$column-$row";
+                               if ( $this->isTagForcedOff( $thisTag ) ) {
+                                       $res[$thisTag] = false;
+                               } elseif ( $this->isTagForcedOn( $thisTag ) ) {
+                                       $res[$thisTag] = true;
+                               } else {
+                                       $res[$thisTag] = in_array( $thisTag, $data );
+                               }
+                       }
+               }
+
+               return $res;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLComboboxField.php b/includes/htmlform/fields/HTMLComboboxField.php
new file mode 100644 (file)
index 0000000..0c3bc5a
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * A combo box field.
+ *
+ * You can think of it as a dropdown select with the ability to add custom options,
+ * or as a text field with input suggestions (autocompletion).
+ *
+ * When JavaScript is not supported or enabled, it uses HTML5 `<datalist>` element.
+ *
+ * Besides the parameters recognized by HTMLTextField, the following are
+ * recognized:
+ *   options-messages - As for HTMLSelectField
+ *   options - As for HTMLSelectField
+ *   options-message - As for HTMLSelectField
+ */
+class HTMLComboboxField extends HTMLTextField {
+       // FIXME Ewww, this shouldn't be adding any attributes not requested in $list :(
+       public function getAttributes( array $list ) {
+               $attribs = [
+                       'type' => 'text',
+                       'list' => $this->mName . '-datalist',
+               ] + parent::getAttributes( $list );
+
+               return $attribs;
+       }
+
+       function getInputHTML( $value ) {
+               $datalist = new XmlSelect( false, $this->mName . '-datalist' );
+               $datalist->setTagName( 'datalist' );
+               $datalist->addOptions( $this->getOptions() );
+
+               return parent::getInputHTML( $value ) . $datalist->getHTML();
+       }
+
+       function getInputOOUI( $value ) {
+               $disabled = false;
+               $allowedParams = [ 'tabindex' ];
+               $attribs = OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( $allowedParams )
+               );
+
+               if ( $this->mClass !== '' ) {
+                       $attribs['classes'] = [ $this->mClass ];
+               }
+
+               if ( !empty( $this->mParams['disabled'] ) ) {
+                       $disabled = true;
+               }
+
+               return new OOUI\ComboBoxInputWidget( [
+                       'name' => $this->mName,
+                       'id' => $this->mID,
+                       'options' => $this->getOptionsOOUI(),
+                       'value' => strval( $value ),
+                       'disabled' => $disabled,
+               ] + $attribs );
+       }
+
+       protected function shouldInfuseOOUI() {
+               return true;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLEditTools.php b/includes/htmlform/fields/HTMLEditTools.php
new file mode 100644 (file)
index 0000000..1b5d1fb
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+class HTMLEditTools extends HTMLFormField {
+       public function getInputHTML( $value ) {
+               return '';
+       }
+
+       public function getTableRow( $value ) {
+               $msg = $this->formatMsg();
+
+               return
+                       '<tr><td></td><td class="mw-input">' .
+                       '<div class="mw-editTools">' .
+                       $msg->parseAsBlock() .
+                       "</div></td></tr>\n";
+       }
+
+       /**
+        * @param string $value
+        * @return string
+        * @since 1.20
+        */
+       public function getDiv( $value ) {
+               $msg = $this->formatMsg();
+
+               return '<div class="mw-editTools">' . $msg->parseAsBlock() . '</div>';
+       }
+
+       /**
+        * @param string $value
+        * @return string
+        * @since 1.20
+        */
+       public function getRaw( $value ) {
+               return $this->getDiv( $value );
+       }
+
+       protected function formatMsg() {
+               if ( empty( $this->mParams['message'] ) ) {
+                       $msg = $this->msg( 'edittools' );
+               } else {
+                       $msg = $this->getMessage( $this->mParams['message'] );
+                       if ( $msg->isDisabled() ) {
+                               $msg = $this->msg( 'edittools' );
+                       }
+               }
+               $msg->inContentLanguage();
+
+               return $msg;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLFloatField.php b/includes/htmlform/fields/HTMLFloatField.php
new file mode 100644 (file)
index 0000000..2ef4978
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * A field that will contain a numeric value
+ */
+class HTMLFloatField extends HTMLTextField {
+       function getSize() {
+               return isset( $this->mParams['size'] ) ? $this->mParams['size'] : 20;
+       }
+
+       function validate( $value, $alldata ) {
+               $p = parent::validate( $value, $alldata );
+
+               if ( $p !== true ) {
+                       return $p;
+               }
+
+               $value = trim( $value );
+
+               # http://www.w3.org/TR/html5/infrastructure.html#floating-point-numbers
+               # with the addition that a leading '+' sign is ok.
+               if ( !preg_match( '/^((\+|\-)?\d+(\.\d+)?(E(\+|\-)?\d+)?)?$/i', $value ) ) {
+                       return $this->msg( 'htmlform-float-invalid' )->parseAsBlock();
+               }
+
+               # The "int" part of these message names is rather confusing.
+               # They make equal sense for all numbers.
+               if ( isset( $this->mParams['min'] ) ) {
+                       $min = $this->mParams['min'];
+
+                       if ( $min > $value ) {
+                               return $this->msg( 'htmlform-int-toolow', $min )->parseAsBlock();
+                       }
+               }
+
+               if ( isset( $this->mParams['max'] ) ) {
+                       $max = $this->mParams['max'];
+
+                       if ( $max < $value ) {
+                               return $this->msg( 'htmlform-int-toohigh', $max )->parseAsBlock();
+                       }
+               }
+
+               return true;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLFormFieldCloner.php b/includes/htmlform/fields/HTMLFormFieldCloner.php
new file mode 100644 (file)
index 0000000..5d8f491
--- /dev/null
@@ -0,0 +1,380 @@
+<?php
+
+/**
+ * A container for HTMLFormFields that allows for multiple copies of the set of
+ * fields to be displayed to and entered by the user.
+ *
+ * Recognized parameters, besides the general ones, include:
+ *   fields - HTMLFormField descriptors for the subfields this cloner manages.
+ *     The format is just like for the HTMLForm. A field with key 'delete' is
+ *     special: it must have type = submit and will serve to delete the group
+ *     of fields.
+ *   required - If specified, at least one group of fields must be submitted.
+ *   format - HTMLForm display format to use when displaying the subfields:
+ *     'table', 'div', or 'raw'.
+ *   row-legend - If non-empty, each group of subfields will be enclosed in a
+ *     fieldset. The value is the name of a message key to use as the legend.
+ *   create-button-message - Message to use as the text of the button to
+ *     add an additional group of fields.
+ *   delete-button-message - Message to use as the text of automatically-
+ *     generated 'delete' button. Ignored if 'delete' is included in 'fields'.
+ *
+ * In the generated HTML, the subfields will be named along the lines of
+ * "clonerName[index][fieldname]", with ids "clonerId--index--fieldid". 'index'
+ * may be a number or an arbitrary string, and may likely change when the page
+ * is resubmitted. Cloners may be nested, resulting in field names along the
+ * lines of "cloner1Name[index1][cloner2Name][index2][fieldname]" and
+ * corresponding ids.
+ *
+ * Use of cloner may result in submissions of the page that are not submissions
+ * of the HTMLForm, when non-JavaScript clients use the create or remove buttons.
+ *
+ * The result is an array, with values being arrays mapping subfield names to
+ * their values. On non-HTMLForm-submission page loads, there may also be
+ * additional (string) keys present with other types of values.
+ *
+ * @since 1.23
+ */
+class HTMLFormFieldCloner extends HTMLFormField {
+       private static $counter = 0;
+
+       /**
+        * @var string String uniquely identifying this cloner instance and
+        * unlikely to exist otherwise in the generated HTML, while still being
+        * valid as part of an HTML id.
+        */
+       protected $uniqueId;
+
+       public function __construct( $params ) {
+               $this->uniqueId = get_class( $this ) . ++self::$counter . 'x';
+               parent::__construct( $params );
+
+               if ( empty( $this->mParams['fields'] ) || !is_array( $this->mParams['fields'] ) ) {
+                       throw new MWException( 'HTMLFormFieldCloner called without any fields' );
+               }
+
+               // Make sure the delete button, if explicitly specified, is sane
+               if ( isset( $this->mParams['fields']['delete'] ) ) {
+                       $class = 'mw-htmlform-cloner-delete-button';
+                       $info = $this->mParams['fields']['delete'] + [
+                               'cssclass' => $class
+                       ];
+                       unset( $info['name'], $info['class'] );
+
+                       if ( !isset( $info['type'] ) || $info['type'] !== 'submit' ) {
+                               throw new MWException(
+                                       'HTMLFormFieldCloner delete field, if specified, must be of type "submit"'
+                               );
+                       }
+
+                       if ( !in_array( $class, explode( ' ', $info['cssclass'] ) ) ) {
+                               $info['cssclass'] .= " $class";
+                       }
+
+                       $this->mParams['fields']['delete'] = $info;
+               }
+       }
+
+       /**
+        * Create the HTMLFormFields that go inside this element, using the
+        * specified key.
+        *
+        * @param string $key Array key under which these fields should be named
+        * @return HTMLFormField[]
+        */
+       protected function createFieldsForKey( $key ) {
+               $fields = [];
+               foreach ( $this->mParams['fields'] as $fieldname => $info ) {
+                       $name = "{$this->mName}[$key][$fieldname]";
+                       if ( isset( $info['name'] ) ) {
+                               $info['name'] = "{$this->mName}[$key][{$info['name']}]";
+                       } else {
+                               $info['name'] = $name;
+                       }
+                       if ( isset( $info['id'] ) ) {
+                               $info['id'] = Sanitizer::escapeId( "{$this->mID}--$key--{$info['id']}" );
+                       } else {
+                               $info['id'] = Sanitizer::escapeId( "{$this->mID}--$key--$fieldname" );
+                       }
+                       $field = HTMLForm::loadInputFromParameters( $name, $info, $this->mParent );
+                       $fields[$fieldname] = $field;
+               }
+               return $fields;
+       }
+
+       /**
+        * Re-key the specified values array to match the names applied by
+        * createFieldsForKey().
+        *
+        * @param string $key Array key under which these fields should be named
+        * @param array $values Values array from the request
+        * @return array
+        */
+       protected function rekeyValuesArray( $key, $values ) {
+               $data = [];
+               foreach ( $values as $fieldname => $value ) {
+                       $name = "{$this->mName}[$key][$fieldname]";
+                       $data[$name] = $value;
+               }
+               return $data;
+       }
+
+       protected function needsLabel() {
+               return false;
+       }
+
+       public function loadDataFromRequest( $request ) {
+               // It's possible that this might be posted with no fields. Detect that
+               // by looking for an edit token.
+               if ( !$request->getCheck( 'wpEditToken' ) && $request->getArray( $this->mName ) === null ) {
+                       return $this->getDefault();
+               }
+
+               $values = $request->getArray( $this->mName );
+               if ( $values === null ) {
+                       $values = [];
+               }
+
+               $ret = [];
+               foreach ( $values as $key => $value ) {
+                       if ( $key === 'create' || isset( $value['delete'] ) ) {
+                               $ret['nonjs'] = 1;
+                               continue;
+                       }
+
+                       // Add back in $request->getValues() so things that look for e.g.
+                       // wpEditToken don't fail.
+                       $data = $this->rekeyValuesArray( $key, $value ) + $request->getValues();
+
+                       $fields = $this->createFieldsForKey( $key );
+                       $subrequest = new DerivativeRequest( $request, $data, $request->wasPosted() );
+                       $row = [];
+                       foreach ( $fields as $fieldname => $field ) {
+                               if ( $field->skipLoadData( $subrequest ) ) {
+                                       continue;
+                               } elseif ( !empty( $field->mParams['disabled'] ) ) {
+                                       $row[$fieldname] = $field->getDefault();
+                               } else {
+                                       $row[$fieldname] = $field->loadDataFromRequest( $subrequest );
+                               }
+                       }
+                       $ret[] = $row;
+               }
+
+               if ( isset( $values['create'] ) ) {
+                       // Non-JS client clicked the "create" button.
+                       $fields = $this->createFieldsForKey( $this->uniqueId );
+                       $row = [];
+                       foreach ( $fields as $fieldname => $field ) {
+                               if ( !empty( $field->mParams['nodata'] ) ) {
+                                       continue;
+                               } else {
+                                       $row[$fieldname] = $field->getDefault();
+                               }
+                       }
+                       $ret[] = $row;
+               }
+
+               return $ret;
+       }
+
+       public function getDefault() {
+               $ret = parent::getDefault();
+
+               // The default default is one entry with all subfields at their
+               // defaults.
+               if ( $ret === null ) {
+                       $fields = $this->createFieldsForKey( $this->uniqueId );
+                       $row = [];
+                       foreach ( $fields as $fieldname => $field ) {
+                               if ( !empty( $field->mParams['nodata'] ) ) {
+                                       continue;
+                               } else {
+                                       $row[$fieldname] = $field->getDefault();
+                               }
+                       }
+                       $ret = [ $row ];
+               }
+
+               return $ret;
+       }
+
+       public function cancelSubmit( $values, $alldata ) {
+               if ( isset( $values['nonjs'] ) ) {
+                       return true;
+               }
+
+               foreach ( $values as $key => $value ) {
+                       $fields = $this->createFieldsForKey( $key );
+                       foreach ( $fields as $fieldname => $field ) {
+                               if ( !array_key_exists( $fieldname, $value ) ) {
+                                       continue;
+                               }
+                               if ( $field->cancelSubmit( $value[$fieldname], $alldata ) ) {
+                                       return true;
+                               }
+                       }
+               }
+
+               return parent::cancelSubmit( $values, $alldata );
+       }
+
+       public function validate( $values, $alldata ) {
+               if ( isset( $this->mParams['required'] )
+                       && $this->mParams['required'] !== false
+                       && !$values
+               ) {
+                       return $this->msg( 'htmlform-cloner-required' )->parseAsBlock();
+               }
+
+               if ( isset( $values['nonjs'] ) ) {
+                       // The submission was a non-JS create/delete click, so fail
+                       // validation in case cancelSubmit() somehow didn't already handle
+                       // it.
+                       return false;
+               }
+
+               foreach ( $values as $key => $value ) {
+                       $fields = $this->createFieldsForKey( $key );
+                       foreach ( $fields as $fieldname => $field ) {
+                               if ( !array_key_exists( $fieldname, $value ) ) {
+                                       continue;
+                               }
+                               $ok = $field->validate( $value[$fieldname], $alldata );
+                               if ( $ok !== true ) {
+                                       return false;
+                               }
+                       }
+               }
+
+               return parent::validate( $values, $alldata );
+       }
+
+       /**
+        * Get the input HTML for the specified key.
+        *
+        * @param string $key Array key under which the fields should be named
+        * @param array $values
+        * @return string
+        */
+       protected function getInputHTMLForKey( $key, array $values ) {
+               $displayFormat = isset( $this->mParams['format'] )
+                       ? $this->mParams['format']
+                       : $this->mParent->getDisplayFormat();
+
+               // Conveniently, PHP method names are case-insensitive.
+               $getFieldHtmlMethod = $displayFormat == 'table' ? 'getTableRow' : ( 'get' . $displayFormat );
+
+               $html = '';
+               $hidden = '';
+               $hasLabel = false;
+
+               $fields = $this->createFieldsForKey( $key );
+               foreach ( $fields as $fieldname => $field ) {
+                       $v = array_key_exists( $fieldname, $values )
+                               ? $values[$fieldname]
+                               : $field->getDefault();
+
+                       if ( $field instanceof HTMLHiddenField ) {
+                               // HTMLHiddenField doesn't generate its own HTML
+                               list( $name, $value, $params ) = $field->getHiddenFieldData( $v );
+                               $hidden .= Html::hidden( $name, $value, $params ) . "\n";
+                       } else {
+                               $html .= $field->$getFieldHtmlMethod( $v );
+
+                               $labelValue = trim( $field->getLabel() );
+                               if ( $labelValue != '&#160;' && $labelValue !== '' ) {
+                                       $hasLabel = true;
+                               }
+                       }
+               }
+
+               if ( !isset( $fields['delete'] ) ) {
+                       $name = "{$this->mName}[$key][delete]";
+                       $label = isset( $this->mParams['delete-button-message'] )
+                               ? $this->mParams['delete-button-message']
+                               : 'htmlform-cloner-delete';
+                       $field = HTMLForm::loadInputFromParameters( $name, [
+                               'type' => 'submit',
+                               'name' => $name,
+                               'id' => Sanitizer::escapeId( "{$this->mID}--$key--delete" ),
+                               'cssclass' => 'mw-htmlform-cloner-delete-button',
+                               'default' => $this->getMessage( $label )->text(),
+                       ], $this->mParent );
+                       $v = $field->getDefault();
+
+                       if ( $displayFormat === 'table' ) {
+                               $html .= $field->$getFieldHtmlMethod( $v );
+                       } else {
+                               $html .= $field->getInputHTML( $v );
+                       }
+               }
+
+               if ( $displayFormat !== 'raw' ) {
+                       $classes = [
+                               'mw-htmlform-cloner-row',
+                       ];
+
+                       if ( !$hasLabel ) { // Avoid strange spacing when no labels exist
+                               $classes[] = 'mw-htmlform-nolabel';
+                       }
+
+                       $attribs = [
+                               'class' => implode( ' ', $classes ),
+                       ];
+
+                       if ( $displayFormat === 'table' ) {
+                               $html = Html::rawElement( 'table',
+                                       $attribs,
+                                       Html::rawElement( 'tbody', [], "\n$html\n" ) ) . "\n";
+                       } else {
+                               $html = Html::rawElement( 'div', $attribs, "\n$html\n" );
+                       }
+               }
+
+               $html .= $hidden;
+
+               if ( !empty( $this->mParams['row-legend'] ) ) {
+                       $legend = $this->msg( $this->mParams['row-legend'] )->text();
+                       $html = Xml::fieldset( $legend, $html );
+               }
+
+               return $html;
+       }
+
+       public function getInputHTML( $values ) {
+               $html = '';
+
+               foreach ( (array)$values as $key => $value ) {
+                       if ( $key === 'nonjs' ) {
+                               continue;
+                       }
+                       $html .= Html::rawElement( 'li', [ 'class' => 'mw-htmlform-cloner-li' ],
+                               $this->getInputHTMLForKey( $key, $value )
+                       );
+               }
+
+               $template = $this->getInputHTMLForKey( $this->uniqueId, [] );
+               $html = Html::rawElement( 'ul', [
+                       'id' => "mw-htmlform-cloner-list-{$this->mID}",
+                       'class' => 'mw-htmlform-cloner-ul',
+                       'data-template' => $template,
+                       'data-unique-id' => $this->uniqueId,
+               ], $html );
+
+               $name = "{$this->mName}[create]";
+               $label = isset( $this->mParams['create-button-message'] )
+                       ? $this->mParams['create-button-message']
+                       : 'htmlform-cloner-create';
+               $field = HTMLForm::loadInputFromParameters( $name, [
+                       'type' => 'submit',
+                       'name' => $name,
+                       'id' => Sanitizer::escapeId( "{$this->mID}--create" ),
+                       'cssclass' => 'mw-htmlform-cloner-create-button',
+                       'default' => $this->getMessage( $label )->text(),
+               ], $this->mParent );
+               $html .= $field->getInputHTML( $field->getDefault() );
+
+               return $html;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLFormFieldWithButton.php b/includes/htmlform/fields/HTMLFormFieldWithButton.php
new file mode 100644 (file)
index 0000000..bcb07bd
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Enables HTMLFormField elements to be build with a button.
+ */
+class HTMLFormFieldWithButton extends HTMLFormField {
+       /** @var string $mButtonClass CSS class for the button in this field */
+       protected $mButtonClass = '';
+
+       /** @var string|integer $mButtonId Element ID for the button in this field */
+       protected $mButtonId = '';
+
+       /** @var string $mButtonName Name the button in this field */
+       protected $mButtonName = '';
+
+       /** @var string $mButtonType Type of the button in this field (e.g. button or submit) */
+       protected $mButtonType = 'submit';
+
+       /** @var string $mButtonType Value for the button in this field */
+       protected $mButtonValue;
+
+       /** @var string $mButtonType Value for the button in this field */
+       protected $mButtonFlags = [ 'progressive' ];
+
+       public function __construct( $info ) {
+               if ( isset( $info['buttonclass'] ) ) {
+                       $this->mButtonClass = $info['buttonclass'];
+               }
+               if ( isset( $info['buttonid'] ) ) {
+                       $this->mButtonId = $info['buttonid'];
+               }
+               if ( isset( $info['buttonname'] ) ) {
+                       $this->mButtonName = $info['buttonname'];
+               }
+               if ( isset( $info['buttondefault'] ) ) {
+                       $this->mButtonValue = $info['buttondefault'];
+               }
+               if ( isset( $info['buttontype'] ) ) {
+                       $this->mButtonType = $info['buttontype'];
+               }
+               if ( isset( $info['buttonflags'] ) ) {
+                       $this->mButtonFlags = $info['buttonflags'];
+               }
+               parent::__construct( $info );
+       }
+
+       public function getInputHTML( $value ) {
+               $attr = [
+                       'class' => 'mw-htmlform-submit ' . $this->mButtonClass,
+                       'id' => $this->mButtonId,
+               ] + $this->getAttributes( [ 'disabled', 'tabindex' ] );
+
+               return Html::input( $this->mButtonName, $this->mButtonValue, $this->mButtonType, $attr );
+       }
+
+       public function getInputOOUI( $value ) {
+               return new OOUI\ButtonInputWidget( [
+                       'name' => $this->mButtonName,
+                       'value' => $this->mButtonValue,
+                       'type' => $this->mButtonType,
+                       'label' => $this->mButtonValue,
+                       'flags' => $this->mButtonFlags,
+               ] + OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( [ 'disabled', 'tabindex' ] )
+               ) );
+       }
+
+       /**
+        * Combines the passed element with a button.
+        * @param String $element Element to combine the button with.
+        * @return String
+        */
+       public function getElement( $element ) {
+               return $element . '&#160;' . $this->getInputHTML( '' );
+       }
+}
diff --git a/includes/htmlform/fields/HTMLHiddenField.php b/includes/htmlform/fields/HTMLHiddenField.php
new file mode 100644 (file)
index 0000000..c0fce2b
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+class HTMLHiddenField extends HTMLFormField {
+       protected $outputAsDefault = true;
+
+       public function __construct( $params ) {
+               parent::__construct( $params );
+
+               if ( isset( $this->mParams['output-as-default'] ) ) {
+                       $this->outputAsDefault = (bool)$this->mParams['output-as-default'];
+               }
+
+               # Per HTML5 spec, hidden fields cannot be 'required'
+               # http://www.w3.org/TR/html5/forms.html#hidden-state-%28type=hidden%29
+               unset( $this->mParams['required'] );
+       }
+
+       public function getHiddenFieldData( $value ) {
+               $params = [];
+               if ( $this->mID ) {
+                       $params['id'] = $this->mID;
+               }
+
+               if ( $this->outputAsDefault ) {
+                       $value = $this->mDefault;
+               }
+
+               return [ $this->mName, $value, $params ];
+       }
+
+       public function getTableRow( $value ) {
+               list( $name, $value, $params ) = $this->getHiddenFieldData( $value );
+               $this->mParent->addHiddenField( $name, $value, $params );
+               return '';
+       }
+
+       /**
+        * @param string $value
+        * @return string
+        * @since 1.20
+        */
+       public function getDiv( $value ) {
+               return $this->getTableRow( $value );
+       }
+
+       /**
+        * @param string $value
+        * @return string
+        * @since 1.20
+        */
+       public function getRaw( $value ) {
+               return $this->getTableRow( $value );
+       }
+
+       public function getInputHTML( $value ) {
+               return '';
+       }
+
+       public function canDisplayErrors() {
+               return false;
+       }
+
+       public function hasVisibleOutput() {
+               return false;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLInfoField.php b/includes/htmlform/fields/HTMLInfoField.php
new file mode 100644 (file)
index 0000000..6dc5d08
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * An information field (text blob), not a proper input.
+ */
+class HTMLInfoField extends HTMLFormField {
+       /**
+        * @param array $info
+        *   In adition to the usual HTMLFormField parameters, this can take the following fields:
+        *   - default: the value (text) of the field. Unlike other form field types, HTMLInfoField can
+        *     take a closure as a default value, which will be evaluated with $info as its only parameter.
+        *   - raw: if true, the value won't be escaped.
+        *   - rawrow: if true, the usual wrapping of form fields (e.g. into a table row + cell when
+        *     display mode is table) will not happen and the value must contain it already.
+        */
+       public function __construct( $info ) {
+               $info['nodata'] = true;
+
+               parent::__construct( $info );
+       }
+
+       function getDefault() {
+               $default = parent::getDefault();
+               if ( $default instanceof Closure ) {
+                       $default = call_user_func( $default, $this->mParams );
+               }
+               return $default;
+       }
+
+       public function getInputHTML( $value ) {
+               return !empty( $this->mParams['raw'] ) ? $value : htmlspecialchars( $value );
+       }
+
+       public function getInputOOUI( $value ) {
+               if ( !empty( $this->mParams['raw'] ) ) {
+                       $value = new OOUI\HtmlSnippet( $value );
+               }
+
+               return new OOUI\LabelWidget( [
+                       'label' => $value,
+               ] );
+       }
+
+       public function getTableRow( $value ) {
+               if ( !empty( $this->mParams['rawrow'] ) ) {
+                       return $value;
+               }
+
+               return parent::getTableRow( $value );
+       }
+
+       /**
+        * @param string $value
+        * @return string
+        * @since 1.20
+        */
+       public function getDiv( $value ) {
+               if ( !empty( $this->mParams['rawrow'] ) ) {
+                       return $value;
+               }
+
+               return parent::getDiv( $value );
+       }
+
+       /**
+        * @param string $value
+        * @return string
+        * @since 1.20
+        */
+       public function getRaw( $value ) {
+               if ( !empty( $this->mParams['rawrow'] ) ) {
+                       return $value;
+               }
+
+               return parent::getRaw( $value );
+       }
+
+       protected function needsLabel() {
+               return false;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLIntField.php b/includes/htmlform/fields/HTMLIntField.php
new file mode 100644 (file)
index 0000000..b0148d9
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * A field that must contain a number
+ */
+class HTMLIntField extends HTMLFloatField {
+       function validate( $value, $alldata ) {
+               $p = parent::validate( $value, $alldata );
+
+               if ( $p !== true ) {
+                       return $p;
+               }
+
+               # http://www.w3.org/TR/html5/infrastructure.html#signed-integers
+               # with the addition that a leading '+' sign is ok. Note that leading zeros
+               # are fine, and will be left in the input, which is useful for things like
+               # phone numbers when you know that they are integers (the HTML5 type=tel
+               # input does not require its value to be numeric).  If you want a tidier
+               # value to, eg, save in the DB, clean it up with intval().
+               if ( !preg_match( '/^((\+|\-)?\d+)?$/', trim( $value ) ) ) {
+                       return $this->msg( 'htmlform-int-invalid' )->parseAsBlock();
+               }
+
+               return true;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLMultiSelectField.php b/includes/htmlform/fields/HTMLMultiSelectField.php
new file mode 100644 (file)
index 0000000..c9fcb09
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * Multi-select field
+ */
+class HTMLMultiSelectField extends HTMLFormField implements HTMLNestedFilterable {
+       /**
+        * @param array $params
+        *   In adition to the usual HTMLFormField parameters, this can take the following fields:
+        *   - dropdown: If given, the options will be displayed inside a dropdown with a text field that
+        *     can be used to filter them. This is desirable mostly for very long lists of options.
+        *     This only works for users with JavaScript support and falls back to the list of checkboxes.
+        *   - flatlist: If given, the options will be displayed on a single line (wrapping to following
+        *     lines if necessary), rather than each one on a line of its own. This is desirable mostly
+        *     for very short lists of concisely labelled options.
+        */
+       public function __construct( $params ) {
+               parent::__construct( $params );
+
+               // For backwards compatibility, also handle the old way with 'cssclass' => 'mw-chosen'
+               if ( isset( $params['dropdown'] ) || strpos( $this->mClass, 'mw-chosen' ) !== false ) {
+                       $this->mClass .= ' mw-htmlform-dropdown';
+               }
+
+               if ( isset( $params['flatlist'] ) ) {
+                       $this->mClass .= ' mw-htmlform-flatlist';
+               }
+       }
+
+       function validate( $value, $alldata ) {
+               $p = parent::validate( $value, $alldata );
+
+               if ( $p !== true ) {
+                       return $p;
+               }
+
+               if ( !is_array( $value ) ) {
+                       return false;
+               }
+
+               # If all options are valid, array_intersect of the valid options
+               # and the provided options will return the provided options.
+               $validOptions = HTMLFormField::flattenOptions( $this->getOptions() );
+
+               $validValues = array_intersect( $value, $validOptions );
+               if ( count( $validValues ) == count( $value ) ) {
+                       return true;
+               } else {
+                       return $this->msg( 'htmlform-select-badoption' )->parse();
+               }
+       }
+
+       function getInputHTML( $value ) {
+               if ( isset( $this->mParams['dropdown'] ) ) {
+                       $this->mParent->getOutput()->addModules( 'jquery.chosen' );
+               }
+
+               $value = HTMLFormField::forceToStringRecursive( $value );
+               $html = $this->formatOptions( $this->getOptions(), $value );
+
+               return $html;
+       }
+
+       function formatOptions( $options, $value ) {
+               $html = '';
+
+               $attribs = $this->getAttributes( [ 'disabled', 'tabindex' ] );
+
+               foreach ( $options as $label => $info ) {
+                       if ( is_array( $info ) ) {
+                               $html .= Html::rawElement( 'h1', [], $label ) . "\n";
+                               $html .= $this->formatOptions( $info, $value );
+                       } else {
+                               $thisAttribs = [
+                                       'id' => "{$this->mID}-$info",
+                                       'value' => $info,
+                               ];
+                               $checked = in_array( $info, $value, true );
+
+                               $checkbox = $this->getOneCheckbox( $checked, $attribs + $thisAttribs, $label );
+
+                               $html .= ' ' . Html::rawElement(
+                                       'div',
+                                       [ 'class' => 'mw-htmlform-flatlist-item' ],
+                                       $checkbox
+                               );
+                       }
+               }
+
+               return $html;
+       }
+
+       protected function getOneCheckbox( $checked, $attribs, $label ) {
+               if ( $this->mParent instanceof OOUIHTMLForm ) {
+                       throw new MWException( 'HTMLMultiSelectField#getOneCheckbox() is not supported' );
+               } else {
+                       $elementFunc = [ 'Html', $this->mOptionsLabelsNotFromMessage ? 'rawElement' : 'element' ];
+                       $checkbox =
+                               Xml::check( "{$this->mName}[]", $checked, $attribs ) .
+                               '&#160;' .
+                               call_user_func( $elementFunc,
+                                       'label',
+                                       [ 'for' => $attribs['id'] ],
+                                       $label
+                               );
+                       if ( $this->mParent->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) {
+                               $checkbox = Html::openElement( 'div', [ 'class' => 'mw-ui-checkbox' ] ) .
+                                       $checkbox .
+                                       Html::closeElement( 'div' );
+                       }
+                       return $checkbox;
+               }
+       }
+
+       /**
+        * Get the OOUI version of this field.
+        *
+        * @since 1.28
+        * @param string[] $value
+        * @return OOUI\CheckboxMultiselectInputWidget
+        */
+       public function getInputOOUI( $value ) {
+               $attr = $this->getTooltipAndAccessKey();
+               $attr['id'] = $this->mID;
+               $attr['name'] = "{$this->mName}[]";
+
+               $attr['value'] = $value;
+               $attr['options'] = $this->getOptionsOOUI();
+
+               if ( $this->mOptionsLabelsNotFromMessage ) {
+                       foreach ( $attr['options'] as &$option ) {
+                               $option['label'] = new OOUI\HtmlSnippet( $option['label'] );
+                       }
+               }
+
+               $attr += OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( [ 'disabled', 'tabindex' ] )
+               );
+
+               if ( $this->mClass !== '' ) {
+                       $attr['classes'] = [ $this->mClass ];
+               }
+
+               return new OOUI\CheckboxMultiselectInputWidget( $attr );
+       }
+
+       /**
+        * @param WebRequest $request
+        *
+        * @return string
+        */
+       function loadDataFromRequest( $request ) {
+               if ( $this->isSubmitAttempt( $request ) ) {
+                       // Checkboxes are just not added to the request arrays if they're not checked,
+                       // so it's perfectly possible for there not to be an entry at all
+                       return $request->getArray( $this->mName, [] );
+               } else {
+                       // That's ok, the user has not yet submitted the form, so show the defaults
+                       return $this->getDefault();
+               }
+       }
+
+       function getDefault() {
+               if ( isset( $this->mDefault ) ) {
+                       return $this->mDefault;
+               } else {
+                       return [];
+               }
+       }
+
+       function filterDataForSubmit( $data ) {
+               $data = HTMLFormField::forceToStringRecursive( $data );
+               $options = HTMLFormField::flattenOptions( $this->getOptions() );
+
+               $res = [];
+               foreach ( $options as $opt ) {
+                       $res["$opt"] = in_array( $opt, $data, true );
+               }
+
+               return $res;
+       }
+
+       protected function needsLabel() {
+               return false;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLRadioField.php b/includes/htmlform/fields/HTMLRadioField.php
new file mode 100644 (file)
index 0000000..42c2fdf
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * Radio checkbox fields.
+ */
+class HTMLRadioField extends HTMLFormField {
+       /**
+        * @param array $params
+        *   In adition to the usual HTMLFormField parameters, this can take the following fields:
+        *   - flatlist: If given, the options will be displayed on a single line (wrapping to following
+        *     lines if necessary), rather than each one on a line of its own. This is desirable mostly
+        *     for very short lists of concisely labelled options.
+        */
+       public function __construct( $params ) {
+               parent::__construct( $params );
+
+               if ( isset( $params['flatlist'] ) ) {
+                       $this->mClass .= ' mw-htmlform-flatlist';
+               }
+       }
+
+       function validate( $value, $alldata ) {
+               $p = parent::validate( $value, $alldata );
+
+               if ( $p !== true ) {
+                       return $p;
+               }
+
+               if ( !is_string( $value ) && !is_int( $value ) ) {
+                       return $this->msg( 'htmlform-required' )->parse();
+               }
+
+               $validOptions = HTMLFormField::flattenOptions( $this->getOptions() );
+
+               if ( in_array( strval( $value ), $validOptions, true ) ) {
+                       return true;
+               } else {
+                       return $this->msg( 'htmlform-select-badoption' )->parse();
+               }
+       }
+
+       /**
+        * This returns a block of all the radio options, in one cell.
+        * @see includes/HTMLFormField#getInputHTML()
+        *
+        * @param string $value
+        *
+        * @return string
+        */
+       function getInputHTML( $value ) {
+               $html = $this->formatOptions( $this->getOptions(), strval( $value ) );
+
+               return $html;
+       }
+
+       function getInputOOUI( $value ) {
+               $options = [];
+               foreach ( $this->getOptions() as $label => $data ) {
+                       $options[] = [
+                               'data' => $data,
+                               'label' => $this->mOptionsLabelsNotFromMessage ? new OOUI\HtmlSnippet( $label ) : $label,
+                       ];
+               }
+
+               return new OOUI\RadioSelectInputWidget( [
+                       'name' => $this->mName,
+                       'id' => $this->mID,
+                       'value' => $value,
+                       'options' => $options,
+               ] + OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( [ 'disabled', 'tabindex' ] )
+               ) );
+       }
+
+       protected function shouldInfuseOOUI() {
+               return true;
+       }
+
+       function formatOptions( $options, $value ) {
+               global $wgUseMediaWikiUIEverywhere;
+
+               $html = '';
+
+               $attribs = $this->getAttributes( [ 'disabled', 'tabindex' ] );
+               $elementFunc = [ 'Html', $this->mOptionsLabelsNotFromMessage ? 'rawElement' : 'element' ];
+
+               # @todo Should this produce an unordered list perhaps?
+               foreach ( $options as $label => $info ) {
+                       if ( is_array( $info ) ) {
+                               $html .= Html::rawElement( 'h1', [], $label ) . "\n";
+                               $html .= $this->formatOptions( $info, $value );
+                       } else {
+                               $id = Sanitizer::escapeId( $this->mID . "-$info" );
+                               $classes = [ 'mw-htmlform-flatlist-item' ];
+                               if ( $wgUseMediaWikiUIEverywhere || $this->mParent instanceof VFormHTMLForm ) {
+                                       $classes[] = 'mw-ui-radio';
+                               }
+                               $radio = Xml::radio( $this->mName, $info, $info === $value, $attribs + [ 'id' => $id ] );
+                               $radio .= '&#160;' . call_user_func( $elementFunc, 'label', [ 'for' => $id ], $label );
+
+                               $html .= ' ' . Html::rawElement(
+                                       'div',
+                                       [ 'class' => $classes ],
+                                       $radio
+                               );
+                       }
+               }
+
+               return $html;
+       }
+
+       protected function needsLabel() {
+               return false;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLSelectAndOtherField.php b/includes/htmlform/fields/HTMLSelectAndOtherField.php
new file mode 100644 (file)
index 0000000..e75c2b2
--- /dev/null
@@ -0,0 +1,134 @@
+<?php
+
+/**
+ * Double field with a dropdown list constructed from a system message in the format
+ *     * Optgroup header
+ *     ** <option value>
+ *     * New Optgroup header
+ * Plus a text field underneath for an additional reason.  The 'value' of the field is
+ * "<select>: <extra reason>", or "<extra reason>" if nothing has been selected in the
+ * select dropdown.
+ * @todo FIXME: If made 'required', only the text field should be compulsory.
+ */
+class HTMLSelectAndOtherField extends HTMLSelectField {
+       function __construct( $params ) {
+               if ( array_key_exists( 'other', $params ) ) {
+                       // Do nothing
+               } elseif ( array_key_exists( 'other-message', $params ) ) {
+                       $params['other'] = $this->getMessage( $params['other-message'] )->plain();
+               } else {
+                       $params['other'] = $this->msg( 'htmlform-selectorother-other' )->plain();
+               }
+
+               parent::__construct( $params );
+
+               if ( $this->getOptions() === null ) {
+                       // Sulk
+                       throw new MWException( 'HTMLSelectAndOtherField called without any options' );
+               }
+               if ( !in_array( 'other', $this->mOptions, true ) ) {
+                       // Have 'other' always as first element
+                       $this->mOptions = [ $params['other'] => 'other' ] + $this->mOptions;
+               }
+               $this->mFlatOptions = self::flattenOptions( $this->getOptions() );
+
+       }
+
+       function getInputHTML( $value ) {
+               $select = parent::getInputHTML( $value[1] );
+
+               $textAttribs = [
+                       'id' => $this->mID . '-other',
+                       'size' => $this->getSize(),
+                       'class' => [ 'mw-htmlform-select-and-other-field' ],
+                       'data-id-select' => $this->mID,
+               ];
+
+               if ( $this->mClass !== '' ) {
+                       $textAttribs['class'][] = $this->mClass;
+               }
+
+               $allowedParams = [
+                       'required',
+                       'autofocus',
+                       'multiple',
+                       'disabled',
+                       'tabindex',
+                       'maxlength', // gets dynamic with javascript, see mediawiki.htmlform.js
+               ];
+
+               $textAttribs += $this->getAttributes( $allowedParams );
+
+               $textbox = Html::input( $this->mName . '-other', $value[2], 'text', $textAttribs );
+
+               return "$select<br />\n$textbox";
+       }
+
+       function getInputOOUI( $value ) {
+               return false;
+       }
+
+       /**
+        * @param WebRequest $request
+        *
+        * @return array("<overall message>","<select value>","<text field value>")
+        */
+       function loadDataFromRequest( $request ) {
+               if ( $request->getCheck( $this->mName ) ) {
+                       $list = $request->getText( $this->mName );
+                       $text = $request->getText( $this->mName . '-other' );
+
+                       // Should be built the same as in mediawiki.htmlform.js
+                       if ( $list == 'other' ) {
+                               $final = $text;
+                       } elseif ( !in_array( $list, $this->mFlatOptions, true ) ) {
+                               # User has spoofed the select form to give an option which wasn't
+                               # in the original offer.  Sulk...
+                               $final = $text;
+                       } elseif ( $text == '' ) {
+                               $final = $list;
+                       } else {
+                               $final = $list . $this->msg( 'colon-separator' )->inContentLanguage()->text() . $text;
+                       }
+               } else {
+                       $final = $this->getDefault();
+
+                       $list = 'other';
+                       $text = $final;
+                       foreach ( $this->mFlatOptions as $option ) {
+                               $match = $option . $this->msg( 'colon-separator' )->inContentLanguage()->text();
+                               if ( strpos( $text, $match ) === 0 ) {
+                                       $list = $option;
+                                       $text = substr( $text, strlen( $match ) );
+                                       break;
+                               }
+                       }
+               }
+
+               return [ $final, $list, $text ];
+       }
+
+       function getSize() {
+               return isset( $this->mParams['size'] ) ? $this->mParams['size'] : 45;
+       }
+
+       function validate( $value, $alldata ) {
+               # HTMLSelectField forces $value to be one of the options in the select
+               # field, which is not useful here.  But we do want the validation further up
+               # the chain
+               $p = parent::validate( $value[1], $alldata );
+
+               if ( $p !== true ) {
+                       return $p;
+               }
+
+               if ( isset( $this->mParams['required'] )
+                       && $this->mParams['required'] !== false
+                       && $value[1] === ''
+               ) {
+                       return $this->msg( 'htmlform-required' )->parse();
+               }
+
+               return true;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLSelectField.php b/includes/htmlform/fields/HTMLSelectField.php
new file mode 100644 (file)
index 0000000..40b31b5
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * A select dropdown field.  Basically a wrapper for Xmlselect class
+ */
+class HTMLSelectField extends HTMLFormField {
+       function validate( $value, $alldata ) {
+               $p = parent::validate( $value, $alldata );
+
+               if ( $p !== true ) {
+                       return $p;
+               }
+
+               $validOptions = HTMLFormField::flattenOptions( $this->getOptions() );
+
+               if ( in_array( strval( $value ), $validOptions, true ) ) {
+                       return true;
+               } else {
+                       return $this->msg( 'htmlform-select-badoption' )->parse();
+               }
+       }
+
+       function getInputHTML( $value ) {
+               $select = new XmlSelect( $this->mName, $this->mID, strval( $value ) );
+
+               if ( !empty( $this->mParams['disabled'] ) ) {
+                       $select->setAttribute( 'disabled', 'disabled' );
+               }
+
+               $allowedParams = [ 'tabindex', 'size' ];
+               $customParams = $this->getAttributes( $allowedParams );
+               foreach ( $customParams as $name => $value ) {
+                       $select->setAttribute( $name, $value );
+               }
+
+               if ( $this->mClass !== '' ) {
+                       $select->setAttribute( 'class', $this->mClass );
+               }
+
+               $select->addOptions( $this->getOptions() );
+
+               return $select->getHTML();
+       }
+
+       function getInputOOUI( $value ) {
+               $disabled = false;
+               $allowedParams = [ 'tabindex' ];
+               $attribs = OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( $allowedParams )
+               );
+
+               if ( $this->mClass !== '' ) {
+                       $attribs['classes'] = [ $this->mClass ];
+               }
+
+               if ( !empty( $this->mParams['disabled'] ) ) {
+                       $disabled = true;
+               }
+
+               return new OOUI\DropdownInputWidget( [
+                       'name' => $this->mName,
+                       'id' => $this->mID,
+                       'options' => $this->getOptionsOOUI(),
+                       'value' => strval( $value ),
+                       'disabled' => $disabled,
+               ] + $attribs );
+       }
+
+       protected function shouldInfuseOOUI() {
+               return true;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLSelectLimitField.php b/includes/htmlform/fields/HTMLSelectLimitField.php
new file mode 100644 (file)
index 0000000..e7f1c04
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * A limit dropdown, which accepts any valid number
+ */
+class HTMLSelectLimitField extends HTMLSelectField {
+       /**
+        * Basically don't do any validation. If it's a number that's fine. Also,
+        * add it to the list if it's not there already
+        *
+        * @param string $value
+        * @param array $alldata
+        * @return bool
+        */
+       function validate( $value, $alldata ) {
+               if ( $value == '' ) {
+                       return true;
+               }
+
+               // Let folks pick an explicit limit not from our list, as long as it's a real numbr.
+               if ( !in_array( $value, $this->mParams['options'] )
+                       && $value == intval( $value )
+                       && $value > 0
+               ) {
+                       // This adds the explicitly requested limit value to the drop-down,
+                       // then makes sure it's sorted correctly so when we output the list
+                       // later, the custom option doesn't just show up last.
+                       $this->mParams['options'][$this->mParent->getLanguage()->formatNum( $value )] =
+                               intval( $value );
+                       asort( $this->mParams['options'] );
+               }
+
+               return true;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLSelectNamespace.php b/includes/htmlform/fields/HTMLSelectNamespace.php
new file mode 100644 (file)
index 0000000..230790d
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Wrapper for Html::namespaceSelector to use in HTMLForm
+ */
+class HTMLSelectNamespace extends HTMLFormField {
+       public function __construct( $params ) {
+               parent::__construct( $params );
+
+               $this->mAllValue = array_key_exists( 'all', $params )
+                       ? $params['all']
+                       : 'all';
+
+       }
+
+       function getInputHTML( $value ) {
+               return Html::namespaceSelector(
+                       [
+                               'selected' => $value,
+                               'all' => $this->mAllValue
+                       ], [
+                               'name' => $this->mName,
+                               'id' => $this->mID,
+                               'class' => 'namespaceselector',
+                       ]
+               );
+       }
+
+       public function getInputOOUI( $value ) {
+               return new MediaWiki\Widget\NamespaceInputWidget( [
+                       'value' => $value,
+                       'name' => $this->mName,
+                       'id' => $this->mID,
+                       'includeAllValue' => $this->mAllValue,
+               ] );
+       }
+
+       protected function getOOUIModules() {
+               // FIXME: NamespaceInputWidget should be in its own module (probably?)
+               return [ 'mediawiki.widgets' ];
+       }
+
+       protected function shouldInfuseOOUI() {
+               return true;
+       }
+}
diff --git a/includes/htmlform/fields/HTMLSelectNamespaceWithButton.php b/includes/htmlform/fields/HTMLSelectNamespaceWithButton.php
new file mode 100644 (file)
index 0000000..5225983
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Creates a Html::namespaceSelector input field with a button assigned to the input field.
+ */
+class HTMLSelectNamespaceWithButton extends HTMLSelectNamespace {
+       /** @var HTMLFormFieldWithButton $mClassWithButton */
+       protected $mClassWithButton = null;
+
+       public function __construct( $info ) {
+               $this->mClassWithButton = new HTMLFormFieldWithButton( $info );
+               parent::__construct( $info );
+       }
+
+       public function getInputHTML( $value ) {
+               return $this->mClassWithButton->getElement( parent::getInputHTML( $value ) );
+       }
+}
diff --git a/includes/htmlform/fields/HTMLSelectOrOtherField.php b/includes/htmlform/fields/HTMLSelectOrOtherField.php
new file mode 100644 (file)
index 0000000..8f7750c
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * Select dropdown field, with an additional "other" textbox.
+ *
+ * HTMLComboboxField implements the same functionality using a single form field
+ * and should be used instead.
+ */
+class HTMLSelectOrOtherField extends HTMLTextField {
+       function __construct( $params ) {
+               parent::__construct( $params );
+               $this->getOptions();
+               if ( !in_array( 'other', $this->mOptions, true ) ) {
+                       $msg =
+                               isset( $params['other'] )
+                                       ? $params['other']
+                                       : wfMessage( 'htmlform-selectorother-other' )->text();
+                       // Have 'other' always as first element
+                       $this->mOptions = [ $msg => 'other' ] + $this->mOptions;
+               }
+
+       }
+
+       function getInputHTML( $value ) {
+               $valInSelect = false;
+
+               if ( $value !== false ) {
+                       $value = strval( $value );
+                       $valInSelect = in_array(
+                               $value, HTMLFormField::flattenOptions( $this->getOptions() ), true
+                       );
+               }
+
+               $selected = $valInSelect ? $value : 'other';
+
+               $select = new XmlSelect( $this->mName, $this->mID, $selected );
+               $select->addOptions( $this->getOptions() );
+
+               $select->setAttribute( 'class', 'mw-htmlform-select-or-other' );
+
+               $tbAttribs = [ 'id' => $this->mID . '-other', 'size' => $this->getSize() ];
+
+               if ( !empty( $this->mParams['disabled'] ) ) {
+                       $select->setAttribute( 'disabled', 'disabled' );
+                       $tbAttribs['disabled'] = 'disabled';
+               }
+
+               if ( isset( $this->mParams['tabindex'] ) ) {
+                       $select->setAttribute( 'tabindex', $this->mParams['tabindex'] );
+                       $tbAttribs['tabindex'] = $this->mParams['tabindex'];
+               }
+
+               $select = $select->getHTML();
+
+               if ( isset( $this->mParams['maxlength'] ) ) {
+                       $tbAttribs['maxlength'] = $this->mParams['maxlength'];
+               }
+
+               if ( $this->mClass !== '' ) {
+                       $tbAttribs['class'] = $this->mClass;
+               }
+
+               $textbox = Html::input( $this->mName . '-other', $valInSelect ? '' : $value, 'text', $tbAttribs );
+
+               return "$select<br />\n$textbox";
+       }
+
+       function getInputOOUI( $value ) {
+               return false;
+       }
+
+       /**
+        * @param WebRequest $request
+        *
+        * @return string
+        */
+       function loadDataFromRequest( $request ) {
+               if ( $request->getCheck( $this->mName ) ) {
+                       $val = $request->getText( $this->mName );
+
+                       if ( $val === 'other' ) {
+                               $val = $request->getText( $this->mName . '-other' );
+                       }
+
+                       return $val;
+               } else {
+                       return $this->getDefault();
+               }
+       }
+}
diff --git a/includes/htmlform/fields/HTMLSubmitField.php b/includes/htmlform/fields/HTMLSubmitField.php
new file mode 100644 (file)
index 0000000..cb98549
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * Add a submit button inline in the form (as opposed to
+ * HTMLForm::addButton(), which will add it at the end).
+ */
+class HTMLSubmitField extends HTMLButtonField {
+       protected $buttonType = 'submit';
+
+       protected $mFlags = [ 'primary', 'constructive' ];
+
+       public function skipLoadData( $request ) {
+               return !$request->getCheck( $this->mName );
+       }
+
+       public function loadDataFromRequest( $request ) {
+               return $request->getCheck( $this->mName );
+       }
+}
diff --git a/includes/htmlform/fields/HTMLTagFilter.php b/includes/htmlform/fields/HTMLTagFilter.php
new file mode 100644 (file)
index 0000000..8075de5
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Wrapper for ChangeTags::buildTagFilterSelector to use in HTMLForm
+ */
+class HTMLTagFilter extends HTMLFormField {
+       protected $tagFilter;
+
+       function getTableRow( $value ) {
+               $this->tagFilter = ChangeTags::buildTagFilterSelector( $value );
+               if ( $this->tagFilter ) {
+                       return parent::getTableRow( $value );
+               }
+               return '';
+       }
+
+       function getDiv( $value ) {
+               $this->tagFilter = ChangeTags::buildTagFilterSelector( $value );
+               if ( $this->tagFilter ) {
+                       return parent::getDiv( $value );
+               }
+               return '';
+       }
+
+       function getInputHTML( $value ) {
+               if ( $this->tagFilter ) {
+                       // we only need the select field, HTMLForm should handle the label
+                       return $this->tagFilter[1];
+               }
+               return '';
+       }
+}
diff --git a/includes/htmlform/fields/HTMLTextAreaField.php b/includes/htmlform/fields/HTMLTextAreaField.php
new file mode 100644 (file)
index 0000000..8ffff43
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+
+class HTMLTextAreaField extends HTMLFormField {
+       const DEFAULT_COLS = 80;
+       const DEFAULT_ROWS = 25;
+
+       protected $mPlaceholder = '';
+
+       /**
+        * @param array $params
+        *   - cols, rows: textarea size
+        *   - placeholder/placeholder-message: set HTML placeholder attribute
+        *   - spellcheck: set HTML spellcheck attribute
+        */
+       public function __construct( $params ) {
+               parent::__construct( $params );
+
+               if ( isset( $params['placeholder-message'] ) ) {
+                       $this->mPlaceholder = $this->getMessage( $params['placeholder-message'] )->parse();
+               } elseif ( isset( $params['placeholder'] ) ) {
+                       $this->mPlaceholder = $params['placeholder'];
+               }
+       }
+
+       function getCols() {
+               return isset( $this->mParams['cols'] ) ? $this->mParams['cols'] : static::DEFAULT_COLS;
+       }
+
+       function getRows() {
+               return isset( $this->mParams['rows'] ) ? $this->mParams['rows'] : static::DEFAULT_ROWS;
+       }
+
+       function getSpellCheck() {
+               $val = isset( $this->mParams['spellcheck'] ) ? $this->mParams['spellcheck'] : null;
+               if ( is_bool( $val ) ) {
+                       // "spellcheck" attribute literally requires "true" or "false" to work.
+                       return $val === true ? 'true' : 'false';
+               }
+               return null;
+       }
+
+       function getInputHTML( $value ) {
+               $attribs = [
+                               'id' => $this->mID,
+                               'cols' => $this->getCols(),
+                               'rows' => $this->getRows(),
+                               'spellcheck' => $this->getSpellCheck(),
+                       ] + $this->getTooltipAndAccessKey();
+
+               if ( $this->mClass !== '' ) {
+                       $attribs['class'] = $this->mClass;
+               }
+               if ( $this->mPlaceholder !== '' ) {
+                       $attribs['placeholder'] = $this->mPlaceholder;
+               }
+
+               $allowedParams = [
+                       'tabindex',
+                       'disabled',
+                       'readonly',
+                       'required',
+                       'autofocus'
+               ];
+
+               $attribs += $this->getAttributes( $allowedParams );
+               return Html::textarea( $this->mName, $value, $attribs );
+       }
+
+       function getInputOOUI( $value ) {
+               if ( isset( $this->mParams['cols'] ) ) {
+                       throw new Exception( "OOUIHTMLForm does not support the 'cols' parameter for textareas" );
+               }
+
+               $attribs = $this->getTooltipAndAccessKey();
+
+               if ( $this->mClass !== '' ) {
+                       $attribs['classes'] = [ $this->mClass ];
+               }
+               if ( $this->mPlaceholder !== '' ) {
+                       $attribs['placeholder'] = $this->mPlaceholder;
+               }
+
+               $allowedParams = [
+                       'tabindex',
+                       'disabled',
+                       'readonly',
+                       'required',
+                       'autofocus',
+               ];
+
+               $attribs += OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( $allowedParams )
+               );
+
+               return new OOUI\TextInputWidget( [
+                       'id' => $this->mID,
+                       'name' => $this->mName,
+                       'multiline' => true,
+                       'value' => $value,
+                       'rows' => $this->getRows(),
+               ] + $attribs );
+       }
+}
diff --git a/includes/htmlform/fields/HTMLTextField.php b/includes/htmlform/fields/HTMLTextField.php
new file mode 100644 (file)
index 0000000..3ab7176
--- /dev/null
@@ -0,0 +1,177 @@
+<?php
+
+class HTMLTextField extends HTMLFormField {
+       protected $mPlaceholder = '';
+
+       /**
+        * @param array $params
+        *   - type: HTML textfield type
+        *   - size: field size in characters (defaults to 45)
+        *   - placeholder/placeholder-message: set HTML placeholder attribute
+        *   - spellcheck: set HTML spellcheck attribute
+        *   - persistent: upon unsuccessful requests, retain the value (defaults to true, except
+        *     for password fields)
+        */
+       public function __construct( $params ) {
+               parent::__construct( $params );
+
+               if ( isset( $params['placeholder-message'] ) ) {
+                       $this->mPlaceholder = $this->getMessage( $params['placeholder-message'] )->parse();
+               } elseif ( isset( $params['placeholder'] ) ) {
+                       $this->mPlaceholder = $params['placeholder'];
+               }
+       }
+
+       function getSize() {
+               return isset( $this->mParams['size'] ) ? $this->mParams['size'] : 45;
+       }
+
+       function getSpellCheck() {
+               $val = isset( $this->mParams['spellcheck'] ) ? $this->mParams['spellcheck'] : null;
+               if ( is_bool( $val ) ) {
+                       // "spellcheck" attribute literally requires "true" or "false" to work.
+                       return $val === true ? 'true' : 'false';
+               }
+               return null;
+       }
+
+       public function isPersistent() {
+               if ( isset( $this->mParams['persistent'] ) ) {
+                       return $this->mParams['persistent'];
+               }
+               // don't put passwords into the HTML body, they could get cached or otherwise leaked
+               return !( isset( $this->mParams['type'] ) && $this->mParams['type'] === 'password' );
+       }
+
+       function getInputHTML( $value ) {
+               if ( !$this->isPersistent() ) {
+                       $value = '';
+               }
+
+               $attribs = [
+                               'id' => $this->mID,
+                               'name' => $this->mName,
+                               'size' => $this->getSize(),
+                               'value' => $value,
+                               'dir' => $this->mDir,
+                               'spellcheck' => $this->getSpellCheck(),
+                       ] + $this->getTooltipAndAccessKey() + $this->getDataAttribs();
+
+               if ( $this->mClass !== '' ) {
+                       $attribs['class'] = $this->mClass;
+               }
+               if ( $this->mPlaceholder !== '' ) {
+                       $attribs['placeholder'] = $this->mPlaceholder;
+               }
+
+               # @todo Enforce pattern, step, required, readonly on the server side as
+               # well
+               $allowedParams = [
+                       'type',
+                       'min',
+                       'max',
+                       'pattern',
+                       'title',
+                       'step',
+                       'list',
+                       'maxlength',
+                       'tabindex',
+                       'disabled',
+                       'required',
+                       'autofocus',
+                       'multiple',
+                       'readonly'
+               ];
+
+               $attribs += $this->getAttributes( $allowedParams );
+
+               # Extract 'type'
+               $type = $this->getType( $attribs );
+               return Html::input( $this->mName, $value, $type, $attribs );
+       }
+
+       protected function getType( &$attribs ) {
+               $type = isset( $attribs['type'] ) ? $attribs['type'] : 'text';
+               unset( $attribs['type'] );
+
+               # Implement tiny differences between some field variants
+               # here, rather than creating a new class for each one which
+               # is essentially just a clone of this one.
+               if ( isset( $this->mParams['type'] ) ) {
+                       switch ( $this->mParams['type'] ) {
+                               case 'int':
+                                       $type = 'number';
+                                       break;
+                               case 'float':
+                                       $type = 'number';
+                                       $attribs['step'] = 'any';
+                                       break;
+                               # Pass through
+                               case 'email':
+                               case 'password':
+                               case 'file':
+                               case 'url':
+                                       $type = $this->mParams['type'];
+                                       break;
+                       }
+               }
+
+               return $type;
+       }
+
+       function getInputOOUI( $value ) {
+               if ( !$this->isPersistent() ) {
+                       $value = '';
+               }
+
+               $attribs = $this->getTooltipAndAccessKey();
+
+               if ( $this->mClass !== '' ) {
+                       $attribs['classes'] = [ $this->mClass ];
+               }
+               if ( $this->mPlaceholder !== '' ) {
+                       $attribs['placeholder'] = $this->mPlaceholder;
+               }
+
+               # @todo Enforce pattern, step, required, readonly on the server side as
+               # well
+               $allowedParams = [
+                       'autofocus',
+                       'autosize',
+                       'disabled',
+                       'flags',
+                       'indicator',
+                       'maxlength',
+                       'readonly',
+                       'required',
+                       'tabindex',
+                       'type',
+               ];
+
+               $attribs += OOUI\Element::configFromHtmlAttributes(
+                       $this->getAttributes( $allowedParams )
+               );
+
+               $type = $this->getType( $attribs );
+
+               return $this->getInputWidget( [
+                       'id' => $this->mID,
+                       'name' => $this->mName,
+                       'value' => $value,
+                       'type' => $type,
+               ] + $attribs );
+       }
+
+       protected function getInputWidget( $params ) {
+               return new OOUI\TextInputWidget( $params );
+       }
+
+       /**
+        * Returns an array of data-* attributes to add to the field.
+        *
+        * @return array
+        */
+       protected function getDataAttribs() {
+               return [];
+       }
+}
diff --git a/includes/htmlform/fields/HTMLTextFieldWithButton.php b/includes/htmlform/fields/HTMLTextFieldWithButton.php
new file mode 100644 (file)
index 0000000..7c1c673
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Creates a text input field with a button assigned to the input field.
+ */
+class HTMLTextFieldWithButton extends HTMLTextField {
+       /** @var HTMLFormFieldWithButton $mClassWithButton */
+       protected $mClassWithButton = null;
+
+       public function __construct( $info ) {
+               $this->mClassWithButton = new HTMLFormFieldWithButton( $info );
+               parent::__construct( $info );
+       }
+
+       public function getInputHTML( $value ) {
+               return $this->mClassWithButton->getElement( parent::getInputHTML( $value ) );
+       }
+}
diff --git a/includes/htmlform/fields/HTMLTitleTextField.php b/includes/htmlform/fields/HTMLTitleTextField.php
new file mode 100644 (file)
index 0000000..a15b90e
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+
+use MediaWiki\Widget\TitleInputWidget;
+
+/**
+ * Implements a text input field for page titles.
+ * Automatically does validation that the title is valid,
+ * as well as autocompletion if using the OOUI display format.
+ *
+ * Note: Forms using GET requests will need to make sure the title value is not
+ * an empty string.
+ *
+ * Optional parameters:
+ * 'namespace' - Namespace the page must be in
+ * 'relative' - If true and 'namespace' given, strip/add the namespace from/to the title as needed
+ * 'creatable' - Whether to validate the title is creatable (not a special page)
+ * 'exists' - Whether to validate that the title already exists
+ *
+ * @since 1.26
+ */
+class HTMLTitleTextField extends HTMLTextField {
+       public function __construct( $params ) {
+               $params += [
+                       'namespace' => false,
+                       'relative' => false,
+                       'creatable' => false,
+                       'exists' => false,
+               ];
+
+               parent::__construct( $params );
+       }
+
+       public function validate( $value, $alldata ) {
+               if ( $this->mParent->getMethod() === 'get' && $value === '' ) {
+                       // If the form is a GET form and has no value, assume it hasn't been
+                       // submitted yet, and skip validation
+                       return parent::validate( $value, $alldata );
+               }
+               try {
+                       if ( !$this->mParams['relative'] ) {
+                               $title = Title::newFromTextThrow( $value );
+                       } else {
+                               // Can't use Title::makeTitleSafe(), because it doesn't throw useful exceptions
+                               global $wgContLang;
+                               $namespaceName = $wgContLang->getNsText( $this->mParams['namespace'] );
+                               $title = Title::newFromTextThrow( $namespaceName . ':' . $value );
+                       }
+               } catch ( MalformedTitleException $e ) {
+                       $msg = $this->msg( $e->getErrorMessage() );
+                       $params = $e->getErrorMessageParameters();
+                       if ( $params ) {
+                               $msg->params( $params );
+                       }
+                       return $msg->parse();
+               }
+
+               $text = $title->getPrefixedText();
+               if ( $this->mParams['namespace'] !== false &&
+                       !$title->inNamespace( $this->mParams['namespace'] )
+               ) {
+                       return $this->msg( 'htmlform-title-badnamespace', $this->mParams['namespace'], $text )->parse();
+               }
+
+               if ( $this->mParams['creatable'] && !$title->canExist() ) {
+                       return $this->msg( 'htmlform-title-not-creatable', $text )->escaped();
+               }
+
+               if ( $this->mParams['exists'] && !$title->exists() ) {
+                       return $this->msg( 'htmlform-title-not-exists', $text )->parse();
+               }
+
+               return parent::validate( $value, $alldata );
+       }
+
+       protected function getInputWidget( $params ) {
+               if ( $this->mParams['namespace'] !== false ) {
+                       $params['namespace'] = $this->mParams['namespace'];
+               }
+               $params['relative'] = $this->mParams['relative'];
+               return new TitleInputWidget( $params );
+       }
+
+       protected function shouldInfuseOOUI() {
+               return true;
+       }
+
+       protected function getOOUIModules() {
+               // FIXME: TitleInputWidget should be in its own module
+               return [ 'mediawiki.widgets' ];
+       }
+
+       public function getInputHtml( $value ) {
+               // add mw-searchInput class to enable search suggestions for non-OOUI, too
+               $this->mClass .= 'mw-searchInput';
+
+               // return the HTMLTextField html
+               return parent::getInputHTML( $value );
+       }
+
+       protected function getDataAttribs() {
+               return [
+                       'data-mw-searchsuggest' => FormatJson::encode( [
+                               'wrapAsLink' => false,
+                       ] ),
+               ];
+       }
+}
diff --git a/includes/htmlform/fields/HTMLUserTextField.php b/includes/htmlform/fields/HTMLUserTextField.php
new file mode 100644 (file)
index 0000000..14b5e59
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+use MediaWiki\Widget\UserInputWidget;
+
+/**
+ * Implements a text input field for user names.
+ * Automatically auto-completes if using the OOUI display format.
+ *
+ * FIXME: Does not work for forms that support GET requests.
+ *
+ * Optional parameters:
+ * 'exists' - Whether to validate that the user already exists
+ *
+ * @since 1.26
+ */
+class HTMLUserTextField extends HTMLTextField {
+       public function __construct( $params ) {
+               $params += [
+                       'exists' => false,
+                       'ipallowed' => false,
+               ];
+
+               parent::__construct( $params );
+       }
+
+       public function validate( $value, $alldata ) {
+               // check, if a user exists with the given username
+               $user = User::newFromName( $value, false );
+
+               if ( !$user ) {
+                       return $this->msg( 'htmlform-user-not-valid', $value )->parse();
+               } elseif (
+                       ( $this->mParams['exists'] && $user->getId() === 0 ) &&
+                       !( $this->mParams['ipallowed'] && User::isIP( $value ) )
+               ) {
+                       return $this->msg( 'htmlform-user-not-exists', $user->getName() )->parse();
+               }
+
+               return parent::validate( $value, $alldata );
+       }
+
+       protected function getInputWidget( $params ) {
+               return new UserInputWidget( $params );
+       }
+
+       protected function shouldInfuseOOUI() {
+               return true;
+       }
+
+       protected function getOOUIModules() {
+               return [ 'mediawiki.widgets.UserInputWidget' ];
+       }
+
+       public function getInputHtml( $value ) {
+               // add the required module and css class for user suggestions in non-OOUI mode
+               $this->mParent->getOutput()->addModules( 'mediawiki.userSuggest' );
+               $this->mClass .= ' mw-autocomplete-user';
+
+               // return parent html
+               return parent::getInputHTML( $value );
+       }
+}
index 259d514..d1a9bc5 100644 (file)
@@ -332,8 +332,7 @@ class WikiImporter {
                }
 
                try {
-                       $dbw = wfGetDB( DB_MASTER );
-                       return $dbw->deadlockLoop( [ $revision, 'importOldRevision' ] );
+                       return $revision->importOldRevision();
                } catch ( MWContentSerializationException $ex ) {
                        $this->notice( 'import-error-unserialize',
                                $revision->getTitle()->getPrefixedText(),
@@ -351,8 +350,7 @@ class WikiImporter {
         * @return bool
         */
        public function importLogItem( $revision ) {
-               $dbw = wfGetDB( DB_MASTER );
-               return $dbw->deadlockLoop( [ $revision, 'importLogItem' ] );
+               return $revision->importLogItem();
        }
 
        /**
@@ -361,8 +359,7 @@ class WikiImporter {
         * @return bool
         */
        public function importUpload( $revision ) {
-               $dbw = wfGetDB( DB_MASTER );
-               return $dbw->deadlockLoop( [ $revision, 'importUpload' ] );
+               return $revision->importUpload();
        }
 
        /**
@@ -380,7 +377,7 @@ class WikiImporter {
                // Update article count statistics (T42009)
                // The normal counting logic in WikiPage->doEditUpdates() is designed for
                // one-revision-at-a-time editing, not bulk imports. In this situation it
-               // suffers from issues of slave lag. We let WikiPage handle the total page
+               // suffers from issues of replica DB lag. We let WikiPage handle the total page
                // and revision count, and we implement our own custom logic for the
                // article (content page) count.
                $page = WikiPage::factory( $title );
@@ -840,7 +837,7 @@ class WikiImporter {
                                'text',
                                ''
                        ] ) ) &&
-                       (int)( strlen( $revisionInfo['text'] ) / 1024 ) > $wgMaxArticleSize
+                       strlen( $revisionInfo['text'] ) > $wgMaxArticleSize * 1024
                ) {
                        throw new MWException( 'The text of ' .
                                ( isset( $revisionInfo['id'] ) ?
index 356a79f..d78d61a 100644 (file)
@@ -574,7 +574,7 @@ class WikiRevision {
                if ( !$this->getTitle() ) {
                        wfDebug( __METHOD__ . ": skipping invalid {$this->type}/{$this->action} log time, timestamp " .
                                $this->timestamp . "\n" );
-                       return;
+                       return false;
                }
                # Check if it exists already
                // @todo FIXME: Use original log ID (better for backups)
@@ -594,7 +594,7 @@ class WikiRevision {
                        wfDebug( __METHOD__
                                . ": skipping existing item for Log:{$this->type}/{$this->action}, timestamp "
                                . $this->timestamp . "\n" );
-                       return;
+                       return false;
                }
                $log_id = $dbw->nextSequenceValue( 'logging_log_id_seq' );
                $data = [
@@ -610,6 +610,8 @@ class WikiRevision {
                        'log_params' => $this->params
                ];
                $dbw->insert( 'logging', $data, __METHOD__ );
+
+               return true;
        }
 
        /**
index 6a20abc..86b2f3b 100644 (file)
@@ -75,6 +75,7 @@ abstract class DatabaseUpdater {
                PopulateFilearchiveSha1::class,
                PopulateBacklinkNamespace::class,
                FixDefaultJsonContentPages::class,
+               CleanupEmptyCategories::class,
        ];
 
        /**
@@ -409,7 +410,6 @@ abstract class DatabaseUpdater {
        public function doUpdates( $what = [ 'core', 'extensions', 'stats' ] ) {
                global $wgVersion;
 
-               $this->db->begin( __METHOD__ );
                $what = array_flip( $what );
                $this->skipSchema = isset( $what['noschema'] ) || $this->fileHandle !== null;
                if ( isset( $what['core'] ) ) {
@@ -431,8 +431,6 @@ abstract class DatabaseUpdater {
                        $this->writeSchemaUpdateFile();
                        $this->setAppliedUpdates( "$wgVersion-schema", $this->updatesSkipped );
                }
-
-               $this->db->commit( __METHOD__ );
        }
 
        /**
index 4d5aa7a..eafb9d4 100644 (file)
@@ -180,6 +180,7 @@ abstract class Installer {
                'wgUseInstantCommons',
                'wgUpgradeKey',
                'wgDefaultSkin',
+               'wgPingback',
        ];
 
        /**
@@ -293,10 +294,6 @@ abstract class Installer {
                        'url' => 'https://creativecommons.org/publicdomain/zero/1.0/',
                        'icon' => '$wgResourceBasePath/resources/assets/licenses/cc-0.png',
                ],
-               'pd' => [
-                       'url' => '',
-                       'icon' => '$wgResourceBasePath/resources/assets/licenses/public-domain.png',
-               ],
                'gfdl' => [
                        'url' => 'https://www.gnu.org/copyleft/fdl.html',
                        'icon' => '$wgResourceBasePath/resources/assets/licenses/gnu-fdl.png',
@@ -1440,10 +1437,10 @@ abstract class Installer {
 
        /**
         * Get an array of install steps. Should always be in the format of
-        * array(
+        * [
         *   'name'     => 'someuniquename',
-        *   'callback' => array( $obj, 'method' ),
-        * )
+        *   'callback' => [ $obj, 'method' ],
+        * ]
         * There must be a config-install-$name message defined per step, which will
         * be shown on install.
         *
@@ -1727,7 +1724,7 @@ abstract class Installer {
         * Add an installation step following the given step.
         *
         * @param callable $callback A valid installation callback array, in this form:
-        *    array( 'name' => 'some-unique-name', 'callback' => array( $obj, 'function' ) );
+        *    [ 'name' => 'some-unique-name', 'callback' => [ $obj, 'function' ] ];
         * @param string $findStep The step to find. Omit to put the step at the beginning
         */
        public function addInstallStep( $callback, $findStep = 'BEGINNING' ) {
index ced7b93..a9e3e85 100644 (file)
@@ -64,7 +64,7 @@ class LocalSettingsGenerator {
                                'wgRightsText', '_MainCacheType', 'wgEnableUploads',
                                '_MemCachedServers', 'wgDBserver', 'wgDBuser',
                                'wgDBpassword', 'wgUseInstantCommons', 'wgUpgradeKey', 'wgDefaultSkin',
-                               'wgMetaNamespace', 'wgLogo', 'wgAuthenticationTokenVersion',
+                               'wgMetaNamespace', 'wgLogo', 'wgAuthenticationTokenVersion', 'wgPingback',
                        ],
                        $db->getGlobalNames()
                );
@@ -72,7 +72,8 @@ class LocalSettingsGenerator {
                $unescaped = [ 'wgRightsIcon', 'wgLogo' ];
                $boolItems = [
                        'wgEnableEmail', 'wgEnableUserEmail', 'wgEnotifUserTalk',
-                       'wgEnotifWatchlist', 'wgEmailAuthentication', 'wgEnableUploads', 'wgUseInstantCommons'
+                       'wgEnotifWatchlist', 'wgEmailAuthentication', 'wgEnableUploads', 'wgUseInstantCommons',
+                       'wgPingback',
                ];
 
                foreach ( $confItems as $c ) {
@@ -97,7 +98,7 @@ class LocalSettingsGenerator {
         * For $wgGroupPermissions, set a given ['group']['permission'] value.
         * @param string $group Group name
         * @param array $rightsArr An array of permissions, in the form of:
-        *   array( 'right' => true, 'right2' => false )
+        *   [ 'right' => true, 'right2' => false ]
         */
        public function setGroupRights( $group, $rightsArr ) {
                $this->groupPermissions[$group] = $rightsArr;
@@ -372,6 +373,11 @@ ${serverSetting}
 # InstantCommons allows wiki to use images from https://commons.wikimedia.org
 \$wgUseInstantCommons = {$this->values['wgUseInstantCommons']};
 
+# Periodically send a pingback to https://www.mediawiki.org/ with basic data
+# about this MediaWiki instance. The Wikimedia Foundation shares this data
+# with MediaWiki developers to help guide future development efforts.
+\$wgPingback = {$this->values['wgPingback']};
+
 ## If you use ImageMagick (or any other shell command) on a
 ## Linux server, this will need to be set to the name of an
 ## available UTF-8 locale
index accc42f..770d3bf 100644 (file)
@@ -87,7 +87,11 @@ class MssqlUpdater extends DatabaseUpdater {
                        [ 'updateSchema', 'recentchanges', 'recentchanges-drop-fks',
                                'patch-recentchanges-drop-fks.sql' ],
                        [ 'updateSchema', 'logging', 'logging-drop-fks', 'patch-logging-drop-fks.sql' ],
-                       [ 'updateSchema', 'archive', 'archive-drop-fks', 'patch-archive-drop-fks.sql' ]
+                       [ 'updateSchema', 'archive', 'archive-drop-fks', 'patch-archive-drop-fks.sql' ],
+
+                       // 1.28
+                       [ 'addIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
+                               'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
                ];
        }
 
index d414d90..65af086 100644 (file)
@@ -155,7 +155,6 @@ class MysqlUpdater extends DatabaseUpdater {
                        [ 'addField', 'ipblocks', 'ipb_allow_usertalk', 'patch-ipb_allow_usertalk.sql' ],
 
                        // 1.15
-                       [ 'doUniquePlTlIl' ],
                        [ 'addTable', 'change_tag', 'patch-change_tag.sql' ],
                        [ 'addTable', 'tag_summary', 'patch-tag_summary.sql' ],
                        [ 'addTable', 'valid_tag', 'patch-valid_tag.sql' ],
@@ -283,6 +282,12 @@ class MysqlUpdater extends DatabaseUpdater {
                        [ 'addIndex', 'categorylinks', 'cl_collation_ext',
                                'patch-add-cl_collation_ext_index.sql' ],
                        [ 'doCollationUpdate' ],
+
+                       // 1.28
+                       [ 'addIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
+                               'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
+                       [ 'doRevisionPageRevIndexNonUnique' ],
+                       [ 'doNonUniquePlTlIl' ],
                ];
        }
 
@@ -969,24 +974,24 @@ class MysqlUpdater extends DatabaseUpdater {
                return true;
        }
 
-       protected function doUniquePlTlIl() {
+       protected function doNonUniquePlTlIl() {
                $info = $this->db->indexInfo( 'pagelinks', 'pl_namespace' );
-               if ( is_array( $info ) && !$info[0]->Non_unique ) {
-                       $this->output( "...pl_namespace, tl_namespace, il_to indices are already UNIQUE.\n" );
+               if ( is_array( $info ) && $info[0]->Non_unique ) {
+                       $this->output( "...pl_namespace, tl_namespace, il_to indices are already non-UNIQUE.\n" );
 
                        return true;
                }
                if ( $this->skipSchema ) {
                        $this->output( "...skipping schema change (making pl_namespace, tl_namespace " .
-                               "and il_to indices UNIQUE).\n" );
+                               "and il_to indices non-UNIQUE).\n" );
 
                        return false;
                }
 
                return $this->applyPatch(
-                       'patch-pl-tl-il-unique.sql',
+                       'patch-pl-tl-il-nonunique.sql',
                        false,
-                       'Making pl_namespace, tl_namespace and il_to indices UNIQUE'
+                       'Making pl_namespace, tl_namespace and il_to indices non-UNIQUE'
                );
        }
 
@@ -1097,4 +1102,24 @@ class MysqlUpdater extends DatabaseUpdater {
                        'Making user_id unsigned int'
                );
        }
+
+       protected function doRevisionPageRevIndexNonUnique() {
+               if ( !$this->doTable( 'revision' ) ) {
+                       return true;
+               } elseif ( !$this->db->indexExists( 'revision', 'rev_page_id' ) ) {
+                       $this->output( "...rev_page_id index not found on revision.\n" );
+                       return true;
+               }
+
+               if ( !$this->db->indexUnique( 'revision', 'rev_page_id' ) ) {
+                       $this->output( "...rev_page_id index already non-unique.\n" );
+                       return true;
+               }
+
+               return $this->applyPatch(
+                       'patch-revision-page-rev-index-nonunique.sql',
+                       false,
+                       'Making rev_page_id index non-unique'
+               );
+       }
 }
index 334256b..8075aac 100644 (file)
@@ -113,6 +113,10 @@ class OracleUpdater extends DatabaseUpdater {
                        [ 'dropTable', 'msg_resource' ],
                        [ 'addField', 'watchlist', 'wl_id', 'patch-watchlist-wl_id.sql' ],
 
+                       // 1.28
+                       [ 'addIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
+                               'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
+
                        // KEEP THIS AT THE BOTTOM!!
                        [ 'doRebuildDuplicateFunction' ],
 
index a3b50ac..be94d91 100644 (file)
@@ -433,6 +433,10 @@ class PostgresUpdater extends DatabaseUpdater {
                                'addPgField', 'watchlist', 'wl_id',
                                "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('watchlist_wl_id_seq')"
                        ],
+
+                       // 1.28
+                       [ 'addPgIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
+                               '( rc_namespace, rc_type, rc_patrolled, rc_timestamp )' ],
                ];
        }
 
index 86dccd7..1c6e6eb 100644 (file)
@@ -152,6 +152,10 @@ class SqliteUpdater extends DatabaseUpdater {
                        [ 'addIndex', 'categorylinks', 'cl_collation_ext',
                                'patch-add-cl_collation_ext_index.sql' ],
                        [ 'doCollationUpdate' ],
+
+                       // 1.28
+                       [ 'addIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
+                               'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
                ];
        }
 
index dcd30cf..e6deed5 100644 (file)
@@ -50,6 +50,11 @@ class WebInstallerName extends WebInstallerPage {
                        wfMessage( 'config-ns-other-default' )->inContentLanguage()->text()
                );
 
+               $pingbackInfo = ( new Pingback() )->getSystemInfo();
+               // Database isn't available in config yet, so take it
+               // from the installer
+               $pingbackInfo['database'] = $this->getVar( 'wgDBtype' );
+
                $this->addHTML(
                        $this->parent->getTextBox( [
                                'var' => 'wgSitename',
@@ -100,6 +105,15 @@ class WebInstallerName extends WebInstallerPage {
                                'label' => 'config-subscribe',
                                'help' => $this->parent->getHelpBox( 'config-subscribe-help' )
                        ] ) .
+                       $this->parent->getCheckBox( [
+                               'var' => 'wgPingback',
+                               'label' => 'config-pingback',
+                               'help' => $this->parent->getHelpBox(
+                                       'config-pingback-help',
+                                       FormatJson::encode( $pingbackInfo, true )
+                               ),
+                               'value' => true,
+                       ] ) .
                        $this->getFieldsetEnd() .
                        $this->parent->getInfoBox( wfMessage( 'config-almost-done' )->text() ) .
                        // getRadioSet() builds a set of labeled radio buttons.
@@ -129,7 +143,7 @@ class WebInstallerName extends WebInstallerPage {
                $retVal = true;
                $this->parent->setVarsFromRequest( [ 'wgSitename', '_NamespaceType',
                        '_AdminName', '_AdminPassword', '_AdminPasswordConfirm', '_AdminEmail',
-                       '_Subscribe', '_SkipOptional', 'wgMetaNamespace' ] );
+                       '_Subscribe', '_SkipOptional', 'wgMetaNamespace', 'wgPingback' ] );
 
                // Validate site name
                if ( strval( $this->getVar( 'wgSitename' ) ) === '' ) {
index f8dc8ee..62fe785 100644 (file)
@@ -267,6 +267,7 @@ class WebInstallerOutput {
                }
 ?>
 <?php echo Html::htmlHeader( $this->getHeadAttribs() ); ?>
+
 <head>
        <meta name="robots" content="noindex, nofollow" />
        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
index ea96cfe..cd9574c 100644 (file)
@@ -3,7 +3,8 @@
                "authors": [
                        "Xuacu",
                        "Fitoschido",
-                       "Enolp"
+                       "Enolp",
+                       "Crucifunked"
                ]
        },
        "config-desc": "L'instalador pa MediaWiki",
        "config-outdated-sqlite": "'''Avisu:''' tien SQLite $1, que ye inferior a la versión mínima necesaria $2. SQLite nun tará disponible.",
        "config-no-fts3": "'''Avisu:''' SQLite ta compiláu ensin el [//sqlite.org/fts3.html módulu FTS3]; les funciones de gueta nun tarán disponibles nesti sistema.",
        "config-pcre-old": "<strong>Fatal:</strong> Ríquese PCRE $1 o posterior.\nEl binariu de PHP ta enllazáu con PCRE $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE Más información].",
+       "config-pcre-no-utf8": "<strong>Erru fatal:</strong> Paez que'l módulu PCRE de PHP foi compiláu ensin el soporte PCRE_UTF8.\nMediaWiki requier compatibilidá con UTF_8 pa furrular correutamente.",
+       "config-memory-raised": "El parámetru <code>memory_limit</code> de PHP ye $1. Auméntase a $2.",
+       "config-memory-bad": "<strong>Alvertencia:</strong>: el parámetru <code>memory_limit</code> de PHP ye $1.\nProbablemente sía demasiáu baxu.\n¡La instalación puede fallar!",
+       "config-xcache": "[http://xcache.lighttpd.net/ XCache] ta instaláu",
        "config-apc": "[http://www.php.net/apc APC] ta instaláu",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] ta instaláu",
+       "config-no-cache-apcu": "<strong>Warning:</strong> Non pudo atopase[http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] o [http://www.iis.net/download/WinCacheForPhp WinCache].\nEl caxé d'oxetos nun ta activáu.",
+       "config-mod-security": "<strong>Alvertencia:</strong> El to servidor web tien activáu [http://modsecurity.org/mod_security]/mod_security2 .Munches de les sos configuraciones comunes pueden causar problemes a MediaWiki o otru software que dexe a los usuarios publicar conteníu arbitrario. De ser posible, tendríes de desactivalo. Si non, consulta la  [http://modsecurity.org/documentation/ mod_security documentation] o contacta col alministrador del to servidor si atopes erros aleatorios.",
        "config-diff3-bad": "Nun s'alcontró GNU diff3.",
        "config-git": "Alcontróse'l software de control de versiones Git: <code>$1</code>.",
        "config-git-bad": "Nun s'alcontró el software de control de versiones Git.",
+       "config-imagemagick": "ImageMagick atopáu: <code>$1</code>.\nLa miniaturización d'imaxes habilitaráse si habilites les cargues.",
+       "config-gd": "Atopóse una biblioteca de gráficos GD integrada.\nLa miniaturización d'imaxes habilitaráse si habilites les xubíes.",
+       "config-no-scaling": "Nun s'atopó la biblioteca GD o ImageMagik.\nVa desactivase la miniaturización d'imaxes.",
+       "config-no-uri": "<strong>Erru:</strong> non pudo determinase el URI actual. Atayóse la instalación.",
+       "config-no-cli-uri": "<strong>Alvertencia:</strong> Nun s'especificó  <code>--scriptpath</code>, úsase'l valor predetermináu <code>$1</code>.",
+       "config-using-server": "Utilizando'l nome de servidor \"<nowiki>$1</nowiki>\".",
+       "config-using-uri": "Utilizando la URL del servidor \"<nowiki>$1$2</nowiki>\".",
+       "config-uploads-not-safe": "<strong>Alvertencia:</strong> El to directoriu predetermináu pa les cargues <code>$1</code> ye vulnerable a la execución de scripts arbitrarios.\nAnque MediaWiki comprueba tolos archivos cargaos por si hubiera amenaces de seguridá, ye altamente recomendable [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security close this security vulnerability] enantes d'activar les cargues.",
+       "config-no-cli-uploads-check": "<strong>Alvertencia:</strong> el to directoriu predetermináu pa cargues <code>$1</code> nun tá comprobáu contra la vulnerabilidá d'execución arbitraria de scripts mientres la instalación per llínea de comandos.",
+       "config-brokenlibxml": "El sistema tien una combinación de versiones de PHP y de libxml2 que ye pocu confiable y puede provocar corrupción oculta nos datos de MediaWiki y otres aplicaciones web. Actualiza a libxml2 2.7.3 o posterior ([https://bugs.php.net/bug.php?díi=45996 bug reportáu con PHP]). Instalación albortada.",
+       "config-suhosin-max-value-length": "Suhosin ta instaláu y llinda el parámetru <code>length</code> GET a $1 bytes.\nEl componente ResourceLoader (xestor de recursos) de MediaWiki va trabayar nesta llende, pero eso va perxudicar el rendimientu.\nSi ye posible, tendríes d'establecer <code>suhosin.get.max_value_length</code> nel valor 1024 o superior en <code>php.ini</code> y establecer <code>$wgResourceLoaderMaxQueryLength</code> nel mesmu valor en <code>LocalSettings.php</code>.",
        "config-db-type": "Tipu de base de datos:",
+       "config-db-host": "Servidor de la base de datos:",
+       "config-db-host-help": "Si'l to servidor de base de datos ta n'otru servidor, escribe'l nome del equipu o la so dirección IP equí.\n\nSi tas utilizando alojamiento web compartíu, el to provisor tendría de date'l nome correctu del servidor na so documentación.\n\nSi vas instalar nun servidor Windows y a utilizar MySQL, l'usu de \"localhost\" como nome del servidor puede nun #funcionar. Si ye asina, intenta poner \"127.0.0.1\" como dirección IP local.\n\nSi utilices PostgreSQL, dexa esti campu vacío pa conectase al traviés d'un socket de Unix.",
+       "config-db-host-oracle": "TNS de la base de datos:",
+       "config-db-host-oracle-help": "Escribe un [http://download.oracle.com/docs/cd/B28359_01/network.111/b28317/tnsnames.htm nome de conexón local] válidu; un archivu tnsnames.ora ten de ser visible pa esta instalación.<br />Si tas utilizando biblioteques de veceru 10g o más recién tamién puedes utilizar el métodu de asignación de nomes [http://download.oracle.com/docs/cd/Y11882_01/network.112/y10836/naming.htm Easy Connect].",
+       "config-db-wiki-settings": "Identifica esta wiki",
        "config-db-name": "Nome de base de datos:",
+       "config-db-name-help": "Escueye un nome qu'identifique la to wiki. Nun tien de contener espacios. \nSi tas utilizando agospiamientu web compartíu, el to provisor va date un nome específicu de base de datos por que lu utilices, o bien va dexate crear bases de datos al traviés d'un panel de control.",
+       "config-db-name-oracle": "Esquema de la base de datos:",
        "config-db-install-account": "Cuenta d'usuariu pa la instalación",
        "config-db-username": "Nome d'usuariu de base de datos:",
        "config-db-password": "Contraseña de base de datos:",
index a1fcfab..606b97b 100644 (file)
@@ -45,7 +45,7 @@
        "config-page-restart": "Урынлаштырыуҙы яңынан башларға",
        "config-page-readme": "Мине уҡы",
        "config-page-releasenotes": "Өлгө тураһында мәғлүмәт",
-       "config-page-copying": "Рөхсәтнәмә",
+       "config-page-copying": "Рөхсәтнамә",
        "config-page-upgradedoc": "Яңыртыу",
        "config-page-existingwiki": "Ғәмәлдәге вики",
        "config-help-restart": "Һеҙ үҙегеҙ индергән һәм  һаҡланған әлеге мәғлүмәттәрҙе юйып, урынлаштырыуҙың яңы процессын ебәрергә теләйһегеҙме?",
index 7dc8cd9..77cd689 100644 (file)
        "config-subscribe": "Падпісацца на [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce сьпіс распаўсюджаньня навінаў пра зьяўленьне новых вэрсіяў].",
        "config-subscribe-help": "Гэта ня вельмі актыўны сьпіс распаўсюджаньня навінаў пра зьяўленьне новых вэрсіяў, які ўключаючы важныя навіны пра бясьпеку.\nВам неабходна падпісацца на яго і абнавіць Вашае ўсталяваньне MediaWiki, калі зьявяцца новыя вэрсіі.",
        "config-subscribe-noemail": "Вы спрабавалі падпісацца на рассылку паведамленьняў пра выхад новых вэрсіяў, не пазначыўшы адрас электроннай пошты.\nКалі ласка, падайце слушны адрас, калі Вы жадаеце падпісацца на рассылку.",
+       "config-pingback": "Дзяліцца зьвесткамі пра гэтую ўсталёўку з распрацоўнікамі MediaWiki.",
+       "config-pingback-help": "Калі вы абярэце гэтую наладу, MediaWiki будзе час ад часу дасылаць базавыя зьвесткі пра гэтую ўсталёўку на https://www.mediawiki.org. Гэтыя зьвесткі ўключаюць, напрыклад, тып сыстэмы, вэрсію PHP і абраную базу зьвестак. Фундацыя «Вікімэдыя» дзеліцца гэтымі зьвесткамі з распрацоўнікамі MediaWiki, каб скіраваць далейшыя шляхі распрацоўкі. Наступныя зьвесткі будуць дасланыя для вашай сыстэмы:\n<pre>$1</pre>",
        "config-almost-done": "Вы амаль што скончылі!\nАстатнія налады можна прапусьціць і пачаць усталяваньне вікі.",
        "config-optional-continue": "Задаць болей пытаньняў.",
        "config-optional-skip": "Хопіць, проста ўсталяваць вікі.",
        "config-install-extension-tables": "Стварэньне табліцаў для ўключаных пашырэньняў",
        "config-install-mainpage-failed": "Немагчыма ўставіць галоўную старонку: $1",
        "config-install-done": "<strong>Віншуем!</strong>\nВы ўсталявалі MediaWiki.\n\nПраграма ўсталяваньня стварыла файл <code>LocalSettings.php</code>.\nЁн утрымлівае ўсе Вашыя налады.\n\nВам неабходна загрузіць яго і захаваць у карэнную дырэкторыю Вашай вікі (у тую ж самую дырэкторыю, дзе знаходзіцца index.php). Загрузка павінна пачацца аўтаматычна.\n\nКалі загрузка не пачалася, ці Вы яе адмянілі, Вы можаце перазапусьціць яе націснуўшы на спасылку ніжэй:\n\n$3\n\n<strong>Заўвага</strong>: калі Вы гэтага ня зробіце зараз, то створаны файл ня будзе даступны Вам потым, калі Вы выйдзеце з праграмы ўсталяваньня безь яго загрузкі.\n\nКалі Вы гэта зробіце, Вы можаце <strong>[$2 ўвайсьці ў Вашую вікі]</strong>.",
+       "config-install-done-path": "<strong>Віншуем!</strong>\nВы ўсталявалі MediaWiki.\n\nПраграма ўсталёўкі стварыла файл <code>LocalSettings.php</code>. Ён утрымлівае ўсе вашыя налады.\n\nВам трэба спампаваць яго і пакласьці ў <code>$4</code>. Спампоўка павінна пачацца аўтаматычна.\n\nКалі спампоўка не пачалася або вы адмянілі яе, вы можаце пачаць яе наноў, калі націсьніце на наступную спасылку:\n\n$3\n\n<strong>Заўвага:</strong> Калі вы ня зробіце гэта зараз, то створаны файл ня будзе даступны вам па выхадзе з праграмы безь яго спампоўкі.\n\nКалі вы зробіце гэта, вы можаце <strong>[$2 ўвайсьці ў вашую вікі]</strong>.",
        "config-download-localsettings": "Загрузіць <code>LocalSettings.php</code>",
        "config-help": "дапамога",
        "config-help-tooltip": "націсьніце, каб разгарнуць",
index cc31402..4e34c7d 100644 (file)
        "config-ns-site-name": "Същото като името на уикито: $1",
        "config-ns-other": "Друго (уточняване)",
        "config-ns-other-default": "МоетоУики",
-       "config-project-namespace-help": "Следвайки примера на Уикипедия, много уикита съхраняват страниците си с правила в \"'''именно пространство на проекта'''\", отделно от основното съдържание.\nВсички заглавия на страниците в това именно пространство започват с определена представка, която може да бъде зададена тук.\nОбикновено представката произлиза от името на уикито, но не може да съдържа символи като \"#\" или \":\".",
+       "config-project-namespace-help": "Следвайки примера на Уикипедия, много уикита съхраняват страниците си с правила в '''именно пространство на проекта''', отделно от основното съдържание.\nВсички заглавия на страниците в това именно пространство започват с определена представка, която може да бъде зададена тук.\nОбикновено представката произлиза от името на уикито, но не може да съдържа символи като \"#\" или \":\".",
        "config-ns-invalid": "Посоченото именно пространство \"<nowiki>$1</nowiki>\" е невалидно.\nНеобходимо е да бъде посочено друго.",
        "config-ns-conflict": "Посоченото именно пространство \"<nowiki>$1</nowiki>\" е в конфликт с използваното по подразбиране именно пространство MediaWiki.\nНеобходимо е да се посочи друго именно пространство.",
        "config-admin-box": "Администраторска сметка",
        "config-subscribe": "Абониране за [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce пощенския списък за нови версии].",
        "config-subscribe-help": "Това е пощенски списък с малко трафик, който се използва за съобщения при излизане на нови версии, както и за важни проблеми със сигурността.\nАбонирането е препоръчително, както и надграждането на инсталацията на МедияУики при излизането на нова версия.",
        "config-subscribe-noemail": "Опитахте да се абонирате за пощенския списък за нови версии без да посочите адрес за електронна поща.\nНеобходимо е да се предостави адрес за електронна поща, в случай че желаете да се абонирате за пощенския списък.",
+       "config-pingback": "Споделяне на данни за инсталацията с разработчиците на МедияУики.",
+       "config-pingback-help": "Ако изберете тази настройка, МедияУики периодично ще пингва https://www.mediawiki.org с основна информация за тази инсталация на МедияУики. Информацията включва например, тип на операционната система, версията на PHP и избраната СУБД. Фондация Уикимедия споделя тези данни с разработчиците на МедияУики, за да им помогне в бъдещото развитие на софтуера. Следните данни ще бъдат изпратени за вашата система:\n<pre>$1</pre>",
        "config-almost-done": "Инсталацията е почти готова!\nВъзможно е пропускане на оставащата конфигурация и моментално инсталиране на уикито.",
        "config-optional-continue": "Задаване на допълнителни въпроси.",
        "config-optional-skip": "Достатъчно, инсталиране на уикито.",
        "config-install-keys": "Генериране на тайни ключове",
        "config-insecure-keys": "'''Предупреждение:''' {{PLURAL:$2|Сигурният ключ, създаден по време на инсталацията, не е напълно надежден|Сигурните ключове, създадени по време на инсталацията, не са напълно надеждни}} $1 . Обмислете да {{PLURAL:$2|го|ги}} смените ръчно.",
        "config-install-updates": "Предотвратяване стартирането на ненужни актуализации",
+       "config-install-updates-failed": "<strong>Грешка:</strong> Вмъкването на обновяващи ключове в таблиците се провали по следната причина: $1",
        "config-install-sysop": "Създаване на администраторска сметка",
        "config-install-subscribe-fail": "Невъзможно беше абонирането за mediawiki-announce: $1",
        "config-install-subscribe-notpossible": "не е инсталиран cURL и <code>allow_url_fopen</code> не е налична.",
        "config-install-mainpage": "Създаване на Началната страница със съдържание по подразбиране",
        "config-install-extension-tables": "Създаване на таблици за включените разширения",
        "config-install-mainpage-failed": "Вмъкването на Началната страница беше невъзможно: $1",
-       "config-install-done": "'''Поздравления!'''\nИнсталирането на МедияУики приключи успешно.\n\nИнсталаторът създаде файл <code>LocalSettings.php</code>.\nТой съдържа всичката необходима основна конфигурация на уикито.\n\nНеобходимо е той да бъде изтеглен и поставен в основната директория на уикито (директорията, в която е и index.php). Изтеглянето би трябвало да започне автоматично.\n\nАко изтеглянето не започне автоматично или е било прекратено, файлът може да бъде изтеглен чрез щракване на препратката по-долу:\n\n$3\n\n'''Забележка''': Ако това не бъде извършено сега, генерираният конфигурационен файл няма да е достъпен на по-късен етап ако не бъде изтеглен сега или инсталацията приключи без изтеглянето му.\n\nКогато файлът вече е в основната директория, '''[$2 уикито ще е достъпно на този адрес]'''.",
+       "config-install-done": "<strong>Поздравления!</strong>\nИнсталирането на МедияУики приключи успешно.\n\nИнсталаторът създаде файл <code>LocalSettings.php</code>.\nТой съдържа всичката необходима основна конфигурация на уикито.\n\nНеобходимо е той да бъде изтеглен и поставен в основната директория на уикито (директорията, в която е и index.php). Изтеглянето би трябвало да започне автоматично.\n\nАко изтеглянето не започне автоматично или е било прекратено, файлът може да бъде изтеглен чрез щракване на препратката по-долу:\n\n$3\n\n<strong>Забележка:</strong> Ако това не бъде извършено сега, генерираният конфигурационен файл няма да е достъпен на по-късен етап ако не бъде изтеглен сега или инсталацията приключи без изтеглянето му.\n\nКогато файлът вече е в основната директория, <strong>[$2 уикито ще е достъпно на този адрес]</strong>.",
+       "config-install-done-path": "<strong>Поздравления!</strong>\nИнсталирането на МедияУики приключи успешно.\n\nИнсталаторът създаде файл <code>LocalSettings.php</code>.\nТой съдържа всички ваши настройки.\n\nНеобходимо е той да бъде изтеглен и поставен в <code>$4</code>. Изтеглянето би трябвало да започне автоматично.\n\nАко изтеглянето не започне автоматично или е било прекратено, файлът може да бъде изтеглен чрез щракване на препратката по-долу:\n\n$3\n\n<strong>Забележка:</strong> Ако това не бъде направено сега, генерираният конфигурационен файл няма да е достъпен на по-късен етап ако не бъде изтеглен сега или инсталацията приключи без изтеглянето му.\n\nКогато файлът вече е в основната директория, <strong>[$2 уикито ще е достъпно на този адрес]</strong>.",
        "config-download-localsettings": "Изтегляне на <code>LocalSettings.php</code>",
        "config-help": "помощ",
        "config-nofile": "Файлът „$1“ не може да бъде открит. Да не е бил изтрит?",
        "config-extension-link": "Знаете ли, че това уики поддържа [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions разширения]?\n\nМожете да разгледате [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category разширенията по категория] или [https://www.mediawiki.org/wiki/Extension_Matrix Матрицата на разширенията] за пълен списък на разширенията.",
-       "mainpagetext": "'''Уикито беше успешно инсталирано.'''",
+       "mainpagetext": "<strong>Уикито беше успешно инсталирано.</strong>",
        "mainpagedocfooter": "Разгледайте [https://meta.wikimedia.org/wiki/Help:Contents ръководството] за подробна информация относно използването на уики софтуера.\n\n== Първи стъпки ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Настройки за конфигуриране]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ ЧЗВ за МедияУики]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Пощенски списък относно нови версии на МедияУики]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Локализиране на МедияУики]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Научете как да се справяте със спама във вашето уики]"
 }
index dacc300..3c6d530 100644 (file)
@@ -14,7 +14,8 @@
        "config-information": "তথ্য",
        "config-localsettings-upgrade": "<code>LocalSettings.php</code> ফাইলটি মুছে ফেলা হয়েছে। এই ইন্সটলেশনটি আরো উন্নত করতে দয়া করে <code>$wgUpgradeKey</code> কোডটি বক্সে দিন। আপনি এটি <code>LocalSettings.php</code> -এ পাবেন।",
        "config-localsettings-key": "হালনাগাদ কি",
-       "config-localsettings-badkey": "আপনি যেই চাবিটি দিয়েছেন তা সঠিক নয়।",
+       "config-localsettings-badkey": "আপনি হালনাগাদের যেই চাবিটি দিয়েছেন তা সঠিক নয়।",
+       "config-upgrade-key-missing": "মিডিয়াউইকির একটি বিদ্যমান ইনস্টলেশন সনাক্ত করা হয়েছে। \nএই ইনস্টলেশন হালনাগাদ করার জন্য, দয়া করে নিম্নলিখিত লাইন আপনার <code>LocalSettings.php</code> -এর নিচে স্থাপন করুন:\n\n$1",
        "config-session-error": "সেশন শুরুতে ত্রুটি: $1",
        "config-your-language": "আপনার ভাষা:",
        "config-your-language-help": "ইন্সটল করা সময় ব্যবহারের জন্য ভাষা নির্বাচন করুন।",
@@ -94,6 +95,7 @@
        "config-admin-password-blank": "প্রশাসক অ্যাকাউন্টের জন্য পাসওয়ার্ড প্রবেশ করান।",
        "config-admin-password-mismatch": "আপনি যে দুটি পাসওয়ার্ড দিয়েছেন তারা পরস্পর মেলেনি।",
        "config-admin-email": "ইমেইল ঠিকানা:",
+       "config-admin-error-bademail": "আপনি একটি অবৈধ ইমেল ঠিকানা দিয়েছেন।",
        "config-optional-continue": "আরও প্রশ্ন জিজ্ঞেস করুন।",
        "config-optional-skip": "আমি ইতিমধ্যেই বিরক্ত হয়ে গেছি, উইকিটি ইন্সটল করো।",
        "config-profile": "ব্যবহারকারী অধিকার প্রোফাইল:",
index 5e24b04..68497c5 100644 (file)
        "config-subscribe": "Přihlásit se k odběru [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce e-mailové konference pro oznamování nových verzí].",
        "config-subscribe-help": "Tohle je e-mailová konference s nízkým provozem, na které se oznamují nové verze, včetně důležitých bezpečnostních oznámení.\nMěli byste se do ní přihlásit a při vydání nových verzí aktualizovat svou instalaci MediaWiki.",
        "config-subscribe-noemail": "Pokusili jste se přihlásit k odběru e-mailové konference pro oznamování nových verzí, aniž byste poskytli e-mailovou adresu.\nPokud se chcete přihlásit k odběru, zadejte e-mailovou adresu.",
+       "config-pingback": "Sdílet údaje o této instalaci s vývojáři MediaWiki.",
+       "config-pingback-help": "Pokud zaškrtnete tuto volbu, bude MediaWiki pravidelně zasílat základní údaje této instance MediaWiki na https://www.mediawiki.org. Tyto údaje zahrnují například typ systému, verzi PHP a zvolené databázové úložiště. Nadace Wikimedia sdílí tato data s vývojáři MediaWiki, aby pomohla směrovat budoucí rozvoj. Pro váš systém budou zaslány tyto údaje:\n<pre>$1</pre>",
        "config-almost-done": "Už jsme skoro hotovi!\nZbývající konfiguraci už můžete přeskočit a nainstalovat wiki hned teď.",
        "config-optional-continue": "Ptejte se mě dál.",
        "config-optional-skip": "Už mě to nudí, prostě nainstalujte wiki.",
index fd4456b..e704141 100644 (file)
        "config-db-web-account-same": "Dasselbe Datenbankkonto wie während des Installationsvorgangs verwenden",
        "config-db-web-create": "Sofern nicht bereits vorhanden, muss nun das Konto erstellt werden",
        "config-db-web-no-create-privs": "Das angegebene und für den Installationsvorgang vorgesehene Datenbankkonto verfügt nicht über ausreichend Berechtigungen, um ein weiteres Datenbankkonto zu erstellen.\nDas hier angegebene Datenbankkonto muss daher bereits vorhanden sein.",
-       "config-mysql-engine": "Speicher-Engine:",
+       "config-mysql-engine": "Datenbanksystem:",
        "config-mysql-innodb": "InnoDB",
        "config-mysql-myisam": "MyISAM",
-       "config-mysql-myisam-dep": "'''Warnung:''' Es wurde MyISAM als Speicher-Engine für MySQL ausgewählt, die aus folgenden Gründen nicht für den Einsatz mit MediaWiki empfohlen ist:\n* Sie unterstützt aufgrund von Tabellensperrungen kaum die nebenläufige Ausführung von Aktionen.\n* Sie ist anfälliger für Datenprobleme.\n* Sie wird von MediaWiki nicht immer adäquat unterstützt.\n\nSofern die vorhandene MySQL-Installation die Speicher-Engine InnoDB unterstützt, wird deren Verwendung eindringlich empfohlen.\nSofern sie sie nicht unterstützt, sollte eine entsprechende Aktualisierung nunmehr Erwägung gezogen werden.",
-       "config-mysql-only-myisam-dep": "'''Warnung:''' MyISAM ist die einzige verfügbare Speicher-Engine für MySQL auf diesem Rechner, und dies wird nicht für die Verwendung mit MediaWiki empfohlen, da sie\n* aufgrund von Tabellensperrungen kaum die nebenläufige Ausführung von Aktionen unterstützt,\n* anfälliger für Datenprobleme ist und\n* von MediaWiki nicht immer adäquat unterstützt wird.\n\nDeine MySQL-Installation unterstützt nicht InnoDB. Eventuell muss eine Aktualisierung durchgeführt werden.",
-       "config-mysql-engine-help": "'''InnoDB''' ist fast immer die bessere Wahl, da es gleichzeitige Zugriffe gut unterstützt.\n\n'''MyISAM''' ist in Einzelnutzerumgebungen sowie bei schreibgeschützten Wikis schneller.\nBei MyISAM-Datenbanken treten tendenziell häufiger Fehler auf als bei InnoDB-Datenbanken.",
+       "config-mysql-myisam-dep": "<strong>Warnung:</strong> Es wurde MyISAM als Speichersubsystem für das Datenbanksystem MySQL ausgewählt. Aus folgenden Gründen wird es nicht für den Einsatz mit MediaWiki empfohlen:\n* Es unterstützt aufgrund von Tabellensperrungen kaum die nebenläufige Ausführung von Aktionen.\n* Es ist anfälliger für Datenprobleme.\n* Es wird von MediaWiki nicht immer adäquat unterstützt.\n\nSofern die vorhandene MySQL-Installation das Speichersubsystem InnoDB unterstützt, wird deren Verwendung eindringlich empfohlen.\nSofern sie es nicht unterstützt, sollte nunmehr eine entsprechende Aktualisierung in Erwägung gezogen werden.",
+       "config-mysql-only-myisam-dep": "<strong>Warnung:</strong> MyISAM ist das einzige verfügbare Speichersubsystem für das Datenbanksystem MySQL auf diesem Server. Es wird nicht für die Verwendung mit MediaWiki empfohlen, da es\n* aufgrund von Tabellensperrungen kaum die nebenläufige Ausführung von Aktionen unterstützt,\n* anfälliger für Datenprobleme ist und\n* von MediaWiki nicht immer adäquat unterstützt wird.\n\nDeine MySQL-Installation unterstützt nicht das Speichersubsystem InnoDB. Eine Aktualisierung wird nunmehr empfohlen.",
+       "config-mysql-engine-help": "<strong>InnoDB</strong> als Speichersubsystem für das Datenbanksystem MySQL ist fast immer die bessere Wahl, da es gleichzeitige Zugriffe gut unterstützt.\n\n<strong>MyISAM</strong> als Speichersubsystem für das Datenbanksystem MySQL ist hingegen in Einzelnutzerumgebungen oder bei schreibgeschützten Wikis schneller.\nDatenbanken, die MyISAM verwenden, sind indes tendenziell fehleranfälliger als solche, die InnoDB verwenden.",
        "config-mysql-charset": "Datenbankzeichensatz:",
        "config-mysql-binary": "binär",
        "config-mysql-utf8": "UTF-8",
        "config-subscribe": "Bitte die Mailingliste [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Mitteilungen zu Versionsveröffentlichungen] abonnieren.",
        "config-subscribe-help": "Es handelt sich hierbei um eine Mailingliste mit wenigen Aussendungen, die für Mitteilungen zu Versionsveröffentlichungen, einschließlich wichtiger Sicherheitsveröffentlichungen, genutzt wird.\nDiese Mailingliste sollte abonniert werden. Zudem sollte die MediaWiki-Installation stets aktualisiert werden, sobald eine neue Programmversion veröffentlicht wurde.",
        "config-subscribe-noemail": "Beim Abonnieren der Mailingliste mit Mitteilungen zu Versionsveröffentlichungen wurde keine E-Mail-Adresse angegeben.\nBitte eine E-Mail-Adresse angeben, sofern die Mailingliste abonniert werden soll.",
+       "config-pingback": "Daten über diese Installation mit den MediaWiki-Entwicklern teilen.",
+       "config-pingback-help": "Sofern diese Option ausgewählt wird, meldet MediaWiki regelmäßig die Basisdaten dieser MediaWiki-Installation an https://www.mediawiki.org. Diese Daten enthalten beispielsweise den Betriebssystemtyp, die PHP-Version sowie das genutzte Datenbanksystem. Die Wikimedia Foundation teilt diese Daten mit den MediaWiki-Entwicklern, um Entscheidungen zur künftigen Softwareentwicklung zu verbessern. Die folgenden Daten werden für diese Installation gesendet:\n<pre>$1</pre>",
        "config-almost-done": "Der Vorgang ist fast abgeschlossen!\nDie verbleibenden Konfigurationseinstellungen können übersprungen und das Wiki umgehend installiert werden.",
        "config-optional-continue": "Ja, es sollen weitere Konfigurationseinstellungen vorgenommen werden.",
        "config-optional-skip": "Nein, das Wiki soll nun installiert werden.",
index f8d26a7..6fa270a 100644 (file)
        "config-page-language": "Zıwan",
        "config-page-welcome": "Şıma xeyr ameyê MediaWiki!",
        "config-page-dbconnect": "Database rê grêdey",
+       "config-page-upgrade": "Est bıyaye versiyoni berz kı",
+       "config-page-dbsettings": "Sazê Database",
        "config-page-name": "Name",
        "config-page-options": "Weçinegi",
-       "config-page-install": "Barine",
+       "config-page-install": "Bar ke",
        "config-page-complete": "Temamyayo",
+       "config-page-restart": "Barkerdışi fına ser kı",
        "config-page-readme": "Mı bıwane",
        "config-page-copying": "Kopyayeno",
        "config-page-upgradedoc": "Berzkerdış",
        "config-restart": "E, fına dest pekê",
        "config-sidebar": "* [https://www.mediawiki.org MediaWiki keye]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Şınasiya Karberi]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Şınasiya İdarekaran]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Peşti]\n----\n* <doclink href=Readme>Mı buwanê</doclink>\n* <doclink href=ReleaseNotes>Notê elaqeyıni</doclink>\n* <doclink href=Copying>Kopyakerdış</doclink>\n* <doclink href=UpgradeDoc>Zêdekerdış</doclink>",
        "config-env-php": "PHP $1 i biyo saz.",
+       "config-env-hhvm": "HHVM $1 saz bi ya.",
        "config-db-type": "Database tipe:",
        "config-db-host": "Database host:",
+       "config-db-host-oracle": "Database TNS:",
        "config-db-name": "Database name:",
+       "config-db-username": "Database nameykarberi:",
+       "config-db-password": "Database parola :",
        "config-db-port": "Portê database:",
        "config-type-mssql": "Microsoft SQL Server",
        "config-header-mysql": "Eyarê MySQL",
diff --git a/includes/installer/i18n/dty.json b/includes/installer/i18n/dty.json
new file mode 100644 (file)
index 0000000..a986e00
--- /dev/null
@@ -0,0 +1,52 @@
+{
+       "@metadata": {
+               "authors": [
+                       "राम प्रसाद जोशी"
+               ]
+       },
+       "config-desc": "मेडियाविकिको लागि स्थापक",
+       "config-title": "मेडिया विकि $1 स्थापना",
+       "config-information": "जानकारी",
+       "config-localsettings-upgrade": "<code>LocalSettings.php</code> फाइल पाइयो ।\nयै स्थापनालाई अपग्रेड गर्न, तलीखाइ दिया  बक्समी <code>$wgUpgradeKey</code>  को मान दर्ज गर ।\nतम <code>LocalSettings.php</code> मा भेट्टे हौ ।",
+       "config-localsettings-cli-upgrade": "<code>LocalSettings.php</code> फाइल पाइयो ।\nयै स्थापनालाई अपग्रेड गद्द, बदलामी कृपया यैलाई चलाओ <code>update.php</code>",
+       "config-localsettings-key": "नवीनीकरण साँचो",
+       "config-localsettings-badkey": "तमले प्रोभाइट गर्याको नवनिकरण साँचो मान्य छैन",
+       "config-upgrade-key-missing": "An existing installation of MediaWiki has been detected.\nTo upgrade this installation, please put the following line at the bottom of your <code>LocalSettings.php</code>:\n\n$1",
+       "config-localsettings-incomplete": "The existing <code>LocalSettings.php</code> appears to be incomplete.\nThe $1 variable is not set.\nPlease change <code>LocalSettings.php</code> so that this variable is set, and click \"{{int:Config-continue}}\".",
+       "config-localsettings-connection-error": "An error was encountered when connecting to the database using the settings specified in <code>LocalSettings.php</code>. Please fix these settings and try again.\n\n$1",
+       "config-session-error": "Error starting session: $1",
+       "config-session-expired": "Your session data seems to have expired.\nSessions are configured for a lifetime of $1.\nYou can increase this by setting <code>session.gc_maxlifetime</code> in php.ini.\nRestart the installation process.",
+       "config-no-session": "तमरो सेसन डाटा मेटिया छ!\nतमरो php चेक गर। ini र make sure <code>session.save_path</code> is set to an appropriate directory.",
+       "config-your-language": "तमरो भाषा:",
+       "config-your-language-help": "इन्स्टाल गर्दा उपयोग गद्दे भाषा छान ।",
+       "config-wiki-language": "विकि भाषाहरू",
+       "config-wiki-language-help": "Select the language that the wiki will predominantly be written in.",
+       "config-back": "← पछाडी",
+       "config-continue": "जारी राख्या",
+       "config-page-language": "भाषा",
+       "config-page-welcome": "मिडीयाविकिमी तमलाई स्वागत छ!",
+       "config-page-dbconnect": "डेटाबेससँग सम्बन्ध बनाउन्या",
+       "config-page-upgrade": "मौजूदा स्थापनाको नवीनीकरण",
+       "config-page-dbsettings": "डेटावेस सेटिङ",
+       "config-page-name": "नाऊ",
+       "config-page-options": "विकल्पहरू",
+       "config-page-install": "स्थापना गद्दे",
+       "config-page-complete": "पूरा भयो !",
+       "config-page-restart": "स्थापना फेरि सुरु गद्दे",
+       "config-page-readme": "थप पड्डे",
+       "config-page-releasenotes": "प्रकाशन टिप्पणी",
+       "config-page-copying": "कपि हून लाग्याको छ",
+       "config-page-upgradedoc": "अद्यावधिक गरिदै",
+       "config-page-existingwiki": "विकि बन्द हुँदै",
+       "config-help-restart": "Do you want to clear all saved data that you have entered and restart the installation process?",
+       "config-restart": "हुन्छ, पुनः सुचारू गद्दे",
+       "config-welcome": "=== Environmental checks ===\nBasic checks will now be performed to see if this environment is suitable for MediaWiki installation.\nRemember to include this information if you seek support on how to complete the installation.",
+       "config-copyright": "=== Copyright and Terms ===\n\n$1\n\nThis 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.\n\nThis program is distributed in the hope that it will be useful, but <strong>without any warranty</strong>; without even the implied warranty of <strong>merchantability</strong> or <strong>fitness for a particular purpose</strong>.\nSee the GNU General Public License for more details.\n\nYou should have received <doclink href=Copying>a copy of the GNU General Public License</doclink> along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, or [http://www.gnu.org/copyleft/gpl.html read it online].",
+       "config-sidebar": "* [https://www.mediawiki.org MediaWiki home]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents User's Guide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Administrator's Guide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]\n----\n* <doclink href=Readme>Read me</doclink>\n* <doclink href=ReleaseNotes>Release notes</doclink>\n* <doclink href=Copying>Copying</doclink>\n* <doclink href=UpgradeDoc>Upgrading</doclink>",
+       "config-env-good": "The environment has been checked.\nYou can install MediaWiki.",
+       "config-env-bad": "The environment has been checked.\nYou cannot install MediaWiki.",
+       "config-env-php": "PHP $1 is installed.",
+       "config-env-hhvm": "HHVM $1 स्थापना गरिएको छ ।",
+       "config-unicode-using-intl": "Using the [http://pecl.php.net/intl intl PECL extension] for Unicode normalization.",
+       "config-unicode-pure-php-warning": "<strong>Warning:</strong> The [http://pecl.php.net/intl intl PECL extension] is not available to handle Unicode normalization, falling back to slow pure-PHP implementation.\nIf you run a high-traffic site, you should read a little on [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations Unicode normalization]."
+}
index 79383f3..3f3032b 100644 (file)
        "config-subscribe": "Subscribe to the [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce release announcements mailing list].",
        "config-subscribe-help": "This is a low-volume mailing list used for release announcements, including important security announcements.\nYou should subscribe to it and update your MediaWiki installation when new versions come out.",
        "config-subscribe-noemail": "You tried to subscribe to the release announcements mailing list without providing an email address.\nPlease provide an email address if you wish to subscribe to the mailing list.",
+       "config-pingback": "Share data about this installation with MediaWiki developers.",
+       "config-pingback-help": "If you select this option, MediaWiki will periodically ping https://www.mediawiki.org with basic data about this MediaWiki instance. This data includes, for example, the type of system, PHP version, and chosen database backend. The Wikimedia Foundation shares this data with MediaWiki developers to help guide future development efforts. The following data will be sent for your system:\n<pre>$1</pre>",
        "config-almost-done": "You are almost done!\nYou can now skip the remaining configuration and install the wiki right now.",
        "config-optional-continue": "Ask me more questions.",
        "config-optional-skip": "I'm bored already, just install the wiki.",
index a215374..50c0ec0 100644 (file)
@@ -59,6 +59,7 @@
        "config-admin-password-confirm": "Retajpu pasvorton:",
        "config-admin-name-blank": "Enigu salutnomon de administranto.",
        "config-admin-email": "Retpoŝtadreso:",
+       "config-profile-private": "Privata vikio",
        "mainpagetext": "'''MediaWiki estis sukcese instalita.'''",
        "mainpagedocfooter": "Konsultu la [https://meta.wikimedia.org/wiki/MediaWiki_User%27s_Guide Gvidilon por uzantoj de MediaWiki] por informoj pri uzado de vikia programaro.\n\n==Kiel komenci==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Listo de konfiguraĵoj] (angle)\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki Oftaj Demandoj] (angle)\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Anonco-dissendolisto pri MediaWiki] (angle)\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Preklad MediaWiki do tvojho jazyka]"
 }
index b1c3d48..09f3a40 100644 (file)
@@ -30,7 +30,8 @@
                        "Matiia",
                        "AlvaroMolina",
                        "Indiralena",
-                       "Peter Bowman"
+                       "Peter Bowman",
+                       "Dgstranz"
                ]
        },
        "config-desc": "El instalador de MediaWiki",
        "config-subscribe": "Suscribirse a la [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce lista de correo de anuncios de versiones].",
        "config-subscribe-help": "Esta es una lista de divulgación de bajo volumen para anuncios de lanzamiento de versiones nuevas, incluyendo anuncios de seguridad importantes.\nTe recomendamos suscribirte y actualizar tu instalación MediaWiki cada vez que se lance una nueva versión.",
        "config-subscribe-noemail": "Has intentado suscribirte a la lista de correo de anuncios de nuevos lanzamientos sin proporcionar una dirección de correo electrónico.\nProporciona una dirección de correo electrónico si quieres suscribirte a la lista de correo.",
+       "config-pingback": "Compartir datos sobre esta instalación con los desarrolladores de MediaWiki.",
+       "config-pingback-help": "Si seleccionas esta opción, MediaWiki enviará periódicamente a https://www.mediawiki.org datos básicos sobre esta instancia de MediaWiki. Se trata de datos tales como el tipo de sistema, la versión de PHP y la base de datos elegida. La Fundación Wikimedia comparte estos datos con los desarrolladores de MediaWiki para ayudar a guiar el desarrollo futuro. Se enviarán los siguientes datos para tu sistema:\n<pre>$1</pre>",
        "config-almost-done": "¡Ya casi has terminado!\nAhora puedes saltarte el resto de los pasos e instalar el wiki ya.",
        "config-optional-continue": "Hazme más preguntas.",
        "config-optional-skip": "Ya estoy aburrido, sólo instala el wiki.",
index b9d5086..dfcf1d3 100644 (file)
@@ -78,6 +78,6 @@
        "config-install-user-alreadyexists": "Kasutaja \"$1\" on juba olemas",
        "config-install-tables": "Tabelite loomine",
        "config-help": "abi",
-       "mainpagetext": "'''MediaWiki tarkvara on edukalt paigaldatud.'''",
+       "mainpagetext": "<strong>MediaWiki tarkvara on paigaldatud.</strong>",
        "mainpagedocfooter": "Vikitarkvara kasutamise kohta leiad lisateavet [https://meta.wikimedia.org/wiki/Help:Contents juhendist].\n\n== Alustamine ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Häälestussätete loend]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki KKK]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki versiooniuuenduste postiloend]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources MediaWiki lokaliseerimine]"
 }
index 1158193..f812471 100644 (file)
@@ -63,6 +63,7 @@
        "config-header-oracle": "Oracle hobespenak",
        "config-header-mssql": "Microsoft SQL Server-en ezarpenak",
        "config-invalid-db-type": "Datu-base mota baliogabea.",
+       "config-db-sys-user-exists-oracle": "$1 erabiltzaile kontua dagoeneko existitzen da. SYSDBA kontu berri bat sortzeko erabili daiteke soilik!",
        "config-mysql-innodb": "InnoDB",
        "config-mysql-myisam": "MyISAM",
        "config-mysql-binary": "Bitarra",
index 24e8d37..dae9f2a 100644 (file)
@@ -13,7 +13,8 @@
                        "Huji",
                        "Macofe",
                        "درفش کاویانی",
-                       "Hamisun"
+                       "Hamisun",
+                       "Alifakoor"
                ]
        },
        "config-desc": "نصب کنندهٔ ویکی‌مدیا",
        "config-subscribe": "عضویت در [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce release announcements mailing list].",
        "config-subscribe-help": "این یک میلینگ لیست کم حجم است که برای اطلاع‌رسانی کاربرد دارد و شامل اطلاعیه‌های امنیتی می‌شود.\nشما باید در آن ثبت نام کنید و زمانی که نسخهٔ جدید مدیاویکی ارائه شد آن را به‌روز نمائید.",
        "config-subscribe-noemail": "شما بدون ثبت نشانی پست الکترونیکی قصد داشتید در فهرست اطلاع‌رسانی پخش نرم‌افزار ثبت‌نام کنید.\nاگر قصد ثبت‌نام دارید لطفاً یک نشانی پست الکترونیکی مشخص کنید.",
+       "config-pingback": "اشتراک گذاری داده‌های نصب با توسعه‌دهندگان مدیاویکی",
+       "config-pingback-help": "اگر این گزینه را انتخاب کنید، ویکی‌مدیا به صورت مداوم به وب‌گاه https://www.mediawiki.org برای ارسال اطلاعات ابتدایی نصب این مدیاویکی ارتباط برقرار می‌کند. اطلاعات شامل نوع سامانه، نسخهٔ پی‌اچ‌پی، دیتابیس انتخاب شده می‌باشد. بنیاد مدیاویکی برای توسعه‌های آینده نرم‌افزار اطلاعات را با توسعه دهندگان مدیاویکی به اشتراک می‌گذارد. اطلاعاتی که از سامانه شما ارسال خواهد شد موارد زیر هستند:\n<pre>$1</pre>",
        "config-almost-done": "شما تقریباً عملیات را کامل کرده‌اید.\nاکنون می‌توانید پیکربندی باقیمانده را نخوانده رها کنید و درحال‌حاضر ویکی را نصب کنید.",
        "config-optional-continue": "سوال‌های بیشتری از من بپرسید.",
        "config-optional-skip": "درحال‌حاضر خسته‌ام،سریعاً ویکی را نصب کنید.",
        "config-install-extension-tables": "ایجاد جداول برای افزونه‌های فعال",
        "config-install-mainpage-failed": "قادر به درج صفحهٔ اصلی نمی‌باشد:$1",
        "config-install-done": "'''تبریک!'''\nبا موفقیت مدیاویکی را نصب کردید.\nبرنامه نصب‌کننده پرونده <code>LocalSettings.php</code> را درست کرد.\nکه شامل تمام تنظیمات می‌باشد.\n\nشما نیاز به دریافت آن دارید و آن را در پایهٔ نصب ویکی قرار دهید (همان پوشهٔ index.php). دریافت باید به صورت خودکار شروع شده‌باشد.\n\nاگر دریافت شروع نشد یا اگر آن را لغو کردید با کلیک روی پیوند زیر می‌توانید آن را دریافت کنید:\n\n$3\n\n'''توجه داشته باشید:''' اگر این را الآن انجام ندهید، این پرونده تولیدشده در صورتی که نصب را بدون دریافت آن تمام کردید بعداً در اختیار شما قرار نخواهد گرفت.\n\nوقتی انجام شد شما می‌توانید '''[$2 وارد ویکی شوید]'''.",
+       "config-install-done-path": "<strong>تبریک!</strong>\nمدیاویکی با موفقیت نصب گردید.\nبرنامه نصب‌کننده یک پرونده <code>LocalSettings.php</code> ایجاد کرده است که شامل تمام تنظیمات می‌باشد.\n\nلازم است شما آن را دریافت کرده و در <code>$4</code> قرار دهید. اِن دریافت می باِست به صورت خودکار شروع شده‌باشد.\n\nاگر دریافت شروع نشده بود و یا آن را لغو کرده اید با کلیک روی پیوند زیر می‌توانید آن را دریافت کنید:\n\n$3\n\n<strong>توجه:</strong> اگر این کار را هم اکنون انجام ندهید و بدون دریافت آن از برنامه نصب خارج شويد، این پرونده تنظیمات تولیدشده در آینده در اختیار شما قرار نخواهد داشت.\n\nوقتی که آن کار را انجام داديد، می‌توانید <strong>[$2 وارد ويکی خودتان شويد]</strong>.",
        "config-download-localsettings": "دریافت <code>LocalSettings.php</code>",
        "config-help": "راهنما",
        "config-help-tooltip": "برای گسترش کلیک کنید",
index 0a61173..7dbca51 100644 (file)
        "config-sqlite-name-help": "Valitse nimi, joka yksilöi tämän wikin.\nÄlä käytä välilyöntejä tai viivoja.\nNimeä käytetään SQLite-tietokannan tiedostonimessä.",
        "config-sqlite-dir-unwritable": "Hakemistoon ”$1” kirjoittaminen epäonnistui.\nMuuta hakemiston käyttöoikeuksia siten, että palvelinohjelmisto voi kirjoittaa siihen ja yritä uudelleen.",
        "config-sqlite-readonly": "Tiedostoon <code>$1</code> ei voi kirjoittaa.",
+       "config-sqlite-cant-create-db": "Tietokantatiedostoa <code>$1</code> ei voitu luoda.",
        "config-sqlite-fts3-downgrade": "PHP:stä puuttuu FTS3-tuki. Poistetaan ominaisuus käytöstä tietokantatauluista.",
        "config-can-upgrade": "Tietokanta sisältää jo MediaWiki tauluja.\nPäivitä ne MediaWiki-versioon $1 painamalla <strong>Jatka</strong>.",
        "config-upgrade-done": "Päivitys valmis.\n\nVoit [$1 aloittaa wikin käytön].\n\nNapsauta alla olevaa painiketta, jos haluat luoda uudelleen <code>LocalSettings.php</code>-tiedoston.\nTämä '''ei ole suositeltavaa''', jos wikissäsi ei ole ongelmia.",
        "config-upgrade-done-no-regenerate": "Päivitys valmis.\n\nVoit [$1 aloittaa wikin käytön].",
        "config-regenerate": "Luo LocalSettings.php uudelleen →",
        "config-show-table-status": "Kysely <code>SHOW TABLE STATUS</code> epäonnistui!",
+       "config-unknown-collation": "<strong>Varoitus:</strong> Tietokanta käyttää tunnistamatonta lajittelua.",
        "config-db-web-help": "Valitse käyttäjänimi ja salasana joita palvelin käyttää muodostaessaan yhteyttä tietokantapalvelimeen wikin normaalin toiminnan aikana.",
        "config-db-web-account-same": "Käytä samaa tiliä kuin asennuksessa",
        "config-db-web-create": "Lisää tili, jos sitä ei ole jo olemassa",
        "config-mssql-auth": "Varmennuksen tyyppi:",
        "config-mssql-install-auth": "Valitse varmennuksen tyyppi, jota käytetään yhdistäessä tietokantaan asennuksen aikana.\nJos valitset \"{{int:config-mssql-windowsauth}}\", käytetään verkkopalvelimen käyttäjän kirjautumistietoja.",
        "config-mssql-web-auth": "Valitse varmennuksen tyyppi, jota verkkopalvelin käyttää yhdistäessään tietokantapalvelimeen wikin tavallisen toiminnan aikana.\nJos valitset \"{{int:config-mssql-windowsauth}}\", käytetään verkkopalvelimen käyttäjän kirjautumistietoja.",
+       "config-mssql-windowsauth": "Windows-varmennus",
        "config-site-name": "Wikin nimi",
        "config-site-name-help": "Tämä näkyy selaimen otsikkona ja muissa kohdissa.",
        "config-site-name-blank": "Kirjoita sivuston nimi.",
        "config-ns-site-name": "Sama kuin wikin nimi: $1",
        "config-ns-other": "Muu (määritä)",
        "config-ns-other-default": "MinunWiki",
+       "config-project-namespace-help": "Wikipedian esimerkkiä seuraten monien wikien käytäntösivut ovat erillään sisältösivuista '''projektinimiavaruudessa'''.\nTämän nimiavaruuden kaikki sivujen nimet alkavat etuliitteellä, jonka voit määrittää tässä.\nYleensä tämä etuliite pohjautuu wikin nimeen, mutta siinä ei saa olla välimerkkejä kuten \"#\" tai \":\".",
        "config-ns-invalid": "Määritelty nimiavaruus \"<nowiki>$1</nowiki>\" on virheellinen.\nSyötä joku muu nimiavaruus.",
        "config-ns-conflict": "Määritelty nimiavaruus \"<nowiki>$1</nowiki>\" on ristiriidassa MediaWikin oletusnimiavaruuksien kanssa.\nSyötä joku muu nimiavaruus.",
        "config-admin-box": "Ylläpitäjän tili",
        "config-install-extension-tables": "Luodaan tauluja käyttöönotetuille laajuennuksille",
        "config-install-mainpage-failed": "Etusivun lisääminen ei onnistunut: $1",
        "config-install-done": "<strong>Onnittelut!</strong>\nOlet asentanut MediaWikin.\n\nAsennusohjelma on luonut <code>LocalSettings.php</code> -tiedoston.\nSiinä on kaikki MediaWikin asetukset.\n\nLataa tiedosto ja laita se MediaWikin asennushakemistoon (sama kuin missä on index.php). Lataamisen olisi pitänyt alkaa automaattisesti.\n\nMikäli latausta ei tarjottu tai keskeytit latauksen, käynnistä se uudestaan tästä linkistä:\n\n$3\n\n<strong>Huom:</strong> Mikäli et nyt lataa tiedostoa, luotu tiedosto ei ole saatavissa myöhemmin, jos poistut asennuksesta lataamatta sitä.\n\nKun olet laittanut tiedoston oikeaan paikkaan, voit <strong>[$2 mennä wikiisi]</strong>.",
+       "config-install-done-path": "<strong>Onnittelut!</strong>\nOlet asentanut MediaWikin.\n\nAsennusohjelma on luonut <code>LocalSettings.php</code> -tiedoston.\nSiinä on kaikki MediaWikin asetukset.\n\nLataa tiedosto ja laita se sijaintiin <code>$4</code>. Lataamisen olisi pitänyt alkaa automaattisesti.\n\nMikäli latausta ei tarjottu tai keskeytit latauksen, käynnistä se uudestaan tästä linkistä:\n\n$3\n\n<strong>Huom:</strong> Mikäli et nyt lataa tiedostoa, luotu tiedosto ei ole saatavissa myöhemmin, jos poistut asennuksesta lataamatta sitä.\n\nKun olet laittanut tiedoston oikeaan paikkaan, voit <strong>[$2 mennä wikiisi]</strong>.",
        "config-download-localsettings": "Lataa <code>LocalSettings.php</code>",
        "config-help": "ohje",
        "config-help-tooltip": "Klikkaa laajentaaksesi",
index d73f61b..3e7f8f3 100644 (file)
        "config-type-sqlite": "SQLite",
        "config-type-oracle": "Oracle",
        "config-type-mssql": "Microsoft SQL Server",
-       "config-support-info": "MediaWiki supporte 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 le support.",
+       "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. Son support peut contenir quelques bogues mineurs et n'est pas recommandé dans un environnement de production.  ([http://www.php.net/manual/en/pgsql.installation.php Comment compiler PHP avec le support de PostgreSQL])",
-       "config-dbsupport-sqlite": "* [{{int:version-db-sqlite-url}} SQLite] est un système de base de données léger bien supporté. ([http://www.php.net/manual/en/pdo.installation.php Comment compiler PHP avec le support de SQLite], en utilisant PDO)",
+       "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-header-mysql": "Paramètres de MySQL",
        "config-mysql-engine": "Moteur de stockage :",
        "config-mysql-innodb": "InnoDB",
        "config-mysql-myisam": "MyISAM",
-       "config-mysql-myisam-dep": "''' Avertissement ''': vous avez sélectionné MyISAM comme moteur de stockage pour MySQL, ce qui n'est pas recommandé pour une utilisation avec MediaWiki, parce que:\n * il supporte à peine la simultanéité en raison de verrouillage de table\n * il est plus sujet à la corruption que les autres moteurs\n * le codebase MediaWiki ne gère pas toujours MyISAM comme il se doit\n\nSi votre installation MySQL supporte InnoDB, il est fortement recommandé que vous le choisissiez plutôt. \nSi votre installation MySQL ne supporte pas les tables InnoDB, il est peut-être temps de faire une mise à niveau.",
-       "config-mysql-only-myisam-dep": "'''Attention :''' MyISAM est le seul moteur de stockage disponible pour MySQL sur cette machine, et cela n’est pas recommandé pour une utilisation avec MédiaWiki, car :\n* il supporte très peu les accès concurrents à cause du verrouillage des tables\n* il est plus sujet à corruption que les autres moteurs\n* le code de base de MédiaWiki ne gère pas toujours MyISAM comme il faudrait\n\nVotre installation MySQL ne supporte pas InnoDB ; il est peut-être temps de la mettre à jour.",
-       "config-mysql-engine-help": "'''InnoDB''' est presque toujours la meilleure option, car il supporte bien les accès concurrents.\n\n'''MyISAM''' peut être plus rapide dans les installations monoposte ou en lecture seule. \nLes bases de données MyISAM ont tendance à se corrompre plus souvent que les bases d'InnoDB.",
+       "config-mysql-myisam-dep": "''' Avertissement ''': vous avez sélectionné MyISAM comme moteur de stockage pour MySQL, ce qui n’est pas recommandé pour une utilisation avec MediaWiki, parce que :\n * il prend à peine en charge la simultanéité en raison de verrouillage de table\n * il est plus sujet à la corruption que les autres moteurs\n * le code de base MediaWiki ne gère pas toujours MyISAM comme il se doit\n\nSi votre installation MySQL prenden charge InnoDB, il est fortement recommandé que vous le choisissiez plutôt. \nSi votre installation MySQL ne prend pas en charge les tables InnoDB, il est peut-être temps de faire une mise à niveau.",
+       "config-mysql-only-myisam-dep": "'''Attention :''' MyISAM est le seul moteur de stockage disponible pour MySQL sur cette machine, et cela n’est pas recommandé pour une utilisation avec MédiaWiki, car :\n* il prend très peu en charge les accès concurrents à cause du verrouillage des tables\n* il est plus sujet à corruption que les autres moteurs\n* le code de base de MédiaWiki ne gère pas toujours MyISAM comme il faudrait\n\nVotre installation MySQL ne prend pas en charge InnoDB ; il est peut-être temps de la mettre à jour.",
+       "config-mysql-engine-help": "'''InnoDB''' est presque toujours la meilleure option, car il prend bien en charge les accès concurrents.\n\n'''MyISAM''' peut être plus rapide dans les installations monoposte ou en lecture seule.\nLes bases de données MyISAM ont tendance à se corrompre plus souvent que les bases d’InnoDB.",
        "config-mysql-charset": "Jeu de caractères de la base de données :",
        "config-mysql-binary": "Binaire",
        "config-mysql-utf8": "UTF-8",
        "config-subscribe": "Abonnez-vous à la [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce liste d'annonce des nouvelles versions]",
        "config-subscribe-help": "Il s'agit d'une liste de diffusion à faible volume utilisée servant à annoncer les nouvelles versions, y compris les versions améliorant la sécurité du logiciel.\nVous devriez y souscrire et mettre à jour votre version de MediaWiki lorsque de nouvelles versions sont publiées.",
        "config-subscribe-noemail": "Vous avez essayé de vous abonner à la liste de diffusion des communiqués, sans fournir une adresse courriel ! S'il vous plaît, fournir une adresse électronique si vous souhaitez vous abonner à la liste de diffusion.",
+       "config-pingback": "Partager des données au sujet de cette installation avec les développeurs de MediaWiki.",
+       "config-pingback-help": "Si vous sélectionnez cette option, MediaWiki fera périodiquement un ping de https://www.mediawiki.org avec les données de base sur cette instance de MediaWiki. Ces données incluent, par exemple, le type de système, la version de PHP, ainsi que la base de données arrière choisie. La Fondation Wikimedia partage ces données avec les développeurs de MediaWiki pour aider à orienter les futurs efforts de développement. Les données suivantes concernant votre système seront envoyées : <pre>$1</pre>",
        "config-almost-done": "Vous avez presque fini !\nVous pouvez passer la configuration restante et installer immédiatement le wiki.",
        "config-optional-continue": "Me poser davantage de questions.",
        "config-optional-skip": "J’en ai assez, installer simplement le wiki.",
        "config-help": "aide",
        "config-help-tooltip": "cliquer pour agrandir",
        "config-nofile": "Le fichier « $1 » est introuvable. A-t-il été supprimé ?",
-       "config-extension-link": "Saviez-vous que votre wiki supporte [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions des extensions] ?\n\nVous pouvez consulter les [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category extensions par catégorie] ou la [https://www.mediawiki.org/wiki/Extension_Matrix matrice des extensions] pour voir la liste complète des extensions.",
+       "config-extension-link": "Saviez-vous que votre wiki prend en charge [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions des extensions] ?\n\nVous pouvez consulter les [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category extensions par catégorie] ou la [https://www.mediawiki.org/wiki/Extension_Matrix matrice des extensions] pour voir la liste complète des extensions.",
        "mainpagetext": "<strong>MediaWiki a été installé.</strong>",
        "mainpagedocfooter": "Consultez le [https://meta.wikimedia.org/wiki/Help:Contents/fr Guide de l’utilisateur] pour plus d’informations sur l’utilisation de ce logiciel de wiki.\n\n== Pour démarrer ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste des paramètres de configuration]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/fr Questions courantes sur MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Liste de discussion sur les distributions de MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Adaptez MediaWiki dans votre langue]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Apprendre comment lutter contre le pourriel dans votre wiki]"
 }
index d57e19f..e9e757a 100644 (file)
@@ -5,7 +5,8 @@
                        "Toliño",
                        "아라",
                        "Vivaelcelta",
-                       "Macofe"
+                       "Macofe",
+                       "Banjo"
                ]
        },
        "config-desc": "O programa de instalación de MediaWiki",
        "config-subscribe": "Subscríbase á [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce lista de correo de anuncios sobre lanzamentos].",
        "config-subscribe-help": "Esta é unha lista de correos de baixo volume usada para anuncios sobre lanzamentos de novas versións, incluíndo avisos de seguridade importantes.\nDebería subscribirse a ela e actualizar a súa instalación MediaWiki cando saian as novas versións.",
        "config-subscribe-noemail": "Intentou subscribirse á lista de correo dos anuncios de novos lanzamentos sen proporcionar o enderezo de correo electrónico.\nDea un enderezo de correo electrónico se quere efectuar a subscrición á lista de correo.",
+       "config-pingback": "Compartir datos de esta instalación cos desenvolvedores de MediaWiki",
+       "config-pingback-help": "Se seleccionas esta opción, MediaWiki enviará periodicamente unha mensaxe a https://www.mediawiki.org con datos básicos sobre esta instancia Mediawiki. Estos datos inclúen, por exemplo, o tipo de sistema, versión de PHP e a base de datos escollida. A Fundación Wikimedia comparte estos datos cos desenvolvedores de MediaWiki para axudar a guiar o traballo futuro de desenvolvemento. Serán enviados os seguintes datos do seu sistemaː\n<pre>$1</pre>",
        "config-almost-done": "Xa case rematou!\nNeste paso pode saltar o resto da configuración e instalar o wiki agora mesmo.",
        "config-optional-continue": "Facédeme máis preguntas.",
        "config-optional-skip": "Xa estou canso. Instalade o wiki.",
index 6f519ec..ac67394 100644 (file)
        "config-subscribe": "להירשם ל[https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce רשימת התפוצה עם הודעות על גרסאות חדשות].",
        "config-subscribe-help": "זוהי רשימת תפוצה עם הודעות מעטות שמשמשת להודעות על הוצאת גרסאות, כולל עדכוני אבטחה חשובים.\nמומלץ להירשם אליה ולעדכן את מדיה־ויקי כאשר יוצאות גרסאות חדשות.",
        "config-subscribe-noemail": "ניסית להירשם לרשימת תפוצה של הודעות בלי לתת כתובת דוא\"ל.\nנא לתת כתובת דוא\"ל אם ברצונך להירשם לרשימת התפוצה.",
+       "config-pingback": "לשתף נתונים אודות ההתקנה הזו עם מפתחי מדיה־ויקי.",
+       "config-pingback-help": "אם האפשרות הזאת תיבחר, מדיה־ויקי תודיע לאתר https://www.mediawiki.org נתונים בסיסיים על מופע המדיה־ויקי הזה. הנתונים האלה כוללים, למשל, את סוג המערכת, גרסת ה־PHP, ושרת מסד הנתונים שבחרת. קרן ויקימדיה משתפת את הנתונים האלה עם מפתחי מדיה־ויקי כדי לעזור למאמצי הפיתוח העתידיים. הנתונים הבאים יישלחו מהמערכת שלך:\n<pre>$1</pre>",
        "config-almost-done": "כמעט סיימת!\nאפשר לדלג על שאר ההגדרות ולהתקין את הוויקי כבר עכשיו.",
        "config-optional-continue": "הצגת שאלות נוספות.",
        "config-optional-skip": "משעמם לי, תתקינו לי כבר את הוויקי הזה.",
index f97b6dc..cc197c8 100644 (file)
@@ -56,8 +56,8 @@
        "config-env-php": "A PHP verziója: $1",
        "config-env-hhvm": "HHVM verziója: $1",
        "config-unicode-using-intl": "A rendszer Unicode normalizálására az [http://pecl.php.net/intl intl PECL kiterjesztést] használja.",
-       "config-unicode-pure-php-warning": "'''Figyelmeztetés''': Az Unicode normalizáláshoz szükséges [http://pecl.php.net/intl intl PECL kiterjesztés] nem érhető el, helyette a lassú, PHP alapú implementáció lesz használva.\nHa nagy látogatottságú oldalt üzemeltetsz, itt találhatsz további információkat [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations a témáról].",
-       "config-unicode-update-warning": "'''Figyelmeztetés''': Az Unicode normalizáláshoz szükséges burkolókönyvtár [http://site.icu-project.org/ ICU projekt] függvénykönyvtárának régebbi változatát használja.\nHa ügyelni kívánsz a Unicode használatára, fontold meg a [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations frissítését].",
+       "config-unicode-pure-php-warning": "<strong>Figyelmeztetés:</strong> Az Unicode normalizáláshoz szükséges [http://pecl.php.net/intl intl PECL kiterjesztés] nem érhető el, helyette a lassú, PHP-alapú implementáció lesz használatban.\nHa nagy látogatottságú oldalt üzemeltetsz, [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations itt] találhatsz további információkat a témáról.",
+       "config-unicode-update-warning": "<strong>Figyelmeztetés:</strong> Az Unicode normalizáláshoz szükséges burkolókönyvtár [http://site.icu-project.org/ ICU projekt] függvénykönyvtárának régebbi változatát használja.\nHa ügyelni kívánsz a Unicode használatára, fontold meg a [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations frissítését].",
        "config-no-db": "Nem sikerült egyetlen használható adatbázis-illesztőprogramot sem találni. Telepítened kell egyet a PHP-hez.\nA következő {{PLURAL:$2|adatbázistípus támogatott|adatbázistípusok támogatottak}}: $1.\n\nHa a PHP-t magad fordítottad, konfiguráld újra úgy, hogy engedélyezve legyen egy adatbáziskliens, pl. a <code>./configure --with-mysql</code> parancs használatával.\nHa a PHP-t Debian vagy Ubuntu csomaggal telepítetted, akkor szükséged lesz például a php5-mysql csomagra is.",
        "config-outdated-sqlite": "<strong>Figyelmeztetés:</strong> SQLite $1 verziód van, ami alacsonyabb a legalább szükséges $2 verziónál. Az SQLite nem lesz elérhető.",
        "config-no-fts3": "'''Figyelmeztetés''': Az SQLite [//sqlite.org/fts3.html FTS3 modul] nélkül lett fordítva, a keresési funkciók nem fognak működni ezen a rendszeren.",
        "config-ns-site-name": "Ugyanaz, mint a wiki neve: $1",
        "config-ns-other": "Más (meg kell adni)",
        "config-ns-other-default": "SajátWiki",
-       "config-project-namespace-help": "A Wikipédia példáját követve számos wiki elkülöníti egy '''projekt névtérbe''' az irányelveit a tartalommal rendelkező lapoktól\nAz ebben a névtérben található lapok nevei egy előtaggal kezdődnek, amit itt adhatsz meg.\nÁltalában az előtag a wiki nevéből származik, de nem tartalmazhat írásjeleket, például „#”-t vagy „:”-t.",
+       "config-project-namespace-help": "A Wikipédia példáját követve számos wiki elkülöníti az irányelveit a tartalmi lapoktól egy '''projektnévtérbe'''.\nAz ebben a névtérben található lapok nevei egy előtaggal kezdődnek, amit itt adhatsz meg.\nÁltalában az előtag a wiki nevéből származik, de nem tartalmazhat írásjeleket, például „#”-t vagy „:”-t.",
        "config-ns-invalid": "A megadott névtér („<nowiki>$1</nowiki>”) érvénytelen.\nVálassz másik projektnévteret!",
        "config-ns-conflict": "A megadott névtér („<nowiki>$1</nowiki>”) ütközik az egyik alapértelmezett MediaWiki-névtérrel.\nVálassz másik projektnévteret!",
        "config-admin-box": "Adminisztrátori fiók",
        "config-logo": "A logó URL-címe:",
        "config-logo-help": "A MediaWiki alapértelmezett felülete helyet ad egy 135×160 pixeles logónak a bal felső sarokban.\nTölts fel egy megfelelő méretű képet, majd írd be ide az URL-címét!\n\nHasználhatsz  <code>$wgStylePath</code>-t vagy <code>$wgScriptPath</code>-t, ha más méretű a logód.\n\nHa nem szeretnél logót használni, egyszerűen hagyd üresen a mezőt.",
        "config-instantcommons": "Instant Commons engedélyezése",
-       "config-instantcommons-help": "Az [https://www.mediawiki.org/wiki/InstantCommons Instant Commons] lehetővé teszi, hogy a wikin használhassák a [https://commons.wikimedia.org/ Wikimedia Commons] oldalon található képeket, hangokat és más médiafájlokat.\nA használatához a MediaWikinek internethozzáférésre van szüksége.\n\nA funkcióról és hogy hogyan állítható be más wikik esetén [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgForeignFileRepos a kézikönyvben] találhatsz további információkat.",
+       "config-instantcommons-help": "Az [https://www.mediawiki.org/wiki/InstantCommons Instant Commons] lehetővé teszi, hogy a wikin használhassák a [https://commons.wikimedia.org/ Wikimédia Commons] oldalon található képeket, hangokat és más médiafájlokat.\nA használatához a MediaWikinek internet-hozzáférésre van szüksége.\n\nA funkcióról és hogy hogyan állítható be más wikik esetén [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgForeignFileRepos a kézikönyvben] találhatsz további információkat.",
        "config-cc-error": "A Creative Commons-licencválasztó nem tért vissza eredménnyel.\nAdd meg kézzel a licencet.",
        "config-cc-again": "Válassz újra…",
        "config-cc-not-chosen": "Válaszd ki a kívánt Creative Commons licencet, majd kattints a „proceed”!",
        "config-install-mainpage": "Kezdőlap létrehozása az alapértelmezett tartalommal",
        "config-install-extension-tables": "Táblák létrehozása az engedélyezett kiterjesztésekhez",
        "config-install-mainpage-failed": "Nemsikerült létrehozni a kezdőlapot: $1",
-       "config-install-done": "'''Gratulálunk!'''\nA MediaWiki telepítése sikeresen befejeződött.\n\nA telepítő elkészítette a <code>LocalSettings.php</code> fájlt, amely tartalmazza az összes beállítást.\n\nEzt le kell tölteni, majd elhelyezni a wiki telepítési könyvtárába (az a könyvtár, ahol az index.php is található).\n\nA letöltés automatikusan elindul. Ha mégsem indulna el, vagy megszakítottad, az alábbi linkre kattintva újra letöltheted:\n\n$3\n\n'''Megjegyzés''': Ha ezt most nem teszed meg, és kilépsz a telepítésből, az elkészített konfigurációs fájlt nem tudod elérni a későbbiekben.\n\nHa végeztél a fájl elhelyezésével, '''[$2 beléphetsz a wikibe]'''.",
+       "config-install-done": "<strong>Gratulálunk!</strong>\nA MediaWiki telepítése sikeresen befejeződött.\n\nA telepítő elkészítette a <code>LocalSettings.php</code> fájlt, amely tartalmazza az összes beállítást.\n\nEzt le kell tölteni, majd elhelyezni a wiki telepítési könyvtárába (az a könyvtár, ahol az index.php is található).\n\nA letöltés automatikusan elindul. Ha mégsem indulna el, vagy megszakítottad, az alábbi linkre kattintva újra letöltheted:\n\n$3\n\n<strong>Megjegyzés:</strong> Ha ezt most nem teszed meg, és kilépsz a telepítésből, az elkészített konfigurációs fájlt nem tudod elérni a későbbiekben.\n\nHa végeztél a fájl elhelyezésével, <strong>[$2 beléphetsz a wikibe]</strong>.",
        "config-download-localsettings": "<code>LocalSettings.php</code> letöltése",
        "config-help": "segítség",
        "config-help-tooltip": "kattints a kibontáshoz",
        "config-nofile": "\"$1\" fájl nem található. Törölve lett?",
        "config-extension-link": "Tudtad, hogy a wikid támogat [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions kiterjesztéseket]?\n\nBöngészhetsz [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category kiterjesztéseket kategóriánként] vagy válogathatsz a [https://www.mediawiki.org/wiki/Extension_Matrix kiterjesztésmátrixból] az összes kiterjesztés áttekintéséhez.",
-       "mainpagetext": "'''A MediaWiki telepítése sikeresen befejeződött.'''",
+       "mainpagetext": "<strong>A MediaWiki telepítése sikeresen befejeződött.</strong>",
        "mainpagedocfooter": "Ha segítségre van szükséged a wikiszoftver használatához, akkor keresd fel a [https://meta.wikimedia.org/wiki/Help:Contents User's Guide] oldalt.\n\n== Alapok (angol nyelven) ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Beállítások listája]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki GyIK]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki-kiadások levelezőlistája]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources A MediaWiki fordítása a saját nyelvedre]"
 }
index 2ebd0b3..4c68cf9 100644 (file)
        "config-subscribe": "Subscribe al [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce lista de diffusion pro annuncios de nove versiones].",
        "config-subscribe-help": "Isto es un lista de e-mail a basse volumine pro annuncios de nove versiones, includente importante annuncios de securitate.\nTu deberea subscriber a illo e actualisar tu installation de MediaWiki quando nove versiones es editate.",
        "config-subscribe-noemail": "Tu tentava abonar te al lista de diffusion pro annunciamento de nove versiones sin fornir un adresse de e-mail.\nPer favor specifica un adresse de e-mail si tu vole abonar te al lista de diffusion.",
+       "config-pingback": "Divider datos sur iste installation con le disveloppatores de MediaWiki.",
+       "config-pingback-help": "Si tu selige option, MediaWiki inviara periodicamente a https://www.mediawiki.org certe datos basic sur iste installation de MediaWiki. Iste datos include, per exemplo, le typo de systema, le version de PHP, e le programma de base de datos seligite. Le Fundation Wikimedia divide iste datos con le disveloppatores de MediaWiki pro adjutar a guidar le effortios de disveloppamento futur. Le sequente datos concernente iste systema essera inviate:\n<pre>$1</pre>",
        "config-almost-done": "Tu ha quasi finite!\nTu pote ora saltar le configuration remanente e installar le wiki immediatemente.",
        "config-optional-continue": "Pone me plus questiones.",
        "config-optional-skip": "Isto me es jam tediose. Simplemente installa le wiki.",
index 0a2f40a..6ed2722 100644 (file)
        "config-subscribe": "Sottoscrivi la [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce mailing list degli annunci di rilasci].",
        "config-subscribe-help": "Si tratta di una mailing list a basso traffico dedicata agli annunci di nuove versioni, compresi importanti segnalazioni riguardanti la sicurezza.\nÈ consigliato iscriversi e aggiornare la propria installazione di MediaWiki quando una nuova versione viene resa pubblica.",
        "config-subscribe-noemail": "Hai provato ad iscriverti alla mailing list dedicata agli annunci delle nuove versioni senza fornire un indirizzo email.\nInserire un indirizzo email se si desidera effettuare l'iscrizione alla mailing list.",
+       "config-pingback": "Condividi i dati su questa installazione con gli sviluppatori di MediaWiki.",
        "config-almost-done": "Hai quasi finito!\nAdesso puoi saltare la rimanente parte della configurazione e semplicemente installare la wiki.",
        "config-optional-continue": "Fammi altre domande.",
        "config-optional-skip": "Sono già stanco, installa solo il wiki.",
index ea51e21..5406be1 100644 (file)
        "config-install-extension-tables": "有効にした拡張機能のためのテーブルを作成しています",
        "config-install-mainpage-failed": "メインページを挿入できませんでした: $1",
        "config-install-done": "<strong>おめでとうございます!</strong>\nMediaWikiのインストールに成功しました。\n\n<code>LocalSettings.php</code>ファイルが生成されました。\nこのファイルはすべての設定を含んでいます。\n\nこれをダウンロードして、ウィキをインストールした基準ディレクトリ (index.phpと同じディレクトリ) に設置する必要があります。ダウンロードは自動的に開始されるはずです。\n\nダウンロードが開始されていない場合、またはダウンロードをキャンセルした場合は、下記のリンクをクリックしてダウンロードを再開できます:\n\n$3\n\n<strong>注意:</strong> この生成された設定ファイルをダウンロードせずにインストールを終了すると、このファイルは利用できなくなります。\n\n上記の作業が完了すると、<strong>[$2 ウィキに入る]</strong>ことができます。",
+       "config-install-done-path": "<strong>おめでとうございます!</strong>\nMediaWikiのインストールに成功しました。\n\n<code>LocalSettings.php</code>ファイルが生成されました。\nこのファイルはすべての設定を含んでいます。\n\nこれをダウンロードして、<code>$4</code> に設置する必要があります。ダウンロードは自動的に開始されるはずです。\n\nダウンロードが開始されていない場合、またはダウンロードをキャンセルした場合は、下記のリンクをクリックしてダウンロードを再開できます:\n\n$3\n\n<strong>注意:</strong> この生成された設定ファイルをダウンロードせずにインストールを終了すると、このファイルは利用できなくなります。\n\n上記の作業が完了すると、<strong>[$2 ウィキに入る]</strong>ことができます。",
        "config-download-localsettings": "<code>LocalSettings.php</code> をダウンロード",
        "config-help": "ヘルプ",
        "config-help-tooltip": "クリックで展開",
index 5e9c34d..c9ae2c8 100644 (file)
@@ -61,7 +61,7 @@
        "config-unicode-pure-php-warning": "<strong>경고:</strong> 유니코드 정규화를 처리할 [http://pecl.php.net/intl intl PECL 확장 기능]을 사용할 수 없기 때문에 느린 pure-PHP 구현을 대신 사용합니다.\n트래픽이 높은 사이트에서 실행하시려면 [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations 유니코드 정규화]를 읽어보셔야 합니다.",
        "config-unicode-update-warning": "<strong>경고:</strong> 유니코드 정규화 래퍼의 설치된 버전은 [http://site.icu-project.org/ ICU 프로젝트]의 라이브러리의 이전 버전을 사용합니다.\n만약 유니코드를 사용하는 것에 대해 우려가 된다면 [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations 업그레이드]해야합니다.",
        "config-no-db": "적절한 데이터베이스 드라이버를 찾을 수 없습니다! PHP용 데이터베이스 드라이버를 설치해야 합니다.\n다음 데이터베이스 {{PLURAL:$2|유형을 지원합니다}}: $1.\n\nPHP를 직접 컴파일했다면, 예를 들어 <code>./configure --with-mysql</code>을 사용하여, 데이터베이스 클라이언트를 활성화하도록 다시 설정하세요.\n데비안이나 우분투 패키지에서 PHP를 설치했다면 <code>php5-mysql</code> 모듈도 설치해야 합니다.",
-       "config-outdated-sqlite": "<strong>경고:</strong> 최소인 $2 버전보다 낮은 SQLite $1(이)가 있습니다. SQLite를 사용할 수 없습니다.",
+       "config-outdated-sqlite": "<strong>경고:</strong> 최소 요구 버전 $2 보다 낮은 SQLite $1이(가) 있습니다. SQLite를 사용할 수 없습니다.",
        "config-no-fts3": "<strong>경고:</strong> SQLite를 [//sqlite.org/fts3.html FTS3 모듈] 없이 컴파일하며, 검색 기능은 백엔드에 사용할 수 없습니다.",
        "config-pcre-old": "<strong>치명:</strong> PCRE $1 또는 그 이상이 필요합니다.\nPHP 바이너리는 PCRE $2에 연결되어 있습니다. [https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/PCRE 자세한 정보].",
        "config-pcre-no-utf8": "<strong>치명:</strong> PHP의 PCRE 모듈은 RCRE_UTF8 지원 없이 컴파일된 것 같습니다.\n미디어위키가 올바르게 작동하려면 UTF-8을 지원해야 합니다.",
@@ -80,8 +80,8 @@
        "config-no-scaling": "GD 라이브러리나 ImageMagick를 찾을 수 없습니다.\n그림 섬네일이 비활성화됩니다.",
        "config-no-uri": "<strong>오류:</strong> 현재 URI를 확인할 수 없습니다.\n설치가 중단되었습니다.",
        "config-no-cli-uri": "<strong>경고:</strong> 기본값을 사용하여 <code>--scriptpath</code>를 지정하지 않았습니다: <code>$1</code>.",
-       "config-using-server": "\"<nowiki>$1</nowiki>\"(을)를 서버 이름으로 사용합니다.",
-       "config-using-uri": "\"<nowiki>$1$2</nowiki>\"(을)를 서버 URL로 사용합니다.",
+       "config-using-server": "\"<nowiki>$1</nowiki>\" 서버 이름을 사용 중입니다.",
+       "config-using-uri": "\"<nowiki>$1$2</nowiki>\" 서버 URL을 사용 중입니다.",
        "config-uploads-not-safe": "<strong>경고:</strong> 올리기에 대한 기본 디렉터리(<code>$1</code>)는 임의의 스크립트 실행에 취약합니다.\n미디어위키는 보안 위협 때문에 모든 올려진 파일을 검사하지만, 올리기를 활성화하기 전에 [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security 이 보안 취약점을 해결할 것]을 매우 권장합니다.",
        "config-no-cli-uploads-check": "<strong>경고:</strong> 올리기를 위한 기본 디렉터리(<code>$1</code>)는 CLI를 설치하는 동안 임의의 스크립트 실행에 대한 취약점에 대해 검사되지 않습니다.",
        "config-brokenlibxml": "시스템에 버그가 있는 PHP와 libxml2의 조합이 있으며 미디어위키나 다른 웹 애플리케이션에 숨겨진 데이터 손상을 일으킬 수 있습니다.\nlibxml2 2.7.3 이후 버전으로 업그레이드하세요. ([https://bugs.php.net/bug.php?id=45996 PHP에 제기한 버그])\n설치가 중단되었습니다.",
        "config-db-wiki-help": "정상적인 위키 작업 동안 데이터베이스에 연결하는 데 사용할 사용자 이름과 비밀번호를 입력하세요.\n계정이 존재하지 않고 설치 계정에 충분한 권한이 있는 경우 이 사용자 계정은 위키를 작동하는 데 필요한 최소 권한으로 만들어집니다.",
        "config-db-prefix": "데이터베이스 테이블 접두어:",
        "config-db-prefix-help": "여러 위키 사이 또는 미디어위키와 다른 웹 애플리케이션 사이에 하나의 데이터베이스를 공유해야 하는 경우, 충돌을 피하기 위해 모든 테이블 이름에 접두어를 추가하도록 선택할 수 있습니다.\n공백을 사용하지 마세요.\n\n이 필드는 일반적으로 비어 있습니다.",
-       "config-mysql-old": "MySQL $1 ì\9d´ì\83\81ì\9d´ í\95\84ì\9a\94í\95\98ë\82\98 $2(ì\9d´)ê°\80 있습니다.",
+       "config-mysql-old": "MySQL $1 ì\9d´ì\83\81ì\9d´ í\95\84ì\9a\94í\95©ë\8b\88ë\8b¤. $2ì\9d´(ê°\80) 있습니다.",
        "config-db-port": "데이터베이스 포트:",
        "config-db-schema": "미디어위키에 대한 스키마:",
        "config-db-schema-help": "보통 이 스키마는 문제가 없습니다.\n필요한 경우에만 바꾸세요.",
        "config-invalid-schema": "미디어위키 \"$1\"에 대한 스키마가 잘못됐습니다.\nASCII 글자 (a-z, A-Z), 숫자 (0-9), 밑줄 (_)과 하이픈 (-)만 사용하세요.",
        "config-db-sys-create-oracle": "설치 관리자는 새 계정을 만들기 위한 SYSDBA 계정만을 지원합니다.",
        "config-db-sys-user-exists-oracle": "\"$1\" 사용자 계정이 이미 존재합니다. SYSDBA는 새 계정을 만드는 데에만 사용할 수 있습니다!",
-       "config-postgres-old": "PostgreSQL $1 ì\9d´ì\83\81ì\9d´ í\95\84ì\9a\94í\95\98ë\82\98 $2(ì\9d´)ê°\80 있습니다.",
+       "config-postgres-old": "PostgreSQL $1 ì\9d´ì\83\81ì\9d´ í\95\84ì\9a\94í\95©ë\8b\88ë\8b¤. $2ì\9d´(ê°\80) 있습니다.",
        "config-mssql-old": "Microsoft SQL 서버 $1 이상의 버전이 필요합니다. 현재 버전은 $2입니다.",
        "config-sqlite-name-help": "위키를 식별하기 위한 이름을 선택하세요.\n공백이나 하이픈을 사용하지 마십시오.\nSQLite 데이터 파일 이름에 사용됩니다.",
        "config-sqlite-parent-unwritable-group": "<code><nowiki>$1</nowiki></code> 데이터 디렉토리를 만들 수 없으며, 이는 웹 서버는 상위 디렉토리인 <code><nowiki>$2</nowiki></code>에 쓸 수 없기 때문입니다.\n\n설치 관리자는 웹 서버로 실행 중인 사용자를 지정할 수 없습니다.\n계속하려면 웹 서버가 쓸 수 있는 <code><nowiki>$3</nowiki></code> 디렉토리를 만드세요.\n유닉스/리눅스 시스템에서의 수행:\n\n<pre>cd $2\nmkdir $3\nchgrp $4 $3\nchmod g+w $3</pre>",
        "config-subscribe": "[https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce 릴리스 발표 메일링 리스트]를 구독합니다.",
        "config-subscribe-help": "중요한 보안 발표를 포함한 배포판 발표에 사용되는 저용량 메일링 리스트입니다.\n이 리스트를 구독하고 새 버전이 나올 때 미디어위키 설치를 업데이트해야 합니다.",
        "config-subscribe-noemail": "이메일 주소를 입력하지 않고 릴리스 발표 메일링 리스트에 가입하려 합니다.\n메일링 리스트에 가입하고자 할 경우 이메일 주소를 입력하세요.",
+       "config-pingback": "본 설치에 관한 데이터를 미디어위키 개발자와 공유합니다.",
+       "config-pingback-help": "이 옵션을 선택하면 미디어위키는 주기적으로 이 미디어위키 인스턴스에 대한 기본 데이터를 가지고 https://www.mediawiki.org에 핑을 합니다. 이 데이터에는 이를테면 시스템의 종류, PHP 버전, 선택한 데이터베이스 백엔드를 포함합니다. 위키미디어 재단은 이 데이터를 미디어위키 개발자들과 공유하여 향후 개발 활동의 길잡이에 도움을 줍니다. 시스템에 대해 다음의 데이터가 전송될 것입니다:\n<pre>$1</pre>",
        "config-almost-done": "거의 다 완료했습니다!\n이제 남은 설정을 생략하고 지금 바로 위키를 설치할 수 있습니다.",
        "config-optional-continue": "더 많은 질문을 물어보세요.",
        "config-optional-skip": "지겨워요, 그냥 위키를 설치할래요.",
        "config-memcache-needservers": "캐시 종류로 Memcached를 선택했지만 어떠한 서버도 지정하지 않았습니다.",
        "config-memcache-badip": "Memcached에 대해 잘못된 IP 주소를 입력했습니다: $1.",
        "config-memcache-noport": "Memcached 서버에 사용할 포트를 지정하지 않았습니다: $1.\n포트를 모를 경우 기본 값은 11211입니다.",
-       "config-memcache-badport": "Memcached 포트 번호는 $1(와)과 $2 사이여야 합니다.",
+       "config-memcache-badport": "Memcached 포트 번호는 $1와(과) $2 사이여야 합니다.",
        "config-extensions": "확장 기능",
        "config-extensions-help": "위에 나열된 확장 기능이 <code>./extensions</code>에서 발견되었습니다.\n\n추가적인 설정이 필요할 수 있습니다만 지금 활성화시킬 수 있습니다.",
        "config-skins": "스킨",
index a39d3a1..3343db7 100644 (file)
        "config-subscribe": "Donn de [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce \n<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>-Leß met de Aanköndijonge vum MehdijaWikki] abonnehre.",
        "config-subscribe-help": "Do kumme bloß winnish Meddeilunge un di jonn övver neu Versiohne vom MediaWiki un weeshtejje Saache vun däm sing Sesherheit.\nDo sullts se abbonneere, un Ding MediWiki_Projramme op der neue Shtand bränge, wann neu Version eruß kumme.",
        "config-subscribe-noemail": "Do has versöhk, der ohne en Addräß för Ding <i lang=\"en\">e-mail<i> aanzejävve, de Aanköndijonge för Aanköndijunge för neue Versione ze abboneere. Jivv en Addräß aan, wann De di Aanköndijonge hann wells.",
+       "config-pingback": "Jivv Dahte övver heh di Enschtallazjuhn vum Mehdijawikki aan de Äntwerkere.",
        "config-almost-done": "Do bes beinah dorsch!\nDo künnts jez der Räß vun de einzel Enschtällonge övverjonn, un et Wiki tiräktemang fähdesch opsäze.",
        "config-optional-continue": "De wells noch mih Frohre jeschtallt krijje un noch mih Enschtällonge maache?",
        "config-optional-skip": "Nä, lohß dä Ömshtand, donn eifarr_et Wiki opsäze.",
index 7124496..3f20c07 100644 (file)
@@ -36,6 +36,7 @@
        "config-db-install-account": "Hesabê bikarhêner bo avakirinê",
        "config-db-username": "Navê bikarhêner bo danagehê:",
        "config-db-password": "Şîfreya danegehê:",
+       "config-invalid-db-type": "Cureya danegehê ya nederbasdar",
        "config-db-web-account": "Hesabê danegehê bô têgihiştina tora înternetê",
        "config-mysql-myisam": "MyISAM",
        "config-mysql-utf8": "UTF-8",
index 3befe74..91df060 100644 (file)
        "config-admin-error-password": "Interne Feeler beim Setze vum Passwuert fir den Admin \"<nowiki>$1</nowiki>\": <pre>$2</pre>",
        "config-admin-error-bademail": "Dir hutt eng E-Mail-Adress aginn déi net valabel ass",
        "config-subscribe": "Sech op d'[https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Ukënnegunge vun neie Versiounen] abonnéieren.",
+       "config-pingback-help": "Wann Dir dës Optioun auswielt schéckt MediaWiki regelméisseg https://www.mediawiki.org Basisdaten iwwer dës MediaWiki-Instanz. An dësen Daten sinn zum Beispill de Systemtyp, d'PHP-Versioun an déi erausgesicht Datebank-Backend. D'Wikimedia Foundation gëtt dës Daten un d'MediaWiki-Entwéckler, fir ze hëllefen d'Entwécklung an der Zukunft efficace z'organiséieren. Dës Date gi fir Äre System geschéckt:\n<pre>$1</pre>",
        "config-almost-done": "Dir sidd bal fäerdeg!\nDir kënnt elo déi Astellungen déi nach iwwreg sinn iwwersprangen an d'Wiki elo direkt installéieren.",
        "config-optional-continue": "Stellt mir méi Froen.",
        "config-optional-skip": "Ech hunn es genuch, installéier just d'Wiki.",
diff --git a/includes/installer/i18n/lij.json b/includes/installer/i18n/lij.json
new file mode 100644 (file)
index 0000000..3670744
--- /dev/null
@@ -0,0 +1,54 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Giromin Cangiaxo"
+               ]
+       },
+       "config-desc": "Programma de installaçion pe MediaWiki",
+       "config-title": "Installaçion de MediaWiki $1",
+       "config-information": "Informaçioin",
+       "config-localsettings-upgrade": "L'è stæto rilevou un file <code>LocalSettings.php</code>.\nPe aggiornâ questa installaçion, se prega de insei o valô de <code>$wgUpgradeKey</code> inta casella chì de sotta.\nO poei atrovâ in <code>LocalSettings.php</code>.",
+       "config-localsettings-cli-upgrade": "L 'è stæto rilevou un file <code>LocalSettings.php</code>.\nPe aggiornâ questa installaçion, eseguî <code>update.php</code>",
+       "config-localsettings-key": "Ciave de aggiornamento:",
+       "config-localsettings-badkey": "A ciave d'agiornamento che t'hæ fornio a no l'è corretta.",
+       "config-upgrade-key-missing": "L'è stæto rilevou un'installaçion existente de MediaWiki.\nPe aggiornâ quest'installaçion, se prega de insei a seguente riga inta parte infeiô do to <code>LocalSettings.php</code>:\n\n$1",
+       "config-localsettings-incomplete": "O file <code>LocalSettings.php</code> existente pâ ese incompleto.\nA variabile $1 a no l'è impostâ.\nCangia <code>LocalSettings.php</code> de moddo che questa variabile a segge impostâ e fanni clic insce \"{{int:Config-continue}}\".",
+       "config-localsettings-connection-error": "S'è veificou un errô durante a connescion a-o database doeuviando e impostaçioin specificæ in <code>LocalSettings.php</code>. Se prega de coreze queste impostaçioin e riprovâ.\n\n$1",
+       "config-session-error": "Errô inte l'avvio da sescion: $1",
+       "config-session-expired": "I dæti da sescion pan ese descheiti.\nE sescioin son configuæ pe 'na duata de $1.\nTi poeu aomentâla impostando <code>session.gc_maxlifetime</code> into file php.ini.\nRiavvia o processo d'installaçion.",
+       "config-no-session": "I dæti da sescion son andæti persci!\nControlla o to file php.ini e aseguite che <code>session.save_path</code> o l'è impostou insce 'na directory apropiâ.",
+       "config-your-language": "A to lengua:",
+       "config-your-language-help": "Seleçion-a una lengua da doeuviâ  durante o processo d'installaçion.",
+       "config-wiki-language": "A lengua do wiki:",
+       "config-wiki-language-help": "Seleçion-a a lengua ch'a saiâ prevalentemente doeuviâ into wiki.",
+       "config-back": "inderê",
+       "config-continue": "Continnoa →",
+       "config-page-language": "Lengua",
+       "config-page-welcome": "Benvegnui a MediaWiki!",
+       "config-page-dbconnect": "Connescion a-o database",
+       "config-page-upgrade": "Agiornamento de l'installaçion existente",
+       "config-page-dbsettings": "Impostaçioin do database",
+       "config-page-name": "Nomme",
+       "config-page-options": "Opçioin",
+       "config-page-install": "Installa",
+       "config-page-complete": "Completa!",
+       "config-page-restart": "Riavvio installaçion",
+       "config-page-readme": "Lezime",
+       "config-page-releasenotes": "Notte de verscion",
+       "config-page-copying": "Copia",
+       "config-page-upgradedoc": "Aggiornamento",
+       "config-page-existingwiki": "Wiki existenti",
+       "config-help-restart": "Ti voeu scassâ tutti i dæti sarvæ che ti t'hæ inseio e riavviâ o processo de installaçion?",
+       "config-restart": "Scì, riavvia",
+       "config-welcome": "=== Controllo de l'ambiente ===\nSaiâ eseguio di controlli de base pe vedde se questo ambiente o l'è adatto pe l'installaçion de MediaWiki.\nRegordite de includde queste informaçioin se ti domandi ascistença insce comme completâ l'installaçion.",
+       "config-copyright": "=== Copyright e termini ===\n\n$1\n\nQuesto programma o l'è un software libero; ti poeu redistriboîlo e/ò modificâlo segondo i termi da GNU General Public License, comme pubbricâ da-a Free Software Foundation; ò a verscion 2 da Liçença ò (a proppia scelta) qualunque verscion succesciva.\n\nQuesto programma o l'è distribuio inta sperança ch'o segge utile, ma SENSA ARCUNA GARANTIA; sença manco a garantia impliçita de NEGOSSIABILITÆ o de APPRICABILITÆ PE UN PARTICOL SCOPO.\nS'amie a GNU General Public License pe maggioî dettaggi.\n\nQuesto programma o dev'ese distribuio insemme a <doclink href=Copying>una copia da GNU General Public License</doclink>; in caxo contraio, se ne poeu otegnî un-a scrivendo a-a Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA oppù [http://www.gnu.org/copyleft/gpl.html lezila inta ræ'].",
+       "config-sidebar": "* [https://www.mediawiki.org Paggina prinçipâ MediaWiki]\n* [https://www.mediawiki.org/wiki/Agiutto:Guidda a-i contegnui pe utenti]\n* [https://www.mediawiki.org/wiki/Manoâ:Guidda ai contegnui per admin]\n* [https://www.mediawiki.org/wiki/Manoâ:FAQ FAQ]\n----\n* <doclink href=Readme>Lezime</doclink>\n* <doclink href=ReleaseNotes>Notte de verscion</doclink>\n* <doclink href=Copying>Copie</doclink>\n* <doclink href=UpgradeDoc>Aggiornamenti</doclink>",
+       "config-env-good": "L'ambiente o l'è stæto controllou.\nL'è poscibile installâ MediaWiki.",
+       "config-env-bad": "L'ambiente o l'è stæto controllou.\nNon l'è poscibbile installâ MediaWiki.",
+       "config-env-php": "PHP $1 o l'è installou.",
+       "config-env-hhvm": "HHVM $1 o l'è installou.",
+       "config-unicode-using-intl": "Adoeuvia [http://pecl.php.net/intl l'estenscion PECL intl] pe-a normalizzaçion Unicode.",
+       "config-unicode-pure-php-warning": "'''Attençion:''' [http://pecl.php.net/intl l'estenscion PECL intl] a no l'è disponibile pe gestî a normalizzaçion Unicode, quindi se torna a-a lenta implementaçion in PHP puo.\nSe ti esegui un scito a ato traffego, ti doviesci leze arcun-e conscidiaçioin in sciâ [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations normalizzaçion Unicode].",
+       "config-unicode-update-warning": "'''Attençion:''' a verscion installaa do gestô pe-a normalizzaçion Unicode a l'adoeuvia una vegia verscion da libraia [http://site.icu-project.org/ do progetto ICU].\nTi doviesci [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations aggiornâ] se ti voeu doeuviâ l'Unicode.",
+       "config-no-db": "Imposcibile trovâ un driver adatto pe-o database! L'è necessaio installâ un driver pe PHP.\n{{PLURAL:$2|O seguente formato de database o l'è supportou|I seguenti formati de database son supportæ}}: $1.\n\nSe ti compilli PHP aotonomamente, riconfiguilo attivando un client database, presempio utilizzando <code>./configure --with-mysqli</code>.\nQualoa t'avesci installou PHP pe mezo de 'n pacchetto Debian ò Ubuntu, alloa ti devi installâ o pacchetto <code>php5-mysql</code> ascì."
+}
index fcba685..22cceb4 100644 (file)
@@ -98,6 +98,7 @@
        "config-mysql-innodb": "InnoDB",
        "config-mysql-myisam": "MyISAM",
        "config-mysql-charset": "Duomenų bazės simbolių rinkinys:",
+       "config-mysql-binary": "Dvejetainis",
        "config-mysql-utf8": "UTF-8",
        "config-mssql-auth": "Autentifikavimo tipas:",
        "config-mssql-sqlauth": "SQL Serverio autentifikavimas",
        "config-install-user": "Kuriamas duomenų bazės naudotojas",
        "config-install-user-alreadyexists": "Naudotojas „$1“ jau yra",
        "config-install-user-create-failed": "Nepavyko sukurti naudotojo „$1“: $2",
+       "config-install-user-missing": "Nurodytas vartotojas „$1“ neegzistuoja.",
+       "config-install-user-missing-create": "Nurodytas vartotojas „$1“ neegzistuoja.\nPrašome pažymėti „sukurti paskyrą“ laukelį žemiau jei norite jį sukurti.",
        "config-install-tables": "Kuriamos lentelės",
+       "config-install-tables-exist": "<strong>Įspėjimas:</strong> MediaWiki lentelės, atrodo, jau egzistuoja.\nKūrimas praleidžiamas.",
+       "config-install-tables-failed": "<strong>Klaida:</strong> Lentelės sukūrimas nepavyko dėl šios klaidos: $1",
+       "config-install-interwiki-list": "Nepavyko perskaityti failo <code>interwiki.list</code>.",
+       "config-install-interwiki-exists": "<strong>Įspėjimas:</strong> Interwiki lentelė, atrodo, jau turi įrašų.\nNumatytasis sąrašas praleidžiamas.",
        "config-install-stats": "Inicijuojamos statistikos",
        "config-install-keys": "Generuojami slapti raktai",
+       "config-install-sysop": "Administratoriaus vartotojo paskyra kuriama",
+       "config-install-mainpage": "Kuriamas pagrindinis puslapis su numatytu turiniu",
+       "config-install-extension-tables": "Kuriamos lentelės įgalintiems plėtiniams",
+       "config-install-mainpage-failed": "Nepavyko įterpti pagrindinio puslapio: $1",
        "config-install-done": "'''Sveikiname!'''\nJūs sėkmingai įdiegėte MediaWiki.\n\nĮdiegimo programa sukūrė <code>LocalSettings.php</code> failą.\nJame yra visos jūsų konfigūracijos.\n\nJums reikės atsisiųsti ir įdėti jį į savo wiki įdiegimo bazę (pačiame kataloge, kaip index.php). Atsisiuntimas turėtų prasidėti automatiškai.\n\nJei atsisiuntimas nebuvo pasiūlytas, arba jį atšaukėte, galite iš naujo atsisiųsti paspaudę žemiau esančią nuorodą:\n\n$3\n\n'''Pastaba:''' Jei jūs to nepadarysite dabar, tada šis sukurtas konfigūracijos failas nebus galimas vėliau, jei išeisite iš įdiegimo be atsisiuntimo.\n\nKai baigsite, jūs galėsite '''[$2 įeiti į savo viki]'''.",
        "config-download-localsettings": "Atsisiųsti <code>LocalSettings.php</code>",
        "config-help": "pagalba",
        "config-help-tooltip": "spustelėkite išplėtimui",
        "config-nofile": "Failas \"$1\" nerastas. Ar jis buvo ištrintas?",
-       "mainpagetext": "'''MediaWiki sėkmingai įdiegta.'''",
+       "mainpagetext": "<strong>MediaWiki sėkmingai įdiegta.</strong>",
        "mainpagedocfooter": "Informacijos apie wiki programinės įrangos naudojimą, ieškokite [https://meta.wikimedia.org/wiki/Help:Contents žinyne].\n\n== Pradžiai ==\n\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Konfigūracijos nustatymų sąrašas]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki DUK]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki pranešimai paštu apie naujas versijas]"
 }
index 36ec508..f8b66c1 100644 (file)
        "config-subscribe": "Претплатете се на [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce release поштенскиот список за известувања].",
        "config-subscribe-help": "Ова е нископрометен поштенски список кој се користи за соопштувања во врска со изданија, вклучувајќи важни безбедносни соопштенија.\nТреба да се претплатите и да ја надградувате вашата воспоставка на МедијаВики кога излегуваат нови верзии.",
        "config-subscribe-noemail": "Се обидовте да се претплатите на поштенскиот список со известувања за нови изданија без да наведете е-пошта.\nНаведете е-поштенска адреса ако сакате да се претплатите на списокот.",
+       "config-pingback": "Споделувај податоци за воспоставката со разработувачите на МедијаВики.",
+       "config-pingback-help": "Ако ја изберете оваа можност, МедијаВики повремено ќе му испраќа на https://www.mediawiki.org основни податоци за овој примерок на МедијаВики. Тука спаѓаат видот на системот, PHP-верзијата и избраната базна заднина. Фондацијата Викимедија ги споделува овие податоци со разработувачите на МедијаВики со цел да им даде насоки за разработка во идните верзии. За вашиот систем ќе се испратат следниве податоци:\n<pre>$1</pre>",
        "config-almost-done": "Уште малку сте готови!\nСега можете да ги прескокнете преостанатите поставувања и веднаш да го воспоставите викито.",
        "config-optional-continue": "Постави ми повеќе прашања.",
        "config-optional-skip": "Веќе ми здосади, дај само воспостави го викито.",
index 1d9a78d..0fc0e9f 100644 (file)
@@ -1,8 +1,9 @@
 {
        "@metadata": {
                "authors": [
-                       "Lionslayer"
+                       "Lionslayer",
+                       "Ninjastrikers"
                ]
        },
-       "mainpagetext": "'''မီဒီယာဝီကီကို အောင်မြင်စွာ သွင်းပြီးပါပြီ။'''"
+       "mainpagetext": "<strong>မီဒီယာဝီကီကို အောင်မြင်စွာ သွင်းပြီးပါပြီ။</strong>"
 }
index fca2977..7c50e82 100644 (file)
@@ -6,13 +6,14 @@
                        "सरोज कुमार ढकाल",
                        "Ganesh Paudel",
                        "बिप्लब आनन्द",
-                       "Nirjal stha"
+                       "Nirjal stha",
+                       "राम प्रसाद जोशी"
                ]
        },
        "config-desc": "मेडियाविकिको लागि स्थापक",
        "config-title": "मेडिया विकि $1 स्थापना",
        "config-information": "जानकारी",
-       "config-localsettings-badkey": "तपाà¤\87लà¥\87 à¤¦à¤¿à¤¨à¥\81 à¤­à¤\8fà¤\95à¥\8b à¤\95à¥\81नà¥\8dà¤\9cà¥\80 à¤\97लत à¤\9b ।",
+       "config-localsettings-badkey": "तपाà¤\88à¤\82लà¥\87 à¤¦à¤¿à¤\8fà¤\95à¥\8b à¤\95à¥\81à¤\82à¤\9cà¥\80 à¤®à¤¿à¤²à¥\87न ।",
        "config-your-language": "तपाईंको भाषा:",
        "config-your-language-help": "इन्स्टल गर्दा उपयोग गर्ने भाषा छान्नुहोस् ।",
        "config-wiki-language": "विकि भाषाहरू",
index 193e445..8d54c98 100644 (file)
        "config-subscribe": "Zapisz się na [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce listę pocztową z ogłoszeniami o nowych wersjach].",
        "config-subscribe-help": "Jest to lista o małej liczbie wiadomości, wykorzystywana do przesyłania informacji o udostępnieniu nowej wersji oraz istotnych sprawach dotyczących bezpieczeństwa.\nPowinieneś zapisać się na tę listę i aktualizować zainstalowane oprogramowanie MediaWiki gdy pojawia się nowa wersja.",
        "config-subscribe-noemail": "Próbowano subskrybować listę mailingową ogłoszeń wersji bez podania adresu e-mail.\nProszę podać adres e-mail, jeśli chcesz subskrybować listę wysyłkową.",
+       "config-pingback": "Udostępnij dane o instalacji twórcom MediaWiki.",
+       "config-pingback-help": "Jeżeli wybierzesz tę opcję, MediaWiki będzie okresowo wysyłać na https://www.mediawiki.org podstawowe dane na temat tej instancji MediaWiki. Te dane zawierają np. typ systemu, wersję PHP i wybrany silnik bazy danych. Fundacja Wikimedia dzieli się tymi danymi z twórcami MediaWiki, aby pomóc w podejmowaniu dalszych wysiłków co do rozwoju. Poniższe dane o Twoim systemie zostaną wysłane:\n<pre>$1</pre>",
        "config-almost-done": "To już prawie koniec!\nMożesz pominąć pozostałe czynności konfiguracyjne i zainstalować wiki.",
        "config-optional-continue": "Zadaj mi więcej pytań.",
        "config-optional-skip": "Jestem już znudzony, po prostu zainstaluj wiki.",
        "config-install-extension-tables": "Tworzenie tabel dla aktywnych rozszerzeń",
        "config-install-mainpage-failed": "Nie udało się wstawić strony głównej: $1",
        "config-install-done": "<strong>'''Gratulacje!</strong>\nUdało Ci się zainstalować MediaWiki.\n\nInstalator wygenerował plik konfiguracyjny <code>LocalSettings.php</code>.\n\nMusisz go pobrać i umieścić w katalogu głównym Twojej instalacji wiki (tym samym katalogu co index.php). Pobieranie powinno zacząć się automatycznie.\n\nJeżeli pobieranie nie zostało zaproponowane lub jeśli użytkownik je anulował, można ponownie uruchomić pobranie klikając poniższe łącze:\n\n$3\n\n<strong>Uwaga</strong>: Jeśli nie zrobisz tego teraz, wygenerowany plik konfiguracyjny nie będzie już dostępny po zakończeniu instalacji.\n\nPo załadowaniu pliku konfiguracyjnego możesz <strong>[$2 wejść na wiki]</strong>.",
+       "config-install-done-path": "<strong>Gratulacje!</strong>\nZainstalowałeś właśnie MediaWiki.\n\nInstalator wygenerował plik <code>LocalSettings.php</code>.\nZawiera całą Twoją konfigurację.\n\nMusisz go pobrać i umieścić w <code>$4</code>. Pobieranie powinno rozpocząć się automatycznie.\n\nJeżeli nie pojawiła się informacja o pobieraniu lub jeżeli ja anulowałeś, kliknij poniższy link:\n\n$3\n\n<strong>Uwaga:</strong> Jeżeli nie zrobisz tego teraz, wygenerowany plik konfiguracyjny nie będzie potem dostępny, jeżeli wyjdziesz z instalacji bez jego pobrania.\n\nGdy to będzie zrobione, możesz <strong>[$2 wejść na swoją wiki]</strong>.",
        "config-download-localsettings": "Pobierz <code>LocalSettings.php</code>",
        "config-help": "pomoc",
        "config-help-tooltip": "kliknij, aby rozwinąć",
index 69a6830..833e7d6 100644 (file)
        "config-subscribe": "Used as label for the installer checkbox",
        "config-subscribe-help": "\"Low-volume\" in this context means that there will be few e-mails to that mailing list per time period.",
        "config-subscribe-noemail": "Error text in MediaWiki installer.",
+       "config-pingback": "Option in the MediaWiki installer to submit data about this installation to MediaWiki.org.",
+       "config-pingback-help": "Explains what data will be shared if the user chooses to submit data to MediaWiki.org. $1 is the JSON data that will be sent",
        "config-almost-done": "Status message in the MediaWiki installer.",
        "config-optional-continue": "Option in the MediaWiki installer to make a more fine-tuned installation.",
        "config-optional-skip": "Option in the MediaWiki installer to start executing the actual installation and stop asking questions.",
index 34ef3e8..53b4c9e 100644 (file)
        "config-subscribe": "Подписаться на [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce рассылку новостей о появлении новых версий MediaWiki].",
        "config-subscribe-help": "Это список рассылки с малым числом сообщений, используется для анонса новых выпусков и сообщений о проблемах с безопасностью.\nВам следует подписаться на него и обновлять движок MediaWiki, по мере выхода новых версий.",
        "config-subscribe-noemail": "Вы попытались подписаться на список рассылки уведомлений о новых выпусках без указания адреса электронной почты.\nУкажите адрес электронной почты, если вы хотите подписаться на список рассылки.",
+       "config-pingback": "Поделиться сведениями об этой установке с разработчикам MediaWiki.",
+       "config-pingback-help": "Если вы выберите этот вариант, MediaWiki будет периодически отправлять на https://www.mediawiki.org основные сведения об этом экземпляре MediaWiki. К этим данным относятся, в частности, тип операционной системы, версия PHP и выбранная СУБД. Фонда Викимедиа делится этими данными с разработчиками MediaWiki, чтобы помочь им в проведении будущих разработок. Следующие данные будут отправлены для вашей системы:\n<pre>$1</pre>",
        "config-almost-done": "Вы почти у цели!\nОстальные настройки можно пропустить и приступить к установке вики.",
        "config-optional-continue": "Произвести тонкую настройку",
        "config-optional-skip": "Хватит, установить вики",
index 45586ef..1dcd2c1 100644 (file)
@@ -11,7 +11,7 @@
        "config-title": "Inštalácia MediaWiki $1",
        "config-information": "Informácie",
        "config-localsettings-key": "Aktualizačný kľúč:",
-       "config-localsettings-badkey": "Zadaný kľúč je nesprávny.",
+       "config-localsettings-badkey": "Zadaný aktualizačný kľúč je nesprávny.",
        "config-your-language": "Váš jazyk:",
        "config-your-language-help": "Vyberte jazyk, ktorý chcete použiť počas inštalácie.",
        "config-wiki-language": "Wiki jazyk:",
@@ -51,6 +51,8 @@
        "config-missing-db-name": "Musíte zadať hodnotu pre \"{{int:config-db-name}}\".",
        "config-missing-db-host": "Musíte zadať hodnotu pre \"{{int:config-db-host}}\".",
        "config-missing-db-server-oracle": "Musíte zadať hodnotu pre \"{{int:config-db-host-oracle}}\".",
+       "config-site-name": "Názov wiki:",
+       "config-site-name-blank": "Zadajte názov stránky.",
        "config-ns-generic": "Projekt",
        "config-admin-box": "Účet správcu",
        "config-admin-name": "Vaše používateľské meno:",
index 829aae4..20ebf9b 100644 (file)
        "config-subscribe": "Prenumerera på [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce e-postlistan för kungörelser av nya versioner].",
        "config-subscribe-help": "Detta är en e-postlista med låg volym vilken används för meddelanden om nya versionssläpp, inklusive viktiga säkerhetsmeddelanden.\nDu bör prenumerera på den och uppdatera din MediaWiki-installation när nya versioner kommer ut.",
        "config-subscribe-noemail": "Du försökte att prenumerera på e-postlistan för versionssläppsmeddelanden utan att tillhandahålla en e-postadress.\nAnge en e-postadress om du vill prenumerera på e-postlistan.",
+       "config-pingback": "Dela data om denna installation med MediaWikis utvecklare.",
+       "config-pingback-help": "Om du väljer det här alternativet kommer MediaWiki periodvis pinga https://www.mediawiki.org med grundläggande data om den här MediaWiki-instansen. Denna data innehåller exempelvis typen av system, PHP-version och valde databas. Wikimedia Foundation delar denna data med MediaWiki-utvecklare för att hjälpa till att guida framtida utvecklingsarbete. Följande data kommer att skickas till ditt system: <pre>$1</pre>",
        "config-almost-done": "Du är nästan färdig!\nDu kan nu hoppa över återstående konfigurationer och installera wikin direkt.",
        "config-optional-continue": "Ställ fler frågor till mig.",
        "config-optional-skip": "Jag är redan uttråkad, bara installera wiki.",
index 6965a15..6ef4dd3 100644 (file)
@@ -1,30 +1,34 @@
 {
        "@metadata": {
                "authors": [
-                       "VASANTH S.N."
+                       "VASANTH S.N.",
+                       "Vishwanatha Badikana"
                ]
        },
-       "config-title": "ಮೀಡಿಯಾವಿಕಿ ಆವೃತ್ತಿ $1 ರ ಅನುಸ್ಥಾಪನೆ",
+       "config-desc": "ಮೀಡಿಯ ವಿಕಿದ ಪ್ರತಿಸ್ಟಾಪನೆ",
+       "config-title": "ಮೀಡಿಯಾವಿಕಿ ಆವೃತ್ತಿ $1 ರ ಪ್ರತಿಸ್ಟಾಪನೆ",
        "config-information": "ಮಾಹಿತಿ",
-       "config-localsettings-key": "ಉನ್ನತೀಕರಣ ಕೀಲಿ",
-       "config-session-error": "ಅವಧಿ ಪ್ರಾರಂಭದ ದೋಷ: $1",
+       "config-localsettings-upgrade": "ಒಂಜಿ<code>LocalSettings.php</code>ದ ಕಡತೊ ಪತ್ತೆ ಆತ್ಂಡ್.  ಈ ಪ್ರತಿಸ್ಟಾಪನೆನ್ ಪೊಸತ್ ಮಲ್ಪೆರೆ, ದಯದೀಡ್ದ್ ತಿರ್ತ್‌ದ ಪಟ್ಟಿಗೆದ <code>$wgUpgradeKey</code>ದ ಬಿಲೆನ್ ನಮೂದಿಸಲೆ.\nಈರ್ <code>LocalSettings.php</code>ನ್ ನಾಡೊಲಿ",
+       "config-localsettings-cli-upgrade": "ಒಂಜಿ<code>LocalSettings.php</code>ದ ಕಡತೊ ಪತ್ತೆ ಆತ್ಂಡ್.  ಈ ಪ್ರತಿಸ್ಟಾಪನೆನ್ ಪೊಸತ್ ಮಲ್ಪೆರೆ, ದಯದೀಡ್ದ್ ತಿರ್ತ್‌ದ ಪಟ್ಟಿಗೆದ <code>$wgUpgradeKey</code>ದ ಬಿಲೆನ್ ನಮೂದಿಸಲೆ.\nದಯಮಲ್ತ್ <code>LocalSettings.php</code>ಗ್ ಬದಲಾದ್  ಬಲಿಪಾಲೆ",
+       "config-localsettings-key": "ಏಲಿಗೆದ ಕೀ",
+       "config-session-error": "ಸಬೆ ಸುರುಮಲ್ಪುನ ದೋಸೊ: $1",
        "config-your-language": "ಇರೆನಾ ಬಾಸೆ",
-       "config-wiki-language": "ವಿà²\95ಿ à²­à²¾à²·ೆ:",
+       "config-wiki-language": "ವಿà²\95ಿ à²¬à²¾à²¸ೆ:",
        "config-back": "← ಪಿರ",
        "config-continue": "ಮುಂದುವರೆಸಾಲೆ →",
-       "config-page-language": "ಭಾಸೆ",
+       "config-page-language": "ಬಾಸೆ",
        "config-page-welcome": "ಮಾಧ್ಯಮವಿಕಿಗ್ ಸ್ವಾಗತ",
-       "config-page-dbconnect": "ದತà³\8dತಾà²\82ಶಸà²\82à²\9aಯà²\97à³\8d à²¸à²\82ಪರà³\8dà²\95ಕೊರ್ಲೆ",
+       "config-page-dbconnect": "ದತà³\8dತಾà²\82ಸà³\8a à²¸à²\82à²\9aಯà³\8aà²\97à³\8d à²\95à³\8aಲಿà²\95à³\86ಕೊರ್ಲೆ",
        "config-page-name": "ಪುದರ್",
        "config-page-options": "ಆಯ್ಕೆಲು",
        "config-page-install": "ಸ್ಥಾಪಿಸಾಲೆ",
-       "config-page-complete": "ಪà³\82ರà³\8dಣ!",
-       "config-page-readme": "à²\8eನನà³\8d à²\93ದà³\81ಲà³\87",
-       "config-page-releasenotes": "ಬà³\81ಡà³\81à²\97à³\8aಡà³\86ದ à²\9fಿಪà³\8dಪಣಿಲà³\81",
+       "config-page-complete": "ಮà³\81à²\97ಿà²\82ಡà³\8d!",
+       "config-page-readme": "à²\8eನನà³\8d à²\93ದà³\81ಲà³\86",
+       "config-page-releasenotes": "ಬುಡುಗಡೆದ ಟಿಪ್ಪಣಿಲು",
        "config-page-copying": "ನಕಲ್ ಮಲ್ತೊಂದುಂಡು",
        "config-page-upgradedoc": "ಪರಿಷ್ಕರಣೆ ಆವೊಂದುಂಡು",
-       "config-page-existingwiki": "ಪà³\8dರಸà³\8dತà³\81ತ ವಿಕಿ",
-       "config-restart": "ಸರಿ,à²\95à³\81ಡ à²ªà³\8dರಾರà²\82ಭ ಮಲ್ಪುಲೆ",
+       "config-page-existingwiki": "à²\87ತà³\8dತà³\86ದ ವಿಕಿ",
+       "config-restart": "ಸರಿ,à²\95à³\81ಡ à²¸à³\81ರà³\81 ಮಲ್ಪುಲೆ",
        "config-db-type": "ದತ್ತಾಂಶಸಂಚಯ ಮಾದರಿ:",
        "config-db-host-oracle": "ದತ್ತಾಂಶಸಂಚಯ TNS:",
        "config-db-wiki-settings": "ಈ ವಿಕಿಯನ್ನು ಗುರುತಿಸಾಲೆ",
index 8bc18b0..5240f13 100644 (file)
        "config-subscribe": "Підписатися на [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce розсилку анонсів нових версій MediaWiki].",
        "config-subscribe-help": "Це список розсилки з малим обсягом повідомлень, що використовується для анонсування релізів, а також важливих повідомлень про безпеку.\nВам варто підписати і оновлювати інсталяцію MediaWiki, коли з'являтимуться нові версії.",
        "config-subscribe-noemail": "Ви намагались підписатись на розсилку анонсів релізів, не вказавши адреси електронної пошти.\nБудь ласка, вкажіть адресу електронної пошти, якщо хочете підписатись на розсилку.",
+       "config-pingback": "Поділитися даними про цю інсталяцію з розробниками MediaWiki.",
+       "config-pingback-help": "Якщо Ви обираєте цю опцію, MediaWiki періодично пінгуватиме https://www.mediawiki.org базовими даними про цю інсталяцію MediaWiki. Дані включають, наприклад, тип системи, версію PHP, обраний бекенд бази даних. Фонд Вікімедіа ділиться цими даними з розробниками MediaWiki, щоб допомогти спрямувати подальні розробки. Від Вашої системи надсилатимуться такі дані:\n<pre>$1</pre>",
        "config-almost-done": "Майже готово!\nВи можете зараз пропустити налаштування, що залишилось, і встановити вікі прямо зараз.",
        "config-optional-continue": "Запитуйте ще.",
        "config-optional-skip": "Це вже втомлює, просто встановити вікі.",
index 7564d19..365231c 100644 (file)
        "config-subscribe": "Theo dõi [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce danh sách thư thông báo phát hành].",
        "config-subscribe-help": "thông báo an ninh.\nBạn nên đồng ý với nó và cập nhật bản cài đặt MediaWiki của bạn khi phiên bản mới xuất hiện.",
        "config-subscribe-noemail": "Bạn đã cố gắng để đăng ký vào danh sách thư thông báo phát hành mà không cung cấp một địa chỉ thư điện tử nào cả.\nVui lòng cung cấp một địa chỉ thư điện tử nếu bạn muốn đăng ký vào danh sách thư.",
+       "config-pingback": "Chia sẻ dữ liệu về bản cài đặt này với nhóm phát triển MediaWiki.",
+       "config-pingback-help": "Nếu bạn kích hoạt chức năng này, MediaWiki sẽ thỉnh thoảng gửi cho https://www.mediawiki.org/ dữ liệu cơ bản về bản cài đặt MediaWiki này. Dữ liệu này có chẳng hạn loại máy, phiên bản PHP, và phía sau cơ sở dữ liệu đã chọn. Quỹ Wikimedia chia sẻ dữ liệu này với các nhà phát triển MediaWiki để giúp họ trong việc phát triển phần mềm vào tương lai. Dữ liệu sau sẽ được gửi từ hệ thống này:\n<pre>$1</pre>",
        "config-almost-done": "Bạn gần như đã hoàn tất!\nBây giờ bạn có thể bỏ qua cấu hình còn lại và cài đặt wiki ngay bây giờ.",
        "config-optional-continue": "Hỏi tôi về thêm chi tiết.",
        "config-optional-skip": "Chán quá, cài đặt wiki rỗi.",
        "config-install-extension-tables": "Đang tạo bảng cho các phần mở rộng được kích hoạt",
        "config-install-mainpage-failed": "Không thể chèn trang đầu: $1",
        "config-install-done": "<strong>Xin chúc mừng!</strong>\nBạn đã cài đặt MediaWiki.\n\nBộ cài đặt đã tạo ra một tập tin <code>LocalSettings.php</code>.\nTập tin này chứa tất cả các cấu hình của bạn.\n\nBạn sẽ cần phải tải nó về và đặt nó trong thư mục cài đặt wiki của bạn (cùng thư mục với index.php). Việc tải về có lẽ sẽ được khởi động tự động.\n\nNếu bản tải về không được cung cấp, hoặc nếu bạn hủy bỏ nó, bạn có thể khởi động lại tải về bằng cách nhấn vào liên kết dưới đây:\n\n$3\n\n<strong>Lưu ý:</strong> Nếu bạn không làm điều này ngay bây giờ, điều này sẽ tạo ra tập tin cấu hình sẽ không có giá trị cho bạn sau này nếu bạn thoát khỏi trình cài đặt mà không tải nó về.\n\nKhi đã việc tải về đã hoàn thành, bạn có thể <strong>[$2 truy cập trang wiki của bạn]</strong>.",
+       "config-install-done-path": "<strong>Xin chúc mừng!</strong>\nBạn đã cài đặt MediaWiki.\n\nBộ cài đặt đã tạo ra một tập tin <code>LocalSettings.php</code>.\nTập tin này chứa tất cả các cấu hình của bạn.\n\nBạn sẽ cần phải tải nó về và đặt nó tại <code>$4</code>. Việc tải về có lẽ sẽ được khởi động tự động.\n\nNếu bản tải về không được cung cấp, hoặc nếu bạn hủy bỏ nó, bạn có thể khởi động lại tải về bằng cách nhấn vào liên kết dưới đây:\n\n$3\n\n<strong>Lưu ý:</strong> Nếu bạn không làm điều này ngay bây giờ, điều này sẽ tạo ra tập tin cấu hình sẽ không có giá trị cho bạn sau này nếu bạn thoát khỏi trình cài đặt mà không tải nó về.\n\nKhi đã việc tải về đã hoàn thành, bạn có thể <strong>[$2 truy cập trang wiki của bạn]</strong>.",
        "config-download-localsettings": "Tải về <code>LocalSettings.php</code>",
        "config-help": "Trợ giúp",
        "config-help-tooltip": "nhấn chuột để mở rộng",
index 8b8d02d..6b5bfe0 100644 (file)
@@ -7,6 +7,7 @@
                ]
        },
        "config-information": "信息",
+       "config-back": "← 转去",
        "config-page-language": "闲话",
        "mainpagetext": "<strong>MediaWiki安装好哉。</strong>",
        "mainpagedocfooter": "请访问[https://meta.wikimedia.org/wiki/Help:Contents 用户手册]以获得使用此维基软件个信息!\n\n== 入门 ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings MediaWiki 配置设置列表]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki 常见问题解答]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki 发布邮件列表]"
index 07e1fc3..d541696 100644 (file)
        "config-subscribe": "订阅[https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce 发行公告邮件列表]。",
        "config-subscribe-help": "此低流量的邮件列表仅用于发行公告,其中包括重要安全公告。请订阅该列表以便在新的版本推出时升级您的MediaWiki。",
        "config-subscribe-noemail": "您选择了订阅发行公告邮件列表,但没有提供电子邮件地址。请提供一个电子邮件地址以订阅邮件列表。",
+       "config-pingback": "与MediaWiki开发人员分享有关此安装程序的数据。",
+       "config-pingback-help": "如果您选择此选项,MediaWiki将定期与https://www.mediawiki.org通信,传输与此MediaWiki实例相关的基础数据。此数据包括例如系统类型、PHP版本和选择的数据库后端。维基媒体基金会与MediaWiki开发人员分享此数据,以帮助引导将来的开发计划。以下数据将为您的系统发送:\n<pre>$1</pre>",
        "config-almost-done": "您几乎已经完成了!现在您可以跳过剩下的配置流程并立即安装wiki。",
        "config-optional-continue": "多问我一些问题吧。",
        "config-optional-skip": "我已经不耐烦了,赶紧安装我的wiki。",
index 53d001e..ab21779 100644 (file)
@@ -15,7 +15,8 @@
                        "LNDDYL",
                        "NigelSoft",
                        "Macofe",
-                       "Reke"
+                       "Reke",
+                       "Suchichi02"
                ]
        },
        "config-desc": "MediaWiki 安裝程式",
        "config-ns-site-name": "同 Wiki 名稱:$1",
        "config-ns-other": "其他 (請註明)",
        "config-ns-other-default": "我的 wiki",
-       "config-project-namespace-help": "許多 Wiki 以維基百科(Wikipedia)做為範例將政策頁面從內容頁面抽離,放置在 \"'''專案命名空間'''\" 中。\n所有在此命名空間裡的頁面都會有特定的字首,您可以在此處設定。\n通常這些字首是由該 Wiki 的名稱所衍伸出來,但無法使用標點符號,如 \"#\" 或 \":\"。",
+       "config-project-namespace-help": "許多 Wiki 以維基百科 (Wikipedia) 做為範例將政策頁面從內容頁面抽離,放置在 \"'''專案命名空間'''\" 中。\n所有在此命名空間裡的頁面都會有特定的字首,您可以在此處設定。\n通常這些字首是由該 Wiki 的名稱所衍伸出來,但無法使用標點符號,如 \"#\" 或 \":\"。",
        "config-ns-invalid": "您指定的命名空間 \"<nowiki>$1</nowiki>\" 無效,\n請指定另一個專案命名空間。",
        "config-ns-conflict": "您指定的命名空間 \"<nowiki>$1</nowiki>\" 與 MediaWiki 預設的命名空間衝突。\n請指定另一個專案命名空間。",
        "config-admin-box": "管理員帳號",
        "config-help-tooltip": "點選以展開",
        "config-nofile": "查無檔案 \"$1\",是否已被刪除?",
        "config-extension-link": "您是否了解您的 Wiki 支援 [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions 擴充套件]?\n\n\n您可以瀏覽 [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category 擴充套件分類] 或 [https://www.mediawiki.org/wiki/Extension_Matrix 擴充套件資料表] 以取得相關的資訊。",
-       "mainpagetext": "<strong>已成功安裝 MediaWiki。</strong>",
+       "mainpagetext": "<strong>已安裝 MediaWiki。</strong>",
        "mainpagedocfooter": "請參閱 [https://meta.wikimedia.org/wiki/Help:Contents 使用者手冊] 以取得使用 Wiki 的相關訊息!\n\n== 新手入門 ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings MediaWiki 系統設定]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki 常見問答集]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki 發佈郵寄清單]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources MediaWiki 介面在地化]"
 }
index 6ac165a..0e107b3 100644 (file)
@@ -37,7 +37,7 @@ use WANObjectCache;
  * and tree storage backends (SQL, CDB, and plain PHP arrays).
  *
  * All information is loaded on creation when called by $this->fetch( $prefix ).
- * All work is done on slave, because this should *never* change (except during
+ * All work is done on replica DB, because this should *never* change (except during
  * schema updates etc, which aren't wiki-related)
  *
  * @since 1.28
@@ -282,7 +282,7 @@ class ClassicInterwikiLookup implements InterwikiLookup {
                        $this->objectCache->makeKey( 'interwiki', $prefix ),
                        $this->objectCacheExpiry,
                        function ( $oldValue, &$ttl, array &$setOpts ) use ( $prefix ) {
-                               $dbr = wfGetDB( DB_SLAVE ); // TODO: inject LoadBalancer
+                               $dbr = wfGetDB( DB_REPLICA ); // TODO: inject LoadBalancer
 
                                $setOpts += Database::getCacheSetOptions( $dbr );
 
@@ -395,7 +395,7 @@ class ClassicInterwikiLookup implements InterwikiLookup {
         * @return array[] Interwiki rows
         */
        private function getAllPrefixesDB( $local ) {
-               $db = wfGetDB( DB_SLAVE ); // TODO: inject DB LoadBalancer
+               $db = wfGetDB( DB_REPLICA ); // TODO: inject DB LoadBalancer
 
                $where = [];
 
index 459910a..d0a7719 100644 (file)
@@ -26,7 +26,7 @@ use Interwiki;
 /**
  * Service interface for looking up Interwiki records.
  *
- * @singe 1.28
+ * @since 1.28
  */
 interface InterwikiLookup {
 
index d64be3c..020a684 100644 (file)
@@ -553,7 +553,7 @@ abstract class JobQueue {
        }
 
        /**
-        * Wait for any slaves or backup servers to catch up.
+        * Wait for any replica DBs or backup servers to catch up.
         *
         * This does nothing for certain queue classes.
         *
index 479ec32..f6b4d53 100644 (file)
@@ -236,7 +236,7 @@ class JobQueueDB extends JobQueue {
                        }
                        // Build the full list of job rows to insert
                        $rows = array_merge( $rowList, array_values( $rowSet ) );
-                       // Insert the job rows in chunks to avoid slave lag...
+                       // Insert the job rows in chunks to avoid replica DB lag...
                        foreach ( array_chunk( $rows, 50 ) as $rowBatch ) {
                                $dbw->insert( 'job', $rowBatch, $method );
                        }
@@ -245,10 +245,7 @@ class JobQueueDB extends JobQueue {
                                count( $rowSet ) + count( $rowList ) - count( $rows )
                        );
                } catch ( DBError $e ) {
-                       if ( $flags & self::QOS_ATOMIC ) {
-                               $dbw->rollback( $method );
-                       }
-                       throw $e;
+                       $this->throwDBException( $e );
                }
                if ( $flags & self::QOS_ATOMIC ) {
                        $dbw->endAtomic( $method );
@@ -264,7 +261,6 @@ class JobQueueDB extends JobQueue {
        protected function doPop() {
                $dbw = $this->getMasterDB();
                try {
-                       $dbw->commit( __METHOD__, 'flush' ); // flush existing transaction
                        $autoTrx = $dbw->getFlag( DBO_TRX ); // get current setting
                        $dbw->clearFlag( DBO_TRX ); // make each query its own transaction
                        $scopedReset = new ScopedCallback( function () use ( $dbw, $autoTrx ) {
@@ -460,7 +456,6 @@ class JobQueueDB extends JobQueue {
 
                $dbw = $this->getMasterDB();
                try {
-                       $dbw->commit( __METHOD__, 'flush' ); // flush existing transaction
                        $autoTrx = $dbw->getFlag( DBO_TRX ); // get current setting
                        $dbw->clearFlag( DBO_TRX ); // make each query its own transaction
                        $scopedReset = new ScopedCallback( function () use ( $dbw, $autoTrx ) {
@@ -737,7 +732,7 @@ class JobQueueDB extends JobQueue {
         */
        protected function getSlaveDB() {
                try {
-                       return $this->getDB( DB_SLAVE );
+                       return $this->getDB( DB_REPLICA );
                } catch ( DBConnectionError $e ) {
                        throw new JobQueueConnectionError( "DBConnectionError:" . $e->getMessage() );
                }
@@ -756,7 +751,7 @@ class JobQueueDB extends JobQueue {
        }
 
        /**
-        * @param int $index (DB_SLAVE/DB_MASTER)
+        * @param int $index (DB_REPLICA/DB_MASTER)
         * @return DBConnRef
         */
        protected function getDB( $index ) {
index a4b3241..8d57562 100644 (file)
@@ -255,7 +255,7 @@ class JobQueueGroup {
        }
 
        /**
-        * Wait for any slaves or backup queue servers to catch up.
+        * Wait for any replica DBs or backup queue servers to catch up.
         *
         * This does nothing for certain queue classes.
         *
index 1350958..022abd9 100644 (file)
@@ -21,7 +21,9 @@
  * @ingroup JobQueue
  */
 
+use MediaWiki\MediaWikiServices;
 use MediaWiki\Logger\LoggerFactory;
+use Liuggio\StatsdClient\Factory\StatsdDataFactory;
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerInterface;
 
@@ -41,7 +43,7 @@ class JobRunner implements LoggerAwareInterface {
        protected $logger;
 
        const MAX_ALLOWED_LAG = 3; // abort if more than this much DB lag is present
-       const LAG_CHECK_PERIOD = 1.0; // check slave lag this many seconds
+       const LAG_CHECK_PERIOD = 1.0; // check replica DB lag this many seconds
        const ERROR_BACKOFF_TTL = 1; // seconds to back off a queue due to errors
 
        /**
@@ -113,18 +115,20 @@ class JobRunner implements LoggerAwareInterface {
                        $response['reached'] = 'read-only';
                        return $response;
                }
+
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                // Bail out if there is too much DB lag.
                // This check should not block as we want to try other wiki queues.
-               list( , $maxLag ) = wfGetLB( wfWikiID() )->getMaxLag();
+               list( , $maxLag ) = $lbFactory->getMainLB( wfWikiID() )->getMaxLag();
                if ( $maxLag >= self::MAX_ALLOWED_LAG ) {
-                       $response['reached'] = 'slave-lag-limit';
+                       $response['reached'] = 'replica-lag-limit';
                        return $response;
                }
 
                // Flush any pending DB writes for sanity
-               wfGetLBFactory()->commitAll( __METHOD__ );
+               $lbFactory->commitAll( __METHOD__ );
 
-               // Catch huge single updates that lead to slave lag
+               // Catch huge single updates that lead to replica DB lag
                $trxProfiler = Profiler::instance()->getTransactionProfiler();
                $trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
                $trxProfiler->setExpectations( $wgTrxProfilerLimits['JobRunner'], __METHOD__ );
@@ -135,11 +139,11 @@ class JobRunner implements LoggerAwareInterface {
                $wait = 'wait'; // block to read backoffs the first time
 
                $group = JobQueueGroup::singleton();
-               $stats = RequestContext::getMain()->getStats();
+               $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
                $jobsPopped = 0;
                $timeMsTotal = 0;
                $startTime = microtime( true ); // time since jobs started running
-               $lastCheckTime = 1; // timestamp of last slave check
+               $lastCheckTime = 1; // timestamp of last replica DB check
                do {
                        // Sync the persistent backoffs with concurrent runners
                        $backoffs = $this->syncBackoffDeltas( $backoffs, $backoffDeltas, $wait );
@@ -157,6 +161,7 @@ class JobRunner implements LoggerAwareInterface {
                        } else {
                                $job = $group->pop( $type ); // job from a single queue
                        }
+                       $lbFactory->commitMasterChanges( __METHOD__ ); // flush any JobQueueDB writes
 
                        if ( $job ) { // found a job
                                ++$jobsPopped;
@@ -176,9 +181,10 @@ class JobRunner implements LoggerAwareInterface {
                                        $backoffs = $this->syncBackoffDeltas( $backoffs, $backoffDeltas, $wait );
                                }
 
-                               $info = $this->executeJob( $job, $stats, $popTime );
+                               $info = $this->executeJob( $job, $lbFactory, $stats, $popTime );
                                if ( $info['status'] !== false || !$job->allowRetries() ) {
                                        $group->ack( $job ); // succeeded or job cannot be retried
+                                       $lbFactory->commitMasterChanges( __METHOD__ ); // flush any JobQueueDB writes
                                }
 
                                // Back off of certain jobs for a while (for throttling and for errors)
@@ -206,23 +212,23 @@ class JobRunner implements LoggerAwareInterface {
                                        break;
                                }
 
-                               // Don't let any of the main DB slaves get backed up.
+                               // Don't let any of the main DB replica DBs get backed up.
                                // This only waits for so long before exiting and letting
                                // other wikis in the farm (on different masters) get a chance.
                                $timePassed = microtime( true ) - $lastCheckTime;
                                if ( $timePassed >= self::LAG_CHECK_PERIOD || $timePassed < 0 ) {
                                        try {
-                                               wfGetLBFactory()->waitForReplication( [
+                                               $lbFactory->waitForReplication( [
                                                        'ifWritesSince' => $lastCheckTime,
                                                        'timeout' => self::MAX_ALLOWED_LAG
                                                ] );
                                        } catch ( DBReplicationWaitError $e ) {
-                                               $response['reached'] = 'slave-lag-limit';
+                                               $response['reached'] = 'replica-lag-limit';
                                                break;
                                        }
                                        $lastCheckTime = microtime( true );
                                }
-                               // Don't let any queue slaves/backups fall behind
+                               // Don't let any queue replica DBs/backups fall behind
                                if ( $jobsPopped > 0 && ( $jobsPopped % 100 ) == 0 ) {
                                        $group->waitForBackups();
                                }
@@ -248,11 +254,12 @@ class JobRunner implements LoggerAwareInterface {
 
        /**
         * @param Job $job
-        * @param BufferingStatsdDataFactory $stats
+        * @param LBFactory $lbFactory
+        * @param StatsdDataFactory $stats
         * @param float $popTime
         * @return array Map of status/error/timeMs
         */
-       private function executeJob( Job $job, $stats, $popTime ) {
+       private function executeJob( Job $job, LBFactory $lbFactory, $stats, $popTime ) {
                $jType = $job->getType();
                $msg = $job->toString() . " STARTING";
                $this->logger->debug( $msg );
@@ -262,12 +269,13 @@ class JobRunner implements LoggerAwareInterface {
                $rssStart = $this->getMaxRssKb();
                $jobStartTime = microtime( true );
                try {
+                       $fnameTrxOwner = get_class( $job ) . '::run'; // give run() outer scope
+                       $lbFactory->beginMasterChanges( $fnameTrxOwner );
                        $status = $job->run();
                        $error = $job->getLastError();
-                       $this->commitMasterChanges( $job );
-
+                       $this->commitMasterChanges( $lbFactory, $job, $fnameTrxOwner );
+                       // Run any deferred update tasks; doUpdates() manages transactions itself
                        DeferredUpdates::doUpdates();
-                       $this->commitMasterChanges( $job );
                } catch ( Exception $e ) {
                        MWExceptionHandler::rollbackMasterChangesAndLog( $e );
                        $status = false;
@@ -283,10 +291,10 @@ class JobRunner implements LoggerAwareInterface {
 
                // Commit all outstanding connections that are in a transaction
                // to get a fresh repeatable read snapshot on every connection.
-               // Note that jobs are still responsible for handling slave lag.
-               wfGetLBFactory()->commitAll( __METHOD__ );
+               // Note that jobs are still responsible for handling replica DB lag.
+               $lbFactory->flushReplicaSnapshots( __METHOD__ );
                // Clear out title cache data from prior snapshots
-               LinkCache::singleton()->clear();
+               MediaWikiServices::getInstance()->getLinkCache()->clear();
                $timeMs = intval( ( microtime( true ) - $jobStartTime ) * 1000 );
                $rssEnd = $this->getMaxRssKb();
 
@@ -307,7 +315,7 @@ class JobRunner implements LoggerAwareInterface {
                $stats->timing( "jobqueue.run.$jType", $timeMs );
                // Track RSS increases for jobs (in case of memory leaks)
                if ( $rssStart && $rssEnd ) {
-                       $stats->increment( "jobqueue.rss_delta.$jType", $rssEnd - $rssStart );
+                       $stats->updateCount( "jobqueue.rss_delta.$jType", $rssEnd - $rssStart );
                }
 
                if ( $status === false ) {
@@ -485,34 +493,42 @@ class JobRunner implements LoggerAwareInterface {
        /**
         * Issue a commit on all masters who are currently in a transaction and have
         * made changes to the database. It also supports sometimes waiting for the
-        * local wiki's slaves to catch up. See the documentation for
+        * local wiki's replica DBs to catch up. See the documentation for
         * $wgJobSerialCommitThreshold for more.
         *
+        * @param LBFactory $lbFactory
         * @param Job $job
+        * @param string $fnameTrxOwner
         * @throws DBError
         */
-       private function commitMasterChanges( Job $job ) {
+       private function commitMasterChanges( LBFactory $lbFactory, Job $job, $fnameTrxOwner ) {
                global $wgJobSerialCommitThreshold;
 
-               $lb = wfGetLB( wfWikiID() );
+               $time = false;
+               $lb = $lbFactory->getMainLB( wfWikiID() );
                if ( $wgJobSerialCommitThreshold !== false && $lb->getServerCount() > 1 ) {
                        // Generally, there is one master connection to the local DB
                        $dbwSerial = $lb->getAnyOpenConnection( $lb->getWriterIndex() );
+                       // We need natively blocking fast locks
+                       if ( $dbwSerial && $dbwSerial->namedLocksEnqueue() ) {
+                               $time = $dbwSerial->pendingWriteQueryDuration( $dbwSerial::ESTIMATE_DB_APPLY );
+                               if ( $time < $wgJobSerialCommitThreshold ) {
+                                       $dbwSerial = false;
+                               }
+                       } else {
+                               $dbwSerial = false;
+                       }
                } else {
+                       // There are no replica DBs or writes are all to foreign DB (we don't handle that)
                        $dbwSerial = false;
                }
 
-               if ( !$dbwSerial
-                       || !$dbwSerial->namedLocksEnqueue()
-                       || $dbwSerial->pendingWriteQueryDuration() < $wgJobSerialCommitThreshold
-               ) {
-                       // Writes are all to foreign DBs, named locks don't form queues,
-                       // or $wgJobSerialCommitThreshold is not reached; commit changes now
-                       wfGetLBFactory()->commitMasterChanges( __METHOD__ );
+               if ( !$dbwSerial ) {
+                       $lbFactory->commitMasterChanges( $fnameTrxOwner );
                        return;
                }
 
-               $ms = intval( 1000 * $dbwSerial->pendingWriteQueryDuration() );
+               $ms = intval( 1000 * $time );
                $msg = $job->toString() . " COMMIT ENQUEUED [{$ms}ms of writes]";
                $this->logger->info( $msg );
                $this->debugCallback( $msg );
@@ -522,27 +538,18 @@ class JobRunner implements LoggerAwareInterface {
                        // This will trigger a rollback in the main loop
                        throw new DBError( $dbwSerial, "Timed out waiting on commit queue." );
                }
-               // Wait for the generic slave to catch up
+               $unlocker = new ScopedCallback( function () use ( $dbwSerial ) {
+                       $dbwSerial->unlock( 'jobrunner-serial-commit', __METHOD__ );
+               } );
+
+               // Wait for the replica DBs to catch up
                $pos = $lb->getMasterPos();
                if ( $pos ) {
-                       $lb->waitForOne( $pos );
+                       $lb->waitForAll( $pos );
                }
 
-               $fname = __METHOD__;
-               // Re-ping all masters with transactions. This throws DBError if some
-               // connection died while waiting on locks/slaves, triggering a rollback.
-               wfGetLBFactory()->forEachLB( function( LoadBalancer $lb ) use ( $fname ) {
-                       $lb->forEachOpenConnection( function( IDatabase $conn ) use ( $fname ) {
-                               if ( $conn->writesOrCallbacksPending() ) {
-                                       $conn->query( "SELECT 1", $fname );
-                               }
-                       } );
-               } );
-
                // Actually commit the DB master changes
-               wfGetLBFactory()->commitMasterChanges( __METHOD__ );
-
-               // Release the lock
-               $dbwSerial->unlock( 'jobrunner-serial-commit', __METHOD__ );
+               $lbFactory->commitMasterChanges( $fnameTrxOwner );
+               ScopedCallback::consume( $unlocker );
        }
 }
index 1e804c4..060cabb 100644 (file)
@@ -73,8 +73,12 @@ class AssembleUploadChunksJob extends Job {
                                return false;
                        }
 
+                       // We can only get warnings like 'duplicate' after concatenating the chunks
+                       $status = Status::newGood();
+                       $status->value = [ 'warnings' => $upload->checkWarnings() ];
+
                        // We have a new filekey for the fully concatenated file
-                       $newFileKey = $upload->getLocalFile()->getFileKey();
+                       $newFileKey = $upload->getStashFile()->getFileKey();
 
                        // Remove the old stash file row and first chunk file
                        $upload->stash->removeFileNoAuth( $this->params['filekey'] );
@@ -95,7 +99,7 @@ class AssembleUploadChunksJob extends Job {
                                        'stage' => 'assembling',
                                        'filekey' => $newFileKey,
                                        'imageinfo' => $imageInfo,
-                                       'status' => Status::newGood()
+                                       'status' => $status
                                ]
                        );
                } catch ( Exception $e ) {
index 57e69b4..1828dd7 100644 (file)
@@ -19,6 +19,7 @@
  *
  * @file
  */
+use MediaWiki\MediaWikiServices;
 
 /**
  * Job to add recent change entries mentioning category membership changes
@@ -48,7 +49,8 @@ class CategoryMembershipChangeJob extends Job {
                        return false; // deleted?
                }
 
-               $dbw = wfGetDB( DB_MASTER );
+               $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+               $dbw = $lb->getConnection( DB_MASTER );
                // Use a named lock so that jobs for this page see each others' changes
                $lockKey = "CategoryMembershipUpdates:{$page->getId()}";
                $scopedLock = $dbw->getScopedLockAndFlush( $lockKey, __METHOD__, 10 );
@@ -57,14 +59,14 @@ class CategoryMembershipChangeJob extends Job {
                        return false;
                }
 
-               $dbr = wfGetDB( DB_SLAVE, [ 'recentchanges' ] );
-               // Wait till the slave is caught up so that jobs for this page see each others' changes
-               if ( !wfGetLB()->safeWaitForMasterPos( $dbr ) ) {
-                       $this->setLastError( "Timed out while waiting for slave to catch up" );
+               $dbr = wfGetDB( DB_REPLICA, [ 'recentchanges' ] );
+               // Wait till the replica DB is caught up so that jobs for this page see each others' changes
+               if ( !$lb->safeWaitForMasterPos( $dbr ) ) {
+                       $this->setLastError( "Timed out while waiting for replica DB to catch up" );
                        return false;
                }
                // Clear any stale REPEATABLE-READ snapshot
-               $dbr->commit( __METHOD__, 'flush' );
+               $dbr->flushSnapshot( __METHOD__ );
 
                $cutoffUnix = wfTimestamp( TS_UNIX, $this->params['revTimestamp'] );
                // Using ENQUEUE_FUDGE_SEC handles jobs inserted out of revision order due to the delay
@@ -118,19 +120,23 @@ class CategoryMembershipChangeJob extends Job {
                );
 
                // Apply all category updates in revision timestamp order
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                foreach ( $res as $row ) {
-                       $this->notifyUpdatesForRevision( $page, Revision::newFromRow( $row ) );
+                       $this->notifyUpdatesForRevision( $lbFactory, $page, Revision::newFromRow( $row ) );
                }
 
                return true;
        }
 
        /**
+        * @param LBFactory $lbFactory
         * @param WikiPage $page
         * @param Revision $newRev
         * @throws MWException
         */
-       protected function notifyUpdatesForRevision( WikiPage $page, Revision $newRev ) {
+       protected function notifyUpdatesForRevision(
+               LBFactory $lbFactory, WikiPage $page, Revision $newRev
+       ) {
                $config = RequestContext::getMain()->getConfig();
                $title = $page->getTitle();
 
@@ -156,7 +162,8 @@ class CategoryMembershipChangeJob extends Job {
                        return; // nothing to do
                }
 
-               $dbw = wfGetDB( DB_MASTER );
+               $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
+
                $catMembChange = new CategoryMembershipChange( $title, $newRev );
                $catMembChange->checkTemplateLinks();
 
@@ -167,8 +174,7 @@ class CategoryMembershipChangeJob extends Job {
                        $categoryTitle = Title::makeTitle( NS_CATEGORY, $categoryName );
                        $catMembChange->triggerCategoryAddedNotification( $categoryTitle );
                        if ( $insertCount++ && ( $insertCount % $batchSize ) == 0 ) {
-                               $dbw->commit( __METHOD__, 'flush' );
-                               wfGetLBFactory()->waitForReplication();
+                               $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
                        }
                }
 
@@ -176,8 +182,7 @@ class CategoryMembershipChangeJob extends Job {
                        $categoryTitle = Title::makeTitle( NS_CATEGORY, $categoryName );
                        $catMembChange->triggerCategoryRemovedNotification( $categoryTitle );
                        if ( $insertCount++ && ( $insertCount++ % $batchSize ) == 0 ) {
-                               $dbw->commit( __METHOD__, 'flush' );
-                               wfGetLBFactory()->waitForReplication();
+                               $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
                        }
                }
        }
index ca5d534..5c0f89f 100644 (file)
@@ -20,6 +20,7 @@
  * @file
  * @ingroup JobQueue
  */
+use \MediaWiki\MediaWikiServices;
 
 /**
  * Job to prune link tables for pages that were deleted
@@ -42,17 +43,23 @@ class DeleteLinksJob extends Job {
                }
 
                $pageId = $this->params['pageId'];
+
+               // Serialize links updates by page ID so they see each others' changes
+               $scopedLock = LinksUpdate::acquirePageLock( wfGetDB( DB_MASTER ), $pageId, 'job' );
+
                if ( WikiPage::newFromID( $pageId, WikiPage::READ_LATEST ) ) {
                        // The page was restored somehow or something went wrong
                        $this->setLastError( "deleteLinks: Page #$pageId exists" );
                        return false;
                }
 
+               $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                $timestamp = isset( $this->params['timestamp'] ) ? $this->params['timestamp'] : null;
-
                $page = WikiPage::factory( $this->title ); // title when deleted
+
                $update = new LinksDeletionUpdate( $page, $pageId, $timestamp );
-               DataUpdate::runUpdates( [ $update ] );
+               $update->setTransactionTicket( $factory->getEmptyTransactionTicket( __METHOD__ ) );
+               $update->doUpdate();
 
                return true;
        }
index a14cdd7..f09ba57 100644 (file)
@@ -113,11 +113,12 @@ class HTMLCacheUpdateJob extends Job {
                $touchTimestamp = wfTimestampNow();
 
                $dbw = wfGetDB( DB_MASTER );
+               $factory = wfGetLBFactory();
+               $ticket = $factory->getEmptyTransactionTicket( __METHOD__ );
                // Update page_touched (skipping pages already touched since the root job).
                // Check $wgUpdateRowsPerQuery for sanity; batch jobs are sized by that already.
                foreach ( array_chunk( $pageIds, $wgUpdateRowsPerQuery ) as $batch ) {
-                       $dbw->commit( __METHOD__, 'flush' );
-                       wfGetLBFactory()->waitForReplication();
+                       $factory->commitAndWaitForReplication( __METHOD__, $ticket );
 
                        $dbw->update( 'page',
                                [ 'page_touched' => $dbw->timestamp( $touchTimestamp ) ],
index 26d3c5c..80826fe 100644 (file)
@@ -31,7 +31,7 @@
  * @code
  * $ php maintenance/eval.php
  * > $queue = JobQueueGroup::singleton();
- * > $job = new NullJob( Title::newMainPage(), array( 'lives' => 10 ) );
+ * > $job = new NullJob( Title::newMainPage(), [ 'lives' => 10 ] );
  * > $queue->push( $job );
  * @endcode
  * You can then confirm the job has been enqueued by using the showJobs.php
index fbc1572..809fb63 100644 (file)
@@ -81,6 +81,8 @@ class RecentChangesUpdateJob extends Job {
                        return; // already in progress
                }
 
+               $factory = wfGetLBFactory();
+               $ticket = $factory->getEmptyTransactionTicket( __METHOD__ );
                $cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
                do {
                        $rcIds = $dbw->selectFieldValues( 'recentchanges',
@@ -91,14 +93,11 @@ class RecentChangesUpdateJob extends Job {
                        );
                        if ( $rcIds ) {
                                $dbw->delete( 'recentchanges', [ 'rc_id' => $rcIds ], __METHOD__ );
-                       }
-                       // Commit in chunks to avoid slave lag
-                       $dbw->commit( __METHOD__, 'flush' );
-
-                       if ( count( $rcIds ) === $wgUpdateRowsPerQuery ) {
-                               // There might be more, so try waiting for slaves
+                               // There might be more, so try waiting for replica DBs
                                try {
-                                       wfGetLBFactory()->waitForReplication( [ 'timeout' => 3 ] );
+                                       $factory->commitAndWaitForReplication(
+                                               __METHOD__, $ticket, [ 'timeout' => 3 ]
+                                       );
                                } catch ( DBReplicationWaitError $e ) {
                                        // Another job will continue anyway
                                        break;
@@ -121,6 +120,8 @@ class RecentChangesUpdateJob extends Job {
                // JobRunner uses DBO_TRX, but doesn't call begin/commit itself;
                // onTransactionIdle() will run immediately since there is no trx.
                $dbw->onTransactionIdle( function() use ( $dbw, $days, $window ) {
+                       $factory = wfGetLBFactory();
+                       $ticket = $factory->getEmptyTransactionTicket( __METHOD__ );
                        // Avoid disconnect/ping() cycle that makes locks fall off
                        $dbw->setSessionOptions( [ 'connTimeout' => 900 ] );
 
@@ -204,7 +205,7 @@ class RecentChangesUpdateJob extends Job {
                                }
                                foreach ( array_chunk( $newRows, 500 ) as $rowBatch ) {
                                        $dbw->insert( 'querycachetwo', $rowBatch, __METHOD__ );
-                                       wfGetLBFactory()->waitForReplication();
+                                       $factory->commitAndWaitForReplication( __METHOD__, $ticket );
                                }
                        }
 
index 8870569..a337da4 100644 (file)
@@ -40,7 +40,7 @@ class RefreshLinksJob extends Job {
        const PARSE_THRESHOLD_SEC = 1.0;
        /** @var integer Lag safety margin when comparing root job times to last-refresh times */
        const CLOCK_FUDGE = 10;
-       /** @var integer How many seconds to wait for slaves to catch up */
+       /** @var integer How many seconds to wait for replica DBs to catch up */
        const LAG_WAIT_TIMEOUT = 15;
 
        function __construct( Title $title, array $params ) {
@@ -83,7 +83,7 @@ class RefreshLinksJob extends Job {
 
                // Job to update all (or a range of) backlink pages for a page
                if ( !empty( $this->params['recursive'] ) ) {
-                       // When the base job branches, wait for the slaves to catch up to the master.
+                       // When the base job branches, wait for the replica DBs to catch up to the master.
                        // From then on, we know that any template changes at the time the base job was
                        // enqueued will be reflected in backlink page parses when the leaf jobs run.
                        if ( !isset( $params['range'] ) ) {
@@ -128,7 +128,18 @@ class RefreshLinksJob extends Job {
         * @return bool
         */
        protected function runForTitle( Title $title ) {
+               $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
+
                $page = WikiPage::factory( $title );
+               $page->loadPageData( WikiPage::READ_LATEST );
+
+               // Serialize links updates by page ID so they see each others' changes
+               $scopedLock = LinksUpdate::acquirePageLock( wfGetDB( DB_MASTER ), $page->getId(), 'job' );
+               // Get the latest ID *after* acquirePageLock() flushed the transaction.
+               // This is used to detect edits/moves after loadPageData() but before the scope lock.
+               // The works around the chicken/egg problem of determining the scope lock key.
+               $latest = $title->getLatestRevID( Title::GAID_FOR_UPDATE );
+
                if ( !empty( $this->params['triggeringRevisionId'] ) ) {
                        // Fetch the specified revision; lockAndGetLatest() below detects if the page
                        // was edited since and aborts in order to avoid corrupting the link tables
@@ -141,15 +152,15 @@ class RefreshLinksJob extends Job {
                        $revision = Revision::newFromTitle( $title, false, Revision::READ_LATEST );
                }
 
-               $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
-
                if ( !$revision ) {
                        $stats->increment( 'refreshlinks.rev_not_found' );
                        $this->setLastError( "Revision not found for {$title->getPrefixedDBkey()}" );
                        return false; // just deleted?
-               } elseif ( !$revision->isCurrent() ) {
-                       // If the revision isn't current, there's no point in doing a bunch
-                       // of work just to fail at the lockAndGetLatest() check later.
+               } elseif ( $revision->getId() != $latest || $revision->getPage() !== $page->getId() ) {
+                       // Do not clobber over newer updates with older ones. If all jobs where FIFO and
+                       // serialized, it would be OK to update links based on older revisions since it
+                       // would eventually get to the latest. Since that is not the case (by design),
+                       // only update the link tables to a state matching the current revision's output.
                        $stats->increment( 'refreshlinks.rev_not_current' );
                        $this->setLastError( "Revision {$revision->getId()} is not current" );
                        return false;
@@ -171,7 +182,7 @@ class RefreshLinksJob extends Job {
 
                        $skewedTimestamp = $this->params['rootJobTimestamp'];
                        if ( $opportunistic ) {
-                               // Neither clock skew nor DB snapshot/slave lag matter much for such
+                               // Neither clock skew nor DB snapshot/replica DB lag matter much for such
                                // updates; focus on reusing the (often recently updated) cache
                        } else {
                                // For transclusion updates, the template changes must be reflected
@@ -230,7 +241,10 @@ class RefreshLinksJob extends Job {
                        $parserOutput
                );
 
+               $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $ticket = $factory->getEmptyTransactionTicket( __METHOD__ );
                foreach ( $updates as $key => $update ) {
+                       $update->setTransactionTicket( $ticket );
                        // FIXME: This code probably shouldn't be here?
                        // Needed by things like Echo notifications which need
                        // to know which user caused the links update
@@ -249,19 +263,10 @@ class RefreshLinksJob extends Job {
                        }
                }
 
-               $latestNow = $page->lockAndGetLatest();
-               if ( !$latestNow || $revision->getId() != $latestNow ) {
-                       // Do not clobber over newer updates with older ones. If all jobs where FIFO and
-                       // serialized, it would be OK to update links based on older revisions since it
-                       // would eventually get to the latest. Since that is not the case (by design),
-                       // only update the link tables to a state matching the current revision's output.
-                       $stats->increment( 'refreshlinks.rev_cas_failure' );
-                       $this->setLastError( "page_latest changed from {$revision->getId()} to $latestNow" );
-                       return false;
+               foreach ( $updates as $update ) {
+                       $update->doUpdate();
                }
 
-               DataUpdate::runUpdates( $updates );
-
                InfoAction::invalidateCache( $title );
 
                return true;
diff --git a/includes/jobqueue/utils/PurgeJobUtils.php b/includes/jobqueue/utils/PurgeJobUtils.php
new file mode 100644 (file)
index 0000000..5eafcb3
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Base code for update jobs that put some secondary data extracted
+ * from article content into the database.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+use MediaWiki\MediaWikiServices;
+
+class PurgeJobUtils {
+       /**
+        * Invalidate the cache of a list of pages from a single namespace.
+        * This is intended for use by subclasses.
+        *
+        * @param IDatabase $dbw
+        * @param int $namespace Namespace number
+        * @param array $dbkeys
+        */
+       public static function invalidatePages( IDatabase $dbw, $namespace, array $dbkeys ) {
+               if ( $dbkeys === [] ) {
+                       return;
+               }
+
+               $dbw->onTransactionIdle( function() use ( $dbw, $namespace, $dbkeys ) {
+                       $services = MediaWikiServices::getInstance();
+                       $lbFactory = $services->getDBLoadBalancerFactory();
+                       // Determine which pages need to be updated.
+                       // This is necessary to prevent the job queue from smashing the DB with
+                       // large numbers of concurrent invalidations of the same page.
+                       $now = $dbw->timestamp();
+                       $ids = $dbw->selectFieldValues(
+                               'page',
+                               'page_id',
+                               [
+                                       'page_namespace' => $namespace,
+                                       'page_title' => $dbkeys,
+                                       'page_touched < ' . $dbw->addQuotes( $now )
+                               ],
+                               __METHOD__
+                       );
+
+                       if ( !$ids ) {
+                               return;
+                       }
+
+                       $batchSize = $services->getMainConfig()->get( 'UpdateRowsPerQuery' );
+                       $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
+                       foreach ( array_chunk( $ids, $batchSize ) as $idBatch ) {
+                               $dbw->update(
+                                       'page',
+                                       [ 'page_touched' => $now ],
+                                       [
+                                               'page_id' => $idBatch,
+                                               'page_touched < ' . $dbw->addQuotes( $now ) // handle races
+                                       ],
+                                       __METHOD__
+                               );
+                               $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
+                       }
+               } );
+       }
+}
index 511e1c9..4ba1bc3 100644 (file)
@@ -50,7 +50,7 @@ class HtmlArmor {
                if ( $input instanceof HtmlArmor ) {
                        return $input->value;
                } else {
-                       return htmlspecialchars( $input );
+                       return htmlspecialchars( $input, ENT_QUOTES );
                }
        }
 }
index 2370ed3..90c9a75 100644 (file)
@@ -84,13 +84,15 @@ class MapCacheLRU {
         * If the item is already set, it will be pushed to the top of the cache.
         *
         * @param string $key
-        * @return mixed
+        * @return mixed Returns null if the key was not found
         */
        public function get( $key ) {
                if ( !array_key_exists( $key, $this->cache ) ) {
                        return null;
                }
+
                $this->ping( $key );
+
                return $this->cache[$key];
        }
 
@@ -102,6 +104,28 @@ class MapCacheLRU {
                return array_keys( $this->cache );
        }
 
+       /**
+        * Get an item with the given key, producing and setting it if not found.
+        *
+        * If the callback returns false, then nothing is stored.
+        *
+        * @since 1.28
+        * @param string $key
+        * @param callable $callback Callback that will produce the value
+        * @return mixed The cached value if found or the result of $callback otherwise
+        */
+       public function getWithSetCallback( $key, callable $callback ) {
+               $value = $this->get( $key );
+               if ( $value === null ) {
+                       $value = call_user_func( $callback );
+                       if ( $value !== false ) {
+                               $this->set( $key, $value );
+                       }
+               }
+
+               return $value;
+       }
+
        /**
         * Clear one or several cache entries, or all cache entries
         *
index 331f2d5..320a0b6 100644 (file)
@@ -35,6 +35,8 @@
  *                use application/x-www-form-urlencoded (headers sent automatically)
  *   - stream   : resource to stream the HTTP response body to
  *   - proxy    : HTTP proxy to use
+ *   - flags    : map of boolean flags which supports:
+ *                  - relayResponseHeaders : write out header via header()
  * Request maps can use integer index 0 instead of 'method' and 1 instead of 'url'.
  *
  * @author Aaron Schulz
@@ -104,7 +106,7 @@ class MultiHttpClient {
         *   - reqTimeout     : post-connection timeout per request (seconds)
         * @return array Response array for request
         */
-       final public function run( array $req, array $opts = [] ) {
+       public function run( array $req, array $opts = [] ) {
                return $this->runMulti( [ $req ], $opts )[0]['response'];
        }
 
@@ -172,6 +174,7 @@ class MultiHttpClient {
                                $req['body'] = '';
                                $req['headers']['content-length'] = 0;
                        }
+                       $req['flags'] = isset( $req['flags'] ) ? $req['flags'] : [];
                        $handles[$index] = $this->getCurlHandle( $req, $opts );
                        if ( count( $reqs ) > 1 ) {
                                // https://github.com/guzzle/guzzle/issues/349
@@ -373,6 +376,9 @@ class MultiHttpClient {
 
                curl_setopt( $ch, CURLOPT_HEADERFUNCTION,
                        function ( $ch, $header ) use ( &$req ) {
+                               if ( !empty( $req['flags']['relayResponseHeaders'] ) ) {
+                                       header( $header );
+                               }
                                $length = strlen( $header );
                                $matches = [];
                                if ( preg_match( "/^(HTTP\/1\.[01]) (\d{3}) (.*)/", $header, $matches ) ) {
index 5a93391..03e23ed 100644 (file)
@@ -149,4 +149,12 @@ class ProcessCacheLRU {
                unset( $this->cache[$key] );
                $this->cache[$key] = $item;
        }
+
+       /**
+        * Get cache size
+        * @return int
+        */
+       public function getSize() {
+               return $this->maxCacheKeys;
+       }
 }
diff --git a/includes/libs/ReverseArrayIterator.php b/includes/libs/ReverseArrayIterator.php
new file mode 100644 (file)
index 0000000..37b68c3
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Convenience class for iterating over an array in reverse order.
+ *
+ * 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
+ * @since 1.27
+ */
+
+/**
+ * Convenience class for iterating over an array in reverse order.
+ *
+ * @since 1.27
+ */
+class ReverseArrayIterator implements Iterator, Countable {
+       /** @var array $array */
+       protected $array;
+
+       /**
+        * Creates an iterator which will visit the keys in $array in
+        * reverse order.  If given an object, will visit the properties
+        * of the object in reverse order.  (Note that the default order
+        * for PHP arrays and objects is declaration/assignment order.)
+        *
+        * @param array|object $array
+        */
+       public function __construct( $array = [] ) {
+               if ( is_array( $array ) ) {
+                       $this->array = $array;
+               } elseif ( is_object( $array ) ) {
+                       $this->array = get_object_vars( $array );
+               } else {
+                       throw new InvalidArgumentException( __METHOD__ . ' requires an array or object' );
+               }
+
+               $this->rewind();
+       }
+
+        public function current() {
+               return current( $this->array );
+        }
+
+        public function key() {
+               return key( $this->array );
+        }
+
+        public function next() {
+               prev( $this->array );
+        }
+
+        public function rewind() {
+               end( $this->array );
+        }
+
+        public function valid() {
+               return key( $this->array ) !== null;
+        }
+
+        public function count() {
+               return count( $this->array );
+        }
+}
index 2e780c9..dd1976c 100644 (file)
@@ -30,6 +30,19 @@ use Liuggio\StatsdClient\Entity\StatsdDataInterface;
  * @since 1.26
  */
 class SamplingStatsdClient extends StatsdClient {
+       protected $samplingRates = [];
+
+       /**
+        * Sampling rates as an associative array of patterns and rates.
+        * Patterns are Unix shell patterns (e.g. 'MediaWiki.api.*').
+        * Rates are sampling probabilities (e.g. 0.1 means 1 in 10 events are sampled).
+        * @param array $samplingRates
+        * @since 1.28
+        */
+       public function setSamplingRates( array $samplingRates ) {
+               $this->samplingRates = $samplingRates;
+       }
+
        /**
         * Sets sampling rate for all items in $data.
         * The sample rate specified in a StatsdData entity overrides the sample rate specified here.
@@ -37,11 +50,18 @@ class SamplingStatsdClient extends StatsdClient {
         * {@inheritDoc}
         */
        public function appendSampleRate( $data, $sampleRate = 1 ) {
-               if ( $sampleRate < 1 ) {
-                       array_walk( $data, function( $item ) use ( $sampleRate ) {
+               $samplingRates = $this->samplingRates;
+               if ( !$samplingRates && $sampleRate !== 1 ) {
+                       $samplingRates = [ '*' => $sampleRate ];
+               }
+               if ( $samplingRates ) {
+                       array_walk( $data, function( $item ) use ( $samplingRates ) {
                                /** @var $item StatsdData */
-                               if ( $item->getSampleRate() === 1 ) {
-                                       $item->setSampleRate( $sampleRate );
+                               foreach ( $samplingRates as $pattern => $rate ) {
+                                       if ( fnmatch( $pattern, $item->getKey(), FNM_NOESCAPE ) ) {
+                                               $item->setSampleRate( $item->getSampleRate() * $rate );
+                                               break;
+                                       }
                                }
                        } );
                }
@@ -74,9 +94,7 @@ class SamplingStatsdClient extends StatsdClient {
                }
 
                // add sampling
-               if ( $sampleRate < 1 ) {
-                       $data = $this->appendSampleRate( $data, $sampleRate );
-               }
+               $data = $this->appendSampleRate( $data, $sampleRate );
                $data = $this->sampleData( $data );
 
                $data = array_map( 'strval', $data );
diff --git a/includes/libs/WaitConditionLoop.php b/includes/libs/WaitConditionLoop.php
new file mode 100644 (file)
index 0000000..969e86e
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+/**
+ * Wait loop that reaches a condition or times out.
+ *
+ * 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 Aaron Schulz
+ */
+
+/**
+ * Wait loop that reaches a condition or times out
+ * @since 1.28
+ */
+class WaitConditionLoop {
+       /** @var callable */
+       private $condition;
+       /** @var callable[] */
+       private $busyCallbacks = [];
+       /** @var float Seconds */
+       private $timeout;
+       /** @var float Seconds */
+       private $lastWaitTime;
+       /** @var integer|null */
+       private $rusageMode;
+
+       const CONDITION_REACHED = 1;
+       const CONDITION_CONTINUE = 0; // evaluates as falsey
+       const CONDITION_FAILED = -1;
+       const CONDITION_TIMED_OUT = -2;
+       const CONDITION_ABORTED = -3;
+
+       /**
+        * @param callable $condition Callback that returns a WaitConditionLoop::CONDITION_ constant
+        * @param float $timeout Timeout in seconds
+        * @param array &$busyCallbacks List of callbacks to do useful work (by reference)
+        */
+       public function __construct( callable $condition, $timeout = 5.0, &$busyCallbacks = [] ) {
+               $this->condition = $condition;
+               $this->timeout = $timeout;
+               $this->busyCallbacks =& $busyCallbacks;
+
+               if ( defined( 'HHVM_VERSION' ) && PHP_OS === 'Linux' ) {
+                       $this->rusageMode = 2; // RUSAGE_THREAD
+               } elseif ( function_exists( 'getrusage' ) ) {
+                       $this->rusageMode = 0; // RUSAGE_SELF
+               }
+       }
+
+       /**
+        * Invoke the loop and continue until either:
+        *   - a) The condition callback returns neither CONDITION_CONTINUE nor false
+        *   - b) The timeout is reached
+        * This a condition callback can return true (stop) or false (continue) for convenience.
+        * In such cases, the halting result of "true" will be converted to CONDITION_REACHED.
+        *
+        * If $timeout is 0, then only the condition callback will be called (no busy callbacks),
+        * and this will immediately return CONDITION_FAILED if the condition was not met.
+        *
+        * Exceptions in callbacks will be caught and the callback will be swapped with
+        * one that simply rethrows that exception back to the caller when invoked.
+        *
+        * @return integer WaitConditionLoop::CONDITION_* constant
+        * @throws Exception Any error from the condition callback
+        */
+       public function invoke() {
+               $elapsed = 0.0; // seconds
+               $sleepUs = 0; // microseconds to sleep each time
+               $lastCheck = false;
+               $finalResult = self::CONDITION_TIMED_OUT;
+               do {
+                       $checkStartTime = $this->getWallTime();
+                       // Check if the condition is met yet
+                       $realStart = $this->getWallTime();
+                       $cpuStart = $this->getCpuTime();
+                       $checkResult = call_user_func( $this->condition );
+                       $cpu = $this->getCpuTime() - $cpuStart;
+                       $real = $this->getWallTime() - $realStart;
+                       // Exit if the condition is reached, and error occurs, or this is non-blocking
+                       if ( $this->timeout <= 0 ) {
+                               $finalResult = $checkResult ? self::CONDITION_REACHED : self::CONDITION_FAILED;
+                               break;
+                       } elseif ( (int)$checkResult !== self::CONDITION_CONTINUE ) {
+                               if ( is_int( $checkResult ) ) {
+                                       $finalResult = $checkResult;
+                               } else {
+                                       $finalResult = self::CONDITION_REACHED;
+                               }
+                               break;
+                       } elseif ( $lastCheck ) {
+                               break; // timeout reached
+                       }
+                       // Detect if condition callback seems to block or if justs burns CPU
+                       $conditionUsesInterrupts = ( $real > 0.100 && $cpu <= $real * .03 );
+                       if ( !$this->popAndRunBusyCallback() && !$conditionUsesInterrupts ) {
+                               // 10 queries = 10(10+100)/2 ms = 550ms, 14 queries = 1050ms
+                               $sleepUs = min( $sleepUs + 10 * 1e3, 1e6 ); // stop incrementing at ~1s
+                               $this->usleep( $sleepUs );
+                       }
+                       $checkEndTime = $this->getWallTime();
+                       // The max() protects against the clock getting set back
+                       $elapsed += max( $checkEndTime - $checkStartTime, 0.010 );
+                       // Do not let slow callbacks timeout without checking the condition one more time
+                       $lastCheck = ( $elapsed >= $this->timeout );
+               } while ( true );
+
+               $this->lastWaitTime = $elapsed;
+
+               return $finalResult;
+       }
+
+       /**
+        * @return float Seconds
+        */
+       public function getLastWaitTime() {
+               return $this->lastWaitTime;
+       }
+
+       /**
+        * @param integer $microseconds
+        */
+       protected function usleep( $microseconds ) {
+               usleep( $microseconds );
+       }
+
+       /**
+        * @return float
+        */
+       protected function getWallTime() {
+               return microtime( true );
+       }
+
+       /**
+        * @return float Returns 0.0 if not supported (Windows on PHP < 7)
+        */
+       protected function getCpuTime() {
+               if ( $this->rusageMode === null ) {
+                       return microtime( true ); // assume worst case (all time is CPU)
+               }
+
+               $ru = getrusage( $this->rusageMode );
+               $time = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
+               $time += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
+
+               return $time;
+       }
+
+       /**
+        * Run one of the callbacks that does work ahead of time for another caller
+        *
+        * @return bool Whether a callback was executed
+        */
+       private function popAndRunBusyCallback() {
+               if ( $this->busyCallbacks ) {
+                       reset( $this->busyCallbacks );
+                       $key = key( $this->busyCallbacks );
+                       /** @var callable $workCallback */
+                       $workCallback =& $this->busyCallbacks[$key];
+                       try {
+                               $workCallback();
+                       } catch ( Exception $e ) {
+                               $workCallback = function () use ( $e ) {
+                                       throw $e;
+                               };
+                       }
+                       unset( $this->busyCallbacks[$key] ); // consume
+
+                       return true;
+               }
+
+               return false;
+       }
+}
index 1a2711a..cd79b67 100644 (file)
@@ -45,31 +45,32 @@ use Psr\Log\NullLogger;
 abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
        /** @var array[] Lock tracking */
        protected $locks = [];
-
-       /** @var integer */
+       /** @var integer ERR_* class constant */
        protected $lastError = self::ERR_NONE;
-
        /** @var string */
        protected $keyspace = 'local';
-
        /** @var LoggerInterface */
        protected $logger;
-
        /** @var callback|null */
        protected $asyncHandler;
+       /** @var integer Seconds */
+       protected $syncTimeout;
 
        /** @var bool */
        private $debugMode = false;
-
        /** @var array */
        private $duplicateKeyLookups = [];
-
        /** @var bool */
        private $reportDupes = false;
-
        /** @var bool */
        private $dupeTrackScheduled = false;
 
+       /** @var callable[] */
+       protected $busyCallbacks = [];
+
+       /** @var integer[] Map of (ATTR_* class constant => QOS_* class constant) */
+       protected $attrMap = [];
+
        /** Possible values for getLastError() */
        const ERR_NONE = 0; // no error
        const ERR_NO_RESPONSE = 1; // no response
@@ -91,6 +92,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         *      In CLI mode, it should run the task immediately.
         *   - reportDupes: Whether to emit warning log messages for all keys that were
         *      requested more than once (requires an asyncHandler).
+        *   - syncTimeout: How long to wait with WRITE_SYNC in seconds.
         * @param array $params
         */
        public function __construct( array $params = [] ) {
@@ -111,6 +113,8 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                if ( !empty( $params['reportDupes'] ) && is_callable( $this->asyncHandler ) ) {
                        $this->reportDupes = true;
                }
+
+               $this->syncTimeout = isset( $params['syncTimeout'] ) ? $params['syncTimeout'] : 3;
        }
 
        /**
@@ -369,6 +373,20 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                return $success;
        }
 
+       /**
+        * Reset the TTL on a key if it exists
+        *
+        * @param string $key
+        * @param int $expiry
+        * @return bool Success Returns false if there is no key
+        * @since 1.28
+        */
+       public function changeTTL( $key, $expiry = 0 ) {
+               $value = $this->get( $key );
+
+               return ( $value === false ) ? false : $this->set( $key, $value, $expiry );
+       }
+
        /**
         * Acquire an advisory lock on a key string
         *
@@ -392,35 +410,21 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                }
 
                $expiry = min( $expiry ?: INF, self::TTL_DAY );
-
-               $this->clearLastError();
-               $timestamp = microtime( true ); // starting UNIX timestamp
-               if ( $this->add( "{$key}:lock", 1, $expiry ) ) {
-                       $locked = true;
-               } elseif ( $this->getLastError() || $timeout <= 0 ) {
-                       $locked = false; // network partition or non-blocking
-               } else {
-                       // Estimate the RTT (us); use 1ms minimum for sanity
-                       $uRTT = max( 1e3, ceil( 1e6 * ( microtime( true ) - $timestamp ) ) );
-                       $sleep = 2 * $uRTT; // rough time to do get()+set()
-
-                       $attempts = 0; // failed attempts
-                       do {
-                               if ( ++$attempts >= 3 && $sleep <= 5e5 ) {
-                                       // Exponentially back off after failed attempts to avoid network spam.
-                                       // About 2*$uRTT*(2^n-1) us of "sleep" happen for the next n attempts.
-                                       $sleep *= 2;
-                               }
-                               usleep( $sleep ); // back off
+               $loop = new WaitConditionLoop(
+                       function () use ( $key, $timeout, $expiry ) {
                                $this->clearLastError();
-                               $locked = $this->add( "{$key}:lock", 1, $expiry );
-                               if ( $this->getLastError() ) {
-                                       $locked = false; // network partition
-                                       break;
+                               if ( $this->add( "{$key}:lock", 1, $expiry ) ) {
+                                       return true; // locked!
+                               } elseif ( $this->getLastError() ) {
+                                       return WaitConditionLoop::CONDITION_ABORTED; // network partition?
                                }
-                       } while ( !$locked && ( microtime( true ) - $timestamp ) < $timeout );
-               }
 
+                               return WaitConditionLoop::CONDITION_CONTINUE;
+                       },
+                       $timeout
+               );
+
+               $locked = ( $loop->invoke() === $loop::CONDITION_REACHED );
                if ( $locked ) {
                        $this->locks[$key] = [ 'class' => $rclass, 'depth' => 1 ];
                }
@@ -625,6 +629,30 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
                $this->lastError = $err;
        }
 
+       /**
+        * Let a callback be run to avoid wasting time on special blocking calls
+        *
+        * The callbacks may or may not be called ever, in any particular order.
+        * They are likely to be invoked when something WRITE_SYNC is used used.
+        * They should follow a caching pattern as shown below, so that any code
+        * using the word will get it's result no matter what happens.
+        * @code
+        *     $result = null;
+        *     $workCallback = function () use ( &$result ) {
+        *         if ( !$result ) {
+        *             $result = ....
+        *         }
+        *         return $result;
+        *     }
+        * @endcode
+        *
+        * @param callable $workCallback
+        * @since 1.28
+        */
+       public function addBusyCallback( callable $workCallback ) {
+               $this->busyCallbacks[] = $workCallback;
+       }
+
        /**
         * Modify a cache update operation array for EventRelayer::notify()
         *
@@ -734,4 +762,34 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
        public function makeKey() {
                return $this->makeKeyInternal( $this->keyspace, func_get_args() );
        }
+
+       /**
+        * @param integer $flag ATTR_* class constant
+        * @return integer QOS_* class constant
+        * @since 1.28
+        */
+       public function getQoS( $flag ) {
+               return isset( $this->attrMap[$flag] ) ? $this->attrMap[$flag] : self::QOS_UNKNOWN;
+       }
+
+       /**
+        * Merge the flag maps of one or more BagOStuff objects into a "lowest common denominator" map
+        *
+        * @param BagOStuff[] $bags
+        * @return integer[] Resulting flag map (class ATTR_* constant => class QOS_* constant)
+        */
+       protected function mergeFlagMaps( array $bags ) {
+               $map = [];
+               foreach ( $bags as $bag ) {
+                       foreach ( $bag->attrMap as $attr => $rank ) {
+                               if ( isset( $map[$attr] ) ) {
+                                       $map[$attr] = min( $map[$attr], $rank );
+                               } else {
+                                       $map[$attr] = $rank;
+                               }
+                       }
+               }
+
+               return $map;
+       }
 }
index 60ec922..74bf4b5 100644 (file)
@@ -42,8 +42,12 @@ class CachedBagOStuff extends HashBagOStuff {
         * @param array $params Parameters for HashBagOStuff
         */
        function __construct( BagOStuff $backend, $params = [] ) {
-               $this->backend = $backend;
+               unset( $params['reportDupes'] ); // useless here
+
                parent::__construct( $params );
+
+               $this->backend = $backend;
+               $this->attrMap = $backend->attrMap;
        }
 
        protected function doGet( $key, $flags = 0 ) {
index 408212a..3f66c06 100644 (file)
@@ -31,6 +31,10 @@ class EmptyBagOStuff extends BagOStuff {
                return false;
        }
 
+       public function add( $key, $value, $exp = 0 ) {
+               return true;
+       }
+
        public function set( $key, $value, $exp = 0, $flags = 0 ) {
                return true;
        }
index 91e7934..62c4fa5 100644 (file)
@@ -42,4 +42,11 @@ interface IExpiringStore {
        const TTL_PROC_LONG = 30; // loose cache time that can survive slow web requests
 
        const TTL_INDEFINITE = 0;
+
+       // Attribute and QoS constants; higher QOS values with the same prefix rank higher...
+       // Medium attributes constants related to emulation or media type
+       const ATTR_EMULATION = 1;
+       const QOS_EMULATION_SQL = 1;
+       // Generic "unknown" value that is useful for comparisons (e.g. always good enough)
+       const QOS_UNKNOWN = INF;
 }
index ba8c736..5967441 100644 (file)
@@ -79,6 +79,11 @@ class MemcachedBagOStuff extends BagOStuff {
                return $this->mergeViaCas( $key, $callback, $exptime, $attempts );
        }
 
+       public function changeTTL( $key, $exptime = 0 ) {
+               return $this->client->touch( $this->validateKeyEncoding( $key ),
+                       $this->fixExpiry( $exptime ) );
+       }
+
        /**
         * Get the underlying client object. This is provided for debugging
         * purposes.
index 668135d..c3fcab9 100644 (file)
@@ -360,6 +360,48 @@ class MemcachedClient {
                return false;
        }
 
+       /**
+        * Changes the TTL on a key from the server to $time
+        *
+        * @param string $key Key
+        * @param int $time TTL in seconds
+        *
+        * @return bool True on success, false on failure
+        */
+       public function touch( $key, $time = 0 ) {
+               if ( !$this->_active ) {
+                       return false;
+               }
+
+               $sock = $this->get_sock( $key );
+               if ( !is_resource( $sock ) ) {
+                       return false;
+               }
+
+               $key = is_array( $key ) ? $key[1] : $key;
+
+               if ( isset( $this->stats['touch'] ) ) {
+                       $this->stats['touch']++;
+               } else {
+                       $this->stats['touch'] = 1;
+               }
+               $cmd = "touch $key $time\r\n";
+               if ( !$this->_fwrite( $sock, $cmd ) ) {
+                       return false;
+               }
+               $res = $this->_fgets( $sock );
+
+               if ( $this->_debug ) {
+                       $this->_debugprint( sprintf( "MemCache: touch %s (%s)", $key, $res ) );
+               }
+
+               if ( $res == "TOUCHED" ) {
+                       return true;
+               }
+
+               return false;
+       }
+
        /**
         * @param string $key
         * @param int $timeout
index fe61470..9dcfa7c 100644 (file)
@@ -83,6 +83,7 @@ class MultiWriteBagOStuff extends BagOStuff {
                                $this->caches[] = ObjectFactory::getObjectFromSpec( $cacheInfo );
                        }
                }
+               $this->mergeFlagMaps( $this->caches );
 
                $this->asyncWrites = (
                        isset( $params['replication'] ) &&
diff --git a/includes/libs/objectcache/RESTBagOStuff.php b/includes/libs/objectcache/RESTBagOStuff.php
new file mode 100644 (file)
index 0000000..9fc3fe1
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * Interface to key-value storage behind an HTTP server.
+ *
+ * Uses URL of the form "baseURL/{KEY}" to store, fetch, and delete values.
+ *
+ * E.g., when base URL is `/v1/sessions/`, then the store would do:
+ *
+ * `PUT /v1/sessions/12345758`
+ *
+ * and fetch would do:
+ *
+ * `GET /v1/sessions/12345758`
+ *
+ * delete would do:
+ *
+ * `DELETE /v1/sessions/12345758`
+ *
+ * Configure with:
+ *
+ * @code
+ * $wgObjectCaches['sessions'] = array(
+ *     'class' => 'RESTBagOStuff',
+ *     'url' => 'http://localhost:7231/wikimedia.org/v1/sessions/'
+ * );
+ * @endcode
+ */
+class RESTBagOStuff extends BagOStuff {
+
+       /**
+        * @var MultiHttpClient
+        */
+       private $client;
+
+       /**
+        * REST URL to use for storage.
+        * @var string
+        */
+       private $url;
+
+       public function __construct( $params ) {
+               if ( empty( $params['url'] ) ) {
+                       throw new InvalidArgumentException( 'URL parameter is required' );
+               }
+               parent::__construct( $params );
+               if ( empty( $params['client'] ) ) {
+                       $this->client = new MultiHttpClient( [] );
+               } else {
+                       $this->client = $params['client'];
+               }
+               // Make sure URL ends with /
+               $this->url = rtrim( $params['url'], '/' ) . '/';
+       }
+
+       /**
+        * @param string  $key
+        * @param integer $flags Bitfield of BagOStuff::READ_* constants [optional]
+        * @return mixed Returns false on failure and if the item does not exist
+        */
+       protected function doGet( $key, $flags = 0 ) {
+               $req = [
+                       'method' => 'GET',
+                   'url' => $this->url . rawurlencode( $key ),
+
+               ];
+               list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->client->run( $req );
+               if ( $rcode === 200 ) {
+                       if ( is_string( $rbody ) ) {
+                               return unserialize( $rbody );
+                       }
+                       return false;
+               }
+               if ( $rcode === 0 || ( $rcode >= 400 && $rcode != 404 ) ) {
+                       return $this->handleError( "Failed to fetch $key", $rcode, $rerr );
+               }
+               return false;
+       }
+
+       /**
+        * Handle storage error
+        * @param string $msg Error message
+        * @param int    $rcode Error code from client
+        * @param string $rerr Error message from client
+        * @return false
+        */
+       protected function handleError( $msg, $rcode, $rerr ) {
+               $this->logger->error( "$msg : ({code}) {error}", [
+                       'code' => $rcode,
+                       'error' => $rerr
+               ] );
+               $this->setLastError( $rcode === 0 ? self::ERR_UNREACHABLE : self::ERR_UNEXPECTED );
+               return false;
+       }
+
+       /**
+        * Set an item
+        *
+        * @param string $key
+        * @param mixed  $value
+        * @param int    $exptime Either an interval in seconds or a unix timestamp for expiry
+        * @param int    $flags Bitfield of BagOStuff::WRITE_* constants
+        * @return bool Success
+        */
+       public function set( $key, $value, $exptime = 0, $flags = 0 ) {
+               $req = [
+                       'method' => 'PUT',
+                       'url' => $this->url . rawurlencode( $key ),
+                       'body' => serialize( $value )
+               ];
+               list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->client->run( $req );
+               if ( $rcode === 200 || $rcode === 201 ) {
+                       return true;
+               }
+               return $this->handleError( "Failed to store $key", $rcode, $rerr );
+       }
+
+       /**
+        * Delete an item.
+        *
+        * @param string $key
+        * @return bool True if the item was deleted or not found, false on failure
+        */
+       public function delete( $key ) {
+               $req = [
+                       'method' => 'DELETE',
+                       'url' => $this->url . rawurlencode( $key ),
+               ];
+               list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->client->run( $req );
+               if ( $rcode === 200 || $rcode === 204 || $rcode === 205 ) {
+                       return true;
+               }
+               return $this->handleError( "Failed to delete $key", $rcode, $rerr );
+       }
+}
index 5f2c509..3bc7ae2 100644 (file)
@@ -22,7 +22,7 @@
 
 /**
  * A cache class that directs writes to one set of servers and reads to
- * another. This assumes that the servers used for reads are setup to slave
+ * another. This assumes that the servers used for reads are setup to replica DB
  * those that writes go to. This can easily be used with redis for example.
  *
  * In the WAN scenario (e.g. multi-datacenter case), this is useful when
@@ -42,7 +42,7 @@ class ReplicatedBagOStuff extends BagOStuff {
         *   - writeFactory : ObjectFactory::getObjectFromSpec array yeilding BagOStuff.
         *                    This object will be used for writes (e.g. the master DB).
         *   - readFactory  : ObjectFactory::getObjectFromSpec array yeilding BagOStuff.
-        *                    This object will be used for reads (e.g. a slave DB).
+        *                    This object will be used for reads (e.g. a replica DB).
         *
         * @param array $params
         * @throws InvalidArgumentException
@@ -59,12 +59,14 @@ class ReplicatedBagOStuff extends BagOStuff {
                                __METHOD__ . ': the "readFactory" parameter is required' );
                }
 
+               $opts = [ 'reportDupes' => false ]; // redundant
                $this->writeStore = ( $params['writeFactory'] instanceof BagOStuff )
                        ? $params['writeFactory']
-                       : ObjectFactory::getObjectFromSpec( $params['writeFactory'] );
+                       : ObjectFactory::getObjectFromSpec( $opts + $params['writeFactory'] );
                $this->readStore = ( $params['readFactory'] instanceof BagOStuff )
                        ? $params['readFactory']
-                       : ObjectFactory::getObjectFromSpec( $params['readFactory'] );
+                       : ObjectFactory::getObjectFromSpec( $opts + $params['readFactory'] );
+               $this->attrMap = $this->mergeFlagMaps( [ $this->readStore, $this->writeStore ] );
        }
 
        public function setDebug( $debug ) {
index ab702d5..5f6e324 100644 (file)
@@ -33,6 +33,7 @@ use Psr\Log\NullLogger;
  * This class is intended for caching data from primary stores.
  * If the get() method does not return a value, then the caller
  * should query the new value and backfill the cache using set().
+ * The preferred way to do this logic is through getWithSetCallback().
  * When querying the store on cache miss, the closest DB replica
  * should be used. Try to avoid heavyweight DB master or quorum reads.
  * When the source data changes, a purge method should be called.
@@ -43,16 +44,23 @@ use Psr\Log\NullLogger;
  *
  * The simplest purge method is delete().
  *
- * Instances of this class must be configured to point to a valid
- * PubSub endpoint, and there must be listeners on the cache servers
- * that subscribe to the endpoint and update the caches.
+ * There are two supported ways to handle broadcasted operations:
+ *   - a) Configure the 'purge' EventRelayer to point to a valid PubSub endpoint
+ *        that has subscribed listeners on the cache servers applying the cache updates.
+ *   - b) Ignore the 'purge' EventRelayer configuration (default is NullEventRelayer)
+ *        and set up mcrouter as the underlying cache backend, using one of the memcached
+ *        BagOStuff classes as 'cache'. Use OperationSelectorRoute in the mcrouter settings
+ *        to configure 'set' and 'delete' operations to go to all DCs via AllAsyncRoute and
+ *        configure other operations to go to the local DC via PoolRoute (for reference,
+ *        see https://github.com/facebook/mcrouter/wiki/List-of-Route-Handles).
  *
- * Broadcasted operations like delete() and touchCheckKey() are done
- * synchronously in the local datacenter, but are relayed asynchronously.
- * This means that callers in other datacenters will see older values
- * for however many milliseconds the datacenters are apart. As with
- * any cache, this should not be relied on for cases where reads are
- * used to determine writes to source (e.g. non-cache) data stores.
+ * Broadcasted operations like delete() and touchCheckKey() are done asynchronously
+ * in all datacenters this way, though the local one should likely be near immediate.
+ *
+ * This means that callers in all datacenters may see older values for however many
+ * milliseconds that the purge took to reach that datacenter. As with any cache, this
+ * should not be relied on for cases where reads are used to determine writes to source
+ * (e.g. non-cache) data stores, except when reading immutable data.
  *
  * All values are wrapped in metadata arrays. Keys use a "WANCache:" prefix
  * to avoid collisions with keys that are not wrapped as metadata arrays. The
@@ -60,6 +68,7 @@ use Psr\Log\NullLogger;
  *   - a) "WANCache:v" : used for regular value keys
  *   - b) "WANCache:i" : used for temporarily storing values of tombstoned keys
  *   - c) "WANCache:t" : used for storing timestamp "check" keys
+ *   - d) "WANCache:m" : used for temporary mutex keys to avoid cache stampedes
  *
  * @ingroup Cache
  * @since 1.26
@@ -67,8 +76,8 @@ use Psr\Log\NullLogger;
 class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        /** @var BagOStuff The local datacenter cache */
        protected $cache;
-       /** @var HashBagOStuff Script instance PHP cache */
-       protected $procCache;
+       /** @var HashBagOStuff[] Map of group PHP instance caches */
+       protected $processCaches = [];
        /** @var string Purge channel name */
        protected $purgeChannel;
        /** @var EventRelayer Bus that handles purge broadcasts */
@@ -95,6 +104,15 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        /** Default time-since-expiry on a miss that makes a key "hot" */
        const LOCK_TSE = 1;
 
+       /** Never consider performing "popularity" refreshes until a key reaches this age */
+       const AGE_NEW = 60;
+       /** The time length of the "popularity" refresh window for hot keys */
+       const HOT_TTR = 900;
+       /** Hits/second for a refresh to be expected within the "popularity" window */
+       const HIT_RATE_HIGH = 1;
+       /** Seconds to ramp up to the "popularity" refresh chance after a key is no longer new */
+       const RAMPUP_TTL = 30;
+
        /** Idiom for getWithSetCallback() callbacks to avoid calling set() */
        const TTL_UNCACHEABLE = -1;
        /** Idiom for getWithSetCallback() callbacks to 'lockTSE' logic */
@@ -103,6 +121,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        const TTL_LAGGED = 30;
        /** Idiom for delete() for "no hold-off" */
        const HOLDOFF_NONE = 0;
+       /** Idiom for getWithSetCallback() for "no minimum required as-of timestamp" */
+       const MIN_TIMESTAMP_NONE = 0.0;
 
        /** Tiny negative float to use when CTL comes up >= 0 due to clock skew */
        const TINY_NEGATIVE = -0.000001;
@@ -129,13 +149,14 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        const VALUE_KEY_PREFIX = 'WANCache:v:';
        const INTERIM_KEY_PREFIX = 'WANCache:i:';
        const TIME_KEY_PREFIX = 'WANCache:t:';
+       const MUTEX_KEY_PREFIX = 'WANCache:m:';
 
        const PURGE_VAL_PREFIX = 'PURGED:';
 
        const VFLD_DATA = 'WOC:d'; // key to the value of versioned data
        const VFLD_VERSION = 'WOC:v'; // key to the version of the value present
 
-       const MAX_PC_KEYS = 1000; // max keys to keep in process cache
+       const PC_PRIMARY = 'primary:1000'; // process cache name and max key count
 
        const DEFAULT_PURGE_CHANNEL = 'wancache-purge';
 
@@ -148,7 +169,6 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         */
        public function __construct( array $params ) {
                $this->cache = $params['cache'];
-               $this->procCache = new HashBagOStuff( [ 'maxKeys' => self::MAX_PC_KEYS ] );
                $this->purgeChannel = isset( $params['channels']['purge'] )
                        ? $params['channels']['purge']
                        : self::DEFAULT_PURGE_CHANNEL;
@@ -354,7 +374,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *
         * Example usage:
         * @code
-        *     $dbr = wfGetDB( DB_SLAVE );
+        *     $dbr = wfGetDB( DB_REPLICA );
         *     $setOpts = Database::getCacheSetOptions( $dbr );
         *     // Fetch the row from the DB
         *     $row = $dbr->selectRow( ... );
@@ -367,22 +387,30 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @param integer $ttl Seconds to live. Special values are:
         *   - WANObjectCache::TTL_INDEFINITE: Cache forever
         * @param array $opts Options map:
-        *   - lag     : Seconds of slave lag. Typically, this is either the slave lag
-        *               before the data was read or, if applicable, the slave lag before
-        *               the snapshot-isolated transaction the data was read from started.
-        *               Default: 0 seconds
-        *   - since   : UNIX timestamp of the data in $value. Typically, this is either
-        *               the current time the data was read or (if applicable) the time when
-        *               the snapshot-isolated transaction the data was read from started.
-        *               Default: 0 seconds
+        *   - lag : Seconds of replica DB lag. Typically, this is either the replica DB lag
+        *      before the data was read or, if applicable, the replica DB lag before
+        *      the snapshot-isolated transaction the data was read from started.
+        *      Use false to indicate that replication is not running.
+        *      Default: 0 seconds
+        *   - since : UNIX timestamp of the data in $value. Typically, this is either
+        *      the current time the data was read or (if applicable) the time when
+        *      the snapshot-isolated transaction the data was read from started.
+        *      Default: 0 seconds
         *   - pending : Whether this data is possibly from an uncommitted write transaction.
-        *               Generally, other threads should not see values from the future and
-        *               they certainly should not see ones that ended up getting rolled back.
-        *               Default: false
+        *      Generally, other threads should not see values from the future and
+        *      they certainly should not see ones that ended up getting rolled back.
+        *      Default: false
         *   - lockTSE : if excessive replication/snapshot lag is detected, then store the value
-        *               with this TTL and flag it as stale. This is only useful if the reads for
-        *               this key use getWithSetCallback() with "lockTSE" set.
-        *               Default: WANObjectCache::TSE_NONE
+        *      with this TTL and flag it as stale. This is only useful if the reads for
+        *      this key use getWithSetCallback() with "lockTSE" set.
+        *      Default: WANObjectCache::TSE_NONE
+        *   - staleTTL : Seconds to keep the key around if it is stale. The get()/getMulti()
+        *      methods return such stale values with a $curTTL of 0, and getWithSetCallback()
+        *      will call the regeneration callback in such cases, passing in the old value
+        *      and its as-of time to the callback. This is useful if adaptiveTTL() is used
+        *      on the old value's as-of time when it is verified as still being correct.
+        *      Default: 0.
+        * @note Options added in 1.28: staleTTL
         * @return bool Success
         */
        final public function set( $key, $value, $ttl = 0, array $opts = [] ) {
@@ -390,6 +418,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                $lockTSE = isset( $opts['lockTSE'] ) ? $opts['lockTSE'] : self::TSE_NONE;
                $age = isset( $opts['since'] ) ? max( 0, $now - $opts['since'] ) : 0;
                $lag = isset( $opts['lag'] ) ? $opts['lag'] : 0;
+               $staleTTL = isset( $opts['staleTTL'] ) ? $opts['staleTTL'] : 0;
 
                // Do not cache potentially uncommitted data as it might get rolled back
                if ( !empty( $opts['pending'] ) ) {
@@ -431,7 +460,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                                : $wrapped;
                };
 
-               return $this->cache->merge( self::VALUE_KEY_PREFIX . $key, $func, $ttl, 1 );
+               return $this->cache->merge( self::VALUE_KEY_PREFIX . $key, $func, $ttl + $staleTTL, 1 );
        }
 
        /**
@@ -456,8 +485,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *
         * When using potentially long-running ACID transactions, a good pattern is
         * to use a pre-commit hook to issue the delete. This means that immediately
-        * after commit, callers will see the tombstone in cache in the local datacenter
-        * and in the others upon relay. It also avoids the following race condition:
+        * after commit, callers will see the tombstone in cache upon purge relay.
+        * It also avoids the following race condition:
         *   - a) T1 begins, changes a row, and calls delete()
         *   - b) The HOLDOFF_TTL passes, expiring the delete() tombstone
         *   - c) T2 starts, reads the row and calls set() due to a cache miss
@@ -466,7 +495,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *
         * Example usage:
         * @code
-        *     $dbw->begin( __METHOD__ ); // start of request
+        *     $dbw->startAtomic( __METHOD__ ); // start of request
         *     ... <execute some stuff> ...
         *     // Update the row in the DB
         *     $dbw->update( ... );
@@ -476,7 +505,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *         $cache->delete( $key );
         *     } );
         *     ... <execute some stuff> ...
-        *     $dbw->commit( __METHOD__ ); // end of request
+        *     $dbw->endAtomic( __METHOD__ ); // end of request
         * @endcode
         *
         * The $ttl parameter can be used when purging values that have not actually changed
@@ -495,18 +524,11 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                $key = self::VALUE_KEY_PREFIX . $key;
 
                if ( $ttl <= 0 ) {
-                       // Update the local datacenter immediately
-                       $ok = $this->cache->delete( $key );
                        // Publish the purge to all datacenters
-                       $ok = $this->relayDelete( $key ) && $ok;
+                       $ok = $this->relayDelete( $key );
                } else {
-                       // Update the local datacenter immediately
-                       $ok = $this->cache->set( $key,
-                               $this->makePurgeValue( microtime( true ), self::HOLDOFF_NONE ),
-                               $ttl
-                       );
                        // Publish the purge to all datacenters
-                       $ok = $this->relayPurge( $key, $ttl, self::HOLDOFF_NONE ) && $ok;
+                       $ok = $this->relayPurge( $key, $ttl, self::HOLDOFF_NONE );
                }
 
                return $ok;
@@ -559,17 +581,19 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * keys, the relevant "check" keys must be supplied for this to work.
         *
         * The "check" key essentially represents a last-modified field.
-        * When touched, keys using it via get(), getMulti(), or getWithSetCallback()
-        * will be invalidated. It is treated as being HOLDOFF_TTL seconds in the future
+        * When touched, the field will be updated on all cache servers.
+        * Keys using it via get(), getMulti(), or getWithSetCallback() will
+        * be invalidated. It is treated as being HOLDOFF_TTL seconds in the future
         * by those methods to avoid race conditions where dependent keys get updated
-        * with stale values (e.g. from a DB slave).
+        * with stale values (e.g. from a DB replica DB).
         *
         * This is typically useful for keys with hardcoded names or in some cases
         * dynamically generated names where a low number of combinations exist.
         * When a few important keys get a large number of hits, a high cache
         * time is usually desired as well as "lockTSE" logic. The resetCheckKey()
         * method is less appropriate in such cases since the "time since expiry"
-        * cannot be inferred.
+        * cannot be inferred, causing any get() after the reset to treat the key
+        * as being "hot", resulting in more stale value usage.
         *
         * Note that "check" keys won't collide with other regular keys.
         *
@@ -582,14 +606,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @return bool True if the item was purged or not found, false on failure
         */
        final public function touchCheckKey( $key, $holdoff = self::HOLDOFF_TTL ) {
-               $key = self::TIME_KEY_PREFIX . $key;
-               // Update the local datacenter immediately
-               $ok = $this->cache->set( $key,
-                       $this->makePurgeValue( microtime( true ), $holdoff ),
-                       self::CHECK_KEY_TTL
-               );
                // Publish the purge to all datacenters
-               return $this->relayPurge( $key, self::CHECK_KEY_TTL, $holdoff ) && $ok;
+               return $this->relayPurge( self::TIME_KEY_PREFIX . $key, self::CHECK_KEY_TTL, $holdoff );
        }
 
        /**
@@ -597,11 +615,14 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *
         * This is similar to touchCheckKey() in that keys using it via get(), getMulti(),
         * or getWithSetCallback() will be invalidated. The differences are:
-        *   - a) The timestamp will be deleted from all caches and lazily
+        *   - a) The "check" key will be deleted from all caches and lazily
         *        re-initialized when accessed (rather than set everywhere)
         *   - b) Thus, dependent keys will be known to be invalid, but not
         *        for how long (they are treated as "just" purged), which
         *        effects any lockTSE logic in getWithSetCallback()
+        *   - c) Since "check" keys are initialized only on the server the key hashes
+        *        to, any temporary ejection of that server will cause the value to be
+        *        seen as purged as a new server will initialize the "check" key.
         *
         * The advantage is that this does not place high TTL keys on every cache
         * server, making it better for code that will cache many different keys
@@ -620,11 +641,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @return bool True if the item was purged or not found, false on failure
         */
        final public function resetCheckKey( $key ) {
-               $key = self::TIME_KEY_PREFIX . $key;
-               // Update the local datacenter immediately
-               $ok = $this->cache->delete( $key );
                // Publish the purge to all datacenters
-               return $this->relayDelete( $key ) && $ok;
+               return $this->relayDelete( self::TIME_KEY_PREFIX . $key );
        }
 
        /**
@@ -636,6 +654,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *   - $oldValue : current cache value or false if not present
         *   - &$ttl : a reference to the TTL which can be altered
         *   - &$setOpts : a reference to options for set() which can be altered
+        *   - $oldAsOf : generation UNIX timestamp of $oldValue or null if not present (since 1.28)
         *
         * It is strongly recommended to set the 'lag' and 'since' fields to avoid race conditions
         * that can cause stale values to get stuck at keys. Usually, callbacks ignore the current
@@ -661,8 +680,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *         $cache::TTL_MINUTE,
         *         // Function that derives the new key value
         *         function ( $oldValue, &$ttl, array &$setOpts ) {
-        *             $dbr = wfGetDB( DB_SLAVE );
-        *             // Account for any snapshot/slave lag
+        *             $dbr = wfGetDB( DB_REPLICA );
+        *             // Account for any snapshot/replica DB lag
         *             $setOpts += Database::getCacheSetOptions( $dbr );
         *
         *             return $dbr->selectRow( ... );
@@ -679,8 +698,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *         $cache::TTL_DAY,
         *         // Function that derives the new key value
         *         function ( $oldValue, &$ttl, array &$setOpts ) {
-        *             $dbr = wfGetDB( DB_SLAVE );
-        *             // Account for any snapshot/slave lag
+        *             $dbr = wfGetDB( DB_REPLICA );
+        *             // Account for any snapshot/replica DB lag
         *             $setOpts += Database::getCacheSetOptions( $dbr );
         *
         *             return CatConfig::newFromRow( $dbr->selectRow( ... ) );
@@ -689,7 +708,9 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *             // Calling touchCheckKey() on this key invalidates the cache
         *             'checkKeys' => [ $cache->makeKey( 'site-cat-config' ) ],
         *             // Try to only let one datacenter thread manage cache updates at a time
-        *             'lockTSE' => 30
+        *             'lockTSE' => 30,
+        *             // Avoid querying cache servers multiple times in a web request
+        *             'pcTTL' => $cache::TTL_PROC_LONG
         *         ]
         *     );
         * @endcode
@@ -704,8 +725,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *         // Function that derives the new key value
         *         function ( $oldValue, &$ttl, array &$setOpts ) {
         *             // Determine new value from the DB
-        *             $dbr = wfGetDB( DB_SLAVE );
-        *             // Account for any snapshot/slave lag
+        *             $dbr = wfGetDB( DB_REPLICA );
+        *             // Account for any snapshot/replica DB lag
         *             $setOpts += Database::getCacheSetOptions( $dbr );
         *
         *             return CatState::newFromResults( $dbr->select( ... ) );
@@ -731,8 +752,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *         10,
         *         // Function that derives the new key value
         *         function ( $oldValue, &$ttl, array &$setOpts ) {
-        *             $dbr = wfGetDB( DB_SLAVE );
-        *             // Account for any snapshot/slave lag
+        *             $dbr = wfGetDB( DB_REPLICA );
+        *             // Account for any snapshot/replica DB lag
         *             $setOpts += Database::getCacheSetOptions( $dbr );
         *
         *             // Start off with the last cached list
@@ -743,8 +764,12 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *             // Merge them and get the new "last 100" rows
         *             return array_slice( array_merge( $new, $list ), 0, 100 );
         *        },
-        *        // Try to only let one datacenter thread manage cache updates at a time
-        *        [ 'lockTSE' => 30 ]
+        *        [
+        *             // Try to only let one datacenter thread manage cache updates at a time
+        *             'lockTSE' => 30,
+        *             // Use a magic value when no cache value is ready rather than stampeding
+        *             'busyValue' => 'computing'
+        *        ]
         *     );
         * @endcode
         *
@@ -759,9 +784,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @param array $opts Options map:
         *   - checkKeys: List of "check" keys. The key at $key will be seen as invalid when either
         *      touchCheckKey() or resetCheckKey() is called on any of these keys.
-        *   - lowTTL: Consider pre-emptive updates when the current TTL (sec) of the key is less than
-        *      this. It becomes more likely over time, becoming a certainty once the key is expired.
-        *      Default: WANObjectCache::LOW_TTL seconds.
+        *      Default: [].
         *   - lockTSE: If the key is tombstoned or expired (by checkKeys) less than this many seconds
         *      ago, then try to have a single thread handle cache regeneration at any given time.
         *      Other threads will try to use stale values if possible. If, on miss, the time since
@@ -770,28 +793,61 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *      higher this is set, the higher the worst-case staleness can be.
         *      Use WANObjectCache::TSE_NONE to disable this logic.
         *      Default: WANObjectCache::TSE_NONE.
-        *   - pcTTL: Process cache the value in this PHP instance with this TTL. This avoids
-        *      network I/O when a key is read several times. This will not cache if the callback
-        *      returns false however. Note that any purges will not be seen while process cached;
-        *      since the callback should use slave DBs and they may be lagged or have snapshot
+        *   - busyValue: If no value exists and another thread is currently regenerating it, use this
+        *      as a fallback value (or a callback to generate such a value). This assures that cache
+        *      stampedes cannot happen if the value falls out of cache. This can be used as insurance
+        *      against cache regeneration becoming very slow for some reason (greater than the TTL).
+        *      Default: null.
+        *   - pcTTL: Process cache the value in this PHP instance for this many seconds. This avoids
+        *      network I/O when a key is read several times. This will not cache when the callback
+        *      returns false, however. Note that any purges will not be seen while process cached;
+        *      since the callback should use replica DBs and they may be lagged or have snapshot
         *      isolation anyway, this should not typically matter.
         *      Default: WANObjectCache::TTL_UNCACHEABLE.
+        *   - pcGroup: Process cache group to use instead of the primary one. If set, this must be
+        *      of the format ALPHANUMERIC_NAME:MAX_KEY_SIZE, e.g. "mydata:10". Use this for storing
+        *      large values, small yet numerous values, or some values with a high cost of eviction.
+        *      It is generally preferable to use a class constant when setting this value.
+        *      This has no effect unless pcTTL is used.
+        *      Default: WANObjectCache::PC_PRIMARY.
         *   - version: Integer version number. This allows for callers to make breaking changes to
         *      how values are stored while maintaining compatability and correct cache purges. New
         *      versions are stored alongside older versions concurrently. Avoid storing class objects
         *      however, as this reduces compatibility (due to serialization).
         *      Default: null.
+        *   - minAsOf: Reject values if they were generated before this UNIX timestamp.
+        *      This is useful if the source of a key is suspected of having possibly changed
+        *      recently, and the caller wants any such changes to be reflected.
+        *      Default: WANObjectCache::MIN_TIMESTAMP_NONE.
+        *   - hotTTR: Expected time-till-refresh for keys that average ~1 hit/second.
+        *      This should be greater than "ageNew". Keys with higher hit rates will regenerate
+        *      more often. This is useful when a popular key is changed but the cache purge was
+        *      delayed or lost. Seldom used keys are rarely affected by this setting, unless an
+        *      extremely low "hotTTR" value is passed in.
+        *      Default: WANObjectCache::HOT_TTR.
+        *   - lowTTL: Consider pre-emptive updates when the current TTL (seconds) of the key is less
+        *      than this. It becomes more likely over time, becoming certain once the key is expired.
+        *      Default: WANObjectCache::LOW_TTL.
+        *   - ageNew: Consider popularity refreshes only once a key reaches this age in seconds.
+        *      Default: WANObjectCache::AGE_NEW.
         * @return mixed Value found or written to the key
+        * @note Options added in 1.28: version, busyValue, hotTTR, ageNew, pcGroup, minAsOf
+        * @note Callable type hints are not used to avoid class-autoloading
         */
        final public function getWithSetCallback( $key, $ttl, $callback, array $opts = [] ) {
                $pcTTL = isset( $opts['pcTTL'] ) ? $opts['pcTTL'] : self::TTL_UNCACHEABLE;
 
                // Try the process cache if enabled
-               $value = ( $pcTTL >= 0 ) ? $this->procCache->get( $key ) : false;
+               if ( $pcTTL >= 0 ) {
+                       $group = isset( $opts['pcGroup'] ) ? $opts['pcGroup'] : self::PC_PRIMARY;
+                       $procCache = $this->getProcessCache( $group );
+                       $value = $procCache->get( $key );
+               } else {
+                       $procCache = false;
+                       $value = false;
+               }
 
                if ( $value === false ) {
-                       unset( $opts['minTime'] ); // not a public feature
-
                        // Fetch the value over the network
                        if ( isset( $opts['version'] ) ) {
                                $version = $opts['version'];
@@ -799,7 +855,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                                $cur = $this->doGetWithSetCallback(
                                        $key,
                                        $ttl,
-                                       function ( $oldValue, &$ttl, &$setOpts ) use ( $callback, $version ) {
+                                       function ( $oldValue, &$ttl, &$setOpts, $oldAsOf )
+                                       use ( $callback, $version ) {
                                                if ( is_array( $oldValue )
                                                        && array_key_exists( self::VFLD_DATA, $oldValue )
                                                ) {
@@ -810,7 +867,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                                                }
 
                                                return [
-                                                       self::VFLD_DATA => $callback( $oldData, $ttl, $setOpts ),
+                                                       self::VFLD_DATA => $callback( $oldData, $ttl, $setOpts, $oldAsOf ),
                                                        self::VFLD_VERSION => $version
                                                ];
                                        },
@@ -828,7 +885,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                                                $ttl,
                                                $callback,
                                                // Regenerate value if not newer than $key
-                                               [ 'version' => null, 'minTime' => $asOf ] + $opts
+                                               [ 'version' => null, 'minAsOf' => $asOf ] + $opts
                                        );
                                }
                        } else {
@@ -836,8 +893,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                        }
 
                        // Update the process cache if enabled
-                       if ( $pcTTL >= 0 && $value !== false ) {
-                               $this->procCache->set( $key, $value, $pcTTL );
+                       if ( $procCache && $value !== false ) {
+                               $procCache->set( $key, $value, $pcTTL );
                        }
                }
 
@@ -852,16 +909,19 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @param string $key
         * @param integer $ttl
         * @param callback $callback
-        * @param array $opts Options map for getWithSetCallback() which also includes:
-        *   - minTime: Treat values older than this UNIX timestamp as not existing. Default: null.
+        * @param array $opts Options map for getWithSetCallback()
         * @param float &$asOf Cache generation timestamp of returned value [returned]
         * @return mixed
+        * @note Callable type hints are not used to avoid class-autoloading
         */
        protected function doGetWithSetCallback( $key, $ttl, $callback, array $opts, &$asOf = null ) {
                $lowTTL = isset( $opts['lowTTL'] ) ? $opts['lowTTL'] : min( self::LOW_TTL, $ttl );
                $lockTSE = isset( $opts['lockTSE'] ) ? $opts['lockTSE'] : self::TSE_NONE;
                $checkKeys = isset( $opts['checkKeys'] ) ? $opts['checkKeys'] : [];
-               $minTime = isset( $opts['minTime'] ) ? $opts['minTime'] : 0.0;
+               $busyValue = isset( $opts['busyValue'] ) ? $opts['busyValue'] : null;
+               $popWindow = isset( $opts['hotTTR'] ) ? $opts['hotTTR'] : self::HOT_TTR;
+               $ageNew = isset( $opts['ageNew'] ) ? $opts['ageNew'] : self::AGE_NEW;
+               $minTime = isset( $opts['minAsOf'] ) ? $opts['minAsOf'] : self::MIN_TIMESTAMP_NONE;
                $versioned = isset( $opts['version'] );
 
                // Get the current key value
@@ -873,7 +933,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                if ( $value !== false
                        && $curTTL > 0
                        && $this->isValid( $value, $versioned, $asOf, $minTime )
-                       && !$this->worthRefresh( $curTTL, $lowTTL )
+                       && !$this->worthRefreshExpiring( $curTTL, $lowTTL )
+                       && !$this->worthRefreshPopular( $asOf, $ageNew, $popWindow )
                ) {
                        return $value;
                }
@@ -882,16 +943,18 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                $isTombstone = ( $curTTL !== null && $value === false );
                // Assume a key is hot if requested soon after invalidation
                $isHot = ( $curTTL !== null && $curTTL <= 0 && abs( $curTTL ) <= $lockTSE );
+               // Use the mutex if there is no value and a busy fallback is given
+               $checkBusy = ( $busyValue !== null && $value === false );
                // Decide whether a single thread should handle regenerations.
                // This avoids stampedes when $checkKeys are bumped and when preemptive
                // renegerations take too long. It also reduces regenerations while $key
                // is tombstoned. This balances cache freshness with avoiding DB load.
-               $useMutex = ( $isHot || ( $isTombstone && $lockTSE > 0 ) );
+               $useMutex = ( $isHot || ( $isTombstone && $lockTSE > 0 ) || $checkBusy );
 
                $lockAcquired = false;
                if ( $useMutex ) {
                        // Acquire a datacenter-local non-blocking lock
-                       if ( $this->cache->lock( $key, 0, self::LOCK_TTL ) ) {
+                       if ( $this->cache->add( self::MUTEX_KEY_PREFIX . $key, 1, self::LOCK_TTL ) ) {
                                // Lock acquired; this thread should update the key
                                $lockAcquired = true;
                        } elseif ( $value !== false && $this->isValid( $value, $versioned, $asOf, $minTime ) ) {
@@ -908,6 +971,10 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
 
                                        return $value;
                                }
+                               // Use the busy fallback value if nothing else
+                               if ( $busyValue !== null ) {
+                                       return is_callable( $busyValue ) ? $busyValue() : $busyValue;
+                               }
                        }
                }
 
@@ -917,18 +984,22 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
 
                // Generate the new value from the callback...
                $setOpts = [];
-               $value = call_user_func_array( $callback, [ $cValue, &$ttl, &$setOpts ] );
-               $asOf = microtime( true );
+               $value = call_user_func_array( $callback, [ $cValue, &$ttl, &$setOpts, $asOf ] );
                // When delete() is called, writes are write-holed by the tombstone,
                // so use a special INTERIM key to pass the new value around threads.
-               if ( $useMutex && $value !== false && $ttl >= 0 ) {
+               if ( ( $isTombstone && $lockTSE > 0 ) && $value !== false && $ttl >= 0 ) {
                        $tempTTL = max( 1, (int)$lockTSE ); // set() expects seconds
-                       $wrapped = $this->wrap( $value, $tempTTL, $asOf );
-                       $this->cache->set( self::INTERIM_KEY_PREFIX . $key, $wrapped, $tempTTL );
-               }
-
-               if ( $lockAcquired ) {
-                       $this->cache->unlock( $key );
+                       $newAsOf = microtime( true );
+                       $wrapped = $this->wrap( $value, $tempTTL, $newAsOf );
+                       // Avoid using set() to avoid pointless mcrouter broadcasting
+                       $this->cache->merge(
+                               self::INTERIM_KEY_PREFIX . $key,
+                               function () use ( $wrapped ) {
+                                       return $wrapped;
+                               },
+                               $tempTTL,
+                               1
+                       );
                }
 
                if ( $value !== false && $ttl >= 0 ) {
@@ -937,6 +1008,11 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                        $this->set( $key, $value, $ttl, $setOpts );
                }
 
+               if ( $lockAcquired ) {
+                       // Avoid using delete() to avoid pointless mcrouter broadcasting
+                       $this->cache->changeTTL( self::MUTEX_KEY_PREFIX . $key, 1 );
+               }
+
                return $value;
        }
 
@@ -962,11 +1038,11 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
 
        /**
         * Get the "last error" registered; clearLastError() should be called manually
-        * @return int ERR_* constant for the "last error" registry
+        * @return int ERR_* class constant for the "last error" registry
         */
        final public function getLastError() {
                if ( $this->lastRelayError ) {
-                       // If the cache and the relayer failed, focus on the later.
+                       // If the cache and the relayer failed, focus on the latter.
                        // An update not making it to the relayer means it won't show up
                        // in other DCs (nor will consistent re-hashing see up-to-date values).
                        // On the other hand, if just the cache update failed, then it should
@@ -1001,7 +1077,53 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @since 1.27
         */
        public function clearProcessCache() {
-               $this->procCache->clear();
+               $this->processCaches = [];
+       }
+
+       /**
+        * @param integer $flag ATTR_* class constant
+        * @return integer QOS_* class constant
+        * @since 1.28
+        */
+       public function getQoS( $flag ) {
+               return $this->cache->getQoS( $flag );
+       }
+
+       /**
+        * Get a TTL that is higher for objects that have not changed recently
+        *
+        * This is useful for keys that get explicit purges and DB or purge relay
+        * lag is a potential concern (especially how it interacts with CDN cache)
+        *
+        * Example usage:
+        * @code
+        *     // Last-modified time of page
+        *     $mtime = wfTimestamp( TS_UNIX, $page->getTimestamp() );
+        *     // Get adjusted TTL. If $mtime is 3600 seconds ago and $minTTL/$factor left at
+        *     // defaults, then $ttl is 3600 * .2 = 720. If $minTTL was greater than 720, then
+        *     // $ttl would be $minTTL. If $maxTTL was smaller than 720, $ttl would be $maxTTL.
+        *     $ttl = $cache->adaptiveTTL( $mtime, $cache::TTL_DAY );
+        * @endcode
+        *
+        * @param integer|float $mtime UNIX timestamp
+        * @param integer $maxTTL Maximum TTL (seconds)
+        * @param integer $minTTL Minimum TTL (seconds); Default: 30
+        * @param float $factor Value in the range (0,1); Default: .2
+        * @return integer Adaptive TTL
+        * @since 1.28
+        */
+       public function adaptiveTTL( $mtime, $maxTTL, $minTTL = 30, $factor = .2 ) {
+               if ( is_float( $mtime ) || ctype_digit( $mtime ) ) {
+                       $mtime = (int)$mtime; // handle fractional seconds and string integers
+               }
+
+               if ( !is_int( $mtime ) || $mtime <= 0 ) {
+                       return $minTTL; // no last-modified time provided
+               }
+
+               $age = time() - $mtime;
+
+               return (int)min( $maxTTL, max( $minTTL, $factor * $age ) );
        }
 
        /**
@@ -1015,17 +1137,25 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @return bool Success
         */
        protected function relayPurge( $key, $ttl, $holdoff ) {
-               $event = $this->cache->modifySimpleRelayEvent( [
-                       'cmd' => 'set',
-                       'key' => $key,
-                       'val' => 'PURGED:$UNIXTIME$:' . (int)$holdoff,
-                       'ttl' => max( $ttl, 1 ),
-                       'sbt' => true, // substitute $UNIXTIME$ with actual microtime
-               ] );
-
-               $ok = $this->purgeRelayer->notify( $this->purgeChannel, $event );
-               if ( !$ok ) {
-                       $this->lastRelayError = self::ERR_RELAY;
+               if ( $this->purgeRelayer instanceof EventRelayerNull ) {
+                       // This handles the mcrouter and the single-DC case
+                       $ok = $this->cache->set( $key,
+                               $this->makePurgeValue( microtime( true ), self::HOLDOFF_NONE ),
+                               $ttl
+                       );
+               } else {
+                       $event = $this->cache->modifySimpleRelayEvent( [
+                               'cmd' => 'set',
+                               'key' => $key,
+                               'val' => 'PURGED:$UNIXTIME$:' . (int)$holdoff,
+                               'ttl' => max( $ttl, 1 ),
+                               'sbt' => true, // substitute $UNIXTIME$ with actual microtime
+                       ] );
+
+                       $ok = $this->purgeRelayer->notify( $this->purgeChannel, $event );
+                       if ( !$ok ) {
+                               $this->lastRelayError = self::ERR_RELAY;
+                       }
                }
 
                return $ok;
@@ -1038,14 +1168,19 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @return bool Success
         */
        protected function relayDelete( $key ) {
-               $event = $this->cache->modifySimpleRelayEvent( [
-                       'cmd' => 'delete',
-                       'key' => $key,
-               ] );
-
-               $ok = $this->purgeRelayer->notify( $this->purgeChannel, $event );
-               if ( !$ok ) {
-                       $this->lastRelayError = self::ERR_RELAY;
+               if ( $this->purgeRelayer instanceof EventRelayerNull ) {
+                       // This handles the mcrouter and the single-DC case
+                       $ok = $this->cache->delete( $key );
+               } else {
+                       $event = $this->cache->modifySimpleRelayEvent( [
+                               'cmd' => 'delete',
+                               'key' => $key,
+                       ] );
+
+                       $ok = $this->purgeRelayer->notify( $this->purgeChannel, $event );
+                       if ( !$ok ) {
+                               $this->lastRelayError = self::ERR_RELAY;
+                       }
                }
 
                return $ok;
@@ -1063,7 +1198,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @param float $lowTTL Consider a refresh when $curTTL is less than this
         * @return bool
         */
-       protected function worthRefresh( $curTTL, $lowTTL ) {
+       protected function worthRefreshExpiring( $curTTL, $lowTTL ) {
                if ( $curTTL >= $lowTTL ) {
                        return false;
                } elseif ( $curTTL <= 0 ) {
@@ -1075,6 +1210,40 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                return mt_rand( 1, 1e9 ) <= 1e9 * $chance;
        }
 
+       /**
+        * Check if a key is due for randomized regeneration due to its popularity
+        *
+        * This is used so that popular keys can preemptively refresh themselves for higher
+        * consistency (especially in the case of purge loss/delay). Unpopular keys can remain
+        * in cache with their high nominal TTL. This means popular keys keep good consistency,
+        * whether the data changes frequently or not, and long-tail keys get to stay in cache
+        * and get hits too. Similar to worthRefreshExpiring(), randomization is used.
+        *
+        * @param float $asOf UNIX timestamp of the value
+        * @param integer $ageNew Age of key when this might recommend refreshing (seconds)
+        * @param integer $timeTillRefresh Age of key when it should be refreshed if popular (seconds)
+        * @return bool
+        */
+       protected function worthRefreshPopular( $asOf, $ageNew, $timeTillRefresh ) {
+               $age = microtime( true ) - $asOf;
+               $timeOld = $age - $ageNew;
+               if ( $timeOld <= 0 ) {
+                       return false;
+               }
+
+               // Lifecycle is: new, ramp-up refresh chance, full refresh chance
+               $refreshWindowSec = max( $timeTillRefresh - $ageNew - self::RAMPUP_TTL / 2, 1 );
+               // P(refresh) * (# hits in $refreshWindowSec) = (expected # of refreshes)
+               // P(refresh) * ($refreshWindowSec * $popularHitsPerSec) = 1
+               // P(refresh) = 1/($refreshWindowSec * $popularHitsPerSec)
+               $chance = 1 / ( self::HIT_RATE_HIGH * $refreshWindowSec );
+
+               // Ramp up $chance from 0 to its nominal value over RAMPUP_TTL seconds to avoid stampedes
+               $chance *= ( $timeOld <= self::RAMPUP_TTL ) ? $timeOld / self::RAMPUP_TTL : 1;
+
+               return mt_rand( 1, 1e9 ) <= 1e9 * $chance;
+       }
+
        /**
         * Check whether $value is appropriately versioned and not older than $minTime (if set)
         *
@@ -1198,4 +1367,17 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        protected function makePurgeValue( $timestamp, $holdoff ) {
                return self::PURGE_VAL_PREFIX . (float)$timestamp . ':' . (int)$holdoff;
        }
+
+       /**
+        * @param string $group
+        * @return HashBagOStuff
+        */
+       protected function getProcessCache( $group ) {
+               if ( !isset( $this->processCaches[$group] ) ) {
+                       list( , $n ) = explode( ':', $group );
+                       $this->processCaches[$group] = new HashBagOStuff( [ 'maxKeys' => (int)$n ] );
+               }
+
+               return $this->processCaches[$group];
+       }
 }
index 16c9331..3afbaa3 100644 (file)
@@ -44,6 +44,9 @@ class RestbaseVirtualRESTService extends VirtualRESTService {
         *   - HTTPProxy      : HTTP proxy to use (optional)
         *   - parsoidCompat  : whether to parse URL as if they were meant for Parsoid
         *                       boolean (optional)
+        *   - fixedUrl       : Do not append domain to the url. For example to use
+        *                       English Wikipedia restbase, you would this to true
+        *                       and url to https://en.wikipedia.org/api/rest_#version#
         */
        public function __construct( array $params ) {
                // set up defaults and merge them with the given params
@@ -54,7 +57,8 @@ class RestbaseVirtualRESTService extends VirtualRESTService {
                        'timeout' => 100,
                        'forwardCookies' => false,
                        'HTTPProxy' => null,
-                       'parsoidCompat' => false
+                       'parsoidCompat' => false,
+                       'fixedUrl' => false,
                ], $params );
                // Ensure that the url parameter has a trailing slash.
                $mparams['url'] = preg_replace(
@@ -81,10 +85,18 @@ class RestbaseVirtualRESTService extends VirtualRESTService {
 
                $result = [];
                foreach ( $reqs as $key => $req ) {
-                       // replace /local/ with the current domain
-                       $req['url'] = preg_replace( '#^local/#', $this->params['domain'] . '/', $req['url'] );
-                       // and prefix it with the service URL
-                       $req['url'] = $this->params['url'] . $req['url'];
+                       if ( $this->params['fixedUrl'] ) {
+                               $version = explode( '/', $req['url'] )[1];
+                               $req['url'] =
+                                       str_replace( '#version#', $version, $this->params['url'] ) .
+                                       preg_replace( '#^local/v./#', '', $req['url'] );
+                       } else {
+                               // replace /local/ with the current domain
+                               $req['url'] = preg_replace( '#^local/#', $this->params['domain'] . '/', $req['url'] );
+                               // and prefix it with the service URL
+                               $req['url'] = $this->params['url'] . $req['url'];
+                       }
+
                        // set the appropriate proxy, timeout and headers
                        if ( $this->params['HTTPProxy'] ) {
                                $req['proxy'] = $this->params['HTTPProxy'];
@@ -99,7 +111,6 @@ class RestbaseVirtualRESTService extends VirtualRESTService {
                }
 
                return $result;
-
        }
 
        /**
index f304bd9..0864e5c 100644 (file)
@@ -45,9 +45,9 @@
  */
 class VirtualRESTServiceClient {
        /** @var MultiHttpClient */
-       protected $http;
-       /** @var VirtualRESTService[] Map of (prefix => VirtualRESTService) */
-       protected $instances = [];
+       private $http;
+       /** @var array Map of (prefix => VirtualRESTService|array) */
+       private $instances = [];
 
        const VALID_MOUNT_REGEX = '#^/[0-9a-z]+/([0-9a-z]+/)*$#';
 
@@ -61,15 +61,24 @@ class VirtualRESTServiceClient {
        /**
         * Map a prefix to service handler
         *
+        * If $instance is in array, it must have these keys:
+        *   - class : string; fully qualified VirtualRESTService class name
+        *   - config : array; map of parameters that is the first __construct() argument
+        *
         * @param string $prefix Virtual path
-        * @param VirtualRESTService $instance
+        * @param VirtualRESTService|array $instance Service or info to yield the service
         */
-       public function mount( $prefix, VirtualRESTService $instance ) {
+       public function mount( $prefix, $instance ) {
                if ( !preg_match( self::VALID_MOUNT_REGEX, $prefix ) ) {
                        throw new UnexpectedValueException( "Invalid service mount point '$prefix'." );
                } elseif ( isset( $this->instances[$prefix] ) ) {
                        throw new UnexpectedValueException( "A service is already mounted on '$prefix'." );
                }
+               if ( !( $instance instanceof VirtualRESTService ) ) {
+                       if ( !isset( $instance['class'] ) || !isset( $instance['config'] ) ) {
+                               throw new UnexpectedValueException( "Missing 'class' or 'config' ('$prefix')." );
+                       }
+               }
                $this->instances[$prefix] = $instance;
        }
 
@@ -104,7 +113,7 @@ class VirtualRESTServiceClient {
                };
 
                $matches = []; // matching prefixes (mount points)
-               foreach ( $this->instances as $prefix => $service ) {
+               foreach ( $this->instances as $prefix => $unused ) {
                        if ( strpos( $path, $prefix ) === 0 ) {
                                $matches[] = $prefix;
                        }
@@ -112,8 +121,8 @@ class VirtualRESTServiceClient {
                usort( $matches, $cmpFunc );
 
                // Return the most specific prefix and corresponding service
-               return isset( $matches[0] )
-                       ? [ $matches[0], $this->instances[$matches[0]] ]
+               return $matches
+                       ? [ $matches[0], $this->getInstance( $matches[0] ) ]
                        : [ null, null ];
        }
 
@@ -216,7 +225,7 @@ class VirtualRESTServiceClient {
                        // defer the original or to set a proxy response to the original.
                        $newReplaceReqsByService = [];
                        foreach ( $replaceReqsByService as $prefix => $servReqs ) {
-                               $service = $this->instances[$prefix];
+                               $service = $this->getInstance( $prefix );
                                foreach ( $service->onRequests( $servReqs, $idFunc ) as $index => $req ) {
                                        // Services use unique IDs for replacement requests
                                        if ( isset( $servReqs[$index] ) || isset( $origPending[$index] ) ) {
@@ -237,8 +246,6 @@ class VirtualRESTServiceClient {
                                        $checkReqIndexesByPrefix[$prefix][$index] = 1;
                                }
                        }
-                       // Update index of requests to inspect for replacement
-                       $replaceReqsByService = $newReplaceReqsByService;
                        // Run the actual work HTTP requests
                        foreach ( $this->http->runMulti( $executeReqs ) as $index => $ranReq ) {
                                $doneReqs[$index] = $ranReq;
@@ -252,7 +259,7 @@ class VirtualRESTServiceClient {
                        // forced by setting 'response' rather than actually be sent over the wire.
                        $newReplaceReqsByService = [];
                        foreach ( $checkReqIndexesByPrefix as $prefix => $servReqIndexes ) {
-                               $service = $this->instances[$prefix];
+                               $service = $this->getInstance( $prefix );
                                // $doneReqs actually has the requests (with 'response' set)
                                $servReqs = array_intersect_key( $doneReqs, $servReqIndexes );
                                foreach ( $service->onResponses( $servReqs, $idFunc ) as $index => $req ) {
@@ -290,4 +297,26 @@ class VirtualRESTServiceClient {
 
                return $responses;
        }
+
+       /**
+        * @param string $prefix
+        * @return VirtualRESTService
+        */
+       private function getInstance( $prefix ) {
+               if ( !isset( $this->instances[$prefix] ) ) {
+                       throw new RunTimeException( "No service registered at prefix '{$prefix}'." );
+               }
+
+               if ( !( $this->instances[$prefix] instanceof VirtualRESTService ) ) {
+                       $config = $this->instances[$prefix]['config'];
+                       $class = $this->instances[$prefix]['class'];
+                       $service = new $class( $config );
+                       if ( !( $service instanceof VirtualRESTService ) ) {
+                               throw new UnexpectedValueException( "Registered service has the wrong class." );
+                       }
+                       $this->instances[$prefix] = $service;
+               }
+
+               return $this->instances[$prefix];
+       }
 }
index 432dcb2..c203a16 100644 (file)
@@ -25,8 +25,10 @@ use DummyLinker;
 use Hooks;
 use Html;
 use HtmlArmor;
+use LinkCache;
 use Linker;
 use MediaWiki\MediaWikiServices;
+use MWNamespace;
 use Sanitizer;
 use Title;
 use TitleFormatter;
@@ -34,6 +36,7 @@ use TitleFormatter;
 /**
  * Class that generates HTML <a> links for pages.
  *
+ * @see https://www.mediawiki.org/wiki/Manual:LinkRenderer
  * @since 1.28
  */
 class LinkRenderer {
@@ -62,6 +65,11 @@ class LinkRenderer {
         */
        private $titleFormatter;
 
+       /**
+        * @var LinkCache
+        */
+       private $linkCache;
+
        /**
         * Whether to run the legacy Linker hooks
         *
@@ -71,9 +79,11 @@ class LinkRenderer {
 
        /**
         * @param TitleFormatter $titleFormatter
+        * @param LinkCache $linkCache
         */
-       public function __construct( TitleFormatter $titleFormatter ) {
+       public function __construct( TitleFormatter $titleFormatter, LinkCache $linkCache ) {
                $this->titleFormatter = $titleFormatter;
+               $this->linkCache = $linkCache;
        }
 
        /**
@@ -225,7 +235,7 @@ class LinkRenderer {
        }
 
        /**
-        * If you have already looked up the proper CSS classes using Linker::getLinkColour()
+        * If you have already looked up the proper CSS classes using LinkRenderer::getLinkClasses()
         * or some other method, use this to avoid looking it up again.
         *
         * @param LinkTarget $target
@@ -276,7 +286,7 @@ class LinkRenderer {
                if ( $target->isExternal() ) {
                        $classes[] = 'extiw';
                }
-               $colour = Linker::getLinkColour( $target, $this->stubThreshold );
+               $colour = $this->getLinkClasses( $target );
                if ( $colour !== '' ) {
                        $classes[] = $colour;
                }
@@ -391,16 +401,13 @@ class LinkRenderer {
        private function getLinkURL( LinkTarget $target, array $query = [] ) {
                // TODO: Use a LinkTargetResolver service instead of Title
                $title = Title::newFromLinkTarget( $target );
-               $proto = $this->expandUrls !== false
-                       ? $this->expandUrls
-                       : PROTO_RELATIVE;
                if ( $this->forceArticlePath ) {
                        $realQuery = $query;
                        $query = [];
                } else {
                        $realQuery = [];
                }
-               $url = $title->getLinkURL( $query, false, $proto );
+               $url = $title->getLinkURL( $query, false, $this->expandUrls );
 
                if ( $this->forceArticlePath && $realQuery ) {
                        $url = wfAppendQuery( $url, $realQuery );
@@ -445,4 +452,30 @@ class LinkRenderer {
                return $ret;
        }
 
+       /**
+        * Return the CSS classes of a known link
+        *
+        * @param LinkTarget $target
+        * @return string CSS class
+        */
+       public function getLinkClasses( LinkTarget $target ) {
+               // Make sure the target is in the cache
+               $id = $this->linkCache->addLinkObj( $target );
+               if ( $id == 0 ) {
+                       // Doesn't exist
+                       return '';
+               }
+
+               if ( $this->linkCache->getGoodLinkFieldObj( $target, 'redirect' ) ) {
+                       # Page is a redirect
+                       return 'mw-redirect';
+               } elseif ( $this->stubThreshold > 0 && MWNamespace::isContent( $target->getNamespace() )
+                       && $this->linkCache->getGoodLinkFieldObj( $target, 'length' ) < $this->stubThreshold
+               ) {
+                       # Page is a stub
+                       return 'stub';
+               }
+
+               return '';
+       }
 }
index 7124be1..b7c05c2 100644 (file)
@@ -21,6 +21,7 @@
  */
 namespace MediaWiki\Linker;
 
+use LinkCache;
 use TitleFormatter;
 use User;
 
@@ -35,18 +36,25 @@ class LinkRendererFactory {
         */
        private $titleFormatter;
 
+       /**
+        * @var LinkCache
+        */
+       private $linkCache;
+
        /**
         * @param TitleFormatter $titleFormatter
+        * @param LinkCache $linkCache
         */
-       public function __construct( TitleFormatter $titleFormatter ) {
+       public function __construct( TitleFormatter $titleFormatter, LinkCache $linkCache ) {
                $this->titleFormatter = $titleFormatter;
+               $this->linkCache = $linkCache;
        }
 
        /**
         * @return LinkRenderer
         */
        public function create() {
-               return new LinkRenderer( $this->titleFormatter );
+               return new LinkRenderer( $this->titleFormatter, $this->linkCache );
        }
 
        /**
index 20d0217..7746d99 100644 (file)
@@ -705,39 +705,41 @@ class ManualLogEntry extends LogEntryBase {
         *
         * @param int $newId Id of the log entry.
         * @param string $to One of: rcandudp (default), rc, udp
-        * @return RecentChange|null
         */
        public function publish( $newId, $to = 'rcandudp' ) {
-               $log = new LogPage( $this->getType() );
-               if ( $log->isRestricted() ) {
-                       return null;
-               }
-
-               $rc = $this->getRecentChange( $newId );
-
-               if ( $to === 'rc' || $to === 'rcandudp' ) {
-                       $rc->save( 'pleasedontudp' );
-               }
-
-               if ( $to === 'udp' || $to === 'rcandudp' ) {
-                       $rc->notifyRCFeeds();
-               }
-
-               // Log the autopatrol if the log entry is patrollable
-               if ( $this->getIsPatrollable() &&
-                       $rc->getAttribute( 'rc_patrolled' ) === 1 ) {
-                       PatrolLog::record( $rc, true, $this->getPerformer() );
-               }
-
-               // Add change tags to the log entry and (if applicable) the associated revision
-               $tags = $this->getTags();
-               if ( !is_null( $tags ) ) {
-                       $rcId = $rc->getAttribute( 'rc_id' );
-                       $revId = $this->getAssociatedRevId(); // Use null if $revId is 0
-                       ChangeTags::addTags( $tags, $rcId, $revId > 0 ? $revId : null, $newId );
-               }
-
-               return $rc;
+               DeferredUpdates::addCallableUpdate(
+                       function () use ( $newId, $to ) {
+                               $log = new LogPage( $this->getType() );
+                               if ( !$log->isRestricted() ) {
+                                       $rc = $this->getRecentChange( $newId );
+
+                                       if ( $to === 'rc' || $to === 'rcandudp' ) {
+                                               $rc->save( 'pleasedontudp' );
+                                       }
+
+                                       if ( $to === 'udp' || $to === 'rcandudp' ) {
+                                               $rc->notifyRCFeeds();
+                                       }
+
+                                       // Log the autopatrol if the log entry is patrollable
+                                       if ( $this->getIsPatrollable() &&
+                                               $rc->getAttribute( 'rc_patrolled' ) === 1
+                                       ) {
+                                               PatrolLog::record( $rc, true, $this->getPerformer() );
+                                       }
+
+                                       // Add change tags to the log entry and (if applicable) the associated revision
+                                       $tags = $this->getTags();
+                                       if ( !is_null( $tags ) ) {
+                                               $rcId = $rc->getAttribute( 'rc_id' );
+                                               $revId = $this->getAssociatedRevId(); // Use null if $revId is 0
+                                               ChangeTags::addTags( $tags, $rcId, $revId > 0 ? $revId : null, $newId );
+                                       }
+                               }
+                       },
+                       DeferredUpdates::POSTSEND,
+                       wfGetDB( DB_MASTER )
+               );
        }
 
        public function getType() {
index 3d04641..68163c1 100644 (file)
@@ -78,7 +78,7 @@ class LogPager extends ReverseChronologicalPager {
                $this->getDateCond( $year, $month );
                $this->mTagFilter = $tagFilter;
 
-               $this->mDb = wfGetDB( DB_SLAVE, 'logpager' );
+               $this->mDb = wfGetDB( DB_REPLICA, 'logpager' );
        }
 
        public function getDefaultQuery() {
@@ -171,6 +171,9 @@ class LogPager extends ReverseChronologicalPager {
                if ( is_null( $usertitle ) ) {
                        return;
                }
+               // Normalize username first so that non-existent users used
+               // in maintenance scripts work
+               $name = $usertitle->getText();
                /* Fetch userid at first, if known, provides awesome query plan afterwards */
                $userid = User::idFromName( $name );
                if ( !$userid ) {
@@ -187,7 +190,7 @@ class LogPager extends ReverseChronologicalPager {
                                ' != ' . LogPage::SUPPRESSED_USER;
                }
 
-               $this->performer = $usertitle->getText();
+               $this->performer = $name;
        }
 
        /**
index 16740d8..ed361eb 100644 (file)
@@ -84,11 +84,9 @@ class DjVuImage {
        function dump() {
                $file = fopen( $this->mFilename, 'rb' );
                $header = fread( $file, 12 );
-               // @todo FIXME: Would be good to replace this extract() call with
-               // something that explicitly initializes local variables.
-               extract( unpack( 'a4magic/a4chunk/NchunkLength', $header ) );
-               /** @var string $chunk
-                * @var string $chunkLength */
+               $arr = unpack( 'a4magic/a4chunk/NchunkLength', $header );
+               $chunk = $arr['chunk'];
+               $chunkLength = $arr['chunkLength'];
                echo "$chunk $chunkLength\n";
                $this->dumpForm( $file, $chunkLength, 1 );
                fclose( $file );
@@ -103,11 +101,9 @@ class DjVuImage {
                        if ( $chunkHeader == '' ) {
                                break;
                        }
-                       // @todo FIXME: Would be good to replace this extract() call with
-                       // something that explicitly initializes local variables.
-                       extract( unpack( 'a4chunk/NchunkLength', $chunkHeader ) );
-                       /** @var string $chunk
-                        * @var string $chunkLength */
+                       $arr = unpack( 'a4chunk/NchunkLength', $chunkHeader );
+                       $chunk = $arr['chunk'];
+                       $chunkLength = $arr['chunkLength'];
                        echo str_repeat( ' ', $indent * 4 ) . "$chunk $chunkLength\n";
 
                        if ( $chunk == 'FORM' ) {
@@ -138,24 +134,19 @@ class DjVuImage {
                if ( strlen( $header ) < 16 ) {
                        wfDebug( __METHOD__ . ": too short file header\n" );
                } else {
-                       // @todo FIXME: Would be good to replace this extract() call with
-                       // something that explicitly initializes local variables.
-                       extract( unpack( 'a4magic/a4form/NformLength/a4subtype', $header ) );
-
-                       /** @var string $magic
-                        * @var string $subtype
-                        * @var string $formLength
-                        * @var string $formType */
-                       if ( $magic != 'AT&T' ) {
+                       $arr = unpack( 'a4magic/a4form/NformLength/a4subtype', $header );
+
+                       $subtype = $arr['subtype'];
+                       if ( $arr['magic'] != 'AT&T' ) {
                                wfDebug( __METHOD__ . ": not a DjVu file\n" );
                        } elseif ( $subtype == 'DJVU' ) {
                                // Single-page document
                                $info = $this->getPageInfo( $file );
                        } elseif ( $subtype == 'DJVM' ) {
                                // Multi-page document
-                               $info = $this->getMultiPageInfo( $file, $formLength );
+                               $info = $this->getMultiPageInfo( $file, $arr['formLength'] );
                        } else {
-                               wfDebug( __METHOD__ . ": unrecognized DJVU file type '$formType'\n" );
+                               wfDebug( __METHOD__ . ": unrecognized DJVU file type '{$arr['subtype']}'\n" );
                        }
                }
                fclose( $file );
@@ -168,13 +159,9 @@ class DjVuImage {
                if ( strlen( $header ) < 8 ) {
                        return [ false, 0 ];
                } else {
-                       // @todo FIXME: Would be good to replace this extract() call with
-                       // something that explicitly initializes local variables.
-                       extract( unpack( 'a4chunk/Nlength', $header ) );
+                       $arr = unpack( 'a4chunk/Nlength', $header );
 
-                       /** @var string $chunk
-                        * @var string $length */
-                       return [ $chunk, $length ];
+                       return [ $arr['chunk'], $arr['length'] ];
                }
        }
 
@@ -236,31 +223,22 @@ class DjVuImage {
                        return false;
                }
 
-               // @todo FIXME: Would be good to replace this extract() call with
-               // something that explicitly initializes local variables.
-               extract( unpack(
+               $arr = unpack(
                        'nwidth/' .
                        'nheight/' .
                        'Cminor/' .
                        'Cmajor/' .
                        'vresolution/' .
-                       'Cgamma', $data ) );
+                       'Cgamma', $data );
 
                # Newer files have rotation info in byte 10, but we don't use it yet.
 
-               /** @var string $width
-                * @var string $height
-                * @var string $major
-                * @var string $minor
-                * @var string $resolution
-                * @var string $length
-                * @var string $gamma */
                return [
-                       'width' => $width,
-                       'height' => $height,
-                       'version' => "$major.$minor",
-                       'resolution' => $resolution,
-                       'gamma' => $gamma / 10.0 ];
+                       'width' => $arr['width'],
+                       'height' => $arr['height'],
+                       'version' => "{$arr['major']}.{$arr['minor']}",
+                       'resolution' => $arr['resolution'],
+                       'gamma' => $arr['gamma'] / 10.0 ];
        }
 
        /**
index 6b0f887..70a43f2 100644 (file)
@@ -20,6 +20,7 @@
  * @file
  * @ingroup Media
  */
+use MediaWiki\MediaWikiServices;
 
 /**
  * Base media handler class
@@ -36,39 +37,15 @@ abstract class MediaHandler {
         */
        const MAX_ERR_LOG_SIZE = 65535;
 
-       /** @var MediaHandler[] Instance cache with array of MediaHandler */
-       protected static $handlers = [];
-
        /**
         * Get a MediaHandler for a given MIME type from the instance cache
         *
         * @param string $type
-        * @return MediaHandler
+        * @return MediaHandler|bool
         */
        static function getHandler( $type ) {
-               global $wgMediaHandlers;
-               if ( !isset( $wgMediaHandlers[$type] ) ) {
-                       wfDebug( __METHOD__ . ": no handler found for $type.\n" );
-
-                       return false;
-               }
-               $class = $wgMediaHandlers[$type];
-               if ( !isset( self::$handlers[$class] ) ) {
-                       self::$handlers[$class] = new $class;
-                       if ( !self::$handlers[$class]->isEnabled() ) {
-                               wfDebug( __METHOD__ . ": $class is not enabled\n" );
-                               self::$handlers[$class] = false;
-                       }
-               }
-
-               return self::$handlers[$class];
-       }
-
-       /**
-        * Resets all static caches
-        */
-       public static function resetCache() {
-               self::$handlers = [];
+               return MediaWikiServices::getInstance()
+                       ->getMediaHandlerFactory()->getHandler( $type );
        }
 
        /**
diff --git a/includes/media/MediaHandlerFactory.php b/includes/media/MediaHandlerFactory.php
new file mode 100644 (file)
index 0000000..543dc80
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+/**
+ * Media-handling base classes and generic functionality.
+ *
+ * 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 Media
+ */
+
+/**
+ * Class to construct MediaHandler objects
+ *
+ * @since 1.28
+ */
+class MediaHandlerFactory {
+
+       /**
+        * Default, MediaWiki core media handlers
+        *
+        * @var array
+        */
+       private static $coreHandlers = [
+               'image/jpeg' => JpegHandler::class,
+               'image/png' => PNGHandler::class,
+               'image/gif' => GIFHandler::class,
+               'image/tiff' => TiffHandler::class,
+               'image/webp' => WebPHandler::class,
+               'image/x-ms-bmp' => BmpHandler::class,
+               'image/x-bmp' => BmpHandler::class,
+               'image/x-xcf' => XCFHandler::class,
+               'image/svg+xml' => SvgHandler::class, // official
+               'image/svg' => SvgHandler::class, // compat
+               'image/vnd.djvu' => DjVuHandler::class, // official
+               'image/x.djvu' => DjVuHandler::class, // compat
+               'image/x-djvu' => DjVuHandler::class, // compat
+       ];
+
+       /**
+        * @var array
+        */
+       private $registry;
+
+       /**
+        * Instance cache of MediaHandler objects by mimetype
+        *
+        * @var MediaHandler[]
+        */
+       private $handlers;
+
+       public function __construct( array $registry ) {
+               $this->registry = $registry + self::$coreHandlers;
+       }
+
+       protected function getHandlerClass( $type ) {
+               if ( isset( $this->registry[$type] ) ) {
+                       return $this->registry[$type];
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * @param string $type mimetype
+        * @return bool|MediaHandler
+        */
+       public function getHandler( $type ) {
+               if ( isset( $this->handlers[$type] ) ) {
+                       return $this->handlers[$type];
+               }
+
+               $class = $this->getHandlerClass( $type );
+               if ( $class !== false ) {
+                       /** @var MediaHandler $handler */
+                       $handler = new $class;
+                       if ( !$handler->isEnabled() ) {
+                               wfDebug( __METHOD__ . ": $class is not enabled\n" );
+                               $handler = false;
+                       }
+               } else {
+                       wfDebug( __METHOD__ . ": no handler found for $type.\n" );
+                       $handler = false;
+               }
+
+               $this->handlers[$type] = $handler;
+               return $handler;
+       }
+}
index 9176b54..b3a555a 100644 (file)
@@ -421,8 +421,10 @@ class ThumbnailImage extends MediaTransformOutput {
                }
 
                // Additional densities for responsive images, if specified.
-               if ( !empty( $this->responsiveUrls ) ) {
-                       $attribs['srcset'] = Html::srcSet( $this->responsiveUrls );
+               // If any of these urls is the same as src url, it'll be excluded.
+               $responsiveUrls = array_diff( $this->responsiveUrls, [ $this->url ] );
+               if ( !empty( $responsiveUrls ) ) {
+                       $attribs['srcset'] = Html::srcSet( $responsiveUrls );
                }
 
                Hooks::run( 'ThumbnailBeforeProduceHTML', [ $this, &$attribs, &$linkAttribs ] );
index 090ace8..aefda79 100644 (file)
@@ -42,6 +42,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
         *   - serializer:          May be either "php" or "igbinary". Igbinary produces more compact
         *                          values, but serialization is much slower unless the php.ini option
         *                          igbinary.compact_strings is off.
+        *   - use_binary_protocol  Whether to enable the binary protocol (default is ASCII) (boolean)
         * @param array $params
         * @throws InvalidArgumentException
         */
@@ -62,8 +63,8 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                        $this->client = new Memcached;
                }
 
-               if ( !isset( $params['serializer'] ) ) {
-                       $params['serializer'] = 'php';
+               if ( $params['use_binary_protocol'] ) {
+                       $this->client->setOption( Memcached::OPT_BINARY_PROTOCOL, true );
                }
 
                if ( isset( $params['retry_timeout'] ) ) {
@@ -119,6 +120,20 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                $this->client->addServers( $servers );
        }
 
+       protected function applyDefaultParams( $params ) {
+               $params = parent::applyDefaultParams( $params );
+
+               if ( !isset( $params['use_binary_protocol'] ) ) {
+                       $params['use_binary_protocol'] = false;
+               }
+
+               if ( !isset( $params['serializer'] ) ) {
+                       $params['serializer'] = 'php';
+               }
+
+               return $params;
+       }
+
        protected function getWithToken( $key, &$casToken, $flags = 0 ) {
                $this->debugLog( "get($key)" );
                $result = $this->client->get( $this->validateKeyEncoding( $key ), null, $casToken );
@@ -226,4 +241,10 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                $result = $this->client->setMulti( $data, $this->fixExpiry( $exptime ) );
                return $this->checkResult( false, $result );
        }
+
+       public function changeTTL( $key, $expiry = 0 ) {
+               $this->debugLog( "touch($key)" );
+               $result = $this->client->touch( $key, $expiry );
+               return $this->checkResult( $key, $result );
+       }
 }
index e1bb2db..1e0013f 100644 (file)
@@ -39,7 +39,7 @@ use MediaWiki\Services\ServiceDisabledException;
  *        stored anywhere else (e.g. a "hoard" of objects).
  *
  * The former should always use strongly consistent stores, so callers don't
- * have to deal with stale reads. The later may be eventually consistent, but
+ * have to deal with stale reads. The latter may be eventually consistent, but
  * callers can use BagOStuff:READ_LATEST to see the latest available data.
  *
  * Primary entry points:
@@ -65,7 +65,7 @@ use MediaWiki\Services\ServiceDisabledException;
  *   Purpose: Ephemeral global storage.
  *   Stored centrally within the primary data-center.
  *   Changes are applied there first and replicated to other DCs (best-effort).
- *   To retrieve the latest value (e.g. not from a slave), use BagOStuff::READ_LATEST.
+ *   To retrieve the latest value (e.g. not from a replica DB), use BagOStuff::READ_LATEST.
  *   This store may be subject to LRU style evictions.
  *
  * - ObjectCache::getInstance( $cacheType )
@@ -228,14 +228,12 @@ class ObjectCache {
                        }
                }
 
-               try {
-                       // Make sure we actually have a DB backend before falling back to CACHE_DB
-                       MediaWikiServices::getInstance()->getDBLoadBalancer();
-                       $candidate = CACHE_DB;
-               } catch ( ServiceDisabledException $e ) {
+               if ( MediaWikiServices::getInstance()->isServiceDisabled( 'DBLoadBalancer' ) ) {
                        // The LoadBalancer is disabled, probably because
                        // MediaWikiServices::disableStorageBackend was called.
                        $candidate = CACHE_NONE;
+               } else {
+                       $candidate = CACHE_DB;
                }
 
                return self::getInstance( $candidate );
@@ -251,7 +249,7 @@ class ObjectCache {
         *     ObjectCache::getLocalServerInstance( $fallbackType );
         *
         *     // From $wgObjectCaches via newFromParams()
-        *     ObjectCache::getLocalServerInstance( array( 'fallback' => $fallbackType ) );
+        *     ObjectCache::getLocalServerInstance( [ 'fallback' => $fallbackType ] );
         *
         * @param int|string|array $fallback Fallback cache or parameter map with 'fallback'
         * @return BagOStuff
@@ -280,7 +278,7 @@ class ObjectCache {
         * @param array $params [optional] Array key 'fallback' for $fallback.
         * @param int|string $fallback Fallback cache, e.g. (CACHE_NONE, "hash") (since 1.24)
         * @return BagOStuff
-        * @deprecated 1.27
+        * @deprecated since 1.27
         */
        public static function newAccelerator( $params = [], $fallback = null ) {
                if ( $fallback === null ) {
index 90508da..f9d201f 100644 (file)
@@ -272,10 +272,10 @@ class RedisBagOStuff extends BagOStuff {
                if ( !$conn ) {
                        return false;
                }
-               if ( !$conn->exists( $key ) ) {
-                       return null;
-               }
                try {
+                       if ( !$conn->exists( $key ) ) {
+                               return null;
+                       }
                        // @FIXME: on races, the key may have a 0 TTL
                        $result = $conn->incrBy( $key, $value );
                } catch ( RedisException $e ) {
@@ -287,6 +287,24 @@ class RedisBagOStuff extends BagOStuff {
                return $result;
        }
 
+       public function changeTTL( $key, $expiry = 0 ) {
+               list( $server, $conn ) = $this->getConnection( $key );
+               if ( !$conn ) {
+                       return false;
+               }
+
+               $expiry = $this->convertToRelative( $expiry );
+               try {
+                       $result = $conn->expire( $key, $expiry );
+               } catch ( RedisException $e ) {
+                       $result = false;
+                       $this->handleException( $conn, $e );
+               }
+
+               $this->logRequest( 'expire', $key, $server, $result );
+               return $result;
+       }
+
        public function modifySimpleRelayEvent( array $event ) {
                if ( array_key_exists( 'val', $event ) ) {
                        $event['val'] = serialize( $event['val'] ); // this class uses PHP serialization
@@ -345,7 +363,7 @@ class RedisBagOStuff extends BagOStuff {
                                try {
                                        if ( $this->getMasterLinkStatus( $conn ) === 'down' ) {
                                                // If the master cannot be reached, fail-over to the next server.
-                                               // If masters are in data-center A, and slaves in data-center B,
+                                               // If masters are in data-center A, and replica DBs in data-center B,
                                                // this helps avoid the case were fail-over happens in A but not
                                                // to the corresponding server in B (e.g. read/write mismatch).
                                                continue;
@@ -366,10 +384,10 @@ class RedisBagOStuff extends BagOStuff {
        }
 
        /**
-        * Check the master link status of a Redis server that is configured as a slave.
+        * Check the master link status of a Redis server that is configured as a replica DB.
         * @param RedisConnRef $conn
         * @return string|null Master link status (either 'up' or 'down'), or null
-        *  if the server is not a slave.
+        *  if the server is not a replica DB.
         */
        protected function getMasterLinkStatus( RedisConnRef $conn ) {
                $info = $conn->info();
index 98b6eb9..3baae50 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup Cache
  */
 
+use \MediaWiki\MediaWikiServices;
+
 /**
  * Class to store objects in the database
  *
@@ -42,10 +44,12 @@ class SqlBagOStuff extends BagOStuff {
        /** @var string */
        protected $tableName = 'objectcache';
        /** @var bool */
-       protected $slaveOnly = false;
+       protected $replicaOnly = false;
        /** @var int */
        protected $syncTimeout = 3;
 
+       /** @var LoadBalancer|null */
+       protected $separateMainLB;
        /** @var array */
        protected $conns;
        /** @var array UNIX timestamps */
@@ -81,16 +85,19 @@ class SqlBagOStuff extends BagOStuff {
         *                  required to hold the largest shard index. Data will be
         *                  distributed across all tables by key hash. This is for
         *                  MySQL bugs 61735 and 61736.
-        *   - slaveOnly:   Whether to only use slave DBs and avoid triggering
+        *   - slaveOnly:   Whether to only use replica DBs and avoid triggering
         *                  garbage collection logic of expired items. This only
         *                  makes sense if the primary DB is used and only if get()
         *                  calls will be used. This is used by ReplicatedBagOStuff.
-        *   - syncTimeout: Max seconds to wait for slaves to catch up for WRITE_SYNC.
+        *   - syncTimeout: Max seconds to wait for replica DBs to catch up for WRITE_SYNC.
         *
         * @param array $params
         */
        public function __construct( $params ) {
                parent::__construct( $params );
+
+               $this->attrMap[self::ATTR_EMULATION] = self::QOS_EMULATION_SQL;
+
                if ( isset( $params['servers'] ) ) {
                        $this->serverInfos = [];
                        $this->serverTags = [];
@@ -109,6 +116,7 @@ class SqlBagOStuff extends BagOStuff {
                        $this->serverInfos = [ $params['server'] ];
                        $this->numServers = count( $this->serverInfos );
                } else {
+                       // Default to using the main wiki's database servers
                        $this->serverInfos = false;
                        $this->numServers = 1;
                }
@@ -124,7 +132,24 @@ class SqlBagOStuff extends BagOStuff {
                if ( isset( $params['syncTimeout'] ) ) {
                        $this->syncTimeout = $params['syncTimeout'];
                }
-               $this->slaveOnly = !empty( $params['slaveOnly'] );
+               $this->replicaOnly = !empty( $params['slaveOnly'] );
+       }
+
+       protected function getSeparateMainLB() {
+               global $wgDBtype;
+
+               if ( $wgDBtype === 'mysql' && $this->usesMainDB() ) {
+                       if ( !$this->separateMainLB ) {
+                               // We must keep a separate connection to MySQL in order to avoid deadlocks
+                               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+                               $this->separateMainLB = $lbFactory->newMainLB();
+                       }
+                       return $this->separateMainLB;
+               } else {
+                       // However, SQLite has an opposite behavior. And PostgreSQL needs to know
+                       // if we are in transaction or not (@TODO: find some PostgreSQL work-around).
+                       return null;
+               }
        }
 
        /**
@@ -158,16 +183,13 @@ class SqlBagOStuff extends BagOStuff {
                                $db = DatabaseBase::factory( $type, $info );
                                $db->clearFlag( DBO_TRX );
                        } else {
-                               // We must keep a separate connection to MySQL in order to avoid deadlocks
-                               // However, SQLite has an opposite behavior. And PostgreSQL needs to know
-                               // if we are in transaction or not (@TODO: find some work-around).
-                               $index = $this->slaveOnly ? DB_SLAVE : DB_MASTER;
-                               if ( wfGetDB( $index )->getType() == 'mysql' ) {
-                                       $lb = wfGetLBFactory()->newMainLB();
-                                       $db = $lb->getConnection( $index );
+                               $index = $this->replicaOnly ? DB_REPLICA : DB_MASTER;
+                               if ( $this->getSeparateMainLB() ) {
+                                       $db = $this->getSeparateMainLB()->getConnection( $index );
                                        $db->clearFlag( DBO_TRX ); // auto-commit mode
                                } else {
                                        $db = wfGetDB( $index );
+                                       // Can't mess with transaction rounds (DBO_TRX) :(
                                }
                        }
                        $this->logger->debug( sprintf( "Connection %s will be used for SqlBagOStuff", $db ) );
@@ -273,6 +295,7 @@ class SqlBagOStuff extends BagOStuff {
                        if ( isset( $dataRows[$key] ) ) { // HIT?
                                $row = $dataRows[$key];
                                $this->debug( "get: retrieved data; expiry time is " . $row->exptime );
+                               $db = null;
                                try {
                                        $db = $this->getDB( $row->serverIndex );
                                        if ( $this->isExpired( $db, $row->exptime ) ) { // MISS
@@ -281,7 +304,7 @@ class SqlBagOStuff extends BagOStuff {
                                                $values[$key] = $this->unserialize( $db->decodeBlob( $row->value ) );
                                        }
                                } catch ( DBQueryError $e ) {
-                                       $this->handleWriteError( $e, $row->serverIndex );
+                                       $this->handleWriteError( $e, $db, $row->serverIndex );
                                }
                        } else { // MISS
                                $this->debug( 'get: no matching rows' );
@@ -303,10 +326,11 @@ class SqlBagOStuff extends BagOStuff {
                $result = true;
                $exptime = (int)$expiry;
                foreach ( $keysByTable as $serverIndex => $serverKeys ) {
+                       $db = null;
                        try {
                                $db = $this->getDB( $serverIndex );
                        } catch ( DBError $e ) {
-                               $this->handleWriteError( $e, $serverIndex );
+                               $this->handleWriteError( $e, $db, $serverIndex );
                                $result = false;
                                continue;
                        }
@@ -339,7 +363,7 @@ class SqlBagOStuff extends BagOStuff {
                                                __METHOD__
                                        );
                                } catch ( DBError $e ) {
-                                       $this->handleWriteError( $e, $serverIndex );
+                                       $this->handleWriteError( $e, $db, $serverIndex );
                                        $result = false;
                                }
 
@@ -353,7 +377,7 @@ class SqlBagOStuff extends BagOStuff {
        public function set( $key, $value, $exptime = 0, $flags = 0 ) {
                $ok = $this->setMulti( [ $key => $value ], $exptime );
                if ( ( $flags & self::WRITE_SYNC ) == self::WRITE_SYNC ) {
-                       $ok = $ok && $this->waitForSlaves();
+                       $ok = $this->waitForReplication() && $ok;
                }
 
                return $ok;
@@ -361,6 +385,7 @@ class SqlBagOStuff extends BagOStuff {
 
        protected function cas( $casToken, $key, $value, $exptime = 0 ) {
                list( $serverIndex, $tableName ) = $this->getTableByKey( $key );
+               $db = null;
                try {
                        $db = $this->getDB( $serverIndex );
                        $exptime = intval( $exptime );
@@ -391,7 +416,7 @@ class SqlBagOStuff extends BagOStuff {
                                __METHOD__
                        );
                } catch ( DBQueryError $e ) {
-                       $this->handleWriteError( $e, $serverIndex );
+                       $this->handleWriteError( $e, $db, $serverIndex );
 
                        return false;
                }
@@ -401,6 +426,7 @@ class SqlBagOStuff extends BagOStuff {
 
        public function delete( $key ) {
                list( $serverIndex, $tableName ) = $this->getTableByKey( $key );
+               $db = null;
                try {
                        $db = $this->getDB( $serverIndex );
                        $db->delete(
@@ -408,7 +434,7 @@ class SqlBagOStuff extends BagOStuff {
                                [ 'keyname' => $key ],
                                __METHOD__ );
                } catch ( DBError $e ) {
-                       $this->handleWriteError( $e, $serverIndex );
+                       $this->handleWriteError( $e, $db, $serverIndex );
                        return false;
                }
 
@@ -417,6 +443,7 @@ class SqlBagOStuff extends BagOStuff {
 
        public function incr( $key, $step = 1 ) {
                list( $serverIndex, $tableName ) = $this->getTableByKey( $key );
+               $db = null;
                try {
                        $db = $this->getDB( $serverIndex );
                        $step = intval( $step );
@@ -452,7 +479,7 @@ class SqlBagOStuff extends BagOStuff {
                                $newValue = null;
                        }
                } catch ( DBError $e ) {
-                       $this->handleWriteError( $e, $serverIndex );
+                       $this->handleWriteError( $e, $db, $serverIndex );
                        return null;
                }
 
@@ -462,12 +489,34 @@ class SqlBagOStuff extends BagOStuff {
        public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
                $ok = $this->mergeViaCas( $key, $callback, $exptime, $attempts );
                if ( ( $flags & self::WRITE_SYNC ) == self::WRITE_SYNC ) {
-                       $ok = $ok && $this->waitForSlaves();
+                       $ok = $this->waitForReplication() && $ok;
                }
 
                return $ok;
        }
 
+       public function changeTTL( $key, $expiry = 0 ) {
+               list( $serverIndex, $tableName ) = $this->getTableByKey( $key );
+               $db = null;
+               try {
+                       $db = $this->getDB( $serverIndex );
+                       $db->update(
+                               $tableName,
+                               [ 'exptime' => $db->timestamp( $this->convertExpiry( $expiry ) ) ],
+                               [ 'keyname' => $key, 'exptime > ' . $db->addQuotes( $db->timestamp( time() ) ) ],
+                               __METHOD__
+                       );
+                       if ( $db->affectedRows() == 0 ) {
+                               return false;
+                       }
+               } catch ( DBError $e ) {
+                       $this->handleWriteError( $e, $db, $serverIndex );
+                       return false;
+               }
+
+               return true;
+       }
+
        /**
         * @param IDatabase $db
         * @param string $exptime
@@ -490,7 +539,7 @@ class SqlBagOStuff extends BagOStuff {
        }
 
        protected function garbageCollect() {
-               if ( !$this->purgePeriod || $this->slaveOnly ) {
+               if ( !$this->purgePeriod || $this->replicaOnly ) {
                        // Disabled
                        return;
                }
@@ -518,6 +567,7 @@ class SqlBagOStuff extends BagOStuff {
         */
        public function deleteObjectsExpiringBefore( $timestamp, $progressCallback = false ) {
                for ( $serverIndex = 0; $serverIndex < $this->numServers; $serverIndex++ ) {
+                       $db = null;
                        try {
                                $db = $this->getDB( $serverIndex );
                                $dbTimestamp = $db->timestamp( $timestamp );
@@ -580,7 +630,7 @@ class SqlBagOStuff extends BagOStuff {
                                        }
                                }
                        } catch ( DBError $e ) {
-                               $this->handleWriteError( $e, $serverIndex );
+                               $this->handleWriteError( $e, $db, $serverIndex );
                                return false;
                        }
                }
@@ -594,13 +644,14 @@ class SqlBagOStuff extends BagOStuff {
         */
        public function deleteAll() {
                for ( $serverIndex = 0; $serverIndex < $this->numServers; $serverIndex++ ) {
+                       $db = null;
                        try {
                                $db = $this->getDB( $serverIndex );
                                for ( $i = 0; $i < $this->shards; $i++ ) {
                                        $db->delete( $this->getTableNameByShard( $i ), '*', __METHOD__ );
                                }
                        } catch ( DBError $e ) {
-                               $this->handleWriteError( $e, $serverIndex );
+                               $this->handleWriteError( $e, $db, $serverIndex );
                                return false;
                        }
                }
@@ -670,18 +721,19 @@ class SqlBagOStuff extends BagOStuff {
         * Handle a DBQueryError which occurred during a write operation.
         *
         * @param DBError $exception
+        * @param IDatabase|null $db DB handle or null if connection failed
         * @param int $serverIndex
+        * @throws Exception
         */
-       protected function handleWriteError( DBError $exception, $serverIndex ) {
-               if ( $exception instanceof DBConnectionError ) {
+       protected function handleWriteError( DBError $exception, IDatabase $db = null, $serverIndex ) {
+               if ( !$db ) {
                        $this->markServerDown( $exception, $serverIndex );
-               }
-               if ( $exception->db && $exception->db->wasReadOnlyError() ) {
-                       if ( $exception->db->trxLevel() ) {
-                               try {
-                                       $exception->db->rollback( __METHOD__ );
-                               } catch ( DBError $e ) {
-                               }
+               } elseif ( $db->wasReadOnlyError() ) {
+                       if ( $db->trxLevel() && $this->usesMainDB() ) {
+                               // Errors like deadlocks and connection drops already cause rollback.
+                               // For consistency, we have no choice but to throw an error and trigger
+                               // complete rollback if the main DB is also being used as the cache DB.
+                               throw $exception;
                        }
                }
 
@@ -701,7 +753,7 @@ class SqlBagOStuff extends BagOStuff {
         * @param DBError $exception
         * @param int $serverIndex
         */
-       protected function markServerDown( $exception, $serverIndex ) {
+       protected function markServerDown( DBError $exception, $serverIndex ) {
                unset( $this->conns[$serverIndex] ); // bug T103435
 
                if ( isset( $this->connFailureTimes[$serverIndex] ) ) {
@@ -738,18 +790,37 @@ class SqlBagOStuff extends BagOStuff {
                }
        }
 
-       protected function waitForSlaves() {
-               if ( !$this->serverInfos ) {
-                       // Main LB is used; wait for any slaves to catch up
-                       try {
-                               wfGetLBFactory()->waitForReplication( [ 'wiki' => wfWikiID() ] );
-                               return true;
-                       } catch ( DBReplicationWaitError $e ) {
-                               return false;
-                       }
-               } else {
+       /**
+        * @return bool Whether the main DB is used, e.g. wfGetDB( DB_MASTER )
+        */
+       protected function usesMainDB() {
+               return !$this->serverInfos;
+       }
+
+       protected function waitForReplication() {
+               if ( !$this->usesMainDB() ) {
                        // Custom DB server list; probably doesn't use replication
                        return true;
                }
+
+               $lb = $this->getSeparateMainLB()
+                       ?: MediaWikiServices::getInstance()->getDBLoadBalancer();
+
+               if ( $lb->getServerCount() <= 1 ) {
+                       return true; // no replica DBs
+               }
+
+               // Main LB is used; wait for any replica DBs to catch up
+               $masterPos = $lb->getMasterPos();
+
+               $loop = new WaitConditionLoop(
+                       function () use ( $lb, $masterPos ) {
+                               return $lb->waitForAll( $masterPos, 1 );
+                       },
+                       $this->syncTimeout,
+                       $this->busyCallbacks
+               );
+
+               return ( $loop->invoke() === $loop::CONDITION_REACHED );
        }
 }
index 2a6f88c..449c9ff 100644 (file)
@@ -149,6 +149,15 @@ class Article implements Page {
                return $article;
        }
 
+       /**
+        * Get the page this view was redirected from
+        * @return Title|null
+        * @since 1.28
+        */
+       public function getRedirectedFrom() {
+               return $this->mRedirectedFrom;
+       }
+
        /**
         * Tell the page view functions that this view was redirected
         * from another page on the wiki.
@@ -467,7 +476,7 @@ class Article implements Page {
         * page of the given title.
         */
        public function view() {
-               global $wgUseFileCache, $wgDebugToolbar, $wgMaxRedirects;
+               global $wgUseFileCache, $wgDebugToolbar;
 
                # Get variables from query string
                # As side effect this will load the revision and update the title
@@ -520,36 +529,8 @@ class Article implements Page {
 
                # Try client and file cache
                if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
-                       # Use the greatest of the page's timestamp or the timestamp of any
-                       # redirect in the chain (bug 67849)
-                       $timestamp = $this->mPage->getTouched();
-                       if ( isset( $this->mRedirectedFrom ) ) {
-                               $timestamp = max( $timestamp, $this->mRedirectedFrom->getTouched() );
-
-                               # If there can be more than one redirect in the chain, we have
-                               # to go through the whole chain too in case an intermediate
-                               # redirect was changed.
-                               if ( $wgMaxRedirects > 1 ) {
-                                       $titles = Revision::newFromTitle( $this->mRedirectedFrom )
-                                               ->getContent( Revision::FOR_THIS_USER, $user )
-                                               ->getRedirectChain();
-                                       $thisTitle = $this->getTitle();
-                                       foreach ( $titles as $title ) {
-                                               if ( Title::compare( $title, $thisTitle ) === 0 ) {
-                                                       break;
-                                               }
-                                               $timestamp = max( $timestamp, $title->getTouched() );
-                                       }
-                               }
-                       }
-
-                       # Is it client cached?
-                       if ( $outputPage->checkLastModified( $timestamp ) ) {
-                               wfDebug( __METHOD__ . ": done 304\n" );
-
-                               return;
-                       # Try file cache
-                       } elseif ( $wgUseFileCache && $this->tryFileCache() ) {
+                       # Try to stream the output from file cache
+                       if ( $wgUseFileCache && $this->tryFileCache() ) {
                                wfDebug( __METHOD__ . ": done file cache\n" );
                                # tell wgOut that output is taken care of
                                $outputPage->disable();
@@ -1087,7 +1068,7 @@ class Article implements Page {
                        return false;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $oldestRevisionTimestamp = $dbr->selectField(
                        'revision',
                        'MIN( rev_timestamp )',
@@ -1170,7 +1151,7 @@ class Article implements Page {
 
                if ( !$rc ) {
                        // Don't cache: This can be hit if the page gets accessed very fast after
-                       // its creation / latest upload or in case we have high slave lag. In case
+                       // its creation / latest upload or in case we have high replica DB lag. In case
                        // the revision is too old, we will already return above.
                        return false;
                }
@@ -1438,14 +1419,13 @@ class Article implements Page {
                        : 'revision-info';
 
                $outputPage = $context->getOutput();
-               $outputPage->addSubtitle( "<div id=\"mw-{$infomsg}\">" .
+               $revisionInfo = "<div id=\"mw-{$infomsg}\">" .
                        $context->msg( $infomsg, $td )
                                ->rawParams( $userlinks )
                                ->params( $revision->getId(), $tddate, $tdtime, $revision->getUserText() )
                                ->rawParams( Linker::revComment( $revision, true, true ) )
                                ->parse() .
-                       "</div>"
-               );
+                       "</div>";
 
                $lnk = $current
                        ? $context->msg( 'currentrevisionlink' )->escaped()
@@ -1517,10 +1497,12 @@ class Article implements Page {
                        $cdel .= ' ';
                }
 
-               $outputPage->addSubtitle( "<div id=\"mw-revision-nav\">" . $cdel .
+               // the outer div is need for styling the revision info and nav in MobileFrontend
+               $outputPage->addSubtitle( "<div class=\"mw-revision\">" . $revisionInfo .
+                       "<div id=\"mw-revision-nav\">" . $cdel .
                        $context->msg( 'revision-nav' )->rawParams(
                                $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
-                       )->escaped() . "</div>" );
+                       )->escaped() . "</div></div>" );
        }
 
        /**
@@ -1723,7 +1705,7 @@ class Article implements Page {
                        // This, as a side-effect, also makes sure that the following query isn't being run for
                        // pages with a larger history, unless the user has the 'bigdelete' right
                        // (and is about to delete this page).
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $revisions = $edits = (int)$dbr->selectField(
                                'revision',
                                'COUNT(rev_page)',
index 607357f..bb8ed24 100644 (file)
@@ -180,7 +180,6 @@ class ImageHistoryList extends ContextSource {
                                        [
                                                'action' => 'revert',
                                                'oldimage' => $img,
-                                               'wpEditToken' => $user->getEditToken( $img )
                                        ]
                                );
                        }
index 106911c..be5535a 100644 (file)
@@ -222,11 +222,12 @@ class ImagePage extends Article {
                                $out->addStyle( $css );
                        }
                }
-               // always show the local local Filepage.css, bug 29277
-               $out->addModuleStyles( 'filepage' );
 
-               // Add MediaWiki styles for a file page
-               $out->addModuleStyles( 'mediawiki.action.view.filepage' );
+               $out->addModuleStyles( [
+                       'filepage', // always show the local local Filepage.css, bug 29277
+                       'mediawiki.action.view.filepage', // Add MediaWiki styles for a file page
+               ] );
+
        }
 
        /**
@@ -809,7 +810,7 @@ EOT
         * @return ResultWrapper
         */
        protected function queryImageLinks( $target, $limit ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                return $dbr->select(
                        [ 'imagelinks', 'page' ],
index b06b519..938f292 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 use \MediaWiki\Logger\LoggerFactory;
+use \MediaWiki\MediaWikiServices;
 
 /**
  * Class representing a MediaWiki article and history.
@@ -90,6 +91,14 @@ class WikiPage implements Page, IDBAccessObject {
                $this->mTitle = $title;
        }
 
+       /**
+        * Makes sure that the mTitle object is cloned
+        * to the newly cloned WikiPage.
+        */
+       public function __clone() {
+               $this->mTitle = clone $this->mTitle;
+       }
+
        /**
         * Create a WikiPage object of the appropriate class for the given title.
         *
@@ -126,7 +135,7 @@ class WikiPage implements Page, IDBAccessObject {
         *
         * @param int $id Article ID to load
         * @param string|int $from One of the following values:
-        *        - "fromdb" or WikiPage::READ_NORMAL to select from a slave database
+        *        - "fromdb" or WikiPage::READ_NORMAL to select from a replica DB
         *        - "fromdbmaster" or WikiPage::READ_LATEST to select from the master database
         *
         * @return WikiPage|null
@@ -138,7 +147,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                $from = self::convertSelectType( $from );
-               $db = wfGetDB( $from === self::READ_LATEST ? DB_MASTER : DB_SLAVE );
+               $db = wfGetDB( $from === self::READ_LATEST ? DB_MASTER : DB_REPLICA );
                $row = $db->selectRow(
                        'page', self::selectFields(), [ 'page_id' => $id ], __METHOD__ );
                if ( !$row ) {
@@ -153,7 +162,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @since 1.20
         * @param object $row Database row containing at least fields returned by selectFields().
         * @param string|int $from Source of $data:
-        *        - "fromdb" or WikiPage::READ_NORMAL: from a slave DB
+        *        - "fromdb" or WikiPage::READ_NORMAL: from a replica DB
         *        - "fromdbmaster" or WikiPage::READ_LATEST: from the master DB
         *        - "forupdate" or WikiPage::READ_LOCKING: from the master DB using SELECT FOR UPDATE
         * @return WikiPage
@@ -338,7 +347,7 @@ class WikiPage implements Page, IDBAccessObject {
         *
         * @param object|string|int $from One of the following:
         *   - A DB query result object.
-        *   - "fromdb" or WikiPage::READ_NORMAL to get from a slave DB.
+        *   - "fromdb" or WikiPage::READ_NORMAL to get from a replica DB.
         *   - "fromdbmaster" or WikiPage::READ_LATEST to get from the master DB.
         *   - "forupdate"  or WikiPage::READ_LOCKING to get from the master DB
         *     using SELECT FOR UPDATE.
@@ -357,7 +366,7 @@ class WikiPage implements Page, IDBAccessObject {
                        $data = $this->pageDataFromTitle( wfGetDB( $index ), $this->mTitle, $opts );
 
                        if ( !$data
-                               && $index == DB_SLAVE
+                               && $index == DB_REPLICA
                                && wfGetLB()->getServerCount() > 1
                                && wfGetLB()->hasOrMadeRecentMasterChanges()
                        ) {
@@ -366,7 +375,7 @@ class WikiPage implements Page, IDBAccessObject {
                                $data = $this->pageDataFromTitle( wfGetDB( $index ), $this->mTitle, $opts );
                        }
                } else {
-                       // No idea from where the caller got this data, assume slave database.
+                       // No idea from where the caller got this data, assume replica DB.
                        $data = $from;
                        $from = self::READ_NORMAL;
                }
@@ -380,7 +389,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @since 1.20
         * @param object|bool $data DB row containing fields returned by selectFields() or false
         * @param string|int $from One of the following:
-        *        - "fromdb" or WikiPage::READ_NORMAL if the data comes from a slave DB
+        *        - "fromdb" or WikiPage::READ_NORMAL if the data comes from a replica DB
         *        - "fromdbmaster" or WikiPage::READ_LATEST if the data comes from the master DB
         *        - "forupdate"  or WikiPage::READ_LOCKING if the data comes from
         *          the master DB using SELECT FOR UPDATE
@@ -496,13 +505,13 @@ class WikiPage implements Page, IDBAccessObject {
 
        /**
         * Loads page_touched and returns a value indicating if it should be used
-        * @return bool True if not a redirect
+        * @return bool True if this page exists and is not a redirect
         */
        public function checkTouched() {
                if ( !$this->mDataLoaded ) {
                        $this->loadPageData();
                }
-               return !$this->mIsRedirect;
+               return ( $this->mId && !$this->mIsRedirect );
        }
 
        /**
@@ -544,9 +553,9 @@ class WikiPage implements Page, IDBAccessObject {
         */
        public function getOldestRevision() {
 
-               // Try using the slave database first, then try the master
+               // Try using the replica DB first, then try the master
                $continue = 2;
-               $db = wfGetDB( DB_SLAVE );
+               $db = wfGetDB( DB_REPLICA );
                $revSelectFields = Revision::selectFields();
 
                $row = null;
@@ -601,7 +610,7 @@ class WikiPage implements Page, IDBAccessObject {
                        $flags = Revision::READ_LOCKING;
                } elseif ( $this->mDataLoadedFrom == self::READ_LATEST ) {
                        // Bug T93976: if page_latest was loaded from the master, fetch the
-                       // revision from there as well, as it may not exist yet on a slave DB.
+                       // revision from there as well, as it may not exist yet on a replica DB.
                        // Also, this keeps the queries in the same REPEATABLE-READ snapshot.
                        $flags = Revision::READ_LATEST;
                } else {
@@ -823,7 +832,7 @@ class WikiPage implements Page, IDBAccessObject {
                                // links.
                                $hasLinks = (bool)count( $editInfo->output->getLinks() );
                        } else {
-                               $hasLinks = (bool)wfGetDB( DB_SLAVE )->selectField( 'pagelinks', 1,
+                               $hasLinks = (bool)wfGetDB( DB_REPLICA )->selectField( 'pagelinks', 1,
                                        [ 'pl_from' => $this->getId() ], __METHOD__ );
                        }
                }
@@ -848,7 +857,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                // Query the redirect table
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $row = $dbr->selectRow( 'redirect',
                        [ 'rd_namespace', 'rd_title', 'rd_fragment', 'rd_interwiki' ],
                        [ 'rd_from' => $this->getId() ],
@@ -887,9 +896,13 @@ class WikiPage implements Page, IDBAccessObject {
                // Update the DB post-send if the page has not cached since now
                $that = $this;
                $latest = $this->getLatest();
-               DeferredUpdates::addCallableUpdate( function() use ( $that, $retval, $latest ) {
-                       $that->insertRedirectEntry( $retval, $latest );
-               } );
+               DeferredUpdates::addCallableUpdate(
+                       function () use ( $that, $retval, $latest ) {
+                               $that->insertRedirectEntry( $retval, $latest );
+                       },
+                       DeferredUpdates::POSTSEND,
+                       wfGetDB( DB_MASTER )
+               );
 
                return $retval;
        }
@@ -977,7 +990,7 @@ class WikiPage implements Page, IDBAccessObject {
        public function getContributors() {
                // @todo FIXME: This is expensive; cache this info somewhere.
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                if ( $dbr->implicitGroupby() ) {
                        $realNameField = 'user_real_name';
@@ -1043,14 +1056,16 @@ class WikiPage implements Page, IDBAccessObject {
         *
         * @since 1.19
         * @param ParserOptions $parserOptions ParserOptions to use for the parse operation
-        * @param null|int $oldid Revision ID to get the text from, passing null or 0 will
-        *   get the current revision (default value)
-        *
-        * @return ParserOutput|bool ParserOutput or false if the revision was not found
+        * @param null|int      $oldid Revision ID to get the text from, passing null or 0 will
+        *                             get the current revision (default value)
+        * @param bool          $forceParse Force reindexing, regardless of cache settings
+        * @return bool|ParserOutput ParserOutput or false if the revision was not found
         */
-       public function getParserOutput( ParserOptions $parserOptions, $oldid = null ) {
-
-               $useParserCache = $this->shouldCheckParserCache( $parserOptions, $oldid );
+       public function getParserOutput(
+               ParserOptions $parserOptions, $oldid = null, $forceParse = false
+       ) {
+               $useParserCache =
+                       ( !$forceParse ) && $this->shouldCheckParserCache( $parserOptions, $oldid );
                wfDebug( __METHOD__ .
                        ': using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
                if ( $parserOptions->getStubThreshold() ) {
@@ -1103,15 +1118,13 @@ class WikiPage implements Page, IDBAccessObject {
                        return false;
                }
 
-               $title = $this->mTitle;
-               wfGetDB( DB_MASTER )->onTransactionIdle( function() use ( $title ) {
-                       // Invalidate the cache in auto-commit mode
-                       $title->invalidateCache();
-               } );
+               $this->mTitle->invalidateCache();
 
+               // Clear file cache
+               HTMLFileCache::clearFileCache( $this->getTitle() );
                // Send purge after above page_touched update was committed
                DeferredUpdates::addUpdate(
-                       new CdnCacheUpdate( $title->getCdnUrls() ),
+                       new CdnCacheUpdate( $this->mTitle->getCdnUrls() ),
                        DeferredUpdates::PRESEND
                );
 
@@ -1147,7 +1160,9 @@ class WikiPage implements Page, IDBAccessObject {
         * @param IDatabase $dbw
         * @param int|null $pageId Custom page ID that will be used for the insert statement
         *
-        * @return bool|int The newly created page_id key; false if the title already existed
+        * @return bool|int The newly created page_id key; false if the row was not
+        *   inserted, e.g. because the title already existed or because the specified
+        *   page ID is already in use.
         */
        public function insertOn( $dbw, $pageId = null ) {
                $pageIdForInsert = $pageId ?: $dbw->nextSequenceValue( 'page_page_id_seq' );
@@ -1375,7 +1390,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                $baseRevId = null;
                if ( $edittime && $sectionId !== 'new' ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $rev = Revision::loadFromTimestamp( $dbr, $this->mTitle, $edittime );
                        // Try the master if this thread may have just added it.
                        // This could be abstracted into a Revision method, but we don't want
@@ -1759,7 +1774,6 @@ class WikiPage implements Page, IDBAccessObject {
                        $revisionId = $revision->insertOn( $dbw );
                        // Update page_latest and friends to reflect the new revision
                        if ( !$this->updateRevisionOn( $dbw, $revision, null, $meta['oldIsRedirect'] ) ) {
-                               $dbw->rollback( __METHOD__ ); // sanity; this should never happen
                                throw new MWException( "Failed to update page row to use new revision." );
                        }
 
@@ -1815,30 +1829,32 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                // Do secondary updates once the main changes have been committed...
-               $that = $this;
-               $dbw->onTransactionIdle(
-                       function () use (
-                               $dbw, &$that, $revision, &$user, $content, $summary, &$flags,
-                               $changed, $meta, &$status
-                       ) {
-                               // Do per-page updates in a transaction
-                               $dbw->setFlag( DBO_TRX );
-                               // Update links tables, site stats, etc.
-                               $that->doEditUpdates(
-                                       $revision,
-                                       $user,
-                                       [
-                                               'changed' => $changed,
-                                               'oldcountable' => $meta['oldCountable'],
-                                               'oldrevision' => $meta['oldRevision']
-                                       ]
-                               );
-                               // Trigger post-save hook
-                               $params = [ &$that, &$user, $content, $summary, $flags & EDIT_MINOR,
-                                       null, null, &$flags, $revision, &$status, $meta['baseRevId'] ];
-                               ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $params );
-                               Hooks::run( 'PageContentSaveComplete', $params );
-                       }
+               DeferredUpdates::addUpdate(
+                       new AtomicSectionUpdate(
+                               $dbw,
+                               __METHOD__,
+                               function () use (
+                                       $revision, &$user, $content, $summary, &$flags,
+                                       $changed, $meta, &$status
+                               ) {
+                                       // Update links tables, site stats, etc.
+                                       $this->doEditUpdates(
+                                               $revision,
+                                               $user,
+                                               [
+                                                       'changed' => $changed,
+                                                       'oldcountable' => $meta['oldCountable'],
+                                                       'oldrevision' => $meta['oldRevision']
+                                               ]
+                                       );
+                                       // Trigger post-save hook
+                                       $params = [ &$this, &$user, $content, $summary, $flags & EDIT_MINOR,
+                                               null, null, &$flags, $revision, &$status, $meta['baseRevId'] ];
+                                       ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $params );
+                                       Hooks::run( 'PageContentSaveComplete', $params );
+                               }
+                       ),
+                       DeferredUpdates::PRESEND
                );
 
                return $status;
@@ -1907,7 +1923,6 @@ class WikiPage implements Page, IDBAccessObject {
                $revisionId = $revision->insertOn( $dbw );
                // Update the page record with revision data
                if ( !$this->updateRevisionOn( $dbw, $revision, 0 ) ) {
-                       $dbw->rollback( __METHOD__ ); // sanity; this should never happen
                        throw new MWException( "Failed to update page row to use new revision." );
                }
 
@@ -1943,26 +1958,28 @@ class WikiPage implements Page, IDBAccessObject {
                $status->value['revision'] = $revision;
 
                // Do secondary updates once the main changes have been committed...
-               $that = $this;
-               $dbw->onTransactionIdle(
-                       function () use (
-                               &$that, $dbw, $revision, &$user, $content, $summary, &$flags, $meta, &$status
-                       ) {
-                               // Do per-page updates in a transaction
-                               $dbw->setFlag( DBO_TRX );
-                               // Update links, etc.
-                               $that->doEditUpdates( $revision, $user, [ 'created' => true ] );
-                               // Trigger post-create hook
-                               $params = [ &$that, &$user, $content, $summary,
-                                       $flags & EDIT_MINOR, null, null, &$flags, $revision ];
-                               ContentHandler::runLegacyHooks( 'ArticleInsertComplete', $params );
-                               Hooks::run( 'PageContentInsertComplete', $params );
-                               // Trigger post-save hook
-                               $params = array_merge( $params, [ &$status, $meta['baseRevId'] ] );
-                               ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $params );
-                               Hooks::run( 'PageContentSaveComplete', $params );
+               DeferredUpdates::addUpdate(
+                       new AtomicSectionUpdate(
+                               $dbw,
+                               __METHOD__,
+                               function () use (
+                                       $revision, &$user, $content, $summary, &$flags, $meta, &$status
+                               ) {
+                                       // Update links, etc.
+                                       $this->doEditUpdates( $revision, $user, [ 'created' => true ] );
+                                       // Trigger post-create hook
+                                       $params = [ &$this, &$user, $content, $summary,
+                                               $flags & EDIT_MINOR, null, null, &$flags, $revision ];
+                                       ContentHandler::runLegacyHooks( 'ArticleInsertComplete', $params );
+                                       Hooks::run( 'PageContentInsertComplete', $params );
+                                       // Trigger post-save hook
+                                       $params = array_merge( $params, [ &$status, $meta['baseRevId'] ] );
+                                       ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $params );
+                                       Hooks::run( 'PageContentSaveComplete', $params );
 
-                       }
+                               }
+                       ),
+                       DeferredUpdates::PRESEND
                );
 
                return $status;
@@ -2053,7 +2070,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                if ( $this->mPreparedEdit
-                       && $this->mPreparedEdit->newContent
+                       && isset( $this->mPreparedEdit->newContent )
                        && $this->mPreparedEdit->newContent->equals( $content )
                        && $this->mPreparedEdit->revid == $revid
                        && $this->mPreparedEdit->format == $serialFormat
@@ -2097,7 +2114,7 @@ class WikiPage implements Page, IDBAccessObject {
                                // We get here if vary-revision is set. This means that this page references
                                // itself (such as via self-transclusion). In this case, we need to make sure
                                // that any such self-references refer to the newly-saved revision, and not
-                               // to the previous one, which could otherwise happen due to slave lag.
+                               // to the previous one, which could otherwise happen due to replica DB lag.
                                $oldCallback = $edit->popts->getCurrentRevisionCallback();
                                $edit->popts->setCurrentRevisionCallback(
                                        function ( Title $title, $parser = false ) use ( $revision, &$oldCallback ) {
@@ -2700,14 +2717,14 @@ class WikiPage implements Page, IDBAccessObject {
                $protectDescription = '';
 
                foreach ( array_filter( $limit ) as $action => $restrictions ) {
-                       # $action is one of $wgRestrictionTypes = array( 'create', 'edit', 'move', 'upload' ).
+                       # $action is one of $wgRestrictionTypes = [ 'create', 'edit', 'move', 'upload' ].
                        # All possible message keys are listed here for easier grepping:
                        # * restriction-create
                        # * restriction-edit
                        # * restriction-move
                        # * restriction-upload
                        $actionText = wfMessage( 'restriction-' . $action )->inContentLanguage()->text();
-                       # $restrictions is one of $wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' ),
+                       # $restrictions is one of $wgRestrictionLevels = [ '', 'autoconfirmed', 'sysop' ],
                        # with '' filtered out. All possible message keys are listed below:
                        # * protect-level-autoconfirmed
                        # * protect-level-sysop
@@ -2860,13 +2877,24 @@ class WikiPage implements Page, IDBAccessObject {
                        return $status;
                }
 
+               // Given the lock above, we can be confident in the title and page ID values
+               $namespace = $this->getTitle()->getNamespace();
+               $dbKey = $this->getTitle()->getDBkey();
+
                // At this point we are now comitted to returning an OK
                // status unless some DB query error or other exception comes up.
                // This way callers don't have to call rollback() if $status is bad
                // unless they actually try to catch exceptions (which is rare).
 
                // we need to remember the old content so we can use it to generate all deletion updates.
-               $content = $this->getContent( Revision::RAW );
+               try {
+                       $content = $this->getContent( Revision::RAW );
+               } catch ( Exception $ex ) {
+                       wfLogWarning( __METHOD__ . ': failed to load content during deletion! '
+                               . $ex->getMessage() );
+
+                       $content = null;
+               }
 
                // Bitfields to further suppress the content
                if ( $suppress ) {
@@ -2876,58 +2904,63 @@ class WikiPage implements Page, IDBAccessObject {
                        $bitfield |= Revision::DELETED_COMMENT;
                        $bitfield |= Revision::DELETED_USER;
                        $bitfield |= Revision::DELETED_RESTRICTED;
+                       $deletionFields = [ $dbw->addQuotes( $bitfield ) . ' AS deleted' ];
                } else {
-                       $bitfield = 'rev_deleted';
-               }
-
-               /**
-                * For now, shunt the revision data into the archive table.
-                * Text is *not* removed from the text table; bulk storage
-                * is left intact to avoid breaking block-compression or
-                * immutable storage schemes.
-                *
-                * For backwards compatibility, note that some older archive
-                * table entries will have ar_text and ar_flags fields still.
-                *
-                * In the future, we may keep revisions and mark them with
-                * the rev_deleted field, which is reserved for this purpose.
-                */
-
-               $row = [
-                       'ar_namespace'  => 'page_namespace',
-                       'ar_title'      => 'page_title',
-                       'ar_comment'    => 'rev_comment',
-                       'ar_user'       => 'rev_user',
-                       'ar_user_text'  => 'rev_user_text',
-                       'ar_timestamp'  => 'rev_timestamp',
-                       'ar_minor_edit' => 'rev_minor_edit',
-                       'ar_rev_id'     => 'rev_id',
-                       'ar_parent_id'  => 'rev_parent_id',
-                       'ar_text_id'    => 'rev_text_id',
-                       'ar_text'       => '\'\'', // Be explicit to appease
-                       'ar_flags'      => '\'\'', // MySQL's "strict mode"...
-                       'ar_len'        => 'rev_len',
-                       'ar_page_id'    => 'page_id',
-                       'ar_deleted'    => $bitfield,
-                       'ar_sha1'       => 'rev_sha1',
-               ];
-
-               if ( $wgContentHandlerUseDB ) {
-                       $row['ar_content_model'] = 'rev_content_model';
-                       $row['ar_content_format'] = 'rev_content_format';
+                       $deletionFields = [ 'rev_deleted AS deleted' ];
+               }
+
+               // For now, shunt the revision data into the archive table.
+               // Text is *not* removed from the text table; bulk storage
+               // is left intact to avoid breaking block-compression or
+               // immutable storage schemes.
+               // In the future, we may keep revisions and mark them with
+               // the rev_deleted field, which is reserved for this purpose.
+
+               // Get all of the page revisions
+               $fields = array_diff( Revision::selectFields(), [ 'rev_deleted' ] );
+               $res = $dbw->select(
+                       'revision',
+                       array_merge( $fields, $deletionFields ),
+                       [ 'rev_page' => $id ],
+                       __METHOD__,
+                       'FOR UPDATE'
+               );
+               // Build their equivalent archive rows
+               $rowsInsert = [];
+               foreach ( $res as $row ) {
+                       $rowInsert = [
+                               'ar_namespace'  => $namespace,
+                               'ar_title'      => $dbKey,
+                               'ar_comment'    => $row->rev_comment,
+                               'ar_user'       => $row->rev_user,
+                               'ar_user_text'  => $row->rev_user_text,
+                               'ar_timestamp'  => $row->rev_timestamp,
+                               'ar_minor_edit' => $row->rev_minor_edit,
+                               'ar_rev_id'     => $row->rev_id,
+                               'ar_parent_id'  => $row->rev_parent_id,
+                               'ar_text_id'    => $row->rev_text_id,
+                               'ar_text'       => '',
+                               'ar_flags'      => '',
+                               'ar_len'        => $row->rev_len,
+                               'ar_page_id'    => $id,
+                               'ar_deleted'    => $row->deleted,
+                               'ar_sha1'       => $row->rev_sha1,
+                       ];
+                       if ( $wgContentHandlerUseDB ) {
+                               $rowInsert['ar_content_model'] = $row->rev_content_model;
+                               $rowInsert['ar_content_format'] = $row->rev_content_format;
+                       }
+                       $rowsInsert[] = $rowInsert;
                }
+               // Copy them into the archive table
+               $dbw->insert( 'archive', $rowsInsert, __METHOD__ );
+               // Save this so we can pass it to the ArticleDeleteComplete hook.
+               $archivedRevisionCount = $dbw->affectedRows();
 
-               // Copy all the page revisions into the archive table
-               $dbw->insertSelect(
-                       'archive',
-                       [ 'page', 'revision' ],
-                       $row,
-                       [
-                               'page_id' => $id,
-                               'page_id = rev_page'
-                       ],
-                       __METHOD__
-               );
+               // Clone the title and wikiPage, so we have the information we need when
+               // we log and run the ArticleDeleteComplete hook.
+               $logTitle = clone $this->mTitle;
+               $wikiPageBeforeDelete = clone $this;
 
                // Now that it's safely backed up, delete it
                $dbw->delete( 'page', [ 'page_id' => $id ], __METHOD__ );
@@ -2936,9 +2969,6 @@ class WikiPage implements Page, IDBAccessObject {
                        $dbw->delete( 'revision', [ 'rev_page' => $id ], __METHOD__ );
                }
 
-               // Clone the title, so we have the information we need when we log
-               $logTitle = clone $this->mTitle;
-
                // Log the deletion, if the page was suppressed, put it in the suppression log instead
                $logtype = $suppress ? 'suppress' : 'delete';
 
@@ -2957,8 +2987,15 @@ class WikiPage implements Page, IDBAccessObject {
 
                $this->doDeleteUpdates( $id, $content );
 
-               Hooks::run( 'ArticleDeleteComplete',
-                       [ &$this, &$user, $reason, $id, $content, $logEntry ] );
+               Hooks::run( 'ArticleDeleteComplete', [
+                       &$wikiPageBeforeDelete,
+                       &$user,
+                       $reason,
+                       $id,
+                       $content,
+                       $logEntry,
+                       $archivedRevisionCount
+               ] );
                $status->value = $logid;
 
                // Show log excerpt on 404 pages rather than just a link
@@ -3000,8 +3037,16 @@ class WikiPage implements Page, IDBAccessObject {
         *   may already return null when the page proper was deleted.
         */
        public function doDeleteUpdates( $id, Content $content = null ) {
+               try {
+                       $countable = $this->isCountable();
+               } catch ( Exception $ex ) {
+                       // fallback for deleting broken pages for which we cannot load the content for
+                       // some reason. Note that doDeleteArticleReal() already logged this problem.
+                       $countable = false;
+               }
+
                // Update site status
-               DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, - (int)$this->isCountable(), -1 ) );
+               DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, - (int)$countable, -1 ) );
 
                // Delete pagelinks, update secondary indexes, etc
                $updates = $this->getDeletionUpdates( $content );
@@ -3193,9 +3238,12 @@ class WikiPage implements Page, IDBAccessObject {
                        $flags |= EDIT_FORCE_BOT;
                }
 
+               $targetContent = $target->getContent();
+               $changingContentModel = $targetContent->getModel() !== $current->getContentModel();
+
                // Actually store the edit
                $status = $this->doEditContent(
-                       $target->getContent(),
+                       $targetContent,
                        $summary,
                        $flags,
                        $target->getId(),
@@ -3245,6 +3293,22 @@ class WikiPage implements Page, IDBAccessObject {
                        ] ];
                }
 
+               if ( $changingContentModel ) {
+                       // If the content model changed during the rollback,
+                       // make sure it gets logged to Special:Log/contentmodel
+                       $log = new ManualLogEntry( 'contentmodel', 'change' );
+                       $log->setPerformer( $guser );
+                       $log->setTarget( $this->mTitle );
+                       $log->setComment( $summary );
+                       $log->setParameters( [
+                               '4::oldmodel' => $current->getContentModel(),
+                               '5::newmodel' => $targetContent->getModel(),
+                       ] );
+
+                       $logId = $log->insert( $dbw );
+                       $log->publish( $logId );
+               }
+
                $revId = $statusRev->getId();
 
                Hooks::run( 'ArticleRollbackComplete', [ $this, $guser, $target, $current ] );
@@ -3279,6 +3343,16 @@ class WikiPage implements Page, IDBAccessObject {
                $title->touchLinks();
                $title->purgeSquid();
                $title->deleteTitleProtection();
+
+               MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $title );
+
+               if ( $title->getNamespace() == NS_CATEGORY ) {
+                       // Load the Category object, which will schedule a job to create
+                       // the category table row if necessary. Checking a replica DB is ok
+                       // here, in the worst case it'll run an unnecessary recount job on
+                       // a category that probably doesn't have many members.
+                       Category::newFromTitle( $title )->getID();
+               }
        }
 
        /**
@@ -3297,6 +3371,8 @@ class WikiPage implements Page, IDBAccessObject {
                $title->touchLinks();
                $title->purgeSquid();
 
+               MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $title );
+
                // File cache
                HTMLFileCache::clearFileCache( $title );
                InfoAction::invalidateCache( $title );
@@ -3340,6 +3416,8 @@ class WikiPage implements Page, IDBAccessObject {
                // Invalidate the caches of all pages which redirect here
                DeferredUpdates::addUpdate( new HTMLCacheUpdate( $title, 'redirect' ) );
 
+               MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle( $title );
+
                // Purge CDN for this page only
                $title->purgeSquid();
                // Clear file cache for this page only
@@ -3365,7 +3443,7 @@ class WikiPage implements Page, IDBAccessObject {
                        return TitleArray::newFromResult( new FakeResultWrapper( [] ) );
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select( 'categorylinks',
                        [ 'cl_to AS page_title, ' . NS_CATEGORY . ' AS page_namespace' ],
                        // Have to do that since DatabaseBase::fieldNamesWithAlias treats numeric indexes
@@ -3390,7 +3468,7 @@ class WikiPage implements Page, IDBAccessObject {
                        return [];
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select( [ 'categorylinks', 'page_props', 'page' ],
                        [ 'cl_to' ],
                        [ 'cl_from' => $id, 'pp_page=page_id', 'pp_propname' => 'hiddencat',
@@ -3525,6 +3603,22 @@ class WikiPage implements Page, IDBAccessObject {
                                        $cat = Category::newFromName( $catName );
                                        Hooks::run( 'CategoryAfterPageRemoved', [ $cat, $this, $id ] );
                                }
+
+                               // Refresh counts on categories that should be empty now, to
+                               // trigger possible deletion. Check master for the most
+                               // up-to-date cat_pages.
+                               if ( count( $deleted ) ) {
+                                       $rows = $dbw->select(
+                                               'category',
+                                               [ 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ],
+                                               [ 'cat_title' => $deleted, 'cat_pages <= 0' ],
+                                               $method
+                                       );
+                                       foreach ( $rows as $row ) {
+                                               $cat = Category::newFromRow( $row );
+                                               $cat->refreshCounts();
+                                       }
+                               }
                        }
                );
        }
@@ -3593,7 +3687,14 @@ class WikiPage implements Page, IDBAccessObject {
                if ( !$content ) {
                        // load content object, which may be used to determine the necessary updates.
                        // XXX: the content may not be needed to determine the updates.
-                       $content = $this->getContent( Revision::RAW );
+                       try {
+                               $content = $this->getContent( Revision::RAW );
+                       } catch ( Exception $ex ) {
+                               // If we can't load the content, something is wrong. Perhaps that's why
+                               // the user is trying to delete the page, so let's not fail in that case.
+                               // Note that doDeleteArticleReal() will already have logged an issue with
+                               // loading the content.
+                       }
                }
 
                if ( !$content ) {
index 393644f..395cee5 100644 (file)
@@ -145,8 +145,8 @@ abstract class IndexPager extends ContextSource implements Pager {
                }
 
                $this->mIsBackwards = ( $this->mRequest->getVal( 'dir' ) == 'prev' );
-               # Let the subclass set the DB here; otherwise use a slave DB for the current wiki
-               $this->mDb = $this->mDb ?: wfGetDB( DB_SLAVE );
+               # Let the subclass set the DB here; otherwise use a replica DB for the current wiki
+               $this->mDb = $this->mDb ?: wfGetDB( DB_REPLICA );
 
                $index = $this->getIndexField(); // column to sort on
                $extraSort = $this->getExtraSortFields(); // extra columns to sort on for query planning
@@ -700,8 +700,8 @@ abstract class IndexPager extends ContextSource implements Pager {
         * not be used in the pager offset or in any links for users.
         *
         * If getIndexField() returns an array of 'querykey' => 'indexfield' pairs then
-        * this must return a corresponding array of 'querykey' => array( fields...) pairs
-        * in order for a request with &count=querykey to use array( fields...) to sort.
+        * this must return a corresponding array of 'querykey' => [ fields... ] pairs
+        * in order for a request with &count=querykey to use [ fields... ] to sort.
         *
         * This is useful for pagers that GROUP BY a unique column (say page_id)
         * and ORDER BY another (say page_len). Using GROUP BY and ORDER BY both on
index 1c6f404..b32f43b 100644 (file)
@@ -288,9 +288,8 @@ class LinkHolderArray {
                $linkCache = LinkCache::singleton();
                $output = $this->parent->getOutput();
                $linkRenderer = $this->parent->getLinkRenderer();
-               $threshold = $linkRenderer->getStubThreshold();
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                # Sort by namespace
                ksort( $this->internals );
@@ -321,7 +320,7 @@ class LinkHolderArray {
                                } else {
                                        $id = $linkCache->getGoodLinkID( $pdbk );
                                        if ( $id != 0 ) {
-                                               $colours[$pdbk] = Linker::getLinkColour( $title, $threshold );
+                                               $colours[$pdbk] = $linkRenderer->getLinkClasses( $title );
                                                $output->addLink( $title, $id );
                                                $linkcolour_ids[$id] = $pdbk;
                                        } elseif ( $linkCache->isBadLink( $pdbk ) ) {
@@ -353,7 +352,7 @@ class LinkHolderArray {
                                $pdbk = $title->getPrefixedDBkey();
                                $linkCache->addGoodLinkObjFromRow( $title, $s );
                                $output->addLink( $title, $s->page_id );
-                               $colours[$pdbk] = Linker::getLinkColour( $title, $threshold );
+                               $colours[$pdbk] = $linkRenderer->getLinkClasses( $title );
                                // add id to the extension todolist
                                $linkcolour_ids[$s->page_id] = $pdbk;
                        }
@@ -456,7 +455,6 @@ class LinkHolderArray {
                $variantMap = []; // maps $pdbkey_Variant => $keys (of link holders)
                $output = $this->parent->getOutput();
                $linkCache = LinkCache::singleton();
-               $threshold = $this->parent->getOptions()->getStubThreshold();
                $titlesToBeConverted = '';
                $titlesAttrs = [];
 
@@ -536,7 +534,7 @@ class LinkHolderArray {
 
                if ( !$linkBatch->isEmpty() ) {
                        // construct query
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $fields = array_merge(
                                LinkCache::getSelectFields(),
                                [ 'page_namespace', 'page_title' ]
@@ -549,6 +547,7 @@ class LinkHolderArray {
                        );
 
                        $linkcolour_ids = [];
+                       $linkRenderer = $this->parent->getLinkRenderer();
 
                        // for each found variants, figure out link holders and replace
                        foreach ( $varRes as $s ) {
@@ -575,7 +574,7 @@ class LinkHolderArray {
                                                $entry['pdbk'] = $varPdbk;
 
                                                // set pdbk and colour
-                                               $colours[$varPdbk] = Linker::getLinkColour( $variantTitle, $threshold );
+                                               $colours[$varPdbk] = $linkRenderer->getLinkClasses( $variantTitle );
                                                $linkcolour_ids[$s->page_id] = $pdbk;
                                        }
                                }
index bdf3efb..5e54615 100644 (file)
@@ -41,6 +41,7 @@ class MWTidy {
         * @param string $text HTML input fragment. This should not contain a
         *                     <body> or <html> tag.
         * @return string Corrected HTML output
+        * @throws MWException
         */
        public static function tidy( $text ) {
                $driver = self::singleton();
@@ -51,30 +52,13 @@ class MWTidy {
                return $driver->tidy( $text );
        }
 
-       /**
-        * Get CSS modules needed if HTML from the current driver is to be displayed.
-        *
-        * This is just a migration tool to allow some changes expected as part of
-        * Tidy replacement (T89331) to be exposed on the client side via user
-        * scripts, without actually replacing tidy. See T49673.
-        *
-        * @return array
-        */
-       public static function getModuleStyles() {
-               $driver = self::singleton();
-               if ( $driver && $driver instanceof MediaWiki\Tidy\RaggettBase ) {
-                       return [ 'mediawiki.raggett' ];
-               } else {
-                       return [];
-               }
-       }
-
        /**
         * Check HTML for errors, used if $wgValidateAllHtml = true.
         *
         * @param string $text
         * @param string &$errorStr Return the error string
         * @return bool Whether the HTML is valid
+        * @throws MWException
         */
        public static function checkErrors( $text, &$errorStr = null ) {
                $driver = self::singleton();
@@ -89,10 +73,16 @@ class MWTidy {
                }
        }
 
+       /**
+        * @return bool
+        */
        public static function isEnabled() {
                return self::singleton() !== false;
        }
 
+       /**
+        * @return bool|\MediaWiki\Tidy\TidyDriverBase
+        */
        protected static function singleton() {
                global $wgUseTidy, $wgTidyInternal, $wgTidyConf, $wgDebugTidy, $wgTidyConfig,
                        $wgTidyBin, $wgTidyOpts;
@@ -119,29 +109,46 @@ class MWTidy {
                        } else {
                                return false;
                        }
-                       switch ( $config['driver'] ) {
-                               case 'RaggettInternalHHVM':
-                                       self::$instance = new MediaWiki\Tidy\RaggettInternalHHVM( $config );
-                                       break;
-                               case 'RaggettInternalPHP':
-                                       self::$instance = new MediaWiki\Tidy\RaggettInternalPHP( $config );
-                                       break;
-                               case 'RaggettExternal':
-                                       self::$instance = new MediaWiki\Tidy\RaggettExternal( $config );
-                                       break;
-                               case 'Html5Depurate':
-                                       self::$instance = new MediaWiki\Tidy\Html5Depurate( $config );
-                                       break;
-                               default:
-                                       throw new MWException( "Invalid tidy driver: \"{$config['driver']}\"" );
-                       }
+                       self::$instance = self::factory( $config );
                }
                return self::$instance;
        }
 
+       /**
+        * Create a new Tidy driver object from configuration.
+        * @see $wgTidyConfig
+        * @param array $config
+        * @return bool|\MediaWiki\Tidy\TidyDriverBase
+        * @throws MWException
+        */
+       public static function factory( array $config ) {
+               switch ( $config['driver'] ) {
+                       case 'RaggettInternalHHVM':
+                               $instance = new MediaWiki\Tidy\RaggettInternalHHVM( $config );
+                               break;
+                       case 'RaggettInternalPHP':
+                               $instance = new MediaWiki\Tidy\RaggettInternalPHP( $config );
+                               break;
+                       case 'RaggettExternal':
+                               $instance = new MediaWiki\Tidy\RaggettExternal( $config );
+                               break;
+                       case 'Html5Depurate':
+                               $instance = new MediaWiki\Tidy\Html5Depurate( $config );
+                               break;
+                       case 'Html5Internal':
+                               $instance = new MediaWiki\Tidy\Html5Internal( $config );
+                               break;
+                       case 'disabled':
+                               return false;
+                       default:
+                               throw new MWException( "Invalid tidy driver: \"{$config['driver']}\"" );
+               }
+               return $instance;
+       }
+
        /**
         * Set the driver to be used. This is for testing.
-        * @param TidyDriverBase|false|null $instance
+        * @param MediaWiki\Tidy\TidyDriverBase|false|null $instance
         */
        public static function setInstance( $instance ) {
                self::$instance = $instance;
index bd2f131..d83ea34 100644 (file)
@@ -251,7 +251,7 @@ class Parser {
        protected $mProfiler;
 
        /**
-        * @var LinkRenderer
+        * @var \MediaWiki\Linker\LinkRenderer
         */
        protected $mLinkRenderer;
 
@@ -395,7 +395,8 @@ class Parser {
         * @param int $revid Number to pass in {{REVISIONID}}
         * @return ParserOutput A ParserOutput
         */
-       public function parse( $text, Title $title, ParserOptions $options,
+       public function parse(
+               $text, Title $title, ParserOptions $options,
                $linestart = true, $clearState = true, $revid = null
        ) {
                /**
@@ -462,6 +463,10 @@ class Parser {
                        }
                }
 
+               # Done parsing! Compute runtime adaptive expiry if set
+               $this->mOutput->finalizeAdaptiveCacheExpiry();
+
+               # Warn if too many heavyweight parser functions were used
                if ( $this->mExpensiveFunctionCount > $this->mOptions->getExpensiveParserFunctionLimit() ) {
                        $this->limitationWarn( 'expensive-parserfunction',
                                $this->mExpensiveFunctionCount,
@@ -501,60 +506,46 @@ class Parser {
                                [ $this->mHighestExpansionDepth, $this->mOptions->getMaxPPExpandDepth() ]
                        );
                        $this->mOutput->setLimitReportData( 'limitreport-expensivefunctioncount',
-                               [ $this->mExpensiveFunctionCount, $this->mOptions->getExpensiveParserFunctionLimit() ]
+                               [ $this->mExpensiveFunctionCount,
+                                       $this->mOptions->getExpensiveParserFunctionLimit() ]
                        );
                        Hooks::run( 'ParserLimitReportPrepare', [ $this, $this->mOutput ] );
 
-                       $limitReport = "NewPP limit report\n";
-                       if ( $wgShowHostnames ) {
-                               $limitReport .= 'Parsed by ' . wfHostname() . "\n";
-                       }
-                       $limitReport .= 'Cached time: ' . $this->mOutput->getCacheTime() . "\n";
-                       $limitReport .= 'Cache expiry: ' . $this->mOutput->getCacheExpiry() . "\n";
-                       $limitReport .= 'Dynamic content: ' .
-                               ( $this->mOutput->hasDynamicContent() ? 'true' : 'false' ) .
-                               "\n";
-
-                       foreach ( $this->mOutput->getLimitReportData() as $key => $value ) {
-                               if ( Hooks::run( 'ParserLimitReportFormat',
-                                       [ $key, &$value, &$limitReport, false, false ]
-                               ) ) {
-                                       $keyMsg = wfMessage( $key )->inLanguage( 'en' )->useDatabase( false );
-                                       $valueMsg = wfMessage( [ "$key-value-text", "$key-value" ] )
-                                               ->inLanguage( 'en' )->useDatabase( false );
-                                       if ( !$valueMsg->exists() ) {
-                                               $valueMsg = new RawMessage( '$1' );
-                                       }
-                                       if ( !$keyMsg->isDisabled() && !$valueMsg->isDisabled() ) {
-                                               $valueMsg->params( $value );
-                                               $limitReport .= "{$keyMsg->text()}: {$valueMsg->text()}\n";
-                                       }
-                               }
-                       }
-                       // Since we're not really outputting HTML, decode the entities and
-                       // then re-encode the things that need hiding inside HTML comments.
-                       $limitReport = htmlspecialchars_decode( $limitReport );
+                       $limitReport = '';
                        Hooks::run( 'ParserLimitReport', [ $this, &$limitReport ] );
+                       if ( $limitReport != '' ) {
+                               // Sanitize for comment. Note '‐' in the replacement is U+2010,
+                               // which looks much like the problematic '-'.
+                               $limitReport = str_replace( [ '-', '&' ], [ '‐', '&amp;' ], $limitReport );
+                               $text .= "\n<!-- \nNewPP limit report\n$limitReport-->\n";
+                       }
 
-                       // Sanitize for comment. Note '‐' in the replacement is U+2010,
-                       // which looks much like the problematic '-'.
-                       $limitReport = str_replace( [ '-', '&' ], [ '‐', '&amp;' ], $limitReport );
-                       $text .= "\n<!-- \n$limitReport-->\n";
-
-                       // Add on template profiling data
+                       // Add on template profiling data in human/machine readable way
                        $dataByFunc = $this->mProfiler->getFunctionStats();
                        uasort( $dataByFunc, function ( $a, $b ) {
                                return $a['real'] < $b['real']; // descending order
                        } );
-                       $profileReport = "Transclusion expansion time report (%,ms,calls,template)\n";
+                       $profileReport = [];
                        foreach ( array_slice( $dataByFunc, 0, 10 ) as $item ) {
-                               $profileReport .= sprintf( "%6.2f%% %8.3f %6d - %s\n",
-                                       $item['%real'], $item['real'], $item['calls'],
-                                       htmlspecialchars( $item['name'] ) );
+                               $profileReport[] = sprintf( "%6.2f%% %8.3f %6d %s",
+                                       $item['%real'], $item['real'], $item['calls'], $item['name'] );
                        }
-                       $text .= "\n<!-- \n$profileReport-->\n";
+                       $this->mOutput->setLimitReportData( 'limitreport-timingprofile', $profileReport );
 
-                       if ( $this->mGeneratedPPNodeCount > $this->mOptions->getMaxGeneratedPPNodeCount() / 10 ) {
+                       // Add other cache related metadata
+                       if ( $wgShowHostnames ) {
+                               $this->mOutput->setLimitReportData( 'cachereport-origin', wfHostname() );
+                       }
+                       $this->mOutput->setLimitReportData( 'cachereport-timestamp',
+                               $this->mOutput->getCacheTime() );
+                       $this->mOutput->setLimitReportData( 'cachereport-ttl',
+                               $this->mOutput->getCacheExpiry() );
+                       $this->mOutput->setLimitReportData( 'cachereport-transientcontent',
+                               $this->mOutput->hasDynamicContent() );
+
+                       if ( $this->mGeneratedPPNodeCount
+                               > $this->mOptions->getMaxGeneratedPPNodeCount() / 10
+                       ) {
                                wfDebugLog( 'generated-pp-node-count', $this->mGeneratedPPNodeCount . ' ' .
                                        $this->mTitle->getPrefixedDBkey() );
                        }
@@ -896,10 +887,10 @@ class Parser {
        }
 
        /**
-        * Get a LinkRenderer instance to make links with
+        * Get a \MediaWiki\Linker\LinkRenderer instance to make links with
         *
         * @since 1.28
-        * @return LinkRenderer
+        * @return \MediaWiki\Linker\LinkRenderer
         */
        public function getLinkRenderer() {
                if ( !$this->mLinkRenderer ) {
@@ -920,11 +911,11 @@ class Parser {
         * the form:
         *
         * @code
-        *   'UNIQ-xxxxx' => array(
+        *   'UNIQ-xxxxx' => [
         *     'element',
         *     'tag content',
-        *     array( 'param' => 'x' ),
-        *     '<element param="x">tag content</element>' ) )
+        *     [ 'param' => 'x' ],
+        *     '<element param="x">tag content</element>' ]
         * @endcode
         *
         * @param array $elements List of element names. Comments are always extracted.
@@ -1264,7 +1255,9 @@ class Parser {
                        $text,
                        [ &$this, 'attributeStripCallback' ],
                        false,
-                       array_keys( $this->mTransparentTagHooks )
+                       array_keys( $this->mTransparentTagHooks ),
+                       [],
+                       [ &$this, 'addTrackingCategory' ]
                );
                Hooks::run( 'InternalParseBeforeLinks', [ &$this, &$text, &$this->mStripState ] );
 
@@ -1353,9 +1346,10 @@ class Parser {
 
                $text = Sanitizer::normalizeCharReferences( $text );
 
-               if ( MWTidy::isEnabled() && $this->mOptions->getTidy() ) {
-                       $text = MWTidy::tidy( $text );
-                       $this->mOutput->addModuleStyles( MWTidy::getModuleStyles() );
+               if ( MWTidy::isEnabled() ) {
+                       if ( $this->mOptions->getTidy() ) {
+                               $text = MWTidy::tidy( $text );
+                       }
                } else {
                        # attempt to sanitize at least some nesting problems
                        # (bug #2702 and quite a few others)
@@ -1781,7 +1775,7 @@ class Parser {
         * Replace external links (REL)
         *
         * Note: this is all very hackish and the order of execution matters a lot.
-        * Make sure to run tests/parserTests.php if you change this code.
+        * Make sure to run tests/parser/parserTests.php if you change this code.
         *
         * @private
         *
@@ -2169,7 +2163,7 @@ class Parser {
                                $might_be_img = true;
                                $text = $m[2];
                                if ( strpos( $m[1], '%' ) !== false ) {
-                                       $m[1] = rawurldecode( $m[1] );
+                                       $m[1] = str_replace( [ '<', '>' ], [ '&lt;', '&gt;' ], rawurldecode( $m[1] ) );
                                }
                                $trail = "";
                        } else { # Invalid form; output directly
@@ -3155,14 +3149,17 @@ class Parser {
                                                $context->setUser( User::newFromName( '127.0.0.1', false ) );
                                        }
                                        $context->setLanguage( $this->mOptions->getUserLangObj() );
-                                       $ret = SpecialPageFactory::capturePath( $title, $context, $this->getLinkRenderer() );
+                                       $ret = SpecialPageFactory::capturePath(
+                                               $title, $context, $this->getLinkRenderer() );
                                        if ( $ret ) {
                                                $text = $context->getOutput()->getHTML();
                                                $this->mOutput->addOutputPageMetadata( $context->getOutput() );
                                                $found = true;
                                                $isHTML = true;
                                                if ( $specialPage && $specialPage->maxIncludeCacheTime() !== false ) {
-                                                       $this->mOutput->updateCacheExpiry( $specialPage->maxIncludeCacheTime() );
+                                                       $this->mOutput->updateRuntimeAdaptiveExpiry(
+                                                               $specialPage->maxIncludeCacheTime()
+                                                       );
                                                }
                                        }
                                } elseif ( MWNamespace::isNonincludable( $title->getNamespace() ) ) {
@@ -3461,10 +3458,18 @@ class Parser {
         * @since 1.24
         * @param Title $title
         * @param Parser|bool $parser
-        * @return Revision
+        * @return Revision|bool False if missing
         */
-       public static function statelessFetchRevision( $title, $parser = false ) {
-               return Revision::newFromTitle( $title );
+       public static function statelessFetchRevision( Title $title, $parser = false ) {
+               $pageId = $title->getArticleID();
+               $revId = $title->getLatestRevID();
+
+               $rev = Revision::newKnownCurrent( wfGetDB( DB_REPLICA ), $pageId, $revId );
+               if ( $rev ) {
+                       $rev->setTitle( $title );
+               }
+
+               return $rev;
        }
 
        /**
@@ -3679,7 +3684,7 @@ class Parser {
         */
        public function fetchScaryTemplateMaybeFromCache( $url ) {
                global $wgTranscludeCacheExpiry;
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $tsCond = $dbr->timestamp( time() - $wgTranscludeCacheExpiry );
                $obj = $dbr->selectRow( 'transcache', [ 'tc_time', 'tc_contents' ],
                                [ 'tc_url' => $url, "tc_time >= " . $dbr->addQuotes( $tsCond ) ] );
@@ -4375,11 +4380,11 @@ class Parser {
                $this->startParse( $title, $options, self::OT_WIKI, $clearState );
                $this->setUser( $user );
 
-               $pairs = [
-                       "\r\n" => "\n",
-                       "\r" => "\n",
-               ];
-               $text = str_replace( array_keys( $pairs ), array_values( $pairs ), $text );
+               // We still normalize line endings for backwards-compatibility
+               // with other code that just calls PST, but this should already
+               // be handled in TextContent subclasses
+               $text = TextContent::normalizeLineEndings( $text );
+
                if ( $options->getPreSaveTransform() ) {
                        $text = $this->pstPass2( $text, $user );
                }
@@ -4457,9 +4462,6 @@ class Parser {
                        $text = preg_replace( $p2, '[[\\1]]', $text );
                }
 
-               # Trim trailing whitespace
-               $text = rtrim( $text );
-
                return $text;
        }
 
index 3462d10..9dfa97c 100644 (file)
@@ -188,9 +188,7 @@ class ParserOutput extends CacheTime {
         */
        private $mExtensionData = [];
 
-       /**
-        * @var array $mLimitReportData Parser limit report data.
-        */
+       /** @var array $mLimitReportData Parser limit report data. */
        private $mLimitReportData = [];
 
        /**
@@ -211,9 +209,21 @@ class ParserOutput extends CacheTime {
        /** @var integer|null Assumed rev ID for {{REVISIONID}} if no revision is set */
        private $mSpeculativeRevId;
 
+       /** @var integer Upper bound of expiry based on parse duration */
+       private $mMaxAdaptiveExpiry = INF;
+
        const EDITSECTION_REGEX =
                '#<(?:mw:)?editsection page="(.*?)" section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#';
 
+       // finalizeAdaptiveCacheExpiry() uses TTL = MAX( m * PARSE_TIME + b, MIN_AR_TTL)
+       // Current values imply that m=3933.333333 and b=-333.333333
+       // See https://www.nngroup.com/articles/website-response-times/
+       const PARSE_FAST_SEC = .100; // perceived "fast" page parse
+       const PARSE_SLOW_SEC = 1.0; // perceived "slow" page parse
+       const FAST_AR_TTL = 60; // adaptive TTL for "fast" pages
+       const SLOW_AR_TTL = 3600; // adaptive TTL for "slow" pages
+       const MIN_AR_TTL = 15; // min adaptive TTL (for sanity, pool counter, and edit stashing)
+
        public function __construct( $text = '', $languageLinks = [], $categoryLinks = [],
                $unused = false, $titletext = ''
        ) {
@@ -367,15 +377,6 @@ class ParserOutput extends CacheTime {
                return $this->mModuleStyles;
        }
 
-       /**
-        * @deprecated since 1.26 Obsolete
-        * @return array
-        */
-       public function getModuleMessages() {
-               wfDeprecated( __METHOD__, '1.26' );
-               return [];
-       }
-
        /** @since 1.23 */
        public function getJsConfigVars() {
                return $this->mJsConfigVars;
@@ -644,14 +645,6 @@ class ParserOutput extends CacheTime {
                $this->mModuleStyles = array_merge( $this->mModuleStyles, (array)$modules );
        }
 
-       /**
-        * @deprecated since 1.26 Use addModules() instead
-        * @param string|array $modules
-        */
-       public function addModuleMessages( $modules ) {
-               wfDeprecated( __METHOD__, '1.26' );
-       }
-
        /**
         * Add one or more variables to be set in mw.config in JavaScript.
         *
@@ -998,24 +991,34 @@ class ParserOutput extends CacheTime {
        /**
         * Sets parser limit report data for a key
         *
-        * The key is used as the prefix for various messages used for formatting:
-        *  - $key: The label for the field in the limit report
-        *  - $key-value-text: Message used to format the value in the "NewPP limit
-        *      report" HTML comment. If missing, uses $key-format.
-        *  - $key-value-html: Message used to format the value in the preview
-        *      limit report table. If missing, uses $key-format.
-        *  - $key-value: Message used to format the value. If missing, uses "$1".
-        *
-        * Note that all values are interpreted as wikitext, and so should be
-        * encoded with htmlspecialchars() as necessary, but should avoid complex
-        * HTML for sanity of display in the "NewPP limit report" comment.
+        * If $value consist of a list of two floats, it will be interpreted as
+        * (actual value, maximum allowed value). The presence of a "-" in $key will cause
+        * the first part of the key to be interpreted as a namespace.
         *
         * @since 1.22
-        * @param string $key Message key
-        * @param mixed $value Appropriate for Message::params()
+        * @param string $key Data key
+        * @param mixed $value Data value One of (float, string, bool, JSON serializable array)
         */
        public function setLimitReportData( $key, $value ) {
-               $this->mLimitReportData[$key] = $value;
+               if ( is_array( $value ) ) {
+                       if ( array_keys( $value ) === [ 0, 1 ]
+                               && is_numeric( $value[0] )
+                               && is_numeric( $value[1] )
+                       ) {
+                               $data = [ 'value' => $value[0], 'limit' => $value[1] ];
+                       } else {
+                               $data = $value;
+                       }
+               } else {
+                       $data = $value;
+               }
+
+               if ( strpos( $key, '-' ) ) {
+                       list( $ns, $name ) = explode( '-', $key, 2 );
+                       $this->mLimitReportData[$ns][$name] = $data;
+               } else {
+                       $this->mLimitReportData[$key] = $data;
+               }
        }
 
        /**
@@ -1046,9 +1049,41 @@ class ParserOutput extends CacheTime {
        }
 
        /**
-        * Save space for serialization by removing useless values
-        * @return array
+        * Lower the runtime adaptive TTL to at most this value
+        *
+        * @param integer $ttl
+        * @since 1.28
+        */
+       public function updateRuntimeAdaptiveExpiry( $ttl ) {
+               $this->mMaxAdaptiveExpiry = min( $ttl, $this->mMaxAdaptiveExpiry );
+               $this->updateCacheExpiry( $ttl );
+       }
+
+       /**
+        * Call this when parsing is done to lower the TTL based on low parse times
+        *
+        * @since 1.28
         */
+       public function finalizeAdaptiveCacheExpiry() {
+               if ( is_infinite( $this->mMaxAdaptiveExpiry ) ) {
+                       return; // not set
+               }
+
+               $runtime = $this->getTimeSinceStart( 'wall' );
+               if ( is_float( $runtime ) ) {
+                       $slope = ( self::SLOW_AR_TTL - self::FAST_AR_TTL )
+                               / ( self::PARSE_SLOW_SEC - self::PARSE_FAST_SEC );
+                       // SLOW_AR_TTL = PARSE_SLOW_SEC * $slope + $point
+                       $point = self::SLOW_AR_TTL - self::PARSE_SLOW_SEC * $slope;
+
+                       $adaptiveTTL = min(
+                               max( $slope * $runtime + $point, self::MIN_AR_TTL ),
+                               $this->mMaxAdaptiveExpiry
+                       );
+                       $this->updateCacheExpiry( $adaptiveTTL );
+               }
+       }
+
        public function __sleep() {
                return array_diff(
                        array_keys( get_object_vars( $this ) ),
index a28c0aa..5da7cd7 100644 (file)
@@ -539,7 +539,7 @@ class Preprocessor_DOM extends Preprocessor {
                        } elseif ( $found == 'line-end' ) {
                                $piece = $stack->top;
                                // A heading must be open, otherwise \n wouldn't have been in the search list
-                               assert( '$piece->open == "\n"' );
+                               assert( $piece->open === "\n" );
                                $part = $piece->getCurrentPart();
                                // Search back through the input to see if it has a proper close.
                                // Do this using the reversed string since the other solutions
index 0e11967..8a4637e 100644 (file)
  * Differences from DOM schema:
  *   * attribute nodes are children
  *   * "<h>" nodes that aren't at the top are replaced with <possible-h>
+ *
+ * Nodes are stored in a recursive array data structure. A node store is an
+ * array where each element may be either a scalar (representing a text node)
+ * or a "descriptor", which is a two-element array where the first element is
+ * the node name and the second element is the node store for the children.
+ *
+ * Attributes are represented as children that have a node name starting with
+ * "@", and a single text node child.
+ *
+ * @todo: Consider replacing descriptor arrays with objects of a new class.
+ * Benchmark and measure resulting memory impact.
+ *
  * @ingroup Parser
  */
 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
@@ -37,6 +49,7 @@ class Preprocessor_Hash extends Preprocessor {
        public $parser;
 
        const CACHE_PREFIX = 'preprocess-hash';
+       const CACHE_VERSION = 2;
 
        public function __construct( $parser ) {
                $this->parser = $parser;
@@ -65,23 +78,20 @@ class Preprocessor_Hash extends Preprocessor {
                $list = [];
 
                foreach ( $values as $k => $val ) {
-                       $partNode = new PPNode_Hash_Tree( 'part' );
-                       $nameNode = new PPNode_Hash_Tree( 'name' );
-
                        if ( is_int( $k ) ) {
-                               $nameNode->addChild( new PPNode_Hash_Attr( 'index', $k ) );
-                               $partNode->addChild( $nameNode );
+                               $store = [ [ 'part', [
+                                       [ 'name', [ [ '@index', [ $k ] ] ] ],
+                                       [ 'value', [ strval( $val ) ] ],
+                               ] ] ];
                        } else {
-                               $nameNode->addChild( new PPNode_Hash_Text( $k ) );
-                               $partNode->addChild( $nameNode );
-                               $partNode->addChild( new PPNode_Hash_Text( '=' ) );
+                               $store = [ [ 'part', [
+                                       [ 'name', [ strval( $k ) ] ],
+                                       '=',
+                                       [ 'value', [ strval( $val ) ] ],
+                               ] ] ];
                        }
 
-                       $valueNode = new PPNode_Hash_Tree( 'value' );
-                       $valueNode->addChild( new PPNode_Hash_Text( $val ) );
-                       $partNode->addChild( $valueNode );
-
-                       $list[] = $partNode;
+                       $list[] = new PPNode_Hash_Tree( $store, 0 );
                }
 
                $node = new PPNode_Hash_Array( $list );
@@ -90,7 +100,6 @@ class Preprocessor_Hash extends Preprocessor {
 
        /**
         * Preprocess some wikitext and return the document tree.
-        * This is the ghost of Parser::replace_variables().
         *
         * @param string $text The text to parse
         * @param int $flags Bitwise combination of:
@@ -104,17 +113,16 @@ class Preprocessor_Hash extends Preprocessor {
         * change in the DOM tree for a given text, must be passed through the section identifier
         * in the section edit link and thus back to extractSections().
         *
-        * The output of this function is currently only cached in process memory, but a persistent
-        * cache may be implemented at a later date which takes further advantage of these strict
-        * dependency requirements.
-        *
         * @throws MWException
         * @return PPNode_Hash_Tree
         */
        public function preprocessToObj( $text, $flags = 0 ) {
                $tree = $this->cacheGetTree( $text, $flags );
                if ( $tree !== false ) {
-                       return unserialize( $tree );
+                       $store = json_decode( $tree );
+                       if ( is_array( $store ) ) {
+                               return new PPNode_Hash_Tree( $store, 0 );
+                       }
                }
 
                $forInclusion = $flags & Parser::PTD_FOR_INCLUSION;
@@ -150,7 +158,7 @@ class Preprocessor_Hash extends Preprocessor {
 
                // Input pointer, starts out pointing to a pseudo-newline before the start
                $i = 0;
-               // Current accumulator
+               // Current accumulator. See the doc comment for Preprocessor_Hash for the format.
                $accum =& $stack->getAccum();
                // True to find equals signs in arguments
                $findEquals = false;
@@ -176,11 +184,11 @@ class Preprocessor_Hash extends Preprocessor {
                                $startPos = strpos( $text, '<onlyinclude>', $i );
                                if ( $startPos === false ) {
                                        // Ignored section runs to the end
-                                       $accum->addNodeWithText( 'ignore', substr( $text, $i ) );
+                                       $accum[] = [ 'ignore', [ substr( $text, $i ) ] ];
                                        break;
                                }
                                $tagEndPos = $startPos + strlen( '<onlyinclude>' ); // past-the-end
-                               $accum->addNodeWithText( 'ignore', substr( $text, $i, $tagEndPos - $i ) );
+                               $accum[] = [ 'ignore', [ substr( $text, $i, $tagEndPos - $i ) ] ];
                                $i = $tagEndPos;
                                $findOnlyinclude = false;
                        }
@@ -208,7 +216,7 @@ class Preprocessor_Hash extends Preprocessor {
                                # Output literal section, advance input counter
                                $literalLength = strcspn( $text, $search, $i );
                                if ( $literalLength > 0 ) {
-                                       $accum->addLiteral( substr( $text, $i, $literalLength ) );
+                                       self::addLiteral( $accum, substr( $text, $i, $literalLength ) );
                                        $i += $literalLength;
                                }
                                if ( $i >= $lengthText ) {
@@ -261,7 +269,7 @@ class Preprocessor_Hash extends Preprocessor {
                                // Determine element name
                                if ( !preg_match( $elementsRegex, $text, $matches, 0, $i + 1 ) ) {
                                        // Element name missing or not listed
-                                       $accum->addLiteral( '<' );
+                                       self::addLiteral( $accum, '<' );
                                        ++$i;
                                        continue;
                                }
@@ -278,7 +286,7 @@ class Preprocessor_Hash extends Preprocessor {
                                        if ( $endPos === false ) {
                                                // Unclosed comment in input, runs to end
                                                $inner = substr( $text, $i );
-                                               $accum->addNodeWithText( 'comment', $inner );
+                                               $accum[] = [ 'comment', [ $inner ] ];
                                                $i = $lengthText;
                                        } else {
                                                // Search backwards for leading whitespace
@@ -309,13 +317,16 @@ class Preprocessor_Hash extends Preprocessor {
                                                        && substr( $text, $wsEnd + 1, 1 ) == "\n"
                                                ) {
                                                        // Remove leading whitespace from the end of the accumulator
-                                                       // Sanity check first though
                                                        $wsLength = $i - $wsStart;
+                                                       $endIndex = count( $accum ) - 1;
+
+                                                       // Sanity check
                                                        if ( $wsLength > 0
-                                                               && $accum->lastNode instanceof PPNode_Hash_Text
-                                                               && strspn( $accum->lastNode->value, " \t", -$wsLength ) === $wsLength
+                                                               && $endIndex >= 0
+                                                               && is_string( $accum[$endIndex] )
+                                                               && strspn( $accum[$endIndex], " \t", -$wsLength ) === $wsLength
                                                        ) {
-                                                               $accum->lastNode->value = substr( $accum->lastNode->value, 0, -$wsLength );
+                                                               $accum[$endIndex] = substr( $accum[$endIndex], 0, -$wsLength );
                                                        }
 
                                                        // Dump all but the last comment to the accumulator
@@ -326,7 +337,7 @@ class Preprocessor_Hash extends Preprocessor {
                                                                        break;
                                                                }
                                                                $inner = substr( $text, $startPos, $endPos - $startPos );
-                                                               $accum->addNodeWithText( 'comment', $inner );
+                                                               $accum[] = [ 'comment', [ $inner ] ];
                                                        }
 
                                                        // Do a line-start run next time to look for headings after the comment
@@ -347,7 +358,7 @@ class Preprocessor_Hash extends Preprocessor {
                                                }
                                                $i = $endPos + 1;
                                                $inner = substr( $text, $startPos, $endPos - $startPos + 1 );
-                                               $accum->addNodeWithText( 'comment', $inner );
+                                               $accum[] = [ 'comment', [ $inner ] ];
                                        }
                                        continue;
                                }
@@ -361,14 +372,14 @@ class Preprocessor_Hash extends Preprocessor {
                                        // Infinite backtrack
                                        // Disable tag search to prevent worst-case O(N^2) performance
                                        $noMoreGT = true;
-                                       $accum->addLiteral( '<' );
+                                       self::addLiteral( $accum, '<' );
                                        ++$i;
                                        continue;
                                }
 
                                // Handle ignored tags
                                if ( in_array( $lowerName, $ignoredTags ) ) {
-                                       $accum->addNodeWithText( 'ignore', substr( $text, $i, $tagEndPos - $i + 1 ) );
+                                       $accum[] = [ 'ignore', [ substr( $text, $i, $tagEndPos - $i + 1 ) ] ];
                                        $i = $tagEndPos + 1;
                                        continue;
                                }
@@ -401,7 +412,8 @@ class Preprocessor_Hash extends Preprocessor {
                                                } else {
                                                        // Don't match the tag, treat opening tag as literal and resume parsing.
                                                        $i = $tagEndPos + 1;
-                                                       $accum->addLiteral( substr( $text, $tagStartPos, $tagEndPos + 1 - $tagStartPos ) );
+                                                       self::addLiteral( $accum,
+                                                               substr( $text, $tagStartPos, $tagEndPos + 1 - $tagStartPos ) );
                                                        // Cache results, otherwise we have O(N^2) performance for input like <foo><foo><foo>...
                                                        $noMoreClosingTag[$name] = true;
                                                        continue;
@@ -410,7 +422,7 @@ class Preprocessor_Hash extends Preprocessor {
                                }
                                // <includeonly> and <noinclude> just become <ignore> tags
                                if ( in_array( $lowerName, $ignoredElements ) ) {
-                                       $accum->addNodeWithText( 'ignore', substr( $text, $tagStartPos, $i - $tagStartPos ) );
+                                       $accum[] = [ 'ignore', [ substr( $text, $tagStartPos, $i - $tagStartPos ) ] ];
                                        continue;
                                }
 
@@ -422,23 +434,23 @@ class Preprocessor_Hash extends Preprocessor {
                                        $attr = substr( $text, $attrStart, $attrEnd - $attrStart );
                                }
 
-                               $extNode = new PPNode_Hash_Tree( 'ext' );
-                               $extNode->addChild( PPNode_Hash_Tree::newWithText( 'name', $name ) );
-                               $extNode->addChild( PPNode_Hash_Tree::newWithText( 'attr', $attr ) );
+                               $children = [
+                                       [ 'name', [ $name ] ],
+                                       [ 'attr', [ $attr ] ] ];
                                if ( $inner !== null ) {
-                                       $extNode->addChild( PPNode_Hash_Tree::newWithText( 'inner', $inner ) );
+                                       $children[] = [ 'inner', [ $inner ] ];
                                }
                                if ( $close !== null ) {
-                                       $extNode->addChild( PPNode_Hash_Tree::newWithText( 'close', $close ) );
+                                       $children[] = [ 'close', [ $close ] ];
                                }
-                               $accum->addNode( $extNode );
+                               $accum[] = [ 'ext', $children ];
                        } elseif ( $found == 'line-start' ) {
                                // Is this the start of a heading?
                                // Line break belongs before the heading element in any case
                                if ( $fakeLineStart ) {
                                        $fakeLineStart = false;
                                } else {
-                                       $accum->addLiteral( $curChar );
+                                       self::addLiteral( $accum, $curChar );
                                        $i++;
                                }
 
@@ -463,7 +475,7 @@ class Preprocessor_Hash extends Preprocessor {
                        } elseif ( $found == 'line-end' ) {
                                $piece = $stack->top;
                                // A heading must be open, otherwise \n wouldn't have been in the search list
-                               assert( '$piece->open == "\n"' );
+                               assert( $piece->open === "\n" );
                                $part = $piece->getCurrentPart();
                                // Search back through the input to see if it has a proper close.
                                // Do this using the reversed string since the other solutions
@@ -494,11 +506,15 @@ class Preprocessor_Hash extends Preprocessor {
                                        }
                                        if ( $count > 0 ) {
                                                // Normal match, output <h>
-                                               $element = new PPNode_Hash_Tree( 'possible-h' );
-                                               $element->addChild( new PPNode_Hash_Attr( 'level', $count ) );
-                                               $element->addChild( new PPNode_Hash_Attr( 'i', $headingIndex++ ) );
-                                               $element->lastChild->nextSibling = $accum->firstNode;
-                                               $element->lastChild = $accum->lastNode;
+                                               $element = [ [ 'possible-h',
+                                                       array_merge(
+                                                               [
+                                                                       [ '@level', [ $count ] ],
+                                                                       [ '@i', [ $headingIndex++ ] ]
+                                                               ],
+                                                               $accum
+                                                       )
+                                               ] ];
                                        } else {
                                                // Single equals sign on its own line, count=0
                                                $element = $accum;
@@ -513,11 +529,8 @@ class Preprocessor_Hash extends Preprocessor {
                                extract( $stack->getFlags() );
 
                                // Append the result to the enclosing accumulator
-                               if ( $element instanceof PPNode ) {
-                                       $accum->addNode( $element );
-                               } else {
-                                       $accum->addAccum( $element );
-                               }
+                               array_splice( $accum, count( $accum ), 0, $element );
+
                                // Note that we do NOT increment the input pointer.
                                // This is because the closing linebreak could be the opening linebreak of
                                // another heading. Infinite loops are avoided because the next iteration MUST
@@ -542,7 +555,7 @@ class Preprocessor_Hash extends Preprocessor {
                                        extract( $stack->getFlags() );
                                } else {
                                        # Add literal brace(s)
-                                       $accum->addLiteral( str_repeat( $curChar, $count ) );
+                                       self::addLiteral( $accum, str_repeat( $curChar, $count ) );
                                }
                                $i += $count;
                        } elseif ( $found == 'close' ) {
@@ -571,7 +584,7 @@ class Preprocessor_Hash extends Preprocessor {
                                if ( $matchingCount <= 0 ) {
                                        # No matching element found in callback array
                                        # Output a literal closing brace and continue
-                                       $accum->addLiteral( str_repeat( $curChar, $count ) );
+                                       self::addLiteral( $accum, str_repeat( $curChar, $count ) );
                                        $i += $count;
                                        continue;
                                }
@@ -579,77 +592,38 @@ class Preprocessor_Hash extends Preprocessor {
                                if ( $name === null ) {
                                        // No element, just literal text
                                        $element = $piece->breakSyntax( $matchingCount );
-                                       $element->addLiteral( str_repeat( $rule['end'], $matchingCount ) );
+                                       self::addLiteral( $element, str_repeat( $rule['end'], $matchingCount ) );
                                } else {
                                        # Create XML element
-                                       # Note: $parts is already XML, does not need to be encoded further
                                        $parts = $piece->parts;
                                        $titleAccum = $parts[0]->out;
                                        unset( $parts[0] );
 
-                                       $element = new PPNode_Hash_Tree( $name );
+                                       $children = [];
 
                                        # The invocation is at the start of the line if lineStart is set in
                                        # the stack, and all opening brackets are used up.
                                        if ( $maxCount == $matchingCount && !empty( $piece->lineStart ) ) {
-                                               $element->addChild( new PPNode_Hash_Attr( 'lineStart', 1 ) );
+                                               $children[] = [ '@lineStart', [ 1 ] ];
                                        }
-                                       $titleNode = new PPNode_Hash_Tree( 'title' );
-                                       $titleNode->firstChild = $titleAccum->firstNode;
-                                       $titleNode->lastChild = $titleAccum->lastNode;
-                                       $element->addChild( $titleNode );
+                                       $titleNode = [ 'title', $titleAccum ];
+                                       $children[] = $titleNode;
                                        $argIndex = 1;
                                        foreach ( $parts as $part ) {
                                                if ( isset( $part->eqpos ) ) {
-                                                       // Find equals
-                                                       $lastNode = false;
-                                                       for ( $node = $part->out->firstNode; $node; $node = $node->nextSibling ) {
-                                                               if ( $node === $part->eqpos ) {
-                                                                       break;
-                                                               }
-                                                               $lastNode = $node;
-                                                       }
-                                                       if ( !$node ) {
-                                                               // if ( $cacheable ) { ... }
-                                                               throw new MWException( __METHOD__ . ': eqpos not found' );
-                                                       }
-                                                       if ( $node->name !== 'equals' ) {
-                                                               // if ( $cacheable ) { ... }
-                                                               throw new MWException( __METHOD__ . ': eqpos is not equals' );
-                                                       }
-                                                       $equalsNode = $node;
-
-                                                       // Construct name node
-                                                       $nameNode = new PPNode_Hash_Tree( 'name' );
-                                                       if ( $lastNode !== false ) {
-                                                               $lastNode->nextSibling = false;
-                                                               $nameNode->firstChild = $part->out->firstNode;
-                                                               $nameNode->lastChild = $lastNode;
-                                                       }
-
-                                                       // Construct value node
-                                                       $valueNode = new PPNode_Hash_Tree( 'value' );
-                                                       if ( $equalsNode->nextSibling !== false ) {
-                                                               $valueNode->firstChild = $equalsNode->nextSibling;
-                                                               $valueNode->lastChild = $part->out->lastNode;
-                                                       }
-                                                       $partNode = new PPNode_Hash_Tree( 'part' );
-                                                       $partNode->addChild( $nameNode );
-                                                       $partNode->addChild( $equalsNode->firstChild );
-                                                       $partNode->addChild( $valueNode );
-                                                       $element->addChild( $partNode );
+                                                       $equalsNode = $part->out[$part->eqpos];
+                                                       $nameNode = [ 'name', array_slice( $part->out, 0, $part->eqpos ) ];
+                                                       $valueNode = [ 'value', array_slice( $part->out, $part->eqpos + 1 ) ];
+                                                       $partNode = [ 'part', [ $nameNode, $equalsNode, $valueNode ] ];
+                                                       $children[] = $partNode;
                                                } else {
-                                                       $partNode = new PPNode_Hash_Tree( 'part' );
-                                                       $nameNode = new PPNode_Hash_Tree( 'name' );
-                                                       $nameNode->addChild( new PPNode_Hash_Attr( 'index', $argIndex++ ) );
-                                                       $valueNode = new PPNode_Hash_Tree( 'value' );
-                                                       $valueNode->firstChild = $part->out->firstNode;
-                                                       $valueNode->lastChild = $part->out->lastNode;
-                                                       $partNode->addChild( $nameNode );
-                                                       $partNode->addChild( $valueNode );
-                                                       $element->addChild( $partNode );
+                                                       $nameNode = [ 'name', [ [ '@index', [ $argIndex++ ] ] ] ];
+                                                       $valueNode = [ 'value', $part->out ];
+                                                       $partNode = [ 'part', [ $nameNode, $valueNode ] ];
+                                                       $children[] = $partNode;
                                                }
                                        }
+                                       $element = [ [ $name, $children ] ];
                                }
 
                                # Advance input pointer
@@ -669,18 +643,14 @@ class Preprocessor_Hash extends Preprocessor {
                                                $stack->push( $piece );
                                                $accum =& $stack->getAccum();
                                        } else {
-                                               $accum->addLiteral( str_repeat( $piece->open, $piece->count ) );
+                                               self::addLiteral( $accum, str_repeat( $piece->open, $piece->count ) );
                                        }
                                }
 
                                extract( $stack->getFlags() );
 
                                # Add XML element to the enclosing accumulator
-                               if ( $element instanceof PPNode ) {
-                                       $accum->addNode( $element );
-                               } else {
-                                       $accum->addAccum( $element );
-                               }
+                               array_splice( $accum, count( $accum ), 0, $element );
                        } elseif ( $found == 'pipe' ) {
                                $findEquals = true; // shortcut for getFlags()
                                $stack->addPart();
@@ -688,33 +658,44 @@ class Preprocessor_Hash extends Preprocessor {
                                ++$i;
                        } elseif ( $found == 'equals' ) {
                                $findEquals = false; // shortcut for getFlags()
-                               $accum->addNodeWithText( 'equals', '=' );
-                               $stack->getCurrentPart()->eqpos = $accum->lastNode;
+                               $accum[] = [ 'equals', [ '=' ] ];
+                               $stack->getCurrentPart()->eqpos = count( $accum ) - 1;
                                ++$i;
                        }
                }
 
                # Output any remaining unclosed brackets
                foreach ( $stack->stack as $piece ) {
-                       $stack->rootAccum->addAccum( $piece->breakSyntax() );
+                       array_splice( $stack->rootAccum, count( $stack->rootAccum ), 0, $piece->breakSyntax() );
                }
 
                # Enable top-level headings
-               for ( $node = $stack->rootAccum->firstNode; $node; $node = $node->nextSibling ) {
-                       if ( isset( $node->name ) && $node->name === 'possible-h' ) {
-                               $node->name = 'h';
+               foreach ( $stack->rootAccum as &$node ) {
+                       if ( is_array( $node ) && $node[PPNode_Hash_Tree::NAME] === 'possible-h' ) {
+                               $node[PPNode_Hash_Tree::NAME] = 'h';
                        }
                }
 
-               $rootNode = new PPNode_Hash_Tree( 'root' );
-               $rootNode->firstChild = $stack->rootAccum->firstNode;
-               $rootNode->lastChild = $stack->rootAccum->lastNode;
+               $rootStore = [ [ 'root', $stack->rootAccum ] ];
+               $rootNode = new PPNode_Hash_Tree( $rootStore, 0 );
 
                // Cache
-               $this->cacheSetTree( $text, $flags, serialize( $rootNode ) );
+               $tree = json_encode( $rootStore, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
+               if ( $tree !== false ) {
+                       $this->cacheSetTree( $text, $flags, $tree );
+               }
 
                return $rootNode;
        }
+
+       private static function addLiteral( array &$accum, $text ) {
+               $n = count( $accum );
+               if ( $n && is_string( $accum[$n - 1] ) ) {
+                       $accum[$n - 1] .= $text;
+               } else {
+                       $accum[] = $text;
+               }
+       }
 }
 
 /**
@@ -728,7 +709,7 @@ class PPDStack_Hash extends PPDStack {
        public function __construct() {
                $this->elementClass = 'PPDStackElement_Hash';
                parent::__construct();
-               $this->rootAccum = new PPDAccum_Hash;
+               $this->rootAccum = [];
        }
 }
 
@@ -748,7 +729,7 @@ class PPDStackElement_Hash extends PPDStackElement {
         * Get the accumulator that would result if the close is not found.
         *
         * @param int|bool $openingCount
-        * @return PPDAccum_Hash
+        * @return array
         */
        public function breakSyntax( $openingCount = false ) {
                if ( $this->open == "\n" ) {
@@ -757,16 +738,24 @@ class PPDStackElement_Hash extends PPDStackElement {
                        if ( $openingCount === false ) {
                                $openingCount = $this->count;
                        }
-                       $accum = new PPDAccum_Hash;
-                       $accum->addLiteral( str_repeat( $this->open, $openingCount ) );
+                       $accum = [ str_repeat( $this->open, $openingCount ) ];
+                       $lastIndex = 0;
                        $first = true;
                        foreach ( $this->parts as $part ) {
                                if ( $first ) {
                                        $first = false;
+                               } elseif ( is_string( $accum[$lastIndex] ) ) {
+                                       $accum[$lastIndex] .= '|';
                                } else {
-                                       $accum->addLiteral( '|' );
+                                       $accum[++$lastIndex] = '|';
+                               }
+                               foreach ( $part->out as $node ) {
+                                       if ( is_string( $node ) && is_string( $accum[$lastIndex] ) ) {
+                                               $accum[$lastIndex] .= $node;
+                                       } else {
+                                               $accum[++$lastIndex] = $node;
+                                       }
                                }
-                               $accum->addAccum( $part->out );
                        }
                }
                return $accum;
@@ -781,81 +770,12 @@ class PPDPart_Hash extends PPDPart {
        // @codingStandardsIgnoreEnd
 
        public function __construct( $out = '' ) {
-               $accum = new PPDAccum_Hash;
                if ( $out !== '' ) {
-                       $accum->addLiteral( $out );
-               }
-               parent::__construct( $accum );
-       }
-}
-
-/**
- * @ingroup Parser
- */
-// @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
-class PPDAccum_Hash {
-       // @codingStandardsIgnoreEnd
-
-       public $firstNode, $lastNode;
-
-       public function __construct() {
-               $this->firstNode = $this->lastNode = false;
-       }
-
-       /**
-        * Append a string literal
-        * @param string $s
-        */
-       public function addLiteral( $s ) {
-               if ( $this->lastNode === false ) {
-                       $this->firstNode = $this->lastNode = new PPNode_Hash_Text( $s );
-               } elseif ( $this->lastNode instanceof PPNode_Hash_Text ) {
-                       $this->lastNode->value .= $s;
-               } else {
-                       $this->lastNode->nextSibling = new PPNode_Hash_Text( $s );
-                       $this->lastNode = $this->lastNode->nextSibling;
-               }
-       }
-
-       /**
-        * Append a PPNode
-        * @param PPNode $node
-        */
-       public function addNode( PPNode $node ) {
-               if ( $this->lastNode === false ) {
-                       $this->firstNode = $this->lastNode = $node;
-               } else {
-                       $this->lastNode->nextSibling = $node;
-                       $this->lastNode = $node;
-               }
-       }
-
-       /**
-        * Append a tree node with text contents
-        * @param string $name
-        * @param string $value
-        */
-       public function addNodeWithText( $name, $value ) {
-               $node = PPNode_Hash_Tree::newWithText( $name, $value );
-               $this->addNode( $node );
-       }
-
-       /**
-        * Append a PPDAccum_Hash
-        * Takes over ownership of the nodes in the source argument. These nodes may
-        * subsequently be modified, especially nextSibling.
-        * @param PPDAccum_Hash $accum
-        */
-       public function addAccum( $accum ) {
-               if ( $accum->lastNode === false ) {
-                       // nothing to add
-               } elseif ( $this->lastNode === false ) {
-                       $this->firstNode = $accum->firstNode;
-                       $this->lastNode = $accum->lastNode;
+                       $accum = [ $out ];
                } else {
-                       $this->lastNode->nextSibling = $accum->firstNode;
-                       $this->lastNode = $accum->lastNode;
+                       $accum = [];
                }
+               parent::__construct( $accum );
        }
 }
 
@@ -1050,130 +970,144 @@ class PPFrame_Hash implements PPFrame {
                        }
 
                        $newIterator = false;
+                       $contextName = false;
+                       $contextChildren = false;
 
                        if ( $contextNode === false ) {
                                // nothing to do
                        } elseif ( is_string( $contextNode ) ) {
                                $out .= $contextNode;
-                       } elseif ( is_array( $contextNode ) || $contextNode instanceof PPNode_Hash_Array ) {
+                       } elseif ( $contextNode instanceof PPNode_Hash_Array ) {
                                $newIterator = $contextNode;
                        } elseif ( $contextNode instanceof PPNode_Hash_Attr ) {
                                // No output
                        } elseif ( $contextNode instanceof PPNode_Hash_Text ) {
                                $out .= $contextNode->value;
                        } elseif ( $contextNode instanceof PPNode_Hash_Tree ) {
-                               if ( $contextNode->name == 'template' ) {
-                                       # Double-brace expansion
-                                       $bits = $contextNode->splitTemplate();
-                                       if ( $flags & PPFrame::NO_TEMPLATES ) {
-                                               $newIterator = $this->virtualBracketedImplode(
-                                                       '{{', '|', '}}',
-                                                       $bits['title'],
-                                                       $bits['parts']
-                                               );
-                                       } else {
-                                               $ret = $this->parser->braceSubstitution( $bits, $this );
-                                               if ( isset( $ret['object'] ) ) {
-                                                       $newIterator = $ret['object'];
-                                               } else {
-                                                       $out .= $ret['text'];
-                                               }
-                                       }
-                               } elseif ( $contextNode->name == 'tplarg' ) {
-                                       # Triple-brace expansion
-                                       $bits = $contextNode->splitTemplate();
-                                       if ( $flags & PPFrame::NO_ARGS ) {
-                                               $newIterator = $this->virtualBracketedImplode(
-                                                       '{{{', '|', '}}}',
-                                                       $bits['title'],
-                                                       $bits['parts']
-                                               );
-                                       } else {
-                                               $ret = $this->parser->argSubstitution( $bits, $this );
-                                               if ( isset( $ret['object'] ) ) {
-                                                       $newIterator = $ret['object'];
-                                               } else {
-                                                       $out .= $ret['text'];
-                                               }
-                                       }
-                               } elseif ( $contextNode->name == 'comment' ) {
-                                       # HTML-style comment
-                                       # Remove it in HTML, pre+remove and STRIP_COMMENTS modes
-                                       # Not in RECOVER_COMMENTS mode (msgnw) though.
-                                       if ( ( $this->parser->ot['html']
-                                               || ( $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() )
-                                               || ( $flags & PPFrame::STRIP_COMMENTS )
-                                               ) && !( $flags & PPFrame::RECOVER_COMMENTS )
-                                       ) {
-                                               $out .= '';
-                                       } elseif ( $this->parser->ot['wiki'] && !( $flags & PPFrame::RECOVER_COMMENTS ) ) {
-                                               # Add a strip marker in PST mode so that pstPass2() can
-                                               # run some old-fashioned regexes on the result.
-                                               # Not in RECOVER_COMMENTS mode (extractSections) though.
-                                               $out .= $this->parser->insertStripItem( $contextNode->firstChild->value );
+                               $contextName = $contextNode->name;
+                               $contextChildren = $contextNode->getRawChildren();
+                       } elseif ( is_array( $contextNode ) ) {
+                               // Node descriptor array
+                               if ( count( $contextNode ) !== 2 ) {
+                                       throw new MWException( __METHOD__.
+                                               ': found an array where a node descriptor should be' );
+                               }
+                               list( $contextName, $contextChildren ) = $contextNode;
+                       } else {
+                               throw new MWException( __METHOD__ . ': Invalid parameter type' );
+                       }
+
+                       // Handle node descriptor array or tree object
+                       if ( $contextName === false ) {
+                               // Not a node, already handled above
+                       } elseif ( $contextName[0] === '@' ) {
+                               // Attribute: no output
+                       } elseif ( $contextName === 'template' ) {
+                               # Double-brace expansion
+                               $bits = PPNode_Hash_Tree::splitRawTemplate( $contextChildren );
+                               if ( $flags & PPFrame::NO_TEMPLATES ) {
+                                       $newIterator = $this->virtualBracketedImplode(
+                                               '{{', '|', '}}',
+                                               $bits['title'],
+                                               $bits['parts']
+                                       );
+                               } else {
+                                       $ret = $this->parser->braceSubstitution( $bits, $this );
+                                       if ( isset( $ret['object'] ) ) {
+                                               $newIterator = $ret['object'];
                                        } else {
-                                               # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
-                                               $out .= $contextNode->firstChild->value;
+                                               $out .= $ret['text'];
                                        }
-                               } elseif ( $contextNode->name == 'ignore' ) {
-                                       # Output suppression used by <includeonly> etc.
-                                       # OT_WIKI will only respect <ignore> in substed templates.
-                                       # The other output types respect it unless NO_IGNORE is set.
-                                       # extractSections() sets NO_IGNORE and so never respects it.
-                                       if ( ( !isset( $this->parent ) && $this->parser->ot['wiki'] )
-                                               || ( $flags & PPFrame::NO_IGNORE )
-                                       ) {
-                                               $out .= $contextNode->firstChild->value;
+                               }
+                       } elseif ( $contextName === 'tplarg' ) {
+                               # Triple-brace expansion
+                               $bits = PPNode_Hash_Tree::splitRawTemplate( $contextChildren );
+                               if ( $flags & PPFrame::NO_ARGS ) {
+                                       $newIterator = $this->virtualBracketedImplode(
+                                               '{{{', '|', '}}}',
+                                               $bits['title'],
+                                               $bits['parts']
+                                       );
+                               } else {
+                                       $ret = $this->parser->argSubstitution( $bits, $this );
+                                       if ( isset( $ret['object'] ) ) {
+                                               $newIterator = $ret['object'];
                                        } else {
-                                               // $out .= '';
+                                               $out .= $ret['text'];
                                        }
-                               } elseif ( $contextNode->name == 'ext' ) {
-                                       # Extension tag
-                                       $bits = $contextNode->splitExt() + [ 'attr' => null, 'inner' => null, 'close' => null ];
-                                       if ( $flags & PPFrame::NO_TAGS ) {
-                                               $s = '<' . $bits['name']->firstChild->value;
-                                               if ( $bits['attr'] ) {
-                                                       $s .= $bits['attr']->firstChild->value;
-                                               }
-                                               if ( $bits['inner'] ) {
-                                                       $s .= '>' . $bits['inner']->firstChild->value;
-                                                       if ( $bits['close'] ) {
-                                                               $s .= $bits['close']->firstChild->value;
-                                                       }
-                                               } else {
-                                                       $s .= '/>';
-                                               }
-                                               $out .= $s;
-                                       } else {
-                                               $out .= $this->parser->extensionSubstitution( $bits, $this );
+                               }
+                       } elseif ( $contextName === 'comment' ) {
+                               # HTML-style comment
+                               # Remove it in HTML, pre+remove and STRIP_COMMENTS modes
+                               # Not in RECOVER_COMMENTS mode (msgnw) though.
+                               if ( ( $this->parser->ot['html']
+                                       || ( $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() )
+                                       || ( $flags & PPFrame::STRIP_COMMENTS )
+                                       ) && !( $flags & PPFrame::RECOVER_COMMENTS )
+                               ) {
+                                       $out .= '';
+                               } elseif ( $this->parser->ot['wiki'] && !( $flags & PPFrame::RECOVER_COMMENTS ) ) {
+                                       # Add a strip marker in PST mode so that pstPass2() can
+                                       # run some old-fashioned regexes on the result.
+                                       # Not in RECOVER_COMMENTS mode (extractSections) though.
+                                       $out .= $this->parser->insertStripItem( $contextChildren[0] );
+                               } else {
+                                       # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
+                                       $out .= $contextChildren[0];
+                               }
+                       } elseif ( $contextName === 'ignore' ) {
+                               # Output suppression used by <includeonly> etc.
+                               # OT_WIKI will only respect <ignore> in substed templates.
+                               # The other output types respect it unless NO_IGNORE is set.
+                               # extractSections() sets NO_IGNORE and so never respects it.
+                               if ( ( !isset( $this->parent ) && $this->parser->ot['wiki'] )
+                                       || ( $flags & PPFrame::NO_IGNORE )
+                               ) {
+                                       $out .= $contextChildren[0];
+                               } else {
+                                       // $out .= '';
+                               }
+                       } elseif ( $contextName === 'ext' ) {
+                               # Extension tag
+                               $bits = PPNode_Hash_Tree::splitRawExt( $contextChildren ) +
+                                       [ 'attr' => null, 'inner' => null, 'close' => null ];
+                               if ( $flags & PPFrame::NO_TAGS ) {
+                                       $s = '<' . $bits['name']->getFirstChild()->value;
+                                       if ( $bits['attr'] ) {
+                                               $s .= $bits['attr']->getFirstChild()->value;
                                        }
-                               } elseif ( $contextNode->name == 'h' ) {
-                                       # Heading
-                                       if ( $this->parser->ot['html'] ) {
-                                               # Expand immediately and insert heading index marker
-                                               $s = '';
-                                               for ( $node = $contextNode->firstChild; $node; $node = $node->nextSibling ) {
-                                                       $s .= $this->expand( $node, $flags );
+                                       if ( $bits['inner'] ) {
+                                               $s .= '>' . $bits['inner']->getFirstChild()->value;
+                                               if ( $bits['close'] ) {
+                                                       $s .= $bits['close']->getFirstChild()->value;
                                                }
-
-                                               $bits = $contextNode->splitHeading();
-                                               $titleText = $this->title->getPrefixedDBkey();
-                                               $this->parser->mHeadings[] = [ $titleText, $bits['i'] ];
-                                               $serial = count( $this->parser->mHeadings ) - 1;
-                                               $marker = Parser::MARKER_PREFIX . "-h-$serial-" . Parser::MARKER_SUFFIX;
-                                               $s = substr( $s, 0, $bits['level'] ) . $marker . substr( $s, $bits['level'] );
-                                               $this->parser->mStripState->addGeneral( $marker, '' );
-                                               $out .= $s;
                                        } else {
-                                               # Expand in virtual stack
-                                               $newIterator = $contextNode->getChildren();
+                                               $s .= '/>';
                                        }
+                                       $out .= $s;
+                               } else {
+                                       $out .= $this->parser->extensionSubstitution( $bits, $this );
+                               }
+                       } elseif ( $contextName === 'h' ) {
+                               # Heading
+                               if ( $this->parser->ot['html'] ) {
+                                       # Expand immediately and insert heading index marker
+                                       $s = $this->expand( $contextChildren, $flags );
+                                       $bits = PPNode_Hash_Tree::splitRawHeading( $contextChildren );
+                                       $titleText = $this->title->getPrefixedDBkey();
+                                       $this->parser->mHeadings[] = [ $titleText, $bits['i'] ];
+                                       $serial = count( $this->parser->mHeadings ) - 1;
+                                       $marker = Parser::MARKER_PREFIX . "-h-$serial-" . Parser::MARKER_SUFFIX;
+                                       $s = substr( $s, 0, $bits['level'] ) . $marker . substr( $s, $bits['level'] );
+                                       $this->parser->mStripState->addGeneral( $marker, '' );
+                                       $out .= $s;
                                } else {
-                                       # Generic recursive expansion
-                                       $newIterator = $contextNode->getChildren();
+                                       # Expand in virtual stack
+                                       $newIterator = $contextChildren;
                                }
                        } else {
-                               throw new MWException( __METHOD__ . ': Invalid parameter type' );
+                               # Generic recursive expansion
+                               $newIterator = $contextChildren;
                        }
 
                        if ( $newIterator !== false ) {
@@ -1689,17 +1623,85 @@ class PPCustomFrame_Hash extends PPFrame_Hash {
 class PPNode_Hash_Tree implements PPNode {
        // @codingStandardsIgnoreEnd
 
-       public $name, $firstChild, $lastChild, $nextSibling;
+       public $name;
+
+       /**
+        * The store array for children of this node. It is "raw" in the sense that
+        * nodes are two-element arrays ("descriptors") rather than PPNode_Hash_*
+        * objects.
+        */
+       private $rawChildren;
+
+       /**
+        * The store array for the siblings of this node, including this node itself.
+        */
+       private $store;
+
+       /**
+        * The index into $this->store which contains the descriptor of this node.
+        */
+       private $index;
+
+       /**
+        * The offset of the name within descriptors, used in some places for
+        * readability.
+        */
+       const NAME = 0;
+
+       /**
+        * The offset of the child list within descriptors, used in some places for
+        * readability.
+        */
+       const CHILDREN = 1;
+
+       /**
+        * Construct an object using the data from $store[$index]. The rest of the
+        * store array can be accessed via getNextSibling().
+        *
+        * @param array $store
+        * @param integer $index
+        */
+       public function __construct( array $store, $index ) {
+               $this->store = $store;
+               $this->index = $index;
+               list( $this->name, $this->rawChildren ) = $this->store[$index];
+       }
+
+       /**
+        * Construct an appropriate PPNode_Hash_* object with a class that depends
+        * on what is at the relevant store index.
+        *
+        * @param array $store
+        * @param integer $index
+        * @return PPNode_Hash_Tree|PPNode_Hash_Attr|PPNode_Hash_Text
+        */
+       public static function factory( array $store, $index ) {
+               if ( !isset( $store[$index] ) ) {
+                       return false;
+               }
 
-       public function __construct( $name ) {
-               $this->name = $name;
-               $this->firstChild = $this->lastChild = $this->nextSibling = false;
+               $descriptor = $store[$index];
+               if ( is_string( $descriptor ) ) {
+                       $class = 'PPNode_Hash_Text';
+               } elseif ( is_array( $descriptor ) ) {
+                       if ( $descriptor[self::NAME][0] === '@' ) {
+                               $class = 'PPNode_Hash_Attr';
+                       } else {
+                               $class = 'PPNode_Hash_Tree';
+                       }
+               } else {
+                       throw new MWException( __METHOD__.': invalid node descriptor' );
+               }
+               return new $class( $store, $index );
        }
 
+       /**
+        * Convert a node to XML, for debugging
+        */
        public function __toString() {
                $inner = '';
                $attribs = '';
-               for ( $node = $this->firstChild; $node; $node = $node->nextSibling ) {
+               for ( $node = $this->getFirstChild(); $node; $node = $node->getNextSibling() ) {
                        if ( $node instanceof PPNode_Hash_Attr ) {
                                $attribs .= ' ' . $node->name . '="' . htmlspecialchars( $node->value ) . '"';
                        } else {
@@ -1713,55 +1715,67 @@ class PPNode_Hash_Tree implements PPNode {
                }
        }
 
-       /**
-        * @param string $name
-        * @param string $text
-        * @return PPNode_Hash_Tree
-        */
-       public static function newWithText( $name, $text ) {
-               $obj = new self( $name );
-               $obj->addChild( new PPNode_Hash_Text( $text ) );
-               return $obj;
-       }
-
-       public function addChild( $node ) {
-               if ( $this->lastChild === false ) {
-                       $this->firstChild = $this->lastChild = $node;
-               } else {
-                       $this->lastChild->nextSibling = $node;
-                       $this->lastChild = $node;
-               }
-       }
-
        /**
         * @return PPNode_Hash_Array
         */
        public function getChildren() {
                $children = [];
-               for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
-                       $children[] = $child;
+               foreach ( $this->rawChildren as $i => $child ) {
+                       $children[] = self::factory( $this->rawChildren, $i );
                }
                return new PPNode_Hash_Array( $children );
        }
 
+       /**
+        * Get the first child, or false if there is none. Note that this will
+        * return a temporary proxy object: different instances will be returned
+        * if this is called more than once on the same node.
+        *
+        * @return PPNode_Hash_Tree|PPNode_Hash_Attr|PPNode_Hash_Text|boolean
+        */
        public function getFirstChild() {
-               return $this->firstChild;
+               if ( !isset( $this->rawChildren[0] ) ) {
+                       return false;
+               } else {
+                       return self::factory( $this->rawChildren, 0 );
+               }
        }
 
+       /**
+        * Get the next sibling, or false if there is none. Note that this will
+        * return a temporary proxy object: different instances will be returned
+        * if this is called more than once on the same node.
+        *
+        * @return PPNode_Hash_Tree|PPNode_Hash_Attr|PPNode_Hash_Text|boolean
+        */
        public function getNextSibling() {
-               return $this->nextSibling;
+               return self::factory( $this->store, $this->index + 1 );
        }
 
+       /**
+        * Get an array of the children with a given node name
+        *
+        * @param string $name
+        * @return PPNode_Hash_Array
+        */
        public function getChildrenOfType( $name ) {
                $children = [];
-               for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
-                       if ( isset( $child->name ) && $child->name === $name ) {
-                               $children[] = $child;
+               foreach ( $this->rawChildren as $i => $child ) {
+                       if ( is_array( $child ) && $child[self::NAME] === $name ) {
+                               $children[] = self::factory( $this->rawChildren, $i );
                        }
                }
                return new PPNode_Hash_Array( $children );
        }
 
+       /**
+        * Get the raw child array. For internal use.
+        * @return array
+        */
+       public function getRawChildren() {
+               return $this->rawChildren;
+       }
+
        /**
         * @return bool
         */
@@ -1794,20 +1808,27 @@ class PPNode_Hash_Tree implements PPNode {
         * @return array
         */
        public function splitArg() {
+               return self::splitRawArg( $this->rawChildren );
+       }
+
+       /**
+        * Like splitArg() but for a raw child array. For internal use only.
+        */
+       public static function splitRawArg( array $children ) {
                $bits = [];
-               for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
-                       if ( !isset( $child->name ) ) {
+               foreach ( $children as $i => $child ) {
+                       if ( !is_array( $child ) ) {
                                continue;
                        }
-                       if ( $child->name === 'name' ) {
-                               $bits['name'] = $child;
-                               if ( $child->firstChild instanceof PPNode_Hash_Attr
-                                       && $child->firstChild->name === 'index'
+                       if ( $child[self::NAME] === 'name' ) {
+                               $bits['name'] = new self( $children, $i );
+                               if ( isset( $child[self::CHILDREN][0][self::NAME] )
+                                       && $child[self::CHILDREN][0][self::NAME] === '@index'
                                ) {
-                                       $bits['index'] = $child->firstChild->value;
+                                       $bits['index'] = $child[self::CHILDREN][0][self::CHILDREN][0];
                                }
-                       } elseif ( $child->name === 'value' ) {
-                               $bits['value'] = $child;
+                       } elseif ( $child[self::NAME] === 'value' ) {
+                               $bits['value'] = new self( $children, $i );
                        }
                }
 
@@ -1828,19 +1849,31 @@ class PPNode_Hash_Tree implements PPNode {
         * @return array
         */
        public function splitExt() {
+               return self::splitRawExt( $this->rawChildren );
+       }
+
+       /**
+        * Like splitExt() but for a raw child array. For internal use only.
+        */
+       public static function splitRawExt( array $children ) {
                $bits = [];
-               for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
-                       if ( !isset( $child->name ) ) {
+               foreach ( $children as $i => $child ) {
+                       if ( !is_array( $child ) ) {
                                continue;
                        }
-                       if ( $child->name == 'name' ) {
-                               $bits['name'] = $child;
-                       } elseif ( $child->name == 'attr' ) {
-                               $bits['attr'] = $child;
-                       } elseif ( $child->name == 'inner' ) {
-                               $bits['inner'] = $child;
-                       } elseif ( $child->name == 'close' ) {
-                               $bits['close'] = $child;
+                       switch ( $child[self::NAME] ) {
+                       case 'name':
+                               $bits['name'] = new self( $children, $i );
+                               break;
+                       case 'attr':
+                               $bits['attr'] = new self( $children, $i );
+                               break;
+                       case 'inner':
+                               $bits['inner'] = new self( $children, $i );
+                               break;
+                       case 'close':
+                               $bits['close'] = new self( $children, $i );
+                               break;
                        }
                }
                if ( !isset( $bits['name'] ) ) {
@@ -1859,15 +1892,22 @@ class PPNode_Hash_Tree implements PPNode {
                if ( $this->name !== 'h' ) {
                        throw new MWException( 'Invalid h node passed to ' . __METHOD__ );
                }
+               return self::splitRawHeading( $this->rawChildren );
+       }
+
+       /**
+        * Like splitHeading() but for a raw child array. For internal use only.
+        */
+       public static function splitRawHeading( array $children ) {
                $bits = [];
-               for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
-                       if ( !isset( $child->name ) ) {
+               foreach ( $children as $i => $child ) {
+                       if ( !is_array( $child ) ) {
                                continue;
                        }
-                       if ( $child->name == 'i' ) {
-                               $bits['i'] = $child->value;
-                       } elseif ( $child->name == 'level' ) {
-                               $bits['level'] = $child->value;
+                       if ( $child[self::NAME] === '@i' ) {
+                               $bits['i'] = $child[self::CHILDREN][0];
+                       } elseif ( $child[self::NAME] === '@level' ) {
+                               $bits['level'] = $child[self::CHILDREN][0];
                        }
                }
                if ( !isset( $bits['i'] ) ) {
@@ -1883,20 +1923,29 @@ class PPNode_Hash_Tree implements PPNode {
         * @return array
         */
        public function splitTemplate() {
+               return self::splitRawTemplate( $this->rawChildren );
+       }
+
+       /**
+        * Like splitTemplate() but for a raw child array. For internal use only.
+        */
+       public static function splitRawTemplate( array $children ) {
                $parts = [];
                $bits = [ 'lineStart' => '' ];
-               for ( $child = $this->firstChild; $child; $child = $child->nextSibling ) {
-                       if ( !isset( $child->name ) ) {
+               foreach ( $children as $i => $child ) {
+                       if ( !is_array( $child ) ) {
                                continue;
                        }
-                       if ( $child->name == 'title' ) {
-                               $bits['title'] = $child;
-                       }
-                       if ( $child->name == 'part' ) {
-                               $parts[] = $child;
-                       }
-                       if ( $child->name == 'lineStart' ) {
+                       switch ( $child[self::NAME] ) {
+                       case 'title':
+                               $bits['title'] = new self( $children, $i );
+                               break;
+                       case 'part':
+                               $parts[] = new self( $children, $i );
+                               break;
+                       case '@lineStart':
                                $bits['lineStart'] = '1';
+                               break;
                        }
                }
                if ( !isset( $bits['title'] ) ) {
@@ -1914,13 +1963,23 @@ class PPNode_Hash_Tree implements PPNode {
 class PPNode_Hash_Text implements PPNode {
        // @codingStandardsIgnoreEnd
 
-       public $value, $nextSibling;
+       public $value;
+       private $store, $index;
 
-       public function __construct( $value ) {
-               if ( is_object( $value ) ) {
+       /**
+        * Construct an object using the data from $store[$index]. The rest of the
+        * store array can be accessed via getNextSibling().
+        *
+        * @param array $store
+        * @param integer $index
+        */
+       public function __construct( array $store, $index ) {
+               $this->value = $store[$index];
+               if ( !is_scalar( $this->value ) ) {
                        throw new MWException( __CLASS__ . ' given object instead of string' );
                }
-               $this->value = $value;
+               $this->store = $store;
+               $this->index = $index;
        }
 
        public function __toString() {
@@ -1928,7 +1987,7 @@ class PPNode_Hash_Text implements PPNode {
        }
 
        public function getNextSibling() {
-               return $this->nextSibling;
+               return PPNode_Hash_Tree::factory( $this->store, $this->index + 1 );
        }
 
        public function getChildren() {
@@ -1975,7 +2034,7 @@ class PPNode_Hash_Text implements PPNode {
 class PPNode_Hash_Array implements PPNode {
        // @codingStandardsIgnoreEnd
 
-       public $value, $nextSibling;
+       public $value;
 
        public function __construct( $value ) {
                $this->value = $value;
@@ -1998,7 +2057,7 @@ class PPNode_Hash_Array implements PPNode {
        }
 
        public function getNextSibling() {
-               return $this->nextSibling;
+               return false;
        }
 
        public function getChildren() {
@@ -2033,11 +2092,25 @@ class PPNode_Hash_Array implements PPNode {
 class PPNode_Hash_Attr implements PPNode {
        // @codingStandardsIgnoreEnd
 
-       public $name, $value, $nextSibling;
+       public $name, $value;
+       private $store, $index;
 
-       public function __construct( $name, $value ) {
-               $this->name = $name;
-               $this->value = $value;
+       /**
+        * Construct an object using the data from $store[$index]. The rest of the
+        * store array can be accessed via getNextSibling().
+        *
+        * @param array $store
+        * @param integer $index
+        */
+       public function __construct( array $store, $index ) {
+               $descriptor = $store[$index];
+               if ( $descriptor[PPNode_Hash_Tree::NAME][0] !== '@' ) {
+                       throw new MWException( __METHOD__.': invalid name in attribute descriptor' );
+               }
+               $this->name = substr( $descriptor[PPNode_Hash_Tree::NAME], 1 );
+               $this->value = $descriptor[PPNode_Hash_Tree::CHILDREN][0];
+               $this->store = $store;
+               $this->index = $index;
        }
 
        public function __toString() {
@@ -2049,7 +2122,7 @@ class PPNode_Hash_Attr implements PPNode {
        }
 
        public function getNextSibling() {
-               return $this->nextSibling;
+               return PPNode_Hash_Tree::factory( $this->store, $this->index + 1 );
        }
 
        public function getChildren() {
index f2c59d2..5e8db07 100644 (file)
@@ -246,7 +246,7 @@ LUA;
                        } elseif ( $slot === 'QUEUE_WAIT' ) {
                                // This process is now registered as waiting
                                $keys = ( $doWakeup == self::AWAKE_ALL )
-                                       // Wait for an open slot or wake-up signal (preferring the later)
+                                       // Wait for an open slot or wake-up signal (preferring the latter)
                                        ? [ $this->getWakeupListKey(), $this->getSlotListKey() ]
                                        // Just wait for an actual pool slot
                                        : [ $this->getSlotListKey() ];
@@ -292,7 +292,7 @@ LUA;
                local rMaxWorkers,rMaxQueue,rTimeout,rExpiry,rSess,rTime = unpack(ARGV)
                -- Initialize if the "next release" time sorted-set is empty. The slot key
                -- itself is empty if all slots are busy or when nothing is initialized.
-               -- If the list is empty but the set is not, then it is the later case.
+               -- If the list is empty but the set is not, then it is the latter case.
                -- For sanity, if the list exists but not the set, then reset everything.
                if redis.call('exists',kSlotsNextRelease) == 0 then
                        redis.call('del',kSlots)
index d787edb..32daeed 100644 (file)
@@ -25,7 +25,7 @@
  * Class for handling function-scope profiling
  *
  * @since 1.22
- * @deprecated 1.25 No-op now
+ * @deprecated since 1.25 No-op now
  */
 class ProfileSection {
        /**
index 50a77ec..cc71630 100644 (file)
@@ -42,7 +42,7 @@ function wfGetRusage() {
 /**
  * Begin profiling of a function
  * @param string $functionname Name of the function we will profile
- * @deprecated 1.25
+ * @deprecated since 1.25
  */
 function wfProfileIn( $functionname ) {
 }
@@ -50,7 +50,7 @@ function wfProfileIn( $functionname ) {
 /**
  * Stop profiling of a function
  * @param string $functionname Name of the function we have profiled
- * @deprecated 1.25
+ * @deprecated since 1.25
  */
 function wfProfileOut( $functionname = 'missing' ) {
 }
index 3fe9cdd..1017e44 100644 (file)
@@ -40,10 +40,6 @@ class ProfilerStub extends Profiler {
        public function close() {
        }
 
-       public function getCurrentSection() {
-               return '';
-       }
-
        public function logData() {
        }
 
index 04ec841..65ac6e6 100644 (file)
@@ -46,8 +46,6 @@ class SectionProfiler {
        protected $collateOnly = true;
        /** @var array Cache of a standard broken collation entry */
        protected $errorEntry;
-       /** @var callable Cache of a profile out callback */
-       protected $profileOutCallback;
 
        /**
         * @param array $params
@@ -55,9 +53,6 @@ class SectionProfiler {
        public function __construct( array $params = [] ) {
                $this->errorEntry = $this->getErrorEntry();
                $this->collateOnly = empty( $params['trace'] );
-               $this->profileOutCallback = function ( $profiler, $section ) {
-                       $profiler->profileOutInternal( $section );
-               };
        }
 
        /**
index 1aba71c..bf26573 100644 (file)
@@ -38,6 +38,8 @@ class TransactionProfiler implements LoggerAwareInterface {
        protected $dbLockThreshold = 3.0;
        /** @var float Seconds */
        protected $eventThreshold = .25;
+       /** @var bool */
+       protected $silenced = false;
 
        /** @var array transaction ID => (write start time, list of DBs involved) */
        protected $dbTrxHoldingLocks = [];
@@ -77,6 +79,14 @@ class TransactionProfiler implements LoggerAwareInterface {
                $this->logger = $logger;
        }
 
+       /**
+        * @param bool $value
+        * @since 1.28
+        */
+       public function setSilenced( $value ) {
+               $this->silenced = $value;
+       }
+
        /**
         * Set performance expectations
         *
@@ -302,6 +312,10 @@ class TransactionProfiler implements LoggerAwareInterface {
         * @param string|float|int $actual [optional]
         */
        protected function reportExpectationViolated( $expect, $query, $actual = null ) {
+               if ( $this->silenced ) {
+                       return;
+               }
+
                $n = $this->expect[$expect];
                $by = $this->expectBy[$expect];
                $actual = ( $actual !== null ) ? " (actual: $actual)" : "";
index f1f7c2e..5555e8b 100644 (file)
@@ -10,7 +10,6 @@ class ExtensionProcessor implements Processor {
        protected static $globalSettings = [
                'ResourceLoaderSources',
                'ResourceLoaderLESSVars',
-               'ResourceLoaderLESSImportPaths',
                'DefaultUserOptions',
                'HiddenPrefs',
                'GroupPermissions',
@@ -110,6 +109,7 @@ class ExtensionProcessor implements Processor {
                'MessagesDirs',
                'type',
                'config',
+               'config_prefix',
                'ParserTestFiles',
                'AutoloadClasses',
                'manifest_version',
@@ -162,9 +162,14 @@ class ExtensionProcessor implements Processor {
         * @return array
         */
        public function extractInfo( $path, array $info, $version ) {
-               $this->extractConfig( $info );
-               $this->extractHooks( $info );
                $dir = dirname( $path );
+               if ( $version === 2 ) {
+                       $this->extractConfig2( $info, $dir );
+               } else {
+                       // $version === 1
+                       $this->extractConfig1( $info );
+               }
+               $this->extractHooks( $info );
                $this->extractExtensionMessagesFiles( $dir, $info );
                $this->extractMessagesDirs( $dir, $info );
                $this->extractNamespaces( $info );
@@ -238,7 +243,10 @@ class ExtensionProcessor implements Processor {
                        foreach ( $info['namespaces'] as $ns ) {
                                $id = $ns['id'];
                                $this->defines[$ns['constant']] = $id;
-                               $this->attributes['ExtensionNamespaces'][$id] = $ns['name'];
+                               if ( !( isset( $ns['conditional'] ) && $ns['conditional'] ) ) {
+                                       // If it is not conditional, register it
+                                       $this->attributes['ExtensionNamespaces'][$id] = $ns['name'];
+                               }
                                if ( isset( $ns['gender'] ) ) {
                                        $this->globals['wgExtraGenderNamespaces'][$id] = $ns['gender'];
                                }
@@ -350,12 +358,12 @@ class ExtensionProcessor implements Processor {
        }
 
        /**
-        * Set configuration settings
+        * Set configuration settings for manifest_version == 1
         * @todo In the future, this should be done via Config interfaces
         *
         * @param array $info
         */
-       protected function extractConfig( array $info ) {
+       protected function extractConfig1( array $info ) {
                if ( isset( $info['config'] ) ) {
                        if ( isset( $info['config']['_prefix'] ) ) {
                                $prefix = $info['config']['_prefix'];
@@ -371,6 +379,33 @@ class ExtensionProcessor implements Processor {
                }
        }
 
+       /**
+        * Set configuration settings for manifest_version == 2
+        * @todo In the future, this should be done via Config interfaces
+        *
+        * @param array $info
+        * @param string $dir
+        */
+       protected function extractConfig2( array $info, $dir ) {
+               if ( isset( $info['config_prefix'] ) ) {
+                       $prefix = $info['config_prefix'];
+               } else {
+                       $prefix = 'wg';
+               }
+               if ( isset( $info['config'] ) ) {
+                       foreach ( $info['config'] as $key => $data ) {
+                               $value = $data['value'];
+                               if ( isset( $value['merge_strategy'] ) ) {
+                                       $value[ExtensionRegistry::MERGE_STRATEGY] = $data['merge_strategy'];
+                               }
+                               if ( isset( $data['path'] ) && $data['path'] ) {
+                                       $value = "$dir/$value";
+                               }
+                               $this->globals["$prefix$key"] = $value;
+                       }
+               }
+       }
+
        protected function extractParserTestFiles( $dir, array $info ) {
                if ( isset( $info['ParserTestFiles'] ) ) {
                        foreach ( $info['ParserTestFiles'] as $path ) {
index dc53ca4..3bec457 100644 (file)
@@ -19,7 +19,7 @@ class ExtensionRegistry {
        /**
         * Version of the highest supported manifest version
         */
-       const MANIFEST_VERSION = 1;
+       const MANIFEST_VERSION = 2;
 
        /**
         * Version of the oldest supported manifest version
index 1db9ce5..418d17f 100644 (file)
@@ -121,7 +121,7 @@ class DerivativeResourceLoaderContext extends ResourceLoaderContext {
        }
 
        /**
-        * @param string $user
+        * @param string|null $user
         */
        public function setUser( $user ) {
                $this->user = $user;
index 09535b7..97a86c3 100644 (file)
@@ -56,17 +56,17 @@ class ResourceLoader implements LoggerAwareInterface {
        protected $moduleInfos = [];
 
        /** @var Config $config */
-       private $config;
+       protected $config;
 
        /**
         * Associative array mapping framework ids to a list of names of test suite modules
-        * like array( 'qunit' => array( 'mediawiki.tests.qunit.suites', 'ext.foo.tests', .. ), .. )
+        * like [ 'qunit' => [ 'mediawiki.tests.qunit.suites', 'ext.foo.tests', ... ], ... ]
         * @var array
         */
        protected $testModuleNames = [];
 
        /**
-        * E.g. array( 'source-id' => 'http://.../load.php' )
+        * E.g. [ 'source-id' => 'http://.../load.php' ]
         * @var array
         */
        protected $sources = [];
@@ -109,7 +109,7 @@ class ResourceLoader implements LoggerAwareInterface {
                        // Or else Database*::select() will explode, plus it's cheaper!
                        return;
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $skin = $context->getSkin();
                $lang = $context->getLanguage();
 
@@ -140,6 +140,9 @@ class ResourceLoader implements LoggerAwareInterface {
                        }
                }
 
+               // Batched version of ResourceLoaderWikiModule::getTitleInfo
+               ResourceLoaderWikiModule::preloadTitleInfo( $context, $dbr, $moduleNames );
+
                // Prime in-object cache for message blobs for modules with messages
                $modules = [];
                foreach ( $moduleNames as $name ) {
@@ -241,7 +244,7 @@ class ResourceLoader implements LoggerAwareInterface {
                $this->config = $config;
 
                // Add 'local' source first
-               $this->addSource( 'local', wfScript( 'load' ) );
+               $this->addSource( 'local', $config->get( 'LoadScript' ) );
 
                // Add other sources
                $this->addSource( $config->get( 'ResourceLoaderSources' ) );
@@ -433,7 +436,7 @@ class ResourceLoader implements LoggerAwareInterface {
         *
         * Source IDs are typically the same as the Wiki ID or database name (e.g. lowercase a-z).
         *
-        * @param array|string $id Source ID (string), or array( id1 => loadUrl, id2 => loadUrl, ... )
+        * @param array|string $id Source ID (string), or [ id1 => loadUrl, id2 => loadUrl, ... ]
         * @param string|array $loadUrl load.php url (string), or array with loadUrl key for
         *  backwards-compatibility.
         * @throws MWException
@@ -573,7 +576,7 @@ class ResourceLoader implements LoggerAwareInterface {
        /**
         * Get the list of sources.
         *
-        * @return array Like array( id => load.php url, .. )
+        * @return array Like [ id => load.php url, ... ]
         */
        public function getSources() {
                return $this->sources;
@@ -601,10 +604,8 @@ class ResourceLoader implements LoggerAwareInterface {
         * @return string Hash
         */
        public static function makeHash( $value ) {
-               // Use base64 to output more entropy in a more compact string (default hex is only base16).
-               // The first 8 chars of a base64 encoded digest represent the same binary as
-               // the first 12 chars of a hex encoded digest.
-               return substr( base64_encode( sha1( $value, true ) ), 0, 8 );
+               $hash = hash( 'fnv132', $value );
+               return Wikimedia\base_convert( $hash, 16, 36, 7 );
        }
 
        /**
@@ -612,17 +613,49 @@ class ResourceLoader implements LoggerAwareInterface {
         *
         * @since 1.26
         * @param ResourceLoaderContext $context
-        * @param array $modules List of ResourceLoaderModule objects
+        * @param string[] $modules List of known module names
         * @return string Hash
         */
-       public function getCombinedVersion( ResourceLoaderContext $context, array $modules ) {
-               if ( !$modules ) {
+       public function getCombinedVersion( ResourceLoaderContext $context, array $moduleNames ) {
+               if ( !$moduleNames ) {
                        return '';
                }
                $hashes = array_map( function ( $module ) use ( $context ) {
                        return $this->getModule( $module )->getVersionHash( $context );
-               }, $modules );
-               return self::makeHash( implode( $hashes ) );
+               }, $moduleNames );
+               return self::makeHash( implode( '', $hashes ) );
+       }
+
+       /**
+        * Get the expected value of the 'version' query parameter.
+        *
+        * This is used by respond() to set a short Cache-Control header for requests with
+        * information newer than the current server has. This avoids pollution of edge caches.
+        * Typically during deployment. (T117587)
+        *
+        * This MUST match return value of `mw.loader#getCombinedVersion()` client-side.
+        *
+        * @since 1.28
+        * @param ResourceLoaderContext $context
+        * @param string[] $modules List of module names
+        * @return string Hash
+        */
+       public function makeVersionQuery( ResourceLoaderContext $context ) {
+               // As of MediaWiki 1.28, the server and client use the same algorithm for combining
+               // version hashes. There is no technical reason for this to be same, and for years the
+               // implementations differed. If getCombinedVersion in PHP (used for StartupModule and
+               // E-Tag headers) differs in the future from getCombinedVersion in JS (used for 'version'
+               // query parameter), then this method must continue to match the JS one.
+               $moduleNames = [];
+               foreach ( $context->getModules() as $name ) {
+                       if ( !$this->getModule( $name ) ) {
+                               // If a versioned request contains a missing module, the version is a mismatch
+                               // as the client considered a module (and version) we don't have.
+                               return '';
+                       }
+                       $moduleNames[] = $name;
+               }
+               return $this->getCombinedVersion( $context, $moduleNames );
        }
 
        /**
@@ -761,10 +794,14 @@ class ResourceLoader implements LoggerAwareInterface {
         */
        protected function sendResponseHeaders( ResourceLoaderContext $context, $etag, $errors ) {
                $rlMaxage = $this->config->get( 'ResourceLoaderMaxage' );
-               // If a version wasn't specified we need a shorter expiry time for updates
-               // to propagate to clients quickly
-               // If there were errors, we also need a shorter expiry time so we can recover quickly
-               if ( is_null( $context->getVersion() ) || $errors ) {
+               // Use a short cache expiry so that updates propagate to clients quickly, if:
+               // - No version specified (shared resources, e.g. stylesheets)
+               // - There were errors (recover quickly)
+               // - Version mismatch (T117587, T47877)
+               if ( is_null( $context->getVersion() )
+                       || $errors
+                       || $context->getVersion() !== $this->makeVersionQuery( $context )
+               ) {
                        $maxage = $rlMaxage['unversioned']['client'];
                        $smaxage = $rlMaxage['unversioned']['server'];
                // If a version was specified we can use a longer expiry time since changing
@@ -859,7 +896,7 @@ class ResourceLoader implements LoggerAwareInterface {
                $good = $fileCache->isCacheGood( wfTimestamp( TS_MW, time() - $maxage ) );
                if ( !$good ) {
                        try { // RL always hits the DB on file cache miss...
-                               wfGetDB( DB_SLAVE );
+                               wfGetDB( DB_REPLICA );
                        } catch ( DBConnectionError $e ) { // ...check if we need to fallback to cache
                                $good = $fileCache->isCacheGood(); // cache existence check
                        }
@@ -1175,7 +1212,7 @@ MESSAGE;
         *    - ResourceLoader::makeLoaderStateScript( $name, $state ):
         *         Set the state of a single module called $name to $state
         *
-        *    - ResourceLoader::makeLoaderStateScript( array( $name => $state, ... ) ):
+        *    - ResourceLoader::makeLoaderStateScript( [ $name => $state, ... ] ):
         *         Set the state of modules with the given names to the given states
         *
         * @param string $name
@@ -1236,9 +1273,9 @@ MESSAGE;
         * Values considered empty:
         *
         * - null
-        * - array()
+        * - []
         * - new XmlJsCode( '{}' )
-        * - new stdClass() // (object) array()
+        * - new stdClass() // (object) []
         *
         * @param Array $array
         */
@@ -1266,14 +1303,14 @@ MESSAGE;
         *     ):
         *        Register a single module.
         *
-        *   - ResourceLoader::makeLoaderRegisterScript( array( $name1, $name2 ) ):
+        *   - ResourceLoader::makeLoaderRegisterScript( [ $name1, $name2 ] ):
         *        Register modules with the given names.
         *
-        *   - ResourceLoader::makeLoaderRegisterScript( array(
-        *        array( $name1, $version1, $dependencies1, $group1, $source1, $skip1 ),
-        *        array( $name2, $version2, $dependencies1, $group2, $source2, $skip2 ),
+        *   - ResourceLoader::makeLoaderRegisterScript( [
+        *        [ $name1, $version1, $dependencies1, $group1, $source1, $skip1 ],
+        *        [ $name2, $version2, $dependencies1, $group2, $source2, $skip2 ],
         *        ...
-        *     ) ):
+        *     ] ):
         *        Registers modules with the given names and parameters.
         *
         * @param string $name Module name
@@ -1331,14 +1368,14 @@ MESSAGE;
         *   - ResourceLoader::makeLoaderSourcesScript( $id, $properties ):
         *       Register a single source
         *
-        *   - ResourceLoader::makeLoaderSourcesScript( array( $id1 => $loadUrl, $id2 => $loadUrl, ... ) );
+        *   - ResourceLoader::makeLoaderSourcesScript( [ $id1 => $loadUrl, $id2 => $loadUrl, ... ] );
         *       Register sources with the given IDs and properties.
         *
         * @param string $id Source ID
-        * @param array $properties Source properties (see addSource())
+        * @param string $loadUrl load.php url
         * @return string
         */
-       public static function makeLoaderSourcesScript( $id, $properties = null ) {
+       public static function makeLoaderSourcesScript( $id, $loadUrl = null ) {
                if ( is_array( $id ) ) {
                        return Xml::encodeJsCall(
                                'mw.loader.addSource',
@@ -1348,7 +1385,7 @@ MESSAGE;
                } else {
                        return Xml::encodeJsCall(
                                'mw.loader.addSource',
-                               [ $id, $properties ],
+                               [ $id, $loadUrl ],
                                ResourceLoader::inDebugMode()
                        );
                }
@@ -1390,26 +1427,27 @@ MESSAGE;
         * the given value.
         *
         * @param array $configuration List of configuration values keyed by variable name
+        * @param bool $pretty Pretty-print with extra whitespace
         * @return string
         */
-       public static function makeConfigSetScript( array $configuration ) {
+       public static function makeConfigSetScript( array $configuration, $pretty = null ) {
                return Xml::encodeJsCall(
                        'mw.config.set',
                        [ $configuration ],
-                       ResourceLoader::inDebugMode()
+                       ( $pretty === null ) ? ResourceLoader::inDebugMode() : $pretty
                );
        }
 
        /**
         * Convert an array of module names to a packed query string.
         *
-        * For example, array( 'foo.bar', 'foo.baz', 'bar.baz', 'bar.quux' )
+        * For example, [ 'foo.bar', 'foo.baz', 'bar.baz', 'bar.quux' ]
         * becomes 'foo.bar,baz|bar.baz,quux'
         * @param array $modules List of module names (strings)
         * @return string Packed query string
         */
        public static function makePackedModulesString( $modules ) {
-               $groups = []; // array( prefix => array( suffixes ) )
+               $groups = []; // [ prefix => [ suffixes ] ]
                foreach ( $modules as $module ) {
                        $pos = strrpos( $module, '.' );
                        $prefix = $pos === false ? '' : substr( $module, 0, $pos );
@@ -1470,34 +1508,6 @@ MESSAGE;
                return wfAppendQuery( $script, $query );
        }
 
-       /**
-        * Build a load.php URL
-        * @deprecated since 1.24 Use createLoaderURL() instead
-        * @param array $modules Array of module names (strings)
-        * @param string $lang Language code
-        * @param string $skin Skin name
-        * @param string|null $user User name. If null, the &user= parameter is omitted
-        * @param string|null $version Versioning timestamp
-        * @param bool $debug Whether the request should be in debug mode
-        * @param string|null $only &only= parameter
-        * @param bool $printable Printable mode
-        * @param bool $handheld Handheld mode
-        * @param array $extraQuery Extra query parameters to add
-        * @return string URL to load.php. May be protocol-relative if $wgLoadScript is, too.
-        */
-       public static function makeLoaderURL( $modules, $lang, $skin, $user = null,
-               $version = null, $debug = false, $only = null, $printable = false,
-               $handheld = false, $extraQuery = []
-       ) {
-               global $wgLoadScript;
-
-               $query = self::makeLoaderQuery( $modules, $lang, $skin, $user, $version, $debug,
-                       $only, $printable, $handheld, $extraQuery
-               );
-
-               return wfAppendQuery( $wgLoadScript, $query );
-       }
-
        /**
         * Helper for createLoaderURL()
         *
@@ -1507,7 +1517,7 @@ MESSAGE;
         * @param array $extraQuery
         * @return array
         */
-       public static function createLoaderQuery( ResourceLoaderContext $context, $extraQuery = [] ) {
+       protected static function createLoaderQuery( ResourceLoaderContext $context, $extraQuery = [] ) {
                return self::makeLoaderQuery(
                        $context->getModules(),
                        $context->getLanguage(),
@@ -1524,7 +1534,7 @@ MESSAGE;
 
        /**
         * Build a query array (array representation of query string) for load.php. Helper
-        * function for makeLoaderURL().
+        * function for createLoaderURL().
         *
         * @param array $modules
         * @param string $lang
@@ -1607,7 +1617,6 @@ MESSAGE;
                        array_fill_keys( $this->config->get( 'ResourceLoaderLESSImportPaths' ), '' )
                );
                $parser->SetOption( 'relativeUrls', false );
-               $parser->SetCacheDir( $this->config->get( 'CacheDirectory' ) ?: wfTempDir() );
 
                return $parser;
        }
diff --git a/includes/resourceloader/ResourceLoaderClientHtml.php b/includes/resourceloader/ResourceLoaderClientHtml.php
new file mode 100644 (file)
index 0000000..5729218
--- /dev/null
@@ -0,0 +1,493 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+use WrappedString\WrappedStringList;
+
+/**
+ * Bootstrap a ResourceLoader client on an HTML page.
+ *
+ * @since 1.28
+ */
+class ResourceLoaderClientHtml {
+
+       /** @var ResourceLoaderContext */
+       private $context;
+
+       /** @var ResourceLoader */
+       private $resourceLoader;
+
+       /** @var string|null */
+       private $target;
+
+       /** @var array */
+       private $config = [];
+
+       /** @var array */
+       private $modules = [];
+
+       /** @var array */
+       private $moduleStyles = [];
+
+       /** @var array */
+       private $moduleScripts = [];
+
+       /** @var array */
+       private $exemptStates = [];
+
+       /** @var array */
+       private $data;
+
+       /**
+        * @param ResourceLoaderContext $context
+        * @param aray $target [optional] Custom 'target' parameter for the startup module
+        */
+       public function __construct( ResourceLoaderContext $context, $target = null ) {
+               $this->context = $context;
+               $this->resourceLoader = $context->getResourceLoader();
+               $this->target = $target;
+       }
+
+       /**
+        * Set mw.config variables.
+        *
+        * @param array $vars Array of key/value pairs
+        */
+       public function setConfig( array $vars ) {
+               foreach ( $vars as $key => $value ) {
+                       $this->config[$key] = $value;
+               }
+       }
+
+       /**
+        * Ensure one or more modules are loaded.
+        *
+        * @param array $modules Array of module names
+        */
+       public function setModules( array $modules ) {
+               $this->modules = $modules;
+       }
+
+       /**
+        * Ensure the styles of one or more modules are loaded.
+        *
+        * @deprecated since 1.28
+        * @param array $modules Array of module names
+        */
+       public function setModuleStyles( array $modules ) {
+               $this->moduleStyles = $modules;
+       }
+
+       /**
+        * Ensure the scripts of one or more modules are loaded.
+        *
+        * @deprecated since 1.28
+        * @param array $modules Array of module names
+        */
+       public function setModuleScripts( array $modules ) {
+               $this->moduleScripts = $modules;
+       }
+
+       /**
+        * Set state of special modules that are handled by the caller manually.
+        *
+        * See OutputPage::buildExemptModules() for use cases.
+        *
+        * @param array $modules Module state keyed by module name
+        */
+       public function setExemptStates( array $states ) {
+               $this->exemptStates = $states;
+       }
+
+       /**
+        * @return array
+        */
+       private function getData() {
+               if ( $this->data ) {
+                       // @codeCoverageIgnoreStart
+                       return $this->data;
+                       // @codeCoverageIgnoreEnd
+               }
+
+               $rl = $this->resourceLoader;
+               $data = [
+                       'states' => [
+                               // moduleName => state
+                       ],
+                       'general' => [
+                               // position => [ moduleName ]
+                               'top' => [],
+                               'bottom' => [],
+                       ],
+                       'styles' => [
+                               // moduleName
+                       ],
+                       'scripts' => [
+                               // position => [ moduleName ]
+                               'top' => [],
+                               'bottom' => [],
+                       ],
+                       // Embedding for private modules
+                       'embed' => [
+                               'styles' => [],
+                               'general' => [
+                                       'top' => [],
+                                       'bottom' => [],
+                               ],
+                       ],
+
+               ];
+
+               foreach ( $this->modules as $name ) {
+                       $module = $rl->getModule( $name );
+                       if ( !$module ) {
+                               continue;
+                       }
+
+                       $group = $module->getGroup();
+                       $position = $module->getPosition();
+
+                       if ( $group === 'private' ) {
+                               // Embed via mw.loader.implement per T36907.
+                               $data['embed']['general'][$position][] = $name;
+                               // Avoid duplicate request from mw.loader
+                               $data['states'][$name] = 'loading';
+                       } else {
+                               // Load via mw.loader.load()
+                               $data['general'][$position][] = $name;
+                       }
+               }
+
+               foreach ( $this->moduleStyles as $name ) {
+                       $module = $rl->getModule( $name );
+                       if ( !$module ) {
+                               continue;
+                       }
+
+                       if ( $module->getType() !== ResourceLoaderModule::LOAD_STYLES ) {
+                               $logger = $rl->getLogger();
+                               $logger->debug( 'Unexpected general module "{module}" in styles queue.', [
+                                       'module' => $name,
+                               ] );
+                       } else {
+                               // Stylesheet doesn't trigger mw.loader callback.
+                               // Set "ready" state to allow dependencies and avoid duplicate requests. (T87871)
+                               $data['states'][$name] = 'ready';
+                       }
+
+                       $group = $module->getGroup();
+                       $context = $this->getContext( $group, ResourceLoaderModule::TYPE_STYLES );
+                       if ( $module->isKnownEmpty( $context ) ) {
+                               // Avoid needless request for empty module
+                               $data['states'][$name] = 'ready';
+                       } else {
+                               if ( $group === 'private' ) {
+                                       // Embed via style element
+                                       $data['embed']['styles'][] = $name;
+                                       // Avoid duplicate request from mw.loader
+                                       $data['states'][$name] = 'ready';
+                               } else {
+                                       // Load from load.php?only=styles via <link rel=stylesheet>
+                                       $data['styles'][] = $name;
+                               }
+                       }
+               }
+
+               foreach ( $this->moduleScripts as $name ) {
+                       $module = $rl->getModule( $name );
+                       if ( !$module ) {
+                               continue;
+                       }
+
+                       $group = $module->getGroup();
+                       $position = $module->getPosition();
+                       $context = $this->getContext( $group, ResourceLoaderModule::TYPE_SCRIPTS );
+                       if ( $module->isKnownEmpty( $context ) ) {
+                               // Avoid needless request for empty module
+                               $data['states'][$name] = 'ready';
+                       } else {
+                               // Load from load.php?only=scripts via <script src></script>
+                               $data['scripts'][$position][] = $name;
+
+                               // Avoid duplicate request from mw.loader
+                               $data['states'][$name] = 'loading';
+                       }
+               }
+
+               return $data;
+       }
+
+       /**
+        * @return array Attribute key-value pairs for the HTML document element
+        */
+       public function getDocumentAttributes() {
+               return [ 'class' => 'client-nojs' ];
+       }
+
+       /**
+        * The order of elements in the head is as follows:
+        * - Inline scripts.
+        * - Stylesheets.
+        * - Async external script-src.
+        *
+        * Reasons:
+        * - Script execution may be blocked on preceeding stylesheets.
+        * - Async scripts are not blocked on stylesheets.
+        * - Inline scripts can't be asynchronous.
+        * - For styles, earlier is better.
+        *
+        * @return string|WrappedStringList HTML
+        */
+       public function getHeadHtml() {
+               $data = $this->getData();
+               $chunks = [];
+
+               // Change "client-nojs" class to client-js. This allows easy toggling of UI components.
+               // This happens synchronously on every page view to avoid flashes of wrong content.
+               // See also #getDocumentAttributes() and /resources/src/startup.js.
+               $chunks[] = Html::inlineScript(
+                       'document.documentElement.className = document.documentElement.className'
+                       . '.replace( /(^|\s)client-nojs(\s|$)/, "$1client-js$2" );'
+               );
+
+               // Inline RLQ: Set page variables
+               if ( $this->config ) {
+                       $chunks[] = ResourceLoader::makeInlineScript(
+                               ResourceLoader::makeConfigSetScript( $this->config )
+                       );
+               }
+
+               // Inline RLQ: Initial module states
+               $states = array_merge( $this->exemptStates, $data['states'] );
+               if ( $states ) {
+                       $chunks[] = ResourceLoader::makeInlineScript(
+                               ResourceLoader::makeLoaderStateScript( $states )
+                       );
+               }
+
+               // Inline RLQ: Embedded modules
+               if ( $data['embed']['general']['top'] ) {
+                       $chunks[] = $this->getLoad(
+                               $data['embed']['general']['top'],
+                               ResourceLoaderModule::TYPE_COMBINED
+                       );
+               }
+
+               // Inline RLQ: Load general modules
+               if ( $data['general']['top'] ) {
+                       $chunks[] = ResourceLoader::makeInlineScript(
+                               Xml::encodeJsCall( 'mw.loader.load', [ $data['general']['top'] ] )
+                       );
+               }
+
+               // Inline RLQ: Load only=scripts
+               if ( $data['scripts']['top'] ) {
+                       $chunks[] = $this->getLoad(
+                               $data['scripts']['top'],
+                               ResourceLoaderModule::TYPE_SCRIPTS
+                       );
+               }
+
+               // External stylesheets
+               if ( $data['styles'] ) {
+                       $chunks[] = $this->getLoad(
+                               $data['styles'],
+                               ResourceLoaderModule::TYPE_STYLES
+                       );
+               }
+
+               // Inline stylesheets (embedded only=styles)
+               if ( $data['embed']['styles'] ) {
+                       $chunks[] = $this->getLoad(
+                               $data['embed']['styles'],
+                               ResourceLoaderModule::TYPE_STYLES
+                       );
+               }
+
+               // Async scripts. Once the startup is loaded, inline RLQ scripts will run.
+               // Pass-through a custom target from OutputPage (T143066).
+               $startupQuery = $this->target ? [ 'target' => $this->target ] : [];
+               $chunks[] = $this->getLoad(
+                       'startup',
+                       ResourceLoaderModule::TYPE_SCRIPTS,
+                       $startupQuery
+               );
+
+               return WrappedStringList::join( "\n", $chunks );
+       }
+
+       /**
+        * @return string|WrappedStringList HTML
+        */
+       public function getBodyHtml() {
+               $data = $this->getData();
+               $chunks = [];
+
+               // Inline RLQ: Embedded modules
+               if ( $data['embed']['general']['bottom'] ) {
+                       $chunks[] = $this->getLoad(
+                               $data['embed']['general']['bottom'],
+                               ResourceLoaderModule::TYPE_COMBINED
+                       );
+               }
+
+               // Inline RLQ: Load only=scripts
+               if ( $data['scripts']['bottom'] ) {
+                       $chunks[] = $this->getLoad(
+                               $data['scripts']['bottom'],
+                               ResourceLoaderModule::TYPE_SCRIPTS
+                       );
+               }
+
+               // Inline RLQ: Load general modules
+               if ( $data['general']['bottom'] ) {
+                       $chunks[] = ResourceLoader::makeInlineScript(
+                               Xml::encodeJsCall( 'mw.loader.load', [ $data['general']['bottom'] ] )
+                       );
+               }
+
+               return WrappedStringList::join( "\n", $chunks );
+       }
+
+       private function getContext( $group, $type ) {
+               return self::makeContext( $this->context, $group, $type );
+       }
+
+       private function getLoad( $modules, $only, array $extraQuery = [] ) {
+               return self::makeLoad( $this->context, (array)$modules, $only, $extraQuery );
+       }
+
+       private static function makeContext( ResourceLoaderContext $mainContext, $group, $type,
+               array $extraQuery = []
+       ) {
+               // Create new ResourceLoaderContext so that $extraQuery may trigger isRaw().
+               $req = new FauxRequest( array_merge( $mainContext->getRequest()->getValues(), $extraQuery ) );
+               // Set 'only' if not combined
+               $req->setVal( 'only', $type === ResourceLoaderModule::TYPE_COMBINED ? null : $type );
+               // Remove user parameter in most cases
+               if ( $group !== 'user' && $group !== 'private' ) {
+                       $req->setVal( 'user', null );
+               }
+               $context = new ResourceLoaderContext( $mainContext->getResourceLoader(), $req );
+               // Allow caller to setVersion() and setModules()
+               return new DerivativeResourceLoaderContext( $context );
+       }
+
+       /**
+        * Explicily load or embed modules on a page.
+        *
+        * @param ResourceLoaderContext $mainContext
+        * @param array $modules One or more module names
+        * @param string $only ResourceLoaderModule TYPE_ class constant
+        * @param array $extraQuery [optional] Array with extra query parameters for the request
+        * @return string|WrappedStringList HTML
+        */
+       public static function makeLoad( ResourceLoaderContext $mainContext, array $modules, $only,
+               array $extraQuery = []
+       ) {
+               $rl = $mainContext->getResourceLoader();
+               $chunks = [];
+
+               if ( $mainContext->getDebug() && count( $modules ) > 1 ) {
+                       $chunks = [];
+                       // Recursively call us for every item
+                       foreach ( $modules as $name ) {
+                               $chunks[] = self::makeLoad( $mainContext, [ $name ], $only, $extraQuery );
+                       }
+                       return new WrappedStringList( "\n", $chunks );
+               }
+
+               // Sort module names so requests are more uniform
+               sort( $modules );
+               // Create keyed-by-source and then keyed-by-group list of module objects from modules list
+               $sortedModules = [];
+               foreach ( $modules as $name ) {
+                       $module = $rl->getModule( $name );
+                       if ( !$module ) {
+                               $rl->getLogger()->warning( 'Unknown module "{module}"', [ 'module' => $name ] );
+                               continue;
+                       }
+                       $sortedModules[$module->getSource()][$module->getGroup()][$name] = $module;
+               }
+
+               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 (bug 27564) 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;
+                               }
+                       }
+               }
+
+               return new WrappedStringList( "\n", $chunks );
+       }
+}
index 85fc53d..30fe3ae 100644 (file)
@@ -91,8 +91,8 @@ class ResourceLoaderContext {
 
        /**
         * Expand a string of the form jquery.foo,bar|jquery.ui.baz,quux to
-        * an array of module names like array( 'jquery.foo', 'jquery.bar',
-        * 'jquery.ui.baz', 'jquery.ui.quux' )
+        * an array of module names like [ 'jquery.foo', 'jquery.bar',
+        * 'jquery.ui.baz', 'jquery.ui.quux' ]
         * @param string $modules Packed module name list
         * @return array Array of module names
         */
@@ -113,7 +113,7 @@ class ResourceLoaderContext {
                                } else {
                                        // We have a prefix and a bunch of suffixes
                                        $prefix = substr( $group, 0, $pos ); // 'foo'
-                                       $suffixes = explode( ',', substr( $group, $pos + 1 ) ); // array( 'bar', 'baz' )
+                                       $suffixes = explode( ',', substr( $group, $pos + 1 ) ); // [ 'bar', 'baz' ]
                                        foreach ( $suffixes as $suffix ) {
                                                $retval[] = "$prefix.$suffix";
                                        }
@@ -175,8 +175,7 @@ class ResourceLoaderContext {
                        // Stricter version of RequestContext::sanitizeLangCode()
                        if ( !Language::isValidBuiltInCode( $lang ) ) {
                                wfDebug( "Invalid user language code\n" );
-                               global $wgLanguageCode;
-                               $lang = $wgLanguageCode;
+                               $lang = $this->getResourceLoader()->getConfig()->get( 'LanguageCode' );
                        }
                        $this->language = $lang;
                }
@@ -260,7 +259,7 @@ class ResourceLoaderContext {
 
        /**
         * @see ResourceLoaderModule::getVersionHash
-        * @see OutputPage::makeResourceLoaderLink
+        * @see ResourceLoaderClientHtml::makeLoad
         * @return string|null
         */
        public function getVersion() {
index b06553a..2dcc841 100644 (file)
@@ -41,7 +41,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @var array List of paths to JavaScript files to always include
         * @par Usage:
         * @code
-        * array( [file-path], [file-path], ... )
+        * [ [file-path], [file-path], ... ]
         * @endcode
         */
        protected $scripts = [];
@@ -50,7 +50,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @var array List of JavaScript files to include when using a specific language
         * @par Usage:
         * @code
-        * array( [language-code] => array( [file-path], [file-path], ... ), ... )
+        * [ [language-code] => [ [file-path], [file-path], ... ], ... ]
         * @endcode
         */
        protected $languageScripts = [];
@@ -59,7 +59,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @var array List of JavaScript files to include when using a specific skin
         * @par Usage:
         * @code
-        * array( [skin-name] => array( [file-path], [file-path], ... ), ... )
+        * [ [skin-name] => [ [file-path], [file-path], ... ], ... ]
         * @endcode
         */
        protected $skinScripts = [];
@@ -68,7 +68,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @var array List of paths to JavaScript files to include in debug mode
         * @par Usage:
         * @code
-        * array( [skin-name] => array( [file-path], [file-path], ... ), ... )
+        * [ [skin-name] => [ [file-path], [file-path], ... ], ... ]
         * @endcode
         */
        protected $debugScripts = [];
@@ -77,7 +77,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @var array List of paths to CSS files to always include
         * @par Usage:
         * @code
-        * array( [file-path], [file-path], ... )
+        * [ [file-path], [file-path], ... ]
         * @endcode
         */
        protected $styles = [];
@@ -86,7 +86,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @var array List of paths to CSS files to include when using specific skins
         * @par Usage:
         * @code
-        * array( [file-path], [file-path], ... )
+        * [ [file-path], [file-path], ... ]
         * @endcode
         */
        protected $skinStyles = [];
@@ -95,7 +95,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @var array List of modules this module depends on
         * @par Usage:
         * @code
-        * array( [file-path], [file-path], ... )
+        * [ [file-path], [file-path], ... ]
         * @endcode
         */
        protected $dependencies = [];
@@ -109,7 +109,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @var array List of message keys used by this module
         * @par Usage:
         * @code
-        * array( [message-key], [message-key], ... )
+        * [ [message-key], [message-key], ... ]
         * @endcode
         */
        protected $messages = [];
@@ -138,7 +138,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @var array Place where readStyleFile() tracks file dependencies
         * @par Usage:
         * @code
-        * array( [file-path], [file-path], ... )
+        * [ [file-path], [file-path], ... ]
         * @endcode
         */
        protected $localFileRefs = [];
@@ -165,7 +165,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @throws InvalidArgumentException
         * @par Construction options:
         * @code
-        *     array(
+        *     [
         *         // Base path to prepend to all local paths in $options. Defaults to $IP
         *         'localBasePath' => [base path],
         *         // Base path to prepend to all remote paths in $options. Defaults to $wgResourceBasePath
@@ -177,26 +177,26 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         *         // Scripts to always include
         *         'scripts' => [file path string or array of file path strings],
         *         // Scripts to include in specific language contexts
-        *         'languageScripts' => array(
+        *         'languageScripts' => [
         *             [language code] => [file path string or array of file path strings],
-        *         ),
+        *         ],
         *         // Scripts to include in specific skin contexts
-        *         'skinScripts' => array(
+        *         'skinScripts' => [
         *             [skin name] => [file path string or array of file path strings],
-        *         ),
+        *         ],
         *         // Scripts to include in debug contexts
         *         'debugScripts' => [file path string or array of file path strings],
         *         // Modules which must be loaded before this module
         *         'dependencies' => [module name string or array of module name strings],
-        *         'templates' => array(
+        *         'templates' => [
         *             [template alias with file.ext] => [file path to a template file],
-        *         ),
+        *         ],
         *         // Styles to always load
         *         'styles' => [file path string or array of file path strings],
         *         // Styles to include in specific skin contexts
-        *         'skinStyles' => array(
+        *         'skinStyles' => [
         *             [skin name] => [file path string or array of file path strings],
-        *         ),
+        *         ],
         *         // Messages to always load
         *         'messages' => [array of message key strings],
         *         // Group which this module should be loaded together with
@@ -207,7 +207,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         *         // The file must contain valid JavaScript for execution in a private function.
         *         // The file must not contain the "function () {" and "}" wrapper though.
         *         'skipFunction' => [file path]
-        *     )
+        *     ]
         * @endcode
         */
        public function __construct(
@@ -255,6 +255,9 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                                                $this->{$member}[$key] = (array)$value;
                                        }
                                        break;
+                               case 'deprecated':
+                                       $this->deprecated = $option;
+                                       break;
                                // Lists of strings
                                case 'dependencies':
                                case 'messages':
@@ -352,7 +355,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         */
        public function getScript( ResourceLoaderContext $context ) {
                $files = $this->getScriptFiles( $context );
-               return $this->readScriptFiles( $files );
+               return $this->getDeprecationInformation() . $this->readScriptFiles( $files );
        }
 
        /**
@@ -922,6 +925,28 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                return $this->targets;
        }
 
+       /**
+        * Get the module's load type.
+        *
+        * @since 1.28
+        * @return string
+        */
+       public function getType() {
+               $canBeStylesOnly = !(
+                       // All options except 'styles', 'skinStyles' and 'debugRaw'
+                       $this->scripts
+                       || $this->debugScripts
+                       || $this->templates
+                       || $this->languageScripts
+                       || $this->skinScripts
+                       || $this->dependencies
+                       || $this->messages
+                       || $this->skipFunction
+                       || $this->raw
+               );
+               return $canBeStylesOnly ? self::LOAD_STYLES : self::LOAD_GENERAL;
+       }
+
        /**
         * Compile a LESS file into CSS.
         *
index 87e5fd7..2503b22 100644 (file)
@@ -54,8 +54,8 @@ class ResourceLoaderImage {
                $this->variants = $variants;
 
                // Expand shorthands:
-               // array( "en,de,fr" => "foo.svg" )
-               // → array( "en" => "foo.svg", "de" => "foo.svg", "fr" => "foo.svg" )
+               // [ "en,de,fr" => "foo.svg" ]
+               // → [ "en" => "foo.svg", "de" => "foo.svg", "fr" => "foo.svg" ]
                if ( is_array( $this->descriptor ) && isset( $this->descriptor['lang'] ) ) {
                        foreach ( array_keys( $this->descriptor['lang'] ) as $langList ) {
                                if ( strpos( $langList, ',' ) !== false ) {
index 3b3bdf7..6a8957e 100644 (file)
@@ -59,7 +59,7 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
         * Below is a description for the $options array:
         * @par Construction options:
         * @code
-        *     array(
+        *     [
         *         // Base path to prepend to all local paths in $options. Defaults to $IP
         *         'localBasePath' => [base path],
         *         // Path to JSON file that contains any of the settings below
@@ -72,33 +72,33 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
         *         'selectorWithoutVariant' => [CSS selector template, variables: {prefix} {name}],
         *         'selectorWithVariant' => [CSS selector template, variables: {prefix} {name} {variant}],
         *         // List of variants that may be used for the image files
-        *         'variants' => array(
-        *             [theme name] => array(
-        *                 [variant name] => array(
+        *         'variants' => [
+        *             [theme name] => [
+        *                 [variant name] => [
         *                     'color' => [color string, e.g. '#ffff00'],
         *                     'global' => [boolean, if true, this variant is available
         *                                  for all images of this type],
-        *                 ),
+        *                 ],
         *                 ...
-        *             ),
+        *             ],
         *             ...
-        *         ),
+        *         ],
         *         // List of image files and their options
-        *         'images' => array(
-        *             [theme name] => array(
-        *                 [icon name] => array(
+        *         'images' => [
+        *             [theme name] => [
+        *                 [icon name] => [
         *                     'file' => [file path string or array whose values are file path strings
         *                                    and whose keys are 'default', 'ltr', 'rtl', a single
         *                                    language code like 'en', or a list of language codes like
         *                                    'en,de,ar'],
         *                     'variants' => [array of variant name strings, variants
         *                                    available for this image],
-        *                 ),
+        *                 ],
         *                 ...
-        *             ),
+        *             ],
         *             ...
-        *         ),
-        *     )
+        *         ],
+        *     ]
         * @endcode
         * @throws InvalidArgumentException
         */
@@ -393,6 +393,8 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
        public function getDefinitionSummary( ResourceLoaderContext $context ) {
                $this->loadFromDefinition();
                $summary = parent::getDefinitionSummary( $context );
+
+               $options = [];
                foreach ( [
                        'localBasePath',
                        'images',
@@ -401,29 +403,27 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
                        'selectorWithoutVariant',
                        'selectorWithVariant',
                ] as $member ) {
-                       $summary[$member] = $this->{$member};
+                       $options[$member] = $this->{$member};
                };
+
+               $summary[] = [
+                       'options' => $options,
+                       'fileHashes' => $this->getFileHashes( $context ),
+               ];
                return $summary;
        }
 
        /**
-        * Get the last modified timestamp of this module.
-        *
-        * @param ResourceLoaderContext $context Context in which to calculate
-        *     the modified time
-        * @return int UNIX timestamp
+        * Helper method for getDefinitionSummary.
         */
-       public function getModifiedTime( ResourceLoaderContext $context ) {
+       protected function getFileHashes( ResourceLoaderContext $context ) {
                $this->loadFromDefinition();
                $files = [];
                foreach ( $this->getImages( $context ) as $name => $image ) {
                        $files[] = $image->getPath( $context );
                }
-
                $files = array_values( array_unique( $files ) );
-               $filesMtime = max( array_map( [ __CLASS__, 'safeFilemtime' ], $files ) );
-
-               return $filesMtime;
+               return array_map( [ __CLASS__, 'safeFileHash' ], $files );
        }
 
        /**
@@ -455,4 +455,11 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
                $this->loadFromDefinition();
                return $this->position;
        }
+
+       /**
+        * @return string
+        */
+       public function getType() {
+               return self::LOAD_STYLES;
+       }
 }
index 121a6c9..2351efd 100644 (file)
@@ -35,6 +35,12 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
        const TYPE_STYLES = 'styles';
        const TYPE_COMBINED = 'combined';
 
+       # Desired load type
+       // Module only has styles (loaded via <style> or <link rel=stylesheet>)
+       const LOAD_STYLES = 'styles';
+       // Module may have other resources (loaded via mw.loader from a script)
+       const LOAD_GENERAL = 'general';
+
        # sitewide core module like a skin file or jQuery component
        const ORIGIN_CORE_SITEWIDE = 1;
 
@@ -76,6 +82,11 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
         */
        protected $config;
 
+       /**
+        * @var array|bool
+        */
+       protected $deprecated = false;
+
        /**
         * @var LoggerInterface
         */
@@ -124,6 +135,28 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                return $wgContLang->getDir() !== $context->getDirection();
        }
 
+       /**
+        * Get JS representing deprecation information for the current module if available
+        *
+        * @return string JavaScript code
+        */
+       protected function getDeprecationInformation() {
+               $deprecationInfo = $this->deprecated;
+               if ( $deprecationInfo ) {
+                       $name = $this->getName();
+                       $warning = 'This page is using the deprecated ResourceLoader module "' . $name . '".';
+                       if ( !is_bool( $deprecationInfo ) && isset( $deprecationInfo['message'] ) ) {
+                               $warning .= "\n" . $deprecationInfo['message'];
+                       }
+                       return Xml::encodeJsCall(
+                               'mw.log.warn',
+                               [ $warning ]
+                       );
+               } else {
+                       return '';
+               }
+       }
+
        /**
         * Get all JS for this module for a given language and skin.
         * Includes all relevant JS except loader scripts.
@@ -231,8 +264,8 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
         *
         * @param ResourceLoaderContext $context
         * @return array List of CSS strings or array of CSS strings keyed by media type.
-        *  like array( 'screen' => '.foo { width: 0 }' );
-        *  or array( 'screen' => array( '.foo { width: 0 }' ) );
+        *  like [ 'screen' => '.foo { width: 0 }' ];
+        *  or [ 'screen' => [ '.foo { width: 0 }' ] ];
         */
        public function getStyles( ResourceLoaderContext $context ) {
                // Stub, override expected
@@ -246,7 +279,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
         * load the files directly. See also getScriptURLsForDebug()
         *
         * @param ResourceLoaderContext $context
-        * @return array Array( mediaType => array( URL1, URL2, ... ), ... )
+        * @return array [ mediaType => [ URL1, URL2, ... ], ... ]
         */
        public function getStyleURLsForDebug( ResourceLoaderContext $context ) {
                $resourceLoader = $context->getResourceLoader();
@@ -343,6 +376,16 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                return $this->targets;
        }
 
+       /**
+        * Get the module's load type.
+        *
+        * @since 1.28
+        * @return string ResourceLoaderModule LOAD_* constant
+        */
+       public function getType() {
+               return self::LOAD_GENERAL;
+       }
+
        /**
         * Get the skip function.
         *
@@ -374,7 +417,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
 
                // Try in-object cache first
                if ( !isset( $this->fileDeps[$vary] ) ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $deps = $dbr->selectField( 'module_deps',
                                'md_deps',
                                [
@@ -443,9 +486,11 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                                        ]
                                );
 
-                               $dbw->onTransactionIdle( function () use ( &$scopeLock ) {
-                                       ScopedCallback::consume( $scopeLock ); // release after commit
-                               } );
+                               if ( $dbw->trxLevel() ) {
+                                       $dbw->onTransactionResolution( function () use ( &$scopeLock ) {
+                                               ScopedCallback::consume( $scopeLock ); // release after commit
+                                       } );
+                               }
                        }
                } catch ( Exception $e ) {
                        wfDebugLog( 'resourceloader', __METHOD__ . ": failed to update DB: $e" );
@@ -594,7 +639,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                // Styles
                if ( $context->shouldIncludeStyles() ) {
                        $styles = [];
-                       // Don't create empty stylesheets like array( '' => '' ) for modules
+                       // Don't create empty stylesheets like [ '' => '' ] for modules
                        // that don't *have* any stylesheets (bug 38024).
                        $stylePairs = $this->getStyles( $context );
                        if ( count( $stylePairs ) ) {
@@ -744,10 +789,10 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
         *
         * @code
         *     $summary = parent::getDefinitionSummary( $context );
-        *     $summary[] = array(
+        *     $summary[] = [
         *         'foo' => 123,
         *         'bar' => 'quux',
-        *     );
+        *     ];
         *     return $summary;
         * @endcode
         *
index e01d0a1..7401d58 100644 (file)
@@ -39,12 +39,13 @@ class ResourceLoaderSiteModule extends ResourceLoaderWikiModule {
                        $pages['MediaWiki:Common.js'] = [ 'type' => 'script' ];
                        $pages['MediaWiki:' . ucfirst( $context->getSkin() ) . '.js'] = [ 'type' => 'script' ];
                }
-               if ( $this->getConfig()->get( 'UseSiteCss' ) ) {
-                       $pages['MediaWiki:Common.css'] = [ 'type' => 'style' ];
-                       $pages['MediaWiki:' . ucfirst( $context->getSkin() ) . '.css'] = [ 'type' => 'style' ];
-                       $pages['MediaWiki:Print.css'] = [ 'type' => 'style', 'media' => 'print' ];
-
-               }
                return $pages;
        }
+
+       /*
+        * @return array
+        */
+       public function getDependencies( ResourceLoaderContext $context = null ) {
+               return [ 'site.styles' ];
+       }
 }
index 6896ad7..79922bf 100644 (file)
@@ -43,4 +43,18 @@ class ResourceLoaderSiteStylesModule extends ResourceLoaderWikiModule {
                }
                return $pages;
        }
+
+       /**
+        * @return string
+        */
+       public function getType() {
+               return self::LOAD_STYLES;
+       }
+
+       /**
+        * @return string
+        */
+       public function getGroup() {
+               return 'site';
+       }
 }
index 34866f3..8970620 100644 (file)
@@ -66,6 +66,8 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                        }
                }
 
+               $illegalFileChars = $conf->get( 'IllegalFileChars' );
+
                // Build list of variables
                $vars = [
                        'wgLoadScript' => wfScript( 'load' ),
@@ -107,6 +109,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                        'wgResourceLoaderMaxQueryLength' => $conf->get( 'ResourceLoaderMaxQueryLength' ),
                        'wgCaseSensitiveNamespaces' => $caseSensitiveNamespaces,
                        'wgLegalTitleChars' => Title::convertByteClassToUnicodeClass( Title::legalChars() ),
+                       'wgIllegalFileChars' => Title::convertByteClassToUnicodeClass( $illegalFileChars ),
                        'wgResourceLoaderStorageVersion' => $conf->get( 'ResourceLoaderStorageVersion' ),
                        'wgResourceLoaderStorageEnabled' => $conf->get( 'ResourceLoaderStorageEnabled' ),
                        'wgResourceLoaderLegacyModules' => self::getLegacyModules(),
@@ -217,7 +220,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                        }
 
                        $versionHash = $module->getVersionHash( $context );
-                       if ( strlen( $versionHash ) !== 8 ) {
+                       if ( strlen( $versionHash ) !== 7 ) {
                                $context->getLogger()->warning(
                                        "Module '{module}' produced an invalid version hash: '{version}'.",
                                        [
@@ -308,19 +311,14 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
         */
        public static function getStartupModulesUrl( ResourceLoaderContext $context ) {
                $rl = $context->getResourceLoader();
-               $moduleNames = self::getStartupModules();
 
-               $query = [
-                       'modules' => ResourceLoader::makePackedModulesString( $moduleNames ),
-                       'only' => 'scripts',
-                       'lang' => $context->getLanguage(),
-                       'skin' => $context->getSkin(),
-                       'debug' => $context->getDebug() ? 'true' : 'false',
-                       'version' => $rl->getCombinedVersion( $context, $moduleNames ),
-               ];
-               // Ensure uniform query order
-               ksort( $query );
-               return wfAppendQuery( wfScript( 'load' ), $query );
+               $derivative = new DerivativeResourceLoaderContext( $context );
+               $derivative->setModules( self::getStartupModules() );
+               $derivative->setOnly( 'scripts' );
+               // Must setModules() before makeVersionQuery()
+               $derivative->setVersion( $rl->makeVersionQuery( $derivative ) );
+
+               return $rl->createLoaderURL( 'local', $derivative );
        }
 
        /**
index 9c198d1..4d0bff7 100644 (file)
@@ -83,4 +83,11 @@ class ResourceLoaderUserCSSPrefsModule extends ResourceLoaderModule {
        public function getGroup() {
                return 'private';
        }
+
+       /**
+        * @return string
+        */
+       public function getType() {
+               return self::LOAD_STYLES;
+       }
 }
index 8d4f263..8f58040 100644 (file)
@@ -1,7 +1,5 @@
 <?php
 /**
- * ResourceLoader module for user customizations.
- *
  * 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
@@ -23,7 +21,7 @@
  */
 
 /**
- * Module for user customizations
+ * Module for user customizations scripts
  */
 class ResourceLoaderUserModule extends ResourceLoaderWikiModule {
 
@@ -31,8 +29,6 @@ class ResourceLoaderUserModule extends ResourceLoaderWikiModule {
        protected $targets = [ 'desktop', 'mobile' ];
 
        /**
-        * Get list of pages used by this module
-        *
         * @param ResourceLoaderContext $context
         * @return array List of pages
         */
@@ -52,35 +48,20 @@ class ResourceLoaderUserModule extends ResourceLoaderWikiModule {
                        $pages["$userPage/" . $context->getSkin() . '.js'] = [ 'type' => 'script' ];
                }
 
-               if ( $config->get( 'AllowUserCss' ) ) {
-                       $pages["$userPage/common.css"] = [ 'type' => 'style' ];
-                       $pages["$userPage/" . $context->getSkin() . '.css'] = [ 'type' => 'style' ];
-               }
-
-               $useSiteJs = $config->get( 'UseSiteJs' );
-               $useSiteCss = $config->get( 'UseSiteCss' );
                // User group pages are maintained site-wide and enabled with site JS/CSS.
-               if ( $useSiteJs || $useSiteCss ) {
+               if ( $config->get( 'UseSiteJs' ) ) {
                        foreach ( $user->getEffectiveGroups() as $group ) {
                                if ( $group == '*' ) {
                                        continue;
                                }
-                               if ( $useSiteJs ) {
-                                       $pages["MediaWiki:Group-$group.js"] = [ 'type' => 'script' ];
-                               }
-                               if ( $useSiteCss ) {
-                                       $pages["MediaWiki:Group-$group.css"] = [ 'type' => 'style' ];
-                               }
+                               $pages["MediaWiki:Group-$group.js"] = [ 'type' => 'script' ];
                        }
                }
 
-               // Hack for bug 26283: if we're on a preview page for a CSS/JS page,
-               // we need to exclude that page from this module. In that case, the excludepage
-               // parameter will be set to the name of the page we need to exclude.
+               // Hack for T28283: Allow excluding pages for preview on a CSS/JS page.
+               // The excludepage parameter is set by OutputPage.
                $excludepage = $context->getRequest()->getVal( 'excludepage' );
                if ( isset( $pages[$excludepage] ) ) {
-                       // This works because $excludepage is generated with getPrefixedDBkey(),
-                       // just like the keys in $pages[] above
                        unset( $pages[$excludepage] );
                }
 
@@ -95,4 +76,11 @@ class ResourceLoaderUserModule extends ResourceLoaderWikiModule {
        public function getGroup() {
                return 'user';
        }
+
+       /**
+        * @return array
+        */
+       public function getDependencies( ResourceLoaderContext $context = null ) {
+               return [ 'user.styles' ];
+       }
 }
index b3b3f16..c1b47bf 100644 (file)
@@ -64,6 +64,13 @@ class ResourceLoaderUserOptionsModule extends ResourceLoaderModule {
                return false;
        }
 
+       /**
+        * @return string
+        */
+       public function getPosition() {
+               return 'top';
+       }
+
        /**
         * @return string
         */
diff --git a/includes/resourceloader/ResourceLoaderUserStylesModule.php b/includes/resourceloader/ResourceLoaderUserStylesModule.php
new file mode 100644 (file)
index 0000000..8d8e008
--- /dev/null
@@ -0,0 +1,86 @@
+<?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 Trevor Parscal
+ * @author Roan Kattouw
+ */
+
+/**
+ * Module for user customizations styles
+ */
+class ResourceLoaderUserStylesModule extends ResourceLoaderWikiModule {
+
+       protected $origin = self::ORIGIN_USER_INDIVIDUAL;
+       protected $targets = [ 'desktop', 'mobile' ];
+
+       /**
+        * @param ResourceLoaderContext $context
+        * @return array List of pages
+        */
+       protected function getPages( ResourceLoaderContext $context ) {
+               $config = $this->getConfig();
+               $user = $context->getUserObj();
+               if ( $user->isAnon() ) {
+                       return [];
+               }
+
+               // Use localised/normalised variant to ensure $excludepage matches
+               $userPage = $user->getUserPage()->getPrefixedDBkey();
+               $pages = [];
+
+               if ( $config->get( 'AllowUserCss' ) ) {
+                       $pages["$userPage/common.css"] = [ 'type' => 'style' ];
+                       $pages["$userPage/" . $context->getSkin() . '.css'] = [ 'type' => 'style' ];
+               }
+
+               // User group pages are maintained site-wide and enabled with site JS/CSS.
+               if ( $config->get( 'UseSiteCss' ) ) {
+                       foreach ( $user->getEffectiveGroups() as $group ) {
+                               if ( $group == '*' ) {
+                                       continue;
+                               }
+                               $pages["MediaWiki:Group-$group.css"] = [ 'type' => 'style' ];
+                       }
+               }
+
+               // Hack for T28283: Allow excluding pages for preview on a CSS/JS page.
+               // The excludepage parameter is set by OutputPage.
+               $excludepage = $context->getRequest()->getVal( 'excludepage' );
+               if ( isset( $pages[$excludepage] ) ) {
+                       unset( $pages[$excludepage] );
+               }
+
+               return $pages;
+       }
+
+       /**
+        * @return string
+        */
+       public function getType() {
+               return self::LOAD_STYLES;
+       }
+
+       /**
+        * Get group name
+        *
+        * @return string
+        */
+       public function getGroup() {
+               return 'user';
+       }
+}
index cea1f39..c8a0ff1 100644 (file)
@@ -74,6 +74,13 @@ class ResourceLoaderUserTokensModule extends ResourceLoaderModule {
                return false;
        }
 
+       /**
+        * @return string
+        */
+       public function getPosition() {
+               return 'top';
+       }
+
        /**
         * @return string
         */
index a3f8825..5580306 100644 (file)
@@ -130,7 +130,7 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
        /**
         * Get the Database object used in getTitleInfo().
         *
-        * Defaults to the local slave DB. Subclasses may want to override this to return a foreign
+        * Defaults to the local replica DB. Subclasses may want to override this to return a foreign
         * database object, or null if getTitleInfo() shouldn't access the database.
         *
         * NOTE: This ONLY works for getTitleInfo() and isKnownEmpty(), NOT FOR ANYTHING ELSE.
@@ -139,7 +139,7 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
         * @return IDatabase|null
         */
        protected function getDB() {
-               return wfGetDB( DB_SLAVE );
+               return wfGetDB( DB_REPLICA );
        }
 
        /**
@@ -246,7 +246,7 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
                $summary = parent::getDefinitionSummary( $context );
                $summary[] = [
                        'pages' => $this->getPages( $context ),
-                       // Includes SHA1 of content
+                       // Includes meta data of current revisions
                        'titleInfo' => $this->getTitleInfo( $context ),
                ];
                return $summary;
@@ -262,7 +262,7 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
                // For user modules, don't needlessly load if there are no non-empty pages
                if ( $this->getGroup() === 'user' ) {
                        foreach ( $revisions as $revision ) {
-                               if ( $revision['rev_len'] > 0 ) {
+                               if ( $revision['page_len'] > 0 ) {
                                        // At least one non-empty page, module should be loaded
                                        return false;
                                }
@@ -276,10 +276,14 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
                return count( $revisions ) === 0;
        }
 
+       private function setTitleInfo( $key, array $titleInfo ) {
+               $this->titleInfo[$key] = $titleInfo;
+       }
+
        /**
         * Get the information about the wiki pages for a given context.
         * @param ResourceLoaderContext $context
-        * @return array Keyed by page name. Contains arrays with 'rev_len' and 'rev_sha1' keys
+        * @return array Keyed by page name
         */
        protected function getTitleInfo( ResourceLoaderContext $context ) {
                $dbr = $this->getDB();
@@ -288,40 +292,94 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
                        return [];
                }
 
-               $pages = $this->getPages( $context );
-               $key = implode( '|', array_keys( $pages ) );
+               $pageNames = array_keys( $this->getPages( $context ) );
+               sort( $pageNames );
+               $key = implode( '|', $pageNames );
                if ( !isset( $this->titleInfo[$key] ) ) {
-                       $this->titleInfo[$key] = [];
-                       $batch = new LinkBatch;
-                       foreach ( $pages as $titleText => $options ) {
-                               $batch->addObj( Title::newFromText( $titleText ) );
+                       $this->titleInfo[$key] = self::fetchTitleInfo( $dbr, $pageNames, __METHOD__ );
+               }
+               return $this->titleInfo[$key];
+       }
+
+       private static function fetchTitleInfo( IDatabase $db, array $pages, $fname = __METHOD__ ) {
+               $titleInfo = [];
+               $batch = new LinkBatch;
+               foreach ( $pages as $titleText ) {
+                       $batch->addObj( Title::newFromText( $titleText ) );
+               }
+               if ( !$batch->isEmpty() ) {
+                       $res = $db->select( 'page',
+                               // Include page_touched to allow purging if cache is poisoned (T117587, T113916)
+                               [ 'page_namespace', 'page_title', 'page_touched', 'page_len', 'page_latest' ],
+                               $batch->constructSet( 'page', $db ),
+                               $fname
+                       );
+                       foreach ( $res as $row ) {
+                               // Avoid including ids or timestamps of revision/page tables so
+                               // that versions are not wasted
+                               $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+                               $titleInfo[$title->getPrefixedText()] = [
+                                       'page_len' => $row->page_len,
+                                       'page_latest' => $row->page_latest,
+                                       'page_touched' => $row->page_touched,
+                               ];
                        }
+               }
+               return $titleInfo;
+       }
 
-                       if ( !$batch->isEmpty() ) {
-                               $res = $dbr->select( [ 'page', 'revision' ],
-                                       // Include page_touched to allow purging if cache is poisoned (T117587, T113916)
-                                       [ 'page_namespace', 'page_title', 'page_touched', 'rev_len', 'rev_sha1' ],
-                                       $batch->constructSet( 'page', $dbr ),
-                                       __METHOD__,
-                                       [],
-                                       [ 'revision' => [ 'INNER JOIN', [ 'page_latest=rev_id' ] ] ]
-                               );
-                               foreach ( $res as $row ) {
-                                       // Avoid including ids or timestamps of revision/page tables so
-                                       // that versions are not wasted
-                                       $title = Title::makeTitle( $row->page_namespace, $row->page_title );
-                                       $this->titleInfo[$key][$title->getPrefixedText()] = [
-                                               'rev_len' => $row->rev_len,
-                                               'rev_sha1' => $row->rev_sha1,
-                                               'page_touched' => $row->page_touched,
-                                       ];
+       /**
+        * @since 1.28
+        * @param ResourceLoaderContext $context
+        * @param IDatabase $db
+        * @param string[] $modules
+        */
+       public static function preloadTitleInfo(
+               ResourceLoaderContext $context, IDatabase $db, array $moduleNames
+       ) {
+               $rl = $context->getResourceLoader();
+               // getDB() can be overridden to point to a foreign database.
+               // For now, only preload local. In the future, we could preload by wikiID.
+               $allPages = [];
+               $wikiModules = [];
+               foreach ( $moduleNames as $name ) {
+                       $module = $rl->getModule( $name );
+                       if ( $module instanceof self ) {
+                               $mDB = $module->getDB();
+                               // Subclasses may disable getDB and implement getTitleInfo differently
+                               if ( $mDB && $mDB->getWikiID() === $db->getWikiID() ) {
+                                       $wikiModules[] = $module;
+                                       $allPages += $module->getPages( $context );
                                }
                        }
                }
-               return $this->titleInfo[$key];
+               $allInfo = self::fetchTitleInfo( $db, array_keys( $allPages ), __METHOD__ );
+               foreach ( $wikiModules as $module ) {
+                       $pages = $module->getPages( $context );
+                       $info = array_intersect_key( $allInfo, $pages );
+                       $pageNames = array_keys( $pages );
+                       sort( $pageNames );
+                       $key = implode( '|', $pageNames );
+                       $module->setTitleInfo( $key, $info );
+               }
+               return $allInfo;
        }
 
+       /**
+        * @return string
+        */
        public function getPosition() {
                return $this->position;
        }
+
+       /**
+        * @since 1.28
+        * @return string
+        */
+       public function getType() {
+               // Check both because subclasses don't always pass pages via the constructor,
+               // they may also override getPages() instead, in which case we should keep
+               // defaulting to LOAD_GENERAL and allow them to override getType() separately.
+               return ( $this->styles && !$this->scripts ) ? self::LOAD_STYLES : self::LOAD_GENERAL;
+       }
 }
index 72c460e..ad9259b 100644 (file)
@@ -77,7 +77,7 @@ class RevDelArchiveList extends RevDelRevisionList {
                return Status::newGood();
        }
 
-       public function doPostCommitUpdates() {
+       public function doPostCommitUpdates( array $visibilityChangeMap ) {
                return Status::newGood();
        }
 }
index e2bb516..52df2e3 100644 (file)
  * Item class for a filearchive table row
  */
 class RevDelArchivedFileItem extends RevDelFileItem {
+       /** @var $list RevDelArchivedFileList */
+       /** @var $file ArchivedFile */
+       /** @var LocalFile */
+       protected $lockFile;
+
        public function __construct( $list, $row ) {
                RevDelItem::__construct( $list, $row );
                $this->file = ArchivedFile::newFromRow( $row );
+               $this->lockFile = RepoGroup::singleton()->getLocalRepo()->newFile( $row->fa_name );
        }
 
        public function getIdField() {
@@ -107,8 +113,7 @@ class RevDelArchivedFileItem extends RevDelFileItem {
                                                'target' => $this->list->title->getPrefixedText(),
                                                'file' => $file->getKey(),
                                                'token' => $user->getEditToken( $file->getKey() )
-                                       ],
-                                       false, PROTO_RELATIVE
+                                       ]
                                ),
                        ];
                }
@@ -126,4 +131,12 @@ class RevDelArchivedFileItem extends RevDelFileItem {
 
                return $ret;
        }
+
+       public function lock() {
+               return $this->lockFile->acquireFileLock();
+       }
+
+       public function unlock() {
+               return $this->lockFile->releaseFileLock();
+       }
 }
index 921fe5a..ff01cee 100644 (file)
  * Item class for an oldimage table row
  */
 class RevDelFileItem extends RevDelItem {
-       /** @var File */
-       public $file;
+       /** @var RevDelFileList */
+       protected $list;
+       /** @var OldLocalFile */
+       protected $file;
 
        public function __construct( $list, $row ) {
                parent::__construct( $list, $row );
@@ -215,8 +217,7 @@ class RevDelFileItem extends RevDelItem {
                                                'target' => $this->list->title->getPrefixedText(),
                                                'file' => $file->getArchiveName(),
                                                'token' => $user->getEditToken( $file->getArchiveName() )
-                                       ],
-                                       false, PROTO_RELATIVE
+                                       ]
                                ),
                        ];
                }
@@ -234,4 +235,12 @@ class RevDelFileItem extends RevDelItem {
 
                return $ret;
        }
+
+       public function lock() {
+               return $this->file->acquireFileLock();
+       }
+
+       public function unlock() {
+               return $this->file->releaseFileLock();
+       }
 }
index 75e1885..00cb2e1 100644 (file)
@@ -104,7 +104,7 @@ class RevDelFileList extends RevDelList {
                return $status;
        }
 
-       public function doPostCommitUpdates() {
+       public function doPostCommitUpdates( array $visibilityChangeMap ) {
                $file = wfLocalFile( $this->title );
                $file->purgeCache();
                $file->purgeDescription();
index dba368d..b114c75 100644 (file)
@@ -61,4 +61,22 @@ abstract class RevDelItem extends RevisionItemBase {
         * @return array Data for the API result
         */
        abstract public function getApiData( ApiResult $result );
+
+       /**
+        * Lock the item against changes outside of the DB
+        * @return Status
+        * @since 1.28
+        */
+       public function lock() {
+               return Status::newGood();
+       }
+
+       /**
+        * Unlock the item against changes outside of the DB
+        * @return Status
+        * @since 1.28
+        */
+       public function unlock() {
+               return Status::newGood();
+       }
 }
index 87e641d..48604e1 100644 (file)
@@ -81,14 +81,13 @@ abstract class RevDelList extends RevisionListBase {
        public function areAnySuppressed() {
                $bit = $this->getSuppressBit();
 
-               // @codingStandardsIgnoreStart Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
-               for ( $this->reset(); $this->current(); $this->next() ) {
-                       // @codingStandardsIgnoreEnd
-                       $item = $this->current();
+               /** @var $item RevDelItem */
+               foreach ( $this as $item ) {
                        if ( $item->getBits() & $bit ) {
                                return true;
                        }
                }
+
                return false;
        }
 
@@ -104,6 +103,8 @@ abstract class RevDelList extends RevisionListBase {
         * @since 1.23 Added 'perItemStatus' param
         */
        public function setVisibility( array $params ) {
+               $status = Status::newGood();
+
                $bitPars = $params['value'];
                $comment = $params['comment'];
                $perItemStatus = isset( $params['perItemStatus'] ) ? $params['perItemStatus'] : false;
@@ -113,9 +114,17 @@ abstract class RevDelList extends RevisionListBase {
                $dbw = wfGetDB( DB_MASTER );
                $this->res = $this->doQuery( $dbw );
 
+               $status->merge( $this->acquireItemLocks() );
+               if ( !$status->isGood() ) {
+                       return $status;
+               }
+
                $dbw->startAtomic( __METHOD__ );
+               $dbw->onTransactionResolution( function () {
+                       // Release locks on commit or error
+                       $this->releaseItemLocks();
+               } );
 
-               $status = Status::newGood();
                $missing = array_flip( $this->ids );
                $this->clearFileOps();
                $idsForLog = [];
@@ -132,11 +141,12 @@ abstract class RevDelList extends RevisionListBase {
                $virtualNewBits = 0;
                $logType = 'delete';
 
-               // @codingStandardsIgnoreStart Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
-               for ( $this->reset(); $this->current(); $this->next() ) {
-                       // @codingStandardsIgnoreEnd
-                       /** @var $item RevDelItem */
-                       $item = $this->current();
+               // Will be filled with id => [old, new bits] information and
+               // passed to doPostCommitUpdates().
+               $visibilityChangeMap = [];
+
+               /** @var $item RevDelItem */
+               foreach ( $this as $item ) {
                        unset( $missing[$item->getId()] );
 
                        if ( $perItemStatus ) {
@@ -205,6 +215,13 @@ abstract class RevDelList extends RevisionListBase {
                                } elseif ( IP::isIPAddress( $item->getAuthorName() ) ) {
                                        $authorIPs[] = $item->getAuthorName();
                                }
+
+                               // Save the old and new bits in $visibilityChangeMap for
+                               // later use.
+                               $visibilityChangeMap[$item->getId()] = [
+                                       'oldBits' => $oldBits,
+                                       'newBits' => $newBits,
+                               ];
                        } else {
                                $itemStatus->error(
                                        'revdelete-concurrent-change', $item->formatDate(), $item->formatTime() );
@@ -223,7 +240,7 @@ abstract class RevDelList extends RevisionListBase {
                }
 
                if ( $status->successCount == 0 ) {
-                       $dbw->rollback( __METHOD__ );
+                       $dbw->endAtomic( __METHOD__ );
                        return $status;
                }
 
@@ -233,8 +250,8 @@ abstract class RevDelList extends RevisionListBase {
                // Move files, if there are any
                $status->merge( $this->doPreCommitUpdates() );
                if ( !$status->isOK() ) {
-                       // Fatal error, such as no configured archive directory
-                       $dbw->rollback( __METHOD__ );
+                       // Fatal error, such as no configured archive directory or I/O failures
+                       wfGetLBFactory()->rollbackMasterChanges( __METHOD__ );
                        return $status;
                }
 
@@ -253,17 +270,40 @@ abstract class RevDelList extends RevisionListBase {
                        ]
                );
 
-               // Clear caches
-               $that = $this;
-               $dbw->onTransactionIdle( function() use ( $that ) {
-                       $that->doPostCommitUpdates();
-               } );
+               // Clear caches after commit
+               DeferredUpdates::addCallableUpdate(
+                       function () use ( $visibilityChangeMap ) {
+                               $this->doPostCommitUpdates( $visibilityChangeMap );
+                       },
+                       DeferredUpdates::PRESEND,
+                       $dbw
+               );
 
                $dbw->endAtomic( __METHOD__ );
 
                return $status;
        }
 
+       final protected function acquireItemLocks() {
+               $status = Status::newGood();
+               /** @var $item RevDelItem */
+               foreach ( $this as $item ) {
+                       $status->merge( $item->lock() );
+               }
+
+               return $status;
+       }
+
+       final protected function releaseItemLocks() {
+               $status = Status::newGood();
+               /** @var $item RevDelItem */
+               foreach ( $this as $item ) {
+                       $status->merge( $item->unlock() );
+               }
+
+               return $status;
+       }
+
        /**
         * Reload the list data from the master DB. This can be done after setVisibility()
         * to allow $item->getHTML() to show the new data.
@@ -351,9 +391,10 @@ abstract class RevDelList extends RevisionListBase {
        /**
         * A hook for setVisibility(): do any necessary updates post-commit.
         * STUB
+        * @param array [id => ['oldBits' => $oldBits, 'newBits' => $newBits], ... ]
         * @return Status
         */
-       public function doPostCommitUpdates() {
+       public function doPostCommitUpdates( array $visibilityChangeMap ) {
                return Status::newGood();
        }
 
index 07fe4a1..ff1d2ed 100644 (file)
@@ -40,7 +40,7 @@ class RevDelLogList extends RevDelList {
        }
 
        public static function suggestTarget( $target, array $ids ) {
-               $result = wfGetDB( DB_SLAVE )->select( 'logging',
+               $result = wfGetDB( DB_REPLICA )->select( 'logging',
                        'log_type',
                        [ 'log_id' => $ids ],
                        __METHOD__,
index 27e5148..f0b1907 100644 (file)
@@ -60,13 +60,16 @@ class RevDelRevisionList extends RevDelList {
        public function doQuery( $db ) {
                $ids = array_map( 'intval', $this->ids );
                $queryInfo = [
-                       'tables' => [ 'revision', 'user' ],
+                       'tables' => [ 'revision', 'page', 'user' ],
                        'fields' => array_merge( Revision::selectFields(), Revision::selectUserFields() ),
                        'conds' => [
                                'rev_page' => $this->title->getArticleID(),
                                'rev_id' => $ids,
                        ],
-                       'options' => [ 'ORDER BY' => 'rev_id DESC' ],
+                       'options' => [
+                               'ORDER BY' => 'rev_id DESC',
+                               'USE INDEX' => [ 'revision' => 'PRIMARY' ] // workaround for MySQL bug (T104313)
+                       ],
                        'join_conds' => [
                                'page' => Revision::pageJoinCond(),
                                'user' => Revision::userJoinCond(),
@@ -170,10 +173,10 @@ class RevDelRevisionList extends RevDelList {
                return Status::newGood();
        }
 
-       public function doPostCommitUpdates() {
+       public function doPostCommitUpdates( array $visibilityChangeMap ) {
                $this->title->purgeSquid();
                // Extensions that require referencing previous revisions may need this
-               Hooks::run( 'ArticleRevisionVisibilitySet', [ $this->title, $this->ids ] );
+               Hooks::run( 'ArticleRevisionVisibilitySet', [ $this->title, $this->ids, $visibilityChangeMap ] );
                return Status::newGood();
        }
 }
index eea0c28..b834c15 100644 (file)
@@ -213,7 +213,7 @@ class RevisionDeleter {
         * @return bool|mixed
         */
        public static function checkRevisionExistence( $title, $revid ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $exists = $dbr->selectField( 'revision', '1',
                                [ 'rev_id' => $revid ], __METHOD__ );
 
diff --git a/includes/search/DummySearchIndexFieldDefinition.php b/includes/search/DummySearchIndexFieldDefinition.php
new file mode 100644 (file)
index 0000000..a2a6760
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * Dummy implementation of SearchIndexFieldDefinition for testing purposes.
+ *
+ * @since 1.28
+ */
+class DummySearchIndexFieldDefinition extends SearchIndexFieldDefinition {
+
+       /**
+        * @param SearchEngine $engine
+        *
+        * @return array
+        */
+       public function getMapping( SearchEngine $engine ) {
+               $mapping = [
+                       'name' => $this->name,
+                       'type' => $this->type,
+                       'flags' => $this->flags,
+                       'subfields' => []
+               ];
+
+               foreach ( $this->subfields as $subfield ) {
+                       $mapping['subfields'][] = $subfield->getMapping();
+               }
+
+               return $mapping;
+       }
+
+}
diff --git a/includes/search/NullIndexField.php b/includes/search/NullIndexField.php
new file mode 100644 (file)
index 0000000..933e0ad
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Null index field - means search engine does not implement this field.
+ */
+class NullIndexField implements SearchIndexField {
+
+       /**
+        * Get mapping for specific search engine
+        * @param SearchEngine $engine
+        * @return array|null Null means this field does not map to anything
+        */
+       public function getMapping( SearchEngine $engine ) {
+               return null;
+       }
+
+       /**
+        * Set global flag for this field.
+        *
+        * @param int  $flag Bit flag to set/unset
+        * @param bool $unset True if flag should be unset, false by default
+        * @return $this
+        */
+       public function setFlag( $flag, $unset = false ) {
+       }
+
+       /**
+        * Check if flag is set.
+        * @param $flag
+        * @return int 0 if unset, !=0 if set
+        */
+       public function checkFlag( $flag ) {
+               return 0;
+       }
+
+       /**
+        * Merge two field definitions if possible.
+        *
+        * @param SearchIndexField $that
+        * @return SearchIndexField|false New definition or false if not mergeable.
+        */
+       public function merge( SearchIndexField $that ) {
+               return $that;
+       }
+}
diff --git a/includes/search/ParserOutputSearchDataExtractor.php b/includes/search/ParserOutputSearchDataExtractor.php
new file mode 100644 (file)
index 0000000..df653f1
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+
+namespace MediaWiki\Search;
+
+use Category;
+use ParserOutput;
+use Title;
+
+/**
+ * Extracts data from ParserOutput for indexing in the search engine.
+ *
+ * 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
+ *
+ * @since 1.28
+ */
+class ParserOutputSearchDataExtractor {
+
+       /**
+        * Get a list of categories, as an array with title text strings.
+        *
+        * @return string[]
+        */
+       public function getCategories( ParserOutput $parserOutput ) {
+               $categories = [];
+
+               foreach ( $parserOutput->getCategoryLinks() as $key ) {
+                       $categories[] = Category::newFromName( $key )->getTitle()->getText();
+               }
+
+               return $categories;
+       }
+
+       /**
+        * Get a list of external links from ParserOutput, as an array of strings.
+        *
+        * @return string[]
+        */
+       public function getExternalLinks( ParserOutput $parserOutput ) {
+               return array_keys( $parserOutput->getExternalLinks() );
+       }
+
+       /**
+        * Get a list of outgoing wiki links (including interwiki links), as
+        * an array of prefixed title strings.
+        *
+        * @return string[]
+        */
+       public function getOutgoingLinks( ParserOutput $parserOutput ) {
+               $outgoingLinks = [];
+
+               foreach ( $parserOutput->getLinks() as $linkedNamespace => $namespaceLinks ) {
+                       foreach ( array_keys( $namespaceLinks ) as $linkedDbKey ) {
+                               $outgoingLinks[] =
+                                       Title::makeTitle( $linkedNamespace, $linkedDbKey )->getPrefixedDBkey();
+                       }
+               }
+
+               return $outgoingLinks;
+       }
+
+       /**
+        * Get a list of templates used in the ParserOutput content, as prefixed title strings
+        *
+        * @return string[]
+        */
+       public function getTemplates( ParserOutput $parserOutput ) {
+               $templates = [];
+
+               foreach ( $parserOutput->getTemplates() as $tNS => $templatesInNS ) {
+                       foreach ( array_keys( $templatesInNS ) as $tDbKey ) {
+                               $templateTitle = Title::makeTitle( $tNS, $tDbKey );
+                               $templates[] = $templateTitle->getPrefixedText();
+                       }
+               }
+
+               return $templates;
+       }
+
+}
index 5b18b7c..38c60d0 100644 (file)
@@ -40,7 +40,7 @@ class SearchDatabase extends SearchEngine {
                if ( $db ) {
                        $this->db = $db;
                } else {
-                       $this->db = wfGetDB( DB_SLAVE );
+                       $this->db = wfGetDB( DB_REPLICA );
                }
        }
 
index 0171ed9..c2ccca0 100644 (file)
@@ -655,6 +655,46 @@ abstract class SearchEngine {
                return null;
        }
 
+       /**
+        * Create a search field definition.
+        * Specific search engines should override this method to create search fields.
+        * @param string $name
+        * @param int    $type One of the types in SearchIndexField::INDEX_TYPE_*
+        * @return SearchIndexField
+        * @since 1.28
+        */
+       public function makeSearchFieldMapping( $name, $type ) {
+               return new NullIndexField();
+       }
+
+       /**
+        * Get fields for search index
+        * @since 1.28
+        * @return SearchIndexField[] Index field definitions for all content handlers
+        */
+       public function getSearchIndexFields() {
+               $models = ContentHandler::getContentModels();
+               $fields = [];
+               foreach ( $models as $model ) {
+                       $handler = ContentHandler::getForModelID( $model );
+                       $handlerFields = $handler->getFieldsForSearchIndex( $this );
+                       foreach ( $handlerFields as $fieldName => $fieldData ) {
+                               if ( empty( $fields[$fieldName] ) ) {
+                                       $fields[$fieldName] = $fieldData;
+                               } else {
+                                       // TODO: do we allow some clashes with the same type or reject all of them?
+                                       $mergeDef = $fields[$fieldName]->merge( $fieldData );
+                                       if ( !$mergeDef ) {
+                                               throw new InvalidArgumentException( "Duplicate field $fieldName for model $model" );
+                                       }
+                                       $fields[$fieldName] = $mergeDef;
+                               }
+                       }
+               }
+               // Hook to allow extensions to produce search mapping fields
+               Hooks::run( 'SearchIndexFields', [ &$fields, $this ] );
+               return $fields;
+       }
 }
 
 /**
index 67f500c..e30869e 100644 (file)
@@ -32,7 +32,7 @@ class SearchEngineFactory {
                } elseif ( $configType !== null ) {
                        $class = $configType;
                } else {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $class = $dbr->getSearchEngine();
                }
 
index 2bd1955..dd41a6e 100644 (file)
@@ -34,10 +34,11 @@ class SearchHighlighter {
        }
 
        /**
-        * Default implementation of wikitext highlighting
+        * Wikitext highlighting when $wgAdvancedSearchHighlighting = true
         *
         * @param string $text
-        * @param array $terms Terms to highlight (unescaped)
+        * @param array $terms Terms to highlight (not html escaped but
+        *   regex escaped via SearchDatabase::regexTerm())
         * @param int $contextlines
         * @param int $contextchars
         * @return string
@@ -145,7 +146,6 @@ class SearchHighlighter {
                }
                $anyterm = implode( '|', $terms );
                $phrase = implode( "$wgSearchHighlightBoundaries+", $terms );
-
                // @todo FIXME: A hack to scale contextchars, a correct solution
                // would be to have contextchars actually be char and not byte
                // length, and do proper utf-8 substrings and lengths everywhere,
@@ -485,8 +485,10 @@ class SearchHighlighter {
         * Simple & fast snippet extraction, but gives completely unrelevant
         * snippets
         *
+        * Used when $wgAdvancedSearchHighlighting is false.
+        *
         * @param string $text
-        * @param array $terms
+        * @param array $terms Escaped for regex by SearchDatabase::regexTerm()
         * @param int $contextlines
         * @param int $contextchars
         * @return string
diff --git a/includes/search/SearchIndexField.php b/includes/search/SearchIndexField.php
new file mode 100644 (file)
index 0000000..6806ee5
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+/**
+ * Definition of a mapping for the search index field.
+ * @since 1.28
+ */
+interface SearchIndexField {
+       /**
+        * Field types
+        */
+       const INDEX_TYPE_TEXT = 0;
+       const INDEX_TYPE_KEYWORD = 1;
+       const INDEX_TYPE_INTEGER = 2;
+       const INDEX_TYPE_NUMBER = 3;
+       const INDEX_TYPE_DATETIME = 4;
+       const INDEX_TYPE_NESTED = 5;
+       const INDEX_TYPE_BOOL = 6;
+       /**
+        * Generic field flags.
+        */
+       /**
+        * This field is case-insensitive.
+        */
+       const FLAG_CASEFOLD = 1;
+       /**
+        * This field contains secondary information, which is
+        * already present in other fields, but can be used for
+        * scoring.
+        */
+       const FLAG_SCORING = 2;
+       /**
+        * This field does not need highlight handling.
+        */
+       const FLAG_NO_HIGHLIGHT = 4;
+       /**
+        * Do not index this field, just store it.
+        */
+       const FLAG_NO_INDEX = 8;
+
+       /**
+        * Get mapping for specific search engine
+        * @param SearchEngine $engine
+        * @return array|null Null means this field does not map to anything
+        */
+       public function getMapping( SearchEngine $engine );
+       /**
+        * Set global flag for this field.
+        *
+        * @param int  $flag Bit flag to set/unset
+        * @param bool $unset True if flag should be unset, false by default
+        * @return $this
+        */
+       public function setFlag( $flag, $unset = false );
+       /**
+        * Check if flag is set.
+        * @param $flag
+        * @return int 0 if unset, !=0 if set
+        */
+       public function checkFlag( $flag );
+       /**
+        * Merge two field definitions if possible.
+        *
+        * @param SearchIndexField $that
+        * @return SearchIndexField|false New definition or false if not mergeable.
+        */
+       public function merge( SearchIndexField $that );
+}
diff --git a/includes/search/SearchIndexFieldDefinition.php b/includes/search/SearchIndexFieldDefinition.php
new file mode 100644 (file)
index 0000000..8a06b65
--- /dev/null
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * Basic infrastructure of the field definition.
+ *
+ * Specific engines should extend this class and at at least,
+ * override the getMapping method, but can reuse other parts.
+ *
+ * @since 1.28
+ */
+abstract class SearchIndexFieldDefinition implements SearchIndexField {
+       /**
+        * Name of the field
+        *
+        * @var string
+        */
+       protected $name;
+       /**
+        * Type of the field, one of the constants above
+        *
+        * @var int
+        */
+       protected $type;
+       /**
+        * Bit flags for the field.
+        *
+        * @var int
+        */
+       protected $flags = 0;
+       /**
+        * Subfields
+        * @var SearchIndexFieldDefinition[]
+        */
+       protected $subfields = [];
+
+       /**
+        * SearchIndexFieldDefinition constructor.
+        * @param string $name Field name
+        * @param int    $type Index type
+        */
+       public function __construct( $name, $type ) {
+               $this->name = $name;
+               $this->type = $type;
+       }
+
+       /**
+        * Get field name
+        * @return string
+        */
+       public function getName() {
+               return $this->name;
+       }
+
+       /**
+        * Get index type
+        * @return int
+        */
+       public function getIndexType() {
+               return $this->type;
+       }
+
+       /**
+        * Set global flag for this field.
+        *
+        * @param int  $flag Bit flag to set/unset
+        * @param bool $unset True if flag should be unset, false by default
+        * @return $this
+        */
+       public function setFlag( $flag, $unset = false ) {
+               if ( $unset ) {
+                       $this->flags &= ~$flag;
+               } else {
+                       $this->flags |= $flag;
+               }
+               return $this;
+       }
+
+       /**
+        * Check if flag is set.
+        * @param $flag
+        * @return int 0 if unset, !=0 if set
+        */
+       public function checkFlag( $flag ) {
+               return $this->flags & $flag;
+       }
+
+       /**
+        * Merge two field definitions if possible.
+        *
+        * @param SearchIndexField $that
+        * @return SearchIndexField|false New definition or false if not mergeable.
+        */
+       public function merge( SearchIndexField $that ) {
+               // TODO: which definitions may be compatible?
+               if ( ( $that instanceof self ) && $this->type === $that->type &&
+                    $this->flags === $that->flags && $this->type !== self::INDEX_TYPE_NESTED
+               ) {
+                       return $that;
+               }
+               return false;
+       }
+
+       /**
+        * Get subfields
+        * @return SearchIndexFieldDefinition[]
+        */
+       public function getSubfields() {
+               return $this->subfields;
+       }
+
+       /**
+        * Set subfields
+        * @param SearchIndexFieldDefinition[] $subfields
+        * @return $this
+        */
+       public function setSubfields( array $subfields ) {
+               $this->subfields = $subfields;
+               return $this;
+       }
+
+       /**
+        * @param SearchEngine $engine
+        *
+        * @return array
+        */
+       abstract public function getMapping( SearchEngine $engine );
+
+}
index 7e82378..36cbbaa 100644 (file)
@@ -437,7 +437,7 @@ class SearchMySQL extends SearchDatabase {
                if ( is_null( self::$mMinSearchLength ) ) {
                        $sql = "SHOW GLOBAL VARIABLES LIKE 'ft\\_min\\_word\\_len'";
 
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $result = $dbr->query( $sql, __METHOD__ );
                        $row = $result->fetchObject();
                        $result->free();
index 719f905..31761c3 100644 (file)
@@ -46,6 +46,9 @@ use WebRequest;
  * @since 1.27
  */
 final class Session implements \Countable, \Iterator, \ArrayAccess {
+       /** @var null|string[] Encryption algorithm to use */
+       private static $encryptionAlgorithm = null;
+
        /** @var SessionBackend Session backend */
        private $backend;
 
@@ -126,6 +129,11 @@ final class Session implements \Countable, \Iterator, \ArrayAccess {
 
        /**
         * Make this session not be persisted across requests
+        *
+        * This will remove persistence information (e.g. delete cookies)
+        * from the associated WebRequest(s), and delete session data in the
+        * backend. The session data will still be available via get() until
+        * the end of the request.
         */
        public function unpersist() {
                $this->backend->unpersist();
@@ -409,24 +417,42 @@ final class Session implements \Countable, \Iterator, \ArrayAccess {
         * Decide what type of encryption to use, based on system capabilities.
         * @return array
         */
-       private function getEncryptionAlgorithm() {
+       private static function getEncryptionAlgorithm() {
                global $wgSessionInsecureSecrets;
 
-               if (
-                       function_exists( 'openssl_encrypt' )
-                       && in_array( 'aes-256-ctr', openssl_get_cipher_methods(), true )
-               ) {
-                       return [ 'openssl', 'aes-256-ctr' ];
-               } elseif (
-                       function_exists( 'mcrypt_encrypt' )
-                       && in_array( 'rijndael-128', mcrypt_list_algorithms(), true )
-                       && in_array( 'ctr', mcrypt_list_modes(), true )
-               ) {
-                       return [ 'mcrypt', 'rijndael-128', 'ctr' ];
-               } elseif ( $wgSessionInsecureSecrets ) {
-                       // @todo: import a pure-PHP library for AES instead of this
-                       return [ 'insecure' ];
-               } else {
+               if ( self::$encryptionAlgorithm === null ) {
+                       if ( function_exists( 'openssl_encrypt' ) ) {
+                               $methods = openssl_get_cipher_methods();
+                               if ( in_array( 'aes-256-ctr', $methods, true ) ) {
+                                       self::$encryptionAlgorithm = [ 'openssl', 'aes-256-ctr' ];
+                                       return self::$encryptionAlgorithm;
+                               }
+                               if ( in_array( 'aes-256-cbc', $methods, true ) ) {
+                                       self::$encryptionAlgorithm = [ 'openssl', 'aes-256-cbc' ];
+                                       return self::$encryptionAlgorithm;
+                               }
+                       }
+
+                       if ( function_exists( 'mcrypt_encrypt' )
+                               && in_array( 'rijndael-128', mcrypt_list_algorithms(), true )
+                       ) {
+                               $modes = mcrypt_list_modes();
+                               if ( in_array( 'ctr', $modes, true ) ) {
+                                       self::$encryptionAlgorithm = [ 'mcrypt', 'rijndael-128', 'ctr' ];
+                                       return self::$encryptionAlgorithm;
+                               }
+                               if ( in_array( 'cbc', $modes, true ) ) {
+                                       self::$encryptionAlgorithm = [ 'mcrypt', 'rijndael-128', 'cbc' ];
+                                       return self::$encryptionAlgorithm;
+                               }
+                       }
+
+                       if ( $wgSessionInsecureSecrets ) {
+                               // @todo: import a pure-PHP library for AES instead of this
+                               self::$encryptionAlgorithm = [ 'insecure' ];
+                               return self::$encryptionAlgorithm;
+                       }
+
                        throw new \BadMethodCallException(
                                'Encryption is not available. You really should install the PHP OpenSSL extension, ' .
                                'or failing that the mcrypt extension. But if you really can\'t and you\'re willing ' .
@@ -435,6 +461,7 @@ final class Session implements \Countable, \Iterator, \ArrayAccess {
                        );
                }
 
+               return self::$encryptionAlgorithm;
        }
 
        /**
@@ -455,7 +482,7 @@ final class Session implements \Countable, \Iterator, \ArrayAccess {
                // Encrypt
                // @todo: import a pure-PHP library for AES instead of doing $wgSessionInsecureSecrets
                $iv = \MWCryptRand::generate( 16, true );
-               $algorithm = $this->getEncryptionAlgorithm();
+               $algorithm = self::getEncryptionAlgorithm();
                switch ( $algorithm[0] ) {
                        case 'openssl':
                                $ciphertext = openssl_encrypt( $serialized, $algorithm[1], $encKey, OPENSSL_RAW_DATA, $iv );
@@ -464,6 +491,11 @@ final class Session implements \Countable, \Iterator, \ArrayAccess {
                                }
                                break;
                        case 'mcrypt':
+                               // PKCS7 padding
+                               $blocksize = mcrypt_get_block_size( $algorithm[1], $algorithm[2] );
+                               $pad = $blocksize - ( strlen( $serialized ) % $blocksize );
+                               $serialized .= str_repeat( chr( $pad ), $pad );
+
                                $ciphertext = mcrypt_encrypt( $algorithm[1], $encKey, $serialized, $algorithm[2], $iv );
                                if ( $ciphertext === false ) {
                                        throw new \UnexpectedValueException( 'Encryption failed' );
@@ -521,7 +553,7 @@ final class Session implements \Countable, \Iterator, \ArrayAccess {
                }
 
                // Decrypt
-               $algorithm = $this->getEncryptionAlgorithm();
+               $algorithm = self::getEncryptionAlgorithm();
                switch ( $algorithm[0] ) {
                        case 'openssl':
                                $serialized = openssl_decrypt( base64_decode( $ciphertext ), $algorithm[1], $encKey,
@@ -540,6 +572,10 @@ final class Session implements \Countable, \Iterator, \ArrayAccess {
                                        $this->logger->debug( $ex->getMessage(), [ 'exception' => $ex ] );
                                        return $default;
                                }
+
+                               // Remove PKCS7 padding
+                               $pad = ord( substr( $serialized, -1 ) );
+                               $serialized = substr( $serialized, 0, -$pad );
                                break;
                        case 'insecure':
                                $ex = new \Exception(
@@ -572,6 +608,9 @@ final class Session implements \Countable, \Iterator, \ArrayAccess {
 
        /**
         * Save the session
+        *
+        * This will update the backend data and might re-persist the session
+        * if needed.
         */
        public function save() {
                $this->backend->save();
index 264e1ae..263cb11 100644 (file)
@@ -599,7 +599,8 @@ final class SessionBackend {
        }
 
        /**
-        * Save and persist session data, unless delayed
+        * Save the session, unless delayed
+        * @see SessionBackend::save()
         */
        private function autosave() {
                if ( $this->delaySave <= 0 ) {
@@ -608,7 +609,12 @@ final class SessionBackend {
        }
 
        /**
-        * Save and persist session data
+        * Save the session
+        *
+        * Update both the backend data and the associated WebRequest(s) to
+        * reflect the state of the the SessionBackend. This might include
+        * persisting or unpersisting the session.
+        *
         * @param bool $closing Whether the session is being closed
         */
        public function save( $closing = false ) {
@@ -716,6 +722,8 @@ final class SessionBackend {
                        }
                }
 
+               $flags = $this->persist ? 0 : CachedBagOStuff::WRITE_CACHE_ONLY;
+               $flags |= CachedBagOStuff::WRITE_SYNC; // write to all datacenters
                $this->store->set(
                        wfMemcKey( 'MWSession', (string)$this->id ),
                        [
@@ -723,7 +731,7 @@ final class SessionBackend {
                                'metadata' => $metadata,
                        ],
                        $metadata['expires'],
-                       $this->persist ? 0 : CachedBagOStuff::WRITE_CACHE_ONLY
+                       $flags
                );
 
                $this->metaDirty = false;
index c235861..287da9d 100644 (file)
@@ -73,7 +73,8 @@ class SessionInfo {
         *    Defaults to true.
         *  - forceHTTPS: (bool) Whether to force HTTPS for this session
         *  - metadata: (array) Provider metadata, to be returned by
-        *    Session::getProviderMetadata().
+        *    Session::getProviderMetadata(). See SessionProvider::mergeMetadata()
+        *    and SessionProvider::refreshSessionInfo().
         *  - idIsSafe: (bool) Set true if the 'id' did not come from the user.
         *    Generally you'll use this from SessionProvider::newEmptySession(),
         *    and not from any other method.
@@ -200,7 +201,8 @@ class SessionInfo {
         * The normal behavior is to discard the SessionInfo if validation against
         * the data stored in the session store fails. If this returns true,
         * SessionManager will instead delete the session store data so this
-        * SessionInfo may still be used.
+        * SessionInfo may still be used. This is important for providers which use
+        * deterministic IDs and so cannot just generate a random new one.
         *
         * @return bool
         */
index 3e5ef3b..87fdcd3 100644 (file)
@@ -35,8 +35,15 @@ use WebRequest;
 /**
  * This serves as the entry point to the MediaWiki session handling system.
  *
+ * Most methods here are for internal use by session handling code. Other callers
+ * should only use getGlobalSession and the methods of SessionManagerInterface;
+ * the rest of the functionality is exposed via MediaWiki\Session\Session methods.
+ *
+ * To provide custom session handling, implement a MediaWiki\Session\SessionProvider.
+ *
  * @ingroup Session
  * @since 1.27
+ * @see https://www.mediawiki.org/wiki/Manual:SessionManager_and_AuthManager
  */
 final class SessionManager implements SessionManagerInterface {
        /** @var SessionManager|null */
@@ -374,202 +381,15 @@ final class SessionManager implements SessionManagerInterface {
         * @deprecated since 1.27, use MediaWiki\Auth\AuthManager::autoCreateUser instead
         * @param User $user User to auto-create
         * @return bool Success
+        * @codeCoverageIgnore
         */
        public static function autoCreateUser( User $user ) {
-               global $wgAuth, $wgDisableAuthManager;
-
-               // @codeCoverageIgnoreStart
-               if ( !$wgDisableAuthManager ) {
-                       wfDeprecated( __METHOD__, '1.27' );
-                       return \MediaWiki\Auth\AuthManager::singleton()->autoCreateUser(
-                               $user,
-                               \MediaWiki\Auth\AuthManager::AUTOCREATE_SOURCE_SESSION,
-                               false
-                       )->isGood();
-               }
-               // @codeCoverageIgnoreEnd
-
-               $logger = self::singleton()->logger;
-
-               // Much of this code is based on that in CentralAuth
-
-               // Try the local user from the slave DB
-               $localId = User::idFromName( $user->getName() );
-               $flags = 0;
-
-               // Fetch the user ID from the master, so that we don't try to create the user
-               // when they already exist, due to replication lag
-               // @codeCoverageIgnoreStart
-               if ( !$localId && wfGetLB()->getReaderIndex() != 0 ) {
-                       $localId = User::idFromName( $user->getName(), User::READ_LATEST );
-                       $flags = User::READ_LATEST;
-               }
-               // @codeCoverageIgnoreEnd
-
-               if ( $localId ) {
-                       // User exists after all.
-                       $user->setId( $localId );
-                       $user->loadFromId( $flags );
-                       return false;
-               }
-
-               // Denied by AuthPlugin? But ignore AuthPlugin itself.
-               if ( get_class( $wgAuth ) !== 'AuthPlugin' && !$wgAuth->autoCreate() ) {
-                       $logger->debug( __METHOD__ . ': denied by AuthPlugin' );
-                       $user->setId( 0 );
-                       $user->loadFromId();
-                       return false;
-               }
-
-               // Wiki is read-only?
-               if ( wfReadOnly() ) {
-                       $logger->debug( __METHOD__ . ': denied by wfReadOnly()' );
-                       $user->setId( 0 );
-                       $user->loadFromId();
-                       return false;
-               }
-
-               $userName = $user->getName();
-
-               // Check the session, if we tried to create this user already there's
-               // no point in retrying.
-               $session = self::getGlobalSession();
-               $reason = $session->get( 'MWSession::AutoCreateBlacklist' );
-               if ( $reason ) {
-                       $logger->debug( __METHOD__ . ": blacklisted in session ($reason)" );
-                       $user->setId( 0 );
-                       $user->loadFromId();
-                       return false;
-               }
-
-               // Is the IP user able to create accounts?
-               $anon = new User;
-               if ( !$anon->isAllowedAny( 'createaccount', 'autocreateaccount' )
-                       || $anon->isBlockedFromCreateAccount()
-               ) {
-                       // Blacklist the user to avoid repeated DB queries subsequently
-                       $logger->debug( __METHOD__ . ': user is blocked from this wiki, blacklisting' );
-                       $session->set( 'MWSession::AutoCreateBlacklist', 'blocked', 600 );
-                       $session->persist();
-                       $user->setId( 0 );
-                       $user->loadFromId();
-                       return false;
-               }
-
-               // Check for validity of username
-               if ( !User::isCreatableName( $userName ) ) {
-                       $logger->debug( __METHOD__ . ': Invalid username, blacklisting' );
-                       $session->set( 'MWSession::AutoCreateBlacklist', 'invalid username', 600 );
-                       $session->persist();
-                       $user->setId( 0 );
-                       $user->loadFromId();
-                       return false;
-               }
-
-               // Give other extensions a chance to stop auto creation.
-               $user->loadDefaults( $userName );
-               $abortMessage = '';
-               if ( !\Hooks::run( 'AbortAutoAccount', [ $user, &$abortMessage ] ) ) {
-                       // In this case we have no way to return the message to the user,
-                       // but we can log it.
-                       $logger->debug( __METHOD__ . ": denied by hook: $abortMessage" );
-                       $session->set( 'MWSession::AutoCreateBlacklist', "hook aborted: $abortMessage", 600 );
-                       $session->persist();
-                       $user->setId( 0 );
-                       $user->loadFromId();
-                       return false;
-               }
-
-               // Make sure the name has not been changed
-               if ( $user->getName() !== $userName ) {
-                       $user->setId( 0 );
-                       $user->loadFromId();
-                       throw new \UnexpectedValueException(
-                               'AbortAutoAccount hook tried to change the user name'
-                       );
-               }
-
-               // Ignore warnings about master connections/writes...hard to avoid here
-               \Profiler::instance()->getTransactionProfiler()->resetExpectations();
-
-               $cache = \ObjectCache::getLocalClusterInstance();
-               $backoffKey = wfMemcKey( 'MWSession', 'autocreate-failed', md5( $userName ) );
-               if ( $cache->get( $backoffKey ) ) {
-                       $logger->debug( __METHOD__ . ': denied by prior creation attempt failures' );
-                       $user->setId( 0 );
-                       $user->loadFromId();
-                       return false;
-               }
-
-               // Checks passed, create the user...
-               $from = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : 'CLI';
-               $logger->info( __METHOD__ . ': creating new user ({username}) - from: {url}',
-                       [
-                               'username' => $userName,
-                               'url' => $from,
-               ] );
-
-               try {
-                       // Insert the user into the local DB master
-                       $status = $user->addToDatabase();
-                       if ( !$status->isOK() ) {
-                               // @codeCoverageIgnoreStart
-                               // double-check for a race condition (T70012)
-                               $id = User::idFromName( $user->getName(), User::READ_LATEST );
-                               if ( $id ) {
-                                       $logger->info( __METHOD__ . ': tried to autocreate existing user',
-                                               [
-                                                       'username' => $userName,
-                                               ] );
-                               } else {
-                                       $logger->error(
-                                               __METHOD__ . ': failed with message ' . $status->getWikiText( false, false, 'en' ),
-                                               [
-                                                       'username' => $userName,
-                                               ]
-                                       );
-                               }
-                               $user->setId( $id );
-                               $user->loadFromId( User::READ_LATEST );
-                               return false;
-                               // @codeCoverageIgnoreEnd
-                       }
-               } catch ( \Exception $ex ) {
-                       // @codeCoverageIgnoreStart
-                       $logger->error( __METHOD__ . ': failed with exception {exception}', [
-                               'exception' => $ex,
-                               'username' => $userName,
-                       ] );
-                       // Do not keep throwing errors for a while
-                       $cache->set( $backoffKey, 1, 600 );
-                       // Bubble up error; which should normally trigger DB rollbacks
-                       throw $ex;
-                       // @codeCoverageIgnoreEnd
-               }
-
-               # Notify AuthPlugin
-               // @codeCoverageIgnoreStart
-               $tmpUser = $user;
-               $wgAuth->initUser( $tmpUser, true );
-               if ( $tmpUser !== $user ) {
-                       $logger->warning( __METHOD__ . ': ' .
-                               get_class( $wgAuth ) . '::initUser() replaced the user object' );
-               }
-               // @codeCoverageIgnoreEnd
-
-               # Notify hooks (e.g. Newuserlog)
-               \Hooks::run( 'AuthPluginAutoCreate', [ $user ] );
-               \Hooks::run( 'LocalUserCreated', [ $user, true ] );
-
-               $user->saveSettings();
-
-               # Update user count
-               \DeferredUpdates::addUpdate( new \SiteStatsUpdate( 0, 0, 0, 0, 1 ) );
-
-               # Watch user's userpage and talk page
-               $user->addWatch( $user->getUserPage(), User::IGNORE_USER_RIGHTS );
-
-               return true;
+               wfDeprecated( __METHOD__, '1.27' );
+               return \MediaWiki\Auth\AuthManager::singleton()->autoCreateUser(
+                       $user,
+                       \MediaWiki\Auth\AuthManager::AUTOCREATE_SOURCE_SESSION,
+                       false
+               )->isGood();
        }
 
        /**
@@ -1006,9 +826,9 @@ final class SessionManager implements SessionManagerInterface {
        }
 
        /**
-        * Create a session corresponding to the passed SessionInfo
+        * Create a Session corresponding to the passed SessionInfo
         * @private For use by a SessionProvider that needs to specially create its
-        *  own session.
+        *  own Session. Most session providers won't need this.
         * @param SessionInfo $info
         * @param WebRequest $request
         * @return Session
@@ -1128,6 +948,7 @@ final class SessionManager implements SessionManagerInterface {
 
        /**
         * Reset the internal caching for unit testing
+        * @protected Unit tests only
         */
        public static function resetCache() {
                if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
index d4e52c7..3ab0f43 100644 (file)
@@ -36,7 +36,8 @@ use WebRequest;
  */
 interface SessionManagerInterface extends LoggerAwareInterface {
        /**
-        * Fetch the session for a request
+        * Fetch the session for a request (or a new empty session if none is
+        * attached to it)
         *
         * @note You probably want to use $request->getSession() instead. It's more
         *  efficient and doesn't break FauxRequests or sessions that were changed
@@ -52,6 +53,7 @@ interface SessionManagerInterface extends LoggerAwareInterface {
 
        /**
         * Fetch a session by ID
+        *
         * @param string $id
         * @param bool $create If no session exists for $id, try to create a new one.
         *  May still return null if a session for $id exists but cannot be loaded.
@@ -62,7 +64,7 @@ interface SessionManagerInterface extends LoggerAwareInterface {
        public function getSessionById( $id, $create = false, WebRequest $request = null );
 
        /**
-        * Fetch a new, empty session
+        * Create a new, empty session
         *
         * The first provider configured that is able to provide an empty session
         * will be used.
index 4d57ad9..61c7500 100644 (file)
@@ -66,13 +66,14 @@ use WebRequest;
  *    would make sense.
  *
  * Note that many methods that are technically "cannot persist ID" could be
- * turned into "can persist ID but not changing User" using a session cookie,
+ * turned into "can persist ID but not change User" using a session cookie,
  * as implemented by ImmutableSessionProviderWithCookie. If doing so, different
  * session cookie names should be used for different providers to avoid
  * collisions.
  *
  * @ingroup Session
  * @since 1.27
+ * @see https://www.mediawiki.org/wiki/Manual:SessionManager_and_AuthManager
  */
 abstract class SessionProvider implements SessionProviderInterface, LoggerAwareInterface {
 
@@ -180,14 +181,23 @@ abstract class SessionProvider implements SessionProviderInterface, LoggerAwareI
        /**
         * Merge saved session provider metadata
         *
+        * This method will be used to compare the metadata returned by
+        * provideSessionInfo() with the saved metadata (which has been returned by
+        * provideSessionInfo() the last time the session was saved), and merge the two
+        * into the new saved metadata, or abort if the current request is not a valid
+        * continuation of the session.
+        *
         * The default implementation checks that anything in both arrays is
         * identical, then returns $providedMetadata.
         *
         * @protected For use by \MediaWiki\Session\SessionManager only
         * @param array $savedMetadata Saved provider metadata
-        * @param array $providedMetadata Provided provider metadata
+        * @param array $providedMetadata Provided provider metadata (from the SessionInfo)
         * @return array Resulting metadata
-        * @throws MetadataMergeException If the metadata cannot be merged
+        * @throws MetadataMergeException If the metadata cannot be merged.
+        *  Such exceptions will be handled by SessionManager and are a safe way of rejecting
+        *  a suspicious or incompatible session. The provider is expected to write an
+        *  appropriate message to its logger.
         */
        public function mergeMetadata( array $savedMetadata, array $providedMetadata ) {
                foreach ( $providedMetadata as $k => $v ) {
@@ -211,7 +221,7 @@ abstract class SessionProvider implements SessionProviderInterface, LoggerAwareI
         * expected to write an appropriate message to its logger.
         *
         * @protected For use by \MediaWiki\Session\SessionManager only
-        * @param SessionInfo $info
+        * @param SessionInfo $info Any changes by mergeMetadata() will already be reflected here.
         * @param WebRequest $request
         * @param array|null &$metadata Provider metadata, may be altered.
         * @return bool Return false to reject the SessionInfo after all.
@@ -420,6 +430,11 @@ abstract class SessionProvider implements SessionProviderInterface, LoggerAwareI
 
        /**
         * Fetch the rights allowed the user when the specified session is active.
+        *
+        * This is mainly meant for allowing the user to restrict access to the account
+        * by certain methods; you probably want to use this with MWGrants. The returned
+        * rights will be intersected with the user's actual rights.
+        *
         * @param SessionBackend $backend
         * @return null|string[] Allowed user rights, or null to allow all.
         */
index 974789f..432d5ce 100644 (file)
@@ -73,7 +73,7 @@ class DBSiteStore implements SiteStore {
        protected function loadSites() {
                $this->sites = new SiteList();
 
-               $dbr = $this->dbLoadBalancer->getConnection( DB_SLAVE );
+               $dbr = $this->dbLoadBalancer->getConnection( DB_REPLICA );
 
                $res = $dbr->select(
                        'sites',
index a4116ae..2f8a113 100644 (file)
@@ -35,8 +35,8 @@ class SiteSQLStore {
         * @note This does not return an instance of SiteSQLStore!
         *
         * @since 1.21
-        * @deprecated 1.27 use MediaWikiServices::getSiteStore() or MediaWikiServices::getSiteLookup()
-        *             instead.
+        * @deprecated since 1.27 use MediaWikiServices::getSiteStore()
+        *             or MediaWikiServices::getSiteLookup() instead.
         *
         * @param null $sitesTable IGNORED
         * @param null $cache IGNORED
index 3408db3..2d37a0f 100644 (file)
@@ -140,7 +140,7 @@ abstract class BaseTemplate extends QuickTemplate {
                        if ( isset( $plink['active'] ) ) {
                                $ptool['active'] = $plink['active'];
                        }
-                       foreach ( [ 'href', 'class', 'text', 'dir' ] as $k ) {
+                       foreach ( [ 'href', 'class', 'text', 'dir', 'data' ] as $k ) {
                                if ( isset( $plink[$k] ) ) {
                                        $ptool['links'][0][$k] = $plink[$k];
                                }
@@ -318,13 +318,22 @@ abstract class BaseTemplate extends QuickTemplate {
         *
         * If you don't want an accesskey, set $item['tooltiponly'] = true;
         *
+        * If a "data" key is present, it must be an array, where the keys represent
+        * the data-xxx properties with their provided values. For example,
+        *  $item['data'] = [
+        *       'foo' => 1,
+        *       'bar' => 'baz',
+        *  ];
+        * will render as element properties:
+        *  data-foo='1' data-bar='baz'
+        *
         * @param array $options Can be used to affect the output of a link.
         * Possible options are:
         *   - 'text-wrapper' key to specify a list of elements to wrap the text of
         *   a link in. This should be an array of arrays containing a 'tag' and
         *   optionally an 'attributes' key. If you only have one element you don't
         *   need to wrap it in another array. eg: To use <a><span>...</span></a>
-        *   in all links use array( 'text-wrapper' => array( 'tag' => 'span' ) )
+        *   in all links use [ 'text-wrapper' => [ 'tag' => 'span' ] ]
         *   for your options.
         *   - 'link-class' key can be used to specify additional classes to apply
         *   to all links.
@@ -363,6 +372,13 @@ abstract class BaseTemplate extends QuickTemplate {
                                unset( $attrs[$k] );
                        }
 
+                       if ( isset( $attrs['data'] ) ) {
+                               foreach ( $attrs['data'] as $key => $value ) {
+                                       $attrs[ 'data-' . $key ] = $value;
+                               }
+                               unset( $attrs[ 'data' ] );
+                       }
+
                        if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) {
                                $item['single-id'] = $item['id'];
                        }
@@ -425,7 +441,8 @@ abstract class BaseTemplate extends QuickTemplate {
         * list item directly so they will not be passed to makeLink
         * (however the link will still support a tooltip and accesskey from it)
         * If you need an id or class on a single link you should include a "links"
-        * array with just one link item inside of it. If you want to add a title
+        * array with just one link item inside of it. You can also set "link-class" in
+        * $item to set a class on the link itself. If you want to add a title
         * to the list item itself, you can set "itemtitle" to the value.
         * $options is also passed on to makeLink calls
         *
@@ -450,6 +467,12 @@ abstract class BaseTemplate extends QuickTemplate {
                                // generating tooltips and accesskeys.
                                $link['single-id'] = $item['id'];
                        }
+                       if ( isset( $link['link-class'] ) ) {
+                               // link-class should be set on the <a> itself,
+                               // so pass it in as 'class'
+                               $link['class'] = $link['link-class'];
+                               unset( $link['link-class'] );
+                       }
                        $html = $this->makeLink( $key, $link, $options );
                }
 
index 729f564..b60aa10 100644 (file)
@@ -846,7 +846,7 @@ abstract class Skin extends ContextSource {
                        $s = '';
                }
 
-               if ( wfGetLB()->getLaggedSlaveMode() ) {
+               if ( wfGetLB()->getLaggedReplicaMode() ) {
                        $s .= ' <strong>' . $this->msg( 'laggedslavemode' )->parse() . '</strong>';
                }
 
@@ -1169,7 +1169,7 @@ abstract class Skin extends ContextSource {
         *
         * BaseTemplate::getSidebar can be used to simplify the format and id generation in new skins.
         *
-        * The format of the returned array is array( heading => content, ... ), where:
+        * The format of the returned array is [ heading => content, ... ], where:
         * - heading is the heading of a navigation portlet. It is either:
         *   - magic string to be handled by the skins ('SEARCH' / 'LANGUAGES' / 'TOOLBOX' / ...)
         *   - a message name (e.g. 'navigation'), the message should be HTML-escaped by the skin
@@ -1241,6 +1241,8 @@ abstract class Skin extends ContextSource {
                $lines = explode( "\n", $text );
 
                $heading = '';
+               $messageTitle = $this->getConfig()->get( 'EnableSidebarCache' )
+                       ? Title::newMainPage() : $this->getTitle();
 
                foreach ( $lines as $line ) {
                        if ( strpos( $line, '*' ) !== 0 ) {
@@ -1257,7 +1259,7 @@ abstract class Skin extends ContextSource {
                                $line = trim( $line, '* ' );
 
                                if ( strpos( $line, '|' ) !== false ) { // sanity check
-                                       $line = MessageCache::singleton()->transform( $line, false, null, $this->getTitle() );
+                                       $line = MessageCache::singleton()->transform( $line, false, null, $messageTitle );
                                        $line = array_map( 'trim', explode( '|', $line, 2 ) );
                                        if ( count( $line ) !== 2 ) {
                                                // Second sanity check, could be hit by people doing
@@ -1267,7 +1269,7 @@ abstract class Skin extends ContextSource {
 
                                        $extraAttribs = [];
 
-                                       $msgLink = $this->msg( $line[0] )->inContentLanguage();
+                                       $msgLink = $this->msg( $line[0] )->title( $messageTitle )->inContentLanguage();
                                        if ( $msgLink->exists() ) {
                                                $link = $msgLink->text();
                                                if ( $link == '-' ) {
@@ -1276,7 +1278,7 @@ abstract class Skin extends ContextSource {
                                        } else {
                                                $link = $line[0];
                                        }
-                                       $msgText = $this->msg( $line[1] );
+                                       $msgText = $this->msg( $line[1] )->title( $messageTitle );
                                        if ( $msgText->exists() ) {
                                                $text = $msgText->text();
                                        } else {
@@ -1564,56 +1566,4 @@ abstract class Skin extends ContextSource {
                return $result;
        }
 
-       /** @deprecated in 1.21 */
-       public function commentBlock( $comment, $title = null, $local = false, $wikiId = null ) {
-               wfDeprecated( __METHOD__, '1.21' );
-               return Linker::commentBlock( $comment, $title, $local, $wikiId );
-       }
-
-       /** @deprecated in 1.21 */
-       public function generateRollback(
-               $rev,
-               IContextSource $context = null,
-               $options = [ 'verify' ]
-       ) {
-               wfDeprecated( __METHOD__, '1.21' );
-               return Linker::generateRollback( $rev, $context, $options );
-       }
-
-       /** @deprecated in 1.21 */
-       public function link( $target, $html = null, $customAttribs = [], $query = [], $options = [] ) {
-               wfDeprecated( __METHOD__, '1.21' );
-               return Linker::link( $target, $html, $customAttribs, $query, $options );
-       }
-
-       /** @deprecated in 1.21 */
-       public function linkKnown(
-               $target,
-               $html = null,
-               $customAttribs = [],
-               $query = [],
-               $options = [ 'known', 'noclasses' ]
-       ) {
-               wfDeprecated( __METHOD__, '1.21' );
-               return Linker::linkKnown( $target, $html, $customAttribs, $query, $options );
-       }
-
-       /** @deprecated in 1.21 */
-       public function userLink( $userId, $userName, $altUserName = false ) {
-               wfDeprecated( __METHOD__, '1.21' );
-               return Linker::userLink( $userId, $userName, $altUserName );
-       }
-
-       /** @deprecated in 1.21 */
-       public function userToolLinks(
-               $userId,
-               $userText,
-               $redContribsWhenNoEdits = false,
-               $flags = 0,
-               $edits = null
-       ) {
-               wfDeprecated( __METHOD__, '1.21' );
-               return Linker::userToolLinks( $userId, $userText, $redContribsWhenNoEdits, $flags, $edits );
-       }
-
 }
index cefc5bc..f185789 100644 (file)
@@ -18,6 +18,9 @@
  * @file
  */
 
+use MediaWiki\Auth\AuthManager;
+use MediaWiki\MediaWikiServices;
+
 /**
  * Base class for template-based skins.
  *
@@ -243,7 +246,7 @@ class SkinTemplate extends Skin {
                $out = $this->getOutput();
 
                $this->initPage( $out );
-               $tpl = $this->prepareQuickTemplate( $out );
+               $tpl = $this->prepareQuickTemplate();
                // execute template
                $res = $tpl->execute();
 
@@ -455,7 +458,6 @@ class SkinTemplate extends Skin {
                $tpl->set( 'indicators', $out->getIndicators() );
 
                $tpl->set( 'sitenotice', $this->getSiteNotice() );
-               $tpl->set( 'bottomscripts', $this->bottomScripts() );
                $tpl->set( 'printfooter', $this->printSource() );
                // Wrap the bodyText with #mw-content-text element
                $out->mBodytext = $this->wrapHTML( $title, $out->mBodytext );
@@ -478,6 +480,9 @@ class SkinTemplate extends Skin {
                $tpl->set( 'sidebar', $this->buildSidebar() );
                $tpl->set( 'nav_urls', $this->buildNavUrls() );
 
+               // Do this last in case hooks above add bottom scripts
+               $tpl->set( 'bottomscripts', $this->bottomScripts() );
+
                // Set the head scripts near the end, in case the above actions resulted in added scripts
                $tpl->set( 'headelement', $out->headElement( $this ) );
 
@@ -571,6 +576,7 @@ class SkinTemplate extends Skin {
                $title = $this->getTitle();
                $request = $this->getRequest();
                $pageurl = $title->getLocalURL();
+               $authManager = AuthManager::singleton();
 
                /* set up the default links for the personal toolbar */
                $personal_urls = [];
@@ -649,49 +655,40 @@ class SkinTemplate extends Skin {
                                'href' => $href,
                                'active' => $active
                        ];
-                       $personal_urls['logout'] = [
-                               'text' => $this->msg( 'pt-userlogout' )->text(),
-                               'href' => self::makeSpecialUrl( 'Userlogout',
-                                       // userlogout link must always contain an & character, otherwise we might not be able
-                                       // to detect a buggy precaching proxy (bug 17790)
-                                       $title->isSpecial( 'Preferences' ) ? 'noreturnto' : $returnto
-                               ),
-                               'active' => false
-                       ];
+
+                       // if we can't set the user, we can't unset it either
+                       if ( $request->getSession()->canSetUser() ) {
+                               $personal_urls['logout'] = [
+                                       'text' => $this->msg( 'pt-userlogout' )->text(),
+                                       'href' => self::makeSpecialUrl( 'Userlogout',
+                                               // userlogout link must always contain an & character, otherwise we might not be able
+                                               // to detect a buggy precaching proxy (bug 17790)
+                                               $title->isSpecial( 'Preferences' ) ? 'noreturnto' : $returnto ),
+                                       'active' => false
+                               ];
+                       }
                } else {
                        $useCombinedLoginLink = $this->useCombinedLoginLink();
+                       if ( !$authManager->canCreateAccounts() || !$authManager->canAuthenticateNow() ) {
+                               // don't show combined login/signup link if one of those is actually not available
+                               $useCombinedLoginLink = false;
+                       }
+
                        $loginlink = $this->getUser()->isAllowed( 'createaccount' ) && $useCombinedLoginLink
                                ? 'nav-login-createaccount'
                                : 'pt-login';
 
-                       // TODO remove this after AuthManager is stable
-                       global $wgDisableAuthManager;
-                       if ( $wgDisableAuthManager ) {
-                               $is_signup = $request->getText( 'type' ) == 'signup';
-                               $login_url = [
-                                       'text' => $this->msg( $loginlink )->text(),
-                                       'href' => self::makeSpecialUrl( 'Userlogin', $returnto ),
-                                       'active' => $title->isSpecial( 'Userlogin' )
-                                               && ( $loginlink == 'nav-login-createaccount' || !$is_signup ),
-                               ];
-                               $createaccount_url = [
-                                       'text' => $this->msg( 'pt-createaccount' )->text(),
-                                       'href' => self::makeSpecialUrl( 'Userlogin', "$returnto&type=signup" ),
-                                       'active' => $title->isSpecial( 'Userlogin' ) && $is_signup,
-                               ];
-                       } else {
-                               $login_url = [
-                                       'text' => $this->msg( $loginlink )->text(),
-                                       'href' => self::makeSpecialUrl( 'Userlogin', $returnto ),
-                                       'active' => $title->isSpecial( 'Userlogin' ) ||
-                                               $title->isSpecial( 'CreateAccount' ) && $useCombinedLoginLink,
-                               ];
-                               $createaccount_url = [
-                                       'text' => $this->msg( 'pt-createaccount' )->text(),
-                                       'href' => self::makeSpecialUrl( 'CreateAccount', $returnto ),
-                                       'active' => $title->isSpecial( 'CreateAccount' ),
-                               ];
-                       }
+                       $login_url = [
+                               'text' => $this->msg( $loginlink )->text(),
+                               'href' => self::makeSpecialUrl( 'Userlogin', $returnto ),
+                               'active' => $title->isSpecial( 'Userlogin' )
+                                       || $title->isSpecial( 'CreateAccount' ) && $useCombinedLoginLink,
+                       ];
+                       $createaccount_url = [
+                               'text' => $this->msg( 'pt-createaccount' )->text(),
+                               'href' => self::makeSpecialUrl( 'CreateAccount', $returnto ),
+                               'active' => $title->isSpecial( 'CreateAccount' ),
+                       ];
 
                        // No need to show Talk and Contributions to anons if they can't contribute!
                        if ( User::groupHasPermission( '*', 'edit' ) ) {
@@ -713,11 +710,17 @@ class SkinTemplate extends Skin {
                                ];
                        }
 
-                       if ( $this->getUser()->isAllowed( 'createaccount' ) && !$useCombinedLoginLink ) {
+                       if (
+                               $authManager->canCreateAccounts()
+                               && $this->getUser()->isAllowed( 'createaccount' )
+                               && !$useCombinedLoginLink
+                       ) {
                                $personal_urls['createaccount'] = $createaccount_url;
                        }
 
-                       $personal_urls['login'] = $login_url;
+                       if ( $authManager->canAuthenticateNow() ) {
+                               $personal_urls['login'] = $login_url;
+                       }
                }
 
                Hooks::run( 'PersonalUrls', [ &$personal_urls, &$title, $this ] );
@@ -749,6 +752,8 @@ class SkinTemplate extends Skin {
                        }
                }
 
+               $linkClass = MediaWikiServices::getInstance()->getLinkRenderer()->getLinkClasses( $title );
+
                // wfMessageFallback will nicely accept $message as an array of fallbacks
                // or just a single key
                $msg = wfMessageFallback( $message )->setContext( $this->getContext() );
@@ -771,11 +776,16 @@ class SkinTemplate extends Skin {
                        return $result;
                }
 
-               return [
+               $result = [
                        'class' => implode( ' ', $classes ),
                        'text' => $text,
                        'href' => $title->getLocalURL( $query ),
                        'primary' => true ];
+               if ( $linkClass !== '' ) {
+                       $result['link-class'] = $linkClass;
+               }
+
+               return $result;
        }
 
        function makeTalkUrlDetails( $name, $urlaction = '' ) {
index 9833c73..3adf5a6 100644 (file)
@@ -4,7 +4,6 @@ use MediaWiki\Auth\AuthenticationRequest;
 use MediaWiki\Auth\AuthenticationResponse;
 use MediaWiki\Auth\AuthManager;
 use MediaWiki\Logger\LoggerFactory;
-use MediaWiki\Session\SessionManager;
 use MediaWiki\Session\Token;
 
 /**
@@ -456,7 +455,7 @@ abstract class AuthManagerSpecialPage extends SpecialPage {
                                // passed to AuthManager. Normally we would display the form with an error message,
                                // but for the data we received via the redirect flow that would not be helpful at all.
                                // Let's just submit the data to AuthManager directly instead.
-                               LoggerFactory::getInstance( 'authmanager' )
+                               LoggerFactory::getInstance( 'authentication' )
                                        ->warning( 'Validation error on return', [ 'data' => $form->mFieldData,
                                                'status' => $status->getWikiText() ] );
                                $status = $this->handleFormSubmit( $form->mFieldData );
@@ -537,7 +536,7 @@ abstract class AuthManagerSpecialPage extends SpecialPage {
                $form->setAction( $this->getFullTitle()->getFullURL( $this->getPreservedParams() ) );
                $form->addHiddenField( $this->getTokenName(), $this->getToken()->toString() );
                $form->addHiddenField( 'authAction', $this->authAction );
-               $form->suppressDefaultSubmit( !$this->needsSubmitButton( $formDescriptor ) );
+               $form->suppressDefaultSubmit( !$this->needsSubmitButton( $requests ) );
 
                return $form;
        }
@@ -555,24 +554,46 @@ abstract class AuthManagerSpecialPage extends SpecialPage {
        }
 
        /**
-        * Returns true if the form has fields which take values. If all available providers use the
-        * redirect flow, the form might contain nothing but submit buttons, in which case we should
-        * not add an extra submit button which does nothing.
+        * Returns true if the form built from the given AuthenticationRequests needs a submit button.
+        * Providers using redirect flow (e.g. Google login) need their own submit buttons; if using
+        * one of those custom buttons is the only way to proceed, there is no point in displaying the
+        * default button which won't do anything useful.
         *
-        * @param array $formDescriptor A HTMLForm descriptor
+        * @param AuthenticationRequest[] $requests An array of AuthenticationRequests from which the
+        *  form will be built
         * @return bool
         */
-       protected function needsSubmitButton( $formDescriptor ) {
-               return (bool)array_filter( $formDescriptor, function ( $item ) {
-                       $class = false;
-                       if ( array_key_exists( 'class', $item ) ) {
-                               $class = $item['class'];
-                       } elseif ( array_key_exists( 'type', $item ) ) {
-                               $class = HTMLForm::$typeMappings[$item['type']];
+       protected function needsSubmitButton( array $requests ) {
+               $customSubmitButtonPresent = false;
+
+               // Secondary and preauth providers always need their data; they will not care what button
+               // is used, so they can be ignored. So can OPTIONAL buttons createdby primary providers;
+               // that's the point in being optional. Se we need to check whether all primary providers
+               // have their own buttons and whether there is at least one button present.
+               foreach ( $requests as $req ) {
+                       if ( $req->required === AuthenticationRequest::PRIMARY_REQUIRED ) {
+                               if ( $this->hasOwnSubmitButton( $req ) ) {
+                                       $customSubmitButtonPresent = true;
+                               } else {
+                                       return true;
+                               }
                        }
-                       return !is_a( $class, \HTMLInfoField::class, true ) &&
-                               !is_a( $class, \HTMLSubmitField::class, true );
-               } );
+               }
+               return !$customSubmitButtonPresent;
+       }
+
+       /**
+        * Checks whether the given AuthenticationRequest has its own submit button.
+        * @param AuthenticationRequest $req
+        * @return bool
+        */
+       protected function hasOwnSubmitButton( AuthenticationRequest $req ) {
+               foreach ( $req->getFieldInfo() as $field => $info ) {
+                       if ( $info['type'] === 'button' ) {
+                               return true;
+                       }
+               }
+               return false;
        }
 
        /**
index 6c061b3..01782f3 100644 (file)
@@ -340,7 +340,7 @@ abstract class ChangesListSpecialPage extends SpecialPage {
         * @return IDatabase
         */
        protected function getDB() {
-               return wfGetDB( DB_SLAVE );
+               return wfGetDB( DB_REPLICA );
        }
 
        /**
@@ -475,9 +475,11 @@ abstract class ChangesListSpecialPage extends SpecialPage {
        protected function addModules() {
                $out = $this->getOutput();
                // Styles and behavior for the legend box (see makeLegend())
-               $out->addModuleStyles( 'mediawiki.special.changeslist.legend' );
+               $out->addModuleStyles( [
+                       'mediawiki.special.changeslist.legend',
+                       'mediawiki.special.changeslist',
+               ] );
                $out->addModules( 'mediawiki.special.changeslist.legend.js' );
-               $out->addModuleStyles( 'mediawiki.special.changeslist' );
        }
 
        protected function getGroupName() {
index 27b4f32..9d17e7d 100644 (file)
@@ -27,7 +27,6 @@ use MediaWiki\Auth\AuthManager;
 use MediaWiki\Auth\Throttler;
 use MediaWiki\Logger\LoggerFactory;
 use MediaWiki\Session\SessionManager;
-use Psr\Log\LogLevel;
 
 /**
  * Holds shared logic for login and account creation pages.
@@ -224,11 +223,16 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                $this->setHeaders();
                $this->checkPermissions();
 
-               // Make sure it's possible to log in
-               if ( !$this->isSignup() && !$session->canSetUser() ) {
-                       throw new ErrorPageError( 'cannotloginnow-title', 'cannotloginnow-text', [
+               // Make sure the system configuration allows log in / sign up
+               if ( !$this->isSignup() && !$authManager->canAuthenticateNow() ) {
+                       if ( !$session->canSetUser() ) {
+                               throw new ErrorPageError( 'cannotloginnow-title', 'cannotloginnow-text', [
                                        $session->getProvider()->describe( RequestContext::getMain()->getLanguage() )
                                ] );
+                       }
+                       throw new ErrorPageError( 'cannotlogin-title', 'cannotlogin-text' );
+               } elseif ( $this->isSignup() && !$authManager->canCreateAccounts() ) {
+                       throw new ErrorPageError( 'cannotcreateaccount-title', 'cannotcreateaccount-text' );
                }
 
                /*
@@ -586,7 +590,7 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                $this->fakeTemplate = $fakeTemplate; // FIXME there should be a saner way to pass this to the hook
                // this will call onAuthChangeFormFields()
                $formDescriptor = static::fieldInfoToFormDescriptor( $requests, $fieldInfo, $this->authAction );
-               $this->postProcessFormDescriptor( $formDescriptor );
+               $this->postProcessFormDescriptor( $formDescriptor, $requests );
 
                $context = $this->getContext();
                if ( $context->getRequest() !== $this->getRequest() ) {
@@ -617,36 +621,6 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                        $form->setId( 'userlogin2' );
                }
 
-               // add pre/post text
-               // header used by ConfirmEdit, CondfirmAccount, Persona, WikimediaIncubator, SemanticSignup
-               // should be above the error message but HTMLForm doesn't support that
-               $form->addHeaderText( $fakeTemplate->get( 'header' ) );
-
-               // FIXME the old form used this for error/warning messages which does not play well with
-               // HTMLForm (maybe it could with a subclass?); for now only display it for signups
-               // (where the JS username validation needs it) and alway empty
-               if ( $this->isSignup() ) {
-                       // used by the mediawiki.special.userlogin.signup.js module
-                       $statusAreaAttribs = [ 'id' => 'mw-createacct-status-area' ];
-                       // $statusAreaAttribs += $msg ? [ 'class' => "{$msgType}box" ] : [ 'style' => 'display: none;' ];
-                       $form->addHeaderText( Html::element( 'div', $statusAreaAttribs ) );
-               }
-
-               // header used by MobileFrontend
-               $form->addHeaderText( $fakeTemplate->get( 'formheader' ) );
-
-               // blank signup footer for site customization
-               if ( $this->isSignup() && $this->showExtraInformation() ) {
-                       // Use signupend-https for HTTPS requests if it's not blank, signupend otherwise
-                       $signupendMsg = $this->msg( 'signupend' );
-                       $signupendHttpsMsg = $this->msg( 'signupend-https' );
-                       if ( !$signupendMsg->isDisabled() ) {
-                               $signupendText = ( $usingHTTPS && !$signupendHttpsMsg->isBlank() )
-                                       ? $signupendHttpsMsg ->parse() : $signupendMsg->parse();
-                               $form->addPostText( Html::rawElement( 'div', [ 'id' => 'signupend' ], $signupendText ) );
-                       }
-               }
-
                // warning header for non-standard workflows (e.g. security reauthentication)
                if ( !$this->isSignup() && $this->getUser()->isLoggedIn() ) {
                        $reauthMessage = $this->securityLevel ? 'userlogin-reauth' : 'userlogin-loggedin';
@@ -654,52 +628,6 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                                $this->msg( $reauthMessage )->params( $this->getUser()->getName() )->parse() ) );
                }
 
-               if ( !$this->isSignup() && $this->showExtraInformation() ) {
-                       $passwordReset = new PasswordReset( $this->getConfig(), AuthManager::singleton() );
-                       if ( $passwordReset->isAllowed( $this->getUser() ) ) {
-                               $form->addFooterText( Html::rawElement(
-                                       'div',
-                                       [ 'class' => 'mw-ui-vform-field mw-form-related-link-container' ],
-                                       Linker::link(
-                                               SpecialPage::getTitleFor( 'PasswordReset' ),
-                                               $this->msg( 'userlogin-resetpassword-link' )->escaped()
-                                       )
-                               ) );
-                       }
-
-                       // Don't show a "create account" link if the user can't.
-                       if ( $this->showCreateAccountLink() ) {
-                               // link to the other action
-                               $linkTitle = $this->getTitleFor( $this->isSignup() ? 'Userlogin' :'CreateAccount' );
-                               $linkq = $this->getReturnToQueryStringFragment();
-                               // Pass any language selection on to the mode switch link
-                               if ( $wgLoginLanguageSelector && $this->mLanguage ) {
-                                       $linkq .= '&uselang=' . $this->mLanguage;
-                               }
-
-                               $loggedIn = $this->getUser()->isLoggedIn();
-                               $createOrLoginHtml = Html::rawElement( 'div',
-                                       [ 'id' => 'mw-createaccount' . ( !$loggedIn ? '-cta' : '' ),
-                                               'class' => ( $loggedIn ? 'mw-form-related-link-container' : 'mw-ui-vform-field' ) ],
-                                       ( $loggedIn ? '' : $this->msg( 'userlogin-noaccount' )->escaped() )
-                                       . Html::element( 'a',
-                                               [
-                                                       'id' => 'mw-createaccount-join' . ( $loggedIn ? '-loggedin' : '' ),
-                                                       'href' => $linkTitle->getLocalURL( $linkq ),
-                                                       'class' => ( $loggedIn ? '' : 'mw-ui-button' ),
-                                                       'tabindex' => 100,
-                                               ],
-                                               $this->msg(
-                                                       ( $this->getUser()->isLoggedIn() ?
-                                                               'userlogin-createanother' :
-                                                               'userlogin-joinproject'
-                                                       ) )->escaped()
-                                       )
-                               );
-                               $form->addFooterText( $createOrLoginHtml );
-                       }
-               }
-
                $form->suppressDefaultSubmit();
 
                $this->authForm = $form;
@@ -838,7 +766,7 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                array $requests, array $fieldInfo, array &$formDescriptor, $action
        ) {
                $coreFieldDescriptors = $this->getFieldDefinitions( $this->fakeTemplate );
-               $specialFields = array_merge( [ 'extraInput', 'linkcontainer', 'entryError' ],
+               $specialFields = array_merge( [ 'extraInput' ],
                        array_keys( $this->fakeTemplate->getExtraInputDefinitions() ) );
 
                // keep the ordering from getCoreFieldDescriptors() where there is no explicit weight
@@ -847,14 +775,19 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                                $formDescriptor[$fieldName] : [];
 
                        // remove everything that is not in the fieldinfo, is not marked as a supplemental field
-                       // to something in the fieldinfo, and is not a generic or B/C field or a submit button
+                       // to something in the fieldinfo, is not B/C for the pre-AuthManager templates,
+                       // and is not an info field or a submit button
                        if (
                                !isset( $fieldInfo[$fieldName] )
                                && (
                                        !isset( $coreField['baseField'] )
                                        || !isset( $fieldInfo[$coreField['baseField']] )
-                               ) && !in_array( $fieldName, $specialFields, true )
-                               && ( !isset( $coreField['type'] ) || $coreField['type'] !== 'submit' )
+                               )
+                               && !in_array( $fieldName, $specialFields, true )
+                               && (
+                                       !isset( $coreField['type'] )
+                                       || !in_array( $coreField['type'], [ 'submit', 'info' ], true )
+                               )
                        ) {
                                $coreFieldDescriptors[$fieldName] = null;
                                continue;
@@ -893,13 +826,12 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
         * @return array
         */
        protected function getFieldDefinitions( $template ) {
-               global $wgEmailConfirmToEdit;
+               global $wgEmailConfirmToEdit, $wgLoginLanguageSelector;
 
                $isLoggedIn = $this->getUser()->isLoggedIn();
                $continuePart = $this->isContinued() ? 'continue-' : '';
                $anotherPart = $isLoggedIn ? 'another-' : '';
-               $expiration = $this->getRequest()->getSession()->getProvider()
-                       ->getRememberUserDuration();
+               $expiration = $this->getRequest()->getSession()->getProvider()->getRememberUserDuration();
                $expirationDays = ceil( $expiration / ( 3600 * 24 ) );
                $secureLoginLink = '';
                if ( $this->mSecureLoginUrl ) {
@@ -908,13 +840,25 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                                'class' => 'mw-ui-flush-right mw-secure',
                        ], $this->msg( 'userlogin-signwithsecure' )->text() );
                }
+               $usernameHelpLink = '';
+               if ( !$this->msg( 'createacct-helpusername' )->isDisabled() ) {
+                       $usernameHelpLink = Html::rawElement( 'span', [
+                               'class' => 'mw-ui-flush-right',
+                       ], $this->msg( 'createacct-helpusername' )->parse() );
+               }
 
                if ( $this->isSignup() ) {
                        $fieldDefinitions = [
+                               'statusarea' => [
+                                       // used by the mediawiki.special.userlogin.signup.js module for error display
+                                       // FIXME merge this with HTMLForm's normal status (error) area
+                                       'type' => 'info',
+                                       'raw' => true,
+                                       'default' => Html::element( 'div', [ 'id' => 'mw-createacct-status-area' ] ),
+                                       'weight' => -105,
+                               ],
                                'username' => [
-                                       'label-message' => 'userlogin-yourname',
-                                       // FIXME help-message does not match old formatting
-                                       'help-message' => 'createacct-helpusername',
+                                       'label-raw' => $this->msg( 'userlogin-yourname' )->escaped() . $usernameHelpLink,
                                        'id' => 'wpName2',
                                        'placeholder-message' => $isLoggedIn ? 'createacct-another-username-ph'
                                                : 'userlogin-yourname-ph',
@@ -1057,6 +1001,7 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                                ],
                        ];
                }
+
                $fieldDefinitions['username'] += [
                        'type' => 'text',
                        'name' => 'wpName',
@@ -1073,6 +1018,19 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                        // 'required' => true,
                ];
 
+               if ( $template->get( 'header' ) || $template->get( 'formheader' ) ) {
+                       // B/C for old extensions that haven't been converted to AuthManager (or have been
+                       // but somebody is using the old version) and still use templates via the
+                       // UserCreateForm/UserLoginForm hook.
+                       // 'header' used by ConfirmEdit, CondfirmAccount, Persona, WikimediaIncubator, SemanticSignup
+                       // 'formheader' used by MobileFrontend
+                       $fieldDefinitions['header'] = [
+                               'type' => 'info',
+                               'raw' => true,
+                               'default' => $template->get( 'header' ) ?: $template->get( 'formheader' ),
+                               'weight' => - 110,
+                       ];
+               }
                if ( $this->mEntryError ) {
                        $fieldDefinitions['entryError'] = [
                                'type' => 'info',
@@ -1083,9 +1041,77 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                                'weight' => -100,
                        ];
                }
-
                if ( !$this->showExtraInformation() ) {
-                       unset( $fieldDefinitions['linkcontainer'] );
+                       unset( $fieldDefinitions['linkcontainer'], $fieldDefinitions['signupend'] );
+               }
+               if ( $this->isSignup() && $this->showExtraInformation() ) {
+                       // blank signup footer for site customization
+                       // uses signupend-https for HTTPS requests if it's not blank, signupend otherwise
+                       $signupendMsg = $this->msg( 'signupend' );
+                       $signupendHttpsMsg = $this->msg( 'signupend-https' );
+                       if ( !$signupendMsg->isDisabled() ) {
+                               $usingHTTPS = $this->getRequest()->getProtocol() === 'https';
+                               $signupendText = ( $usingHTTPS && !$signupendHttpsMsg->isBlank() )
+                                       ? $signupendHttpsMsg ->parse() : $signupendMsg->parse();
+                               $fieldDefinitions['signupend'] = [
+                                       'type' => 'info',
+                                       'raw' => true,
+                                       'default' => Html::rawElement( 'div', [ 'id' => 'signupend' ], $signupendText ),
+                                       'weight' => 225,
+                               ];
+                       }
+               }
+               if ( !$this->isSignup() && $this->showExtraInformation() ) {
+                       $passwordReset = new PasswordReset( $this->getConfig(), AuthManager::singleton() );
+                       if ( $passwordReset->isAllowed( $this->getUser() ) ) {
+                               $fieldDefinitions['passwordReset'] = [
+                                       'type' => 'info',
+                                       'raw' => true,
+                                       'cssclass' => 'mw-form-related-link-container',
+                                       'default' => Linker::link(
+                                               SpecialPage::getTitleFor( 'PasswordReset' ),
+                                               $this->msg( 'userlogin-resetpassword-link' )->escaped()
+                                       ),
+                                       'weight' => 230,
+                               ];
+                       }
+
+                       // Don't show a "create account" link if the user can't.
+                       if ( $this->showCreateAccountLink() ) {
+                               // link to the other action
+                               $linkTitle = $this->getTitleFor( $this->isSignup() ? 'Userlogin' :'CreateAccount' );
+                               $linkq = $this->getReturnToQueryStringFragment();
+                               // Pass any language selection on to the mode switch link
+                               if ( $wgLoginLanguageSelector && $this->mLanguage ) {
+                                       $linkq .= '&uselang=' . $this->mLanguage;
+                               }
+                               $loggedIn = $this->getUser()->isLoggedIn();
+
+                               $fieldDefinitions['createOrLogin'] = [
+                                       'type' => 'info',
+                                       'raw' => true,
+                                       'linkQuery' => $linkq,
+                                       'default' => function ( $params ) use ( $loggedIn, $linkTitle ) {
+                                               return Html::rawElement( 'div',
+                                                       [ 'id' => 'mw-createaccount' . ( !$loggedIn ? '-cta' : '' ),
+                                                               'class' => ( $loggedIn ? 'mw-form-related-link-container' : 'mw-ui-vform-field' ) ],
+                                                       ( $loggedIn ? '' : $this->msg( 'userlogin-noaccount' )->escaped() )
+                                                       . Html::element( 'a',
+                                                               [
+                                                                       'id' => 'mw-createaccount-join' . ( $loggedIn ? '-loggedin' : '' ),
+                                                                       'href' => $linkTitle->getLocalURL( $params['linkQuery'] ),
+                                                                       'class' => ( $loggedIn ? '' : 'mw-ui-button' ),
+                                                                       'tabindex' => 100,
+                                                               ],
+                                                               $this->msg(
+                                                                       $loggedIn ? 'userlogin-createanother' : 'userlogin-joinproject'
+                                                               )->escaped()
+                                                       )
+                                               );
+                                       },
+                                       'weight' => 235,
+                               ];
+                       }
                }
 
                $fieldDefinitions = $this->getBCFieldDefinitions( $fieldDefinitions, $template );
@@ -1238,7 +1264,7 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
        /**
         * @param array $formDescriptor
         */
-       protected function postProcessFormDescriptor( &$formDescriptor ) {
+       protected function postProcessFormDescriptor( &$formDescriptor, $requests ) {
                // Pre-fill username (if not creating an account, T46775).
                if (
                        isset( $formDescriptor['username'] ) &&
@@ -1256,7 +1282,7 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
 
                // don't show a submit button if there is nothing to submit (i.e. the only form content
                // is other submit buttons, for redirect flows)
-               if ( !$this->needsSubmitButton( $formDescriptor ) ) {
+               if ( !$this->needsSubmitButton( $requests ) ) {
                        unset( $formDescriptor['createaccount'], $formDescriptor['loginattempt'] );
                }
 
@@ -1343,196 +1369,12 @@ class FakeAuthTemplate extends BaseTemplate {
        }
 }
 
-/**
- * A horrible hack to handle AuthManager's feature flag. For other special pages this is done in
- * SpecialPageFactory, but LoginForm is used directly by some extensions. Will be killed as soon
- * as AuthManager is stable.
- */
-class LoginForm extends SpecialPage {
-       private $realLoginForm;
-
-       public function __construct( $request = null ) {
-               global $wgDisableAuthManager;
-               if ( $wgDisableAuthManager ) {
-                       $this->realLoginForm = new LoginFormPreAuthManager( $request );
-               } else {
-                       $this->realLoginForm = new LoginFormAuthManager( $request );
-               }
-       }
-
-       // proxy everything
-
-       public function __get( $name ) {
-               return $this->realLoginForm->$name;
-       }
-
-       public function __set( $name, $value ) {
-               $this->realLoginForm->$name = $value;
-       }
-
-       public function __call( $name, $args ) {
-               return call_user_func_array( [ $this->realLoginForm, $name ], $args );
-       }
-
-       public static function __callStatic( $name, $args ) {
-               global $wgDisableAuthManager;
-               return call_user_func_array( [ $wgDisableAuthManager ? LoginFormPreAuthManager::class
-                       : LoginFormAuthManager::class, $name ], $args );
-       }
-
-       // all public SpecialPage methods need to be proxied explicitly
-
-       public function getName() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function getRestriction() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function isListed() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function setListed( $listed ) {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function listed( $x = null ) {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function isIncludable() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function including( $x = null ) {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function getLocalName() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function isExpensive() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function isCached() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function isRestricted() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function userCanExecute( User $user ) {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function displayRestrictionError() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function checkPermissions() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function checkReadOnly() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function requireLogin(
-               $reasonMsg = 'exception-nologin-text', $titleMsg = 'exception-nologin'
-       ) {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function prefixSearchSubpages( $search, $limit, $offset ) {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function execute( $subPage ) {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function getDescription() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       function getTitle( $subpage = false ) {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       function getPageTitle( $subpage = false ) {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function setContext( $context ) {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function getContext() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function getRequest() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function getOutput() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function getUser() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function getSkin() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function getLanguage() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function getConfig() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function getFullTitle() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function getFinalGroupName() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-       public function doesWrites() {
-               return call_user_func_array( [ $this->realLoginForm, __FUNCTION__ ], func_get_args() );
-       }
-
-       // no way to proxy constants and static properties
-
-       const SUCCESS = 0;
-       const NO_NAME = 1;
-       const ILLEGAL = 2;
-       const WRONG_PLUGIN_PASS = 3;
-       const NOT_EXISTS = 4;
-       const WRONG_PASS = 5;
-       const EMPTY_PASS = 6;
-       const RESET_PASS = 7;
-       const ABORTED = 8;
-       const CREATE_BLOCKED = 9;
-       const THROTTLED = 10;
-       const USER_BLOCKED = 11;
-       const NEED_TOKEN = 12;
-       const WRONG_TOKEN = 13;
-       const USER_MIGRATED = 14;
-
-       public static $statusCodes = [
-               self::SUCCESS => 'success',
-               self::NO_NAME => 'no_name',
-               self::ILLEGAL => 'illegal',
-               self::WRONG_PLUGIN_PASS => 'wrong_plugin_pass',
-               self::NOT_EXISTS => 'not_exists',
-               self::WRONG_PASS => 'wrong_pass',
-               self::EMPTY_PASS => 'empty_pass',
-               self::RESET_PASS => 'reset_pass',
-               self::ABORTED => 'aborted',
-               self::CREATE_BLOCKED => 'create_blocked',
-               self::THROTTLED => 'throttled',
-               self::USER_BLOCKED => 'user_blocked',
-               self::NEED_TOKEN => 'need_token',
-               self::WRONG_TOKEN => 'wrong_token',
-               self::USER_MIGRATED => 'user_migrated',
-       ];
-
-       public static $validErrorMessages = [
-               'exception-nologin-text',
-               'watchlistanontext',
-               'changeemail-no-info',
-               'resetpass-no-info',
-               'confirmemail_needlogin',
-               'prefsnologintext2',
-       ];
-}
-
 /**
  * LoginForm as a special page has been replaced by SpecialUserLogin and SpecialCreateAccount,
  * but some extensions called its public methods directly, so the class is retained as a
  * B/C wrapper. Anything that used it before should use AuthManager instead.
  */
-class LoginFormAuthManager extends SpecialPage {
+class LoginForm extends SpecialPage {
        const SUCCESS = 0;
        const NO_NAME = 1;
        const ILLEGAL = 2;
index 1beac43..afbc581 100644 (file)
@@ -376,7 +376,7 @@ abstract class QueryPage extends SpecialPage {
         * @return IDatabase
         */
        function getRecacheDB() {
-               return wfGetDB( DB_SLAVE, [ $this->getName(), 'QueryPage::recache', 'vslow' ] );
+               return wfGetDB( DB_REPLICA, [ $this->getName(), 'QueryPage::recache', 'vslow' ] );
        }
 
        /**
@@ -453,7 +453,7 @@ abstract class QueryPage extends SpecialPage {
         * @since 1.18
         */
        public function fetchFromCache( $limit, $offset = false ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $options = [];
                if ( $limit !== false ) {
                        $options['LIMIT'] = intval( $limit );
@@ -477,7 +477,7 @@ abstract class QueryPage extends SpecialPage {
 
        public function getCachedTimestamp() {
                if ( is_null( $this->cachedTimestamp ) ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $fname = get_class( $this ) . '::getCachedTimestamp';
                        $this->cachedTimestamp = $dbr->selectField( 'querycache_info', 'qci_timestamp',
                                [ 'qci_type' => $this->getName() ], $fname );
index 35ecc6e..00d8c4a 100644 (file)
@@ -61,12 +61,14 @@ class SpecialPage {
        protected $mContext;
 
        /**
-        * @var LinkRenderer|null
+        * @var \MediaWiki\Linker\LinkRenderer|null
         */
        private $linkRenderer;
 
        /**
         * Get a localised Title object for a specified special page name
+        * If you don't need a full Title object, consider using TitleValue through
+        * getTitleValueFor() below.
         *
         * @since 1.9
         * @since 1.21 $fragment parameter added
@@ -78,9 +80,24 @@ class SpecialPage {
         * @throws MWException
         */
        public static function getTitleFor( $name, $subpage = false, $fragment = '' ) {
+               return Title::newFromTitleValue(
+                       self::getTitleValueFor( $name, $subpage, $fragment )
+               );
+       }
+
+       /**
+        * Get a localised TitleValue object for a specified special page name
+        *
+        * @since 1.28
+        * @param string $name
+        * @param string|bool $subpage Subpage string, or false to not use a subpage
+        * @param string $fragment The link fragment (after the "#")
+        * @return TitleValue
+        */
+       public static function getTitleValueFor( $name, $subpage = false, $fragment = '' ) {
                $name = SpecialPageFactory::getLocalNameFor( $name, $subpage );
 
-               return Title::makeTitle( NS_SPECIAL, $name, $fragment );
+               return new TitleValue( NS_SPECIAL, $name, $fragment );
        }
 
        /**
@@ -834,7 +851,7 @@ class SpecialPage {
 
        /**
         * @since 1.28
-        * @return LinkRenderer
+        * @return \MediaWiki\Linker\LinkRenderer
         */
        protected function getLinkRenderer() {
                if ( $this->linkRenderer ) {
@@ -846,7 +863,7 @@ class SpecialPage {
 
        /**
         * @since 1.28
-        * @param LinkRenderer $linkRenderer
+        * @param \MediaWiki\Linker\LinkRenderer $linkRenderer
         */
        public function setLinkRenderer( LinkRenderer $linkRenderer ) {
                $this->linkRenderer = $linkRenderer;
index b69b28a..4356bc5 100644 (file)
@@ -84,8 +84,8 @@ class SpecialPageFactory {
 
                // Authentication
                'Userlogin' => 'SpecialUserLogin',
-               'Userlogout' => 'SpecialUserlogoutPreAuthManager',
-               'CreateAccount' => 'SpecialCreateAccountPreAuthManager',
+               'Userlogout' => 'SpecialUserLogout',
+               'CreateAccount' => 'SpecialCreateAccount',
                'LinkAccounts' => 'SpecialLinkAccounts',
                'UnlinkAccounts' => 'SpecialUnlinkAccounts',
                'ChangeCredentials' => 'SpecialChangeCredentials',
@@ -96,9 +96,9 @@ class SpecialPageFactory {
                'Block' => 'SpecialBlock',
                'Unblock' => 'SpecialUnblock',
                'BlockList' => 'SpecialBlockList',
-               'ChangePassword' => 'SpecialChangePasswordPreAuthManager',
+               'ChangePassword' => 'SpecialChangePassword',
                'BotPasswords' => 'SpecialBotPasswords',
-               'PasswordReset' => 'SpecialPasswordResetPreAuthManager',
+               'PasswordReset' => 'SpecialPasswordReset',
                'DeletedContributions' => 'DeletedContributionsPage',
                'Preferences' => 'SpecialPreferences',
                'ResetTokens' => 'SpecialResetTokens',
@@ -231,7 +231,6 @@ class SpecialPageFactory {
                global $wgDisableInternalSearch, $wgEmailAuthentication;
                global $wgEnableEmail, $wgEnableJavaScriptTest;
                global $wgPageLanguageUseDB, $wgContentHandlerUseDB;
-               global $wgDisableAuthManager;
 
                if ( !is_array( self::$list ) ) {
 
@@ -247,7 +246,7 @@ class SpecialPageFactory {
                        }
 
                        if ( $wgEnableEmail ) {
-                               self::$list['ChangeEmail'] = 'SpecialChangeEmailPreAuthManager';
+                               self::$list['ChangeEmail'] = 'SpecialChangeEmail';
                        }
 
                        if ( $wgEnableJavaScriptTest ) {
@@ -261,20 +260,6 @@ class SpecialPageFactory {
                                self::$list['ChangeContentModel'] = 'SpecialChangeContentModel';
                        }
 
-                       // horrible hack to allow selection between old and new classes via a feature flag - T110756
-                       // will be removed once AuthManager is stable
-                       if ( !$wgDisableAuthManager ) {
-                               self::$list = array_map( function ( $class ) {
-                                       return preg_replace( '/PreAuthManager$/', '', $class );
-                               }, self::$list );
-                               self::$list['Userlogout'] = 'SpecialUserLogout'; // case matters
-                       } else {
-                               self::$list['Userlogin'] = 'LoginForm';
-                               self::$list = array_diff_key( self::$list, array_fill_keys( [
-                                       'LinkAccounts', 'UnlinkAccounts', 'ChangeCredentials', 'RemoveCredentials',
-                               ], true ) );
-                       }
-
                        // Add extension special pages
                        self::$list = array_merge( self::$list, $wgSpecialPages );
 
@@ -714,6 +699,8 @@ class SpecialPageFactory {
                }
 
                if ( $subpage !== false && !is_null( $subpage ) ) {
+                       // Make sure it's in dbkey form
+                       $subpage = str_replace( ' ', '_', $subpage );
                        $name = "$name/$subpage";
                }
 
index c697ca7..2da441b 100644 (file)
@@ -62,7 +62,7 @@ class SpecialActiveUsers extends SpecialPage {
 
                // Mention the level of cache staleness...
                $cacheText = '';
-               $dbr = wfGetDB( DB_SLAVE, 'recentchanges' );
+               $dbr = wfGetDB( DB_REPLICA, 'recentchanges' );
                $rcMax = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', '', __METHOD__ );
                if ( $rcMax ) {
                        $cTime = $dbr->selectField( 'querycache_info',
index b55f0b4..4a2a619 100644 (file)
@@ -181,7 +181,7 @@ class SpecialAllPages extends IncludableSpecialPage {
                        list( $namespace, $fromKey, $from ) = $fromList;
                        list( , $toKey, $to ) = $toList;
 
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $filterConds = [ 'page_namespace' => $namespace ];
                        if ( $hideredirects ) {
                                $filterConds['page_is_redirect'] = 0;
index fcadede..ce7d24e 100644 (file)
@@ -645,8 +645,10 @@ class SpecialBlock extends FormSpecialPage {
                                return [ 'ipb-blockingself', 'ipb-confirmaction' ];
                        }
                } elseif ( $type == Block::TYPE_RANGE ) {
+                       $user = null;
                        $userId = 0;
                } elseif ( $type == Block::TYPE_IP ) {
+                       $user = null;
                        $target = $target->getName();
                        $userId = 0;
                } else {
@@ -729,6 +731,7 @@ class SpecialBlock extends FormSpecialPage {
                        return $reason;
                }
 
+               $priorBlock = null;
                # Try to insert block. Is there a conflicting block?
                $status = $block->insert();
                if ( !$status ) {
@@ -748,17 +751,16 @@ class SpecialBlock extends FormSpecialPage {
                                # This returns direct blocks before autoblocks/rangeblocks, since we should
                                # be sure the user is blocked by now it should work for our purposes
                                $currentBlock = Block::newFromTarget( $target );
-
                                if ( $block->equals( $currentBlock ) ) {
                                        return [ [ 'ipb_already_blocked', $block->getTarget() ] ];
                                }
-
                                # If the name was hidden and the blocking user cannot hide
                                # names, then don't allow any block changes...
                                if ( $currentBlock->mHideName && !$performer->isAllowed( 'hideuser' ) ) {
                                        return [ 'cant-see-hidden-user' ];
                                }
 
+                               $priorBlock = clone $currentBlock;
                                $currentBlock->isHardblock( $block->isHardblock() );
                                $currentBlock->prevents( 'createaccount', $block->prevents( 'createaccount' ) );
                                $currentBlock->mExpiry = $block->mExpiry;
@@ -786,7 +788,7 @@ class SpecialBlock extends FormSpecialPage {
                        $logaction = 'block';
                }
 
-               Hooks::run( 'BlockIpComplete', [ $block, $performer ] );
+               Hooks::run( 'BlockIpComplete', [ $block, $performer, $priorBlock ] );
 
                # Set *_deleted fields if requested
                if ( $data['HideUser'] ) {
index 7c7f017..c4fb316 100644 (file)
@@ -136,8 +136,7 @@ class SpecialBlockList extends SpecialPage {
                                case Block::TYPE_IP:
                                case Block::TYPE_RANGE:
                                        list( $start, $end ) = IP::parseRange( $target );
-                                       $dbr = wfGetDB( DB_SLAVE );
-                                       $conds[] = $dbr->makeList(
+                                       $conds[] = wfGetDB( DB_REPLICA )->makeList(
                                                [
                                                        'ipb_address' => $target,
                                                        Block::getRangeCond( $start, $end )
index 7e330aa..61ab642 100644 (file)
@@ -163,7 +163,7 @@ class SpecialBotPasswords extends FormSpecialPage {
 
                } else {
                        $linkRenderer = $this->getLinkRenderer();
-                       $dbr = BotPassword::getDB( DB_SLAVE );
+                       $dbr = BotPassword::getDB( DB_REPLICA );
                        $res = $dbr->select(
                                'bot_passwords',
                                [ 'bp_app_id' ],
index 4c3fbe5..b9b2051 100644 (file)
@@ -49,7 +49,7 @@ class BrokenRedirectsPage extends QueryPage {
        }
 
        public function getQueryInfo() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                return [
                        'tables' => [
index ccbb275..b37c475 100644 (file)
@@ -191,6 +191,12 @@ class SpecialChangeContentModel extends FormSpecialPage {
                        // Page doesn't exist, create an empty content object
                        $newContent = ContentHandler::getForModelID( $data['model'] )->makeEmptyContent();
                }
+
+               // All other checks have passed, let's check rate limits
+               if ( $user->pingLimiter( 'editcontentmodel' ) ) {
+                       throw new ThrottledError();
+               }
+
                $flags = $this->oldRevision ? EDIT_UPDATE : EDIT_NEW;
                $flags |= EDIT_INTERNAL;
                if ( $user->isAllowed( 'bot' ) ) {
index 4da8049..ff70848 100644 (file)
@@ -149,7 +149,7 @@ class SpecialChangeCredentials extends AuthManagerSpecialPage {
                return $form;
        }
 
-       protected function needsSubmitButton( $formDescriptor ) {
+       protected function needsSubmitButton( array $requests ) {
                // Change/remove forms show are built from a single AuthenticationRequest and do not allow
                // for redirect flow; they always need a submit button.
                return true;
@@ -184,6 +184,7 @@ class SpecialChangeCredentials extends AuthManagerSpecialPage {
                        $groupedRequests[(string)$info['provider']][] = $req;
                }
 
+               $linkRenderer = $this->getLinkRenderer();
                $out->addHTML( Html::openElement( 'dl' ) );
                foreach ( $groupedRequests as $group => $members ) {
                        $out->addHTML( Html::element( 'dt', [], $group ) );
@@ -191,8 +192,10 @@ class SpecialChangeCredentials extends AuthManagerSpecialPage {
                                /** @var AuthenticationRequest $req */
                                $info = $req->describeCredentials();
                                $out->addHTML( Html::rawElement( 'dd', [],
-                                       Linker::link( $this->getPageTitle( $req->getUniqueId() ),
-                                               htmlspecialchars( $info['account'], ENT_QUOTES ) )
+                                       $linkRenderer->makeLink(
+                                               $this->getPageTitle( $req->getUniqueId() ),
+                                               $info['account']
+                                       )
                                ) );
                        }
                }
index a656c2e..7b4e9db 100644 (file)
@@ -49,10 +49,9 @@ class EmailConfirmation extends UnlistedSpecialPage {
        function execute( $code ) {
                // Ignore things like master queries/connections on GET requests.
                // It's very convenient to just allow formless link usage.
-               Profiler::instance()->getTransactionProfiler()->resetExpectations();
+               $trxProfiler = Profiler::instance()->getTransactionProfiler();
 
                $this->setHeaders();
-
                $this->checkReadOnly();
                $this->checkPermissions();
 
@@ -70,7 +69,9 @@ class EmailConfirmation extends UnlistedSpecialPage {
                                $this->getOutput()->addWikiMsg( 'confirmemail_noemail' );
                        }
                } else {
+                       $trxProfiler->setSilenced( true );
                        $this->attemptConfirm( $code );
+                       $trxProfiler->setSilenced( false );
                }
        }
 
@@ -146,7 +147,7 @@ class EmailConfirmation extends UnlistedSpecialPage {
         *
         * @param string $code Confirmation code
         */
-       function attemptConfirm( $code ) {
+       private function attemptConfirm( $code ) {
                $user = User::newFromConfirmationCode( $code, User::READ_LATEST );
                if ( !is_object( $user ) ) {
                        $this->getOutput()->addWikiMsg( 'confirmemail_invalid' );
index ac7e62e..68289a7 100644 (file)
@@ -37,7 +37,10 @@ class SpecialContributions extends IncludableSpecialPage {
                $this->setHeaders();
                $this->outputHeader();
                $out = $this->getOutput();
-               $out->addModuleStyles( 'mediawiki.special' );
+               $out->addModuleStyles( [
+                       'mediawiki.special',
+                       'mediawiki.special.changeslist',
+               ] );
                $this->addHelpLink( 'Help:User contributions' );
 
                $this->opts = [];
@@ -200,7 +203,7 @@ class SpecialContributions extends IncludableSpecialPage {
                        if ( !$pager->getNumRows() ) {
                                $out->addWikiMsg( 'nocontribs', $target );
                        } else {
-                               # Show a message about slave lag, if applicable
+                               # Show a message about replica DB lag, if applicable
                                $lag = wfGetLB()->safeGetLag( $pager->getDatabase() );
                                if ( $lag > 0 ) {
                                        $out->showLagWarning( $lag );
@@ -264,13 +267,13 @@ class SpecialContributions extends IncludableSpecialPage {
                        }
                        $user = htmlspecialchars( $userObj->getName() );
                } else {
-                       $user = Linker::link( $userObj->getUserPage(), htmlspecialchars( $userObj->getName() ) );
+                       $user = $this->getLinkRenderer()->makeLink( $userObj->getUserPage(), $userObj->getName() );
                }
                $nt = $userObj->getUserPage();
                $talk = $userObj->getTalkPage();
                $links = '';
                if ( $talk ) {
-                       $tools = $this->getUserLinks( $nt, $talk, $userObj );
+                       $tools = self::getUserLinks( $this, $userObj );
                        $links = $this->getLanguage()->pipeList( $tools );
 
                        // Show a note if the user is blocked and display the last block log entry.
@@ -310,86 +313,93 @@ class SpecialContributions extends IncludableSpecialPage {
 
        /**
         * Links to different places.
-        * @param Title $userpage Target user page
-        * @param Title $talkpage Talk page
+        *
+        * @note This function is also called in DeletedContributionsPage
+        * @param SpecialPage $sp SpecialPage instance, for context
         * @param User $target Target user object
         * @return array
         */
-       public function getUserLinks( Title $userpage, Title $talkpage, User $target ) {
+       public static function getUserLinks( SpecialPage $sp, User $target ) {
 
                $id = $target->getId();
                $username = $target->getName();
+               $userpage = $target->getUserPage();
+               $talkpage = $target->getTalkPage();
 
-               $tools[] = Linker::link( $talkpage, $this->msg( 'sp-contributions-talk' )->escaped() );
+               $linkRenderer = $sp->getLinkRenderer();
+               $tools['user-talk'] = $linkRenderer->makeLink(
+                       $talkpage,
+                       $sp->msg( 'sp-contributions-talk' )->text()
+               );
 
                if ( ( $id !== null ) || ( $id === null && IP::isIPAddress( $username ) ) ) {
-                       if ( $this->getUser()->isAllowed( 'block' ) ) { # Block / Change block / Unblock links
+                       if ( $sp->getUser()->isAllowed( 'block' ) ) { # Block / Change block / Unblock links
                                if ( $target->isBlocked() && $target->getBlock()->getType() != Block::TYPE_AUTO ) {
-                                       $tools[] = Linker::linkKnown( # Change block link
+                                       $tools['block'] = $linkRenderer->makeKnownLink( # Change block link
                                                SpecialPage::getTitleFor( 'Block', $username ),
-                                               $this->msg( 'change-blocklink' )->escaped()
+                                               $sp->msg( 'change-blocklink' )->text()
                                        );
-                                       $tools[] = Linker::linkKnown( # Unblock link
+                                       $tools['unblock'] = $linkRenderer->makeKnownLink( # Unblock link
                                                SpecialPage::getTitleFor( 'Unblock', $username ),
-                                               $this->msg( 'unblocklink' )->escaped()
+                                               $sp->msg( 'unblocklink' )->text()
                                        );
                                } else { # User is not blocked
-                                       $tools[] = Linker::linkKnown( # Block link
+                                       $tools['block'] = $linkRenderer->makeKnownLink( # Block link
                                                SpecialPage::getTitleFor( 'Block', $username ),
-                                               $this->msg( 'blocklink' )->escaped()
+                                               $sp->msg( 'blocklink' )->text()
                                        );
                                }
                        }
 
                        # Block log link
-                       $tools[] = Linker::linkKnown(
+                       $tools['log-block'] = $linkRenderer->makeKnownLink(
                                SpecialPage::getTitleFor( 'Log', 'block' ),
-                               $this->msg( 'sp-contributions-blocklog' )->escaped(),
+                               $sp->msg( 'sp-contributions-blocklog' )->text(),
                                [],
                                [ 'page' => $userpage->getPrefixedText() ]
                        );
 
                        # Suppression log link (bug 59120)
-                       if ( $this->getUser()->isAllowed( 'suppressionlog' ) ) {
-                               $tools[] = Linker::linkKnown(
+                       if ( $sp->getUser()->isAllowed( 'suppressionlog' ) ) {
+                               $tools['log-suppression'] = $linkRenderer->makeKnownLink(
                                        SpecialPage::getTitleFor( 'Log', 'suppress' ),
-                                       $this->msg( 'sp-contributions-suppresslog' )->escaped(),
+                                       $sp->msg( 'sp-contributions-suppresslog', $username )->text(),
                                        [],
                                        [ 'offender' => $username ]
                                );
                        }
                }
                # Uploads
-               $tools[] = Linker::linkKnown(
+               $tools['uploads'] = $linkRenderer->makeKnownLink(
                        SpecialPage::getTitleFor( 'Listfiles', $username ),
-                       $this->msg( 'sp-contributions-uploads' )->escaped()
+                       $sp->msg( 'sp-contributions-uploads' )->text()
                );
 
                # Other logs link
-               $tools[] = Linker::linkKnown(
+               $tools['logs'] = $linkRenderer->makeKnownLink(
                        SpecialPage::getTitleFor( 'Log', $username ),
-                       $this->msg( 'sp-contributions-logs' )->escaped()
+                       $sp->msg( 'sp-contributions-logs' )->text()
                );
 
                # Add link to deleted user contributions for priviledged users
-               if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) {
-                       $tools[] = Linker::linkKnown(
+               if ( $sp->getUser()->isAllowed( 'deletedhistory' ) ) {
+                       $tools['deletedcontribs'] = $linkRenderer->makeKnownLink(
                                SpecialPage::getTitleFor( 'DeletedContributions', $username ),
-                               $this->msg( 'sp-contributions-deleted' )->escaped()
+                               $sp->msg( 'sp-contributions-deleted', $username )->text()
                        );
                }
 
                # Add a link to change user rights for privileged users
                $userrightsPage = new UserrightsPage();
-               $userrightsPage->setContext( $this->getContext() );
+               $userrightsPage->setContext( $sp->getContext() );
                if ( $userrightsPage->userCanChangeRights( $target ) ) {
-                       $tools[] = Linker::linkKnown(
+                       $tools['userrights'] = $linkRenderer->makeKnownLink(
                                SpecialPage::getTitleFor( 'Userrights', $username ),
-                               $this->msg( 'sp-contributions-userrights' )->escaped()
+                               $sp->msg( 'sp-contributions-userrights' )->text()
                        );
                }
 
-               Hooks::run( 'ContributionsToolLinks', [ $id, $userpage, &$tools ] );
+               Hooks::run( 'ContributionsToolLinks', [ $id, $userpage, &$tools, $sp ] );
 
                return $tools;
        }
index e47b255..73beafc 100644 (file)
@@ -23,7 +23,6 @@
 
 use MediaWiki\Auth\AuthManager;
 use MediaWiki\Logger\LoggerFactory;
-use Psr\Log\LogLevel;
 
 /**
  * Implements Special:CreateAccount
@@ -120,7 +119,12 @@ class SpecialCreateAccount extends LoginSignupSpecialPage {
                                } else {
                                        $out->addWikiMsg( 'accountcreatedtext', $user->getName() );
                                }
-                               $out->addReturnTo( $this->getPageTitle() );
+
+                               $rt = Title::newFromText( $this->mReturnTo );
+                               $out->addReturnTo(
+                                       ( $rt && !$rt->isExternal() ) ? $rt : $this->getPageTitle(),
+                                       wfCgiToArray( $this->mReturnToQuery )
+                               );
                                return;
                        }
                }
@@ -130,7 +134,7 @@ class SpecialCreateAccount extends LoginSignupSpecialPage {
                # Run any hooks; display injected HTML
                $injected_html = '';
                $welcome_creation_msg = 'welcomecreation-msg';
-               Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html ] );
+               Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html, $direct ] );
 
                /**
                 * Let any extensions change what message is shown.
@@ -160,7 +164,7 @@ class SpecialCreateAccount extends LoginSignupSpecialPage {
        }
 
        protected function logAuthResult( $success, $status = null ) {
-               LoggerFactory::getInstance( 'authmanager' )->info( 'Account creation attempt', [
+               LoggerFactory::getInstance( 'authevents' )->info( 'Account creation attempt', [
                        'event' => 'accountcreation',
                        'successful' => $success,
                        'status' => $status,
index 190bf9f..ad12046 100644 (file)
@@ -98,7 +98,7 @@ class DeletedContributionsPage extends SpecialPage {
                        return;
                }
 
-               # Show a message about slave lag, if applicable
+               # Show a message about replica DB lag, if applicable
                $lag = wfGetLB()->safeGetLag( $pager->getDatabase() );
                if ( $lag > 0 ) {
                        $out->showLagWarning( $lag );
@@ -129,97 +129,29 @@ class DeletedContributionsPage extends SpecialPage {
         * Generates the subheading with links
         * @param User $userObj User object for the target
         * @return string Appropriately-escaped HTML to be output literally
-        * @todo FIXME: Almost the same as contributionsSub in SpecialContributions.php. Could be combined.
         */
        function getSubTitle( $userObj ) {
+               $linkRenderer = $this->getLinkRenderer();
                if ( $userObj->isAnon() ) {
                        $user = htmlspecialchars( $userObj->getName() );
                } else {
-                       $user = Linker::link( $userObj->getUserPage(), htmlspecialchars( $userObj->getName() ) );
+                       $user = $linkRenderer->makeLink( $userObj->getUserPage(), $userObj->getName() );
                }
                $links = '';
                $nt = $userObj->getUserPage();
-               $id = $userObj->getId();
                $talk = $nt->getTalkPage();
                if ( $talk ) {
-                       # Talk page link
-                       $tools[] = Linker::link( $talk, $this->msg( 'sp-contributions-talk' )->escaped() );
-                       if ( ( $id !== null ) || ( $id === null && IP::isIPAddress( $nt->getText() ) ) ) {
-                               # Block / Change block / Unblock links
-                               if ( $this->getUser()->isAllowed( 'block' ) ) {
-                                       if ( $userObj->isBlocked() && $userObj->getBlock()->getType() !== Block::TYPE_AUTO ) {
-                                               $tools[] = Linker::linkKnown( # Change block link
-                                                       SpecialPage::getTitleFor( 'Block', $nt->getDBkey() ),
-                                                       $this->msg( 'change-blocklink' )->escaped()
-                                               );
-                                               $tools[] = Linker::linkKnown( # Unblock link
-                                                       SpecialPage::getTitleFor( 'BlockList' ),
-                                                       $this->msg( 'unblocklink' )->escaped(),
-                                                       [],
-                                                       [
-                                                               'action' => 'unblock',
-                                                               'ip' => $nt->getDBkey()
-                                                       ]
-                                               );
-                                       } else {
-                                               # User is not blocked
-                                               $tools[] = Linker::linkKnown( # Block link
-                                                       SpecialPage::getTitleFor( 'Block', $nt->getDBkey() ),
-                                                       $this->msg( 'blocklink' )->escaped()
-                                               );
-                                       }
-                               }
-                               # Block log link
-                               $tools[] = Linker::linkKnown(
-                                       SpecialPage::getTitleFor( 'Log' ),
-                                       $this->msg( 'sp-contributions-blocklog' )->escaped(),
-                                       [],
-                                       [
-                                               'type' => 'block',
-                                               'page' => $nt->getPrefixedText()
-                                       ]
-                               );
-                               # Suppression log link (bug 59120)
-                               if ( $this->getUser()->isAllowed( 'suppressionlog' ) ) {
-                                       $tools[] = Linker::linkKnown(
-                                               SpecialPage::getTitleFor( 'Log', 'suppress' ),
-                                               $this->msg( 'sp-contributions-suppresslog' )->escaped(),
-                                               [],
-                                               [ 'offender' => $userObj->getName() ]
-                                       );
-                               }
-                       }
+                       $tools = SpecialContributions::getUserLinks( $this, $userObj );
 
-                       # Uploads
-                       $tools[] = Linker::linkKnown(
-                               SpecialPage::getTitleFor( 'Listfiles', $userObj->getName() ),
-                               $this->msg( 'sp-contributions-uploads' )->escaped()
-                       );
-
-                       # Other logs link
-                       $tools[] = Linker::linkKnown(
-                               SpecialPage::getTitleFor( 'Log' ),
-                               $this->msg( 'sp-contributions-logs' )->escaped(),
-                               [],
-                               [ 'user' => $nt->getText() ]
-                       );
                        # Link to contributions
-                       $tools[] = Linker::linkKnown(
+                       $insert['contribs'] = $linkRenderer->makeKnownLink(
                                SpecialPage::getTitleFor( 'Contributions', $nt->getDBkey() ),
-                               $this->msg( 'sp-deletedcontributions-contribs' )->escaped()
+                               $this->msg( 'sp-deletedcontributions-contribs' )->text()
                        );
 
-                       # Add a link to change user rights for privileged users
-                       $userrightsPage = new UserrightsPage();
-                       $userrightsPage->setContext( $this->getContext() );
-                       if ( $userrightsPage->userCanChangeRights( $userObj ) ) {
-                               $tools[] = Linker::linkKnown(
-                                       SpecialPage::getTitleFor( 'Userrights', $nt->getDBkey() ),
-                                       $this->msg( 'sp-contributions-userrights' )->escaped()
-                               );
-                       }
-
-                       Hooks::run( 'ContributionsToolLinks', [ $id, $nt, &$tools ] );
+                       // Swap out the deletedcontribs link for our contribs one
+                       $tools = wfArrayInsertAfter( $tools, $insert, 'deletedcontribs' );
+                       unset( $tools['deletedcontribs'] );
 
                        $links = $this->getLanguage()->pipeList( $tools );
 
index 7b00064..0cec9d0 100644 (file)
@@ -50,7 +50,7 @@ class DoubleRedirectsPage extends QueryPage {
 
        function reallyGetQueryInfo( $namespace = null, $title = null ) {
                $limitToTitle = !( $namespace === null && $title === null );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $retval = [
                        'tables' => [
                                'ra' => 'redirect',
@@ -121,7 +121,7 @@ class DoubleRedirectsPage extends QueryPage {
                // get a little more detail about each individual entry quickly
                // using the filter of reallyGetQueryInfo.
                if ( $result && !isset( $result->nsb ) ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $qi = $this->reallyGetQueryInfo(
                                $result->namespace,
                                $result->title
index 627dd2c..a5a45d5 100644 (file)
@@ -2,7 +2,6 @@
 /**
  * @defgroup Watchlist Users watchlist handling
  */
-use MediaWiki\Linker\LinkTarget;
 
 /**
  * Implements Special:EditWatchlist
@@ -27,6 +26,8 @@ use MediaWiki\Linker\LinkTarget;
  * @ingroup Watchlist
  */
 
+use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\Linker\LinkTarget;
 use MediaWiki\MediaWikiServices;
 
 /**
@@ -138,7 +139,13 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
        protected function outputSubtitle() {
                $out = $this->getOutput();
                $out->addSubtitle( $this->msg( 'watchlistfor2', $this->getUser()->getName() )
-                       ->rawParams( SpecialEditWatchlist::buildTools( null ) ) );
+                       ->rawParams(
+                               self::buildTools(
+                                       $this->getLanguage(),
+                                       $this->getLinkRenderer()
+                               )
+                       )
+               );
        }
 
        /**
@@ -277,7 +284,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
         * @param string $output
         */
        private function showTitles( $titles, &$output ) {
-               $talk = $this->msg( 'talkpagelinktext' )->escaped();
+               $talk = $this->msg( 'talkpagelinktext' )->text();
                // Do a batch existence check
                $batch = new LinkBatch();
                if ( count( $titles ) >= 100 ) {
@@ -300,6 +307,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
                // Print out the list
                $output .= "<ul>\n";
 
+               $linkRenderer = $this->getLinkRenderer();
                foreach ( $titles as $title ) {
                        if ( !$title instanceof Title ) {
                                $title = Title::newFromText( $title );
@@ -307,9 +315,9 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
 
                        if ( $title instanceof Title ) {
                                $output .= '<li>' .
-                                       Linker::link( $title ) . ' ' .
+                                       $linkRenderer->makeLink( $title ) . ' ' .
                                        $this->msg( 'parentheses' )->rawParams(
-                                               Linker::link( $title->getTalkPage(), $talk )
+                                               $linkRenderer->makeLink( $title->getTalkPage(), $talk )
                                        )->escaped() .
                                        "</li>\n";
                        }
@@ -610,26 +618,27 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
         * @return string
         */
        private function buildRemoveLine( $title ) {
-               $link = Linker::link( $title );
+               $linkRenderer = $this->getLinkRenderer();
+               $link = $linkRenderer->makeLink( $title );
 
-               $tools['talk'] = Linker::link(
+               $tools['talk'] = $linkRenderer->makeLink(
                        $title->getTalkPage(),
-                       $this->msg( 'talkpagelinktext' )->escaped()
+                       $this->msg( 'talkpagelinktext' )->text()
                );
 
                if ( $title->exists() ) {
-                       $tools['history'] = Linker::linkKnown(
+                       $tools['history'] = $linkRenderer->makeKnownLink(
                                $title,
-                               $this->msg( 'history_short' )->escaped(),
+                               $this->msg( 'history_short' )->text(),
                                [],
                                [ 'action' => 'history' ]
                        );
                }
 
                if ( $title->getNamespace() == NS_USER && !$title->isSubpage() ) {
-                       $tools['contributions'] = Linker::linkKnown(
+                       $tools['contributions'] = $linkRenderer->makeKnownLink(
                                SpecialPage::getTitleFor( 'Contributions', $title->getText() ),
-                               $this->msg( 'contributions' )->escaped()
+                               $this->msg( 'contributions' )->text()
                        );
                }
 
@@ -724,11 +733,19 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
         * Build a set of links for convenient navigation
         * between watchlist viewing and editing modes
         *
-        * @param null $unused
+        * @param Language $lang
+        * @param LinkRenderer|null $linkRenderer
         * @return string
         */
-       public static function buildTools( $unused ) {
-               global $wgLang;
+       public static function buildTools( $lang, LinkRenderer $linkRenderer = null ) {
+               if ( !$lang instanceof Language ) {
+                       // back-compat where the first parameter was $unused
+                       global $wgLang;
+                       $lang = $wgLang;
+               }
+               if ( !$linkRenderer ) {
+                       $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
+               }
 
                $tools = [];
                $modes = [
@@ -740,16 +757,16 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
 
                foreach ( $modes as $mode => $arr ) {
                        // can use messages 'watchlisttools-view', 'watchlisttools-edit', 'watchlisttools-raw'
-                       $tools[] = Linker::linkKnown(
+                       $tools[] = $linkRenderer->makeKnownLink(
                                SpecialPage::getTitleFor( $arr[0], $arr[1] ),
-                               wfMessage( "watchlisttools-{$mode}" )->escaped()
+                               wfMessage( "watchlisttools-{$mode}" )->text()
                        );
                }
 
                return Html::rawElement(
                        'span',
                        [ 'class' => 'mw-watchlist-toollinks' ],
-                       wfMessage( 'parentheses' )->rawParams( $wgLang->pipeList( $tools ) )->escaped()
+                       wfMessage( 'parentheses' )->rawParams( $lang->pipeList( $tools ) )->escaped()
                );
        }
 }
index b5c66ff..d2e3e7f 100644 (file)
@@ -39,12 +39,15 @@ class EmailInvalidation extends UnlistedSpecialPage {
        function execute( $code ) {
                // Ignore things like master queries/connections on GET requests.
                // It's very convenient to just allow formless link usage.
-               Profiler::instance()->getTransactionProfiler()->resetExpectations();
+               $trxProfiler = Profiler::instance()->getTransactionProfiler();
 
                $this->setHeaders();
                $this->checkReadOnly();
                $this->checkPermissions();
+
+               $trxProfiler->setSilenced( true );
                $this->attemptInvalidate( $code );
+               $trxProfiler->setSilenced( false );
        }
 
        /**
@@ -53,7 +56,7 @@ class EmailInvalidation extends UnlistedSpecialPage {
         *
         * @param string $code Confirmation code
         */
-       function attemptInvalidate( $code ) {
+       private function attemptInvalidate( $code ) {
                $user = User::newFromConfirmationCode( $code, User::READ_LATEST );
                if ( !is_object( $user ) ) {
                        $this->getOutput()->addWikiMsg( 'confirmemail_invalid' );
index fb1943f..06be7bc 100644 (file)
@@ -388,13 +388,29 @@ class SpecialEmailUser extends UnlistedSpecialPage {
                        // unless they are emailing themselves, in which case one
                        // copy of the message is sufficient.
                        if ( $data['CCMe'] && $to != $from ) {
-                               $cc_subject = $context->msg( 'emailccsubject' )->rawParams(
+                               $ccTo = $from;
+                               $ccFrom = $from;
+                               $ccSubject = $context->msg( 'emailccsubject' )->rawParams(
                                        $target->getName(), $subject )->text();
-
-                               // target and sender are equal, because this is the CC for the sender
-                               Hooks::run( 'EmailUserCC', [ &$from, &$from, &$cc_subject, &$text ] );
-
-                               $ccStatus = UserMailer::send( $from, $from, $cc_subject, $text );
+                               $ccText = $text;
+
+                               Hooks::run( 'EmailUserCC', [ &$ccTo, &$ccFrom, &$ccSubject, &$ccText ] );
+
+                               if ( $config->get( 'UserEmailUseReplyTo' ) ) {
+                                       $mailFrom = new MailAddress(
+                                               $config->get( 'PasswordSender' ),
+                                               wfMessage( 'emailsender' )->inContentLanguage()->text()
+                                       );
+                                       $replyTo = $ccFrom;
+                               } else {
+                                       $mailFrom = $ccFrom;
+                                       $replyTo = null;
+                               }
+
+                               $ccStatus = UserMailer::send(
+                                       $ccTo, $mailFrom, $ccSubject, $ccText, [
+                                               'replyTo' => $replyTo,
+                               ] );
                                $status->merge( $ccStatus );
                        }
 
index 3e66ab0..bf535a6 100644 (file)
@@ -201,6 +201,7 @@ class SpecialExport extends SpecialPage {
                                'buttontype' => 'submit',
                                'buttonname' => 'addcat',
                                'buttondefault' => $this->msg( 'export-addcat' )->text(),
+                               'hide-if' => [ '===', 'exportall', '1' ],
                        ],
                ];
                if ( $config->get( 'ExportFromNamespaces' ) ) {
@@ -216,6 +217,7 @@ class SpecialExport extends SpecialPage {
                                        'buttontype' => 'submit',
                                        'buttonname' => 'addns',
                                        'buttondefault' => $this->msg( 'export-addns' )->text(),
+                                       'hide-if' => [ '===', 'exportall', '1' ],
                                ],
                        ];
                }
@@ -240,6 +242,7 @@ class SpecialExport extends SpecialPage {
                                'nodata' => true,
                                'rows' => 10,
                                'default' => $page,
+                               'hide-if' => [ '===', 'exportall', '1' ],
                        ],
                ];
 
@@ -367,12 +370,12 @@ class SpecialExport extends SpecialPage {
                /* Ok, let's get to it... */
                if ( $history == WikiExporter::CURRENT ) {
                        $lb = false;
-                       $db = wfGetDB( DB_SLAVE );
+                       $db = wfGetDB( DB_REPLICA );
                        $buffer = WikiExporter::BUFFER;
                } else {
                        // Use an unbuffered query; histories may be very long!
                        $lb = wfGetLBFactory()->newMainLB();
-                       $db = $lb->getConnection( DB_SLAVE );
+                       $db = $lb->getConnection( DB_REPLICA );
                        $buffer = WikiExporter::STREAM;
 
                        // This might take a while... :D
@@ -423,7 +426,7 @@ class SpecialExport extends SpecialPage {
 
                $name = $title->getDBkey();
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select(
                        [ 'page', 'categorylinks' ],
                        [ 'page_namespace', 'page_title' ],
@@ -456,7 +459,7 @@ class SpecialExport extends SpecialPage {
 
                $maxPages = $this->getConfig()->get( 'ExportPagelistLimit' );
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select(
                        'page',
                        [ 'page_namespace', 'page_title' ],
@@ -553,7 +556,7 @@ class SpecialExport extends SpecialPage {
         * @return array
         */
        private function getLinks( $inputPages, $pageSet, $table, $fields, $join ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                foreach ( $inputPages as $page ) {
                        $title = Title::newFromText( $page );
index 5d36a3c..0e2e7db 100644 (file)
@@ -118,7 +118,7 @@ class SpecialJavaScriptTest extends SpecialPage {
                        . 'window.__karma__.loaded = function () {};'
                        . '}';
 
-               // The below is essentially a pure-javascript version of OutputPage::getHeadScripts.
+               // The below is essentially a pure-javascript version of OutputPage::headElement().
                $startup = $rl->makeModuleResponse( $startupContext, [
                        'startup' => $rl->getModule( 'startup' ),
                ] );
@@ -166,7 +166,7 @@ class SpecialJavaScriptTest extends SpecialPage {
                        [ 'raw' => true, 'sync' => true ]
                );
 
-               $head = implode( "\n", array_merge( $styles['html'], $scripts['html'] ) );
+               $head = implode( "\n", [ $styles, $scripts ] );
                $summary = $this->getSummaryHtml();
                $html = <<<HTML
 <!DOCTYPE html>
index d4886f0..307d6c3 100644 (file)
@@ -153,7 +153,7 @@ class LinkSearchPage extends QueryPage {
         */
        static function mungeQuery( $query, $prot ) {
                $field = 'el_index';
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                if ( $query === '*' && $prot !== '' ) {
                        // Allow queries like 'ftp://*' to find all ftp links
@@ -185,7 +185,7 @@ class LinkSearchPage extends QueryPage {
        }
 
        public function getQueryInfo() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                // strip everything past first wildcard, so that
                // index-based-only lookup would be done
                list( $this->mungedQuery, $clause ) = self::mungeQuery( $this->mQuery, $this->mProt );
index af087a6..533a331 100644 (file)
@@ -37,6 +37,7 @@ class SpecialLog extends SpecialPage {
                $this->setHeaders();
                $this->outputHeader();
                $this->getOutput()->addModules( 'mediawiki.userSuggest' );
+               $this->addHelpLink( 'Help:Log' );
 
                $opts = new FormOptions;
                $opts->add( 'type', '' );
index e51e8b5..ec87716 100644 (file)
@@ -62,7 +62,7 @@ class MediaStatisticsPage extends QueryPage {
         * Special:BrokenRedirects also rely on this.
         */
        public function getQueryInfo() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $fakeTitle = $dbr->buildConcat( [
                        'img_media_type',
                        $dbr->addQuotes( ';' ),
index 058d1ce..b916c1f 100644 (file)
  * @ingroup SpecialPage
  */
 class SpecialMergeHistory extends SpecialPage {
-       /** @var FormOptions */
-       protected $mOpts;
+       /** @var string */
+       protected $mAction;
 
-       /** @var Status */
-       protected $mStatus;
+       /** @var string */
+       protected $mTarget;
 
-       /** @var Title|null */
-       protected $mTargetObj, $mDestObj;
+       /** @var string */
+       protected $mDest;
+
+       /** @var string */
+       protected $mTimestamp;
+
+       /** @var int */
+       protected $mTargetID;
+
+       /** @var int */
+       protected $mDestID;
+
+       /** @var string */
+       protected $mComment;
+
+       /** @var bool Was posted? */
+       protected $mMerge;
+
+       /** @var bool Was submitted? */
+       protected $mSubmitted;
+
+       /** @var Title */
+       protected $mTargetObj;
+
+       /** @var Title */
+       protected $mDestObj;
 
        /** @var int[] */
        public $prevId;
@@ -48,106 +72,124 @@ class SpecialMergeHistory extends SpecialPage {
                return true;
        }
 
+       /**
+        * @return void
+        */
+       private function loadRequestParams() {
+               $request = $this->getRequest();
+               $this->mAction = $request->getVal( 'action' );
+               $this->mTarget = $request->getVal( 'target' );
+               $this->mDest = $request->getVal( 'dest' );
+               $this->mSubmitted = $request->getBool( 'submitted' );
+
+               $this->mTargetID = intval( $request->getVal( 'targetID' ) );
+               $this->mDestID = intval( $request->getVal( 'destID' ) );
+               $this->mTimestamp = $request->getVal( 'mergepoint' );
+               if ( !preg_match( '/[0-9]{14}/', $this->mTimestamp ) ) {
+                       $this->mTimestamp = '';
+               }
+               $this->mComment = $request->getText( 'wpComment' );
+
+               $this->mMerge = $request->wasPosted()
+                       && $this->getUser()->matchEditToken( $request->getVal( 'wpEditToken' ) );
+
+               // target page
+               if ( $this->mSubmitted ) {
+                       $this->mTargetObj = Title::newFromText( $this->mTarget );
+                       $this->mDestObj = Title::newFromText( $this->mDest );
+               } else {
+                       $this->mTargetObj = null;
+                       $this->mDestObj = null;
+               }
+       }
+
        public function execute( $par ) {
                $this->useTransactionalTimeLimit();
 
                $this->checkPermissions();
                $this->checkReadOnly();
 
+               $this->loadRequestParams();
+
                $this->setHeaders();
                $this->outputHeader();
 
-               $this->addHelpLink( 'Help:Merge history' );
-
-               $opts = new FormOptions();
-
-               $opts->add( 'target', '' );
-               $opts->add( 'dest', '' );
-               $opts->add( 'target', '' );
-               $opts->add( 'mergepoint', '' );
-               $opts->add( 'reason', '' );
-               $opts->add( 'merge', false );
-
-               $opts->fetchValuesFromRequest( $this->getRequest() );
-
-               $target = $opts->getValue( 'target' );
-               $dest = $opts->getValue( 'dest' );
-               $targetObj = Title::newFromText( $target );
-               $destObj = Title::newFromText( $dest );
-               $status = Status::newGood();
-
-               $this->mOpts = $opts;
-               $this->mTargetObj = $targetObj;
-               $this->mDestObj = $destObj;
-
-               if ( $opts->getValue( 'merge' ) && $targetObj && $destObj ) {
+               if ( $this->mTargetID && $this->mDestID && $this->mAction == 'submit' && $this->mMerge ) {
                        $this->merge();
 
                        return;
                }
 
-               if ( $target === '' && $dest === '' ) {
+               if ( !$this->mSubmitted ) {
                        $this->showMergeForm();
 
                        return;
                }
 
-               if ( !$targetObj instanceof Title ) {
-                       $status->merge( Status::newFatal( 'mergehistory-invalid-source' ) );
-               } elseif ( !$targetObj->exists() ) {
-                       $status->merge( Status::newFatal( 'mergehistory-no-source',
-                               wfEscapeWikiText( $targetObj->getPrefixedText() )
-                       ) );
+               $errors = [];
+               if ( !$this->mTargetObj instanceof Title ) {
+                       $errors[] = $this->msg( 'mergehistory-invalid-source' )->parseAsBlock();
+               } elseif ( !$this->mTargetObj->exists() ) {
+                       $errors[] = $this->msg( 'mergehistory-no-source',
+                               wfEscapeWikiText( $this->mTargetObj->getPrefixedText() )
+                       )->parseAsBlock();
                }
 
-               if ( !$destObj instanceof Title ) {
-                       $status->merge( Status::newFatal( 'mergehistory-invalid-destination' ) );
-               } elseif ( !$destObj->exists() ) {
-                       $status->merge( Status::newFatal( 'mergehistory-no-destination',
-                               wfEscapeWikiText( $destObj->getPrefixedText() )
-                       ) );
+               if ( !$this->mDestObj instanceof Title ) {
+                       $errors[] = $this->msg( 'mergehistory-invalid-destination' )->parseAsBlock();
+               } elseif ( !$this->mDestObj->exists() ) {
+                       $errors[] = $this->msg( 'mergehistory-no-destination',
+                               wfEscapeWikiText( $this->mDestObj->getPrefixedText() )
+                       )->parseAsBlock();
                }
 
-               if ( $targetObj && $destObj && $targetObj->equals( $destObj ) ) {
-                       $status->merge( Status::newFatal( 'mergehistory-same-destination' ) );
+               if ( $this->mTargetObj && $this->mDestObj && $this->mTargetObj->equals( $this->mDestObj ) ) {
+                       $errors[] = $this->msg( 'mergehistory-same-destination' )->parseAsBlock();
                }
 
-               $this->mStatus = $status;
-
-               $this->showMergeForm();
-
-               if ( $status->isOK() ) {
+               if ( count( $errors ) ) {
+                       $this->showMergeForm();
+                       $this->getOutput()->addHTML( implode( "\n", $errors ) );
+               } else {
                        $this->showHistory();
                }
        }
 
        function showMergeForm() {
-               $formDescriptor = [
-                       'target' => [
-                               'type' => 'title',
-                               'name' => 'target',
-                               'label-message' => 'mergehistory-from',
-                               'required' => true,
-                       ],
-
-                       'dest' => [
-                               'type' => 'title',
-                               'name' => 'dest',
-                               'label-message' => 'mergehistory-into',
-                               'required' => true,
-                       ],
-               ];
-
-               $form = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
-                       ->setIntro( $this->msg( 'mergehistory-header' ) )
-                       ->setWrapperLegendMsg( 'mergehistory-box' )
-                       ->setSubmitTextMsg( 'mergehistory-go' )
-                       ->setMethod( 'post' )
-                       ->prepareForm()
-                       ->displayForm( $this->mStatus );
+               $out = $this->getOutput();
+               $out->addWikiMsg( 'mergehistory-header' );
+
+               $out->addHTML(
+                       Xml::openElement( 'form', [
+                               'method' => 'get',
+                               'action' => wfScript() ] ) .
+                               '<fieldset>' .
+                               Xml::element( 'legend', [],
+                                       $this->msg( 'mergehistory-box' )->text() ) .
+                               Html::hidden( 'title', $this->getPageTitle()->getPrefixedDBkey() ) .
+                               Html::hidden( 'submitted', '1' ) .
+                               Html::hidden( 'mergepoint', $this->mTimestamp ) .
+                               Xml::openElement( 'table' ) .
+                               '<tr>
+                               <td>' . Xml::label( $this->msg( 'mergehistory-from' )->text(), 'target' ) . '</td>
+                               <td>' . Xml::input( 'target', 30, $this->mTarget, [ 'id' => 'target' ] ) . '</td>
+                       </tr><tr>
+                               <td>' . Xml::label( $this->msg( 'mergehistory-into' )->text(), 'dest' ) . '</td>
+                               <td>' . Xml::input( 'dest', 30, $this->mDest, [ 'id' => 'dest' ] ) . '</td>
+                       </tr><tr><td>' .
+                               Xml::submitButton( $this->msg( 'mergehistory-go' )->text() ) .
+                               '</td></tr>' .
+                               Xml::closeElement( 'table' ) .
+                               '</fieldset>' .
+                               '</form>'
+               );
+
+               $this->addHelpLink( 'Help:Merge history' );
        }
 
        private function showHistory() {
+               $this->showMergeForm();
+
                # List all stored revisions
                $revisions = new MergeHistoryPager(
                        $this, [], $this->mTargetObj, $this->mDestObj
@@ -155,46 +197,62 @@ class SpecialMergeHistory extends SpecialPage {
                $haveRevisions = $revisions && $revisions->getNumRows() > 0;
 
                $out = $this->getOutput();
-               $header = '<h2 id="mw-mergehistory">' .
-                       $this->msg( 'mergehistory-list' )->escaped() . "</h2>\n";
+               $titleObj = $this->getPageTitle();
+               $action = $titleObj->getLocalURL( [ 'action' => 'submit' ] );
+               # Start the form here
+               $top = Xml::openElement(
+                       'form',
+                       [
+                               'method' => 'post',
+                               'action' => $action,
+                               'id' => 'merge'
+                       ]
+               );
+               $out->addHTML( $top );
+
+               if ( $haveRevisions ) {
+                       # Format the user-visible controls (comment field, submission button)
+                       # in a nice little table
+                       $table =
+                               Xml::openElement( 'fieldset' ) .
+                                       $this->msg( 'mergehistory-merge', $this->mTargetObj->getPrefixedText(),
+                                               $this->mDestObj->getPrefixedText() )->parse() .
+                                       Xml::openElement( 'table', [ 'id' => 'mw-mergehistory-table' ] ) .
+                                       '<tr>
+                                               <td class="mw-label">' .
+                                       Xml::label( $this->msg( 'mergehistory-reason' )->text(), 'wpComment' ) .
+                                       '</td>
+                                       <td class="mw-input">' .
+                                       Xml::input( 'wpComment', 50, $this->mComment, [ 'id' => 'wpComment' ] ) .
+                                       '</td>
+                                       </tr>
+                                       <tr>
+                                               <td>&#160;</td>
+                                               <td class="mw-submit">' .
+                                       Xml::submitButton(
+                                               $this->msg( 'mergehistory-submit' )->text(),
+                                               [ 'name' => 'merge', 'id' => 'mw-merge-submit' ]
+                                       ) .
+                                       '</td>
+                                       </tr>' .
+                                       Xml::closeElement( 'table' ) .
+                                       Xml::closeElement( 'fieldset' );
+
+                       $out->addHTML( $table );
+               }
+
+               $out->addHTML(
+                       '<h2 id="mw-mergehistory">' .
+                               $this->msg( 'mergehistory-list' )->escaped() . "</h2>\n"
+               );
 
                if ( $haveRevisions ) {
-                       $hiddenFields = [
-                               'merge' => true,
-                               'target' => $this->mOpts->getValue( 'target' ),
-                               'dest' => $this->mOpts->getValue( 'dest' ),
-                       ];
-
-                       $formDescriptor = [
-                               'reason' => [
-                                       'type' => 'text',
-                                       'name' => 'reason',
-                                       'label-message' => 'mergehistory-reason',
-                               ],
-                       ];
-
-                       $mergeText = $this->msg( 'mergehistory-merge',
-                               $this->mTargetObj->getPrefixedText(),
-                               $this->mDestObj->getPrefixedText()
-                       )->parse();
-
-                       $history = $header .
-                               $revisions->getNavigationBar() .
-                               '<ul>' .
-                               $revisions->getBody() .
-                               '</ul>' .
-                               $revisions->getNavigationBar();
-
-                       $form = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
-                               ->addHiddenFields( $hiddenFields )
-                               ->setPreText( $mergeText )
-                               ->setFooterText( $history )
-                               ->setSubmitTextMsg( 'mergehistory-submit' )
-                               ->setMethod( 'post' )
-                               ->prepareForm()
-                               ->displayForm( false );
+                       $out->addHTML( $revisions->getNavigationBar() );
+                       $out->addHTML( '<ul>' );
+                       $out->addHTML( $revisions->getBody() );
+                       $out->addHTML( '</ul>' );
+                       $out->addHTML( $revisions->getNavigationBar() );
                } else {
-                       $out->addHTML( $header );
                        $out->addWikiMsg( 'mergehistory-empty' );
                }
 
@@ -202,6 +260,18 @@ class SpecialMergeHistory extends SpecialPage {
                $mergeLogPage = new LogPage( 'merge' );
                $out->addHTML( '<h2>' . $mergeLogPage->getName()->escaped() . "</h2>\n" );
                LogEventsList::showLogExtract( $out, 'merge', $this->mTargetObj );
+
+               # When we submit, go by page ID to avoid some nasty but unlikely collisions.
+               # Such would happen if a page was renamed after the form loaded, but before submit
+               $misc = Html::hidden( 'targetID', $this->mTargetObj->getArticleID() );
+               $misc .= Html::hidden( 'destID', $this->mDestObj->getArticleID() );
+               $misc .= Html::hidden( 'target', $this->mTarget );
+               $misc .= Html::hidden( 'dest', $this->mDest );
+               $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
+               $misc .= Xml::closeElement( 'form' );
+               $out->addHTML( $misc );
+
+               return true;
        }
 
        function formatRevisionRow( $row ) {
@@ -211,7 +281,7 @@ class SpecialMergeHistory extends SpecialPage {
                $last = $this->msg( 'last' )->escaped();
 
                $ts = wfTimestamp( TS_MW, $row->rev_timestamp );
-               $checkBox = Xml::radio( 'mergepoint', $ts, ( $this->mOpts->getValue( 'mergepoint' ) === $ts ) );
+               $checkBox = Xml::radio( 'mergepoint', $ts, ( $this->mTimestamp === $ts ) );
 
                $user = $this->getUser();
 
@@ -266,24 +336,23 @@ class SpecialMergeHistory extends SpecialPage {
         * @return bool Success
         */
        function merge() {
-               $opts = $this->mOpts;
-
                # Get the titles directly from the IDs, in case the target page params
                # were spoofed. The queries are done based on the IDs, so it's best to
                # keep it consistent...
-               $targetObj = $this->mTargetObj;
-               $destObj = $this->mDestObj;
-
-               if ( is_null( $targetObj ) || is_null( $destObj ) ||
-                       $targetObj->getArticleID() == $destObj->getArticleID() ) {
+               $targetTitle = Title::newFromID( $this->mTargetID );
+               $destTitle = Title::newFromID( $this->mDestID );
+               if ( is_null( $targetTitle ) || is_null( $destTitle ) ) {
+                       return false; // validate these
+               }
+               if ( $targetTitle->getArticleID() == $destTitle->getArticleID() ) {
                        return false;
                }
 
                // MergeHistory object
-               $mh = new MergeHistory( $targetObj, $destObj, $opts->getValue( 'mergepoint' ) );
+               $mh = new MergeHistory( $targetTitle, $destTitle, $this->mTimestamp );
 
                // Merge!
-               $mergeStatus = $mh->merge( $this->getUser(), $opts->getValue( 'reason' ) );
+               $mergeStatus = $mh->merge( $this->getUser(), $this->mComment );
                if ( !$mergeStatus->isOK() ) {
                        // Failed merge
                        $this->getOutput()->addWikiMsg( $mergeStatus->getMessage() );
@@ -291,7 +360,7 @@ class SpecialMergeHistory extends SpecialPage {
                }
 
                $targetLink = Linker::link(
-                       $targetObj,
+                       $targetTitle,
                        null,
                        [],
                        [ 'redirect' => 'no' ]
@@ -299,7 +368,7 @@ class SpecialMergeHistory extends SpecialPage {
 
                $this->getOutput()->addWikiMsg( $this->msg( 'mergehistory-done' )
                        ->rawParams( $targetLink )
-                       ->params( $destObj->getPrefixedText() )
+                       ->params( $destTitle->getPrefixedText() )
                        ->numParams( $mh->getMergedRevisionCount() )
                );
 
index b8316f3..3ead08a 100644 (file)
@@ -94,7 +94,7 @@ class MostlinkedCategoriesPage extends QueryPage {
                }
 
                $text = $wgContLang->convert( $nt->getText() );
-               $plink = Linker::link( $nt, htmlspecialchars( $text ) );
+               $plink = $this->getLinkRenderer()->makeLink( $nt, $text );
                $nlinks = $this->msg( 'nmembers' )->numParams( $result->value )->escaped();
 
                return $this->getLanguage()->specialList( $plink, $nlinks );
index 20a508d..3a12cf3 100644 (file)
@@ -139,8 +139,6 @@ class MovePageForm extends UnlistedSpecialPage {
         *    parameters, like the second argument to OutputPage::wrapWikiMsg().
         */
        function showForm( $err ) {
-               global $wgContLang;
-
                $this->getSkin()->setRelevantTitle( $this->oldTitle );
 
                $out = $this->getOutput();
@@ -225,7 +223,7 @@ class MovePageForm extends UnlistedSpecialPage {
                        ( $oldTalk->exists()
                                || ( $oldTitleTalkSubpages && $canMoveSubpage ) );
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                if ( $this->getConfig()->get( 'FixDoubleRedirects' ) ) {
                        $hasRedirects = $dbr->selectField( 'redirect', '1',
                                [
@@ -316,7 +314,7 @@ class MovePageForm extends UnlistedSpecialPage {
                                'title' => [
                                        'id' => 'wpNewTitleMain',
                                        'name' => 'wpNewTitleMain',
-                                       'value' => $wgContLang->recodeForEdit( $newTitle->getText() ),
+                                       'value' => $newTitle->getText(),
                                        // Inappropriate, since we're expecting the user to input a non-existent page's title
                                        'suggestions' => false,
                                ],
@@ -355,6 +353,7 @@ class MovePageForm extends UnlistedSpecialPage {
                                        'help' => new OOUI\HtmlSnippet( $this->msg( 'movepagetalktext' )->parseAsBlock() ),
                                        'align' => 'inline',
                                        'infusable' => true,
+                                       'id' => 'wpMovetalk-field',
                                ]
                        );
                }
@@ -805,6 +804,10 @@ class MovePageForm extends UnlistedSpecialPage {
                $out->addWikiMsg( 'movesubpagetext', $this->getLanguage()->formatNum( $count ) );
                $out->addHTML( "<ul>\n" );
 
+               $linkBatch = new LinkBatch( $subpages );
+               $linkBatch->setCaller( __METHOD__ );
+               $linkBatch->execute();
+
                $linkRenderer = $this->getLinkRenderer();
                foreach ( $subpages as $subpage ) {
                        $link = $linkRenderer->makeLink( $subpage );
index d11fbe6..9cb6d4b 100644 (file)
@@ -91,7 +91,7 @@ class SpecialMyLanguage extends RedirectSpecialArticle {
 
                $uiCode = $this->getLanguage()->getCode();
                $proposed = $base->getSubpage( $uiCode );
-               if ( $uiCode !== $this->getConfig()->get( 'LanguageCode' ) && $proposed && $proposed->exists() ) {
+               if ( $proposed && $proposed->exists() && $uiCode !== $base->getPageLanguage()->getCode() ) {
                        return $proposed;
                } elseif ( $provided && $provided->exists() ) {
                        return $provided;
index 14391d2..077a5d2 100644 (file)
@@ -22,6 +22,9 @@
  */
 
 class SpecialNewFiles extends IncludableSpecialPage {
+       /** @var FormOptions */
+       protected $opts;
+
        public function __construct() {
                parent::__construct( 'Newimages' );
        }
@@ -33,21 +36,86 @@ class SpecialNewFiles extends IncludableSpecialPage {
                $out = $this->getOutput();
                $this->addHelpLink( 'Help:New images' );
 
-               $pager = new NewFilesPager( $this->getContext(), $par );
+               $opts = new FormOptions();
+
+               $opts->add( 'like', '' );
+               $opts->add( 'showbots', false );
+               $opts->add( 'hidepatrolled', false );
+               $opts->add( 'limit', 50 );
+               $opts->add( 'offset', '' );
+
+               $opts->fetchValuesFromRequest( $this->getRequest() );
+
+               if ( $par !== null ) {
+                       $opts->setValue( is_numeric( $par ) ? 'limit' : 'like', $par );
+               }
+
+               $opts->validateIntBounds( 'limit', 0, 500 );
+
+               $this->opts = $opts;
 
                if ( !$this->including() ) {
                        $this->setTopText();
-                       $form = $pager->getForm();
-                       $form->prepareForm();
-                       $form->displayForm( '' );
+                       $this->buildForm();
                }
 
+               $pager = new NewFilesPager( $this->getContext(), $opts );
+
                $out->addHTML( $pager->getBody() );
                if ( !$this->including() ) {
                        $out->addHTML( $pager->getNavigationBar() );
                }
        }
 
+       protected function buildForm() {
+               $formDescriptor = [
+                       'like' => [
+                               'type' => 'text',
+                               'label-message' => 'newimages-label',
+                               'name' => 'like',
+                       ],
+
+                       'showbots' => [
+                               'type' => 'check',
+                               'label-message' => 'newimages-showbots',
+                               'name' => 'showbots',
+                       ],
+
+                       'hidepatrolled' => [
+                               'type' => 'check',
+                               'label-message' => 'newimages-hidepatrolled',
+                               'name' => 'hidepatrolled',
+                       ],
+
+                       'limit' => [
+                               'type' => 'hidden',
+                               'default' => $this->opts->getValue( 'limit' ),
+                               'name' => 'limit',
+                       ],
+
+                       'offset' => [
+                               'type' => 'hidden',
+                               'default' => $this->opts->getValue( 'offset' ),
+                               'name' => 'offset',
+                       ],
+               ];
+
+               if ( $this->getConfig()->get( 'MiserMode' ) ) {
+                       unset( $formDescriptor['like'] );
+               }
+
+               if ( !$this->getUser()->useFilePatrol() ) {
+                       unset( $formDescriptor['hidepatrolled'] );
+               }
+
+               $form = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
+                       ->setWrapperLegendMsg( 'newimages-legend' )
+                       ->setSubmitTextMsg( 'ilsubmit' )
+                       ->setMethod( 'get' )
+                       ->prepareForm()
+                       ->displayForm( false );
+       }
+
        protected function getGroupName() {
                return 'changes';
        }
index 5322a04..61b6a8c 100644 (file)
@@ -188,6 +188,9 @@ class SpecialPageLanguage extends FormSpecialPage {
                $logid = $entry->insert();
                $entry->publish( $logid );
 
+               // Force re-render so that language-based content (parser functions etc.) gets updated
+               $title->invalidateCache();
+
                return true;
        }
 
index 327ddda..706a1d7 100644 (file)
@@ -174,7 +174,7 @@ class SpecialPagesWithProp extends QueryPage {
                        $opts['OFFSET'] = $offset;
                }
 
-               $res = wfGetDB( DB_SLAVE )->select(
+               $res = wfGetDB( DB_REPLICA )->select(
                        'page_props',
                        'pp_propname',
                        '',
index 2e7b4cd..3697e5d 100644 (file)
@@ -53,21 +53,22 @@ class SpecialPreferences extends SpecialPage {
                $out->addModules( 'mediawiki.special.preferences' );
                $out->addModuleStyles( 'mediawiki.special.preferences.styles' );
 
-               $request = $this->getRequest();
-               if ( $request->getSessionData( 'specialPreferencesSaveSuccess' ) ) {
+               $session = $this->getRequest()->getSession();
+               if ( $session->get( 'specialPreferencesSaveSuccess' ) ) {
                        // Remove session data for the success message
-                       $request->setSessionData( 'specialPreferencesSaveSuccess', null );
+                       $session->remove( 'specialPreferencesSaveSuccess' );
+                       $out->addModuleStyles( 'mediawiki.notification.convertmessagebox.styles' );
 
-                       $out->wrapWikiMsg(
+                       $out->addHtml(
                                Html::rawElement(
                                        'div',
                                        [
-                                               'class' => 'mw-preferences-messagebox successbox',
-                                               'id' => 'mw-preferences-success'
+                                               'class' => 'mw-preferences-messagebox mw-notify-success successbox',
+                                               'id' => 'mw-preferences-success',
+                                               'data-mw-autohide' => 'false',
                                        ],
-                                       Html::element( 'p', [], '$1' )
-                               ),
-                               'savedprefs'
+                                       Html::element( 'p', [], $this->msg( 'savedprefs' )->text() )
+                               )
                        );
                }
 
@@ -145,7 +146,7 @@ class SpecialPreferences extends SpecialPage {
                $user->saveSettings();
 
                // Set session data for the success message
-               $this->getRequest()->setSessionData( 'specialPreferencesSaveSuccess', 1 );
+               $this->getRequest()->getSession()->set( 'specialPreferencesSaveSuccess', 1 );
 
                $url = $this->getPageTitle()->getFullURL();
                $this->getOutput()->redirect( $url );
index 87a5b27..5e3e430 100644 (file)
@@ -181,7 +181,7 @@ class SpecialPrefixindex extends SpecialAllPages {
 
                        # ## @todo FIXME: Should complain if $fromNs != $namespace
 
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
 
                        $conds = [
                                'page_namespace' => $namespace,
index 54b471e..5bdae15 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup SpecialPage
  */
 
+use MediaWiki\Linker\LinkRenderer;
+
 /**
  * A special page that lists protected pages
  *
@@ -59,7 +61,8 @@ class SpecialProtectedpages extends SpecialPage {
                        $size,
                        $indefOnly,
                        $cascadeOnly,
-                       $noRedirect
+                       $noRedirect,
+                       $this->getLinkRenderer()
                );
 
                $this->getOutput()->addHTML( $this->showOptions(
@@ -279,8 +282,27 @@ class ProtectedPagesPager extends TablePager {
        public $mForm, $mConds;
        private $type, $level, $namespace, $sizetype, $size, $indefonly, $cascadeonly, $noredirect;
 
+       /**
+        * @var LinkRenderer
+        */
+       private $linkRenderer;
+
+       /**
+        * @param SpecialProtectedpages $form
+        * @param array $conds
+        * @param $type
+        * @param $level
+        * @param $namespace
+        * @param string $sizetype
+        * @param int $size
+        * @param bool $indefonly
+        * @param bool $cascadeonly
+        * @param bool $noredirect
+        * @param LinkRenderer $linkRenderer
+        */
        function __construct( $form, $conds = [], $type, $level, $namespace,
-               $sizetype = '', $size = 0, $indefonly = false, $cascadeonly = false, $noredirect = false
+               $sizetype = '', $size = 0, $indefonly = false, $cascadeonly = false, $noredirect = false,
+               LinkRenderer $linkRenderer
        ) {
                $this->mForm = $form;
                $this->mConds = $conds;
@@ -292,6 +314,7 @@ class ProtectedPagesPager extends TablePager {
                $this->indefonly = (bool)$indefonly;
                $this->cascadeonly = (bool)$cascadeonly;
                $this->noredirect = (bool)$noredirect;
+               $this->linkRenderer = $linkRenderer;
                parent::__construct( $form->getContext() );
        }
 
@@ -354,8 +377,6 @@ class ProtectedPagesPager extends TablePager {
                /** @var $row object */
                $row = $this->mCurrentRow;
 
-               $formatted = '';
-
                switch ( $field ) {
                        case 'log_timestamp':
                                // when timestamp is null, this is a old protection row
@@ -384,7 +405,7 @@ class ProtectedPagesPager extends TablePager {
                                                )
                                        );
                                } else {
-                                       $formatted = Linker::link( $title );
+                                       $formatted = $this->linkRenderer->makeLink( $title );
                                }
                                if ( !is_null( $row->page_len ) ) {
                                        $formatted .= $this->getLanguage()->getDirMark() .
@@ -401,9 +422,9 @@ class ProtectedPagesPager extends TablePager {
                                        $value, /* User preference timezone */true ) );
                                $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
                                if ( $this->getUser()->isAllowed( 'protect' ) && $title ) {
-                                       $changeProtection = Linker::linkKnown(
+                                       $changeProtection = $this->linkRenderer->makeKnownLink(
                                                $title,
-                                               $this->msg( 'protect_change' )->escaped(),
+                                               $this->msg( 'protect_change' )->text(),
                                                [],
                                                [ 'action' => 'unprotect' ]
                                        );
@@ -537,7 +558,7 @@ class ProtectedPagesPager extends TablePager {
                        'join_conds' => [
                                'log_search' => [
                                        'LEFT JOIN', [
-                                               'ls_field' => 'pr_id', 'ls_value = pr_id'
+                                               'ls_field' => 'pr_id', 'ls_value = ' . $this->mDb->buildStringCast( 'pr_id' )
                                        ]
                                ],
                                'logging' => [
index a5e538f..fc924a4 100644 (file)
@@ -223,7 +223,7 @@ class SpecialRandomInCategory extends FormSpecialPage {
                        ]
                ];
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $minClTime = $this->getTimestampOffset( $rand );
                if ( $minClTime ) {
                        $qi['conds'][] = 'cl_timestamp ' . $op . ' ' .
@@ -264,7 +264,7 @@ class SpecialRandomInCategory extends FormSpecialPage {
         * @throws MWException If category has no entries.
         */
        protected function getMinAndMaxForCat( Title $category ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->selectRow(
                        'categorylinks',
                        [
@@ -294,7 +294,7 @@ class SpecialRandomInCategory extends FormSpecialPage {
         * @return array Info for the title selected.
         */
        private function selectRandomPageFromDB( $rand, $offset, $up, $fname = __METHOD__ ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $query = $this->getQueryInfo( $rand, $offset, $up );
                $res = $dbr->select(
index d7835d1..e3b567d 100644 (file)
@@ -159,7 +159,7 @@ class RandomPage extends SpecialPage {
        }
 
        private function selectRandomPageFromDB( $randstr, $fname = __METHOD__ ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $query = $this->getQueryInfo( $randstr );
                $res = $dbr->select(
index 31a290d..0df8423 100644 (file)
@@ -28,7 +28,7 @@ class SpecialRandomrootpage extends RandomPage {
 
        public function __construct() {
                parent::__construct( 'Randomrootpage' );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $this->extra[] = 'page_title NOT ' . $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString() );
        }
 
index d4fb72c..8aff690 100644 (file)
@@ -281,7 +281,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
        }
 
        protected function getDB() {
-               return wfGetDB( DB_SLAVE, 'recentchanges' );
+               return wfGetDB( DB_REPLICA, 'recentchanges' );
        }
 
        public function outputFeedLinks() {
index 57a3d92..aab0f6d 100644 (file)
@@ -74,7 +74,7 @@ class SpecialRecentChangesLinked extends SpecialRecentChanges {
                 * expects only one result set so we use UNION instead.
                 */
 
-               $dbr = wfGetDB( DB_SLAVE, 'recentchangeslinked' );
+               $dbr = wfGetDB( DB_REPLICA, 'recentchangeslinked' );
                $id = $title->getArticleID();
                $ns = $title->getNamespace();
                $dbkey = $title->getDBkey();
index 80dc797..1d1df6a 100644 (file)
@@ -182,7 +182,7 @@ class SpecialRedirect extends FormSpecialPage {
                        'log_user_text',
                ];
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Gets the nested SQL statement which
                // returns timestamp of the log with the given log ID
index a475f05..3e89686 100644 (file)
@@ -25,7 +25,7 @@
  * Let users reset tokens like the watchlist token.
  *
  * @ingroup SpecialPage
- * @deprecated 1.26
+ * @deprecated since 1.26
  */
 class SpecialResetTokens extends FormSpecialPage {
        private $tokensList;
index ce5533f..e1e2049 100644 (file)
@@ -40,7 +40,6 @@ class SpecialRunJobs extends UnlistedSpecialPage {
 
        public function execute( $par = '' ) {
                $this->getOutput()->disable();
-
                if ( wfReadOnly() ) {
                        // HTTP 423 Locked
                        HttpStatus::header( 423 );
index aed2fa6..26b86f9 100644 (file)
@@ -100,6 +100,25 @@ class SpecialSearch extends SpecialPage {
         * @param string $par
         */
        public function execute( $par ) {
+               $request = $this->getRequest();
+
+               // Fetch the search term
+               $search = str_replace( "\n", " ", $request->getText( 'search' ) );
+
+               // Historically search terms have been accepted not only in the search query
+               // parameter, but also as part of the primary url. This can have PII implications
+               // in releasing page view data. As such issue a 301 redirect to the correct
+               // URL.
+               if ( strlen( $par ) && !strlen( $search ) ) {
+                       $query = $request->getValues();
+                       unset( $query['title'] );
+                       // Strip underscores from title parameter; most of the time we'll want
+                       // text form here. But don't strip underscores from actual text params!
+                       $query['search'] = str_replace( '_', ' ', $par );
+                       $this->getOutput()->redirect( $this->getPageTitle()->getFullURL( $query ), 301 );
+                       return;
+               }
+
                $this->setHeaders();
                $this->outputHeader();
                $out = $this->getOutput();
@@ -110,15 +129,6 @@ class SpecialSearch extends SpecialPage {
                ] );
                $this->addHelpLink( 'Help:Searching' );
 
-               // Strip underscores from title parameter; most of the time we'll want
-               // text form here. But don't strip underscores from actual text params!
-               $titleParam = str_replace( '_', ' ', $par );
-
-               $request = $this->getRequest();
-
-               // Fetch the search term
-               $search = str_replace( "\n", " ", $request->getText( 'search', $titleParam ) );
-
                $this->load();
                if ( !is_null( $request->getVal( 'nsRemember' ) ) ) {
                        $this->saveNamespaces();
@@ -386,7 +396,7 @@ class SpecialSearch extends SpecialPage {
                if ( $textMatches && !$textStatus ) {
                        // output appropriate heading
                        if ( $numTextMatches > 0 && $numTitleMatches > 0 ) {
-                               $out->addHTML( '<div class="visualClear"></div>' );
+                               $out->addHTML( '<div class="mw-search-visualclear"></div>' );
                                // if no title matches the heading is redundant
                                $out->wrapWikiMsg( "==$1==\n", 'textmatches' );
                        }
@@ -435,7 +445,7 @@ class SpecialSearch extends SpecialPage {
                        $textMatches->free();
                }
 
-               $out->addHTML( '<div class="visualClear"></div>' );
+               $out->addHTML( '<div class="mw-search-visualclear"></div>' );
 
                if ( $prevnext ) {
                        $out->addHTML( "<p class='mw-search-pager-bottom'>{$prevnext}</p>\n" );
@@ -456,7 +466,7 @@ class SpecialSearch extends SpecialPage {
        protected function interwikiHeader( $interwiki, $interwikiResult ) {
                // TODO: we need to figure out how to name wikis correctly
                $wikiMsg = $this->msg( 'search-interwiki-results-' . $interwiki )->parse();
-               return "<p class=\"mw-search-interwiki-header\">\n$wikiMsg</p>";
+               return "<p class=\"mw-search-interwiki-header mw-search-visualclear\">\n$wikiMsg</p>";
        }
 
        /**
@@ -1166,7 +1176,7 @@ class SpecialSearch extends SpecialPage {
         * @return string
         */
        protected function searchProfileTabs( $term ) {
-               $out = Html::element( 'div', [ 'class' => 'visualClear' ] ) .
+               $out = Html::element( 'div', [ 'class' => 'mw-search-visualclear' ] ) .
                        Xml::openElement( 'div', [ 'class' => 'mw-search-profile-tabs' ] );
 
                $bareterm = $term;
@@ -1244,6 +1254,7 @@ class SpecialSearch extends SpecialPage {
                        'autofocus' => trim( $term ) === '',
                        'value' => $term,
                        'dataLocation' => 'content',
+                       'infusable' => true,
                ] );
 
                $layout = new OOUI\ActionFieldLayout( $searchWidget, new OOUI\ButtonInputWidget( [
index 20f2fc8..a76b511 100644 (file)
@@ -104,19 +104,20 @@ class ShortPagesPage extends QueryPage {
                                Linker::getInvalidTitleDescription( $this->getContext(), $result->namespace, $result->title ) );
                }
 
-               $hlink = Linker::linkKnown(
+               $linkRenderer = $this->getLinkRenderer();
+               $hlink = $linkRenderer->makeKnownLink(
                        $title,
-                       $this->msg( 'hist' )->escaped(),
+                       $this->msg( 'hist' )->text(),
                        [],
                        [ 'action' => 'history' ]
                );
                $hlinkInParentheses = $this->msg( 'parentheses' )->rawParams( $hlink )->escaped();
 
                if ( $this->isCached() ) {
-                       $plink = Linker::link( $title );
+                       $plink = $linkRenderer->makeLink( $title );
                        $exists = $title->exists();
                } else {
-                       $plink = Linker::linkKnown( $title );
+                       $plink = $linkRenderer->makeKnownLink( $title );
                        $exists = true;
                }
 
index 2139949..5e5ed25 100644 (file)
@@ -110,7 +110,7 @@ class SpecialTags extends SpecialPage {
                        // continuing with this, as the user is just going to end up getting sent
                        // somewhere else. Additionally, if we keep going here, we end up
                        // populating the memcache of tag data (see ChangeTags::listDefinedTags)
-                       // with out-of-date data from the slave, because the slave hasn't caught
+                       // with out-of-date data from the replica DB, because the replica DB hasn't caught
                        // up to the fact that a new tag has been created as part of an implicit,
                        // as yet uncommitted transaction on master.
                        if ( $out->getRedirect() !== '' ) {
@@ -128,8 +128,7 @@ class SpecialTags extends SpecialPage {
                        ChangeTags::listExtensionDefinedTags(), true );
 
                // List all defined tags, even if they were never applied
-               $definedTags = array_keys( array_merge(
-                       $this->explicitlyDefinedTags, $this->extensionDefinedTags ) );
+               $definedTags = array_keys( $this->explicitlyDefinedTags + $this->extensionDefinedTags );
 
                // Show header only if there exists atleast one tag
                if ( !$tagStats && !$definedTags ) {
@@ -143,7 +142,7 @@ class SpecialTags extends SpecialPage {
                        Xml::tags( 'th', null, $this->msg( 'tags-source-header' )->parse() ) .
                        Xml::tags( 'th', null, $this->msg( 'tags-active-header' )->parse() ) .
                        Xml::tags( 'th', null, $this->msg( 'tags-hitcount-header' )->parse() ) .
-                       ( $userCanManage ?
+                       ( ( $userCanManage || $userCanDelete ) ?
                                Xml::tags( 'th', [ 'class' => 'unsortable' ],
                                        $this->msg( 'tags-actions-header' )->parse() ) :
                                '' )
@@ -176,12 +175,13 @@ class SpecialTags extends SpecialPage {
                $newRow = '';
                $newRow .= Xml::tags( 'td', null, Xml::element( 'code', null, $tag ) );
 
+               $linkRenderer = $this->getLinkRenderer();
                $disp = ChangeTags::tagDescription( $tag );
                if ( $showEditLinks ) {
                        $disp .= ' ';
-                       $editLink = Linker::link(
+                       $editLink = $linkRenderer->makeLink(
                                $this->msg( "tag-$tag" )->inContentLanguage()->getTitle(),
-                               $this->msg( 'tags-edit' )->escaped()
+                               $this->msg( 'tags-edit' )->text()
                        );
                        $disp .= $this->msg( 'parentheses' )->rawParams( $editLink )->escaped();
                }
@@ -191,9 +191,9 @@ class SpecialTags extends SpecialPage {
                $desc = !$msg->exists() ? '' : $msg->parse();
                if ( $showEditLinks ) {
                        $desc .= ' ';
-                       $editDescLink = Linker::link(
+                       $editDescLink = $linkRenderer->makeLink(
                                $this->msg( "tag-$tag-description" )->inContentLanguage()->getTitle(),
-                               $this->msg( 'tags-edit' )->escaped()
+                               $this->msg( 'tags-edit' )->text()
                        );
                        $desc .= $this->msg( 'parentheses' )->rawParams( $editDescLink )->escaped();
                }
@@ -217,14 +217,16 @@ class SpecialTags extends SpecialPage {
                $activeMsg = ( $isActive ? 'tags-active-yes' : 'tags-active-no' );
                $newRow .= Xml::tags( 'td', null, $this->msg( $activeMsg )->escaped() );
 
-               $hitcountLabel = $this->msg( 'tags-hitcount' )->numParams( $hitcount )->escaped();
+               $hitcountLabelMsg = $this->msg( 'tags-hitcount' )->numParams( $hitcount );
                if ( $this->getConfig()->get( 'UseTagFilter' ) ) {
-                       $hitcountLabel = Linker::link(
+                       $hitcountLabel = $linkRenderer->makeLink(
                                SpecialPage::getTitleFor( 'Recentchanges' ),
-                               $hitcountLabel,
+                               $hitcountLabelMsg->text(),
                                [],
                                [ 'tagfilter' => $tag ]
                        );
+               } else {
+                       $hitcountLabel = $hitcountLabelMsg->escaped();
                }
 
                // add raw $hitcount for sorting, because tags-hitcount contains numbers and letters
@@ -235,8 +237,9 @@ class SpecialTags extends SpecialPage {
 
                // delete
                if ( $showDeleteActions && ChangeTags::canDeleteTag( $tag )->isOK() ) {
-                       $actionLinks[] = Linker::linkKnown( $this->getPageTitle( 'delete' ),
-                               $this->msg( 'tags-delete' )->escaped(),
+                       $actionLinks[] = $linkRenderer->makeKnownLink(
+                               $this->getPageTitle( 'delete' ),
+                               $this->msg( 'tags-delete' )->text(),
                                [],
                                [ 'tag' => $tag ] );
                }
@@ -245,23 +248,25 @@ class SpecialTags extends SpecialPage {
 
                        // activate
                        if ( ChangeTags::canActivateTag( $tag )->isOK() ) {
-                               $actionLinks[] = Linker::linkKnown( $this->getPageTitle( 'activate' ),
-                                       $this->msg( 'tags-activate' )->escaped(),
+                               $actionLinks[] = $linkRenderer->makeKnownLink(
+                                       $this->getPageTitle( 'activate' ),
+                                       $this->msg( 'tags-activate' )->text(),
                                        [],
                                        [ 'tag' => $tag ] );
                        }
 
                        // deactivate
                        if ( ChangeTags::canDeactivateTag( $tag )->isOK() ) {
-                               $actionLinks[] = Linker::linkKnown( $this->getPageTitle( 'deactivate' ),
-                                       $this->msg( 'tags-deactivate' )->escaped(),
+                               $actionLinks[] = $linkRenderer->makeKnownLink(
+                                       $this->getPageTitle( 'deactivate' ),
+                                       $this->msg( 'tags-deactivate' )->text(),
                                        [],
                                        [ 'tag' => $tag ] );
                        }
 
                }
 
-               if ( $actionLinks ) {
+               if ( $showDeleteActions || $showManageActions ) {
                        $newRow .= Xml::tags( 'td', null, $this->getLanguage()->pipeList( $actionLinks ) );
                }
 
index f2eb88d..4c892b2 100644 (file)
@@ -53,6 +53,7 @@ class SpecialTrackingCategories extends SpecialPage {
                'node-count-exceeded-category',
                'expansion-depth-exceeded-category',
                'restricted-displaytitle-ignored',
+               'deprecated-self-close-category',
        ];
 
        function execute( $par ) {
index 5d230c0..91753a9 100644 (file)
@@ -63,7 +63,7 @@ class PageArchive {
         * @return ResultWrapper
         */
        public static function listAllPages() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                return self::listPages( $dbr, '' );
        }
@@ -77,7 +77,7 @@ class PageArchive {
         * @return ResultWrapper
         */
        public static function listPagesByPrefix( $prefix ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $title = Title::newFromText( $prefix );
                if ( $title ) {
@@ -127,7 +127,7 @@ class PageArchive {
         * @return ResultWrapper
         */
        function listRevisions() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $tables = [ 'archive' ];
 
@@ -179,7 +179,7 @@ class PageArchive {
                        return null;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                return $dbr->select(
                        'filearchive',
                        ArchivedFile::selectFields(),
@@ -197,7 +197,7 @@ class PageArchive {
         * @return Revision|null
         */
        function getRevision( $timestamp ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $fields = [
                        'ar_rev_id',
@@ -244,7 +244,7 @@ class PageArchive {
         * @return Revision|null Null when there is no previous revision
         */
        function getPreviousRevision( $timestamp ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Check the previous deleted revision...
                $row = $dbr->selectRow( 'archive',
@@ -300,7 +300,7 @@ class PageArchive {
                }
 
                // New-style: keyed to the text storage backend.
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $text = $dbr->selectRow( 'text',
                        [ 'old_text', 'old_flags' ],
                        [ 'old_id' => $row->ar_text_id ],
@@ -318,7 +318,7 @@ class PageArchive {
         * @return string|null
         */
        function getLastRevisionText() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $row = $dbr->selectRow( 'archive',
                        [ 'ar_text', 'ar_flags', 'ar_text_id' ],
                        [ 'ar_namespace' => $this->title->getNamespace(),
@@ -339,7 +339,7 @@ class PageArchive {
         * @return bool
         */
        function isDeleted() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $n = $dbr->selectField( 'archive', 'COUNT(ar_title)',
                        [ 'ar_namespace' => $this->title->getNamespace(),
                                'ar_title' => $this->title->getDBkey() ],
@@ -354,6 +354,9 @@ class PageArchive {
         * Once restored, the items will be removed from the archive tables.
         * The deletion log will be updated with an undeletion notice.
         *
+        * This also sets Status objects, $this->fileStatus and $this->revisionStatus
+        * (depending what operations are attempted).
+        *
         * @param array $timestamps Pass an empty array to restore all revisions,
         *   otherwise list the ones to undelete.
         * @param string $comment
@@ -439,9 +442,8 @@ class PageArchive {
        }
 
        /**
-        * This is the meaty bit -- restores archived revisions of the given page
-        * to the cur/old tables. If the page currently exists, all revisions will
-        * be stuffed into old, otherwise the most recent will go into cur.
+        * This is the meaty bit -- It restores archived revisions of the given page
+        * to the revision table.
         *
         * @param array $timestamps Pass an empty array to restore all revisions,
         *   otherwise list the ones to undelete.
@@ -455,8 +457,10 @@ class PageArchive {
                        throw new ReadOnlyError();
                }
 
-               $restoreAll = empty( $timestamps );
                $dbw = wfGetDB( DB_MASTER );
+               $dbw->startAtomic( __METHOD__ );
+
+               $restoreAll = empty( $timestamps );
 
                # Does this page already exist? We'll have to update it...
                $article = WikiPage::factory( $this->title );
@@ -477,11 +481,9 @@ class PageArchive {
                        # Page already exists. Import the history, and if necessary
                        # we'll update the latest revision field in the record.
 
-                       $previousRevId = $page->page_latest;
-
                        # Get the time span of this page
                        $previousTimestamp = $dbw->selectField( 'revision', 'rev_timestamp',
-                               [ 'rev_id' => $previousRevId ],
+                               [ 'rev_id' => $page->page_latest ],
                                __METHOD__ );
 
                        if ( $previousTimestamp === false ) {
@@ -489,13 +491,13 @@ class PageArchive {
 
                                $status = Status::newGood( 0 );
                                $status->warning( 'undeleterevision-missing' );
+                               $dbw->endAtomic( __METHOD__ );
 
                                return $status;
                        }
                } else {
                        # Have to create a new article...
                        $makepage = true;
-                       $previousRevId = 0;
                        $previousTimestamp = 0;
                }
 
@@ -508,7 +510,9 @@ class PageArchive {
                }
 
                $fields = [
+                       'ar_id',
                        'ar_rev_id',
+                       'rev_id',
                        'ar_text',
                        'ar_comment',
                        'ar_user',
@@ -531,11 +535,14 @@ class PageArchive {
                /**
                 * Select each archived revision...
                 */
-               $result = $dbw->select( 'archive',
+               $result = $dbw->select(
+                       [ 'archive', 'revision' ],
                        $fields,
                        $oldWhere,
                        __METHOD__,
-                       /* options */ [ 'ORDER BY' => 'ar_timestamp' ]
+                       /* options */
+                       [ 'ORDER BY' => 'ar_timestamp' ],
+                       [ 'revision' => [ 'LEFT JOIN', 'ar_rev_id=rev_id' ] ]
                );
 
                $rev_count = $result->numRows();
@@ -544,116 +551,182 @@ class PageArchive {
 
                        $status = Status::newGood( 0 );
                        $status->warning( "undelete-no-results" );
+                       $dbw->endAtomic( __METHOD__ );
 
                        return $status;
                }
 
-               $result->seek( $rev_count - 1 ); // move to last
-               $row = $result->fetchObject(); // get newest archived rev
-               $oldPageId = (int)$row->ar_page_id; // pass this to ArticleUndelete hook
-               $result->seek( 0 ); // move back
+               // We use ar_id because there can be duplicate ar_rev_id even for the same
+               // page.  In this case, we may be able to restore the first one.
+               $restoreFailedArIds = [];
 
-               // grab the content to check consistency with global state before restoring the page.
-               $revision = Revision::newFromArchiveRow( $row,
-                       [
-                               'title' => $article->getTitle(), // used to derive default content model
-                       ]
-               );
-               $user = User::newFromName( $revision->getUserText( Revision::RAW ), false );
-               $content = $revision->getContent( Revision::RAW );
+               // Map rev_id to the ar_id that is allowed to use it.  When checking later,
+               // if it doesn't match, the current ar_id can not be restored.
 
-               // NOTE: article ID may not be known yet. prepareSave() should not modify the database.
-               $status = $content->prepareSave( $article, 0, -1, $user );
+               // Value can be an ar_id or -1 (-1 means no ar_id can use it, since the
+               // rev_id is taken before we even start the restore).
+               $allowedRevIdToArIdMap = [];
 
-               if ( !$status->isOK() ) {
-                       return $status;
-               }
+               $latestRestorableRow = null;
 
-               if ( $makepage ) {
-                       // Check the state of the newest to-be version...
-                       if ( !$unsuppress && ( $row->ar_deleted & Revision::DELETED_TEXT ) ) {
-                               return Status::newFatal( "undeleterevdel" );
+               foreach ( $result as $row ) {
+                       if ( $row->ar_rev_id ) {
+                               // rev_id is taken even before we start restoring.
+                               if ( $row->ar_rev_id === $row->rev_id ) {
+                                       $restoreFailedArIds[] = $row->ar_id;
+                                       $allowedRevIdToArIdMap[$row->ar_rev_id] = -1;
+                               } else {
+                                       // rev_id is not taken yet in the DB, but it might be taken
+                                       // by a prior revision in the same restore operation. If
+                                       // not, we need to reserve it.
+                                       if ( isset( $allowedRevIdToArIdMap[$row->ar_rev_id] ) ) {
+                                               $restoreFailedArIds[] = $row->ar_id;
+                                       } else {
+                                               $allowedRevIdToArIdMap[$row->ar_rev_id] = $row->ar_id;
+                                               $latestRestorableRow = $row;
+                                       }
+                               }
+                       } else {
+                               // If ar_rev_id is null, there can't be a collision, and a
+                               // rev_id will be chosen automatically.
+                               $latestRestorableRow = $row;
                        }
-                       // Safe to insert now...
-                       $newid = $article->insertOn( $dbw, $row->ar_page_id );
-                       if ( $newid === false ) {
-                               // The old ID is reserved; let's pick another
-                               $newid = $article->insertOn( $dbw );
+               }
+
+               $result->seek( 0 ); // move back
+
+               $oldPageId = 0;
+               if ( $latestRestorableRow !== null ) {
+                       $oldPageId = (int)$latestRestorableRow->ar_page_id; // pass this to ArticleUndelete hook
+
+                       // grab the content to check consistency with global state before restoring the page.
+                       $revision = Revision::newFromArchiveRow( $latestRestorableRow,
+                               [
+                                       'title' => $article->getTitle(), // used to derive default content model
+                               ]
+                       );
+                       $user = User::newFromName( $revision->getUserText( Revision::RAW ), false );
+                       $content = $revision->getContent( Revision::RAW );
+
+                       // NOTE: article ID may not be known yet. prepareSave() should not modify the database.
+                       $status = $content->prepareSave( $article, 0, -1, $user );
+                       if ( !$status->isOK() ) {
+                               $dbw->endAtomic( __METHOD__ );
+
+                               return $status;
                        }
-                       $pageId = $newid;
+               }
+
+               $newid = false; // newly created page ID
+               $restored = 0; // number of revisions restored
+               /** @var Revision $revision */
+               $revision = null;
+
+               // If there are no restorable revisions, we can skip most of the steps.
+               if ( $latestRestorableRow === null ) {
+                       $failedRevisionCount = $rev_count;
                } else {
-                       // Check if a deleted revision will become the current revision...
-                       if ( $row->ar_timestamp > $previousTimestamp ) {
+                       if ( $makepage ) {
                                // Check the state of the newest to-be version...
-                               if ( !$unsuppress && ( $row->ar_deleted & Revision::DELETED_TEXT ) ) {
+                               if ( !$unsuppress
+                                       && ( $latestRestorableRow->ar_deleted & Revision::DELETED_TEXT )
+                               ) {
+                                       $dbw->endAtomic( __METHOD__ );
+
                                        return Status::newFatal( "undeleterevdel" );
                                }
+                               // Safe to insert now...
+                               $newid = $article->insertOn( $dbw, $latestRestorableRow->ar_page_id );
+                               if ( $newid === false ) {
+                                       // The old ID is reserved; let's pick another
+                                       $newid = $article->insertOn( $dbw );
+                               }
+                               $pageId = $newid;
+                       } else {
+                               // Check if a deleted revision will become the current revision...
+                               if ( $latestRestorableRow->ar_timestamp > $previousTimestamp ) {
+                                       // Check the state of the newest to-be version...
+                                       if ( !$unsuppress
+                                               && ( $latestRestorableRow->ar_deleted & Revision::DELETED_TEXT )
+                                       ) {
+                                               $dbw->endAtomic( __METHOD__ );
+
+                                               return Status::newFatal( "undeleterevdel" );
+                                       }
+                               }
+
+                               $newid = false;
+                               $pageId = $article->getId();
                        }
 
-                       $newid = false;
-                       $pageId = $article->getId();
-               }
+                       foreach ( $result as $row ) {
+                               // Check for key dupes due to needed archive integrity.
+                               if ( $row->ar_rev_id && $allowedRevIdToArIdMap[$row->ar_rev_id] !== $row->ar_id ) {
+                                       continue;
+                               }
+                               // Insert one revision at a time...maintaining deletion status
+                               // unless we are specifically removing all restrictions...
+                               $revision = Revision::newFromArchiveRow( $row,
+                                       [
+                                               'page' => $pageId,
+                                               'title' => $this->title,
+                                               'deleted' => $unsuppress ? 0 : $row->ar_deleted
+                                       ] );
 
-               $revision = null;
-               $restored = 0;
+                               $revision->insertOn( $dbw );
+                               $restored++;
 
-               foreach ( $result as $row ) {
-                       // Check for key dupes due to needed archive integrity.
-                       if ( $row->ar_rev_id ) {
-                               $exists = $dbw->selectField( 'revision', '1',
-                                       [ 'rev_id' => $row->ar_rev_id ], __METHOD__ );
-                               if ( $exists ) {
-                                       continue; // don't throw DB errors
-                               }
+                               Hooks::run( 'ArticleRevisionUndeleted',
+                                       [ &$this->title, $revision, $row->ar_page_id ] );
                        }
-                       // Insert one revision at a time...maintaining deletion status
-                       // unless we are specifically removing all restrictions...
-                       $revision = Revision::newFromArchiveRow( $row,
-                               [
-                                       'page' => $pageId,
-                                       'title' => $this->title,
-                                       'deleted' => $unsuppress ? 0 : $row->ar_deleted
-                               ] );
-
-                       $revision->insertOn( $dbw );
-                       $restored++;
 
-                       Hooks::run( 'ArticleRevisionUndeleted', [ &$this->title, $revision, $row->ar_page_id ] );
-               }
-               # Now that it's safely stored, take it out of the archive
-               $dbw->delete( 'archive',
-                       $oldWhere,
-                       __METHOD__ );
+                       // Now that it's safely stored, take it out of the archive
+                       // Don't delete rows that we failed to restore
+                       $toDeleteConds = $oldWhere;
+                       $failedRevisionCount = count( $restoreFailedArIds );
+                       if ( $failedRevisionCount > 0 ) {
+                               $toDeleteConds[] = 'ar_id NOT IN ( ' . $dbw->makeList( $restoreFailedArIds ) . ' )';
+                       }
 
-               // Was anything restored at all?
-               if ( $restored == 0 ) {
-                       return Status::newGood( 0 );
+                       $dbw->delete( 'archive',
+                               $toDeleteConds,
+                               __METHOD__ );
                }
 
-               $created = (bool)$newid;
+               $status = Status::newGood( $restored );
 
-               // Attach the latest revision to the page...
-               $wasnew = $article->updateIfNewerOn( $dbw, $revision, $previousRevId );
-               if ( $created || $wasnew ) {
-                       // Update site stats, link tables, etc
-                       $article->doEditUpdates(
-                               $revision,
-                               User::newFromName( $revision->getUserText( Revision::RAW ), false ),
-                               [
-                                       'created' => $created,
-                                       'oldcountable' => $oldcountable,
-                                       'restored' => true
-                               ]
-                       );
+               if ( $failedRevisionCount > 0 ) {
+                       $status->warning(
+                               wfMessage( 'undeleterevision-duplicate-revid', $failedRevisionCount ) );
                }
 
-               Hooks::run( 'ArticleUndelete', [ &$this->title, $created, $comment, $oldPageId ] );
+               // Was anything restored at all?
+               if ( $restored ) {
+                       $created = (bool)$newid;
+                       // Attach the latest revision to the page...
+                       $wasnew = $article->updateIfNewerOn( $dbw, $revision );
+                       if ( $created || $wasnew ) {
+                               // Update site stats, link tables, etc
+                               $article->doEditUpdates(
+                                       $revision,
+                                       User::newFromName( $revision->getUserText( Revision::RAW ), false ),
+                                       [
+                                               'created' => $created,
+                                               'oldcountable' => $oldcountable,
+                                               'restored' => true
+                                       ]
+                               );
+                       }
 
-               if ( $this->title->getNamespace() == NS_FILE ) {
-                       DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this->title, 'imagelinks' ) );
+                       Hooks::run( 'ArticleUndelete', [ &$this->title, $created, $comment, $oldPageId ] );
+                       if ( $this->title->getNamespace() == NS_FILE ) {
+                               DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this->title, 'imagelinks' ) );
+                       }
                }
 
-               return Status::newGood( $restored );
+               $dbw->endAtomic( __METHOD__ );
+
+               return $status;
        }
 
        /**
@@ -1174,7 +1247,7 @@ class SpecialUndelete extends SpecialPage {
 
                $minor = $rev->isMinor() ? ChangesList::flag( 'minor' ) : '';
 
-               $tags = wfGetDB( DB_SLAVE )->selectField(
+               $tags = wfGetDB( DB_REPLICA )->selectField(
                        'tag_summary',
                        'ts_tags',
                        [ 'ts_rev_id' => $rev->getId() ],
index 4b731cb..4583305 100644 (file)
@@ -22,6 +22,9 @@
  * @ingroup Upload
  */
 
+use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\MediaWikiServices;
+
 /**
  * Form for handling uploads and special page.
  *
@@ -261,7 +264,7 @@ class SpecialUpload extends SpecialPage {
                        'texttop' => $this->uploadFormTextTop,
                        'textaftersummary' => $this->uploadFormTextAfterSummary,
                        'destfile' => $this->mDesiredDestName,
-               ], $context );
+               ], $context, $this->getLinkRenderer() );
 
                # Check the token, but only if necessary
                if (
@@ -313,9 +316,9 @@ class SpecialUpload extends SpecialPage {
                if ( $title instanceof Title ) {
                        $count = $title->isDeleted();
                        if ( $count > 0 && $user->isAllowed( 'deletedhistory' ) ) {
-                               $restorelink = Linker::linkKnown(
+                               $restorelink = $this->getLinkRenderer()->makeKnownLink(
                                        SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedText() ),
-                                       $this->msg( 'restorelink' )->numParams( $count )->escaped()
+                                       $this->msg( 'restorelink' )->numParams( $count )->text()
                                );
                                $link = $this->msg( $user->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted' )
                                        ->rawParams( $restorelink )->parseAsBlock();
@@ -336,7 +339,13 @@ class SpecialUpload extends SpecialPage {
         * @param string $message HTML message to be passed to mainUploadForm
         */
        protected function showRecoverableUploadError( $message ) {
-               $sessionKey = $this->mUpload->stashSession();
+               $stashStatus = $this->mUpload->tryStashFile( $this->getUser() );
+               if ( $stashStatus->isGood() ) {
+                       $sessionKey = $stashStatus->getValue()->getFileKey();
+               } else {
+                       $sessionKey = null;
+                       // TODO Add a warning message about the failure to stash here?
+               }
                $message = '<h2>' . $this->msg( 'uploaderror' )->escaped() . "</h2>\n" .
                        '<div class="error">' . $message . "</div>\n";
 
@@ -365,11 +374,18 @@ class SpecialUpload extends SpecialPage {
                        return false;
                }
 
-               $sessionKey = $this->mUpload->stashSession();
+               $stashStatus = $this->mUpload->tryStashFile( $this->getUser() );
+               if ( $stashStatus->isGood() ) {
+                       $sessionKey = $stashStatus->getValue()->getFileKey();
+               } else {
+                       $sessionKey = null;
+                       // TODO Add a warning message about the failure to stash here?
+               }
 
                // Add styles for the warning, reused from the live preview
                $this->getOutput()->addModuleStyles( 'mediawiki.special.upload.styles' );
 
+               $linkRenderer = $this->getLinkRenderer();
                $warningHtml = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n"
                        . '<div class="mw-destfile-warning"><ul>';
                foreach ( $warnings as $warning => $args ) {
@@ -378,12 +394,24 @@ class SpecialUpload extends SpecialPage {
                        }
                        if ( $warning == 'exists' ) {
                                $msg = "\t<li>" . self::getExistsWarning( $args ) . "</li>\n";
+                       } elseif ( $warning == 'no-change' ) {
+                               $file = $args;
+                               $filename = $file->getTitle()->getPrefixedText();
+                               $msg = "\t<li>" . wfMessage( 'fileexists-no-change', $filename )->parse() . "</li>\n";
+                       } elseif ( $warning == 'duplicate-version' ) {
+                               $file = $args[0];
+                               $count = count( $args );
+                               $filename = $file->getTitle()->getPrefixedText();
+                               $message = wfMessage( 'fileexists-duplicate-version' )
+                                       ->params( $filename )
+                                       ->numParams( $count );
+                               $msg = "\t<li>" . $message->parse() . "</li>\n";
                        } elseif ( $warning == 'was-deleted' ) {
                                # If the file existed before and was deleted, warn the user of this
                                $ltitle = SpecialPage::getTitleFor( 'Log' );
-                               $llink = Linker::linkKnown(
+                               $llink = $linkRenderer->makeKnownLink(
                                        $ltitle,
-                                       wfMessage( 'deletionlog' )->escaped(),
+                                       wfMessage( 'deletionlog' )->text(),
                                        [],
                                        [
                                                'type' => 'delete',
@@ -535,7 +563,7 @@ class SpecialUpload extends SpecialPage {
                );
 
                if ( !$status->isGood() ) {
-                       $this->showUploadError( $this->getOutput()->parse( $status->getWikiText() ) );
+                       $this->showRecoverableUploadError( $this->getOutput()->parse( $status->getWikiText() ) );
 
                        return;
                }
@@ -747,31 +775,31 @@ class SpecialUpload extends SpecialPage {
 
                $file = $exists['file'];
                $filename = $file->getTitle()->getPrefixedText();
-               $warning = '';
+               $warnMsg = null;
 
                if ( $exists['warning'] == 'exists' ) {
                        // Exact match
-                       $warning = wfMessage( 'fileexists', $filename )->parse();
+                       $warnMsg = wfMessage( 'fileexists', $filename );
                } elseif ( $exists['warning'] == 'page-exists' ) {
                        // Page exists but file does not
-                       $warning = wfMessage( 'filepageexists', $filename )->parse();
+                       $warnMsg = wfMessage( 'filepageexists', $filename );
                } elseif ( $exists['warning'] == 'exists-normalized' ) {
-                       $warning = wfMessage( 'fileexists-extension', $filename,
-                               $exists['normalizedFile']->getTitle()->getPrefixedText() )->parse();
+                       $warnMsg = wfMessage( 'fileexists-extension', $filename,
+                               $exists['normalizedFile']->getTitle()->getPrefixedText() );
                } elseif ( $exists['warning'] == 'thumb' ) {
                        // Swapped argument order compared with other messages for backwards compatibility
-                       $warning = wfMessage( 'fileexists-thumbnail-yes',
-                               $exists['thumbFile']->getTitle()->getPrefixedText(), $filename )->parse();
+                       $warnMsg = wfMessage( 'fileexists-thumbnail-yes',
+                               $exists['thumbFile']->getTitle()->getPrefixedText(), $filename );
                } elseif ( $exists['warning'] == 'thumb-name' ) {
                        // Image w/o '180px-' does not exists, but we do not like these filenames
                        $name = $file->getName();
                        $badPart = substr( $name, 0, strpos( $name, '-' ) + 1 );
-                       $warning = wfMessage( 'file-thumbnail-no', $badPart )->parse();
+                       $warnMsg = wfMessage( 'file-thumbnail-no', $badPart );
                } elseif ( $exists['warning'] == 'bad-prefix' ) {
-                       $warning = wfMessage( 'filename-bad-prefix', $exists['prefix'] )->parse();
+                       $warnMsg = wfMessage( 'filename-bad-prefix', $exists['prefix'] );
                }
 
-               return $warning;
+               return $warnMsg ? $warnMsg->title( $file->getTitle() )->parse() : '';
        }
 
        /**
@@ -833,11 +861,17 @@ class UploadForm extends HTMLForm {
 
        protected $mMaxUploadSize = [];
 
-       public function __construct( array $options = [], IContextSource $context = null ) {
+       public function __construct( array $options = [], IContextSource $context = null,
+               LinkRenderer $linkRenderer = null
+       ) {
                if ( $context instanceof IContextSource ) {
                        $this->setContext( $context );
                }
 
+               if ( !$linkRenderer ) {
+                       $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
+               }
+
                $this->mWatch = !empty( $options['watch'] );
                $this->mForReUpload = !empty( $options['forreupload'] );
                $this->mSessionKey = isset( $options['sessionkey'] ) ? $options['sessionkey'] : '';
@@ -862,12 +896,12 @@ class UploadForm extends HTMLForm {
                Hooks::run( 'UploadFormInitDescriptor', [ &$descriptor ] );
                parent::__construct( $descriptor, $context, 'upload' );
 
-               # Add a link to edit MediaWik:Licenses
+               # Add a link to edit MediaWiki:Licenses
                if ( $this->getUser()->isAllowed( 'editinterface' ) ) {
-                       $this->getOutput()->addModuleStyles( 'mediawiki.special' );
-                       $licensesLink = Linker::linkKnown(
+                       $this->getOutput()->addModuleStyles( 'mediawiki.special.upload.styles' );
+                       $licensesLink = $linkRenderer->makeKnownLink(
                                $this->msg( 'licenses' )->inContentLanguage()->getTitle(),
-                               $this->msg( 'licenses-edit' )->escaped(),
+                               $this->msg( 'licenses-edit' )->text(),
                                [],
                                [ 'action' => 'edit' ]
                        );
index 555fe5c..9573f33 100644 (file)
@@ -181,7 +181,7 @@ class SpecialUploadStash extends UnlistedSpecialPage {
         * Scale a file (probably with a locally installed imagemagick, or similar)
         * and output it to STDOUT.
         * @param File $file
-        * @param array $params Scaling parameters ( e.g. array( width => '50' ) );
+        * @param array $params Scaling parameters ( e.g. [ width => '50' ] );
         * @param int $flags Scaling flags ( see File:: constants )
         * @throws MWException|UploadStashFileNotFoundException
         * @return bool Success
@@ -227,7 +227,7 @@ class SpecialUploadStash extends UnlistedSpecialPage {
         * client to cache it forever.
         *
         * @param File $file
-        * @param array $params Scaling parameters ( e.g. array( width => '50' ) );
+        * @param array $params Scaling parameters ( e.g. [ width => '50' ] );
         * @param int $flags Scaling flags ( see File:: constants )
         * @throws MWException
         * @return bool Success
@@ -391,17 +391,21 @@ class SpecialUploadStash extends UnlistedSpecialPage {
                if ( $files && count( $files ) ) {
                        sort( $files );
                        $fileListItemsHtml = '';
+                       $linkRenderer = $this->getLinkRenderer();
                        foreach ( $files as $file ) {
-                               $itemHtml = Linker::linkKnown( $this->getPageTitle( "file/$file" ), htmlspecialchars( $file ) );
+                               $itemHtml = $linkRenderer->makeKnownLink(
+                                       $this->getPageTitle( "file/$file" ),
+                                       $file
+                               );
                                try {
                                        $fileObj = $this->stash->getFile( $file );
                                        $thumb = $fileObj->generateThumbName( $file, [ 'width' => 220 ] );
                                        $itemHtml .=
                                                $this->msg( 'word-separator' )->escaped() .
                                                $this->msg( 'parentheses' )->rawParams(
-                                                       Linker::linkKnown(
+                                                       $linkRenderer->makeKnownLink(
                                                                $this->getPageTitle( "thumb/$file/$thumb" ),
-                                                               $this->msg( 'uploadstash-thumbnail' )->escaped()
+                                                               $this->msg( 'uploadstash-thumbnail' )->text()
                                                        )
                                                )->escaped();
                                } catch ( Exception $e ) {
index c04b239..253cd50 100644 (file)
@@ -23,7 +23,6 @@
 
 use MediaWiki\Auth\AuthManager;
 use MediaWiki\Logger\LoggerFactory;
-use Psr\Log\LogLevel;
 
 /**
  * Implements Special:UserLogin
@@ -125,7 +124,7 @@ class SpecialUserLogin extends LoginSignupSpecialPage {
 
                # Run any hooks; display injected HTML if any, else redirect
                $injected_html = '';
-               Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html ] );
+               Hooks::run( 'UserLoginComplete', [ &$user, &$injected_html, $direct ] );
 
                if ( $injected_html !== '' || $extraMessages ) {
                        $this->showSuccessPage( 'success', $this->msg( 'loginsuccesstitle' ),
@@ -154,7 +153,7 @@ class SpecialUserLogin extends LoginSignupSpecialPage {
        }
 
        protected function logAuthResult( $success, $status = null ) {
-               LoggerFactory::getInstance( 'authmanager' )->info( 'Login attempt', [
+               LoggerFactory::getInstance( 'authevents' )->info( 'Login attempt', [
                        'event' => 'login',
                        'successful' => $success,
                        'status' => $status,
index d5affc7..8a06abf 100644 (file)
  * @ingroup SpecialPage
  */
 class UserrightsPage extends SpecialPage {
-       # The target of the local right-adjuster's interest.  Can be gotten from
-       # either a GET parameter or a subpage-style parameter, so have a member
-       # variable for it.
+       /**
+        * The target of the local right-adjuster's interest.  Can be gotten from
+        * either a GET parameter or a subpage-style parameter, so have a member
+        * variable for it.
+        * @var null|string $mTarget
+        */
        protected $mTarget;
        /*
         * @var null|User $mFetchedUser The user object of the target username or null.
@@ -101,6 +104,10 @@ class UserrightsPage extends SpecialPage {
                        $this->mTarget = $request->getVal( 'user' );
                }
 
+               if ( is_string( $this->mTarget ) ) {
+                       $this->mTarget = trim( $this->mTarget );
+               }
+
                $available = $this->changeableGroups();
 
                if ( $this->mTarget === null ) {
@@ -147,9 +154,22 @@ class UserrightsPage extends SpecialPage {
 
                // show a successbox, if the user rights was saved successfully
                if ( $request->getCheck( 'success' ) && $this->mFetchedUser !== null ) {
-                       $out->wrapWikiMsg(
-                               "<div class=\"successbox\">\n$1\n</div>",
-                               [ 'savedrights', $this->mFetchedUser->getName() ]
+                       $out->addModules( [ 'mediawiki.special.userrights' ] );
+                       $out->addModuleStyles( 'mediawiki.notification.convertmessagebox.styles' );
+                       $out->addHtml(
+                               Html::rawElement(
+                                       'div',
+                                       [
+                                               'class' => 'mw-notify-success successbox',
+                                               'id' => 'mw-preferences-success',
+                                               'data-mw-autohide' => 'false',
+                                       ],
+                                       Html::element(
+                                               'p',
+                                               [],
+                                               $this->msg( 'savedrights', $this->mFetchedUser->getName() )->text()
+                                       )
+                               )
                        );
                }
 
index 6f71a51..c8b85ae 100644 (file)
@@ -209,7 +209,7 @@ class SpecialVersion extends SpecialPage {
         * @return string
         */
        public static function softwareInformation() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Put the software in an array of form 'name' => 'version'. All messages should
                // be loaded here, so feel free to use wfMessage in the 'name'. Raw HTML or
index 58cde7e..99f9c7c 100644 (file)
@@ -313,7 +313,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
         * @return IDatabase
         */
        protected function getDB() {
-               return wfGetDB( DB_SLAVE, 'watchlist' );
+               return wfGetDB( DB_REPLICA, 'watchlist' );
        }
 
        /**
@@ -343,7 +343,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                $user = $this->getUser();
                $output = $this->getOutput();
 
-               # Show a message about slave lag, if applicable
+               # Show a message about replica DB lag, if applicable
                $lag = wfGetLB()->safeGetLag( $dbr );
                if ( $lag > 0 ) {
                        $output->showLagWarning( $lag );
@@ -429,7 +429,10 @@ class SpecialWatchlist extends ChangesListSpecialPage {
 
                $out->addSubtitle(
                        $this->msg( 'watchlistfor2', $user->getName() )
-                               ->rawParams( SpecialEditWatchlist::buildTools( null ) )
+                               ->rawParams( SpecialEditWatchlist::buildTools(
+                                       $this->getLanguage(),
+                                       $this->getLinkRenderer()
+                               ) )
                );
 
                $this->setTopText( $opts );
index baa55f0..1ead290 100644 (file)
@@ -105,7 +105,7 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
         */
        function showIndirectLinks( $level, $target, $limit, $from = 0, $back = 0 ) {
                $out = $this->getOutput();
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $hidelinks = $this->opts->getValue( 'hidelinks' );
                $hideredirs = $this->opts->getValue( 'hideredirs' );
index 259d1f9..a1e5156 100644 (file)
@@ -97,7 +97,7 @@ class WithoutInterwikiPage extends PageQueryPage {
                        'join_conds' => [ 'langlinks' => [ 'LEFT JOIN', 'll_from = page_id' ] ]
                ];
                if ( $this->prefix ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $query['conds'][] = 'page_title ' . $dbr->buildLike( $this->prefix, $dbr->anyString() );
                }
 
index 60f642d..5609310 100644 (file)
@@ -184,7 +184,7 @@ class AllMessagesTablePager extends TablePager {
 
        /**
         * Determine which of the MediaWiki and MediaWiki_talk namespace pages exist.
-        * Returns array( 'pages' => ..., 'talks' => ... ), where the subarrays have
+        * Returns [ 'pages' => ..., 'talks' => ... ], where the subarrays have
         * an entry for each existing page, with the key being the message name and
         * value arbitrary.
         *
@@ -196,7 +196,7 @@ class AllMessagesTablePager extends TablePager {
        public static function getCustomisedStatuses( $messageNames, $langcode = 'en', $foreign = false ) {
                // FIXME: This function should be moved to Language:: or something.
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select( 'page',
                        [ 'page_namespace', 'page_title' ],
                        [ 'page_namespace' => [ NS_MEDIAWIKI, NS_MEDIAWIKI_TALK ] ],
index f4f2748..a145e45 100644 (file)
@@ -70,11 +70,11 @@ class ContribsPager extends ReverseChronologicalPager {
                $month = isset( $options['month'] ) ? $options['month'] : false;
                $this->getDateCond( $year, $month );
 
-               // Most of this code will use the 'contributions' group DB, which can map to slaves
+               // Most of this code will use the 'contributions' group DB, which can map to replica DBs
                // with extra user based indexes or partioning by user. The additional metadata
-               // queries should use a regular slave since the lookup pattern is not all by user.
-               $this->mDbSecondary = wfGetDB( DB_SLAVE ); // any random slave
-               $this->mDb = wfGetDB( DB_SLAVE, 'contributions' );
+               // queries should use a regular replica DB since the lookup pattern is not all by user.
+               $this->mDbSecondary = wfGetDB( DB_REPLICA ); // any random replica DB
+               $this->mDb = wfGetDB( DB_REPLICA, 'contributions' );
        }
 
        function getDefaultQuery() {
@@ -224,6 +224,11 @@ class ContribsPager extends ReverseChronologicalPager {
                                        ]
                                ];
                        }
+                       // (T140537) Disallow looking too far in the past for 'newbies' queries. If the user requested
+                       // a timestamp offset far in the past such that there are no edits by users with user_ids in
+                       // the range, we would end up scanning all revisions from that offset until start of time.
+                       $condition[] = 'rev_timestamp > ' .
+                               $this->mDb->addQuotes( $this->mDb->timestamp( wfTimestamp() - 30 * 24 * 60 * 60 ) );
                } else {
                        $uid = User::idFromName( $this->target );
                        if ( $uid ) {
@@ -371,8 +376,9 @@ class ContribsPager extends ReverseChronologicalPager {
                        # Mark current revisions
                        $topmarktext = '';
                        $user = $this->getUser();
-                       if ( $row->rev_id == $row->page_latest ) {
+                       if ( $row->rev_id === $row->page_latest ) {
                                $topmarktext .= '<span class="mw-uctop">' . $this->messages['uctop'] . '</span>';
+                               $classes[] = 'mw-contributions-current';
                                # Add rollback link
                                if ( !$row->page_is_new && $page->quickUserCan( 'rollback', $user )
                                        && $page->quickUserCan( 'edit', $user )
index f2421f8..1acbba1 100644 (file)
@@ -43,7 +43,7 @@ class DeletedContribsPager extends IndexPager {
                }
                $this->target = $target;
                $this->namespace = $namespace;
-               $this->mDb = wfGetDB( DB_SLAVE, 'contributions' );
+               $this->mDb = wfGetDB( DB_REPLICA, 'contributions' );
        }
 
        function getDefaultQuery() {
index 40706fa..7fc4a95 100644 (file)
@@ -74,7 +74,7 @@ class ImageListPager extends TablePager {
                        $nt = Title::newFromText( $this->mSearch );
 
                        if ( $nt ) {
-                               $dbr = wfGetDB( DB_SLAVE );
+                               $dbr = wfGetDB( DB_REPLICA );
                                $this->mQueryConds[] = 'LOWER(img_name)' .
                                        $dbr->buildLike( $dbr->anyString(),
                                                strtolower( $nt->getDBkey() ), $dbr->anyString() );
@@ -136,7 +136,7 @@ class ImageListPager extends TablePager {
                if ( $this->mSearch !== '' ) {
                        $nt = Title::newFromText( $this->mSearch );
                        if ( $nt ) {
-                               $dbr = wfGetDB( DB_SLAVE );
+                               $dbr = wfGetDB( DB_REPLICA );
                                $conds[] = 'LOWER(' . $prefix . '_name)' .
                                        $dbr->buildLike( $dbr->anyString(),
                                                strtolower( $nt->getDBkey() ), $dbr->anyString() );
@@ -272,7 +272,7 @@ class ImageListPager extends TablePager {
                        }
                        unset( $field );
 
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        if ( $dbr->implicitGroupby() ) {
                                $options = [ 'GROUP BY' => 'img_name' ];
                        } else {
@@ -429,8 +429,11 @@ class ImageListPager extends TablePager {
                                // If statement for paranoia
                                if ( $file ) {
                                        $thumb = $file->transform( [ 'width' => 180, 'height' => 360 ] );
-
-                                       return $thumb->toHtml( [ 'desc-link' => true ] );
+                                       if ( $thumb ) {
+                                               return $thumb->toHtml( [ 'desc-link' => true ] );
+                                       } else {
+                                               return wfMessage( 'thumbnail_error', '' )->escaped();
+                                       }
                                } else {
                                        return htmlspecialchars( $value );
                                }
index 0b9587c..56229b3 100644 (file)
@@ -36,7 +36,7 @@ class MergeHistoryPager extends ReverseChronologicalPager {
                $this->title = $source;
                $this->articleID = $source->getArticleID();
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $maxtimestamp = $dbr->selectField(
                        'revision',
                        'MIN(rev_timestamp)',
index ae57736..41819fc 100644 (file)
@@ -30,33 +30,30 @@ class NewFilesPager extends ReverseChronologicalPager {
        protected $gallery;
 
        /**
-        * @var bool
+        * @var FormOptions
         */
-       protected $showBots;
+       protected $opts;
 
        /**
-        * @var bool
+        * @param IContextSource $context
+        * @param FormOptions $opts
         */
-       protected $hidePatrolled;
-
-       function __construct( IContextSource $context, $par = null ) {
-               $this->like = $context->getRequest()->getText( 'like' );
-               $this->showBots = $context->getRequest()->getBool( 'showbots', 0 );
-               $this->hidePatrolled = $context->getRequest()->getBool( 'hidepatrolled', 0 );
-               if ( is_numeric( $par ) ) {
-                       $this->setLimit( $par );
-               }
+       function __construct( IContextSource $context, FormOptions $opts ) {
+               $this->opts = $opts;
+
+               $this->setLimit( $opts->getValue( 'limit' ) );
 
                parent::__construct( $context );
        }
 
        function getQueryInfo() {
+               $opts = $this->opts;
                $conds = $jconds = [];
                $tables = [ 'image' ];
                $fields = [ 'img_name', 'img_user', 'img_timestamp' ];
                $options = [];
 
-               if ( !$this->showBots ) {
+               if ( !$opts->getValue( 'showbots' ) ) {
                        $groupsWithBotPermission = User::getGroupsWithPermission( 'bot' );
 
                        if ( count( $groupsWithBotPermission ) ) {
@@ -72,7 +69,7 @@ class NewFilesPager extends ReverseChronologicalPager {
                        }
                }
 
-               if ( $this->hidePatrolled ) {
+               if ( $opts->getValue( 'hidepatrolled' ) ) {
                        $tables[] = 'recentchanges';
                        $conds['rc_type'] = RC_LOG;
                        $conds['rc_log_type'] = 'upload';
@@ -92,9 +89,10 @@ class NewFilesPager extends ReverseChronologicalPager {
                        $options[] = 'STRAIGHT_JOIN';
                }
 
-               if ( !$this->getConfig()->get( 'MiserMode' ) && $this->like !== null ) {
-                       $dbr = wfGetDB( DB_SLAVE );
-                       $likeObj = Title::newFromText( $this->like );
+               $likeVal = $opts->getValue( 'like' );
+               if ( !$this->getConfig()->get( 'MiserMode' ) && $likeVal !== '' ) {
+                       $dbr = wfGetDB( DB_REPLICA );
+                       $likeObj = Title::newFromText( $likeVal );
                        if ( $likeObj instanceof Title ) {
                                $like = $dbr->buildLike(
                                        $dbr->anyString(),
@@ -154,54 +152,4 @@ class NewFilesPager extends ReverseChronologicalPager {
                        . "</i><br />\n"
                );
        }
-
-       function getForm() {
-               $fields = [
-                       'like' => [
-                               'type' => 'text',
-                               'label-message' => 'newimages-label',
-                               'name' => 'like',
-                       ],
-                       'showbots' => [
-                               'type' => 'check',
-                               'label-message' => 'newimages-showbots',
-                               'name' => 'showbots',
-                       ],
-                       'hidepatrolled' => [
-                               'type' => 'check',
-                               'label-message' => 'newimages-hidepatrolled',
-                               'name' => 'hidepatrolled',
-                       ],
-                       'limit' => [
-                               'type' => 'hidden',
-                               'default' => $this->mLimit,
-                               'name' => 'limit',
-                       ],
-                       'offset' => [
-                               'type' => 'hidden',
-                               'default' => $this->getRequest()->getText( 'offset' ),
-                               'name' => 'offset',
-                       ],
-               ];
-
-               if ( $this->getConfig()->get( 'MiserMode' ) ) {
-                       unset( $fields['like'] );
-               }
-
-               if ( !$this->getUser()->useFilePatrol() ) {
-                       unset( $fields['hidepatrolled'] );
-               }
-
-               $context = new DerivativeContext( $this->getContext() );
-               $context->setTitle( $this->getTitle() ); // Remove subpage
-               $form = new HTMLForm( $fields, $context );
-
-               $form->setSubmitTextMsg( 'ilsubmit' );
-               $form->setSubmitProgressive();
-
-               $form->setMethod( 'get' );
-               $form->setWrapperLegendMsg( 'newimages-legend' );
-
-               return $form;
-       }
 }
index 7b058c1..901be38 100644 (file)
@@ -100,7 +100,7 @@ class UsersPager extends AlphabeticPager {
         * @return array
         */
        function getQueryInfo() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $conds = [];
 
                // Don't show hidden names
@@ -228,7 +228,7 @@ class UsersPager extends AlphabeticPager {
                }
 
                // Lookup groups for all the users
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $groupRes = $dbr->select(
                        'user_groups',
                        [ 'ug_user', 'ug_group' ],
@@ -241,6 +241,11 @@ class UsersPager extends AlphabeticPager {
                        $cache[intval( $row->ug_user )][] = $row->ug_group;
                        $groups[$row->ug_group] = true;
                }
+
+               // Give extensions a chance to add things like global user group data
+               // into the cache array to ensure proper output later on
+               Hooks::run( 'UsersPagerDoBatchLookups', [ $dbr, $userIds, &$cache, &$groups ] );
+
                $this->userGroupCache = $cache;
 
                // Add page of groups to link batch
diff --git a/includes/specials/pre-authmanager/README b/includes/specials/pre-authmanager/README
deleted file mode 100644 (file)
index 1cfdd5f..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-This directory temporarily hosts pre-AuthManager code as a way of feature-flagging.
-Class names are postfixed with 'PreAuthManager' and SpecialPageFactory adds/removes
-that postfix based on the feature flag.
-
-This is a horrible hack that will only be in place for a few weeks, to allow instant
-rollback while AuthManager is tested in WMF production and major problems are ironed
-out. In the past such issues have been handled via deployment branches, but that
-meant blocking the work of all WMF developers from being deployed. This is hoped
-to be a less disruptive method.
-
diff --git a/includes/specials/pre-authmanager/SpecialChangeEmail.php b/includes/specials/pre-authmanager/SpecialChangeEmail.php
deleted file mode 100644 (file)
index 7861562..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-<?php
-/**
- * Implements Special:ChangeEmail
- *
- * 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 SpecialPage
- */
-
-/**
- * Let users change their email address.
- *
- * @ingroup SpecialPage
- */
-class SpecialChangeEmailPreAuthManager extends FormSpecialPage {
-       /**
-        * @var Status
-        */
-       private $status;
-
-       public function __construct() {
-               parent::__construct( 'ChangeEmail', 'editmyprivateinfo' );
-       }
-
-       public function doesWrites() {
-               return true;
-       }
-
-       /**
-        * @return bool
-        */
-       public function isListed() {
-               global $wgAuth;
-
-               return $wgAuth->allowPropChange( 'emailaddress' );
-       }
-
-       /**
-        * Main execution point
-        * @param string $par
-        */
-       function execute( $par ) {
-               $out = $this->getOutput();
-               $out->disallowUserJs();
-
-               parent::execute( $par );
-       }
-
-       protected function checkExecutePermissions( User $user ) {
-               global $wgAuth;
-
-               if ( !$wgAuth->allowPropChange( 'emailaddress' ) ) {
-                       throw new ErrorPageError( 'changeemail', 'cannotchangeemail' );
-               }
-
-               $this->requireLogin( 'changeemail-no-info' );
-
-               // This could also let someone check the current email address, so
-               // require both permissions.
-               if ( !$this->getUser()->isAllowed( 'viewmyprivateinfo' ) ) {
-                       throw new PermissionsError( 'viewmyprivateinfo' );
-               }
-
-               parent::checkExecutePermissions( $user );
-       }
-
-       protected function getFormFields() {
-               $user = $this->getUser();
-
-               $fields = [
-                       'Name' => [
-                               'type' => 'info',
-                               'label-message' => 'username',
-                               'default' => $user->getName(),
-                       ],
-                       'OldEmail' => [
-                               'type' => 'info',
-                               'label-message' => 'changeemail-oldemail',
-                               'default' => $user->getEmail() ?: $this->msg( 'changeemail-none' )->text(),
-                       ],
-                       'NewEmail' => [
-                               'type' => 'email',
-                               'label-message' => 'changeemail-newemail',
-                               'autofocus' => true,
-                               'help-message' => 'changeemail-newemail-help',
-                       ],
-               ];
-
-               if ( $this->getConfig()->get( 'RequirePasswordforEmailChange' ) ) {
-                       $fields['Password'] = [
-                               'type' => 'password',
-                               'label-message' => 'changeemail-password'
-                       ];
-               }
-
-               return $fields;
-       }
-
-       protected function getDisplayFormat() {
-               return 'ooui';
-       }
-
-       protected function alterForm( HTMLForm $form ) {
-               $form->setId( 'mw-changeemail-form' );
-               $form->setTableId( 'mw-changeemail-table' );
-               $form->setSubmitTextMsg( 'changeemail-submit' );
-               $form->addHiddenFields( $this->getRequest()->getValues( 'returnto', 'returntoquery' ) );
-
-               $form->addHeaderText( $this->msg( 'changeemail-header' )->parseAsBlock() );
-               if ( $this->getConfig()->get( 'RequirePasswordforEmailChange' ) ) {
-                       $form->addHeaderText( $this->msg( 'changeemail-passwordrequired' )->parseAsBlock() );
-               }
-       }
-
-       public function onSubmit( array $data ) {
-               $password = isset( $data['Password'] ) ? $data['Password'] : null;
-               $status = $this->attemptChange( $this->getUser(), $password, $data['NewEmail'] );
-
-               $this->status = $status;
-
-               return $status;
-       }
-
-       public function onSuccess() {
-               $request = $this->getRequest();
-
-               $returnto = $request->getVal( 'returnto' );
-               $titleObj = $returnto !== null ? Title::newFromText( $returnto ) : null;
-               if ( !$titleObj instanceof Title ) {
-                       $titleObj = Title::newMainPage();
-               }
-               $query = $request->getVal( 'returntoquery' );
-
-               if ( $this->status->value === true ) {
-                       $this->getOutput()->redirect( $titleObj->getFullURL( $query ) );
-               } elseif ( $this->status->value === 'eauth' ) {
-                       # Notify user that a confirmation email has been sent...
-                       $this->getOutput()->wrapWikiMsg( "<div class='error' style='clear: both;'>\n$1\n</div>",
-                               'eauthentsent', $this->getUser()->getName() );
-                       // just show the link to go back
-                       $this->getOutput()->addReturnTo( $titleObj, wfCgiToArray( $query ) );
-               }
-       }
-
-       /**
-        * @param User $user
-        * @param string $pass
-        * @param string $newaddr
-        * @return Status
-        */
-       private function attemptChange( User $user, $pass, $newaddr ) {
-               global $wgAuth;
-
-               if ( $newaddr != '' && !Sanitizer::validateEmail( $newaddr ) ) {
-                       return Status::newFatal( 'invalidemailaddress' );
-               }
-
-               if ( $newaddr === $user->getEmail() ) {
-                       return Status::newFatal( 'changeemail-nochange' );
-               }
-
-               $throttleInfo = LoginForm::incrementLoginThrottle( $user->getName() );
-               if ( $throttleInfo ) {
-                       $lang = $this->getLanguage();
-                       return Status::newFatal(
-                               'changeemail-throttled',
-                               $lang->formatDuration( $throttleInfo['wait'] )
-                       );
-               }
-
-               if ( $this->getConfig()->get( 'RequirePasswordforEmailChange' )
-                       && !$user->checkTemporaryPassword( $pass )
-                       && !$user->checkPassword( $pass )
-               ) {
-                       return Status::newFatal( 'wrongpassword' );
-               }
-
-               LoginForm::clearLoginThrottle( $user->getName() );
-
-               $oldaddr = $user->getEmail();
-               $status = $user->setEmailWithConfirmation( $newaddr );
-               if ( !$status->isGood() ) {
-                       return $status;
-               }
-
-               Hooks::run( 'PrefsEmailAudit', [ $user, $oldaddr, $newaddr ] );
-
-               $user->saveSettings();
-
-               $wgAuth->updateExternalDB( $user );
-
-               return $status;
-       }
-
-       public function requiresUnblock() {
-               return false;
-       }
-
-       protected function getGroupName() {
-               return 'users';
-       }
-}
diff --git a/includes/specials/pre-authmanager/SpecialChangePassword.php b/includes/specials/pre-authmanager/SpecialChangePassword.php
deleted file mode 100644 (file)
index 3955fee..0000000
+++ /dev/null
@@ -1,343 +0,0 @@
-<?php
-/**
- * Implements Special:ChangePassword
- *
- * 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 SpecialPage
- */
-
-/**
- * Let users recover their password.
- *
- * @ingroup SpecialPage
- */
-class SpecialChangePasswordPreAuthManager extends FormSpecialPage {
-       protected $mUserName;
-       protected $mDomain;
-
-       // Optional Wikitext Message to show above the password change form
-       protected $mPreTextMessage = null;
-
-       // label for old password input
-       protected $mOldPassMsg = null;
-
-       public function __construct() {
-               parent::__construct( 'ChangePassword', 'editmyprivateinfo' );
-               $this->listed( false );
-       }
-
-       public function doesWrites() {
-               return true;
-       }
-
-       /**
-        * Main execution point
-        * @param string|null $par
-        */
-       function execute( $par ) {
-               $this->getOutput()->disallowUserJs();
-
-               parent::execute( $par );
-       }
-
-       protected function checkExecutePermissions( User $user ) {
-               parent::checkExecutePermissions( $user );
-
-               if ( !$this->getRequest()->wasPosted() ) {
-                       $this->requireLogin( 'resetpass-no-info' );
-               }
-       }
-
-       /**
-        * Set a message at the top of the Change Password form
-        * @since 1.23
-        * @param Message $msg Message to parse and add to the form header
-        */
-       public function setChangeMessage( Message $msg ) {
-               $this->mPreTextMessage = $msg;
-       }
-
-       /**
-        * Set a message at the top of the Change Password form
-        * @since 1.23
-        * @param string $msg Message label for old/temp password field
-        */
-       public function setOldPasswordMessage( $msg ) {
-               $this->mOldPassMsg = $msg;
-       }
-
-       protected function getFormFields() {
-               $user = $this->getUser();
-               $request = $this->getRequest();
-
-               $oldpassMsg = $this->mOldPassMsg;
-               if ( $oldpassMsg === null ) {
-                       $oldpassMsg = $user->isLoggedIn() ? 'oldpassword' : 'resetpass-temp-password';
-               }
-
-               $fields = [
-                       'Name' => [
-                               'type' => 'info',
-                               'label-message' => 'username',
-                               'default' => $request->getVal( 'wpName', $user->getName() ),
-                       ],
-                       'Password' => [
-                               'type' => 'password',
-                               'label-message' => $oldpassMsg,
-                       ],
-                       'NewPassword' => [
-                               'type' => 'password',
-                               'label-message' => 'newpassword',
-                       ],
-                       'Retype' => [
-                               'type' => 'password',
-                               'label-message' => 'retypenew',
-                       ],
-               ];
-
-               if ( !$this->getUser()->isLoggedIn() ) {
-                       $fields['LoginOnChangeToken'] = [
-                               'type' => 'hidden',
-                               'label' => 'Change Password Token',
-                               'default' => LoginForm::getLoginToken()->toString(),
-                       ];
-               }
-
-               $extraFields = [];
-               Hooks::run( 'ChangePasswordForm', [ &$extraFields ] );
-               foreach ( $extraFields as $extra ) {
-                       list( $name, $label, $type, $default ) = $extra;
-                       $fields[$name] = [
-                               'type' => $type,
-                               'name' => $name,
-                               'label-message' => $label,
-                               'default' => $default,
-                       ];
-               }
-
-               if ( !$user->isLoggedIn() ) {
-                       $fields['Remember'] = [
-                               'type' => 'check',
-                               'label' => $this->msg( 'remembermypassword' )
-                                               ->numParams(
-                                                       ceil( $this->getConfig()->get( 'CookieExpiration' ) / ( 3600 * 24 ) )
-                                               )->text(),
-                               'default' => $request->getVal( 'wpRemember' ),
-                       ];
-               }
-
-               return $fields;
-       }
-
-       protected function alterForm( HTMLForm $form ) {
-               $form->setId( 'mw-resetpass-form' );
-               $form->setTableId( 'mw-resetpass-table' );
-               $form->setWrapperLegendMsg( 'resetpass_header' );
-               $form->setSubmitTextMsg(
-                       $this->getUser()->isLoggedIn()
-                               ? 'resetpass-submit-loggedin'
-                               : 'resetpass_submit'
-               );
-               $form->addButton( [
-                       'name' => 'wpCancel',
-                       'value' => $this->msg( 'resetpass-submit-cancel' )->text()
-               ] );
-               $form->setHeaderText( $this->msg( 'resetpass_text' )->parseAsBlock() );
-               if ( $this->mPreTextMessage instanceof Message ) {
-                       $form->addPreText( $this->mPreTextMessage->parseAsBlock() );
-               }
-               $form->addHiddenFields(
-                       $this->getRequest()->getValues( 'wpName', 'wpDomain', 'returnto', 'returntoquery' ) );
-       }
-
-       public function onSubmit( array $data ) {
-               global $wgAuth;
-
-               $request = $this->getRequest();
-
-               if ( $request->getCheck( 'wpLoginToken' ) ) {
-                       // This comes from Special:Userlogin when logging in with a temporary password
-                       return false;
-               }
-
-               if ( !$this->getUser()->isLoggedIn()
-                       && !LoginForm::getLoginToken()->match( $request->getVal( 'wpLoginOnChangeToken' ) )
-               ) {
-                       // Potential CSRF (bug 62497)
-                       return false;
-               }
-
-               if ( $request->getCheck( 'wpCancel' ) ) {
-                       $returnto = $request->getVal( 'returnto' );
-                       $titleObj = $returnto !== null ? Title::newFromText( $returnto ) : null;
-                       if ( !$titleObj instanceof Title ) {
-                               $titleObj = Title::newMainPage();
-                       }
-                       $query = $request->getVal( 'returntoquery' );
-                       $this->getOutput()->redirect( $titleObj->getFullURL( $query ) );
-
-                       return true;
-               }
-
-               $this->mUserName = $request->getVal( 'wpName', $this->getUser()->getName() );
-               $this->mDomain = $wgAuth->getDomain();
-
-               if ( !$wgAuth->allowPasswordChange() ) {
-                       throw new ErrorPageError( 'changepassword', 'resetpass_forbidden' );
-               }
-
-               $status = $this->attemptReset( $data['Password'], $data['NewPassword'], $data['Retype'] );
-
-               return $status;
-       }
-
-       public function onSuccess() {
-               if ( $this->getUser()->isLoggedIn() ) {
-                       $this->getOutput()->wrapWikiMsg(
-                               "<div class=\"successbox\">\n$1\n</div>",
-                               'changepassword-success'
-                       );
-                       $this->getOutput()->returnToMain();
-               } else {
-                       $request = $this->getRequest();
-                       LoginForm::clearLoginToken();
-                       $token = LoginForm::getLoginToken()->toString();
-                       $data = [
-                               'action' => 'submitlogin',
-                               'wpName' => $this->mUserName,
-                               'wpDomain' => $this->mDomain,
-                               'wpLoginToken' => $token,
-                               'wpPassword' => $request->getVal( 'wpNewPassword' ),
-                       ] + $request->getValues( 'wpRemember', 'returnto', 'returntoquery' );
-                       $login = new LoginForm( new DerivativeRequest( $request, $data, true ) );
-                       $login->setContext( $this->getContext() );
-                       $login->execute( null );
-               }
-       }
-
-       /**
-        * Checks the new password if it meets the requirements for passwords and set
-        * it as a current password, otherwise set the passed Status object to fatal
-        * and doesn't change anything
-        *
-        * @param string $oldpass The current (temporary) password.
-        * @param string $newpass The password to set.
-        * @param string $retype The string of the retype password field to check with newpass
-        * @return Status
-        */
-       protected function attemptReset( $oldpass, $newpass, $retype ) {
-               $isSelf = ( $this->mUserName === $this->getUser()->getName() );
-               if ( $isSelf ) {
-                       $user = $this->getUser();
-               } else {
-                       $user = User::newFromName( $this->mUserName );
-               }
-
-               if ( !$user || $user->isAnon() ) {
-                       return Status::newFatal( $this->msg( 'nosuchusershort', $this->mUserName ) );
-               }
-
-               if ( $newpass !== $retype ) {
-                       Hooks::run( 'PrefsPasswordAudit', [ $user, $newpass, 'badretype' ] );
-                       return Status::newFatal( $this->msg( 'badretype' ) );
-               }
-
-               $throttleInfo = LoginForm::incrementLoginThrottle( $this->mUserName );
-               if ( $throttleInfo ) {
-                       return Status::newFatal( $this->msg( 'changepassword-throttled' )
-                               ->durationParams( $throttleInfo['wait'] )
-                       );
-               }
-
-               // @todo Make these separate messages, since the message is written for both cases
-               if ( !$user->checkTemporaryPassword( $oldpass ) && !$user->checkPassword( $oldpass ) ) {
-                       Hooks::run( 'PrefsPasswordAudit', [ $user, $newpass, 'wrongpassword' ] );
-                       return Status::newFatal( $this->msg( 'resetpass-wrong-oldpass' ) );
-               }
-
-               // User is resetting their password to their old password
-               if ( $oldpass === $newpass ) {
-                       return Status::newFatal( $this->msg( 'resetpass-recycled' ) );
-               }
-
-               // Do AbortChangePassword after checking mOldpass, so we don't leak information
-               // by possibly aborting a new password before verifying the old password.
-               $abortMsg = 'resetpass-abort-generic';
-               if ( !Hooks::run( 'AbortChangePassword', [ $user, $oldpass, $newpass, &$abortMsg ] ) ) {
-                       Hooks::run( 'PrefsPasswordAudit', [ $user, $newpass, 'abortreset' ] );
-                       return Status::newFatal( $this->msg( $abortMsg ) );
-               }
-
-               // Please reset throttle for successful logins, thanks!
-               LoginForm::clearLoginThrottle( $this->mUserName );
-
-               try {
-                       $user->setPassword( $newpass );
-                       Hooks::run( 'PrefsPasswordAudit', [ $user, $newpass, 'success' ] );
-               } catch ( PasswordError $e ) {
-                       Hooks::run( 'PrefsPasswordAudit', [ $user, $newpass, 'error' ] );
-                       return Status::newFatal( new RawMessage( $e->getMessage() ) );
-               }
-
-               if ( $isSelf ) {
-                       // This is needed to keep the user connected since
-                       // changing the password also modifies the user's token.
-                       $remember = $this->getRequest()->getCookie( 'Token' ) !== null;
-                       $user->setCookies( null, null, $remember );
-               }
-               $user->saveSettings();
-               $this->resetPasswordExpiration( $user );
-               return Status::newGood();
-       }
-
-       public function requiresUnblock() {
-               return false;
-       }
-
-       protected function getGroupName() {
-               return 'users';
-       }
-
-       /**
-        * For resetting user password expiration, until AuthManager comes along
-        * @param User $user
-        */
-       private function resetPasswordExpiration( User $user ) {
-               global $wgPasswordExpirationDays;
-               $newExpire = null;
-               if ( $wgPasswordExpirationDays ) {
-                       $newExpire = wfTimestamp(
-                               TS_MW,
-                               time() + ( $wgPasswordExpirationDays * 24 * 3600 )
-                       );
-               }
-               // Give extensions a chance to force an expiration
-               Hooks::run( 'ResetPasswordExpiration', [ $this, &$newExpire ] );
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->update(
-                       'user',
-                       [ 'user_password_expires' => $dbw->timestampOrNull( $newExpire ) ],
-                       [ 'user_id' => $user->getId() ],
-                       __METHOD__
-               );
-       }
-
-       protected function getDisplayFormat() {
-               return 'ooui';
-       }
-}
diff --git a/includes/specials/pre-authmanager/SpecialCreateAccount.php b/includes/specials/pre-authmanager/SpecialCreateAccount.php
deleted file mode 100644 (file)
index 14f70b5..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-/**
- * Redirect page: Special:CreateAccount --> Special:UserLogin/signup.
- *
- * 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 SpecialPage
- */
-
-/**
- * Redirect page: Special:CreateAccount --> Special:UserLogin/signup.
- * @todo FIXME: This (and the rest of the login frontend) needs to die a horrible painful death
- *
- * @ingroup SpecialPage
- */
-class SpecialCreateAccountPreAuthManager extends SpecialRedirectToSpecial {
-       function __construct() {
-               parent::__construct(
-                       'CreateAccount',
-                       'Userlogin',
-                       'signup',
-                       [ 'returnto', 'returntoquery', 'uselang' ]
-               );
-       }
-
-       public function doesWrites() {
-               return true;
-       }
-
-       // No reason to hide this link on Special:Specialpages
-       public function isListed() {
-               return true;
-       }
-
-       public function isRestricted() {
-               return !User::groupHasPermission( '*', 'createaccount' );
-       }
-
-       public function userCanExecute( User $user ) {
-               return $user->isAllowed( 'createaccount' );
-       }
-
-       protected function getGroupName() {
-               return 'login';
-       }
-}
diff --git a/includes/specials/pre-authmanager/SpecialPasswordReset.php b/includes/specials/pre-authmanager/SpecialPasswordReset.php
deleted file mode 100644 (file)
index e8719a7..0000000
+++ /dev/null
@@ -1,378 +0,0 @@
-<?php
-/**
- * Implements Special:PasswordReset
- *
- * 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 SpecialPage
- */
-
-/**
- * Special page for requesting a password reset email
- *
- * @ingroup SpecialPage
- */
-class SpecialPasswordResetPreAuthManager extends FormSpecialPage {
-       /**
-        * @var Message
-        */
-       private $email;
-
-       /**
-        * @var User
-        */
-       private $firstUser;
-
-       /**
-        * @var Status
-        */
-       private $result;
-
-       /**
-        * @var string $method Identifies which password reset field was specified by the user.
-        */
-       private $method;
-
-       public function __construct() {
-               parent::__construct( 'PasswordReset', 'editmyprivateinfo' );
-       }
-
-       public function doesWrites() {
-               return true;
-       }
-
-       public function userCanExecute( User $user ) {
-               return $this->canChangePassword( $user ) === true && parent::userCanExecute( $user );
-       }
-
-       public function checkExecutePermissions( User $user ) {
-               $error = $this->canChangePassword( $user );
-               if ( is_string( $error ) ) {
-                       throw new ErrorPageError( 'internalerror', $error );
-               } elseif ( !$error ) {
-                       throw new ErrorPageError( 'internalerror', 'resetpass_forbidden' );
-               }
-
-               parent::checkExecutePermissions( $user );
-       }
-
-       protected function getFormFields() {
-               global $wgAuth;
-               $resetRoutes = $this->getConfig()->get( 'PasswordResetRoutes' );
-               $a = [];
-               if ( isset( $resetRoutes['username'] ) && $resetRoutes['username'] ) {
-                       $a['Username'] = [
-                               'type' => 'text',
-                               'label-message' => 'passwordreset-username',
-                       ];
-
-                       if ( $this->getUser()->isLoggedIn() ) {
-                               $a['Username']['default'] = $this->getUser()->getName();
-                       }
-               }
-
-               if ( isset( $resetRoutes['email'] ) && $resetRoutes['email'] ) {
-                       $a['Email'] = [
-                               'type' => 'email',
-                               'label-message' => 'passwordreset-email',
-                       ];
-               }
-
-               if ( isset( $resetRoutes['domain'] ) && $resetRoutes['domain'] ) {
-                       $domains = $wgAuth->domainList();
-                       $a['Domain'] = [
-                               'type' => 'select',
-                               'options' => $domains,
-                               'label-message' => 'passwordreset-domain',
-                       ];
-               }
-
-               if ( $this->getUser()->isAllowed( 'passwordreset' ) ) {
-                       $a['Capture'] = [
-                               'type' => 'check',
-                               'label-message' => 'passwordreset-capture',
-                               'help-message' => 'passwordreset-capture-help',
-                       ];
-               }
-
-               return $a;
-       }
-
-       protected function getDisplayFormat() {
-               return 'ooui';
-       }
-
-       public function alterForm( HTMLForm $form ) {
-               $resetRoutes = $this->getConfig()->get( 'PasswordResetRoutes' );
-
-               $form->addHiddenFields( $this->getRequest()->getValues( 'returnto', 'returntoquery' ) );
-
-               $i = 0;
-               if ( isset( $resetRoutes['username'] ) && $resetRoutes['username'] ) {
-                       $i++;
-               }
-               if ( isset( $resetRoutes['email'] ) && $resetRoutes['email'] ) {
-                       $i++;
-               }
-               if ( isset( $resetRoutes['domain'] ) && $resetRoutes['domain'] ) {
-                       $i++;
-               }
-
-               $message = ( $i > 1 ) ? 'passwordreset-text-many' : 'passwordreset-text-one';
-
-               $form->setHeaderText( $this->msg( $message, $i )->parseAsBlock() );
-               $form->setSubmitTextMsg( 'mailmypassword' );
-       }
-
-       /**
-        * Process the form.  At this point we know that the user passes all the criteria in
-        * userCanExecute(), and if the data array contains 'Username', etc, then Username
-        * resets are allowed.
-        * @param array $data
-        * @throws MWException
-        * @throws ThrottledError|PermissionsError
-        * @return bool|array
-        */
-       public function onSubmit( array $data ) {
-               global $wgAuth, $wgMinimalPasswordLength;
-
-               if ( isset( $data['Domain'] ) ) {
-                       if ( $wgAuth->validDomain( $data['Domain'] ) ) {
-                               $wgAuth->setDomain( $data['Domain'] );
-                       } else {
-                               $wgAuth->setDomain( 'invaliddomain' );
-                       }
-               }
-
-               if ( isset( $data['Capture'] ) && !$this->getUser()->isAllowed( 'passwordreset' ) ) {
-                       // The user knows they don't have the passwordreset permission,
-                       // but they tried to spoof the form. That's naughty
-                       throw new PermissionsError( 'passwordreset' );
-               }
-
-               /**
-                * @var $firstUser User
-                * @var $users User[]
-                */
-
-               if ( isset( $data['Username'] ) && $data['Username'] !== '' ) {
-                       $method = 'username';
-                       $users = [ User::newFromName( $data['Username'] ) ];
-               } elseif ( isset( $data['Email'] )
-                       && $data['Email'] !== ''
-                       && Sanitizer::validateEmail( $data['Email'] )
-               ) {
-                       $method = 'email';
-                       $res = wfGetDB( DB_SLAVE )->select(
-                               'user',
-                               User::selectFields(),
-                               [ 'user_email' => $data['Email'] ],
-                               __METHOD__
-                       );
-
-                       if ( $res ) {
-                               $users = [];
-
-                               foreach ( $res as $row ) {
-                                       $users[] = User::newFromRow( $row );
-                               }
-                       } else {
-                               // Some sort of database error, probably unreachable
-                               throw new MWException( 'Unknown database error in ' . __METHOD__ );
-                       }
-               } else {
-                       // The user didn't supply any data
-                       return false;
-               }
-
-               // Check for hooks (captcha etc), and allow them to modify the users list
-               $error = [];
-               if ( !Hooks::run( 'SpecialPasswordResetOnSubmit', [ &$users, $data, &$error ] ) ) {
-                       return [ $error ];
-               }
-
-               $this->method = $method;
-
-               if ( count( $users ) == 0 ) {
-                       if ( $method == 'email' ) {
-                               // Don't reveal whether or not an email address is in use
-                               return true;
-                       } else {
-                               return [ 'noname' ];
-                       }
-               }
-
-               $firstUser = $users[0];
-
-               if ( !$firstUser instanceof User || !$firstUser->getId() ) {
-                       // Don't parse username as wikitext (bug 65501)
-                       return [ [ 'nosuchuser', wfEscapeWikiText( $data['Username'] ) ] ];
-               }
-
-               // Check against the rate limiter
-               if ( $this->getUser()->pingLimiter( 'mailpassword' ) ) {
-                       throw new ThrottledError;
-               }
-
-               // Check against password throttle
-               foreach ( $users as $user ) {
-                       if ( $user->isPasswordReminderThrottled() ) {
-
-                               # Round the time in hours to 3 d.p., in case someone is specifying
-                               # minutes or seconds.
-                               return [ [
-                                       'throttled-mailpassword',
-                                       round( $this->getConfig()->get( 'PasswordReminderResendTime' ), 3 )
-                               ] ];
-                       }
-               }
-
-               // All the users will have the same email address
-               if ( $firstUser->getEmail() == '' ) {
-                       // This won't be reachable from the email route, so safe to expose the username
-                       return [ [ 'noemail', wfEscapeWikiText( $firstUser->getName() ) ] ];
-               }
-
-               // We need to have a valid IP address for the hook, but per bug 18347, we should
-               // send the user's name if they're logged in.
-               $ip = $this->getRequest()->getIP();
-               if ( !$ip ) {
-                       return [ 'badipaddress' ];
-               }
-               $caller = $this->getUser();
-               Hooks::run( 'User::mailPasswordInternal', [ &$caller, &$ip, &$firstUser ] );
-               $username = $caller->getName();
-               $msg = IP::isValid( $username )
-                       ? 'passwordreset-emailtext-ip'
-                       : 'passwordreset-emailtext-user';
-
-               // Send in the user's language; which should hopefully be the same
-               $userLanguage = $firstUser->getOption( 'language' );
-
-               $passwords = [];
-               foreach ( $users as $user ) {
-                       $password = PasswordFactory::generateRandomPasswordString( $wgMinimalPasswordLength );
-                       $user->setNewpassword( $password );
-                       $user->saveSettings();
-                       $passwords[] = $this->msg( 'passwordreset-emailelement', $user->getName(), $password )
-                               ->inLanguage( $userLanguage )->text(); // We'll escape the whole thing later
-               }
-               $passwordBlock = implode( "\n\n", $passwords );
-
-               $this->email = $this->msg( $msg )->inLanguage( $userLanguage );
-               $this->email->params(
-                       $username,
-                       $passwordBlock,
-                       count( $passwords ),
-                       '<' . Title::newMainPage()->getCanonicalURL() . '>',
-                       round( $this->getConfig()->get( 'NewPasswordExpiry' ) / 86400 )
-               );
-
-               $title = $this->msg( 'passwordreset-emailtitle' )->inLanguage( $userLanguage );
-
-               $this->result = $firstUser->sendMail( $title->text(), $this->email->text() );
-
-               if ( isset( $data['Capture'] ) && $data['Capture'] ) {
-                       // Save the user, will be used if an error occurs when sending the email
-                       $this->firstUser = $firstUser;
-               } else {
-                       // Blank the email if the user is not supposed to see it
-                       $this->email = null;
-               }
-
-               if ( $this->result->isGood() ) {
-                       return true;
-               } elseif ( isset( $data['Capture'] ) && $data['Capture'] ) {
-                       // The email didn't send, but maybe they knew that and that's why they captured it
-                       return true;
-               } else {
-                       // @todo FIXME: The email wasn't sent, but we have already set
-                       // the password throttle timestamp, so they won't be able to try
-                       // again until it expires...  :(
-                       return [ [ 'mailerror', $this->result->getMessage() ] ];
-               }
-       }
-
-       public function onSuccess() {
-               if ( $this->getUser()->isAllowed( 'passwordreset' ) && $this->email != null ) {
-                       // @todo Logging
-
-                       if ( $this->result->isGood() ) {
-                               $this->getOutput()->addWikiMsg( 'passwordreset-emailsent-capture' );
-                       } else {
-                               $this->getOutput()->addWikiMsg( 'passwordreset-emailerror-capture',
-                                       $this->result->getMessage(), $this->firstUser->getName() );
-                       }
-
-                       $this->getOutput()->addHTML( Html::rawElement( 'pre', [], $this->email->escaped() ) );
-               }
-
-               if ( $this->method === 'email' ) {
-                       $this->getOutput()->addWikiMsg( 'passwordreset-emailsentemail' );
-               } else {
-                       $this->getOutput()->addWikiMsg( 'passwordreset-emailsentusername' );
-               }
-
-               $this->getOutput()->returnToMain();
-       }
-
-       protected function canChangePassword( User $user ) {
-               global $wgAuth;
-               $resetRoutes = $this->getConfig()->get( 'PasswordResetRoutes' );
-
-               // Maybe password resets are disabled, or there are no allowable routes
-               if ( !is_array( $resetRoutes ) ||
-                       !in_array( true, array_values( $resetRoutes ) )
-               ) {
-                       return 'passwordreset-disabled';
-               }
-
-               // Maybe the external auth plugin won't allow local password changes
-               if ( !$wgAuth->allowPasswordChange() ) {
-                       return 'resetpass_forbidden';
-               }
-
-               // Maybe email features have been disabled
-               if ( !$this->getConfig()->get( 'EnableEmail' ) ) {
-                       return 'passwordreset-emaildisabled';
-               }
-
-               // Maybe the user is blocked (check this here rather than relying on the parent
-               // method as we have a more specific error message to use here
-               if ( $user->isBlocked() ) {
-                       return 'blocked-mailpassword';
-               }
-
-               return true;
-       }
-
-       /**
-        * Hide the password reset page if resets are disabled.
-        * @return bool
-        */
-       function isListed() {
-               if ( $this->canChangePassword( $this->getUser() ) === true ) {
-                       return parent::isListed();
-               }
-
-               return false;
-       }
-
-       protected function getGroupName() {
-               return 'users';
-       }
-}
diff --git a/includes/specials/pre-authmanager/SpecialUserlogin.php b/includes/specials/pre-authmanager/SpecialUserlogin.php
deleted file mode 100644 (file)
index 951cb52..0000000
+++ /dev/null
@@ -1,1845 +0,0 @@
-<?php
-/**
- * Implements Special:UserLogin
- *
- * 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 SpecialPage
- */
-use MediaWiki\Logger\LoggerFactory;
-use Psr\Log\LogLevel;
-use MediaWiki\Session\SessionManager;
-
-/**
- * Implements Special:UserLogin
- *
- * @ingroup SpecialPage
- */
-class LoginFormPreAuthManager extends SpecialPage {
-       const SUCCESS = 0;
-       const NO_NAME = 1;
-       const ILLEGAL = 2;
-       const WRONG_PLUGIN_PASS = 3;
-       const NOT_EXISTS = 4;
-       const WRONG_PASS = 5;
-       const EMPTY_PASS = 6;
-       const RESET_PASS = 7;
-       const ABORTED = 8;
-       const CREATE_BLOCKED = 9;
-       const THROTTLED = 10;
-       const USER_BLOCKED = 11;
-       const NEED_TOKEN = 12;
-       const WRONG_TOKEN = 13;
-       const USER_MIGRATED = 14;
-
-       public static $statusCodes = [
-               self::SUCCESS => 'success',
-               self::NO_NAME => 'no_name',
-               self::ILLEGAL => 'illegal',
-               self::WRONG_PLUGIN_PASS => 'wrong_plugin_pass',
-               self::NOT_EXISTS => 'not_exists',
-               self::WRONG_PASS => 'wrong_pass',
-               self::EMPTY_PASS => 'empty_pass',
-               self::RESET_PASS => 'reset_pass',
-               self::ABORTED => 'aborted',
-               self::CREATE_BLOCKED => 'create_blocked',
-               self::THROTTLED => 'throttled',
-               self::USER_BLOCKED => 'user_blocked',
-               self::NEED_TOKEN => 'need_token',
-               self::WRONG_TOKEN => 'wrong_token',
-               self::USER_MIGRATED => 'user_migrated',
-       ];
-
-       /**
-        * Valid error and warning messages
-        *
-        * Special:Userlogin can show an error or warning message on the form when
-        * coming from another page. This is done via the ?error= or ?warning= GET
-        * parameters.
-        *
-        * This array is the list of valid message keys. All other values will be
-        * ignored.
-        *
-        * @since 1.24
-        * @var string[]
-        */
-       public static $validErrorMessages = [
-               'exception-nologin-text',
-               'watchlistanontext',
-               'changeemail-no-info',
-               'resetpass-no-info',
-               'confirmemail_needlogin',
-               'prefsnologintext2',
-       ];
-
-       public $mAbortLoginErrorMsg = null;
-       /**
-        * @var int How many seconds user is throttled for
-        * @since 1.27
-        */
-       public $mThrottleWait = '?';
-
-       protected $mUsername;
-       protected $mPassword;
-       protected $mRetype;
-       protected $mReturnTo;
-       protected $mCookieCheck;
-       protected $mPosted;
-       protected $mAction;
-       protected $mCreateaccount;
-       protected $mCreateaccountMail;
-       protected $mLoginattempt;
-       protected $mRemember;
-       protected $mEmail;
-       protected $mDomain;
-       protected $mLanguage;
-       protected $mSkipCookieCheck;
-       protected $mReturnToQuery;
-       protected $mToken;
-       protected $mStickHTTPS;
-       protected $mType;
-       protected $mReason;
-       protected $mRealName;
-       protected $mEntryError = '';
-       protected $mEntryErrorType = 'error';
-
-       private $mTempPasswordUsed;
-       private $mLoaded = false;
-       private $mSecureLoginUrl;
-
-       /** @var WebRequest */
-       private $mOverrideRequest = null;
-
-       /** @var WebRequest Effective request; set at the beginning of load */
-       private $mRequest = null;
-
-       /**
-        * @param WebRequest $request
-        */
-       public function __construct( $request = null ) {
-               global $wgUseMediaWikiUIEverywhere;
-               parent::__construct( 'Userlogin' );
-
-               $this->mOverrideRequest = $request;
-               // Override UseMediaWikiEverywhere to true, to force login and create form to use mw ui
-               $wgUseMediaWikiUIEverywhere = true;
-       }
-
-       public function doesWrites() {
-               return true;
-       }
-
-       /**
-        * Returns an array of all valid error messages.
-        *
-        * @return array
-        */
-       public static function getValidErrorMessages() {
-               static $messages = null;
-               if ( !$messages ) {
-                       $messages = self::$validErrorMessages;
-                       Hooks::run( 'LoginFormValidErrorMessages', [ &$messages ] );
-               }
-
-               return $messages;
-       }
-
-       /**
-        * Loader
-        */
-       function load() {
-               global $wgAuth, $wgHiddenPrefs, $wgEnableEmail;
-
-               if ( $this->mLoaded ) {
-                       return;
-               }
-               $this->mLoaded = true;
-
-               if ( $this->mOverrideRequest === null ) {
-                       $request = $this->getRequest();
-               } else {
-                       $request = $this->mOverrideRequest;
-               }
-               $this->mRequest = $request;
-
-               $this->mType = $request->getText( 'type' );
-               $this->mUsername = $request->getText( 'wpName' );
-               $this->mPassword = $request->getText( 'wpPassword' );
-               $this->mRetype = $request->getText( 'wpRetype' );
-               $this->mDomain = $request->getText( 'wpDomain' );
-               $this->mReason = $request->getText( 'wpReason' );
-               $this->mCookieCheck = $request->getVal( 'wpCookieCheck' );
-               $this->mPosted = $request->wasPosted();
-               $this->mCreateaccountMail = $request->getCheck( 'wpCreateaccountMail' )
-                       && $wgEnableEmail;
-               $this->mCreateaccount = $request->getCheck( 'wpCreateaccount' ) && !$this->mCreateaccountMail;
-               $this->mLoginattempt = $request->getCheck( 'wpLoginattempt' );
-               $this->mAction = $request->getVal( 'action' );
-               $this->mRemember = $request->getCheck( 'wpRemember' );
-               $this->mFromHTTP = $request->getBool( 'fromhttp', false )
-                       || $request->getBool( 'wpFromhttp', false );
-               $this->mStickHTTPS = ( !$this->mFromHTTP && $request->getProtocol() === 'https' )
-                       || $request->getBool( 'wpForceHttps', false );
-               $this->mLanguage = $request->getText( 'uselang' );
-               $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' );
-               $this->mToken = $this->mType == 'signup'
-                       ? $request->getVal( 'wpCreateaccountToken' )
-                       : $request->getVal( 'wpLoginToken' );
-               $this->mReturnTo = $request->getVal( 'returnto', '' );
-               $this->mReturnToQuery = $request->getVal( 'returntoquery', '' );
-
-               // Show an error or warning passed on from a previous page
-               $entryError = $this->msg( $request->getVal( 'error', '' ) );
-               $entryWarning = $this->msg( $request->getVal( 'warning', '' ) );
-               // bc: provide login link as a parameter for messages where the translation
-               // was not updated
-               $loginreqlink = Linker::linkKnown(
-                       $this->getPageTitle(),
-                       $this->msg( 'loginreqlink' )->escaped(),
-                       [],
-                       [
-                               'returnto' => $this->mReturnTo,
-                               'returntoquery' => $this->mReturnToQuery,
-                               'uselang' => $this->mLanguage,
-                               'fromhttp' => $this->mFromHTTP ? '1' : '0',
-                       ]
-               );
-
-               // Only show valid error or warning messages.
-               if ( $entryError->exists()
-                       && in_array( $entryError->getKey(), self::getValidErrorMessages() )
-               ) {
-                       $this->mEntryErrorType = 'error';
-                       $this->mEntryError = $entryError->rawParams( $loginreqlink )->parse();
-
-               } elseif ( $entryWarning->exists()
-                       && in_array( $entryWarning->getKey(), self::getValidErrorMessages() )
-               ) {
-                       $this->mEntryErrorType = 'warning';
-                       $this->mEntryError = $entryWarning->rawParams( $loginreqlink )->parse();
-               }
-
-               if ( $wgEnableEmail ) {
-                       $this->mEmail = $request->getText( 'wpEmail' );
-               } else {
-                       $this->mEmail = '';
-               }
-               if ( !in_array( 'realname', $wgHiddenPrefs ) ) {
-                       $this->mRealName = $request->getText( 'wpRealName' );
-               } else {
-                       $this->mRealName = '';
-               }
-
-               if ( !$wgAuth->validDomain( $this->mDomain ) ) {
-                       $this->mDomain = $wgAuth->getDomain();
-               }
-               $wgAuth->setDomain( $this->mDomain );
-
-               # 1. When switching accounts, it sucks to get automatically logged out
-               # 2. Do not return to PasswordReset after a successful password change
-               #    but goto Wiki start page (Main_Page) instead ( bug 33997 )
-               $returnToTitle = Title::newFromText( $this->mReturnTo );
-               if ( is_object( $returnToTitle )
-                       && ( $returnToTitle->isSpecial( 'Userlogout' )
-                               || $returnToTitle->isSpecial( 'PasswordReset' ) )
-               ) {
-                       $this->mReturnTo = '';
-                       $this->mReturnToQuery = '';
-               }
-       }
-
-       function getDescription() {
-               if ( $this->mType === 'signup' ) {
-                       return $this->msg( 'createaccount' )->text();
-               } else {
-                       return $this->msg( 'login' )->text();
-               }
-       }
-
-       /**
-        * @param string|null $subPage
-        */
-       public function execute( $subPage ) {
-               // Make sure session is persisted
-               $session = SessionManager::getGlobalSession();
-               $session->persist();
-
-               $this->load();
-
-               // Check for [[Special:Userlogin/signup]]. This affects form display and
-               // page title.
-               if ( $subPage == 'signup' ) {
-                       $this->mType = 'signup';
-               }
-               $this->setHeaders();
-
-               // Make sure it's possible to log in
-               if ( $this->mType !== 'signup' && !$session->canSetUser() ) {
-                       throw new ErrorPageError(
-                               'cannotloginnow-title',
-                               'cannotloginnow-text',
-                               [
-                                       $session->getProvider()->describe( RequestContext::getMain()->getLanguage() )
-                               ]
-                       );
-               }
-
-               /**
-                * In the case where the user is already logged in, and was redirected to
-                * the login form from a page that requires login, do not show the login
-                * page. The use case scenario for this is when a user opens a large number
-                * of tabs, is redirected to the login page on all of them, and then logs
-                * in on one, expecting all the others to work properly.
-                *
-                * However, do show the form if it was visited intentionally (no 'returnto'
-                * is present). People who often switch between several accounts have grown
-                * accustomed to this behavior.
-                */
-               if (
-                       $this->mType !== 'signup' &&
-                       !$this->mPosted &&
-                       $this->getUser()->isLoggedIn() &&
-                       ( $this->mReturnTo !== '' || $this->mReturnToQuery !== '' )
-               ) {
-                       $this->successfulLogin();
-               }
-
-               // If logging in and not on HTTPS, either redirect to it or offer a link.
-               global $wgSecureLogin;
-               if ( $this->mRequest->getProtocol() !== 'https' ) {
-                       $title = $this->getFullTitle();
-                       $query = [
-                               'returnto' => $this->mReturnTo !== '' ? $this->mReturnTo : null,
-                               'returntoquery' => $this->mReturnToQuery !== '' ?
-                                       $this->mReturnToQuery : null,
-                               'title' => null,
-                               ( $this->mEntryErrorType === 'error' ? 'error' : 'warning' ) => $this->mEntryError,
-                       ] + $this->mRequest->getQueryValues();
-                       $url = $title->getFullURL( $query, false, PROTO_HTTPS );
-                       if ( $wgSecureLogin
-                               && wfCanIPUseHTTPS( $this->getRequest()->getIP() )
-                               && !$this->mFromHTTP ) // Avoid infinite redirect
-                       {
-                               $url = wfAppendQuery( $url, 'fromhttp=1' );
-                               $this->getOutput()->redirect( $url );
-                               // Since we only do this redir to change proto, always vary
-                               $this->getOutput()->addVaryHeader( 'X-Forwarded-Proto' );
-
-                               return;
-                       } else {
-                               // A wiki without HTTPS login support should set $wgServer to
-                               // http://somehost, in which case the secure URL generated
-                               // above won't actually start with https://
-                               if ( substr( $url, 0, 8 ) === 'https://' ) {
-                                       $this->mSecureLoginUrl = $url;
-                               }
-                       }
-               }
-
-               if ( !is_null( $this->mCookieCheck ) ) {
-                       $this->onCookieRedirectCheck( $this->mCookieCheck );
-
-                       return;
-               } elseif ( $this->mPosted ) {
-                       if ( $this->mCreateaccount ) {
-                               $this->addNewAccount();
-
-                               return;
-                       } elseif ( $this->mCreateaccountMail ) {
-                               $this->addNewAccountMailPassword();
-
-                               return;
-                       } elseif ( ( 'submitlogin' == $this->mAction ) || $this->mLoginattempt ) {
-                               $this->processLogin();
-
-                               return;
-                       }
-               }
-               $this->mainLoginForm( $this->mEntryError, $this->mEntryErrorType );
-       }
-
-       /**
-        * @private
-        */
-       function addNewAccountMailPassword() {
-               if ( $this->mEmail == '' ) {
-                       $this->mainLoginForm( $this->msg( 'noemailcreate' )->escaped() );
-
-                       return;
-               }
-
-               $status = $this->addNewAccountInternal();
-               LoggerFactory::getInstance( 'authmanager' )->info(
-                       'Account creation attempt with mailed password',
-                       [ 'event' => 'accountcreation', 'status' => $status ]
-               );
-               if ( !$status->isGood() ) {
-                       $error = $status->getMessage();
-                       $this->mainLoginForm( $error->toString() );
-
-                       return;
-               }
-
-               /** @var User $u */
-               $u = $status->getValue();
-
-               // Wipe the initial password and mail a temporary one
-               $u->setPassword( null );
-               $u->saveSettings();
-               $result = $this->mailPasswordInternal( $u, false, 'createaccount-title', 'createaccount-text' );
-
-               Hooks::run( 'AddNewAccount', [ $u, true ] );
-               $u->addNewUserLogEntry( 'byemail', $this->mReason );
-
-               $out = $this->getOutput();
-               $out->setPageTitle( $this->msg( 'accmailtitle' ) );
-
-               if ( !$result->isGood() ) {
-                       $this->mainLoginForm( $this->msg( 'mailerror', $result->getWikiText() )->text() );
-               } else {
-                       $out->addWikiMsg( 'accmailtext', $u->getName(), $u->getEmail() );
-                       $this->executeReturnTo( 'success' );
-               }
-       }
-
-       /**
-        * @private
-        * @return bool
-        */
-       function addNewAccount() {
-               global $wgContLang, $wgUser, $wgEmailAuthentication, $wgLoginLanguageSelector;
-
-               # Create the account and abort if there's a problem doing so
-               $status = $this->addNewAccountInternal();
-               LoggerFactory::getInstance( 'authmanager' )->info( 'Account creation attempt', [
-                       'event' => 'accountcreation',
-                       'status' => $status,
-               ] );
-
-               if ( !$status->isGood() ) {
-                       $error = $status->getMessage();
-                       $this->mainLoginForm( $error->toString() );
-
-                       return false;
-               }
-
-               $u = $status->getValue();
-
-               # Only save preferences if the user is not creating an account for someone else.
-               if ( $this->getUser()->isAnon() ) {
-                       # If we showed up language selection links, and one was in use, be
-                       # smart (and sensible) and save that language as the user's preference
-                       if ( $wgLoginLanguageSelector && $this->mLanguage ) {
-                               $u->setOption( 'language', $this->mLanguage );
-                       } else {
-
-                               # Otherwise the user's language preference defaults to $wgContLang,
-                               # but it may be better to set it to their preferred $wgContLang variant,
-                               # based on browser preferences or URL parameters.
-                               $u->setOption( 'language', $wgContLang->getPreferredVariant() );
-                       }
-                       if ( $wgContLang->hasVariants() ) {
-                               $u->setOption( 'variant', $wgContLang->getPreferredVariant() );
-                       }
-               }
-
-               $out = $this->getOutput();
-
-               # Send out an email authentication message if needed
-               if ( $wgEmailAuthentication && Sanitizer::validateEmail( $u->getEmail() ) ) {
-                       $status = $u->sendConfirmationMail();
-                       if ( $status->isGood() ) {
-                               $out->addWikiMsg( 'confirmemail_oncreate' );
-                       } else {
-                               $out->addWikiText( $status->getWikiText( 'confirmemail_sendfailed' ) );
-                       }
-               }
-
-               # Save settings (including confirmation token)
-               $u->saveSettings();
-
-               # If not logged in, assume the new account as the current one and set
-               # session cookies then show a "welcome" message or a "need cookies"
-               # message as needed
-               if ( $this->getUser()->isAnon() ) {
-                       $u->setCookies();
-                       $wgUser = $u;
-                       // This should set it for OutputPage and the Skin
-                       // which is needed or the personal links will be
-                       // wrong.
-                       $this->getContext()->setUser( $u );
-                       Hooks::run( 'AddNewAccount', [ $u, false ] );
-                       $u->addNewUserLogEntry( 'create' );
-                       if ( $this->hasSessionCookie() ) {
-                               $this->successfulCreation();
-                       } else {
-                               $this->cookieRedirectCheck( 'new' );
-                       }
-               } else {
-                       # Confirm that the account was created
-                       $out->setPageTitle( $this->msg( 'accountcreated' ) );
-                       $out->addWikiMsg( 'accountcreatedtext', $u->getName() );
-                       $out->addReturnTo( $this->getPageTitle() );
-                       Hooks::run( 'AddNewAccount', [ $u, false ] );
-                       $u->addNewUserLogEntry( 'create2', $this->mReason );
-               }
-
-               return true;
-       }
-
-       /**
-        * Make a new user account using the loaded data.
-        * @private
-        * @throws PermissionsError|ReadOnlyError
-        * @return Status
-        */
-       public function addNewAccountInternal() {
-               global $wgAuth, $wgAccountCreationThrottle, $wgEmailConfirmToEdit;
-
-               // If the user passes an invalid domain, something is fishy
-               if ( !$wgAuth->validDomain( $this->mDomain ) ) {
-                       return Status::newFatal( 'wrongpassword' );
-               }
-
-               // If we are not allowing users to login locally, we should be checking
-               // to see if the user is actually able to authenticate to the authenti-
-               // cation server before they create an account (otherwise, they can
-               // create a local account and login as any domain user). We only need
-               // to check this for domains that aren't local.
-               if ( 'local' != $this->mDomain && $this->mDomain != '' ) {
-                       if (
-                               !$wgAuth->canCreateAccounts() &&
-                               (
-                                       !$wgAuth->userExists( $this->mUsername ) ||
-                                       !$wgAuth->authenticate( $this->mUsername, $this->mPassword )
-                               )
-                       ) {
-                               return Status::newFatal( 'wrongpassword' );
-                       }
-               }
-
-               if ( wfReadOnly() ) {
-                       throw new ReadOnlyError;
-               }
-
-               # Request forgery checks.
-               $token = self::getCreateaccountToken();
-               if ( $token->wasNew() ) {
-                       return Status::newFatal( 'nocookiesfornew' );
-               }
-
-               # The user didn't pass a createaccount token
-               if ( !$this->mToken ) {
-                       return Status::newFatal( 'sessionfailure' );
-               }
-
-               # Validate the createaccount token
-               if ( !$token->match( $this->mToken ) ) {
-                       return Status::newFatal( 'sessionfailure' );
-               }
-
-               # Check permissions
-               $currentUser = $this->getUser();
-               $creationBlock = $currentUser->isBlockedFromCreateAccount();
-               if ( !$currentUser->isAllowed( 'createaccount' ) ) {
-                       throw new PermissionsError( 'createaccount' );
-               } elseif ( $creationBlock instanceof Block ) {
-                       // Throws an ErrorPageError.
-                       $this->userBlockedMessage( $creationBlock );
-
-                       // This should never be reached.
-                       return false;
-               }
-
-               # Include checks that will include GlobalBlocking (Bug 38333)
-               $permErrors = $this->getPageTitle()->getUserPermissionsErrors(
-                       'createaccount',
-                       $currentUser,
-                       true
-               );
-
-               if ( count( $permErrors ) ) {
-                       throw new PermissionsError( 'createaccount', $permErrors );
-               }
-
-               $ip = $this->getRequest()->getIP();
-               if ( $currentUser->isDnsBlacklisted( $ip, true /* check $wgProxyWhitelist */ ) ) {
-                       return Status::newFatal( 'sorbs_create_account_reason' );
-               }
-
-               # Now create a dummy user ($u) and check if it is valid
-               $u = User::newFromName( $this->mUsername, 'creatable' );
-               if ( !$u ) {
-                       return Status::newFatal( 'noname' );
-               }
-
-               $cache = ObjectCache::getLocalClusterInstance();
-               # Make sure the user does not exist already
-               $lock = $cache->getScopedLock( $cache->makeGlobalKey( 'account', md5( $this->mUsername ) ) );
-               if ( !$lock ) {
-                       return Status::newFatal( 'usernameinprogress' );
-               } elseif ( $u->idForName( User::READ_LOCKING ) ) {
-                       return Status::newFatal( 'userexists' );
-               }
-
-               if ( $this->mCreateaccountMail ) {
-                       # do not force a password for account creation by email
-                       # set invalid password, it will be replaced later by a random generated password
-                       $this->mPassword = null;
-               } else {
-                       if ( $this->mPassword !== $this->mRetype ) {
-                               return Status::newFatal( 'badretype' );
-                       }
-
-                       # check for password validity, return a fatal Status if invalid
-                       $validity = $u->checkPasswordValidity( $this->mPassword, 'create' );
-                       if ( !$validity->isGood() ) {
-                               $validity->ok = false; // make sure this Status is fatal
-                               return $validity;
-                       }
-               }
-
-               # if you need a confirmed email address to edit, then obviously you
-               # need an email address.
-               if ( $wgEmailConfirmToEdit && strval( $this->mEmail ) === '' ) {
-                       return Status::newFatal( 'noemailtitle' );
-               }
-
-               if ( strval( $this->mEmail ) !== '' && !Sanitizer::validateEmail( $this->mEmail ) ) {
-                       return Status::newFatal( 'invalidemailaddress' );
-               }
-
-               # Set some additional data so the AbortNewAccount hook can be used for
-               # more than just username validation
-               $u->setEmail( $this->mEmail );
-               $u->setRealName( $this->mRealName );
-
-               $abortError = '';
-               $abortStatus = null;
-               if ( !Hooks::run( 'AbortNewAccount', [ $u, &$abortError, &$abortStatus ] ) ) {
-                       // Hook point to add extra creation throttles and blocks
-                       wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
-                       if ( $abortStatus === null ) {
-                               // Report back the old string as a raw message status.
-                               // This will report the error back as 'createaccount-hook-aborted'
-                               // with the given string as the message.
-                               // To return a different error code, return a Status object.
-                               $abortError = new Message( 'createaccount-hook-aborted', [ $abortError ] );
-                               $abortError->text();
-
-                               return Status::newFatal( $abortError );
-                       } else {
-                               // For MediaWiki 1.23+ and updated hooks, return the Status object
-                               // returned from the hook.
-                               return $abortStatus;
-                       }
-               }
-
-               // Hook point to check for exempt from account creation throttle
-               if ( !Hooks::run( 'ExemptFromAccountCreationThrottle', [ $ip ] ) ) {
-                       wfDebug( "LoginForm::exemptFromAccountCreationThrottle: a hook " .
-                               "allowed account creation w/o throttle\n" );
-               } else {
-                       if ( ( $wgAccountCreationThrottle && $currentUser->isPingLimitable() ) ) {
-                               $key = wfGlobalCacheKey( 'acctcreate', 'ip', $ip );
-                               $value = $cache->get( $key );
-                               if ( !$value ) {
-                                       $cache->set( $key, 0, $cache::TTL_DAY );
-                               }
-                               if ( $value >= $wgAccountCreationThrottle ) {
-                                       return Status::newFatal( 'acct_creation_throttle_hit', $wgAccountCreationThrottle );
-                               }
-                               $cache->incr( $key );
-                       }
-               }
-
-               if ( !$wgAuth->addUser( $u, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
-                       return Status::newFatal( 'externaldberror' );
-               }
-
-               self::clearCreateaccountToken();
-
-               return $this->initUser( $u, false );
-       }
-
-       /**
-        * Actually add a user to the database.
-        * Give it a User object that has been initialised with a name.
-        *
-        * @param User $u
-        * @param bool $autocreate True if this is an autocreation via auth plugin
-        * @return Status Status object, with the User object in the value member on success
-        * @private
-        */
-       function initUser( $u, $autocreate ) {
-               global $wgAuth;
-
-               $status = $u->addToDatabase();
-               if ( !$status->isOK() ) {
-                       return $status;
-               }
-
-               if ( $wgAuth->allowPasswordChange() ) {
-                       $u->setPassword( $this->mPassword );
-               }
-
-               $u->setEmail( $this->mEmail );
-               $u->setRealName( $this->mRealName );
-               $u->setToken();
-
-               Hooks::run( 'LocalUserCreated', [ $u, $autocreate ] );
-               $oldUser = $u;
-               $wgAuth->initUser( $u, $autocreate );
-               if ( $oldUser !== $u ) {
-                       wfWarn( get_class( $wgAuth ) . '::initUser() replaced the user object' );
-               }
-
-               $u->saveSettings();
-
-               // Update user count
-               DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 0, 0, 0, 1 ) );
-
-               // Watch user's userpage and talk page
-               $u->addWatch( $u->getUserPage(), User::IGNORE_USER_RIGHTS );
-
-               return Status::newGood( $u );
-       }
-
-       /**
-        * Internally authenticate the login request.
-        *
-        * This may create a local account as a side effect if the
-        * authentication plugin allows transparent local account
-        * creation.
-        * @return int
-        */
-       public function authenticateUserData() {
-               global $wgUser, $wgAuth;
-
-               $this->load();
-
-               if ( $this->mUsername == '' ) {
-                       return self::NO_NAME;
-               }
-
-               // We require a login token to prevent login CSRF
-               // Handle part of this before incrementing the throttle so
-               // token-less login attempts don't count towards the throttle
-               // but wrong-token attempts do.
-
-               // If the user doesn't have a login token yet, set one.
-               $token = self::getLoginToken();
-               if ( $token->wasNew() ) {
-                       return self::NEED_TOKEN;
-               }
-               // If the user didn't pass a login token, tell them we need one
-               if ( !$this->mToken ) {
-                       return self::NEED_TOKEN;
-               }
-
-               $throttleCount = self::incrementLoginThrottle( $this->mUsername );
-               if ( $throttleCount ) {
-                       $this->mThrottleWait = $throttleCount['wait'];
-                       return self::THROTTLED;
-               }
-
-               // Validate the login token
-               if ( !$token->match( $this->mToken ) ) {
-                       return self::WRONG_TOKEN;
-               }
-
-               // Load the current user now, and check to see if we're logging in as
-               // the same name. This is necessary because loading the current user
-               // (say by calling getName()) calls the UserLoadFromSession hook, which
-               // potentially creates the user in the database. Until we load $wgUser,
-               // checking for user existence using User::newFromName($name)->getId() below
-               // will effectively be using stale data.
-               if ( $this->getUser()->getName() === $this->mUsername ) {
-                       wfDebug( __METHOD__ . ": already logged in as {$this->mUsername}\n" );
-
-                       return self::SUCCESS;
-               }
-
-               $u = User::newFromName( $this->mUsername );
-               if ( $u === false ) {
-                       return self::ILLEGAL;
-               }
-
-               $msg = null;
-               // Give extensions a way to indicate the username has been updated,
-               // rather than telling the user the account doesn't exist.
-               if ( !Hooks::run( 'LoginUserMigrated', [ $u, &$msg ] ) ) {
-                       $this->mAbortLoginErrorMsg = $msg;
-                       return self::USER_MIGRATED;
-               }
-
-               if ( !User::isUsableName( $u->getName() ) ) {
-                       return self::ILLEGAL;
-               }
-
-               $isAutoCreated = false;
-               if ( $u->getId() == 0 ) {
-                       $status = $this->attemptAutoCreate( $u );
-                       if ( $status !== self::SUCCESS ) {
-                               return $status;
-                       } else {
-                               $isAutoCreated = true;
-                       }
-               } else {
-                       $u->load();
-               }
-
-               // Give general extensions, such as a captcha, a chance to abort logins
-               $abort = self::ABORTED;
-               if ( !Hooks::run( 'AbortLogin', [ $u, $this->mPassword, &$abort, &$msg ] ) ) {
-                       if ( !in_array( $abort, array_keys( self::$statusCodes ), true ) ) {
-                               throw new Exception( 'Invalid status code returned from AbortLogin hook: ' . $abort );
-                       }
-                       $this->mAbortLoginErrorMsg = $msg;
-                       return $abort;
-               }
-
-               global $wgBlockDisablesLogin;
-               if ( !$u->checkPassword( $this->mPassword ) ) {
-                       if ( $u->checkTemporaryPassword( $this->mPassword ) ) {
-                               /**
-                                * The e-mailed temporary password should not be used for actu-
-                                * al logins; that's a very sloppy habit, and insecure if an
-                                * attacker has a few seconds to click "search" on someone's
-                                * open mail reader.
-                                *
-                                * Allow it to be used only to reset the password a single time
-                                * to a new value, which won't be in the user's e-mail ar-
-                                * chives.
-                                *
-                                * For backwards compatibility, we'll still recognize it at the
-                                * login form to minimize surprises for people who have been
-                                * logging in with a temporary password for some time.
-                                *
-                                * As a side-effect, we can authenticate the user's e-mail ad-
-                                * dress if it's not already done, since the temporary password
-                                * was sent via e-mail.
-                                */
-                               if ( !$u->isEmailConfirmed() && !wfReadOnly() ) {
-                                       $u->confirmEmail();
-                                       $u->saveSettings();
-                               }
-
-                               // At this point we just return an appropriate code/ indicating
-                               // that the UI should show a password reset form; bot inter-
-                               // faces etc will probably just fail cleanly here.
-                               $this->mAbortLoginErrorMsg = 'resetpass-temp-emailed';
-                               $this->mTempPasswordUsed = true;
-                               $retval = self::RESET_PASS;
-                       } else {
-                               $retval = ( $this->mPassword == '' ) ? self::EMPTY_PASS : self::WRONG_PASS;
-                       }
-               } elseif ( $wgBlockDisablesLogin && $u->isBlocked() ) {
-                       // If we've enabled it, make it so that a blocked user cannot login
-                       $retval = self::USER_BLOCKED;
-               } elseif ( $this->checkUserPasswordExpired( $u ) == 'hard' ) {
-                       // Force reset now, without logging in
-                       $retval = self::RESET_PASS;
-                       $this->mAbortLoginErrorMsg = 'resetpass-expired';
-               } else {
-                       Hooks::run( 'UserLoggedIn', [ $u ] );
-                       $oldUser = $u;
-                       $wgAuth->updateUser( $u );
-                       if ( $oldUser !== $u ) {
-                               wfWarn( get_class( $wgAuth ) . '::updateUser() replaced the user object' );
-                       }
-                       $wgUser = $u;
-                       // This should set it for OutputPage and the Skin
-                       // which is needed or the personal links will be
-                       // wrong.
-                       $this->getContext()->setUser( $u );
-
-                       // Please reset throttle for successful logins, thanks!
-                       self::clearLoginThrottle( $this->mUsername );
-
-                       if ( $isAutoCreated ) {
-                               // Must be run after $wgUser is set, for correct new user log
-                               Hooks::run( 'AuthPluginAutoCreate', [ $u ] );
-                       }
-
-                       $retval = self::SUCCESS;
-               }
-               Hooks::run( 'LoginAuthenticateAudit', [ $u, $this->mPassword, $retval ] );
-
-               return $retval;
-       }
-
-       /**
-        * Increment the login attempt throttle hit count for the (username,current IP)
-        * tuple unless the throttle was already reached.
-        *
-        * @since 1.27 Return value changed.
-        * @param string $username The user name
-        * @return bool|array false if below limit or an array if above limit
-        *   Array contains keys wait, count, and throttleIndex
-        */
-       public static function incrementLoginThrottle( $username ) {
-               global $wgPasswordAttemptThrottle, $wgRequest;
-               $canUsername = User::getCanonicalName( $username, 'usable' );
-               $username = $canUsername !== false ? $canUsername : $username;
-
-               $throttleCount = 0;
-               if ( is_array( $wgPasswordAttemptThrottle ) ) {
-                       $throttleConfig = $wgPasswordAttemptThrottle;
-                       if ( isset( $wgPasswordAttemptThrottle['count'] ) ) {
-                               // old style. Convert for backwards compat.
-                               $throttleConfig = [ $wgPasswordAttemptThrottle ];
-                       }
-                       foreach ( $throttleConfig as $index => $specificThrottle ) {
-                               if ( isset( $specificThrottle['allIPs'] ) ) {
-                                       $ip = 'All';
-                               } else {
-                                       $ip = $wgRequest->getIP();
-                               }
-                               $throttleKey = wfGlobalCacheKey( 'password-throttle',
-                                       $index, $ip, md5( $username )
-                               );
-                               $count = $specificThrottle['count'];
-                               $period = $specificThrottle['seconds'];
-
-                               $cache = ObjectCache::getLocalClusterInstance();
-                               $throttleCount = $cache->get( $throttleKey );
-                               if ( !$throttleCount ) {
-                                       $cache->add( $throttleKey, 1, $period ); // start counter
-                               } elseif ( $throttleCount < $count ) {
-                                       $cache->incr( $throttleKey );
-                               } elseif ( $throttleCount >= $count ) {
-                                       $logMsg = 'Login attempt rejected because logins to '
-                                               . '{acct} from IP {ip} have been throttled for '
-                                               . '{period} seconds due to {count} failed attempts';
-                                       // If we are hitting a throttle for >= 50 attempts,
-                                       // it is much more likely to be an attack than someone
-                                       // simply forgetting their password, so log it at a
-                                       // higher level.
-                                       $level = $count >= 50 ? LogLevel::WARNING : LogLevel::INFO;
-                                       // It should be noted that once the throttle is hit,
-                                       // every attempt to login will generate the log message
-                                       // until the throttle expires, not just the attempt that
-                                       // puts the throttle over the top.
-                                       LoggerFactory::getInstance( 'password-throttle' )->log(
-                                               $level,
-                                               $logMsg,
-                                               [
-                                                       'ip' => $ip,
-                                                       'period' => $period,
-                                                       'acct' => $username,
-                                                       'count' => $count,
-                                                       'throttleIdentifier' => $index,
-                                                       'method' => __METHOD__
-                                               ]
-                                       );
-
-                                       return [
-                                               'throttleIndex' => $index,
-                                               'wait' => $period,
-                                               'count' => $count
-                                       ];
-                               }
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * Increment the login attempt throttle hit count for the (username,current IP)
-        * tuple unless the throttle was already reached.
-        *
-        * @deprecated Use LoginForm::incrementLoginThrottle instead
-        * @param string $username The user name
-        * @return bool|int true if above throttle, or 0 (prior to 1.27, returned current count)
-        */
-       public static function incLoginThrottle( $username ) {
-               wfDeprecated( __METHOD__, "1.27" );
-               $res = self::incrementLoginThrottle( $username );
-               return is_array( $res ) ? true : 0;
-       }
-
-       /**
-        * Clear the login attempt throttle hit count for the (username,current IP) tuple.
-        * @param string $username The user name
-        * @return void
-        */
-       public static function clearLoginThrottle( $username ) {
-               global $wgRequest, $wgPasswordAttemptThrottle;
-               $canUsername = User::getCanonicalName( $username, 'usable' );
-               $username = $canUsername !== false ? $canUsername : $username;
-
-               if ( is_array( $wgPasswordAttemptThrottle ) ) {
-                       $throttleConfig = $wgPasswordAttemptThrottle;
-                       if ( isset( $wgPasswordAttemptThrottle['count'] ) ) {
-                               // old style. Convert for backwards compat.
-                               $throttleConfig = [ $wgPasswordAttemptThrottle ];
-                       }
-                       foreach ( $throttleConfig as $index => $specificThrottle ) {
-                               if ( isset( $specificThrottle['allIPs'] ) ) {
-                                       $ip = 'All';
-                               } else {
-                                       $ip = $wgRequest->getIP();
-                               }
-                               $throttleKey = wfGlobalCacheKey( 'password-throttle', $index,
-                                       $ip, md5( $username )
-                               );
-                               ObjectCache::getLocalClusterInstance()->delete( $throttleKey );
-                       }
-               }
-       }
-
-       /**
-        * Attempt to automatically create a user on login. Only succeeds if there
-        * is an external authentication method which allows it.
-        *
-        * @param User $user
-        *
-        * @return int Status code
-        */
-       function attemptAutoCreate( $user ) {
-               global $wgAuth;
-
-               if ( $this->getUser()->isBlockedFromCreateAccount() ) {
-                       wfDebug( __METHOD__ . ": user is blocked from account creation\n" );
-
-                       return self::CREATE_BLOCKED;
-               }
-
-               if ( !$wgAuth->autoCreate() ) {
-                       return self::NOT_EXISTS;
-               }
-
-               if ( !$wgAuth->userExists( $user->getName() ) ) {
-                       wfDebug( __METHOD__ . ": user does not exist\n" );
-
-                       return self::NOT_EXISTS;
-               }
-
-               if ( !$wgAuth->authenticate( $user->getName(), $this->mPassword ) ) {
-                       wfDebug( __METHOD__ . ": \$wgAuth->authenticate() returned false, aborting\n" );
-
-                       return self::WRONG_PLUGIN_PASS;
-               }
-
-               $abortError = '';
-               if ( !Hooks::run( 'AbortAutoAccount', [ $user, &$abortError ] ) ) {
-                       // Hook point to add extra creation throttles and blocks
-                       wfDebug( "LoginForm::attemptAutoCreate: a hook blocked creation: $abortError\n" );
-                       $this->mAbortLoginErrorMsg = $abortError;
-
-                       return self::ABORTED;
-               }
-
-               wfDebug( __METHOD__ . ": creating account\n" );
-               $status = $this->initUser( $user, true );
-
-               if ( !$status->isOK() ) {
-                       $errors = $status->getErrorsByType( 'error' );
-                       $this->mAbortLoginErrorMsg = $errors[0]['message'];
-
-                       return self::ABORTED;
-               }
-
-               return self::SUCCESS;
-       }
-
-       function processLogin() {
-               global $wgLang, $wgSecureLogin, $wgInvalidPasswordReset;
-
-               $authRes = $this->authenticateUserData();
-               switch ( $authRes ) {
-                       case self::SUCCESS:
-                               # We've verified now, update the real record
-                               $user = $this->getUser();
-                               $user->touch();
-
-                               if ( $user->requiresHTTPS() ) {
-                                       $this->mStickHTTPS = true;
-                               }
-
-                               if ( $wgSecureLogin && !$this->mStickHTTPS ) {
-                                       $user->setCookies( $this->mRequest, false, $this->mRemember );
-                               } else {
-                                       $user->setCookies( $this->mRequest, null, $this->mRemember );
-                               }
-                               self::clearLoginToken();
-
-                               // Reset the throttle
-                               self::clearLoginThrottle( $this->mUsername );
-
-                               $request = $this->getRequest();
-                               if ( $this->hasSessionCookie() || $this->mSkipCookieCheck ) {
-                                       /* Replace the language object to provide user interface in
-                                        * correct language immediately on this first page load.
-                                        */
-                                       $code = $request->getVal( 'uselang', $user->getOption( 'language' ) );
-                                       $userLang = Language::factory( $code );
-                                       $wgLang = $userLang;
-                                       RequestContext::getMain()->setLanguage( $userLang );
-                                       $this->getContext()->setLanguage( $userLang );
-                                       // Reset SessionID on Successful login (bug 40995)
-                                       $this->renewSessionId();
-                                       if ( $this->checkUserPasswordExpired( $this->getUser() ) == 'soft' ) {
-                                               $this->resetLoginForm( $this->msg( 'resetpass-expired-soft' ) );
-                                       } elseif ( $wgInvalidPasswordReset
-                                               && !$user->isValidPassword( $this->mPassword )
-                                       ) {
-                                               $status = $user->checkPasswordValidity(
-                                                       $this->mPassword,
-                                                       'login'
-                                               );
-                                               $this->resetLoginForm(
-                                                       $status->getMessage( 'resetpass-validity-soft' )
-                                               );
-                                       } else {
-                                               $this->successfulLogin();
-                                       }
-                               } else {
-                                       $this->cookieRedirectCheck( 'login' );
-                               }
-                               break;
-
-                       case self::NEED_TOKEN:
-                               $error = $this->mAbortLoginErrorMsg ?: 'nocookiesforlogin';
-                               $this->mainLoginForm( $this->msg( $error )->parse() );
-                               break;
-                       case self::WRONG_TOKEN:
-                               $error = $this->mAbortLoginErrorMsg ?: 'sessionfailure';
-                               $this->mainLoginForm( $this->msg( $error )->text() );
-                               break;
-                       case self::NO_NAME:
-                       case self::ILLEGAL:
-                               $error = $this->mAbortLoginErrorMsg ?: 'noname';
-                               $this->mainLoginForm( $this->msg( $error )->text() );
-                               break;
-                       case self::WRONG_PLUGIN_PASS:
-                               $error = $this->mAbortLoginErrorMsg ?: 'wrongpassword';
-                               $this->mainLoginForm( $this->msg( $error )->text() );
-                               break;
-                       case self::NOT_EXISTS:
-                               if ( $this->getUser()->isAllowed( 'createaccount' ) ) {
-                                       $error = $this->mAbortLoginErrorMsg ?: 'nosuchuser';
-                                       $this->mainLoginForm( $this->msg( $error,
-                                               wfEscapeWikiText( $this->mUsername ) )->parse() );
-                               } else {
-                                       $error = $this->mAbortLoginErrorMsg ?: 'nosuchusershort';
-                                       $this->mainLoginForm( $this->msg( $error,
-                                               wfEscapeWikiText( $this->mUsername ) )->text() );
-                               }
-                               break;
-                       case self::WRONG_PASS:
-                               $error = $this->mAbortLoginErrorMsg ?: 'wrongpassword';
-                               $this->mainLoginForm( $this->msg( $error )->text() );
-                               break;
-                       case self::EMPTY_PASS:
-                               $error = $this->mAbortLoginErrorMsg ?: 'wrongpasswordempty';
-                               $this->mainLoginForm( $this->msg( $error )->text() );
-                               break;
-                       case self::RESET_PASS:
-                               $error = $this->mAbortLoginErrorMsg ?: 'resetpass_announce';
-                               $this->resetLoginForm( $this->msg( $error ) );
-                               break;
-                       case self::CREATE_BLOCKED:
-                               $this->userBlockedMessage( $this->getUser()->isBlockedFromCreateAccount() );
-                               break;
-                       case self::THROTTLED:
-                               $error = $this->mAbortLoginErrorMsg ?: 'login-throttled';
-                               $this->mainLoginForm( $this->msg( $error )
-                                       ->durationParams( $this->mThrottleWait )->text()
-                               );
-                               break;
-                       case self::USER_BLOCKED:
-                               $error = $this->mAbortLoginErrorMsg ?: 'login-userblocked';
-                               $this->mainLoginForm( $this->msg( $error, $this->mUsername )->escaped() );
-                               break;
-                       case self::ABORTED:
-                               $error = $this->mAbortLoginErrorMsg ?: 'login-abort-generic';
-                               $this->mainLoginForm( $this->msg( $error,
-                                               wfEscapeWikiText( $this->mUsername ) )->text() );
-                               break;
-                       case self::USER_MIGRATED:
-                               $error = $this->mAbortLoginErrorMsg ?: 'login-migrated-generic';
-                               $params = [];
-                               if ( is_array( $error ) ) {
-                                       $error = array_shift( $this->mAbortLoginErrorMsg );
-                                       $params = $this->mAbortLoginErrorMsg;
-                               }
-                               $this->mainLoginForm( $this->msg( $error, $params )->text() );
-                               break;
-                       default:
-                               throw new MWException( 'Unhandled case value' );
-               }
-
-               LoggerFactory::getInstance( 'authmanager' )->info( 'Login attempt', [
-                       'event' => 'login',
-                       'successful' => $authRes === self::SUCCESS,
-                       'status' => LoginForm::$statusCodes[$authRes],
-               ] );
-       }
-
-       /**
-        * Show the Special:ChangePassword form, with custom message
-        * @param Message $msg
-        */
-       protected function resetLoginForm( Message $msg ) {
-               // Allow hooks to explain this password reset in more detail
-               Hooks::run( 'LoginPasswordResetMessage', [ &$msg, $this->mUsername ] );
-               $reset = new SpecialChangePasswordPreAuthManager();
-               $derivative = new DerivativeContext( $this->getContext() );
-               $derivative->setTitle( $reset->getPageTitle() );
-               $reset->setContext( $derivative );
-               if ( !$this->mTempPasswordUsed ) {
-                       $reset->setOldPasswordMessage( 'oldpassword' );
-               }
-               $reset->setChangeMessage( $msg );
-               $reset->execute( null );
-       }
-
-       /**
-        * @param User $u
-        * @param bool $throttle
-        * @param string $emailTitle Message name of email title
-        * @param string $emailText Message name of email text
-        * @return Status
-        */
-       function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle',
-               $emailText = 'passwordremindertext'
-       ) {
-               global $wgNewPasswordExpiry, $wgMinimalPasswordLength;
-
-               if ( $u->getEmail() == '' ) {
-                       return Status::newFatal( 'noemail', $u->getName() );
-               }
-               $ip = $this->getRequest()->getIP();
-               if ( !$ip ) {
-                       return Status::newFatal( 'badipaddress' );
-               }
-
-               $currentUser = $this->getUser();
-               Hooks::run( 'User::mailPasswordInternal', [ &$currentUser, &$ip, &$u ] );
-
-               $np = PasswordFactory::generateRandomPasswordString( $wgMinimalPasswordLength );
-               $u->setNewpassword( $np, $throttle );
-               $u->saveSettings();
-               $userLanguage = $u->getOption( 'language' );
-
-               $mainPage = Title::newMainPage();
-               $mainPageUrl = $mainPage->getCanonicalURL();
-
-               $m = $this->msg( $emailText, $ip, $u->getName(), $np, '<' . $mainPageUrl . '>',
-                       round( $wgNewPasswordExpiry / 86400 ) )->inLanguage( $userLanguage )->text();
-               $result = $u->sendMail( $this->msg( $emailTitle )->inLanguage( $userLanguage )->text(), $m );
-
-               return $result;
-       }
-
-       /**
-        * Run any hooks registered for logins, then HTTP redirect to
-        * $this->mReturnTo (or Main Page if that's undefined).  Formerly we had a
-        * nice message here, but that's really not as useful as just being sent to
-        * wherever you logged in from.  It should be clear that the action was
-        * successful, given the lack of error messages plus the appearance of your
-        * name in the upper right.
-        *
-        * @private
-        */
-       function successfulLogin() {
-               # Run any hooks; display injected HTML if any, else redirect
-               $currentUser = $this->getUser();
-               $injected_html = '';
-               Hooks::run( 'UserLoginComplete', [ &$currentUser, &$injected_html ] );
-
-               if ( $injected_html !== '' ) {
-                       $this->displaySuccessfulAction( 'success', $this->msg( 'loginsuccesstitle' ),
-                               'loginsuccess', $injected_html );
-               } else {
-                       $this->executeReturnTo( 'successredirect' );
-               }
-       }
-
-       /**
-        * Run any hooks registered for logins, then display a message welcoming
-        * the user.
-        *
-        * @private
-        */
-       function successfulCreation() {
-               # Run any hooks; display injected HTML
-               $currentUser = $this->getUser();
-               $injected_html = '';
-               $welcome_creation_msg = 'welcomecreation-msg';
-
-               Hooks::run( 'UserLoginComplete', [ &$currentUser, &$injected_html ] );
-
-               /**
-                * Let any extensions change what message is shown.
-                * @see https://www.mediawiki.org/wiki/Manual:Hooks/BeforeWelcomeCreation
-                * @since 1.18
-                */
-               Hooks::run( 'BeforeWelcomeCreation', [ &$welcome_creation_msg, &$injected_html ] );
-
-               $this->displaySuccessfulAction(
-                       'signup',
-                       $this->msg( 'welcomeuser', $this->getUser()->getName() ),
-                       $welcome_creation_msg, $injected_html
-               );
-       }
-
-       /**
-        * Display a "successful action" page.
-        *
-        * @param string $type Condition of return to; see `executeReturnTo`
-        * @param string|Message $title Page's title
-        * @param string $msgname
-        * @param string $injected_html
-        */
-       private function displaySuccessfulAction( $type, $title, $msgname, $injected_html ) {
-               $out = $this->getOutput();
-               $out->setPageTitle( $title );
-               if ( $msgname ) {
-                       $out->addWikiMsg( $msgname, wfEscapeWikiText( $this->getUser()->getName() ) );
-               }
-
-               $out->addHTML( $injected_html );
-
-               $this->executeReturnTo( $type );
-       }
-
-       /**
-        * Output a message that informs the user that they cannot create an account because
-        * there is a block on them or their IP which prevents account creation.  Note that
-        * User::isBlockedFromCreateAccount(), which gets this block, ignores the 'hardblock'
-        * setting on blocks (bug 13611).
-        * @param Block $block The block causing this error
-        * @throws ErrorPageError
-        */
-       function userBlockedMessage( Block $block ) {
-               # Let's be nice about this, it's likely that this feature will be used
-               # for blocking large numbers of innocent people, e.g. range blocks on
-               # schools. Don't blame it on the user. There's a small chance that it
-               # really is the user's fault, i.e. the username is blocked and they
-               # haven't bothered to log out before trying to create an account to
-               # evade it, but we'll leave that to their guilty conscience to figure
-               # out.
-               $errorParams = [
-                       $block->getTarget(),
-                       $block->mReason ? $block->mReason : $this->msg( 'blockednoreason' )->text(),
-                       $block->getByName()
-               ];
-
-               if ( $block->getType() === Block::TYPE_RANGE ) {
-                       $errorMessage = 'cantcreateaccount-range-text';
-                       $errorParams[] = $this->getRequest()->getIP();
-               } else {
-                       $errorMessage = 'cantcreateaccount-text';
-               }
-
-               throw new ErrorPageError(
-                       'cantcreateaccounttitle',
-                       $errorMessage,
-                       $errorParams
-               );
-       }
-
-       /**
-        * Add a "return to" link or redirect to it.
-        * Extensions can use this to reuse the "return to" logic after
-        * inject steps (such as redirection) into the login process.
-        *
-        * @param string $type One of the following:
-        *    - error: display a return to link ignoring $wgRedirectOnLogin
-        *    - signup: display a return to link using $wgRedirectOnLogin if needed
-        *    - success: display a return to link using $wgRedirectOnLogin if needed
-        *    - successredirect: send an HTTP redirect using $wgRedirectOnLogin if needed
-        * @param string $returnTo
-        * @param array|string $returnToQuery
-        * @param bool $stickHTTPs Keep redirect link on HTTPs
-        * @since 1.22
-        */
-       public function showReturnToPage(
-               $type, $returnTo = '', $returnToQuery = '', $stickHTTPs = false
-       ) {
-               $this->mReturnTo = $returnTo;
-               $this->mReturnToQuery = $returnToQuery;
-               $this->mStickHTTPS = $stickHTTPs;
-               $this->executeReturnTo( $type );
-       }
-
-       /**
-        * Add a "return to" link or redirect to it.
-        *
-        * @param string $type One of the following:
-        *    - error: display a return to link ignoring $wgRedirectOnLogin
-        *    - signup: display a return to link using $wgRedirectOnLogin if needed
-        *    - success: display a return to link using $wgRedirectOnLogin if needed
-        *    - successredirect: send an HTTP redirect using $wgRedirectOnLogin if needed
-        */
-       private function executeReturnTo( $type ) {
-               global $wgRedirectOnLogin, $wgSecureLogin;
-
-               if ( $type != 'error' && $wgRedirectOnLogin !== null ) {
-                       $returnTo = $wgRedirectOnLogin;
-                       $returnToQuery = [];
-               } else {
-                       $returnTo = $this->mReturnTo;
-                       $returnToQuery = wfCgiToArray( $this->mReturnToQuery );
-               }
-
-               // Allow modification of redirect behavior
-               Hooks::run( 'PostLoginRedirect', [ &$returnTo, &$returnToQuery, &$type ] );
-
-               $returnToTitle = Title::newFromText( $returnTo );
-               if ( !$returnToTitle ) {
-                       $returnToTitle = Title::newMainPage();
-               }
-
-               if ( $wgSecureLogin && !$this->mStickHTTPS ) {
-                       $options = [ 'http' ];
-                       $proto = PROTO_HTTP;
-               } elseif ( $wgSecureLogin ) {
-                       $options = [ 'https' ];
-                       $proto = PROTO_HTTPS;
-               } else {
-                       $options = [];
-                       $proto = PROTO_RELATIVE;
-               }
-
-               if ( $type == 'successredirect' ) {
-                       $redirectUrl = $returnToTitle->getFullURL( $returnToQuery, false, $proto );
-                       $this->getOutput()->redirect( $redirectUrl );
-               } else {
-                       $this->getOutput()->addReturnTo( $returnToTitle, $returnToQuery, null, $options );
-               }
-       }
-
-       /**
-        * @param string $msg
-        * @param string $msgtype
-        * @throws ErrorPageError
-        * @throws Exception
-        * @throws FatalError
-        * @throws MWException
-        * @throws PermissionsError
-        * @throws ReadOnlyError
-        * @private
-        */
-       function mainLoginForm( $msg, $msgtype = 'error' ) {
-               global $wgEnableEmail, $wgEnableUserEmail;
-               global $wgHiddenPrefs, $wgLoginLanguageSelector;
-               global $wgAuth, $wgEmailConfirmToEdit;
-               global $wgSecureLogin, $wgPasswordResetRoutes;
-               global $wgExtendedLoginCookieExpiration, $wgCookieExpiration;
-
-               $titleObj = $this->getPageTitle();
-               $user = $this->getUser();
-               $out = $this->getOutput();
-
-               if ( $this->mType == 'signup' ) {
-                       // Block signup here if in readonly. Keeps user from
-                       // going through the process (filling out data, etc)
-                       // and being informed later.
-                       $permErrors = $titleObj->getUserPermissionsErrors( 'createaccount', $user, true );
-                       if ( count( $permErrors ) ) {
-                               throw new PermissionsError( 'createaccount', $permErrors );
-                       } elseif ( $user->isBlockedFromCreateAccount() ) {
-                               $this->userBlockedMessage( $user->isBlockedFromCreateAccount() );
-
-                               return;
-                       } elseif ( wfReadOnly() ) {
-                               throw new ReadOnlyError;
-                       }
-               }
-
-               // Pre-fill username (if not creating an account, bug 44775).
-               if ( $this->mUsername == '' && $this->mType != 'signup' ) {
-                       if ( $user->isLoggedIn() ) {
-                               $this->mUsername = $user->getName();
-                       } else {
-                               $this->mUsername = $this->getRequest()->getSession()->suggestLoginUsername();
-                       }
-               }
-
-               // Generic styles and scripts for both login and signup form
-               $out->addModuleStyles( [
-                       'mediawiki.ui',
-                       'mediawiki.ui.button',
-                       'mediawiki.ui.checkbox',
-                       'mediawiki.ui.input',
-                       'mediawiki.special.userlogin.common.styles'
-               ] );
-
-               if ( $this->mType == 'signup' ) {
-                       // Additional styles and scripts for signup form
-                       $out->addModules( [
-                               'mediawiki.special.userlogin.signup.js'
-                       ] );
-                       $out->addModuleStyles( [
-                               'mediawiki.special.userlogin.signup.styles'
-                       ] );
-
-                       $template = new UsercreateTemplate( $this->getConfig() );
-
-                       // Must match number of benefits defined in messages
-                       $template->set( 'benefitCount', 3 );
-
-                       $q = 'action=submitlogin&type=signup';
-                       $linkq = 'type=login';
-               } else {
-                       // Additional styles for login form
-                       $out->addModuleStyles( [
-                               'mediawiki.special.userlogin.login.styles'
-                       ] );
-
-                       $template = new UserloginTemplate( $this->getConfig() );
-
-                       $q = 'action=submitlogin&type=login';
-                       $linkq = 'type=signup';
-               }
-
-               if ( $this->mReturnTo !== '' ) {
-                       $returnto = '&returnto=' . wfUrlencode( $this->mReturnTo );
-                       if ( $this->mReturnToQuery !== '' ) {
-                               $returnto .= '&returntoquery=' .
-                                       wfUrlencode( $this->mReturnToQuery );
-                       }
-                       $q .= $returnto;
-                       $linkq .= $returnto;
-               }
-
-               # Don't show a "create account" link if the user can't.
-               if ( $this->showCreateOrLoginLink( $user ) ) {
-                       # Pass any language selection on to the mode switch link
-                       if ( $wgLoginLanguageSelector && $this->mLanguage ) {
-                               $linkq .= '&uselang=' . $this->mLanguage;
-                       }
-                       // Supply URL, login template creates the button.
-                       $template->set( 'createOrLoginHref', $titleObj->getLocalURL( $linkq ) );
-               } else {
-                       $template->set( 'link', '' );
-               }
-
-               $resetLink = $this->mType == 'signup'
-                       ? null
-                       : is_array( $wgPasswordResetRoutes ) && in_array( true, array_values( $wgPasswordResetRoutes ) );
-
-               $template->set( 'header', '' );
-               $template->set( 'formheader', '' );
-               $template->set( 'skin', $this->getSkin() );
-               $template->set( 'name', $this->mUsername );
-               $template->set( 'password', $this->mPassword );
-               $template->set( 'retype', $this->mRetype );
-               $template->set( 'createemailset', $this->mCreateaccountMail );
-               $template->set( 'email', $this->mEmail );
-               $template->set( 'realname', $this->mRealName );
-               $template->set( 'domain', $this->mDomain );
-               $template->set( 'reason', $this->mReason );
-
-               $template->set( 'action', $titleObj->getLocalURL( $q ) );
-               $template->set( 'message', $msg );
-               $template->set( 'messagetype', $msgtype );
-               $template->set( 'createemail', $wgEnableEmail && $user->isLoggedIn() );
-               $template->set( 'userealname', !in_array( 'realname', $wgHiddenPrefs ) );
-               $template->set( 'useemail', $wgEnableEmail );
-               $template->set( 'emailrequired', $wgEmailConfirmToEdit );
-               $template->set( 'emailothers', $wgEnableUserEmail );
-               $template->set( 'canreset', $wgAuth->allowPasswordChange() );
-               $template->set( 'resetlink', $resetLink );
-               $template->set( 'canremember', $wgExtendedLoginCookieExpiration === null ?
-                       ( $wgCookieExpiration > 0 ) :
-                       ( $wgExtendedLoginCookieExpiration > 0 ) );
-               $template->set( 'usereason', $user->isLoggedIn() );
-               $template->set( 'remember', $this->mRemember );
-               $template->set( 'cansecurelogin', ( $wgSecureLogin === true ) );
-               $template->set( 'stickhttps', (int)$this->mStickHTTPS );
-               $template->set( 'loggedin', $user->isLoggedIn() );
-               $template->set( 'loggedinuser', $user->getName() );
-
-               if ( $this->mType == 'signup' ) {
-                       $template->set( 'token', self::getCreateaccountToken()->toString() );
-               } else {
-                       $template->set( 'token', self::getLoginToken()->toString() );
-               }
-
-               # Prepare language selection links as needed
-               if ( $wgLoginLanguageSelector ) {
-                       $template->set( 'languages', $this->makeLanguageSelector() );
-                       if ( $this->mLanguage ) {
-                               $template->set( 'uselang', $this->mLanguage );
-                       }
-               }
-
-               $template->set( 'secureLoginUrl', $this->mSecureLoginUrl );
-               // Use signupend-https for HTTPS requests if it's not blank, signupend otherwise
-               $usingHTTPS = $this->mRequest->getProtocol() == 'https';
-               $signupendHTTPS = $this->msg( 'signupend-https' );
-               if ( $usingHTTPS && !$signupendHTTPS->isBlank() ) {
-                       $template->set( 'signupend', $signupendHTTPS->parse() );
-               } else {
-                       $template->set( 'signupend', $this->msg( 'signupend' )->parse() );
-               }
-
-               // If using HTTPS coming from HTTP, then the 'fromhttp' parameter must be preserved
-               if ( $usingHTTPS ) {
-                       $template->set( 'fromhttp', $this->mFromHTTP );
-               }
-
-               // Give authentication and captcha plugins a chance to modify the form
-               $wgAuth->modifyUITemplate( $template, $this->mType );
-               if ( $this->mType == 'signup' ) {
-                       Hooks::run( 'UserCreateForm', [ &$template ] );
-               } else {
-                       Hooks::run( 'UserLoginForm', [ &$template ] );
-               }
-
-               $out->disallowUserJs(); // just in case...
-               $out->addTemplate( $template );
-       }
-
-       /**
-        * Whether the login/create account form should display a link to the
-        * other form (in addition to whatever the skin provides).
-        *
-        * @param User $user
-        * @return bool
-        */
-       private function showCreateOrLoginLink( &$user ) {
-               if ( $this->mType == 'signup' ) {
-                       return true;
-               } elseif ( $user->isAllowed( 'createaccount' ) ) {
-                       return true;
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Check if a session cookie is present.
-        *
-        * This will not pick up a cookie set during _this_ request, but is meant
-        * to ensure that the client is returning the cookie which was set on a
-        * previous pass through the system.
-        *
-        * @private
-        * @return bool
-        */
-       function hasSessionCookie() {
-               global $wgDisableCookieCheck, $wgInitialSessionId;
-
-               return $wgDisableCookieCheck || (
-                       $wgInitialSessionId &&
-                       $this->getRequest()->getSession()->getId() === (string)$wgInitialSessionId
-               );
-       }
-
-       /**
-        * Get the login token from the current session
-        * @since 1.27 returns a MediaWiki\Session\Token instead of a string
-        * @return MediaWiki\Session\Token
-        */
-       public static function getLoginToken() {
-               global $wgRequest;
-               return $wgRequest->getSession()->getToken( '', 'login' );
-       }
-
-       /**
-        * Formerly randomly generated a login token that would be returned by
-        * $this->getLoginToken().
-        *
-        * Since 1.27, this is a no-op. The token is generated as necessary by
-        * $this->getLoginToken().
-        *
-        * @deprecated since 1.27
-        */
-       public static function setLoginToken() {
-               wfDeprecated( __METHOD__, '1.27' );
-       }
-
-       /**
-        * Remove any login token attached to the current session
-        */
-       public static function clearLoginToken() {
-               global $wgRequest;
-               $wgRequest->getSession()->resetToken( 'login' );
-       }
-
-       /**
-        * Get the createaccount token from the current session
-        * @since 1.27 returns a MediaWiki\Session\Token instead of a string
-        * @return MediaWiki\Session\Token
-        */
-       public static function getCreateaccountToken() {
-               global $wgRequest;
-               return $wgRequest->getSession()->getToken( '', 'createaccount' );
-       }
-
-       /**
-        * Formerly randomly generated a createaccount token that would be returned
-        * by $this->getCreateaccountToken().
-        *
-        * Since 1.27, this is a no-op. The token is generated as necessary by
-        * $this->getCreateaccountToken().
-        *
-        * @deprecated since 1.27
-        */
-       public static function setCreateaccountToken() {
-               wfDeprecated( __METHOD__, '1.27' );
-       }
-
-       /**
-        * Remove any createaccount token attached to the current session
-        */
-       public static function clearCreateaccountToken() {
-               global $wgRequest;
-               $wgRequest->getSession()->resetToken( 'createaccount' );
-       }
-
-       /**
-        * Renew the user's session id, using strong entropy
-        */
-       private function renewSessionId() {
-               global $wgSecureLogin, $wgCookieSecure;
-               if ( $wgSecureLogin && !$this->mStickHTTPS ) {
-                       $wgCookieSecure = false;
-               }
-
-               SessionManager::getGlobalSession()->resetId();
-               SessionManager::getGlobalSession()->resetAllTokens();
-       }
-
-       /**
-        * @param string $type
-        * @private
-        */
-       function cookieRedirectCheck( $type ) {
-               $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
-               $query = [ 'wpCookieCheck' => $type ];
-               if ( $this->mReturnTo !== '' ) {
-                       $query['returnto'] = $this->mReturnTo;
-                       $query['returntoquery'] = $this->mReturnToQuery;
-               }
-               $check = $titleObj->getFullURL( $query );
-
-               $this->getOutput()->redirect( $check );
-       }
-
-       /**
-        * @param string $type
-        * @private
-        */
-       function onCookieRedirectCheck( $type ) {
-               if ( !$this->hasSessionCookie() ) {
-                       if ( $type == 'new' ) {
-                               $this->mainLoginForm( $this->msg( 'nocookiesnew' )->parse() );
-                       } elseif ( $type == 'login' ) {
-                               $this->mainLoginForm( $this->msg( 'nocookieslogin' )->parse() );
-                       } else {
-                               # shouldn't happen
-                               $this->mainLoginForm( $this->msg( 'error' )->text() );
-                       }
-               } else {
-                       $this->successfulLogin();
-               }
-       }
-
-       /**
-        * Produce a bar of links which allow the user to select another language
-        * during login/registration but retain "returnto"
-        *
-        * @return string
-        */
-       function makeLanguageSelector() {
-               $msg = $this->msg( 'loginlanguagelinks' )->inContentLanguage();
-               if ( $msg->isBlank() ) {
-                       return '';
-               }
-               $langs = explode( "\n", $msg->text() );
-               $links = [];
-               foreach ( $langs as $lang ) {
-                       $lang = trim( $lang, '* ' );
-                       $parts = explode( '|', $lang );
-                       if ( count( $parts ) >= 2 ) {
-                               $links[] = $this->makeLanguageSelectorLink( $parts[0], trim( $parts[1] ) );
-                       }
-               }
-
-               return count( $links ) > 0 ? $this->msg( 'loginlanguagelabel' )->rawParams(
-                       $this->getLanguage()->pipeList( $links ) )->escaped() : '';
-       }
-
-       /**
-        * Create a language selector link for a particular language
-        * Links back to this page preserving type and returnto
-        *
-        * @param string $text Link text
-        * @param string $lang Language code
-        * @return string
-        */
-       function makeLanguageSelectorLink( $text, $lang ) {
-               if ( $this->getLanguage()->getCode() == $lang ) {
-                       // no link for currently used language
-                       return htmlspecialchars( $text );
-               }
-               $query = [ 'uselang' => $lang ];
-               if ( $this->mType == 'signup' ) {
-                       $query['type'] = 'signup';
-               }
-               if ( $this->mReturnTo !== '' ) {
-                       $query['returnto'] = $this->mReturnTo;
-                       $query['returntoquery'] = $this->mReturnToQuery;
-               }
-
-               $attr = [];
-               $targetLanguage = Language::factory( $lang );
-               $attr['lang'] = $attr['hreflang'] = $targetLanguage->getHtmlCode();
-
-               return Linker::linkKnown(
-                       $this->getPageTitle(),
-                       htmlspecialchars( $text ),
-                       $attr,
-                       $query
-               );
-       }
-
-       protected function getGroupName() {
-               return 'login';
-       }
-
-       /**
-        * Private function to check password expiration, until AuthManager comes
-        * along to handle that.
-        * @param User $user
-        * @return string|bool
-        */
-       private function checkUserPasswordExpired( User $user ) {
-               global $wgPasswordExpireGrace;
-               $dbr = wfGetDB( DB_SLAVE );
-               $ts = $dbr->selectField( 'user', 'user_password_expires', [ 'user_id' => $user->getId() ] );
-
-               $expired = false;
-               $now = wfTimestamp();
-               $expUnix = wfTimestamp( TS_UNIX, $ts );
-               if ( $ts !== null && $expUnix < $now ) {
-                       $expired = ( $expUnix + $wgPasswordExpireGrace < $now ) ? 'hard' : 'soft';
-               }
-               return $expired;
-       }
-
-       protected function getSubpagesForPrefixSearch() {
-               return [ 'signup' ];
-       }
-}
diff --git a/includes/specials/pre-authmanager/SpecialUserlogout.php b/includes/specials/pre-authmanager/SpecialUserlogout.php
deleted file mode 100644 (file)
index 6d6a714..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-/**
- * Implements Special:Userlogout
- *
- * 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 SpecialPage
- */
-
-/**
- * Implements Special:Userlogout
- *
- * @ingroup SpecialPage
- */
-class SpecialUserlogoutPreAuthManager extends UnlistedSpecialPage {
-       function __construct() {
-               parent::__construct( 'Userlogout' );
-       }
-
-       public function doesWrites() {
-               return true;
-       }
-
-       function execute( $par ) {
-               /**
-                * Some satellite ISPs use broken precaching schemes that log people out straight after
-                * they're logged in (bug 17790). Luckily, there's a way to detect such requests.
-                */
-               if ( isset( $_SERVER['REQUEST_URI'] ) && strpos( $_SERVER['REQUEST_URI'], '&amp;' ) !== false ) {
-                       wfDebug( "Special:Userlogout request {$_SERVER['REQUEST_URI']} looks suspicious, denying.\n" );
-                       throw new HttpError( 400, $this->msg( 'suspicious-userlogout' ), $this->msg( 'loginerror' ) );
-               }
-
-               $this->setHeaders();
-               $this->outputHeader();
-
-               // Make sure it's possible to log out
-               $session = MediaWiki\Session\SessionManager::getGlobalSession();
-               if ( !$session->canSetUser() ) {
-                       throw new ErrorPageError(
-                               'cannotlogoutnow-title',
-                               'cannotlogoutnow-text',
-                               [
-                                       $session->getProvider()->describe( RequestContext::getMain()->getLanguage() )
-                               ]
-                       );
-               }
-
-               $user = $this->getUser();
-               $oldName = $user->getName();
-               $user->logout();
-
-               $loginURL = SpecialPage::getTitleFor( 'Userlogin' )->getFullURL(
-                       $this->getRequest()->getValues( 'returnto', 'returntoquery' ) );
-
-               $out = $this->getOutput();
-               $out->addWikiMsg( 'logouttext', $loginURL );
-
-               // Hook.
-               $injected_html = '';
-               Hooks::run( 'UserLogoutComplete', [ &$user, &$injected_html, $oldName ] );
-               $out->addHTML( $injected_html );
-
-               $out->returnToMain();
-       }
-
-       protected function getGroupName() {
-               return 'login';
-       }
-}
diff --git a/includes/templates/Usercreate.php b/includes/templates/Usercreate.php
deleted file mode 100644 (file)
index 0a5aa61..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-<?php
-// @codingStandardsIgnoreFile
-/**
- * Html form for account creation (since 1.22 with VForm appearance).
- *
- * 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 Templates
- * @deprecated Will be removed when AuthManager lands.
- *   The signup form will be generated via HTMLForm.
- */
-
-class UsercreateTemplate extends BaseTemplate {
-       /**
-        * Extensions (AntiSpoof and TitleBlacklist) call this in response to
-        * UserCreateForm hook to add checkboxes to the create account form.
-        */
-       function addInputItem( $name, $value, $type, $msg, $helptext = false ) {
-               $this->data['extraInput'][] = [
-                       'name' => $name,
-                       'value' => $value,
-                       'type' => $type,
-                       'msg' => $msg,
-                       'helptext' => $helptext,
-               ];
-       }
-
-       function execute() {
-?>
-<div class="mw-ui-container">
-       <?php if ( $this->haveData( 'languages' ) ) { ?>
-               <div id="languagelinks">
-                       <p><?php $this->html( 'languages' ); ?></p>
-               </div>
-       <?php }
-             if ( !wfMessage( 'signupstart' )->isDisabled() ) { ?>
-               <div id="signupstart"><?php $this->msgWiki( 'signupstart' ); ?></div>
-       <?php } ?>
-       <div id="userloginForm">
-               <form name="userlogin2" id="userlogin2" class="mw-ui-vform" method="post" action="<?php $this->text( 'action' ); ?>">
-                       <section class="mw-form-header">
-                               <?php $this->html( 'header' ); ?>
-                       </section>
-                       <!-- This element is used by the mediawiki.special.userlogin.signup.js module. -->
-                       <div
-                               id="mw-createacct-status-area"
-                               <?php if ( $this->data['message'] ) { ?>
-                                       class="<?php echo $this->data['messagetype']; ?>box"
-                               <?php } else { ?>
-                                       style="display: none;"
-                               <?php } ?>
-                       >
-                       <?php if ( $this->data['message'] ) { ?>
-                                       <?php if ( $this->data['messagetype'] == 'error' ) { ?>
-                                               <strong><?php $this->msg( 'createacct-error' ); ?></strong>
-                                               <br />
-                                       <?php } ?>
-                                       <?php $this->html( 'message' ); ?>
-                       <?php } ?>
-                       </div>
-
-                       <?php if ( $this->data['formheader'] ) { ?>
-                               <div class="mw-form-formheader">
-                                       <?php $this->html( 'formheader' ); /* extensions such as MobileFrontend add html here */ ?>
-                               </div>
-                       <?php } ?>
-
-                       <div class="mw-ui-vform-field">
-                               <label for='wpName2'>
-                                       <?php $this->msg( 'userlogin-yourname' ); ?>
-
-                                       <span class="mw-ui-flush-right"><?php echo $this->getMsg( 'createacct-helpusername' )->parse(); ?></span>
-                               </label>
-                               <?php
-                               echo Html::input( 'wpName', $this->data['name'], 'text', [
-                                       'class' => 'mw-ui-input loginText',
-                                       'id' => 'wpName2',
-                                       'tabindex' => '1',
-                                       'size' => '20',
-                                       'required',
-                                       'placeholder' => $this->getMsg( $this->data['loggedin'] ?
-                                               'createacct-another-username-ph' : 'userlogin-yourname-ph' )->text(),
-                               ] );
-                               ?>
-                       </div>
-
-                       <div class="mw-ui-vform-field">
-                               <?php if ( $this->data['createemail'] ) { ?>
-                                       <div class="mw-ui-checkbox">
-                                               <input name="wpCreateaccountMail" type="checkbox" value="1" id="wpCreateaccountMail" tabindex="2"
-                                                       <?php if ( $this->data['createemailset'] ) {
-                                                               echo 'checked="checked"';
-                                                       } ?>
-                                               ><label for="wpCreateaccountMail">
-                                                       <?php $this->msg( 'createaccountmail' ); ?>
-                                               </label>
-                                       </div>
-                               <?php } ?>
-                       </div>
-
-                       <div class="mw-ui-vform-field mw-row-password">
-                               <label for='wpPassword2'><?php $this->msg( 'userlogin-yourpassword' ); ?></label>
-                               <?php
-                               echo Html::input( 'wpPassword', null, 'password', [
-                                       'class' => 'mw-ui-input loginPassword',
-                                       'id' => 'wpPassword2',
-                                       'tabindex' => '3',
-                                       'size' => '20',
-                                       'required',
-                                       'placeholder' => $this->getMsg( 'createacct-yourpassword-ph' )->text()
-                               ] + User::passwordChangeInputAttribs() );
-                               ?>
-                       </div>
-
-                       <?php
-                       if ( $this->data['usedomain'] ) {
-                               $select = new XmlSelect( 'wpDomain', false, $this->data['domain'] );
-                               $select->setAttribute( 'tabindex', 4 );
-                               foreach ( $this->data['domainnames'] as $dom ) {
-                                       $select->addOption( $dom );
-                               }
-                       ?>
-                               <div class="mw-ui-vform-field" id="mw-user-domain-section">
-                                       <label for="wpDomain"><?php $this->msg( 'yourdomainname' ); ?></label>
-                                       <div>
-                                               <?php echo $select->getHTML(); ?>
-                                       </div>
-                               </div>
-                       <?php } ?>
-
-                       <div class="mw-ui-vform-field mw-row-password">
-                               <label for='wpRetype'><?php $this->msg( 'createacct-yourpasswordagain' ); ?></label>
-                               <?php
-                               echo Html::input( 'wpRetype', null, 'password', [
-                                       'class' => 'mw-ui-input loginPassword',
-                                       'id' => 'wpRetype',
-                                       'tabindex' => '5',
-                                       'size' => '20',
-                                       'required',
-                                       'placeholder' => $this->getMsg( 'createacct-yourpasswordagain-ph' )->text()
-                               ] + User::passwordChangeInputAttribs() );
-                               ?>
-                       </div>
-
-                       <div class="mw-ui-vform-field">
-                               <?php if ( $this->data['useemail'] ) { ?>
-                                       <label for='wpEmail'>
-                                               <?php
-                                                       $this->msg( $this->data['emailrequired'] ?
-                                                               'createacct-emailrequired' :
-                                                               'createacct-emailoptional'
-                                                       );
-                                               ?>
-                                       </label>
-                                       <?php
-                                               echo Html::input( 'wpEmail', $this->data['email'], 'email', [
-                                                       'class' => 'mw-ui-input loginText',
-                                                       'id' => 'wpEmail',
-                                                       'tabindex' => '6',
-                                                       'size' => '20',
-                                                       'required' => $this->data['emailrequired'],
-                                                       'placeholder' => $this->getMsg( $this->data['loggedin'] ?
-                                                               'createacct-another-email-ph' : 'createacct-email-ph' )->text()
-                                               ] );
-                                       ?>
-                               <?php } ?>
-                       </div>
-
-                       <?php if ( $this->data['userealname'] ) { ?>
-                               <div class="mw-ui-vform-field">
-                                       <label for='wpRealName'><?php $this->msg( 'createacct-realname' ); ?></label>
-                                       <input type='text' class='mw-ui-input loginText' name="wpRealName" id="wpRealName"
-                                               tabindex="7"
-                                               value="<?php $this->text( 'realname' ); ?>" size='20' />
-                                       <div class="prefsectiontip">
-                                               <?php $this->msgWiki( $this->data['loggedin'] ? 'createacct-another-realname-tip' : 'prefs-help-realname' ); ?>
-                                       </div>
-                               </div>
-                       <?php } ?>
-
-                       <?php if ( $this->data['usereason'] ) { ?>
-                               <div class="mw-ui-vform-field">
-                                       <label for='wpReason'><?php $this->msg( 'createacct-reason' ); ?></label>
-                                       <?php echo Html::input( 'wpReason', $this->data['reason'], 'text', [
-                                               'class' => 'mw-ui-input loginText',
-                                               'id' => 'wpReason',
-                                               'tabindex' => '8',
-                                               'size' => '20',
-                                               'placeholder' => $this->getMsg( 'createacct-reason-ph' )->text()
-                                       ] ); ?>
-                               </div>
-                       <?php } ?>
-
-                       <?php
-                       $tabIndex = 9;
-                       if ( isset( $this->data['extraInput'] ) && is_array( $this->data['extraInput'] ) ) {
-                               foreach ( $this->data['extraInput'] as $inputItem ) { ?>
-                                       <div class="mw-ui-vform-field">
-                                               <?php
-                                               // If it's a checkbox, output the whole thing (assume it has a msg).
-                                               if ( $inputItem['type'] == 'checkbox' ) {
-                                               ?>
-                                                       <div class="mw-ui-checkbox">
-                                                               <input
-                                                                       name="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
-                                                                       id="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
-                                                                       type="checkbox" value="1"
-                                                                       tabindex="<?php echo $tabIndex++; ?>"
-                                                                       <?php if ( !empty( $inputItem['value'] ) ) {
-                                                                               echo 'checked="checked"';
-                                                                       } ?>
-                                                               ><label for="<?php echo htmlspecialchars( $inputItem['name'] ); ?>">
-                                                                       <?php $this->msg( $inputItem['msg'] ); ?>
-                                                               </label>
-                                                       </div>
-                                               <?php
-                                               } else {
-                                                       // Not a checkbox.
-                                                       // TODO (bug 31909) support other input types, e.g. select boxes.
-                                               ?>
-                                                       <?php if ( !empty( $inputItem['msg'] ) ) { ?>
-                                                               <label for="<?php echo htmlspecialchars( $inputItem['name'] ); ?>">
-                                                                       <?php $this->msgWiki( $inputItem['msg'] ); ?>
-                                                               </label>
-                                                       <?php } ?>
-                                                       <input
-                                                               type="<?php echo htmlspecialchars( $inputItem['type'] ); ?>"
-                                                               class="mw-ui-input"
-                                                               name="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
-                                                               tabindex="<?php echo $tabIndex++; ?>"
-                                                               value="<?php echo htmlspecialchars( $inputItem['value'] ); ?>"
-                                                               id="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
-                                                       />
-                                               <?php } ?>
-                                               <?php if ( $inputItem['helptext'] !== false ) { ?>
-                                                       <div class="prefsectiontip">
-                                                               <?php $this->msgWiki( $inputItem['helptext'] ); ?>
-                                                       </div>
-                                               <?php } ?>
-                                       </div>
-                               <?php
-                               }
-                       }
-
-                       // A separate placeholder for any inserting any extrafields, e.g used by ConfirmEdit extension
-                       if ( $this->haveData( 'extrafields' ) ) {
-                               echo $this->data['extrafields'];
-                       }
-                       // skip one index.
-                       $tabIndex++;
-                       ?>
-                       <div class="mw-ui-vform-field mw-submit">
-                               <?php
-                               echo Html::submitButton(
-                                       $this->getMsg( $this->data['loggedin'] ? 'createacct-another-submit' : 'createacct-submit' ),
-                                       [
-                                               'id' => 'wpCreateaccount',
-                                               'name' => 'wpCreateaccount',
-                                               'tabindex' => $tabIndex++
-                                       ],
-                                       [
-                                               'mw-ui-block',
-                                               'mw-ui-constructive',
-                                       ]
-                               );
-                               ?>
-                       </div>
-                       <?php if ( $this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?>
-                       <?php if ( $this->haveData( 'token' ) ) { ?><input type="hidden" name="wpCreateaccountToken" value="<?php $this->text( 'token' ); ?>" /><?php } ?>
-               </form>
-               <?php if ( !wfMessage( 'signupend' )->isDisabled() ) { ?>
-                       <div id="signupend"><?php $this->html( 'signupend' ); ?></div>
-               <?php } ?>
-       </div>
-       <div class="mw-createacct-benefits-container">
-               <h2><?php $this->msg( 'createacct-benefit-heading' ); ?></h2>
-               <div class="mw-createacct-benefits-list">
-                       <?php
-                       for ( $benefitIdx = 1; $benefitIdx <= $this->data['benefitCount']; $benefitIdx++ ) {
-                               // Pass each benefit's head text (by default a number) as a parameter to the body's message for PLURAL handling.
-                               $headUnescaped = $this->getMsg( "createacct-benefit-head$benefitIdx" )->text();
-                       ?>
-                               <div class="mw-number-text <?php $this->msg( "createacct-benefit-icon$benefitIdx" ); ?>">
-                                       <h3><?php $this->msg( "createacct-benefit-head$benefitIdx" ); ?></h3>
-                                       <p><?php echo $this->getMsg( "createacct-benefit-body$benefitIdx" )->params( $headUnescaped )->escaped(); ?></p>
-                               </div>
-                       <?php } ?>
-               </div>
-       </div>
-</div>
-<?php
-
-       }
-}
diff --git a/includes/templates/Userlogin.php b/includes/templates/Userlogin.php
deleted file mode 100644 (file)
index e816b62..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-<?php
-// @codingStandardsIgnoreFile
-/**
- * HTML form for user login (since 1.22 with VForm appearance).
- *
- * 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 Templates
- * @deprecated Will be removed when AuthManager lands.
- *   The login form will be generated via HTMLForm.
- */
-
-class UserloginTemplate extends BaseTemplate {
-
-       function execute() {
-               global $wgCookieExpiration;
-               $expirationDays = ceil( $wgCookieExpiration / ( 3600 * 24 ) );
-?>
-<div class="mw-ui-container">
-       <div id="userloginprompt"><?php $this->msgWiki('loginprompt') ?></div>
-       <?php if ( $this->haveData( 'languages' ) ) { ?>
-               <div id="languagelinks">
-                       <p><?php $this->html( 'languages' ); ?></p>
-               </div>
-       <?php } ?>
-       <div id="userloginForm">
-               <form name="userlogin" class="mw-ui-vform" method="post" action="<?php $this->text( 'action' ); ?>">
-                       <?php if ( $this->data['loggedin'] ) { ?>
-                               <div class="warningbox">
-                                       <?php echo $this->getMsg( 'userlogin-loggedin' )->params( $this->data['loggedinuser'] )->parse(); ?>
-                               </div>
-                       <?php } ?>
-                       <section class="mw-form-header">
-                               <?php $this->html( 'header' ); /* extensions such as ConfirmEdit add form HTML here */ ?>
-                       </section>
-
-                       <?php if ( $this->data['message'] ) { ?>
-                               <div class="<?php $this->text( 'messagetype' ); ?>box">
-                                       <?php if ( $this->data['messagetype'] == 'error' ) { ?>
-                                               <strong><?php $this->msg( 'loginerror' ); ?></strong>
-                                               <br />
-                                       <?php } ?>
-                                       <?php $this->html( 'message' ); ?>
-                               </div>
-                       <?php } ?>
-
-                       <?php if ( $this->data['formheader'] ) { ?>
-                               <div class="mw-form-formheader">
-                                       <?php $this->html( 'formheader' ); /* extensions such as MobileFrontend add HTML here */ ?>
-                               </div>
-                       <?php } ?>
-                       <div class="mw-ui-vform-field">
-                               <label for="wpName1">
-                                       <?php
-                                       $this->msg( 'userlogin-yourname' );
-
-                                       if ( $this->data['secureLoginUrl'] ) {
-                                               echo Html::element( 'a', [
-                                                       'href' => $this->data['secureLoginUrl'],
-                                                       'class' => 'mw-ui-flush-right mw-secure',
-                                               ], $this->getMsg( 'userlogin-signwithsecure' )->text() );
-                                       }
-                                       ?>
-                               </label>
-                               <?php
-                               echo Html::input( 'wpName', $this->data['name'], 'text', [
-                                       'class' => 'loginText mw-ui-input',
-                                       'id' => 'wpName1',
-                                       'tabindex' => '1',
-                                       // 'required' is blacklisted for now in Html.php due to browser issues.
-                                       // Keeping here in case that changes.
-                                       'required' => true,
-                                       // Set focus to this field if it's blank.
-                                       'autofocus' => !$this->data['name'],
-                                       'placeholder' => $this->getMsg( 'userlogin-yourname-ph' )->text()
-                               ] );
-                               ?>
-                       </div>
-
-                       <div class="mw-ui-vform-field">
-                               <label for="wpPassword1">
-                                       <?php
-                                       $this->msg( 'userlogin-yourpassword' );
-                                       ?>
-                               </label>
-                               <?php
-                               echo Html::input( 'wpPassword', null, 'password', [
-                                       'class' => 'loginPassword mw-ui-input',
-                                       'id' => 'wpPassword1',
-                                       'tabindex' => '2',
-                                       // Set focus to this field if username is filled in.
-                                       'autofocus' => (bool)$this->data['name'],
-                                       'placeholder' => $this->getMsg( 'userlogin-yourpassword-ph' )->text()
-                               ] );
-                               ?>
-                       </div>
-
-                       <?php
-                       if ( isset( $this->data['usedomain'] ) && $this->data['usedomain'] ) {
-                               $select = new XmlSelect( 'wpDomain', false, $this->data['domain'] );
-                               $select->setAttribute( 'tabindex', 3 );
-                               foreach ( $this->data['domainnames'] as $dom ) {
-                                       $select->addOption( $dom );
-                               }
-                       ?>
-                               <div class="mw-ui-vform-field" id="mw-user-domain-section">
-                                       <label for="wpDomain"><?php $this->msg( 'yourdomainname' ); ?></label>
-                                       <?php echo $select->getHTML(); ?>
-                               </div>
-                       <?php } ?>
-
-                       <?php
-                       if ( $this->haveData( 'extrafields' ) ) {
-                               echo $this->data['extrafields'];
-                       }
-                       ?>
-
-                       <div class="mw-ui-vform-field">
-                               <?php if ( $this->data['canremember'] ) { ?>
-                                       <div class="mw-ui-checkbox">
-                                               <input name="wpRemember" type="checkbox" value="1" id="wpRemember" tabindex="4"
-                                                       <?php if ( $this->data['remember'] ) {
-                                                               echo 'checked="checked"';
-                                                       } ?>
-                                               ><label for="wpRemember">
-                                                       <?php echo $this->getMsg( 'userlogin-remembermypassword' )->numParams( $expirationDays )->escaped(); ?></label>
-                                       </div>
-                               <?php } ?>
-                       </div>
-
-                       <div class="mw-ui-vform-field">
-                               <?php
-                               $attrs = [
-                                       'id' => 'wpLoginAttempt',
-                                       'name' => 'wpLoginAttempt',
-                                       'tabindex' => '6',
-                               ];
-                               $modifiers = [
-                                       'mw-ui-progressive',
-                               ];
-                               echo Html::submitButton( $this->getMsg( 'pt-login-button' )->text(), $attrs, $modifiers );
-                               ?>
-                       </div>
-
-                       <div class="mw-ui-vform-field mw-form-related-link-container" id="mw-userlogin-help">
-                               <?php
-                               echo Html::element(
-                                       'a',
-                                       [
-                                               'href' => Skin::makeInternalOrExternalUrl(
-                                                       wfMessage( 'helplogin-url' )->inContentLanguage()->text()
-                                               ),
-                                       ],
-                                       $this->getMsg( 'userlogin-helplink2' )->text()
-                               );
-                               ?>
-                       </div>
-                       <?php
-
-                       if ( $this->data['useemail'] && $this->data['canreset'] && $this->data['resetlink'] === true ) {
-                               echo Html::rawElement(
-                                       'div',
-                                       [
-                                               'class' => 'mw-ui-vform-field mw-form-related-link-container',
-                                       ],
-                                       Linker::link(
-                                               SpecialPage::getTitleFor( 'PasswordReset' ),
-                                               $this->getMsg( 'userlogin-resetpassword-link' )->escaped()
-                                       )
-                               );
-                       }
-
-                       if ( $this->haveData( 'createOrLoginHref' ) ) {
-                               if ( $this->data['loggedin'] ) { ?>
-                                       <div class="mw-form-related-link-container mw-ui-vform-field">
-                                               <a href="<?php $this->text( 'createOrLoginHref' ); ?>" id="mw-createaccount-join" tabindex="7"><?php $this->msg( 'userlogin-createanother' ); ?></a>
-                                       </div>
-                               <?php } else { ?>
-                                       <div id="mw-createaccount-cta" class="mw-ui-vform-field">
-                                               <?php $this->msg( 'userlogin-noaccount' ); ?><a href="<?php $this->text( 'createOrLoginHref' ); ?>" id="mw-createaccount-join" tabindex="7" class="mw-ui-button"><?php $this->msg( 'userlogin-joinproject' ); ?></a>
-                                       </div>
-                               <?php
-                               }
-                       }
-
-                       // Hidden fields
-                       $fields = '';
-                       if ( $this->haveData( 'uselang' ) ) {
-                               $fields .= Html::hidden( 'uselang', $this->data['uselang'] );
-                       }
-                       if ( $this->haveData( 'token' ) ) {
-                               $fields .= Html::hidden( 'wpLoginToken', $this->data['token'] );
-                       }
-                       if ( $this->data['cansecurelogin'] ) {
-                               $fields .= Html::hidden( 'wpForceHttps', $this->data['stickhttps'] );
-                       }
-                       if ( $this->data['cansecurelogin'] && $this->haveData( 'fromhttp' ) ) {
-                               $fields .= Html::hidden( 'wpFromhttp', $this->data['fromhttp'] );
-                       }
-                       echo $fields;
-
-                       ?>
-               </form>
-       </div>
-</div>
-<?php
-
-       }
-}
diff --git a/includes/tidy/Balancer.php b/includes/tidy/Balancer.php
new file mode 100644 (file)
index 0000000..666f3f9
--- /dev/null
@@ -0,0 +1,3528 @@
+<?php
+/**
+ * An implementation of the tree building portion of the HTML5 parsing
+ * spec.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Parser
+ * @since 1.27
+ * @author C. Scott Ananian, 2016
+ */
+namespace MediaWiki\Tidy;
+
+use Wikimedia\Assert\Assert;
+use Wikimedia\Assert\ParameterAssertionException;
+use \ExplodeIterator;
+use \IteratorAggregate;
+use \ReverseArrayIterator;
+use \Sanitizer;
+
+// A note for future librarization[1] -- this file is a good candidate
+// for splitting into an independent library, except that it is currently
+// highly optimized for MediaWiki use.  It only implements the portions
+// of the HTML5 tree builder used by tags supported by MediaWiki, and
+// does not contain a true tokenizer pass, instead relying on
+// comment stripping, attribute normalization, and escaping done by
+// the MediaWiki Sanitizer.  It also deliberately avoids building
+// a true DOM in memory, instead serializing elements to an output string
+// as soon as possible (usually as soon as the tag is closed) to reduce
+// its memory footprint.
+
+// We've been gradually lifting some of these restrictions to handle
+// non-sanitized output generated by extensions, but we shortcut the tokenizer
+// for speed (primarily by splitting on `<`) and so rely on syntactic
+// well-formedness.
+
+// On the other hand, I've been pretty careful to note with comments in the
+// code the places where this implementation omits features of the spec or
+// depends on the MediaWiki Sanitizer.  Perhaps in the future we'll want to
+// implement the missing pieces and make this a standalone PHP HTML5 parser.
+// In order to do so, some sort of MediaWiki-specific API will need
+// to be added to (a) allow the Balancer to bypass the tokenizer,
+// and (b) support on-the-fly flattening instead of DOM node creation.
+
+// [1]: https://www.mediawiki.org/wiki/Library_infrastructure_for_MediaWiki
+
+/**
+ * Utility constants and sets for the HTML5 tree building algorithm.
+ * Sets are associative arrays indexed first by namespace and then by
+ * lower-cased tag name.
+ *
+ * @ingroup Parser
+ * @since 1.27
+ */
+class BalanceSets {
+       const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
+       const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
+       const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
+
+       public static $unsupportedSet = [
+               self::HTML_NAMESPACE => [
+                       'html' => true, 'head' => true, 'body' => true, 'frameset' => true,
+                       'frame' => true,
+                       'plaintext' => true, 'isindex' => true,
+                       'xmp' => true, 'iframe' => true, 'noembed' => true,
+                       'noscript' => true, 'script' => true,
+                       'title' => true
+               ]
+       ];
+
+       public static $emptyElementSet = [
+               self::HTML_NAMESPACE => [
+                       'area' => true, 'base' => true, 'basefont' => true,
+                       'bgsound' => true, 'br' => true, 'col' => true, 'command' => true,
+                       'embed' => true, 'frame' => true, 'hr' => true, 'img' => true,
+                       'input' => true, 'keygen' => true, 'link' => true, 'meta' => true,
+                       'param' => true, 'source' => true, 'track' => true, 'wbr' => true
+               ]
+       ];
+
+       public static $extraLinefeedSet = [
+               self::HTML_NAMESPACE => [
+                       'pre' => true, 'textarea' => true, 'listing' => true,
+               ]
+       ];
+
+       public static $headingSet = [
+               self::HTML_NAMESPACE => [
+                       'h1' => true, 'h2' => true, 'h3' => true,
+                       'h4' => true, 'h5' => true, 'h6' => true
+               ]
+       ];
+
+       public static $specialSet = [
+               self::HTML_NAMESPACE => [
+                       'address' => true, 'applet' => true, 'area' => true,
+                       'article' => true, 'aside' => true, 'base' => true,
+                       'basefont' => true, 'bgsound' => true, 'blockquote' => true,
+                       'body' => true, 'br' => true, 'button' => true, 'caption' => true,
+                       'center' => true, 'col' => true, 'colgroup' => true, 'dd' => true,
+                       'details' => true, 'dir' => true, 'div' => true, 'dl' => true,
+                       'dt' => true, 'embed' => true, 'fieldset' => true,
+                       'figcaption' => true, 'figure' => true, 'footer' => true,
+                       'form' => true, 'frame' => true, 'frameset' => true, 'h1' => true,
+                       'h2' => true, 'h3' => true, 'h4' => true, 'h5' => true,
+                       'h6' => true, 'head' => true, 'header' => true, 'hgroup' => true,
+                       'hr' => true, 'html' => true, 'iframe' => true, 'img' => true,
+                       'input' => true, 'isindex' => true, 'li' => true, 'link' => true,
+                       'listing' => true, 'main' => true, 'marquee' => true,
+                       'menu' => true, 'menuitem' => true, 'meta' => true, 'nav' => true,
+                       'noembed' => true, 'noframes' => true, 'noscript' => true,
+                       'object' => true, 'ol' => true, 'p' => true, 'param' => true,
+                       'plaintext' => true, 'pre' => true, 'script' => true,
+                       'section' => true, 'select' => true, 'source' => true,
+                       'style' => true, 'summary' => true, 'table' => true,
+                       'tbody' => true, 'td' => true, 'template' => true,
+                       'textarea' => true, 'tfoot' => true, 'th' => true, 'thead' => true,
+                       'title' => true, 'tr' => true, 'track' => true, 'ul' => true,
+                       'wbr' => true, 'xmp' => true
+               ],
+               self::SVG_NAMESPACE => [
+                       'foreignobject' => true, 'desc' => true, 'title' => true
+               ],
+               self::MATHML_NAMESPACE => [
+                       'mi' => true, 'mo' => true, 'mn' => true, 'ms' => true,
+                       'mtext' => true, 'annotation-xml' => true
+               ]
+       ];
+
+       public static $addressDivPSet = [
+               self::HTML_NAMESPACE => [
+                       'address' => true, 'div' => true, 'p' => true
+               ]
+       ];
+
+       public static $tableSectionRowSet = [
+               self::HTML_NAMESPACE => [
+                       'table' => true, 'thead' => true, 'tbody' => true,
+                       'tfoot' => true, 'tr' => true
+               ]
+       ];
+
+       public static $impliedEndTagsSet = [
+               self::HTML_NAMESPACE => [
+                       'dd' => true, 'dt' => true, 'li' => true, 'optgroup' => true,
+                       'option' => true, 'p' => true, 'rb' => true, 'rp' => true,
+                       'rt' => true, 'rtc' => true
+               ]
+       ];
+
+       public static $thoroughImpliedEndTagsSet = [
+               self::HTML_NAMESPACE => [
+                       'caption' => true, 'colgroup' => true, 'dd' => true, 'dt' => true,
+                       'li' => true, 'optgroup' => true, 'option' => true, 'p' => true,
+                       'rb' => true, 'rp' => true, 'rt' => true, 'rtc' => true,
+                       'tbody' => true, 'td' => true, 'tfoot' => true, 'th' => true,
+                       'thead' => true, 'tr' => true
+               ]
+       ];
+
+       public static $tableCellSet = [
+               self::HTML_NAMESPACE => [
+                       'td' => true, 'th' => true
+               ]
+       ];
+       public static $tableContextSet = [
+               self::HTML_NAMESPACE => [
+                       'table' => true, 'template' => true, 'html' => true
+               ]
+       ];
+
+       public static $tableBodyContextSet = [
+               self::HTML_NAMESPACE => [
+                       'tbody' => true, 'tfoot' => true, 'thead' => true,
+                       'template' => true, 'html' => true
+               ]
+       ];
+
+       public static $tableRowContextSet = [
+               self::HTML_NAMESPACE => [
+                       'tr' => true, 'template' => true, 'html' => true
+               ]
+       ];
+
+       // See https://html.spec.whatwg.org/multipage/forms.html#form-associated-element
+       public static $formAssociatedSet = [
+               self::HTML_NAMESPACE => [
+                       'button' => true, 'fieldset' => true, 'input' => true,
+                       'keygen' => true, 'object' => true, 'output' => true,
+                       'select' => true, 'textarea' => true, 'img' => true
+               ]
+       ];
+
+       public static $inScopeSet = [
+               self::HTML_NAMESPACE => [
+                       'applet' => true, 'caption' => true, 'html' => true,
+                       'marquee' => true, 'object' => true,
+                       'table' => true, 'td' => true, 'template' => true,
+                       'th' => true
+               ],
+               self::SVG_NAMESPACE => [
+                       'foreignobject' => true, 'desc' => true, 'title' => true
+               ],
+               self::MATHML_NAMESPACE => [
+                       'mi' => true, 'mo' => true, 'mn' => true, 'ms' => true,
+                       'mtext' => true, 'annotation-xml' => true
+               ]
+       ];
+
+       private static $inListItemScopeSet = null;
+       public static function inListItemScopeSet() {
+               if ( self::$inListItemScopeSet === null ) {
+                       self::$inListItemScopeSet = self::$inScopeSet;
+                       self::$inListItemScopeSet[self::HTML_NAMESPACE]['ol'] = true;
+                       self::$inListItemScopeSet[self::HTML_NAMESPACE]['ul'] = true;
+               }
+               return self::$inListItemScopeSet;
+       }
+
+       private static $inButtonScopeSet = null;
+       public static function inButtonScopeSet() {
+               if ( self::$inButtonScopeSet === null ) {
+                       self::$inButtonScopeSet = self::$inScopeSet;
+                       self::$inButtonScopeSet[self::HTML_NAMESPACE]['button'] = true;
+               }
+               return self::$inButtonScopeSet;
+       }
+
+       public static $inTableScopeSet = [
+               self::HTML_NAMESPACE => [
+                       'html' => true, 'table' => true, 'template' => true
+               ]
+       ];
+
+       public static $inInvertedSelectScopeSet = [
+               self::HTML_NAMESPACE => [
+                       'option' => true, 'optgroup' => true
+               ]
+       ];
+
+       public static $mathmlTextIntegrationPointSet = [
+               self::MATHML_NAMESPACE => [
+                       'mi' => true, 'mo' => true, 'mn' => true, 'ms' => true,
+                       'mtext' => true
+               ]
+       ];
+
+       public static $htmlIntegrationPointSet = [
+               self::SVG_NAMESPACE => [
+                       'foreignobject' => true,
+                       'desc' => true,
+                       'title' => true
+               ]
+       ];
+
+       // For tidy compatibility.
+       public static $tidyPWrapSet = [
+               self::HTML_NAMESPACE => [
+                       'body' => true, 'blockquote' => true,
+                       // We parse with <body> as the fragment context, but the top-level
+                       // element on the stack is actually <html>.  We could use the
+                       // "adjusted current node" everywhere to work around this, but it's
+                       // easier just to add <html> to the p-wrap set.
+                       'html' => true,
+               ],
+       ];
+       public static $tidyInlineSet = [
+               self::HTML_NAMESPACE => [
+                       'a' => true, 'abbr' => true, 'acronym' => true, 'applet' => true,
+                       'b' => true, 'basefont' => true, 'bdo' => true, 'big' => true,
+                       'br' => true, 'button' => true, 'cite' => true, 'code' => true,
+                       'dfn' => true, 'em' => true, 'font' => true, 'i' => true,
+                       'iframe' => true, 'img' => true, 'input' => true, 'kbd' => true,
+                       'label' => true, 'legend' => true, 'map' => true, 'object' => true,
+                       'param' => true, 'q' => true, 'rb' => true, 'rbc' => true,
+                       'rp' => true, 'rt' => true, 'rtc' => true, 'ruby' => true,
+                       's' => true, 'samp' => true, 'select' => true, 'small' => true,
+                       'span' => true, 'strike' => true, 'strong' => true, 'sub' => true,
+                       'sup' => true, 'textarea' => true, 'tt' => true, 'u' => true,
+                       'var' => true,
+               ],
+       ];
+}
+
+/**
+ * A BalanceElement is a simplified version of a DOM Node.  The main
+ * difference is that we only keep BalanceElements around for nodes
+ * currently on the BalanceStack of open elements.  As soon as an
+ * element is closed, with some minor exceptions relating to the
+ * tree builder "adoption agency algorithm", the element and all its
+ * children are serialized to a string using the flatten() method.
+ * This keeps our memory usage low.
+ *
+ * @ingroup Parser
+ * @since 1.27
+ */
+class BalanceElement {
+       /**
+        * The namespace of the element.
+        * @var string $namespaceURI
+        */
+       public $namespaceURI;
+       /**
+        * The lower-cased name of the element.
+        * @var string $localName
+        */
+       public $localName;
+       /**
+        * Attributes for the element, in array form
+        * @var array $attribs
+        */
+       public $attribs;
+
+       /**
+        * Parent of this element, or the string "flat" if this element has
+        * already been flattened into its parent.
+        * @var string|null $parent
+        */
+       public $parent;
+
+       /**
+        * An array of children of this element.  Typically only the last
+        * child will be an actual BalanceElement object; the rest will
+        * be strings, representing either text nodes or flattened
+        * BalanceElement objects.
+        * @var array $children
+        */
+       public $children;
+
+       /**
+        * A unique string identifier for Noah's Ark purposes, lazy initialized
+        */
+       private $noahKey;
+
+       /**
+        * The next active formatting element in the list, or null if this is the
+        * end of the AFE list or if the element is not in the AFE list.
+        */
+       public $nextAFE;
+
+       /**
+        * The previous active formatting element in the list, or null if this is
+        * the start of the list or if the element is not in the AFE list.
+        */
+       public $prevAFE;
+
+       /**
+        * The next element in the Noah's Ark species bucket.
+        */
+       public $nextNoah;
+
+       /**
+        * Make a new BalanceElement corresponding to the HTML DOM Element
+        * with the given localname, namespace, and attributes.
+        *
+        * @param string $namespaceURI The namespace of the element.
+        * @param string $localName The lowercased name of the tag.
+        * @param array $attribs Attributes of the element
+        */
+       public function __construct( $namespaceURI, $localName, array $attribs ) {
+               $this->localName = $localName;
+               $this->namespaceURI = $namespaceURI;
+               $this->attribs = $attribs;
+               $this->contents = '';
+               $this->parent = null;
+               $this->children = [];
+       }
+
+       /**
+        * Remove the given child from this element.
+        * @param BalanceElement $elt
+        */
+       private function removeChild( BalanceElement $elt ) {
+               Assert::precondition(
+                       $this->parent !== 'flat', "Can't removeChild after flattening $this"
+               );
+               Assert::parameter(
+                       $elt->parent === $this, 'elt', 'must have $this as a parent'
+               );
+               $idx = array_search( $elt, $this->children, true );
+               Assert::parameter( $idx !== false, '$elt', 'must be a child of $this' );
+               $elt->parent = null;
+               array_splice( $this->children, $idx, 1 );
+       }
+
+       /**
+        * Find $a in the list of children and insert $b before it.
+        * @param BalanceElement $a
+        * @param BalanceElement|string $b
+        */
+       public function insertBefore( BalanceElement $a, $b ) {
+               Assert::precondition(
+                       $this->parent !== 'flat', "Can't insertBefore after flattening."
+               );
+               $idx = array_search( $a, $this->children, true );
+               Assert::parameter( $idx !== false, '$a', 'must be a child of $this' );
+               if ( is_string( $b ) ) {
+                       array_splice( $this->children, $idx, 0, [ $b ] );
+               } else {
+                       Assert::parameter( $b->parent !== 'flat', '$b', "Can't be flat" );
+                       if ( $b->parent !== null ) {
+                               $b->parent->removeChild( $b );
+                       }
+                       array_splice( $this->children, $idx, 0, [ $b ] );
+                       $b->parent = $this;
+               }
+       }
+
+       /**
+        * Append $elt to the end of the list of children.
+        * @param BalanceElement|string $elt
+        */
+       public function appendChild( $elt ) {
+               Assert::precondition(
+                       $this->parent !== 'flat', "Can't appendChild after flattening."
+               );
+               if ( is_string( $elt ) ) {
+                       array_push( $this->children, $elt );
+                       return;
+               }
+               // Remove $elt from parent, if it had one.
+               if ( $elt->parent !== null ) {
+                       $elt->parent->removeChild( $elt );
+               }
+               array_push( $this->children, $elt );
+               $elt->parent = $this;
+       }
+
+       /**
+        * Transfer all of the children of $elt to $this.
+        * @param BalanceElement $elt
+        */
+       public function adoptChildren( BalanceElement $elt ) {
+               Assert::precondition(
+                       $elt->parent !== 'flat', "Can't adoptChildren after flattening."
+               );
+               foreach ( $elt->children as $child ) {
+                       if ( !is_string( $child ) ) {
+                               // This is an optimization which avoids an O(n^2) set of
+                               // array_splice operations.
+                               $child->parent = null;
+                       }
+                       $this->appendChild( $child );
+               }
+               $elt->children = [];
+       }
+
+       /**
+        * Flatten this node and all of its children into a string, as specified
+        * by the HTML serialization specification, and replace this node
+        * in its parent by that string.
+        *
+        * @param array $config Balancer configuration; see Balancer::__construct().
+        *
+        * @see __toString()
+        */
+       public function flatten( array $config ) {
+               Assert::parameter( $this->parent !== null, '$this', 'must be a child' );
+               Assert::parameter( $this->parent !== 'flat', '$this', 'already flat' );
+               $idx = array_search( $this, $this->parent->children, true );
+               Assert::parameter(
+                       $idx !== false, '$this', 'must be a child of its parent'
+               );
+               $tidyCompat = $config['tidyCompat'];
+               if ( $tidyCompat ) {
+                       $blank = true;
+                       foreach ( $this->children as $elt ) {
+                               if ( !is_string( $elt ) ) {
+                                       $elt = $elt->flatten( $config );
+                               }
+                               if ( $blank && preg_match( '/[^\t\n\f\r ]/', $elt ) ) {
+                                       $blank = false;
+                               }
+                       }
+                       if ( $this->isHtmlNamed( 'mw:p-wrap' ) ) {
+                               $this->localName = 'p';
+                       } elseif ( $blank ) {
+                               // Add 'mw-empty-elt' class so elements can be hidden via CSS
+                               // for compatibility with legacy tidy.
+                               if ( !count( $this->attribs ) &&
+                                       ( $this->localName === 'tr' || $this->localName === 'li' )
+                               ) {
+                                       $this->attribs = [ 'class' => "mw-empty-elt" ];
+                               }
+                               $blank = false;
+                       }
+                       $flat = $blank ? '' : "{$this}";
+               } else {
+                       $flat = "{$this}";
+               }
+               $this->parent->children[$idx] = $flat;
+               $this->parent = 'flat'; // for assertion checking
+               return $flat;
+       }
+
+       /**
+        * Serialize this node and all of its children to a string, as specified
+        * by the HTML serialization specification.
+        *
+        * @return string The serialization of the BalanceElement
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#serialising-html-fragments
+        */
+       public function __toString() {
+               $encAttribs = '';
+               foreach ( $this->attribs as $name => $value ) {
+                       $encValue = Sanitizer::encodeAttribute( $value );
+                       $encAttribs .= " $name=\"$encValue\"";
+               }
+               if ( !$this->isA( BalanceSets::$emptyElementSet ) ) {
+                       $out = "<{$this->localName}{$encAttribs}>";
+                       $len = strlen( $out );
+                       // flatten children
+                       foreach ( $this->children as $elt ) {
+                               $out .= "{$elt}";
+                       }
+                       $out .= "</{$this->localName}>";
+                       if (
+                               $this->isA( BalanceSets::$extraLinefeedSet ) &&
+                               $out[$len] === "\n"
+                       ) {
+                               // Double the linefeed after pre/listing/textarea
+                               // according to the HTML5 fragment serialization algorithm.
+                               $out = substr( $out, 0, $len + 1 ) .
+                                       substr( $out, $len );
+                       }
+               } else {
+                       $out = "<{$this->localName}{$encAttribs} />";
+                       Assert::invariant(
+                               count( $this->children ) === 0,
+                               "Empty elements shouldn't have children."
+                       );
+               }
+               return $out;
+       }
+
+       // Utility functions on BalanceElements.
+
+       /**
+        * Determine if $this represents a specific HTML tag, is a member of
+        * a tag set, or is equal to another BalanceElement.
+        *
+        * @param BalanceElement|array|string $set The target BalanceElement,
+        *   set (from the BalanceSets class), or string (HTML tag name).
+        * @return bool
+        */
+       public function isA( $set ) {
+               if ( $set instanceof BalanceElement ) {
+                       return $this === $set;
+               } elseif ( is_array( $set ) ) {
+                       return isset( $set[$this->namespaceURI] ) &&
+                               isset( $set[$this->namespaceURI][$this->localName] );
+               } else {
+                       // assume this is an HTML element name.
+                       return $this->isHtml() && $this->localName === $set;
+               }
+       }
+
+       /**
+        * Determine if this element is an HTML element with the specified name
+        * @param string $tagName
+        * @return bool
+        */
+       public function isHtmlNamed( $tagName ) {
+               return $this->namespaceURI === BalanceSets::HTML_NAMESPACE
+                       && $this->localName === $tagName;
+       }
+
+       /**
+        * Determine if $this represents an element in the HTML namespace.
+        *
+        * @return bool
+        */
+       public function isHtml() {
+               return $this->namespaceURI === BalanceSets::HTML_NAMESPACE;
+       }
+
+       /**
+        * Determine if $this represents a MathML text integration point,
+        * as defined in the HTML5 specification.
+        *
+        * @return bool
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#mathml-text-integration-point
+        */
+       public function isMathmlTextIntegrationPoint() {
+               return $this->isA( BalanceSets::$mathmlTextIntegrationPointSet );
+       }
+
+       /**
+        * Determine if $this represents an HTML integration point,
+        * as defined in the HTML5 specification.
+        *
+        * @return bool
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#html-integration-point
+        */
+       public function isHtmlIntegrationPoint() {
+               if ( $this->isA( BalanceSets::$htmlIntegrationPointSet ) ) {
+                       return true;
+               }
+               if (
+                       $this->namespaceURI === BalanceSets::MATHML_NAMESPACE &&
+                       $this->localName === 'annotation-xml' &&
+                       isset( $this->attribs['encoding'] ) &&
+                       ( strcasecmp( $this->attribs['encoding'], 'text/html' ) == 0 ||
+                       strcasecmp( $this->attribs['encoding'], 'application/xhtml+xml' ) == 0 )
+               ) {
+                       return true;
+               }
+               return false;
+       }
+
+       /**
+        * Get a string key for the Noah's Ark algorithm
+        */
+       public function getNoahKey() {
+               if ( $this->noahKey === null ) {
+                       $attribs = $this->attribs;
+                       ksort( $attribs );
+                       $this->noahKey = serialize( [ $this->namespaceURI, $this->localName, $attribs ] );
+               }
+               return $this->noahKey;
+       }
+}
+
+/**
+ * The "stack of open elements" as defined in the HTML5 tree builder
+ * spec.  This contains methods to ensure that content (start tags, text)
+ * are inserted at the correct place in the output string, and to
+ * flatten BalanceElements are they are closed to avoid holding onto
+ * a complete DOM tree for the document in memory.
+ *
+ * The stack defines a PHP iterator to traverse it in "reverse order",
+ * that is, the most-recently-added element is visited first in a
+ * foreach loop.
+ *
+ * @ingroup Parser
+ * @since 1.27
+ * @see https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements
+ */
+class BalanceStack implements IteratorAggregate {
+       /**
+        * Backing storage for the stack.
+        * @var array $elements
+        */
+       private $elements = [];
+       /**
+        * Foster parent mode determines how nodes are inserted into the
+        * stack.
+        * @var bool $fosterParentMode
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#foster-parent
+        */
+       public $fosterParentMode = false;
+       /**
+        * Configuration options governing flattening.
+        * @var array $config
+        * @see Balancer::__construct()
+        */
+       private $config;
+       /**
+        * Reference to the current element
+        */
+       public $currentNode;
+
+       /**
+        * Create a new BalanceStack with a single BalanceElement on it,
+        * representing the root &lt;html&gt; node.
+        * @param array $config Balancer configuration; see Balancer::_construct().
+        */
+       public function __construct( array $config ) {
+               // always a root <html> element on the stack
+               array_push(
+                       $this->elements,
+                       new BalanceElement( BalanceSets::HTML_NAMESPACE, 'html', [] )
+               );
+               $this->currentNode = $this->elements[0];
+               $this->config = $config;
+       }
+
+       /**
+        * Return a string representing the output of the tree builder:
+        * all the children of the root &lt;html&gt; node.
+        * @return string
+        */
+       public function getOutput() {
+               // Don't include the outer '<html>....</html>'
+               $out = '';
+               foreach ( $this->elements[0]->children as $elt ) {
+                       $out .= is_string( $elt ) ? $elt :
+                               $elt->flatten( $this->config );
+               }
+               return $out;
+       }
+
+       /**
+        * Insert a comment at the appropriate place for inserting a node.
+        * @param string $value Content of the comment.
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#insert-a-comment
+        */
+       public function insertComment( $value ) {
+               // Just another type of text node, except for tidy p-wrapping.
+               return $this->insertText( '<!--' . $value . '-->', true );
+       }
+
+       /**
+        * Insert text at the appropriate place for inserting a node.
+        * @param string $value
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#appropriate-place-for-inserting-a-node
+        */
+       public function insertText( $value, $isComment = false ) {
+               if (
+                       $this->fosterParentMode &&
+                       $this->currentNode->isA( BalanceSets::$tableSectionRowSet )
+               ) {
+                       $this->fosterParent( $value );
+               } elseif (
+                       $this->config['tidyCompat'] && !$isComment &&
+                       $this->currentNode->isA( BalanceSets::$tidyPWrapSet )
+               ) {
+                       $this->insertHTMLELement( 'mw:p-wrap', [] );
+                       return $this->insertText( $value );
+               } else {
+                       $this->currentNode->appendChild( $value );
+               }
+       }
+
+       /**
+        * Insert a BalanceElement at the appropriate place, pushing it
+        * on to the open elements stack.
+        * @param string $namespaceURI The element namespace
+        * @param string $tag The tag name
+        * @param string $attribs Normalized attributes, as a string.
+        * @return BalanceElement
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#insert-a-foreign-element
+        */
+       public function insertForeignElement( $namespaceURI, $tag, $attribs ) {
+               return $this->insertElement(
+                       new BalanceElement( $namespaceURI, $tag, $attribs )
+               );
+       }
+
+       /**
+        * Insert an HTML element at the appropriate place, pushing it on to
+        * the open elements stack.
+        * @param string $tag The tag name
+        * @param string $attribs Normalized attributes, as a string.
+        * @return BalanceElement
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#insert-an-html-element
+        */
+       public function insertHTMLElement( $tag, $attribs ) {
+               return $this->insertForeignElement(
+                       BalanceSets::HTML_NAMESPACE, $tag, $attribs
+               );
+       }
+
+       /**
+        * Insert an element at the appropriate place and push it on to the
+        * open elements stack.
+        * @param BalanceElement $elt
+        * @return BalanceElement
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#appropriate-place-for-inserting-a-node
+        */
+       public function insertElement( BalanceElement $elt ) {
+               if (
+                       $this->currentNode->isHtmlNamed( 'mw:p-wrap' ) &&
+                       !$elt->isA( BalanceSets::$tidyInlineSet )
+               ) {
+                       // Tidy compatibility.
+                       $this->pop();
+               }
+               if (
+                       $this->fosterParentMode &&
+                       $this->currentNode->isA( BalanceSets::$tableSectionRowSet )
+               ) {
+                       $elt = $this->fosterParent( $elt );
+               } else {
+                       $this->currentNode->appendChild( $elt );
+               }
+               Assert::invariant( $elt->parent !== null, "$elt must be in tree" );
+               Assert::invariant( $elt->parent !== 'flat', "$elt must not have been previous flattened" );
+               array_push( $this->elements, $elt );
+               $this->currentNode = $elt;
+               return $elt;
+       }
+
+       /**
+        * Determine if the stack has $tag in scope.
+        * @param BalanceElement|array|string $tag
+        * @return bool
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
+        */
+       public function inScope( $tag ) {
+               return $this->inSpecificScope( $tag, BalanceSets::$inScopeSet );
+       }
+
+       /**
+        * Determine if the stack has $tag in button scope.
+        * @param BalanceElement|array|string $tag
+        * @return bool
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-button-scope
+        */
+       public function inButtonScope( $tag ) {
+               return $this->inSpecificScope( $tag, BalanceSets::inButtonScopeSet() );
+       }
+
+       /**
+        * Determine if the stack has $tag in list item scope.
+        * @param BalanceElement|array|string $tag
+        * @return bool
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-list-item-scope
+        */
+       public function inListItemScope( $tag ) {
+               return $this->inSpecificScope( $tag, BalanceSets::inListItemScopeSet() );
+       }
+
+       /**
+        * Determine if the stack has $tag in table scope.
+        * @param BalanceElement|array|string $tag
+        * @return bool
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-table-scope
+        */
+       public function inTableScope( $tag ) {
+               return $this->inSpecificScope( $tag, BalanceSets::$inTableScopeSet );
+       }
+
+       /**
+        * Determine if the stack has $tag in select scope.
+        * @param BalanceElement|array|string $tag
+        * @return bool
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-select-scope
+        */
+       public function inSelectScope( $tag ) {
+               // Can't use inSpecificScope to implement this, since it involves
+               // *inverting* a set of tags.  Implement manually.
+               foreach ( $this as $elt ) {
+                       if ( $elt->isA( $tag ) ) {
+                               return true;
+                       }
+                       if ( !$elt->isA( BalanceSets::$inInvertedSelectScopeSet ) ) {
+                               return false;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Determine if the stack has $tag in a specific scope, $set.
+        * @param BalanceElement|array|string $tag
+        * @param BalanceElement|array|string $set
+        * @return bool
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-the-specific-scope
+        */
+       public function inSpecificScope( $tag, $set ) {
+               foreach ( $this as $elt ) {
+                       if ( $elt->isA( $tag ) ) {
+                               return true;
+                       }
+                       if ( $elt->isA( $set ) ) {
+                               return false;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Generate implied end tags.
+        * @param string $butnot
+        * @param bool $thorough True if we should generate end tags thoroughly.
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#generate-implied-end-tags
+        */
+       public function generateImpliedEndTags( $butnot = null, $thorough = false ) {
+               $endTagSet = $thorough ?
+                       BalanceSets::$thoroughImpliedEndTagsSet :
+                       BalanceSets::$impliedEndTagsSet;
+               while ( $this->currentNode ) {
+                       if ( $butnot !== null && $this->currentNode->isHtmlNamed( $butnot ) ) {
+                               break;
+                       }
+                       if ( !$this->currentNode->isA( $endTagSet ) ) {
+                               break;
+                       }
+                       $this->pop();
+               }
+       }
+
+       /**
+        * Return the adjusted current node.
+        */
+       public function adjustedCurrentNode( $fragmentContext ) {
+               return ( $fragmentContext && count( $this->elements ) === 1 ) ?
+                       $fragmentContext : $this->currentNode;
+       }
+
+       /**
+        * Return an iterator over this stack which visits the current node
+        * first, and the root node last.
+        * @return Iterator
+        */
+       public function getIterator() {
+               return new ReverseArrayIterator( $this->elements );
+       }
+
+       /**
+        * Return the BalanceElement at the given position $idx, where
+        * position 0 represents the root element.
+        * @param int $idx
+        * @return BalanceElement
+        */
+       public function node( $idx ) {
+               return $this->elements[ $idx ];
+       }
+
+       /**
+        * Replace the element at position $idx in the BalanceStack with $elt.
+        * @param int $idx
+        * @param BalanceElement $elt
+        */
+       public function replaceAt( $idx, BalanceElement $elt ) {
+               Assert::precondition(
+                       $this->elements[$idx]->parent !== 'flat',
+                       'Replaced element should not have already been flattened.'
+               );
+               Assert::precondition(
+                       $elt->parent !== 'flat',
+                       'New element should not have already been flattened.'
+               );
+               $this->elements[$idx] = $elt;
+               if ( $idx === count( $this->elements ) - 1 ) {
+                       $this->currentNode = $elt;
+               }
+       }
+
+       /**
+        * Return the position of the given BalanceElement, set, or
+        * HTML tag name string in the BalanceStack.
+        * @param BalanceElement|array|string $tag
+        * @return int
+        */
+       public function indexOf( $tag ) {
+               for ( $i = count( $this->elements ) - 1; $i >= 0; $i-- ) {
+                       if ( $this->elements[$i]->isA( $tag ) ) {
+                               return $i;
+                       }
+               }
+               return -1;
+       }
+
+       /**
+        * Return the number of elements currently in the BalanceStack.
+        * @return int
+        */
+       public function length() {
+               return count( $this->elements );
+       }
+
+       /**
+        * Remove the current node from the BalanceStack, flattening it
+        * in the process.
+        */
+       public function pop() {
+               $elt = array_pop( $this->elements );
+               if ( count( $this->elements ) ) {
+                       $this->currentNode = $this->elements[ count( $this->elements ) - 1 ];
+               } else {
+                       $this->currentNode = null;
+               }
+               if ( !$elt->isHtmlNamed( 'mw:p-wrap' ) ) {
+                       $elt->flatten( $this->config );
+               }
+       }
+
+       /**
+        * Remove all nodes up to and including position $idx from the
+        * BalanceStack, flattening them in the process.
+        * @param int $idx
+        */
+       public function popTo( $idx ) {
+               for ( $length = count( $this->elements ); $length > $idx; $length-- ) {
+                       $this->pop();
+               }
+       }
+
+       /**
+        * Pop elements off the stack up to and including the first
+        * element with the specified HTML tagname (or matching the given
+        * set).
+        * @param BalanceElement|array|string $tag
+        */
+       public function popTag( $tag ) {
+               while ( $this->currentNode ) {
+                       if ( $this->currentNode->isA( $tag ) ) {
+                               $this->pop();
+                               break;
+                       }
+                       $this->pop();
+               }
+       }
+
+       /**
+        * Pop elements off the stack *not including* the first element
+        * in the specified set.
+        * @param BalanceElement|array|string $set
+        */
+       public function clearToContext( $set ) {
+               // Note that we don't loop to 0. Never pop the <html> elt off.
+               for ( $length = count( $this->elements ); $length > 1; $length-- ) {
+                       if ( $this->currentNode->isA( $set ) ) {
+                               break;
+                       }
+                       $this->pop();
+               }
+       }
+
+       /**
+        * Remove the given $elt from the BalanceStack, optionally
+        * flattening it in the process.
+        * @param BalanceElement $elt The element to remove.
+        * @param bool $flatten Whether to flatten the removed element.
+        */
+       public function removeElement( BalanceElement $elt, $flatten = true ) {
+               Assert::parameter(
+                       $elt->parent !== 'flat',
+                       '$elt',
+                       '$elt should not already have been flattened.'
+               );
+               Assert::parameter(
+                       $elt->parent->parent !== 'flat',
+                       '$elt',
+                       'The parent of $elt should not already have been flattened.'
+               );
+               $idx = array_search( $elt, $this->elements, true );
+               Assert::parameter( $idx !== false, '$elt', 'must be in stack' );
+               array_splice( $this->elements, $idx, 1 );
+               if ( $idx === count( $this->elements ) ) {
+                       $this->currentNode = $this->elements[$idx - 1];
+               }
+               if ( $flatten ) {
+                       // serialize $elt into its parent
+                       // otherwise, it will eventually serialize when the parent
+                       // is serialized, we just hold onto the memory for its
+                       // tree of objects a little longer.
+                       $elt->flatten( $this->config );
+               }
+               Assert::postcondition(
+                       array_search( $elt, $this->elements, true ) === false,
+                       '$elt should no longer be in open elements stack'
+               );
+       }
+
+       /**
+        * Find $a in the BalanceStack and insert $b after it.
+        * @param BalanceElement $a
+        * @param BalanceElement $b
+        */
+       public function insertAfter( BalanceElement $a, BalanceElement $b ) {
+               $idx = $this->indexOf( $a );
+               Assert::parameter( $idx !== false, '$a', 'must be in stack' );
+               if ( $idx === count( $this->elements ) - 1 ) {
+                       array_push( $this->elements, $b );
+                       $this->currentNode = $b;
+               } else {
+                       array_splice( $this->elements, $idx + 1, 0, [ $b ] );
+               }
+       }
+
+       // Fostering and adoption.
+
+       /**
+        * Foster parent the given $elt in the stack of open elements.
+        * @param BalanceElement|string $elt
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#foster-parent
+        */
+       private function fosterParent( $elt ) {
+               $lastTable = $this->indexOf( 'table' );
+               $lastTemplate = $this->indexOf( 'template' );
+               $parent = null;
+               $before = null;
+
+               if ( $lastTemplate >= 0 && ( $lastTable < 0 || $lastTemplate > $lastTable ) ) {
+                       $parent = $this->elements[$lastTemplate];
+               } elseif ( $lastTable >= 0 ) {
+                       $parent = $this->elements[$lastTable]->parent;
+                       // Assume all tables have parents, since we're not running scripts!
+                       Assert::invariant(
+                               $parent !== null, "All tables should have parents"
+                       );
+                       $before = $this->elements[$lastTable];
+               } else {
+                       $parent = $this->elements[0]; // the `html` element.
+               }
+
+               if ( $this->config['tidyCompat'] ) {
+                       if ( is_string( $elt ) ) {
+                               // We're fostering text: do we need a p-wrapper?
+                               if ( $parent->isA( BalanceSets::$tidyPWrapSet ) ) {
+                                       $this->insertHTMLElement( 'mw:p-wrap', [] );
+                                       $this->insertText( $elt );
+                                       return $elt;
+                               }
+                       } else {
+                               // We're fostering an element; do we need to merge p-wrappers?
+                               if ( $elt->isHtmlNamed( 'mw:p-wrap' ) ) {
+                                       $idx = $before ?
+                                               array_search( $before, $parent->children, true ) :
+                                               count( $parent->children );
+                                       $after = $idx > 0 ? $parent->children[$idx - 1] : '';
+                                       if (
+                                               $after instanceof BalanceElement &&
+                                               $after->isHtmlNamed( 'mw:p-wrap' )
+                                       ) {
+                                               return $after; // Re-use existing p-wrapper.
+                                       }
+                               }
+                       }
+               }
+
+               if ( $before ) {
+                       $parent->insertBefore( $before, $elt );
+               } else {
+                       $parent->appendChild( $elt );
+               }
+               return $elt;
+       }
+
+       /**
+        * Run the "adoption agency algoritm" (AAA) for the given subject
+        * tag name.
+        * @param string $tag The subject tag name.
+        * @param BalanceActiveFormattingElements $afe The current
+        *   active formatting elements list.
+        * @return true if the adoption agency algorithm "did something", false
+        *   if more processing is required by the caller.
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#adoption-agency-algorithm
+        */
+       public function adoptionAgency( $tag, $afe ) {
+               // If the current node is an HTML element whose tag name is subject,
+               // and the current node is not in the list of active formatting
+               // elements, then pop the current node off the stack of open
+               // elements and abort these steps.
+               if (
+                       $this->currentNode->isHtmlNamed( $tag ) &&
+                       !$afe->isInList( $this->currentNode )
+               ) {
+                       $this->pop();
+                       return true; // no more handling required
+               }
+
+               // Outer loop: If outer loop counter is greater than or
+               // equal to eight, then abort these steps.
+               for ( $outer = 0; $outer < 8; $outer++ ) {
+                       // Let the formatting element be the last element in the list
+                       // of active formatting elements that: is between the end of
+                       // the list and the last scope marker in the list, if any, or
+                       // the start of the list otherwise, and has the same tag name
+                       // as the token.
+                       $fmtElt = $afe->findElementByTag( $tag );
+
+                       // If there is no such node, then abort these steps and instead
+                       // act as described in the "any other end tag" entry below.
+                       if ( !$fmtElt ) {
+                               return false; // false means handle by the default case
+                       }
+
+                       // Otherwise, if there is such a node, but that node is not in
+                       // the stack of open elements, then this is a parse error;
+                       // remove the element from the list, and abort these steps.
+                       $index = $this->indexOf( $fmtElt );
+                       if ( $index < 0 ) {
+                               $afe->remove( $fmtElt );
+                               return true;   // true means no more handling required
+                       }
+
+                       // Otherwise, if there is such a node, and that node is also in
+                       // the stack of open elements, but the element is not in scope,
+                       // then this is a parse error; ignore the token, and abort
+                       // these steps.
+                       if ( !$this->inScope( $fmtElt ) ) {
+                               return true;
+                       }
+
+                       // Let the furthest block be the topmost node in the stack of
+                       // open elements that is lower in the stack than the formatting
+                       // element, and is an element in the special category. There
+                       // might not be one.
+                       $furthestBlock = null;
+                       $furthestBlockIndex = -1;
+                       $stackLength = $this->length();
+                       for ( $i = $index+1; $i < $stackLength; $i++ ) {
+                               if ( $this->node( $i )->isA( BalanceSets::$specialSet ) ) {
+                                       $furthestBlock = $this->node( $i );
+                                       $furthestBlockIndex = $i;
+                                       break;
+                               }
+                       }
+
+                       // If there is no furthest block, then the UA must skip the
+                       // subsequent steps and instead just pop all the nodes from the
+                       // bottom of the stack of open elements, from the current node
+                       // up to and including the formatting element, and remove the
+                       // formatting element from the list of active formatting
+                       // elements.
+                       if ( !$furthestBlock ) {
+                               $this->popTag( $fmtElt );
+                               $afe->remove( $fmtElt );
+                               return true;
+                       }
+
+                       // Let the common ancestor be the element immediately above
+                       // the formatting element in the stack of open elements.
+                       $ancestor = $this->node( $index-1 );
+
+                       // Let a bookmark note the position of the formatting
+                       // element in the list of active formatting elements
+                       // relative to the elements on either side of it in the
+                       // list.
+                       $BOOKMARK = new BalanceElement( '[bookmark]', '[bookmark]', [] );
+                       $afe->insertAfter( $fmtElt, $BOOKMARK );
+
+                       // Let node and last node be the furthest block.
+                       $node = $furthestBlock;
+                       $lastNode = $furthestBlock;
+                       $nodeIndex = $furthestBlockIndex;
+                       $isAFE = false;
+
+                       // Inner loop
+                       for ( $inner = 1; true; $inner++ ) {
+                               // Let node be the element immediately above node in
+                               // the stack of open elements, or if node is no longer
+                               // in the stack of open elements (e.g. because it got
+                               // removed by this algorithm), the element that was
+                               // immediately above node in the stack of open elements
+                               // before node was removed.
+                               $node = $this->node( --$nodeIndex );
+
+                               // If node is the formatting element, then go
+                               // to the next step in the overall algorithm.
+                               if ( $node === $fmtElt ) break;
+
+                               // If the inner loop counter is greater than three and node
+                               // is in the list of active formatting elements, then remove
+                               // node from the list of active formatting elements.
+                               $isAFE = $afe->isInList( $node );
+                               if ( $inner > 3 && $isAFE ) {
+                                       $afe->remove( $node );
+                                       $isAFE = false;
+                               }
+
+                               // If node is not in the list of active formatting
+                               // elements, then remove node from the stack of open
+                               // elements and then go back to the step labeled inner
+                               // loop.
+                               if ( !$isAFE ) {
+                                       // Don't flatten here, since we're about to relocate
+                                       // parts of this $node.
+                                       $this->removeElement( $node, false );
+                                       continue;
+                               }
+
+                               // Create an element for the token for which the
+                               // element node was created with common ancestor as
+                               // the intended parent, replace the entry for node
+                               // in the list of active formatting elements with an
+                               // entry for the new element, replace the entry for
+                               // node in the stack of open elements with an entry for
+                               // the new element, and let node be the new element.
+                               $newElt = new BalanceElement(
+                                       $node->namespaceURI, $node->localName, $node->attribs );
+                               $afe->replace( $node, $newElt );
+                               $this->replaceAt( $nodeIndex, $newElt );
+                               $node = $newElt;
+
+                               // If last node is the furthest block, then move the
+                               // aforementioned bookmark to be immediately after the
+                               // new node in the list of active formatting elements.
+                               if ( $lastNode === $furthestBlock ) {
+                                       $afe->remove( $BOOKMARK );
+                                       $afe->insertAfter( $newElt, $BOOKMARK );
+                               }
+
+                               // Insert last node into node, first removing it from
+                               // its previous parent node if any.
+                               $node->appendChild( $lastNode );
+
+                               // Let last node be node.
+                               $lastNode = $node;
+                       }
+
+                       // If the common ancestor node is a table, tbody, tfoot,
+                       // thead, or tr element, then, foster parent whatever last
+                       // node ended up being in the previous step, first removing
+                       // it from its previous parent node if any.
+                       if (
+                               $this->fosterParentMode &&
+                               $ancestor->isA( BalanceSets::$tableSectionRowSet )
+                       ) {
+                               $this->fosterParent( $lastNode );
+                       } else {
+                               // Otherwise, append whatever last node ended up being in
+                               // the previous step to the common ancestor node, first
+                               // removing it from its previous parent node if any.
+                               $ancestor->appendChild( $lastNode );
+                       }
+
+                       // Create an element for the token for which the
+                       // formatting element was created, with furthest block
+                       // as the intended parent.
+                       $newElt2 = new BalanceElement(
+                               $fmtElt->namespaceURI, $fmtElt->localName, $fmtElt->attribs );
+
+                       // Take all of the child nodes of the furthest block and
+                       // append them to the element created in the last step.
+                       $newElt2->adoptChildren( $furthestBlock );
+
+                       // Append that new element to the furthest block.
+                       $furthestBlock->appendChild( $newElt2 );
+
+                       // Remove the formatting element from the list of active
+                       // formatting elements, and insert the new element into the
+                       // list of active formatting elements at the position of
+                       // the aforementioned bookmark.
+                       $afe->remove( $fmtElt );
+                       $afe->replace( $BOOKMARK, $newElt2 );
+
+                       // Remove the formatting element from the stack of open
+                       // elements, and insert the new element into the stack of
+                       // open elements immediately below the position of the
+                       // furthest block in that stack.
+                       $this->removeElement( $fmtElt );
+                       $this->insertAfter( $furthestBlock, $newElt2 );
+               }
+
+               return true;
+       }
+
+       /**
+        * Return the contents of the open elements stack as a string for
+        * debugging.
+        * @return string
+        */
+       public function __toString() {
+               $r = [];
+               foreach ( $this->elements as $elt ) {
+                       array_push( $r, $elt->localName );
+               }
+               return implode( $r, ' ' );
+       }
+}
+
+/**
+ * A pseudo-element used as a marker in the list of active formatting elements
+ *
+ * @ingroup Parser
+ * @since 1.27
+ */
+class BalanceMarker {
+       public $nextAFE;
+       public $prevAFE;
+}
+
+/**
+ * The list of active formatting elements, which is used to handle
+ * mis-nested formatting element tags in the HTML5 tree builder
+ * specification.
+ *
+ * @ingroup Parser
+ * @since 1.27
+ * @see https://html.spec.whatwg.org/multipage/syntax.html#list-of-active-formatting-elements
+ */
+class BalanceActiveFormattingElements {
+       /** The last (most recent) element in the list */
+       private $tail;
+
+       /** The first (least recent) element in the list */
+       private $head;
+
+       /**
+        * An array of arrays representing the population of elements in each bucket
+        * according to the Noah's Ark clause. The outer array is stack-like, with each
+        * integer-indexed element representing a segment of the list, bounded by
+        * markers. The first element represents the segment of the list before the
+        * first marker.
+        *
+        * The inner arrays are indexed by "Noah key", which is a string which uniquely
+        * identifies each bucket according to the rules in the spec. The value in
+        * the inner array is the first (least recently inserted) element in the bucket,
+        * and subsequent members of the bucket can be found by iterating through the
+        * singly-linked list via $node->nextNoah.
+        *
+        * This is optimised for the most common case of inserting into a bucket
+        * with zero members, and deleting a bucket containing one member. In the
+        * worst case, iteration through the list is still O(1) in the document
+        * size, since each bucket can have at most 3 members.
+        */
+       private $noahTableStack = [ [] ];
+
+       public function __destruct() {
+               for ( $node = $this->head; $node; $node = $next ) {
+                       $next = $node->nextAFE;
+                       $node->prevAFE = $node->nextAFE = $node->nextNoah = null;
+               }
+               $this->head = $this->tail = $this->noahTableStack = null;
+       }
+
+       public function insertMarker() {
+               $elt = new BalanceMarker;
+               if ( $this->tail ) {
+                       $this->tail->nextAFE = $elt;
+                       $elt->prevAFE = $this->tail;
+               } else {
+                       $this->head = $elt;
+               }
+               $this->tail = $elt;
+               $this->noahTableStack[] = [];
+       }
+
+       /**
+        * Follow the steps required when the spec requires us to "push onto the
+        * list of active formatting elements".
+        * @param BalanceElement $elt
+        */
+       public function push( BalanceElement $elt ) {
+               // Must not be in the list already
+               if ( $elt->prevAFE !== null || $this->head === $elt ) {
+                       throw new ParameterAssertionException( '$elt',
+                               'Cannot insert a node into the AFE list twice' );
+               }
+
+               // "Noah's Ark clause" -- if there are already three copies of
+               // this element before we encounter a marker, then drop the last
+               // one.
+               $noahKey = $elt->getNoahKey();
+               $table =& $this->noahTableStack[ count( $this->noahTableStack ) - 1 ];
+               if ( !isset( $table[$noahKey] ) ) {
+                       $table[$noahKey] = $elt;
+               } else {
+                       $count = 1;
+                       $head = $tail = $table[$noahKey];
+                       while ( $tail->nextNoah ) {
+                               $tail = $tail->nextNoah;
+                               $count++;
+                       }
+                       if ( $count >= 3 ) {
+                               $this->remove( $head );
+                       }
+                       $tail->nextNoah = $elt;
+               }
+               // Add to the main AFE list
+               if ( $this->tail ) {
+                       $this->tail->nextAFE = $elt;
+                       $elt->prevAFE = $this->tail;
+               } else {
+                       $this->head = $elt;
+               }
+               $this->tail = $elt;
+       }
+
+       /**
+        * Follow the steps required when the spec asks us to "clear the list of
+        * active formatting elements up to the last marker".
+        */
+       public function clearToMarker() {
+               // Iterate back through the list starting from the tail
+               $tail = $this->tail;
+               while ( $tail && !( $tail instanceof BalanceMarker ) ) {
+                       // Unlink the element
+                       $prev = $tail->prevAFE;
+                       $tail->prevAFE = null;
+                       if ( $prev ) {
+                               $prev->nextAFE = null;
+                       }
+                       $tail->nextNoah = null;
+                       $tail = $prev;
+               }
+               // If we finished on a marker, unlink it and pop it off the Noah table stack
+               if ( $tail ) {
+                       $prev = $tail->prevAFE;
+                       if ( $prev ) {
+                               $prev->nextAFE = null;
+                       }
+                       $tail = $prev;
+                       array_pop( $this->noahTableStack );
+               } else {
+                       // No marker: wipe the top-level Noah table (which is the only one)
+                       $this->noahTableStack[0] = [];
+               }
+               // If we removed all the elements, clear the head pointer
+               if ( !$tail ) {
+                       $this->head = null;
+               }
+               $this->tail = $tail;
+       }
+
+       /**
+        * Find and return the last element with the specified tag between the
+        * end of the list and the last marker on the list.
+        * Used when parsing &lt;a&gt; "in body mode".
+        */
+       public function findElementByTag( $tag ) {
+               $elt = $this->tail;
+               while ( $elt && !( $elt instanceof BalanceMarker ) ) {
+                       if ( $elt->localName === $tag ) {
+                               return $elt;
+                       }
+                       $elt = $elt->prevAFE;
+               }
+               return null;
+       }
+
+       /**
+        * Determine whether an element is in the list of formatting elements.
+        * @return boolean
+        */
+       public function isInList( BalanceElement $elt ) {
+               return $this->head === $elt || $elt->prevAFE;
+       }
+
+       /**
+        * Find the element $elt in the list and remove it.
+        * Used when parsing &lt;a&gt; in body mode.
+        */
+       public function remove( BalanceElement $elt ) {
+               if ( $this->head !== $elt && !$elt->prevAFE ) {
+                       throw new ParameterAssertionException( '$elt',
+                               "Attempted to remove an element which is not in the AFE list" );
+               }
+               // Update head and tail pointers
+               if ( $this->head === $elt ) {
+                       $this->head = $elt->nextAFE;
+               }
+               if ( $this->tail === $elt ) {
+                       $this->tail = $elt->prevAFE;
+               }
+               // Update previous element
+               if ( $elt->prevAFE ) {
+                       $elt->prevAFE->nextAFE = $elt->nextAFE;
+               }
+               // Update next element
+               if ( $elt->nextAFE ) {
+                       $elt->nextAFE->prevAFE = $elt->prevAFE;
+               }
+               // Clear pointers so that isInList() etc. will work
+               $elt->prevAFE = $elt->nextAFE = null;
+               // Update Noah list
+               $this->removeFromNoahList( $elt );
+       }
+
+       private function addToNoahList( BalanceElement $elt ) {
+               $noahKey = $elt->getNoahKey();
+               $table =& $this->noahTableStack[ count( $this->noahTableStack ) - 1 ];
+               if ( !isset( $table[$noahKey] ) ) {
+                       $table[$noahKey] = $elt;
+               } else {
+                       $tail = $table[$noahKey];
+                       while ( $tail->nextNoah ) {
+                               $tail = $tail->nextNoah;
+                       }
+                       $tail->nextNoah = $elt;
+               }
+       }
+
+       private function removeFromNoahList( BalanceElement $elt ) {
+               $table =& $this->noahTableStack[ count( $this->noahTableStack ) - 1 ];
+               $key = $elt->getNoahKey();
+               $noahElt = $table[$key];
+               if ( $noahElt === $elt ) {
+                       if ( $noahElt->nextNoah ) {
+                               $table[$key] = $noahElt->nextNoah;
+                               $noahElt->nextNoah = null;
+                       } else {
+                               unset( $table[$key] );
+                       }
+               } else {
+                       do {
+                               $prevNoahElt = $noahElt;
+                               $noahElt = $prevNoahElt->nextNoah;
+                               if ( $noahElt === $elt ) {
+                                       // Found it, unlink
+                                       $prevNoahElt->nextNoah = $elt->nextNoah;
+                                       $elt->nextNoah = null;
+                                       break;
+                               }
+                       } while ( $noahElt );
+               }
+       }
+
+       /**
+        * Find element $a in the list and replace it with element $b
+        */
+       public function replace( BalanceElement $a, BalanceElement $b ) {
+               if ( $this->head !== $a && !$a->prevAFE ) {
+                       throw new ParameterAssertionException( '$a',
+                               "Attempted to replace an element which is not in the AFE list" );
+               }
+               // Update head and tail pointers
+               if ( $this->head === $a ) {
+                       $this->head = $b;
+               }
+               if ( $this->tail === $a ) {
+                       $this->tail = $b;
+               }
+               // Update previous element
+               if ( $a->prevAFE ) {
+                       $a->prevAFE->nextAFE = $b;
+               }
+               // Update next element
+               if ( $a->nextAFE ) {
+                       $a->nextAFE->prevAFE = $b;
+               }
+               $b->prevAFE = $a->prevAFE;
+               $b->nextAFE = $a->nextAFE;
+               $a->nextAFE = $a->prevAFE = null;
+               // Update Noah list
+               $this->removeFromNoahList( $a );
+               $this->addToNoahList( $b );
+       }
+
+       /**
+        * Find $a in the list and insert $b after it.
+        */
+       public function insertAfter( BalanceElement $a, BalanceElement $b ) {
+               if ( $this->head !== $a && !$a->prevAFE ) {
+                       throw new ParameterAssertionException( '$a',
+                               "Attempted to insert after an element which is not in the AFE list" );
+               }
+               if ( $this->tail === $a ) {
+                       $this->tail = $b;
+               }
+               if ( $a->nextAFE ) {
+                       $a->nextAFE->prevAFE = $b;
+               }
+               $b->nextAFE = $a->nextAFE;
+               $b->prevAFE = $a;
+               $a->nextAFE = $b;
+               $this->addToNoahList( $b );
+       }
+
+       // @codingStandardsIgnoreStart Generic.Files.LineLength.TooLong
+       /**
+        * Reconstruct the active formatting elements.
+        * @param BalanceStack $stack The open elements stack
+        * @see https://html.spec.whatwg.org/multipage/syntax.html#reconstruct-the-active-formatting-elements
+        */
+       // @codingStandardsIgnoreEnd
+       public function reconstruct( $stack ) {
+               $entry = $this->tail;
+               // If there are no entries in the list of active formatting elements,
+               // then there is nothing to reconstruct
+               if ( !$entry ) {
+                       return;
+               }
+               // If the last is a marker, do nothing.
+               if ( $entry instanceof BalanceMarker ) {
+                       return;
+               }
+               // Or if it is an open element, do nothing.
+               if ( $stack->indexOf( $entry ) >= 0 ) {
+                       return;
+               }
+
+               // Loop backward through the list until we find a marker or an
+               // open element
+               $foundIt = false;
+               while ( $entry->prevAFE ) {
+                       $entry = $entry->prevAFE;
+                       if ( $entry instanceof BalanceMarker || $stack->indexOf( $entry ) >= 0 ) {
+                               $foundIt = true;
+                               break;
+                       }
+               }
+
+               // Now loop forward, starting from the element after the current one (or
+               // the first element if we didn't find a marker or open element),
+               // recreating formatting elements and pushing them back onto the list
+               // of open elements.
+               if ( $foundIt ) {
+                       $entry = $entry->nextAFE;
+               }
+               do {
+                       $newElement = $stack->insertHTMLElement(
+                               $entry->localName,
+                               $entry->attribs );
+                       $this->replace( $entry, $newElement );
+                       $entry = $newElement->nextAFE;
+               } while ( $entry );
+       }
+
+       /**
+        * Get a string representation of the AFE list, for debugging
+        */
+       public function __toString() {
+               $prev = null;
+               $s = '';
+               for ( $node = $this->head; $node; $prev = $node, $node = $node->nextAFE ) {
+                       if ( $node instanceof BalanceMarker ) {
+                               $s .= "MARKER\n";
+                               continue;
+                       }
+                       $s .= $node->localName . '#' . substr( md5( spl_object_hash( $node ) ), 0, 8 );
+                       if ( $node->nextNoah ) {
+                               $s .= " (noah sibling: {$node->nextNoah->localName}#" .
+                                       substr( md5( spl_object_hash( $node->nextNoah ) ), 0, 8 ) .
+                                       ')';
+                       }
+                       if ( $node->nextAFE && $node->nextAFE->prevAFE !== $node ) {
+                               $s .= " (reverse link is wrong!)";
+                       }
+                       $s .= "\n";
+               }
+               if ( $prev !== $this->tail ) {
+                       $s .= "(tail pointer is wrong!)\n";
+               }
+               return $s;
+       }
+}
+
+/**
+ * An implementation of the tree building portion of the HTML5 parsing
+ * spec.
+ *
+ * This is used to balance and tidy output so that the result can
+ * always be cleanly serialized/deserialized by an HTML5 parser.  It
+ * does *not* guarantee "conforming" output -- the HTML5 spec contains
+ * a number of constraints which are not enforced by the HTML5 parsing
+ * process.  But the result will be free of gross errors: misnested or
+ * unclosed tags, for example, and will be unchanged by spec-complient
+ * parsing followed by serialization.
+ *
+ * The tree building stage is structured as a state machine.
+ * When comparing the implementation to
+ * https://www.w3.org/TR/html5/syntax.html#tree-construction
+ * note that each state is implemented as a function with a
+ * name ending in `Mode` (because the HTML spec refers to them
+ * as insertion modes).  The current insertion mode is held by
+ * the $parseMode property.
+ *
+ * The following simplifications have been made:
+ * - We handle body content only (ie, we start `in body`.)
+ * - The document is never in "quirks mode".
+ * - All occurrences of < and > have been entity escaped, so we
+ *   can parse tags by simply splitting on those two characters.
+ *   (This also simplifies the handling of < inside <textarea>.)
+ *   The character < must not appear inside comments.
+ *   Similarly, all attributes have been "cleaned" and are double-quoted
+ *   and escaped.
+ * - All null characters are assumed to have been removed.
+ * - The following elements are disallowed: <html>, <head>, <body>, <frameset>,
+ *   <frame>, <plaintext>, <isindex>, <xmp>, <iframe>,
+ *   <noembed>, <noscript>, <script>, <title>.  As a result,
+ *   further simplifications can be made:
+ *   - `frameset-ok` is not tracked.
+ *   - `head element pointer` is not tracked (but presumed non-null)
+ *   - Tokenizer has only a single mode. (<textarea> wants RCDATA and
+ *     <style>/<noframes> want RAWTEXT modes which we only loosely emulate.)
+ *
+ *   We generally mark places where we omit cases from the spec due to
+ *   disallowed elements with a comment: `// OMITTED: <element-name>`.
+ *
+ *   The HTML spec keeps a flag during the parsing process to track
+ *   whether or not a "parse error" has been encountered.  We don't
+ *   bother to track that flag, we just implement the error-handling
+ *   process as specified.
+ *
+ * @ingroup Parser
+ * @since 1.27
+ * @see https://html.spec.whatwg.org/multipage/syntax.html#tree-construction
+ */
+class Balancer {
+       private $parseMode;
+       private $bitsIterator;
+       private $allowedHtmlElements;
+       private $afe;
+       private $stack;
+       private $strict;
+       private $allowComments;
+       private $config;
+
+       private $textIntegrationMode;
+       private $pendingTableText;
+       private $originalInsertionMode;
+       private $fragmentContext;
+       private $formElementPointer;
+       private $ignoreLinefeed;
+       private $inRCDATA;
+       private $inRAWTEXT;
+
+       /**
+        * Valid HTML5 comments.
+        * Regex borrowed from Tim Starling's "remex-html" project.
+        */
+       const VALID_COMMENT_REGEX = "~ !--
+               (                             # 1. Comment match detector
+                       > | -> | # Invalid short close
+                       (                         # 2. Comment contents
+                               (?:
+                                       (?! --> )
+                                       (?! --!> )
+                                       (?! --! \z )
+                                       (?! -- \z )
+                                       (?! - \z )
+                                       .
+                               )*+
+                       )
+                       (                         # 3. Comment close
+                               --> |   # Normal close
+                               --!> |  # Comment end bang
+                               (                     # 4. Indicate matches requiring EOF
+                                       --! |   # EOF in comment end bang state
+                                       -- |    # EOF in comment end state
+                                       -  |    # EOF in comment end dash state
+                                               # EOF in comment state
+                               )
+                       )
+               )
+               ([^<]*) \z                    # 5. Non-tag text after the comment
+               ~xs";
+
+       /**
+        * Create a new Balancer.
+        * @param array $config Balancer configuration.  Includes:
+        *     'strict' : boolean, defaults to false.
+        *         When true, enforces syntactic constraints on input:
+        *         all non-tag '<' must be escaped, all attributes must be
+        *         separated by a single space and double-quoted.  This is
+        *         consistent with the output of the Sanitizer.
+        *     'allowedHtmlElements' : array, defaults to null.
+        *         When present, the keys of this associative array give
+        *         the acceptable HTML tag names.  When not present, no
+        *         tag sanitization is done.
+        *     'tidyCompat' : boolean, defaults to false.
+        *         When true, the serialization algorithm is tweaked to
+        *         provide historical compatibility with the old "tidy"
+        *         program: <p>-wrapping is done to the children of
+        *         <body> and <blockquote> elements, and empty elements
+        *         are removed.
+        *     'allowComments': boolean, defaults to true.
+        *         When true, allows HTML comments in the input.
+        *         The Sanitizer generally strips all comments, so if you
+        *         are running on sanitized output you can set this to
+        *         false to get a bit more performance.
+        */
+       public function __construct( array $config = [] ) {
+               $this->config = $config = $config + [
+                       'strict' => false,
+                       'allowedHtmlElements' => null,
+                       'tidyCompat' => false,
+                       'allowComments' => true,
+               ];
+               $this->allowedHtmlElements = $config['allowedHtmlElements'];
+               $this->strict = $config['strict'];
+               $this->allowComments = $config['allowComments'];
+               if ( $this->allowedHtmlElements !== null ) {
+                       // Sanity check!
+                       $bad = array_uintersect_assoc(
+                               $this->allowedHtmlElements,
+                               BalanceSets::$unsupportedSet[BalanceSets::HTML_NAMESPACE],
+                               function( $a, $b ) {
+                                       // Ignore the values (just intersect the keys) by saying
+                                       // all values are equal to each other.
+                                       return 0;
+                               }
+                       );
+                       if ( count( $bad ) > 0 ) {
+                               $badstr = implode( array_keys( $bad ), ',' );
+                               throw new ParameterAssertionException(
+                                       '$config',
+                                       'Balance attempted with sanitization including ' .
+                                       "unsupported elements: {$badstr}"
+                               );
+                       }
+               }
+       }
+
+       /**
+        * Return a balanced HTML string for the HTML fragment given by $text,
+        * subject to the caveats listed in the class description.  The result
+        * will typically be idempotent -- that is, rebalancing the output
+        * would result in no change.
+        *
+        * @param string $text The markup to be balanced
+        * @param callable $processingCallback Callback to do any variable or
+        *   parameter replacements in HTML attributes values
+        * @param array|bool $processingArgs Arguments for the processing callback
+        * @return string The balanced markup
+        */
+       public function balance( $text, $processingCallback = null, $processingArgs = [] ) {
+               $this->parseMode = 'inBodyMode';
+               $this->bitsIterator = new ExplodeIterator( '<', $text );
+               $this->afe = new BalanceActiveFormattingElements();
+               $this->stack = new BalanceStack( $this->config );
+               $this->processingCallback = $processingCallback;
+               $this->processingArgs = $processingArgs;
+
+               $this->textIntegrationMode =
+                       $this->ignoreLinefeed =
+                       $this->inRCDATA =
+                       $this->inRAWTEXT = false;
+
+               // The stack is constructed with an <html> element already on it.
+               // Set this up as a fragment parsed with <body> as the context.
+               $this->fragmentContext =
+                       new BalanceElement( BalanceSets::HTML_NAMESPACE, 'body', [] );
+               $this->resetInsertionMode();
+               $this->formElementPointer = null;
+               for ( $e = $this->fragmentContext; $e != null; $e = $e->parent ) {
+                       if ( $e->isHtmlNamed( 'form' ) ) {
+                               $this->formElementPointer = $e;
+                               break;
+                       }
+               }
+
+               // First element is text not tag
+               $x = $this->bitsIterator->current();
+               $this->bitsIterator->next();
+               $this->insertToken( 'text', str_replace( '>', '&gt;', $x ) );
+               // Now process each tag.
+               while ( $this->bitsIterator->valid() ) {
+                       $this->advance();
+               }
+               $this->insertToken( 'eof', null );
+               $result = $this->stack->getOutput();
+               // Free memory before returning.
+               $this->bitsIterator = null;
+               $this->afe = null;
+               $this->stack = null;
+               $this->fragmentContext = null;
+               $this->formElementPointer = null;
+               return $result;
+       }
+
+       /**
+        * Pass a token to the tree builder.  The $token will be one of the
+        * strings "tag", "endtag", or "text".
+        */
+       private function insertToken( $token, $value, $attribs = null, $selfClose = false ) {
+               // validate tags against $unsupportedSet
+               if ( $token === 'tag' || $token === 'endtag' ) {
+                       if ( isset( BalanceSets::$unsupportedSet[BalanceSets::HTML_NAMESPACE][$value] ) ) {
+                               // As described in "simplifications" above, these tags are
+                               // not supported in the balancer.
+                               Assert::invariant(
+                                       !$this->strict,
+                                       "Unsupported $token <$value> found."
+                               );
+                               return false;
+                       }
+               } elseif ( $token === 'text' && $value === '' ) {
+                       // Don't actually inject the empty string as a text token.
+                       return true;
+               }
+               // Support pre/listing/textarea by suppressing initial linefeed
+               if ( $this->ignoreLinefeed ) {
+                       $this->ignoreLinefeed = false;
+                       if ( $token === 'text' ) {
+                               if ( $value[0] === "\n" ) {
+                                       if ( $value === "\n" ) {
+                                               // Nothing would be left, don't inject the empty string.
+                                               return true;
+                                       }
+                                       $value = substr( $value, 1 );
+                               }
+                       }
+               }
+               // Some hoops we have to jump through
+               $adjusted = $this->stack->adjustedCurrentNode( $this->fragmentContext );
+
+               $isForeign = true;
+               if (
+                       $this->stack->length() === 0 ||
+                       $adjusted->isHtml() ||
+                       $token === 'eof'
+               ) {
+                       $isForeign = false;
+               } elseif ( $adjusted->isMathmlTextIntegrationPoint() ) {
+                       if ( $token === 'text' ) {
+                               $isForeign = false;
+                       } elseif (
+                               $token === 'tag' &&
+                               $value !== 'mglyph' && $value !== 'malignmark'
+                       ) {
+                               $isForeign = false;
+                       }
+               } elseif (
+                       $adjusted->namespaceURI === BalanceSets::MATHML_NAMESPACE &&
+                       $adjusted->localName === 'annotation-xml' &&
+                       $token === 'tag' && $value === 'svg'
+               ) {
+                       $isForeign = false;
+               } elseif (
+                       $adjusted->isHtmlIntegrationPoint() &&
+                       ( $token === 'tag' || $token === 'text' )
+               ) {
+                       $isForeign = false;
+               }
+               if ( $isForeign ) {
+                       return $this->insertForeignToken( $token, $value, $attribs, $selfClose );
+               } else {
+                       $func = $this->parseMode;
+                       return $this->$func( $token, $value, $attribs, $selfClose );
+               }
+       }
+
+       private function insertForeignToken( $token, $value, $attribs = null, $selfClose = false ) {
+               if ( $token === 'text' ) {
+                       $this->stack->insertText( $value );
+                       return true;
+               } elseif ( $token === 'tag' ) {
+                       switch ( $value ) {
+                       case 'font':
+                               if ( isset( $attribs['color'] )
+                                       || isset( $attribs['face'] )
+                                       || isset( $attribs['size'] )
+                               ) {
+                                       break;
+                               }
+                               // otherwise, fall through
+                       case 'b':
+                       case 'big':
+                       case 'blockquote':
+                       case 'body':
+                       case 'br':
+                       case 'center':
+                       case 'code':
+                       case 'dd':
+                       case 'div':
+                       case 'dl':
+                       case 'dt':
+                       case 'em':
+                       case 'embed':
+                       case 'h1':
+                       case 'h2':
+                       case 'h3':
+                       case 'h4':
+                       case 'h5':
+                       case 'h6':
+                       case 'head':
+                       case 'hr':
+                       case 'i':
+                       case 'img':
+                       case 'li':
+                       case 'listing':
+                       case 'menu':
+                       case 'meta':
+                       case 'nobr':
+                       case 'ol':
+                       case 'p':
+                       case 'pre':
+                       case 'ruby':
+                       case 's':
+                       case 'small':
+                       case 'span':
+                       case 'strong':
+                       case 'strike':
+                       case 'sub':
+                       case 'sup':
+                       case 'table':
+                       case 'tt':
+                       case 'u':
+                       case 'ul':
+                       case 'var':
+                               if ( $this->fragmentContext ) {
+                                       break;
+                               }
+                               while ( true ) {
+                                       $this->stack->pop();
+                                       $node = $this->stack->currentNode;
+                                       if (
+                                               $node->isMathmlTextIntegrationPoint() ||
+                                               $node->isHtmlIntegrationPoint() ||
+                                               $node->isHtml()
+                                       ) {
+                                               break;
+                                       }
+                               }
+                               return $this->insertToken( $token, $value, $attribs, $selfClose );
+                       }
+                       // "Any other start tag"
+                       $adjusted = ( $this->fragmentContext && $this->stack->length()===1 ) ?
+                               $this->fragmentContext : $this->stack->currentNode;
+                       $this->stack->insertForeignElement(
+                               $adjusted->namespaceURI, $value, $attribs
+                       );
+                       if ( $selfClose ) {
+                               $this->stack->pop();
+                       }
+                       return true;
+               } elseif ( $token === 'endtag' ) {
+                       $first = true;
+                       foreach ( $this->stack as $i => $node ) {
+                               if ( $node->isHtml() && !$first ) {
+                                       // process the end tag as HTML
+                                       $func = $this->parseMode;
+                                       return $this->$func( $token, $value, $attribs, $selfClose );
+                               } elseif ( $i === 0 ) {
+                                       return true;
+                               } elseif ( $node->localName === $value ) {
+                                       $this->stack->popTag( $node );
+                                       return true;
+                               }
+                               $first = false;
+                       }
+               }
+       }
+
+       /**
+        * Grab the next "token" from $bitsIterator.  This is either a open/close
+        * tag or text or a comment, depending on whether the Sanitizer approves.
+        */
+       private function advance() {
+               $x = $this->bitsIterator->current();
+               $this->bitsIterator->next();
+               $regs = [];
+               // Handle comments.  These won't be generated by mediawiki (they
+               // are stripped in the Sanitizer) but may be generated by extensions.
+               if (
+                       $this->allowComments &&
+                       !( $this->inRCDATA || $this->inRAWTEXT ) &&
+                       preg_match( Balancer::VALID_COMMENT_REGEX, $x, $regs, PREG_OFFSET_CAPTURE ) &&
+                       // verify EOF condition where necessary
+                       ( $regs[4][1] < 0 || !$this->bitsIterator->valid() )
+               ) {
+                       $contents = $regs[2][0];
+                       $rest = $regs[5][0];
+                       $this->insertToken( 'comment', $contents );
+                       $this->insertToken( 'text', str_replace( '>', '&gt;', $rest ) );
+                       return;
+               }
+               // $slash: Does the current element start with a '/'?
+               // $t: Current element name
+               // $attribStr: String between element name and >
+               // $brace: Ending '>' or '/>'
+               // $rest: Everything until the next element from the $bitsIterator
+               if ( preg_match( Sanitizer::ELEMENT_BITS_REGEX, $x, $regs ) ) {
+                       list( /* $qbar */, $slash, $t, $attribStr, $brace, $rest ) = $regs;
+                       $t = strtolower( $t );
+                       if ( $this->strict ) {
+                               // Verify that attributes are all properly double-quoted
+                               Assert::invariant(
+                                       preg_match(
+                                               '/^( [:_A-Z0-9][-.:_A-Z0-9]*="[^"]*")*[ ]*$/i', $attribStr
+                                       ),
+                                       "Bad attribute string found"
+                               );
+                       }
+               } else {
+                       Assert::invariant(
+                               !$this->strict, "< found which does not start a valid tag"
+                       );
+                       $slash = $t = $attribStr = $brace = $rest = null;
+               }
+               $goodTag = $t;
+               if ( $this->inRCDATA ) {
+                       if ( $slash && $t === $this->inRCDATA ) {
+                               $this->inRCDATA = false;
+                       } else {
+                               // No tags allowed; this emulates the "rcdata" tokenizer mode.
+                               $goodTag = false;
+                       }
+               }
+               if ( $this->inRAWTEXT ) {
+                       if ( $slash && $t === $this->inRAWTEXT ) {
+                               $this->inRAWTEXT = false;
+                       } else {
+                               // No tags allowed, no entity-escaping done.
+                               $goodTag = false;
+                       }
+               }
+               $sanitize = $this->allowedHtmlElements !== null;
+               if ( $sanitize ) {
+                       $goodTag = $t && isset( $this->allowedHtmlElements[$t] );
+               }
+               if ( $goodTag ) {
+                       if ( is_callable( $this->processingCallback ) ) {
+                               call_user_func_array( $this->processingCallback, [ &$attribStr, $this->processingArgs ] );
+                       }
+                       if ( $sanitize ) {
+                               $goodTag = Sanitizer::validateTag( $attribStr, $t );
+                       }
+               }
+               if ( $goodTag ) {
+                       if ( $sanitize ) {
+                               $attribs = Sanitizer::decodeTagAttributes( $attribStr );
+                               $attribs = Sanitizer::validateTagAttributes( $attribs, $t );
+                       } else {
+                               $attribs = Sanitizer::decodeTagAttributes( $attribStr );
+                       }
+                       $goodTag = $this->insertToken(
+                               $slash ? 'endtag' : 'tag', $t, $attribs, $brace === '/>'
+                       );
+               }
+               if ( $goodTag ) {
+                       $rest = str_replace( '>', '&gt;', $rest );
+                       $this->insertToken( 'text', str_replace( '>', '&gt;', $rest ) );
+               } elseif ( $this->inRAWTEXT ) {
+                       $this->insertToken( 'text', "<$x" );
+               } else {
+                       // bad tag; serialize entire thing as text.
+                       $this->insertToken( 'text', '&lt;' . str_replace( '>', '&gt;', $x ) );
+               }
+       }
+
+       private function switchMode( $mode ) {
+               Assert::parameter(
+                       substr( $mode, -4 )==='Mode', '$mode', 'should end in Mode'
+               );
+               $oldMode = $this->parseMode;
+               $this->parseMode = $mode;
+               return $oldMode;
+       }
+
+       private function switchModeAndReprocess( $mode, $token, $value, $attribs, $selfClose ) {
+               $this->switchMode( $mode );
+               return $this->insertToken( $token, $value, $attribs, $selfClose );
+       }
+
+       private function resetInsertionMode() {
+               $last = false;
+               foreach ( $this->stack as $i => $node ) {
+                       if ( $i === 0 ) {
+                               $last = true;
+                               if ( $this->fragmentContext ) {
+                                       $node = $this->fragmentContext;
+                               }
+                       }
+                       if ( $node->isHtml() ) {
+                               switch ( $node->localName ) {
+                               case 'select':
+                                       $stackLength = $this->stack->length();
+                                       for ( $j = $i + 1; $j < $stackLength-1; $j++ ) {
+                                               $ancestor = $this->stack->node( $stackLength-$j-1 );
+                                               if ( $ancestor->isHtmlNamed( 'template' ) ) {
+                                                       break;
+                                               }
+                                               if ( $ancestor->isHtmlNamed( 'table' ) ) {
+                                                       $this->switchMode( 'inSelectInTableMode' );
+                                                       return;
+                                               }
+                                       }
+                                       $this->switchMode( 'inSelectMode' );
+                                       return;
+                               case 'tr':
+                                       $this->switchMode( 'inRowMode' );
+                                       return;
+                               case 'tbody':
+                               case 'tfoot':
+                               case 'thead':
+                                       $this->switchMode( 'inTableBodyMode' );
+                                       return;
+                               case 'caption':
+                                       $this->switchMode( 'inCaptionMode' );
+                                       return;
+                               case 'colgroup':
+                                       $this->switchMode( 'inColumnGroupMode' );
+                                       return;
+                               case 'table':
+                                       $this->switchMode( 'inTableMode' );
+                                       return;
+                               case 'template':
+                                       $this->switchMode(
+                                               array_slice( $this->templateInsertionModes, -1 )[0]
+                                       );
+                                       return;
+                               case 'body':
+                                       $this->switchMode( 'inBodyMode' );
+                                       return;
+                               // OMITTED: <frameset>
+                               // OMITTED: <html>
+                               // OMITTED: <head>
+                               default:
+                                       if ( !$last ) {
+                                               // OMITTED: <head>
+                                               if ( $node->isA( BalanceSets::$tableCellSet ) ) {
+                                                       $this->switchMode( 'inCellMode' );
+                                                       return;
+                                               }
+                                       }
+                               }
+                       }
+                       if ( $last ) {
+                               $this->switchMode( 'inBodyMode' );
+                               return;
+                       }
+               }
+       }
+
+       private function stopParsing() {
+               // Most of the spec methods are inapplicable, other than step 2:
+               // "pop all the nodes off the stack of open elements".
+               // We're going to keep the top-most <html> element on the stack, though.
+
+               // Clear the AFE list first, otherwise the element objects will stay live
+               // during serialization, potentially using O(N^2) memory. Note that
+               // popping the stack will never result in reconstructing the active
+               // formatting elements.
+               $this->afe = null;
+               $this->stack->popTo( 1 );
+       }
+
+       private function parseRawText( $value, $attribs = null ) {
+               $this->stack->insertHTMLElement( $value, $attribs );
+               $this->inRAWTEXT = $value;
+               $this->originalInsertionMode = $this->switchMode( 'inTextMode' );
+               return true;
+       }
+
+       private function inTextMode( $token, $value, $attribs = null, $selfClose = false ) {
+               if ( $token === 'text' ) {
+                       $this->stack->insertText( $value );
+                       return true;
+               } elseif ( $token === 'eof' ) {
+                       $this->stack->pop();
+                       return $this->switchModeAndReprocess(
+                               $this->originalInsertionMode, $token, $value, $attribs, $selfClose
+                       );
+               } elseif ( $token === 'endtag' ) {
+                       $this->stack->pop();
+                       $this->switchMode( $this->originalInsertionMode );
+                       return true;
+               }
+               return true;
+       }
+
+       private function inHeadMode( $token, $value, $attribs = null, $selfClose = false ) {
+               if ( $token === 'text' ) {
+                       if ( preg_match( '/^[\x09\x0A\x0C\x0D\x20]+/', $value, $matches ) ) {
+                               $this->stack->insertText( $matches[0] );
+                               $value = substr( $value, strlen( $matches[0] ) );
+                       }
+                       if ( strlen( $value ) === 0 ) {
+                               return true; // All text handled.
+                       }
+                       // Fall through to handle non-whitespace below.
+               } elseif ( $token === 'tag' ) {
+                       switch ( $value ) {
+                       case 'meta':
+                               // OMITTED: in a full HTML parser, this might change the encoding.
+                               // falls through
+                       // OMITTED: <html>
+                       case 'base':
+                       case 'basefont':
+                       case 'bgsound':
+                       case 'link':
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->stack->pop();
+                               return true;
+                       // OMITTED: <title>
+                       // OMITTED: <noscript>
+                       case 'noframes':
+                       case 'style':
+                               return $this->parseRawText( $value, $attribs );
+                       // OMITTED: <script>
+                       case 'template':
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->afe->insertMarker();
+                               // OMITTED: frameset_ok
+                               $this->switchMode( 'inTemplateMode' );
+                               $this->templateInsertionModes[] = $this->parseMode;
+                               return true;
+                       // OMITTED: <head>
+                       }
+               } elseif ( $token === 'endtag' ) {
+                       switch ( $value ) {
+                       // OMITTED: <head>
+                       // OMITTED: <body>
+                       // OMITTED: <html>
+                       case 'br':
+                               break; // handle at the bottom of the function
+                       case 'template':
+                               if ( $this->stack->indexOf( $value ) < 0 ) {
+                                       return true; // Ignore the token.
+                               }
+                               $this->stack->generateImpliedEndTags( null, true /* thorough */ );
+                               $this->stack->popTag( $value );
+                               $this->afe->clearToMarker();
+                               array_pop( $this->templateInsertionModes );
+                               $this->resetInsertionMode();
+                               return true;
+                       default:
+                               // ignore any other end tag
+                               return true;
+                       }
+               } elseif ( $token === 'comment' ) {
+                       $this->stack->insertComment( $value );
+                       return true;
+               }
+
+               // If not handled above
+               $this->inHeadMode( 'endtag', 'head' ); // synthetic </head>
+               // Then redo this one
+               return $this->insertToken( $token, $value, $attribs, $selfClose );
+       }
+
+       private function inBodyMode( $token, $value, $attribs = null, $selfClose = false ) {
+               if ( $token === 'text' ) {
+                       $this->afe->reconstruct( $this->stack );
+                       $this->stack->insertText( $value );
+                       return true;
+               } elseif ( $token === 'eof' ) {
+                       if ( !empty( $this->templateInsertionModes ) ) {
+                               return $this->inTemplateMode( $token, $value, $attribs, $selfClose );
+                       }
+                       $this->stopParsing();
+                       return true;
+               } elseif ( $token === 'tag' ) {
+                       switch ( $value ) {
+                       // OMITTED: <html>
+                       case 'base':
+                       case 'basefont':
+                       case 'bgsound':
+                       case 'link':
+                       case 'meta':
+                       case 'noframes':
+                       // OMITTED: <script>
+                       case 'style':
+                       case 'template':
+                       // OMITTED: <title>
+                               return $this->inHeadMode( $token, $value, $attribs, $selfClose );
+                       // OMITTED: <body>
+                       // OMITTED: <frameset>
+
+                       case 'address':
+                       case 'article':
+                       case 'aside':
+                       case 'blockquote':
+                       case 'center':
+                       case 'details':
+                       case 'dialog':
+                       case 'dir':
+                       case 'div':
+                       case 'dl':
+                       case 'fieldset':
+                       case 'figcaption':
+                       case 'figure':
+                       case 'footer':
+                       case 'header':
+                       case 'hgroup':
+                       case 'main':
+                       case 'menu':
+                       case 'nav':
+                       case 'ol':
+                       case 'p':
+                       case 'section':
+                       case 'summary':
+                       case 'ul':
+                               if ( $this->stack->inButtonScope( 'p' ) ) {
+                                       $this->inBodyMode( 'endtag', 'p' );
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               return true;
+
+                       case 'h1':
+                       case 'h2':
+                       case 'h3':
+                       case 'h4':
+                       case 'h5':
+                       case 'h6':
+                               if ( $this->stack->inButtonScope( 'p' ) ) {
+                                       $this->inBodyMode( 'endtag', 'p' );
+                               }
+                               if ( $this->stack->currentNode->isA( BalanceSets::$headingSet ) ) {
+                                       $this->stack->pop();
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               return true;
+
+                       case 'pre':
+                       case 'listing':
+                               if ( $this->stack->inButtonScope( 'p' ) ) {
+                                       $this->inBodyMode( 'endtag', 'p' );
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->ignoreLinefeed = true;
+                               // OMITTED: frameset_ok
+                               return true;
+
+                       case 'form':
+                               if (
+                                       $this->formElementPointer &&
+                                       $this->stack->indexOf( 'template' ) < 0
+                               ) {
+                                       return true; // in a form, not in a template.
+                               }
+                               if ( $this->stack->inButtonScope( "p" ) ) {
+                                       $this->inBodyMode( 'endtag', 'p' );
+                               }
+                               $elt = $this->stack->insertHTMLElement( $value, $attribs );
+                               if ( $this->stack->indexOf( 'template' ) < 0 ) {
+                                       $this->formElementPointer = $elt;
+                               }
+                               return true;
+
+                       case 'li':
+                               // OMITTED: frameset_ok
+                               foreach ( $this->stack as $node ) {
+                                       if ( $node->isHtmlNamed( 'li' ) ) {
+                                               $this->inBodyMode( 'endtag', 'li' );
+                                               break;
+                                       }
+                                       if (
+                                               $node->isA( BalanceSets::$specialSet ) &&
+                                               !$node->isA( BalanceSets::$addressDivPSet )
+                                       ) {
+                                               break;
+                                       }
+                               }
+                               if ( $this->stack->inButtonScope( 'p' ) ) {
+                                       $this->inBodyMode( 'endtag', 'p' );
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               return true;
+
+                       case 'dd':
+                       case 'dt':
+                               // OMITTED: frameset_ok
+                               foreach ( $this->stack as $node ) {
+                                       if ( $node->isHtmlNamed( 'dd' ) ) {
+                                               $this->inBodyMode( 'endtag', 'dd' );
+                                               break;
+                                       }
+                                       if ( $node->isHtmlNamed( 'dt' ) ) {
+                                               $this->inBodyMode( 'endtag', 'dt' );
+                                               break;
+                                       }
+                                       if (
+                                               $node->isA( BalanceSets::$specialSet ) &&
+                                               !$node->isA( BalanceSets::$addressDivPSet )
+                                       ) {
+                                               break;
+                                       }
+                               }
+                               if ( $this->stack->inButtonScope( 'p' ) ) {
+                                       $this->inBodyMode( 'endtag', 'p' );
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               return true;
+
+                       // OMITTED: <plaintext>
+
+                       case 'button':
+                               if ( $this->stack->inScope( 'button' ) ) {
+                                       $this->inBodyMode( 'endtag', 'button' );
+                                       return $this->insertToken( $token, $value, $attribs, $selfClose );
+                               }
+                               $this->afe->reconstruct( $this->stack );
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               return true;
+
+                       case 'a':
+                               $activeElement = $this->afe->findElementByTag( 'a' );
+                               if ( $activeElement ) {
+                                       $this->inBodyMode( 'endtag', 'a' );
+                                       if ( $this->afe->isInList( $activeElement ) ) {
+                                               $this->afe->remove( $activeElement );
+                                               // Don't flatten here, since when we fall
+                                               // through below we might foster parent
+                                               // the new <a> tag inside this one.
+                                               $this->stack->removeElement( $activeElement, false );
+                                       }
+                               }
+                               // Falls through
+                       case 'b':
+                       case 'big':
+                       case 'code':
+                       case 'em':
+                       case 'font':
+                       case 'i':
+                       case 's':
+                       case 'small':
+                       case 'strike':
+                       case 'strong':
+                       case 'tt':
+                       case 'u':
+                               $this->afe->reconstruct( $this->stack );
+                               $this->afe->push( $this->stack->insertHTMLElement( $value, $attribs ), $attribs );
+                               return true;
+
+                       case 'nobr':
+                               $this->afe->reconstruct( $this->stack );
+                               if ( $this->stack->inScope( 'nobr' ) ) {
+                                       $this->inBodyMode( 'endtag', 'nobr' );
+                                       $this->afe->reconstruct( $this->stack );
+                               }
+                               $this->afe->push( $this->stack->insertHTMLElement( $value, $attribs ), $attribs );
+                               return true;
+
+                       case 'applet':
+                       case 'marquee':
+                       case 'object':
+                               $this->afe->reconstruct( $this->stack );
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->afe->insertMarker();
+                               // OMITTED: frameset_ok
+                               return true;
+
+                       case 'table':
+                               // The document is never in "quirks mode"; see simplifications
+                               // above.
+                               if ( $this->stack->inButtonScope( 'p' ) ) {
+                                       $this->inBodyMode( 'endtag', 'p' );
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               // OMITTED: frameset_ok
+                               $this->switchMode( 'inTableMode' );
+                               return true;
+
+                       case 'area':
+                       case 'br':
+                       case 'embed':
+                       case 'img':
+                       case 'keygen':
+                       case 'wbr':
+                               $this->afe->reconstruct( $this->stack );
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->stack->pop();
+                               // OMITTED: frameset_ok
+                               return true;
+
+                       case 'input':
+                               $this->afe->reconstruct( $this->stack );
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->stack->pop();
+                               // OMITTED: frameset_ok
+                               // (hence we don't need to examine the tag's "type" attribute)
+                               return true;
+
+                       case 'menuitem':
+                       case 'param':
+                       case 'source':
+                       case 'track':
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->stack->pop();
+                               return true;
+
+                       case 'hr':
+                               if ( $this->stack->inButtonScope( 'p' ) ) {
+                                       $this->inBodyMode( 'endtag', 'p' );
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->stack->pop();
+                               return true;
+
+                       case 'image':
+                               // warts!
+                               return $this->inBodyMode( $token, 'img', $attribs, $selfClose );
+
+                       // OMITTED: <isindex>
+
+                       case 'textarea':
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->ignoreLinefeed = true;
+                               $this->inRCDATA = $value; // emulate rcdata tokenizer mode
+                               // OMITTED: frameset_ok
+                               return true;
+
+                       // OMITTED: <xmp>
+                       // OMITTED: <iframe>
+                       // OMITTED: <noembed>
+                       // OMITTED: <noscript>
+
+                       case 'select':
+                               $this->afe->reconstruct( $this->stack );
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               switch ( $this->parseMode ) {
+                               case 'inTableMode':
+                               case 'inCaptionMode':
+                               case 'inTableBodyMode':
+                               case 'inRowMode':
+                               case 'inCellMode':
+                                       $this->switchMode( 'inSelectInTableMode' );
+                                       return true;
+                               default:
+                                       $this->switchMode( 'inSelectMode' );
+                                       return true;
+                               }
+
+                       case 'optgroup':
+                       case 'option':
+                               if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) {
+                                       $this->inBodyMode( 'endtag', 'option' );
+                               }
+                               $this->afe->reconstruct( $this->stack );
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               return true;
+
+                       case 'rb':
+                       case 'rtc':
+                               if ( $this->stack->inScope( 'ruby' ) ) {
+                                       $this->stack->generateImpliedEndTags();
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               return true;
+
+                       case 'rp':
+                       case 'rt':
+                               if ( $this->stack->inScope( 'ruby' ) ) {
+                                       $this->stack->generateImpliedEndTags( 'rtc' );
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               return true;
+
+                       case 'math':
+                               $this->afe->reconstruct( $this->stack );
+                               // We skip the spec's "adjust MathML attributes" and
+                               // "adjust foreign attributes" steps, since the browser will
+                               // do this later when it parses the output and it doesn't affect
+                               // balancing.
+                               $this->stack->insertForeignElement(
+                                       BalanceSets::MATHML_NAMESPACE, $value, $attribs
+                               );
+                               if ( $selfClose ) {
+                                       // emit explicit </math> tag.
+                                       $this->stack->pop();
+                               }
+                               return true;
+
+                       case 'svg':
+                               $this->afe->reconstruct( $this->stack );
+                               // We skip the spec's "adjust SVG attributes" and
+                               // "adjust foreign attributes" steps, since the browser will
+                               // do this later when it parses the output and it doesn't affect
+                               // balancing.
+                               $this->stack->insertForeignElement(
+                                       BalanceSets::SVG_NAMESPACE, $value, $attribs
+                               );
+                               if ( $selfClose ) {
+                                       // emit explicit </svg> tag.
+                                       $this->stack->pop();
+                               }
+                               return true;
+
+                       case 'caption':
+                       case 'col':
+                       case 'colgroup':
+                       // OMITTED: <frame>
+                       case 'head':
+                       case 'tbody':
+                       case 'td':
+                       case 'tfoot':
+                       case 'th':
+                       case 'thead':
+                       case 'tr':
+                               // Ignore table tags if we're not inTableMode
+                               return true;
+                       }
+
+                       // Handle any other start tag here
+                       $this->afe->reconstruct( $this->stack );
+                       $this->stack->insertHTMLElement( $value, $attribs );
+                       return true;
+               } elseif ( $token === 'endtag' ) {
+                       switch ( $value ) {
+                       // </body>,</html> are unsupported.
+
+                       case 'template':
+                               return $this->inHeadMode( $token, $value, $attribs, $selfClose );
+
+                       case 'address':
+                       case 'article':
+                       case 'aside':
+                       case 'blockquote':
+                       case 'button':
+                       case 'center':
+                       case 'details':
+                       case 'dialog':
+                       case 'dir':
+                       case 'div':
+                       case 'dl':
+                       case 'fieldset':
+                       case 'figcaption':
+                       case 'figure':
+                       case 'footer':
+                       case 'header':
+                       case 'hgroup':
+                       case 'listing':
+                       case 'main':
+                       case 'menu':
+                       case 'nav':
+                       case 'ol':
+                       case 'pre':
+                       case 'section':
+                       case 'summary':
+                       case 'ul':
+                               // Ignore if there is not a matching open tag
+                               if ( !$this->stack->inScope( $value ) ) {
+                                       return true;
+                               }
+                               $this->stack->generateImpliedEndTags();
+                               $this->stack->popTag( $value );
+                               return true;
+
+                       case 'form':
+                               if ( $this->stack->indexOf( 'template' ) < 0 ) {
+                                       $openform = $this->formElementPointer;
+                                       $this->formElementPointer = null;
+                                       if ( !$openform || !$this->stack->inScope( $openform ) ) {
+                                               return true;
+                                       }
+                                       $this->stack->generateImpliedEndTags();
+                                       // Don't flatten yet if we're removing a <form> element
+                                       // out-of-order. (eg. `<form><div></form>`)
+                                       $flatten = ( $this->stack->currentNode === $openform );
+                                       $this->stack->removeElement( $openform, $flatten );
+                               } else {
+                                       if ( !$this->stack->inScope( 'form' ) ) {
+                                               return true;
+                                       }
+                                       $this->stack->generateImpliedEndTags();
+                                       $this->stack->popTag( 'form' );
+                               }
+                               return true;
+
+                       case 'p':
+                               if ( !$this->stack->inButtonScope( 'p' ) ) {
+                                       $this->inBodyMode( 'tag', 'p', [] );
+                                       return $this->insertToken( $token, $value, $attribs, $selfClose );
+                               }
+                               $this->stack->generateImpliedEndTags( $value );
+                               $this->stack->popTag( $value );
+                               return true;
+
+                       case 'li':
+                               if ( !$this->stack->inListItemScope( $value ) ) {
+                                       return true; // ignore
+                               }
+                               $this->stack->generateImpliedEndTags( $value );
+                               $this->stack->popTag( $value );
+                               return true;
+
+                       case 'dd':
+                       case 'dt':
+                               if ( !$this->stack->inScope( $value ) ) {
+                                       return true; // ignore
+                               }
+                               $this->stack->generateImpliedEndTags( $value );
+                               $this->stack->popTag( $value );
+                               return true;
+
+                       case 'h1':
+                       case 'h2':
+                       case 'h3':
+                       case 'h4':
+                       case 'h5':
+                       case 'h6':
+                               if ( !$this->stack->inScope( BalanceSets::$headingSet ) ) {
+                                       return true; // ignore
+                               }
+                               $this->stack->generateImpliedEndTags();
+                               $this->stack->popTag( BalanceSets::$headingSet );
+                               return true;
+
+                       case 'sarcasm':
+                               // Take a deep breath, then:
+                               break;
+
+                       case 'a':
+                       case 'b':
+                       case 'big':
+                       case 'code':
+                       case 'em':
+                       case 'font':
+                       case 'i':
+                       case 'nobr':
+                       case 's':
+                       case 'small':
+                       case 'strike':
+                       case 'strong':
+                       case 'tt':
+                       case 'u':
+                               if ( $this->stack->adoptionAgency( $value, $this->afe ) ) {
+                                       return true; // If we did something, we're done.
+                               }
+                               break; // Go to the "any other end tag" case.
+
+                       case 'applet':
+                       case 'marquee':
+                       case 'object':
+                               if ( !$this->stack->inScope( $value ) ) {
+                                       return true; // ignore
+                               }
+                               $this->stack->generateImpliedEndTags();
+                               $this->stack->popTag( $value );
+                               $this->afe->clearToMarker();
+                               return true;
+
+                       case 'br':
+                               // Turn </br> into <br>
+                               return $this->inBodyMode( 'tag', $value, [] );
+                       }
+
+                       // Any other end tag goes here
+                       foreach ( $this->stack as $i => $node ) {
+                               if ( $node->isHtmlNamed( $value ) ) {
+                                       $this->stack->generateImpliedEndTags( $value );
+                                       $this->stack->popTo( $i ); // including $i
+                                       break;
+                               } elseif ( $node->isA( BalanceSets::$specialSet ) ) {
+                                       return true; // ignore this close token.
+                               }
+                       }
+                       return true;
+               } elseif ( $token === 'comment' ) {
+                       $this->stack->insertComment( $value );
+                       return true;
+               } else {
+                       Assert::invariant( false, "Bad token type: $token" );
+               }
+       }
+
+       private function inTableMode( $token, $value, $attribs = null, $selfClose = false ) {
+               if ( $token === 'text' ) {
+                       if ( $this->textIntegrationMode ) {
+                               return $this->inBodyMode( $token, $value, $attribs, $selfClose );
+                       } elseif ( $this->stack->currentNode->isA( BalanceSets::$tableSectionRowSet ) ) {
+                               $this->pendingTableText = '';
+                               $this->originalInsertionMode = $this->parseMode;
+                               return $this->switchModeAndReprocess( 'inTableTextMode',
+                                       $token, $value, $attribs, $selfClose );
+                       }
+                       // fall through to default case.
+               } elseif ( $token === 'eof' ) {
+                       $this->stopParsing();
+                       return true;
+               } elseif ( $token === 'tag' ) {
+                       switch ( $value ) {
+                       case 'caption':
+                               $this->afe->insertMarker();
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->switchMode( 'inCaptionMode' );
+                               return true;
+                       case 'colgroup':
+                               $this->stack->clearToContext( BalanceSets::$tableContextSet );
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->switchMode( 'inColumnGroupMode' );
+                               return true;
+                       case 'col':
+                               $this->inTableMode( 'tag', 'colgroup', [] );
+                               return $this->insertToken( $token, $value, $attribs, $selfClose );
+                       case 'tbody':
+                       case 'tfoot':
+                       case 'thead':
+                               $this->stack->clearToContext( BalanceSets::$tableContextSet );
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->switchMode( 'inTableBodyMode' );
+                               return true;
+                       case 'td':
+                       case 'th':
+                       case 'tr':
+                               $this->inTableMode( 'tag', 'tbody', [] );
+                               return $this->insertToken( $token, $value, $attribs, $selfClose );
+                       case 'table':
+                               if ( !$this->stack->inTableScope( $value ) ) {
+                                       return true; // Ignore this tag.
+                               }
+                               $this->inTableMode( 'endtag', $value );
+                               return $this->insertToken( $token, $value, $attribs, $selfClose );
+
+                       case 'style':
+                       // OMITTED: <script>
+                       case 'template':
+                               return $this->inHeadMode( $token, $value, $attribs, $selfClose );
+
+                       case 'input':
+                               if ( !isset( $attribs['type'] ) || strcasecmp( $attribs['type'], 'hidden' ) !== 0 ) {
+                                       break; // Handle this as "everything else"
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->stack->pop();
+                               return true;
+
+                       case 'form':
+                               if (
+                                       $this->formElementPointer ||
+                                       $this->stack->indexOf( 'template' ) >= 0
+                               ) {
+                                       return true; // ignore this token
+                               }
+                               $this->formElementPointer =
+                                       $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->stack->popTag( $this->formElementPointer );
+                               return true;
+                       }
+                       // Fall through for "anything else" clause.
+               } elseif ( $token === 'endtag' ) {
+                       switch ( $value ) {
+                       case 'table':
+                               if ( !$this->stack->inTableScope( $value ) ) {
+                                       return true; // Ignore.
+                               }
+                               $this->stack->popTag( $value );
+                               $this->resetInsertionMode();
+                               return true;
+                       // OMITTED: <body>
+                       case 'caption':
+                       case 'col':
+                       case 'colgroup':
+                       // OMITTED: <html>
+                       case 'tbody':
+                       case 'td':
+                       case 'tfoot':
+                       case 'th':
+                       case 'thead':
+                       case 'tr':
+                               return true; // Ignore the token.
+                       case 'template':
+                               return $this->inHeadMode( $token, $value, $attribs, $selfClose );
+                       }
+                       // Fall through for "anything else" clause.
+               } elseif ( $token === 'comment' ) {
+                       $this->stack->insertComment( $value );
+                       return true;
+               }
+               // This is the "anything else" case:
+               $this->stack->fosterParentMode = true;
+               $this->inBodyMode( $token, $value, $attribs, $selfClose );
+               $this->stack->fosterParentMode = false;
+               return true;
+       }
+
+       private function inTableTextMode( $token, $value, $attribs = null, $selfClose = false ) {
+               if ( $token === 'text' ) {
+                       $this->pendingTableText .= $value;
+                       return true;
+               }
+               // Non-text token:
+               $text = $this->pendingTableText;
+               $this->pendingTableText = '';
+               if ( preg_match( '/[^\x09\x0A\x0C\x0D\x20]/', $text ) ) {
+                       // This should match the "anything else" case inTableMode
+                       $this->stack->fosterParentMode = true;
+                       $this->inBodyMode( 'text', $text );
+                       $this->stack->fosterParentMode = false;
+               } else {
+                       // Pending text is just whitespace.
+                       $this->stack->insertText( $text );
+               }
+               return $this->switchModeAndReprocess(
+                       $this->originalInsertionMode, $token, $value, $attribs, $selfClose
+               );
+       }
+
+       // helper for inCaptionMode
+       private function endCaption() {
+               if ( !$this->stack->inTableScope( 'caption' ) ) {
+                       return false;
+               }
+               $this->stack->generateImpliedEndTags();
+               $this->stack->popTag( 'caption' );
+               $this->afe->clearToMarker();
+               $this->switchMode( 'inTableMode' );
+               return true;
+       }
+
+       private function inCaptionMode( $token, $value, $attribs = null, $selfClose = false ) {
+               if ( $token === 'tag' ) {
+                       switch ( $value ) {
+                       case 'caption':
+                       case 'col':
+                       case 'colgroup':
+                       case 'tbody':
+                       case 'td':
+                       case 'tfoot':
+                       case 'th':
+                       case 'thead':
+                       case 'tr':
+                               if ( $this->endCaption() ) {
+                                       $this->insertToken( $token, $value, $attribs, $selfClose );
+                               }
+                               return true;
+                       }
+                       // Fall through to "anything else" case.
+               } elseif ( $token === 'endtag' ) {
+                       switch ( $value ) {
+                       case 'caption':
+                               $this->endCaption();
+                               return true;
+                       case 'table':
+                               if ( $this->endCaption() ) {
+                                       $this->insertToken( $token, $value, $attribs, $selfClose );
+                               }
+                               return true;
+                       case 'body':
+                       case 'col':
+                       case 'colgroup':
+                       // OMITTED: <html>
+                       case 'tbody':
+                       case 'td':
+                       case 'tfoot':
+                       case 'th':
+                       case 'thead':
+                       case 'tr':
+                               // Ignore the token
+                               return true;
+                       }
+                       // Fall through to "anything else" case.
+               }
+               // The Anything Else case
+               return $this->inBodyMode( $token, $value, $attribs, $selfClose );
+       }
+
+       private function inColumnGroupMode( $token, $value, $attribs = null, $selfClose = false ) {
+               if ( $token === 'text' ) {
+                       if ( preg_match( '/^[\x09\x0A\x0C\x0D\x20]+/', $value, $matches ) ) {
+                               $this->stack->insertText( $matches[0] );
+                               $value = substr( $value, strlen( $matches[0] ) );
+                       }
+                       if ( strlen( $value ) === 0 ) {
+                               return true; // All text handled.
+                       }
+                       // Fall through to handle non-whitespace below.
+               } elseif ( $token === 'tag' ) {
+                       switch ( $value ) {
+                       // OMITTED: <html>
+                       case 'col':
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->stack->pop();
+                               return true;
+                       case 'template':
+                               return $this->inHeadMode( $token, $value, $attribs, $selfClose );
+                       }
+                       // Fall through for "anything else".
+               } elseif ( $token === 'endtag' ) {
+                       switch ( $value ) {
+                       case 'colgroup':
+                               if ( !$this->stack->currentNode->isHtmlNamed( 'colgroup' ) ) {
+                                       return true; // Ignore the token.
+                               }
+                               $this->stack->pop();
+                               $this->switchMode( 'inTableMode' );
+                               return true;
+                       case 'col':
+                               return true; // Ignore the token.
+                       case 'template':
+                               return $this->inHeadMode( $token, $value, $attribs, $selfClose );
+                       }
+                       // Fall through for "anything else".
+               } elseif ( $token === 'eof' ) {
+                       return $this->inBodyMode( $token, $value, $attribs, $selfClose );
+               } elseif ( $token === 'comment' ) {
+                       $this->stack->insertComment( $value );
+                       return true;
+               }
+
+               // Anything else
+               if ( !$this->stack->currentNode->isHtmlNamed( 'colgroup' ) ) {
+                       return true; // Ignore the token.
+               }
+               $this->inColumnGroupMode( 'endtag', 'colgroup' );
+               return $this->insertToken( $token, $value, $attribs, $selfClose );
+       }
+
+       // Helper function for inTableBodyMode
+       private function endSection() {
+               if ( !(
+                       $this->stack->inTableScope( 'tbody' ) ||
+                       $this->stack->inTableScope( 'thead' ) ||
+                       $this->stack->inTableScope( 'tfoot' )
+               ) ) {
+                       return false;
+               }
+               $this->stack->clearToContext( BalanceSets::$tableBodyContextSet );
+               $this->stack->pop();
+               $this->switchMode( 'inTableMode' );
+               return true;
+       }
+       private function inTableBodyMode( $token, $value, $attribs = null, $selfClose = false ) {
+               if ( $token === 'tag' ) {
+                       switch ( $value ) {
+                       case 'tr':
+                               $this->stack->clearToContext( BalanceSets::$tableBodyContextSet );
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->switchMode( 'inRowMode' );
+                               return true;
+                       case 'th':
+                       case 'td':
+                               $this->inTableBodyMode( 'tag', 'tr', [] );
+                               $this->insertToken( $token, $value, $attribs, $selfClose );
+                               return true;
+                       case 'caption':
+                       case 'col':
+                       case 'colgroup':
+                       case 'tbody':
+                       case 'tfoot':
+                       case 'thead':
+                               if ( $this->endSection() ) {
+                                       $this->insertToken( $token, $value, $attribs, $selfClose );
+                               }
+                               return true;
+                       }
+               } elseif ( $token === 'endtag' ) {
+                       switch ( $value ) {
+                       case 'table':
+                               if ( $this->endSection() ) {
+                                       $this->insertToken( $token, $value, $attribs, $selfClose );
+                               }
+                               return true;
+                       case 'tbody':
+                       case 'tfoot':
+                       case 'thead':
+                               if ( $this->stack->inTableScope( $value ) ) {
+                                       $this->endSection();
+                               }
+                               return true;
+                       // OMITTED: <body>
+                       case 'caption':
+                       case 'col':
+                       case 'colgroup':
+                       // OMITTED: <html>
+                       case 'td':
+                       case 'th':
+                       case 'tr':
+                               return true; // Ignore the token.
+                       }
+               }
+               // Anything else:
+               return $this->inTableMode( $token, $value, $attribs, $selfClose );
+       }
+
+       // Helper function for inRowMode
+       private function endRow() {
+               if ( !$this->stack->inTableScope( 'tr' ) ) {
+                       return false;
+               }
+               $this->stack->clearToContext( BalanceSets::$tableRowContextSet );
+               $this->stack->pop();
+               $this->switchMode( 'inTableBodyMode' );
+               return true;
+       }
+       private function inRowMode( $token, $value, $attribs = null, $selfClose = false ) {
+               if ( $token === 'tag' ) {
+                       switch ( $value ) {
+                       case 'th':
+                       case 'td':
+                               $this->stack->clearToContext( BalanceSets::$tableRowContextSet );
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               $this->switchMode( 'inCellMode' );
+                               $this->afe->insertMarker();
+                               return true;
+                       case 'caption':
+                       case 'col':
+                       case 'colgroup':
+                       case 'tbody':
+                       case 'tfoot':
+                       case 'thead':
+                       case 'tr':
+                               if ( $this->endRow() ) {
+                                       $this->insertToken( $token, $value, $attribs, $selfClose );
+                               }
+                               return true;
+                       }
+               } elseif ( $token === 'endtag' ) {
+                       switch ( $value ) {
+                       case 'tr':
+                               $this->endRow();
+                               return true;
+                       case 'table':
+                               if ( $this->endRow() ) {
+                                       $this->insertToken( $token, $value, $attribs, $selfClose );
+                               }
+                               return true;
+                       case 'tbody':
+                       case 'tfoot':
+                       case 'thead':
+                               if (
+                                       $this->stack->inTableScope( $value ) &&
+                                       $this->endRow()
+                               ) {
+                                       $this->insertToken( $token, $value, $attribs, $selfClose );
+                               }
+                               return true;
+                       // OMITTED: <body>
+                       case 'caption':
+                       case 'col':
+                       case 'colgroup':
+                       // OMITTED: <html>
+                       case 'td':
+                       case 'th':
+                               return true; // Ignore the token.
+                       }
+               }
+               // Anything else:
+               return $this->inTableMode( $token, $value, $attribs, $selfClose );
+       }
+
+       // Helper for inCellMode
+       private function endCell() {
+               if ( $this->stack->inTableScope( 'td' ) ) {
+                       $this->inCellMode( 'endtag', 'td' );
+                       return true;
+               } elseif ( $this->stack->inTableScope( 'th' ) ) {
+                       $this->inCellMode( 'endtag', 'th' );
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+       private function inCellMode( $token, $value, $attribs = null, $selfClose = false ) {
+               if ( $token === 'tag' ) {
+                       switch ( $value ) {
+                       case 'caption':
+                       case 'col':
+                       case 'colgroup':
+                       case 'tbody':
+                       case 'td':
+                       case 'tfoot':
+                       case 'th':
+                       case 'thead':
+                       case 'tr':
+                               if ( $this->endCell() ) {
+                                       $this->insertToken( $token, $value, $attribs, $selfClose );
+                               }
+                               return true;
+                       }
+               } elseif ( $token === 'endtag' ) {
+                       switch ( $value ) {
+                       case 'td':
+                       case 'th':
+                               if ( $this->stack->inTableScope( $value ) ) {
+                                       $this->stack->generateImpliedEndTags();
+                                       $this->stack->popTag( $value );
+                                       $this->afe->clearToMarker();
+                                       $this->switchMode( 'inRowMode' );
+                               }
+                               return true;
+                       // OMITTED: <body>
+                       case 'caption':
+                       case 'col':
+                       case 'colgroup':
+                       // OMITTED: <html>
+                               return true;
+
+                       case 'table':
+                       case 'tbody':
+                       case 'tfoot':
+                       case 'thead':
+                       case 'tr':
+                               if ( $this->stack->inTableScope( $value ) ) {
+                                       $this->stack->generateImpliedEndTags();
+                                       $this->stack->popTag( BalanceSets::$tableCellSet );
+                                       $this->afe->clearToMarker();
+                                       $this->switchMode( 'inRowMode' );
+                                       $this->insertToken( $token, $value, $attribs, $selfClose );
+                               }
+                               return true;
+                       }
+               }
+               // Anything else:
+               return $this->inBodyMode( $token, $value, $attribs, $selfClose );
+       }
+
+       private function inSelectMode( $token, $value, $attribs = null, $selfClose = false ) {
+               if ( $token === 'text' ) {
+                       $this->stack->insertText( $value );
+                       return true;
+               } elseif ( $token === 'eof' ) {
+                       return $this->inBodyMode( $token, $value, $attribs, $selfClose );
+               } elseif ( $token === 'tag' ) {
+                       switch ( $value ) {
+                       // OMITTED: <html>
+                       case 'option':
+                               if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) {
+                                       $this->stack->pop();
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               return true;
+                       case 'optgroup':
+                               if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) {
+                                       $this->stack->pop();
+                               }
+                               if ( $this->stack->currentNode->isHtmlNamed( 'optgroup' ) ) {
+                                       $this->stack->pop();
+                               }
+                               $this->stack->insertHTMLElement( $value, $attribs );
+                               return true;
+                       case 'select':
+                               $this->inSelectMode( 'endtag', $value ); // treat it like endtag
+                               return true;
+                       case 'input':
+                       case 'keygen':
+                       case 'textarea':
+                               if ( !$this->stack->inSelectScope( 'select' ) ) {
+                                       return true; // ignore token (fragment case)
+                               }
+                               $this->inSelectMode( 'endtag', 'select' );
+                               return $this->insertToken( $token, $value, $attribs, $selfClose );
+                       case 'script':
+                       case 'template':
+                               return $this->inHeadMode( $token, $value, $attribs, $selfClose );
+                       }
+               } elseif ( $token === 'endtag' ) {
+                       switch ( $value ) {
+                       case 'optgroup':
+                               if (
+                                       $this->stack->currentNode->isHtmlNamed( 'option' ) &&
+                                       $this->stack->length() >= 2 &&
+                                       $this->stack->node( $this->stack->length() - 2 )->isHtmlNamed( 'optgroup' )
+                               ) {
+                                       $this->stack->pop();
+                               }
+                               if ( $this->stack->currentNode->isHtmlNamed( 'optgroup' ) ) {
+                                       $this->stack->pop();
+                               }
+                               return true;
+                       case 'option':
+                               if ( $this->stack->currentNode->isHtmlNamed( 'option' ) ) {
+                                       $this->stack->pop();
+                               }
+                               return true;
+                       case 'select':
+                               if ( !$this->stack->inSelectScope( $value ) ) {
+                                       return true; // fragment case
+                               }
+                               $this->stack->popTag( $value );
+                               $this->resetInsertionMode();
+                               return true;
+                       case 'template':
+                               return $this->inHeadMode( $token, $value, $attribs, $selfClose );
+                       }
+               } elseif ( $token === 'comment' ) {
+                       $this->stack->insertComment( $value );
+                       return true;
+               }
+               // anything else: just ignore the token
+               return true;
+       }
+
+       private function inSelectInTableMode( $token, $value, $attribs = null, $selfClose = false ) {
+               switch ( $value ) {
+               case 'caption':
+               case 'table':
+               case 'tbody':
+               case 'tfoot':
+               case 'thead':
+               case 'tr':
+               case 'td':
+               case 'th':
+                       if ( $token === 'tag' ) {
+                               $this->inSelectInTableMode( 'endtag', 'select' );
+                               return $this->insertToken( $token, $value, $attribs, $selfClose );
+                       } elseif ( $token === 'endtag' ) {
+                               if ( $this->stack->inTableScope( $value ) ) {
+                                       $this->inSelectInTableMode( 'endtag', 'select' );
+                                       return $this->insertToken( $token, $value, $attribs, $selfClose );
+                               }
+                               return true;
+                       }
+               }
+               // anything else
+               return $this->inSelectMode( $token, $value, $attribs, $selfClose );
+       }
+
+       private function inTemplateMode( $token, $value, $attribs = null, $selfClose = false ) {
+               if ( $token === 'text' || $token === 'comment' ) {
+                       return $this->inBodyMode( $token, $value, $attribs, $selfClose );
+               } elseif ( $token === 'eof' ) {
+                       if ( $this->stack->indexOf( 'template' ) < 0 ) {
+                               $this->stopParsing();
+                       } else {
+                               $this->stack->popTag( 'template' );
+                               $this->afe->clearToMarker();
+                               array_pop( $this->templateInsertionModes );
+                               $this->resetInsertionMode();
+                               $this->insertToken( $token, $value, $attribs, $selfClose );
+                       }
+                       return true;
+               } elseif ( $token === 'tag' ) {
+                       switch ( $value ) {
+                       case 'base':
+                       case 'basefont':
+                       case 'bgsound':
+                       case 'link':
+                       case 'meta':
+                       case 'noframes':
+                       // OMITTED: <script>
+                       case 'style':
+                       case 'template':
+                       // OMITTED: <title>
+                               return $this->inHeadMode( $token, $value, $attribs, $selfClose );
+
+                       case 'caption':
+                       case 'colgroup':
+                       case 'tbody':
+                       case 'tfoot':
+                       case 'thead':
+                               return $this->switchModeAndReprocess(
+                                       'inTableMode', $token, $value, $attribs, $selfClose
+                               );
+
+                       case 'col':
+                               return $this->switchModeAndReprocess(
+                                       'inColumnGroupMode', $token, $value, $attribs, $selfClose
+                               );
+
+                       case 'tr':
+                               return $this->switchModeAndReprocess(
+                                       'inTableBodyMode', $token, $value, $attribs, $selfClose
+                               );
+
+                       case 'td':
+                       case 'th':
+                               return $this->switchModeAndReprocess(
+                                       'inRowMode', $token, $value, $attribs, $selfClose
+                               );
+                       }
+                       return $this->switchModeAndReprocess(
+                               'inBodyMode', $token, $value, $attribs, $selfClose
+                       );
+               } elseif ( $token === 'endtag' ) {
+                       switch ( $value ) {
+                       case 'template':
+                               return $this->inHeadMode( $token, $value, $attribs, $selfClose );
+                       }
+                       return true;
+               } else {
+                       Assert::invariant( false, "Bad token type: $token" );
+               }
+       }
+}
diff --git a/includes/tidy/Html5Internal.php b/includes/tidy/Html5Internal.php
new file mode 100644 (file)
index 0000000..4ad8200
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+namespace MediaWiki\Tidy;
+
+class Html5Internal extends TidyDriverBase {
+       private $balancer;
+       public function __construct( array $config ) {
+               parent::__construct( $config + [
+                       'strict' => true,
+                       'tidyCompat' => true,
+               ] );
+               $this->balancer = new Balancer( $this->config );
+       }
+
+       public function tidy( $text ) {
+               return $this->balancer->balance( $text );
+       }
+}
index ab19142..56d5ce7 100644 (file)
@@ -54,7 +54,7 @@ class RaggettWrapper {
                // Preserve empty li elements (T49673) by abusing Tidy's datafld hack
                // The whitespace class is as in TY_(InitMap)
                $wrappedtext = preg_replace( "!<li>([ \r\n\t\f]*)</li>!",
-                       '<li datafld="" class="mw-empty-li">\1</li>', $wrappedtext );
+                       '<li datafld="" class="mw-empty-elt">\1</li>', $wrappedtext );
 
                // Wrap the whole thing in a doctype and body for Tidy.
                $wrappedtext = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"' .
index 08fbeea..1be5c24 100644 (file)
@@ -44,7 +44,7 @@ abstract class UploadBase {
        protected $mDesiredDestName, $mDestName, $mRemoveTempFile, $mSourceType;
        protected $mTitle = false, $mTitleError = 0;
        protected $mFilteredName, $mFinalExtension;
-       protected $mLocalFile, $mFileSize, $mFileProps;
+       protected $mLocalFile, $mStashFile, $mFileSize, $mFileProps;
        protected $mBlackListedExtensions;
        protected $mJavaDetected, $mSVGNSError;
 
@@ -53,7 +53,16 @@ abstract class UploadBase {
                'ISO-8859-1',
                'ISO-8859-2',
                'UTF-16',
-               'UTF-32'
+               'UTF-32',
+               'WINDOWS-1250',
+               'WINDOWS-1251',
+               'WINDOWS-1252',
+               'WINDOWS-1253',
+               'WINDOWS-1254',
+               'WINDOWS-1255',
+               'WINDOWS-1256',
+               'WINDOWS-1257',
+               'WINDOWS-1258',
        ];
 
        const SUCCESS = 0;
@@ -467,9 +476,13 @@ abstract class UploadBase {
                        }
                }
 
-               Hooks::run( 'UploadVerifyFile', [ $this, $mime, &$status ] );
-               if ( $status !== true ) {
-                       return $status;
+               $error = true;
+               Hooks::run( 'UploadVerifyFile', [ $this, $mime, &$error ] );
+               if ( $error !== true ) {
+                       if ( !is_array( $error ) ) {
+                               $error = [ $error ];
+                       }
+                       return $error;
                }
 
                wfDebug( __METHOD__ . ": all clear; passing.\n" );
@@ -666,9 +679,23 @@ abstract class UploadBase {
                        $warnings['empty-file'] = true;
                }
 
+               $hash = $this->getTempFileSha1Base36();
                $exists = self::getExistsWarning( $localFile );
                if ( $exists !== false ) {
                        $warnings['exists'] = $exists;
+
+                       // check if file is an exact duplicate of current file version
+                       if ( $hash === $localFile->getSha1() ) {
+                               $warnings['no-change'] = $localFile;
+                       }
+
+                       // check if file is an exact duplicate of older versions of this file
+                       $history = $localFile->getHistory();
+                       foreach ( $history as $oldFile ) {
+                               if ( $hash === $oldFile->getSha1() ) {
+                                       $warnings['duplicate-version'][] = $oldFile;
+                               }
+                       }
                }
 
                if ( $localFile->wasDeleted() && !$localFile->exists() ) {
@@ -676,7 +703,6 @@ abstract class UploadBase {
                }
 
                // Check dupes against existing files
-               $hash = $this->getTempFileSha1Base36();
                $dupes = RepoGroup::singleton()->findBySha1( $hash );
                $title = $this->getTitle();
                // Remove all matches against self
@@ -717,13 +743,23 @@ abstract class UploadBase {
         */
        public function performUpload( $comment, $pageText, $watch, $user, $tags = [] ) {
                $this->getLocalFile()->load( File::READ_LATEST );
+               $props = $this->mFileProps;
+
+               $error = null;
+               Hooks::run( 'UploadVerifyUpload', [ $this, $user, $props, $comment, $pageText, &$error ] );
+               if ( $error ) {
+                       if ( !is_array( $error ) ) {
+                               $error = [ $error ];
+                       }
+                       return call_user_func_array( 'Status::newFatal', $error );
+               }
 
                $status = $this->getLocalFile()->upload(
                        $this->mTempPath,
                        $comment,
                        $pageText,
                        File::DELETE_SOURCE,
-                       $this->mFileProps,
+                       $props,
                        false,
                        $user,
                        $tags
@@ -751,27 +787,6 @@ abstract class UploadBase {
         * @since  1.25
         */
        public function postProcessUpload() {
-               global $wgUploadThumbnailRenderMap;
-
-               $jobs = [];
-
-               $sizes = $wgUploadThumbnailRenderMap;
-               rsort( $sizes );
-
-               $file = $this->getLocalFile();
-
-               foreach ( $sizes as $size ) {
-                       if ( $file->isVectorized() || $file->getWidth() > $size ) {
-                               $jobs[] = new ThumbnailRenderJob(
-                                       $file->getTitle(),
-                                       [ 'transformParams' => [ 'width' => $size ] ]
-                               );
-                       }
-               }
-
-               if ( $jobs ) {
-                       JobQueueGroup::singleton()->push( $jobs );
-               }
        }
 
        /**
@@ -910,7 +925,7 @@ abstract class UploadBase {
        /**
         * Return the local file and initializes if necessary.
         *
-        * @return LocalFile|UploadStashFile|null
+        * @return LocalFile|null
         */
        public function getLocalFile() {
                if ( is_null( $this->mLocalFile ) ) {
@@ -921,6 +936,55 @@ abstract class UploadBase {
                return $this->mLocalFile;
        }
 
+       /**
+        * @return UploadStashFile|null
+        */
+       public function getStashFile() {
+               return $this->mStashFile;
+       }
+
+       /**
+        * Like stashFile(), but respects extensions' wishes to prevent the stashing. verifyUpload() must
+        * be called before calling this method (unless $isPartial is true).
+        *
+        * Upload stash exceptions are also caught and converted to an error status.
+        *
+        * @since 1.28
+        * @param User $user
+        * @param bool $isPartial Pass `true` if this is a part of a chunked upload (not a complete file).
+        * @return Status If successful, value is an UploadStashFile instance
+        */
+       public function tryStashFile( User $user, $isPartial = false ) {
+               if ( !$isPartial ) {
+                       $error = $this->runUploadStashFileHook( $user );
+                       if ( $error ) {
+                               return call_user_func_array( 'Status::newFatal', $error );
+                       }
+               }
+               try {
+                       $file = $this->doStashFile( $user );
+                       return Status::newGood( $file );
+               } catch ( UploadStashException $e ) {
+                       return Status::newFatal( 'uploadstash-exception', get_class( $e ), $e->getMessage() );
+               }
+       }
+
+       /**
+        * @param User $user
+        * @return array|null Error message and parameters, null if there's no error
+        */
+       protected function runUploadStashFileHook( User $user ) {
+               $props = $this->mFileProps;
+               $error = null;
+               Hooks::run( 'UploadStashFile', [ $this, $user, $props, &$error ] );
+               if ( $error ) {
+                       if ( !is_array( $error ) ) {
+                               $error = [ $error ];
+                       }
+               }
+               return $error;
+       }
+
        /**
         * If the user does not supply all necessary information in the first upload
         * form submission (either by accident or by design) then we may want to
@@ -933,15 +997,27 @@ abstract class UploadBase {
         * which can be passed through a form or API request to find this stashed
         * file again.
         *
+        * @deprecated since 1.28 Use tryStashFile() instead
         * @param User $user
         * @return UploadStashFile Stashed file
+        * @throws UploadStashBadPathException
+        * @throws UploadStashFileException
+        * @throws UploadStashNotLoggedInException
         */
        public function stashFile( User $user = null ) {
-               // was stashSessionFile
+               return $this->doStashFile( $user );
+       }
 
+       /**
+        * Implementation for stashFile() and tryStashFile().
+        *
+        * @param User $user
+        * @return UploadStashFile Stashed file
+        */
+       protected function doStashFile( User $user = null ) {
                $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash( $user );
                $file = $stash->stashFile( $this->mTempPath, $this->getSourceType() );
-               $this->mLocalFile = $file;
+               $this->mStashFile = $file;
 
                return $file;
        }
@@ -950,19 +1026,23 @@ abstract class UploadBase {
         * Stash a file in a temporary directory, returning a key which can be used
         * to find the file again. See stashFile().
         *
+        * @deprecated since 1.28
         * @return string File key
         */
        public function stashFileGetKey() {
-               return $this->stashFile()->getFileKey();
+               wfDeprecated( __METHOD__, '1.28' );
+               return $this->doStashFile()->getFileKey();
        }
 
        /**
         * alias for stashFileGetKey, for backwards compatibility
         *
+        * @deprecated since 1.28
         * @return string File key
         */
        public function stashSession() {
-               return $this->stashFileGetKey();
+               wfDeprecated( __METHOD__, '1.28' );
+               return $this->doStashFile()->getFileKey();
        }
 
        /**
@@ -1592,7 +1672,7 @@ abstract class UploadBase {
         * @return array Containing the namespace URI and prefix
         */
        private static function splitXmlNamespace( $element ) {
-               // 'http://www.w3.org/2000/svg:script' -> array( 'http://www.w3.org/2000/svg', 'script' )
+               // 'http://www.w3.org/2000/svg:script' -> [ 'http://www.w3.org/2000/svg', 'script' ]
                $parts = explode( ':', strtolower( $element ) );
                $name = array_pop( $parts );
                $ns = implode( ':', $parts );
@@ -1915,18 +1995,16 @@ abstract class UploadBase {
         * @return array Image info
         */
        public function getImageInfo( $result ) {
-               $file = $this->getLocalFile();
-               /** @todo This cries out for refactoring.
-                *  We really want to say $file->getAllInfo(); here.
-                * Perhaps "info" methods should be moved into files, and the API should
-                * just wrap them in queries.
-                */
-               if ( $file instanceof UploadStashFile ) {
+               $localFile = $this->getLocalFile();
+               $stashFile = $this->getStashFile();
+               // Calling a different API module depending on whether the file was stashed is less than optimal.
+               // In fact, calling API modules here at all is less than optimal. Maybe it should be refactored.
+               if ( $stashFile ) {
                        $imParam = ApiQueryStashImageInfo::getPropertyNames();
-                       $info = ApiQueryStashImageInfo::getInfo( $file, array_flip( $imParam ), $result );
+                       $info = ApiQueryStashImageInfo::getInfo( $stashFile, array_flip( $imParam ), $result );
                } else {
                        $imParam = ApiQueryImageInfo::getPropertyNames();
-                       $info = ApiQueryImageInfo::getInfo( $file, array_flip( $imParam ), $result );
+                       $info = ApiQueryImageInfo::getInfo( $localFile, array_flip( $imParam ), $result );
                }
 
                return $info;
index 0323b68..9145a85 100644 (file)
@@ -38,12 +38,11 @@ class UploadFromChunks extends UploadFromFile {
        /**
         * Setup local pointers to stash, repo and user (similar to UploadFromStash)
         *
-        * @param User|null $user Default: null
+        * @param User $user
         * @param UploadStash|bool $stash Default: false
         * @param FileRepo|bool $repo Default: false
         */
-       public function __construct( $user = null, $stash = false, $repo = false ) {
-               // user object. sometimes this won't exist, as when running from cron.
+       public function __construct( User $user, $stash = false, $repo = false ) {
                $this->user = $user;
 
                if ( $repo ) {
@@ -65,30 +64,30 @@ class UploadFromChunks extends UploadFromFile {
        }
 
        /**
-        * Calls the parent stashFile and updates the uploadsession table to handle "chunks"
+        * Calls the parent doStashFile and updates the uploadsession table to handle "chunks"
         *
         * @param User|null $user
         * @return UploadStashFile Stashed file
         */
-       public function stashFile( User $user = null ) {
+       protected function doStashFile( User $user = null ) {
                // Stash file is the called on creating a new chunk session:
                $this->mChunkIndex = 0;
                $this->mOffset = 0;
 
                $this->verifyChunk();
                // Create a local stash target
-               $this->mLocalFile = parent::stashFile( $user );
+               $this->mStashFile = parent::doStashFile( $user );
                // Update the initial file offset (based on file size)
-               $this->mOffset = $this->mLocalFile->getSize();
-               $this->mFileKey = $this->mLocalFile->getFileKey();
+               $this->mOffset = $this->mStashFile->getSize();
+               $this->mFileKey = $this->mStashFile->getFileKey();
 
                // Output a copy of this first to chunk 0 location:
-               $this->outputChunk( $this->mLocalFile->getPath() );
+               $this->outputChunk( $this->mStashFile->getPath() );
 
                // Update db table to reflect initial "chunk" state
                $this->updateChunkStatus();
 
-               return $this->mLocalFile;
+               return $this->mStashFile;
        }
 
        /**
@@ -159,12 +158,25 @@ class UploadFromChunks extends UploadFromFile {
                        return $status;
                }
 
-               // Update the mTempPath and mLocalFile
+               // Update the mTempPath and mStashFile
                // (for FileUpload or normal Stash to take over)
                $tStart = microtime( true );
-               $this->mLocalFile = parent::stashFile( $this->user );
+               // This is a re-implementation of UploadBase::tryStashFile(), we can't call it because we
+               // override doStashFile() with completely different functionality in this class...
+               $error = $this->runUploadStashFileHook( $this->user );
+               if ( $error ) {
+                       call_user_func_array( [ $status, 'fatal' ], $error );
+                       return $status;
+               }
+               try {
+                       $this->mStashFile = parent::doStashFile( $this->user );
+               } catch ( UploadStashException $e ) {
+                       $status->fatal( 'uploadstash-exception', get_class( $e ), $e->getMessage() );
+                       return $status;
+               }
+
                $tAmount = microtime( true ) - $tStart;
-               $this->mLocalFile->setLocalReference( $tmpFile ); // reuse (e.g. for getImageInfo())
+               $this->mStashFile->setLocalReference( $tmpFile ); // reuse (e.g. for getImageInfo())
                wfDebugLog( 'fileconcatenate', "Stashed combined file ($i chunks) in $tAmount seconds." );
 
                return $status;
@@ -235,8 +247,6 @@ class UploadFromChunks extends UploadFromFile {
                        $this->getOffset() . ' inx:' . $this->getChunkIndex() . "\n" );
 
                $dbw = $this->repo->getMasterDB();
-               // Use a quick transaction since we will upload the full temp file into shared
-               // storage, which takes time for large files. We don't want to hold locks then.
                $dbw->update(
                        'uploadstash',
                        [
@@ -247,7 +257,6 @@ class UploadFromChunks extends UploadFromFile {
                        [ 'us_key' => $this->mFileKey ],
                        __METHOD__
                );
-               $dbw->commit( __METHOD__, 'flush' );
        }
 
        /**
index 1276842..1fbdb7d 100644 (file)
@@ -143,32 +143,6 @@ class UploadFromStash extends UploadBase {
                return $this->mFileProps['sha1'];
        }
 
-       /*
-        * protected function verifyFile() inherited
-        */
-
-       /**
-        * Stash the file.
-        *
-        * @param User $user
-        * @return UploadStashFile
-        */
-       public function stashFile( User $user = null ) {
-               // replace mLocalFile with an instance of UploadStashFile, which adds some methods
-               // that are useful for stashed files.
-               $this->mLocalFile = parent::stashFile( $user );
-
-               return $this->mLocalFile;
-       }
-
-       /**
-        * This should return the key instead of the UploadStashFile instance, for backward compatibility.
-        * @return string
-        */
-       public function stashSession() {
-               return $this->stashFile()->getFileKey();
-       }
-
        /**
         * Remove a temporarily kept file stashed by saveTempUploadedFile().
         * @return bool Success
index c171ded..000c6a4 100644 (file)
@@ -499,10 +499,10 @@ class UploadStash {
         * Helper function: do the actual database query to fetch file metadata.
         *
         * @param string $key
-        * @param int $readFromDB Constant (default: DB_SLAVE)
+        * @param int $readFromDB Constant (default: DB_REPLICA)
         * @return bool
         */
-       protected function fetchFileMetadata( $key, $readFromDB = DB_SLAVE ) {
+       protected function fetchFileMetadata( $key, $readFromDB = DB_REPLICA ) {
                // populate $fileMetadata[$key]
                $dbr = null;
                if ( $readFromDB === DB_MASTER ) {
index 49a7163..4ce3cde 100644 (file)
@@ -67,7 +67,7 @@ class BotPassword implements IDBAccessObject {
 
        /**
         * Get a database connection for the bot passwords database
-        * @param int $db Index of the connection to get, e.g. DB_MASTER or DB_SLAVE.
+        * @param int $db Index of the connection to get, e.g. DB_MASTER or DB_REPLICA.
         * @return DatabaseBase
         */
        public static function getDB( $db ) {
index f7c5408..0d8b1a8 100644 (file)
@@ -60,7 +60,7 @@ class LocalIdLookup extends CentralIdLookup {
                }
 
                $audience = $this->checkAudience( $audience );
-               $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+               $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
                $options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
                        ? [ 'LOCK IN SHARE MODE' ]
                        : [];
@@ -93,7 +93,7 @@ class LocalIdLookup extends CentralIdLookup {
                }
 
                $audience = $this->checkAudience( $audience );
-               $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
+               $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
                $options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
                        ? [ 'LOCK IN SHARE MODE' ]
                        : [];
index bc87cd0..889ec92 100644 (file)
@@ -236,7 +236,7 @@ class PasswordReset {
         * @throws MWException On unexpected database errors
         */
        protected function getUsersByEmail( $email ) {
-               $res = wfGetDB( DB_SLAVE )->select(
+               $res = wfGetDB( DB_REPLICA )->select(
                        'user',
                        User::selectFields(),
                        [ 'user_email' => $email ],
index 40b5b40..7109a4a 100644 (file)
@@ -473,8 +473,8 @@ class User implements IDBAccessObject {
                $data = $cache->getWithSetCallback(
                        $this->getCacheKey( $cache ),
                        $cache::TTL_HOUR,
-                       function ( $oldValue, &$ttl, array &$setOpts ) {
-                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) );
+                       function ( $oldValue, &$ttl, array &$setOpts ) use ( $cache ) {
+                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
                                wfDebug( "User: cache miss for user {$this->mId}\n" );
 
                                $this->loadFromDatabase( self::READ_NORMAL );
@@ -486,6 +486,8 @@ class User implements IDBAccessObject {
                                        $data[$name] = $this->$name;
                                }
 
+                               $ttl = $cache->adaptiveTTL( wfTimestamp( TS_UNIX, $this->mTouched ), $ttl );
+
                                return $data;
 
                        },
@@ -564,7 +566,7 @@ class User implements IDBAccessObject {
        public static function newFromConfirmationCode( $code, $flags = 0 ) {
                $db = ( $flags & self::READ_LATEST ) == self::READ_LATEST
                        ? wfGetDB( DB_MASTER )
-                       : wfGetDB( DB_SLAVE );
+                       : wfGetDB( DB_REPLICA );
 
                $id = $db->selectField(
                        'user',
@@ -648,8 +650,6 @@ class User implements IDBAccessObject {
         * @since 1.27
         */
        public static function newSystemUser( $name, $options = [] ) {
-               global $wgDisableAuthManager;
-
                $options += [
                        'validate' => 'valid',
                        'create' => true,
@@ -662,9 +662,6 @@ class User implements IDBAccessObject {
                }
 
                $fields = self::selectFields();
-               if ( $wgDisableAuthManager ) {
-                       $fields = array_merge( $fields, [ 'user_password', 'user_newpassword' ] );
-               }
 
                $dbw = wfGetDB( DB_MASTER );
                $row = $dbw->selectRow(
@@ -681,49 +678,15 @@ class User implements IDBAccessObject {
 
                // A user is considered to exist as a non-system user if it can
                // authenticate, or has an email set, or has a non-invalid token.
-               if ( !$user->mEmail && $user->mToken === self::INVALID_TOKEN ) {
-                       if ( $wgDisableAuthManager ) {
-                               $passwordFactory = new PasswordFactory();
-                               $passwordFactory->init( RequestContext::getMain()->getConfig() );
-                               try {
-                                       $password = $passwordFactory->newFromCiphertext( $row->user_password );
-                               } catch ( PasswordError $e ) {
-                                       wfDebug( 'Invalid password hash found in database.' );
-                                       $password = PasswordFactory::newInvalidPassword();
-                               }
-                               try {
-                                       $newpassword = $passwordFactory->newFromCiphertext( $row->user_newpassword );
-                               } catch ( PasswordError $e ) {
-                                       wfDebug( 'Invalid password hash found in database.' );
-                                       $newpassword = PasswordFactory::newInvalidPassword();
-                               }
-                               $canAuthenticate = !$password instanceof InvalidPassword ||
-                                       !$newpassword instanceof InvalidPassword;
-                       } else {
-                               $canAuthenticate = AuthManager::singleton()->userCanAuthenticate( $name );
-                       }
-               }
-               if ( $user->mEmail || $user->mToken !== self::INVALID_TOKEN || $canAuthenticate ) {
+               if ( $user->mEmail || $user->mToken !== self::INVALID_TOKEN ||
+                       AuthManager::singleton()->userCanAuthenticate( $name )
+               ) {
                        // User exists. Steal it?
                        if ( !$options['steal'] ) {
                                return null;
                        }
 
-                       if ( $wgDisableAuthManager ) {
-                               $nopass = PasswordFactory::newInvalidPassword()->toString();
-                               $dbw->update(
-                                       'user',
-                                       [
-                                               'user_password' => $nopass,
-                                               'user_newpassword' => $nopass,
-                                               'user_newpass_time' => null,
-                                       ],
-                                       [ 'user_id' => $user->getId() ],
-                                       __METHOD__
-                               );
-                       } else {
-                               AuthManager::singleton()->revokeAccessForUser( $name );
-                       }
+                       AuthManager::singleton()->revokeAccessForUser( $name );
 
                        $user->invalidateEmail();
                        $user->mToken = self::INVALID_TOKEN;
@@ -772,15 +735,15 @@ class User implements IDBAccessObject {
                        return self::$idCacheByName[$name];
                }
 
-               $db = ( $flags & self::READ_LATEST )
-                       ? wfGetDB( DB_MASTER )
-                       : wfGetDB( DB_SLAVE );
+               list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
+               $db = wfGetDB( $index );
 
                $s = $db->selectRow(
                        'user',
                        [ 'user_id' ],
                        [ 'user_name' => $nt->getText() ],
-                       __METHOD__
+                       __METHOD__,
+                       $options
                );
 
                if ( $s === false ) {
@@ -911,6 +874,44 @@ class User implements IDBAccessObject {
                return true;
        }
 
+       /**
+        * Return the users who are members of the given group(s). In case of multiple groups,
+        * users who are members of at least one of them are returned.
+        *
+        * @param string|array $groups A single group name or an array of group names
+        * @param int $limit Max number of users to return. The actual limit will never exceed 5000
+        *   records; larger values are ignored.
+        * @param int $after ID the user to start after
+        * @return UserArrayFromResult
+        */
+       public static function findUsersByGroup( $groups, $limit = 5000, $after = null ) {
+               if ( $groups === [] ) {
+                       return UserArrayFromResult::newFromIDs( [] );
+               }
+
+               $groups = array_unique( (array)$groups );
+               $limit = min( 5000, $limit );
+
+               $conds = [ 'ug_group' => $groups ];
+               if ( $after !== null ) {
+                       $conds[] = 'ug_user > ' . (int)$after;
+               }
+
+               $dbr = wfGetDB( DB_REPLICA );
+               $ids = $dbr->selectFieldValues(
+                       'user_groups',
+                       'ug_user',
+                       $conds,
+                       __METHOD__,
+                       [
+                               'DISTINCT' => true,
+                               'ORDER BY' => 'ug_user',
+                               'LIMIT' => $limit,
+                       ]
+               ) ?: [];
+               return UserArray::newFromIDs( $ids );
+       }
+
        /**
         * Usernames which fail to pass this function will be blocked
         * from new account registrations, but may be used internally
@@ -1349,8 +1350,6 @@ class User implements IDBAccessObject {
         */
        protected function loadFromUserObject( $user ) {
                $user->load();
-               $user->loadGroups();
-               $user->loadOptions();
                foreach ( self::$mCacheVars as $var ) {
                        $this->$var = $user->$var;
                }
@@ -1363,7 +1362,7 @@ class User implements IDBAccessObject {
                if ( is_null( $this->mGroups ) ) {
                        $db = ( $this->queryFlagsUsed & self::READ_LATEST )
                                ? wfGetDB( DB_MASTER )
-                               : wfGetDB( DB_SLAVE );
+                               : wfGetDB( DB_REPLICA );
                        $res = $db->select( 'user_groups',
                                [ 'ug_group' ],
                                [ 'ug_user' => $this->mId ],
@@ -1524,22 +1523,28 @@ class User implements IDBAccessObject {
                global $wgNamespacesToBeSearchedDefault, $wgDefaultUserOptions, $wgContLang, $wgDefaultSkin;
 
                static $defOpt = null;
-               if ( !defined( 'MW_PHPUNIT_TEST' ) && $defOpt !== null ) {
-                       // Disabling this for the unit tests, as they rely on being able to change $wgContLang
-                       // mid-request and see that change reflected in the return value of this function.
-                       // Which is insane and would never happen during normal MW operation
+               static $defOptLang = null;
+
+               if ( $defOpt !== null && $defOptLang === $wgContLang->getCode() ) {
+                       // $wgContLang does not change (and should not change) mid-request,
+                       // but the unit tests change it anyway, and expect this method to
+                       // return values relevant to the current $wgContLang.
                        return $defOpt;
                }
 
                $defOpt = $wgDefaultUserOptions;
                // Default language setting
-               $defOpt['language'] = $wgContLang->getCode();
+               $defOptLang = $wgContLang->getCode();
+               $defOpt['language'] = $defOptLang;
                foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
                        $defOpt[$langCode == $wgContLang->getCode() ? 'variant' : "variant-$langCode"] = $langCode;
                }
-               $namespaces = MediaWikiServices::getInstance()->getSearchEngineConfig()->searchableNamespaces();
-               foreach ( $namespaces as $nsnum => $nsname ) {
-                       $defOpt['searchNs' . $nsnum] = !empty( $wgNamespacesToBeSearchedDefault[$nsnum] );
+
+               // NOTE: don't use SearchEngineConfig::getSearchableNamespaces here,
+               // 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['skin'] = Skin::normalizeKey( $wgDefaultSkin );
 
@@ -1565,8 +1570,8 @@ class User implements IDBAccessObject {
 
        /**
         * Get blocking information
-        * @param bool $bFromSlave Whether to check the slave database first.
-        *   To improve performance, non-critical checks are done against slaves.
+        * @param bool $bFromSlave Whether to check the replica DB first.
+        *   To improve performance, non-critical checks are done against replica DBs.
         *   Check when actually saving should be done against master.
         */
        private function getBlockedStatus( $bFromSlave = true ) {
@@ -1917,7 +1922,7 @@ class User implements IDBAccessObject {
        /**
         * Check if user is blocked
         *
-        * @param bool $bFromSlave Whether to check the slave database instead of
+        * @param bool $bFromSlave Whether to check the replica DB instead of
         *   the master. Hacked from false due to horrible probs on site.
         * @return bool True if blocked, false otherwise
         */
@@ -1928,7 +1933,7 @@ class User implements IDBAccessObject {
        /**
         * Get the block affecting the user, or null if the user is not blocked
         *
-        * @param bool $bFromSlave Whether to check the slave database instead of the master
+        * @param bool $bFromSlave Whether to check the replica DB instead of the master
         * @return Block|null
         */
        public function getBlock( $bFromSlave = true ) {
@@ -1940,7 +1945,7 @@ class User implements IDBAccessObject {
         * Check if user is blocked from editing a particular article
         *
         * @param Title $title Title to check
-        * @param bool $bFromSlave Whether to check the slave database instead of the master
+        * @param bool $bFromSlave Whether to check the replica DB instead of the master
         * @return bool
         */
        public function isBlockedFrom( $title, $bFromSlave = false ) {
@@ -2185,7 +2190,7 @@ class User implements IDBAccessObject {
                        return [];
                }
                $utp = $this->getTalkPage();
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                // Get the "last viewed rev" timestamp from the oldest message notification
                $timestamp = $dbr->selectField( 'user_newtalk',
                        'MIN(user_last_timestamp)',
@@ -2228,7 +2233,7 @@ class User implements IDBAccessObject {
         * @return bool True if the user has new messages
         */
        protected function checkNewtalk( $field, $id ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $ok = $dbr->selectField( 'user_newtalk', $field, [ $field => $id ], __METHOD__ );
 
@@ -2469,31 +2474,7 @@ class User implements IDBAccessObject {
         * @return bool
         */
        public function setPassword( $str ) {
-               global $wgAuth, $wgDisableAuthManager;
-
-               if ( !$wgDisableAuthManager ) {
-                       return $this->setPasswordInternal( $str );
-               }
-
-               if ( $str !== null ) {
-                       if ( !$wgAuth->allowPasswordChange() ) {
-                               throw new PasswordError( wfMessage( 'password-change-forbidden' )->text() );
-                       }
-
-                       $status = $this->checkPasswordValidity( $str );
-                       if ( !$status->isGood() ) {
-                               throw new PasswordError( $status->getMessage()->text() );
-                       }
-               }
-
-               if ( !$wgAuth->setPassword( $this, $str ) ) {
-                       throw new PasswordError( wfMessage( 'externaldberror' )->text() );
-               }
-
-               $this->setOption( 'watchlisttoken', false );
-               $this->setPasswordInternal( $str );
-
-               return true;
+               return $this->setPasswordInternal( $str );
        }
 
        /**
@@ -2505,16 +2486,7 @@ class User implements IDBAccessObject {
         *  through the web interface.
         */
        public function setInternalPassword( $str ) {
-               global $wgAuth, $wgDisableAuthManager;
-
-               if ( !$wgDisableAuthManager ) {
-                       $this->setPasswordInternal( $str );
-               }
-
-               if ( $wgAuth->allowSetLocalPassword() ) {
-                       $this->setOption( 'watchlisttoken', false );
-                       $this->setPasswordInternal( $str );
-               }
+               $this->setPasswordInternal( $str );
        }
 
        /**
@@ -2526,55 +2498,26 @@ class User implements IDBAccessObject {
         * @return bool Success
         */
        private function setPasswordInternal( $str ) {
-               global $wgDisableAuthManager;
-
-               if ( $wgDisableAuthManager ) {
-                       $id = self::idFromName( $this->getName(), self::READ_LATEST );
-                       if ( $id == 0 ) {
-                               throw new LogicException( 'Cannot set a password for a user that is not in the database.' );
-                       }
-
-                       $passwordFactory = new PasswordFactory();
-                       $passwordFactory->init( RequestContext::getMain()->getConfig() );
-                       $dbw = wfGetDB( DB_MASTER );
-                       $dbw->update(
-                               'user',
-                               [
-                                       'user_password' => $passwordFactory->newFromPlaintext( $str )->toString(),
-                                       'user_newpassword' => PasswordFactory::newInvalidPassword()->toString(),
-                                       'user_newpass_time' => $dbw->timestampOrNull( null ),
-                               ],
-                               [
-                                       'user_id' => $id,
-                               ],
-                               __METHOD__
-                       );
-
-                       // When the main password is changed, invalidate all bot passwords too
-                       BotPassword::invalidateAllPasswordsForUser( $this->getName() );
-               } else {
-                       $manager = AuthManager::singleton();
-
-                       // If the user doesn't exist yet, fail
-                       if ( !$manager->userExists( $this->getName() ) ) {
-                               throw new LogicException( 'Cannot set a password for a user that is not in the database.' );
-                       }
+               $manager = AuthManager::singleton();
 
-                       $status = $this->changeAuthenticationData( [
-                               'username' => $this->getName(),
-                               'password' => $str,
-                               'retype' => $str,
-                       ] );
-                       if ( !$status->isGood() ) {
-                               \MediaWiki\Logger\LoggerFactory::getInstance( 'authentication' )
-                                       ->info( __METHOD__ . ': Password change rejected: '
-                                               . $status->getWikiText( null, null, 'en' ) );
-                               return false;
-                       }
+               // If the user doesn't exist yet, fail
+               if ( !$manager->userExists( $this->getName() ) ) {
+                       throw new LogicException( 'Cannot set a password for a user that is not in the database.' );
+               }
 
-                       $this->setOption( 'watchlisttoken', false );
+               $status = $this->changeAuthenticationData( [
+                       'username' => $this->getName(),
+                       'password' => $str,
+                       'retype' => $str,
+               ] );
+               if ( !$status->isGood() ) {
+                       \MediaWiki\Logger\LoggerFactory::getInstance( 'authentication' )
+                               ->info( __METHOD__ . ': Password change rejected: '
+                                       . $status->getWikiText( null, null, 'en' ) );
+                       return false;
                }
 
+               $this->setOption( 'watchlisttoken', false );
                SessionManager::singleton()->invalidateSessionsForUser( $this );
 
                return true;
@@ -2593,12 +2536,6 @@ class User implements IDBAccessObject {
         * @since 1.27
         */
        public function changeAuthenticationData( array $data ) {
-               global $wgDisableAuthManager;
-               if ( $wgDisableAuthManager ) {
-                       throw new LogicException( __METHOD__ . ' cannot be called when $wgDisableAuthManager '
-                               . 'is true' );
-               }
-
                $manager = AuthManager::singleton();
                $reqs = $manager->getAuthenticationRequests( AuthManager::ACTION_CHANGE, $this );
                $reqs = AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
@@ -2684,32 +2621,7 @@ class User implements IDBAccessObject {
         * @param bool $throttle If true, reset the throttle timestamp to the present
         */
        public function setNewpassword( $str, $throttle = true ) {
-               global $wgDisableAuthManager;
-
-               if ( $wgDisableAuthManager ) {
-                       $id = $this->getId();
-                       if ( $id == 0 ) {
-                               throw new LogicException( 'Cannot set new password for a user that is not in the database.' );
-                       }
-
-                       $dbw = wfGetDB( DB_MASTER );
-
-                       $passwordFactory = new PasswordFactory();
-                       $passwordFactory->init( RequestContext::getMain()->getConfig() );
-                       $update = [
-                               'user_newpassword' => $passwordFactory->newFromPlaintext( $str )->toString(),
-                       ];
-
-                       if ( $str === null ) {
-                               $update['user_newpass_time'] = null;
-                       } elseif ( $throttle ) {
-                               $update['user_newpass_time'] = $dbw->timestamp();
-                       }
-
-                       $dbw->update( 'user', $update, [ 'user_id' => $id ], __METHOD__ );
-               } else {
-                       throw new BadMethodCallException( __METHOD__ . ' has been removed in 1.27' );
-               }
+               throw new BadMethodCallException( __METHOD__ . ' has been removed in 1.27' );
        }
 
        /**
@@ -2719,33 +2631,7 @@ class User implements IDBAccessObject {
         * @return bool
         */
        public function isPasswordReminderThrottled() {
-               global $wgPasswordReminderResendTime, $wgDisableAuthManager;
-
-               if ( $wgDisableAuthManager ) {
-                       if ( !$wgPasswordReminderResendTime ) {
-                               return false;
-                       }
-
-                       $this->load();
-
-                       $db = ( $this->queryFlagsUsed & self::READ_LATEST )
-                               ? wfGetDB( DB_MASTER )
-                               : wfGetDB( DB_SLAVE );
-                       $newpassTime = $db->selectField(
-                               'user',
-                               'user_newpass_time',
-                               [ 'user_id' => $this->getId() ],
-                               __METHOD__
-                       );
-
-                       if ( $newpassTime === null ) {
-                               return false;
-                       }
-                       $expiry = wfTimestamp( TS_UNIX, $newpassTime ) + $wgPasswordReminderResendTime * 3600;
-                       return time() < $expiry;
-               } else {
-                       throw new BadMethodCallException( __METHOD__ . ' has been removed in 1.27' );
-               }
+               throw new BadMethodCallException( __METHOD__ . ' has been removed in 1.27' );
        }
 
        /**
@@ -2977,7 +2863,7 @@ class User implements IDBAccessObject {
         * @return string|bool User's current value for the option, or false if this option is disabled.
         * @see resetTokenFromOption()
         * @see getOption()
-        * @deprecated 1.26 Applications should use the OAuth extension
+        * @deprecated since 1.26 Applications should use the OAuth extension
         */
        public function getTokenFromOption( $oname ) {
                global $wgHiddenPrefs;
@@ -3250,6 +3136,7 @@ class User implements IDBAccessObject {
        public function getRights() {
                if ( is_null( $this->mRights ) ) {
                        $this->mRights = self::getGroupPermissions( $this->getEffectiveGroups() );
+                       Hooks::run( 'UserGetRights', [ $this, &$this->mRights ] );
 
                        // Deny any rights denied by the user's session, unless this
                        // endpoint has no sessions.
@@ -3260,9 +3147,24 @@ class User implements IDBAccessObject {
                                }
                        }
 
-                       Hooks::run( 'UserGetRights', [ $this, &$this->mRights ] );
                        // Force reindexation of rights when a hook has unset one of them
                        $this->mRights = array_values( array_unique( $this->mRights ) );
+
+                       // If block disables login, we should also remove any
+                       // extra rights blocked users might have, in case the
+                       // blocked user has a pre-existing session (T129738).
+                       // This is checked here for cases where people only call
+                       // $user->isAllowed(). It is also checked in Title::checkUserBlock()
+                       // to give a better error message in the common case.
+                       $config = RequestContext::getMain()->getConfig();
+                       if (
+                               $this->isLoggedIn() &&
+                               $config->get( 'BlockDisablesLogin' ) &&
+                               $this->isBlocked()
+                       ) {
+                               $anon = new User;
+                               $this->mRights = array_intersect( $this->mRights, $anon->getRights() );
+                       }
                }
                return $this->mRights;
        }
@@ -3341,7 +3243,7 @@ class User implements IDBAccessObject {
                if ( is_null( $this->mFormerGroups ) ) {
                        $db = ( $this->queryFlagsUsed & self::READ_LATEST )
                                ? wfGetDB( DB_MASTER )
-                               : wfGetDB( DB_SLAVE );
+                               : wfGetDB( DB_REPLICA );
                        $res = $db->select( 'user_former_groups',
                                [ 'ufg_group' ],
                                [ 'ufg_user' => $this->mId ],
@@ -3366,7 +3268,7 @@ class User implements IDBAccessObject {
 
                if ( $this->mEditCount === null ) {
                        /* Populate the count, if it has not been populated yet */
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        // check if the user_editcount field has been initialized
                        $count = $dbr->selectField(
                                'user', 'user_editcount',
@@ -3693,7 +3595,7 @@ class User implements IDBAccessObject {
 
                // Only update the timestamp if the page is being watched.
                // The query to find out if it is watched is cached both in memcached and per-invocation,
-               // and when it does have to be executed, it can be on a slave
+               // and when it does have to be executed, it can be on a replica DB
                // If this is the user's newtalk page, we always update the timestamp
                $force = '';
                if ( $title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName() ) {
@@ -3887,7 +3789,7 @@ class User implements IDBAccessObject {
                        ScopedCallback::consume( $delay );
                        $error = false;
                }
-               \MediaWiki\Logger\LoggerFactory::getInstance( 'authmanager' )->info( 'Logout', [
+               \MediaWiki\Logger\LoggerFactory::getInstance( 'authevents' )->info( 'Logout', [
                        'event' => 'logout',
                        'successful' => $error === false,
                        'status' => $error ?: 'success',
@@ -3916,7 +3818,7 @@ class User implements IDBAccessObject {
 
                // Get a new user_touched that is higher than the old one.
                // This will be used for a CAS check as a last-resort safety
-               // check against race conditions and slave lag.
+               // check against race conditions and replica DB lag.
                $newTouched = $this->newTouchedTimestamp();
 
                $dbw = wfGetDB( DB_MASTER );
@@ -3939,7 +3841,7 @@ class User implements IDBAccessObject {
                        // Maybe the problem was a missed cache update; clear it to be safe
                        $this->clearSharedCache( 'refresh' );
                        // User was changed in the meantime or loaded with stale data
-                       $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'slave';
+                       $from = ( $this->queryFlagsUsed & self::READ_LATEST ) ? 'master' : 'replica';
                        throw new MWException(
                                "CAS update failed on user_touched for user ID '{$this->mId}' (read from $from);" .
                                " the version of the user to be saved is older than the current version."
@@ -3968,7 +3870,7 @@ class User implements IDBAccessObject {
 
                $db = ( ( $flags & self::READ_LATEST ) == self::READ_LATEST )
                        ? wfGetDB( DB_MASTER )
-                       : wfGetDB( DB_SLAVE );
+                       : wfGetDB( DB_REPLICA );
 
                $options = ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING )
                        ? [ 'LOCK IN SHARE MODE' ]
@@ -4077,7 +3979,6 @@ class User implements IDBAccessObject {
                $noPass = PasswordFactory::newInvalidPassword()->toString();
 
                $dbw = wfGetDB( DB_MASTER );
-               $inWrite = $dbw->writesOrCallbacksPending();
                $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
                $dbw->insert( 'user',
                        [
@@ -4096,25 +3997,17 @@ class User implements IDBAccessObject {
                        [ 'IGNORE' ]
                );
                if ( !$dbw->affectedRows() ) {
-                       // The queries below cannot happen in the same REPEATABLE-READ snapshot.
-                       // Handle this by COMMIT, if possible, or by LOCK IN SHARE MODE otherwise.
-                       if ( $inWrite ) {
-                               // Can't commit due to pending writes that may need atomicity.
-                               // This may cause some lock contention unlike the case below.
-                               $options = [ 'LOCK IN SHARE MODE' ];
-                               $flags = self::READ_LOCKING;
-                       } else {
-                               // Often, this case happens early in views before any writes when
-                               // using CentralAuth. It's should be OK to commit and break the snapshot.
-                               $dbw->commit( __METHOD__, 'flush' );
-                               $options = [];
-                               $flags = self::READ_LATEST;
-                       }
-                       $this->mId = $dbw->selectField( 'user', 'user_id',
-                               [ 'user_name' => $this->mName ], __METHOD__, $options );
+                       // Use locking reads to bypass any REPEATABLE-READ snapshot.
+                       $this->mId = $dbw->selectField(
+                               'user',
+                               'user_id',
+                               [ 'user_name' => $this->mName ],
+                               __METHOD__,
+                               [ 'LOCK IN SHARE MODE' ]
+                       );
                        $loaded = false;
                        if ( $this->mId ) {
-                               if ( $this->loadFromDatabase( $flags ) ) {
+                               if ( $this->loadFromDatabase( self::READ_LOCKING ) ) {
                                        $loaded = true;
                                }
                        }
@@ -4241,87 +4134,27 @@ class User implements IDBAccessObject {
         * @return bool True if the given password is correct, otherwise False
         */
        public function checkPassword( $password ) {
-               global $wgAuth, $wgLegacyEncoding, $wgDisableAuthManager;
-
-               if ( $wgDisableAuthManager ) {
-                       $this->load();
-
-                       // Some passwords will give a fatal Status, which means there is
-                       // some sort of technical or security reason for this password to
-                       // be completely invalid and should never be checked (e.g., T64685)
-                       if ( !$this->checkPasswordValidity( $password )->isOK() ) {
-                               return false;
-                       }
-
-                       // Certain authentication plugins do NOT want to save
-                       // domain passwords in a mysql database, so we should
-                       // check this (in case $wgAuth->strict() is false).
-                       if ( $wgAuth->authenticate( $this->getName(), $password ) ) {
+               $manager = AuthManager::singleton();
+               $reqs = AuthenticationRequest::loadRequestsFromSubmission(
+                       $manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN ),
+                       [
+                               'username' => $this->getName(),
+                               'password' => $password,
+                       ]
+               );
+               $res = AuthManager::singleton()->beginAuthentication( $reqs, 'null:' );
+               switch ( $res->status ) {
+                       case AuthenticationResponse::PASS:
                                return true;
-                       } elseif ( $wgAuth->strict() ) {
-                               // Auth plugin doesn't allow local authentication
-                               return false;
-                       } elseif ( $wgAuth->strictUserAuth( $this->getName() ) ) {
-                               // Auth plugin doesn't allow local authentication for this user name
+                       case AuthenticationResponse::FAIL:
+                               // Hope it's not a PreAuthenticationProvider that failed...
+                               \MediaWiki\Logger\LoggerFactory::getInstance( 'authentication' )
+                                       ->info( __METHOD__ . ': Authentication failed: ' . $res->message->plain() );
                                return false;
-                       }
-
-                       $passwordFactory = new PasswordFactory();
-                       $passwordFactory->init( RequestContext::getMain()->getConfig() );
-                       $db = ( $this->queryFlagsUsed & self::READ_LATEST )
-                               ? wfGetDB( DB_MASTER )
-                               : wfGetDB( DB_SLAVE );
-
-                       try {
-                               $mPassword = $passwordFactory->newFromCiphertext( $db->selectField(
-                                       'user', 'user_password', [ 'user_id' => $this->getId() ], __METHOD__
-                               ) );
-                       } catch ( PasswordError $e ) {
-                               wfDebug( 'Invalid password hash found in database.' );
-                               $mPassword = PasswordFactory::newInvalidPassword();
-                       }
-
-                       if ( !$mPassword->equals( $password ) ) {
-                               if ( $wgLegacyEncoding ) {
-                                       // Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
-                                       // Check for this with iconv
-                                       $cp1252Password = iconv( 'UTF-8', 'WINDOWS-1252//TRANSLIT', $password );
-                                       if ( $cp1252Password === $password || !$mPassword->equals( $cp1252Password ) ) {
-                                               return false;
-                                       }
-                               } else {
-                                       return false;
-                               }
-                       }
-
-                       if ( $passwordFactory->needsUpdate( $mPassword ) && !wfReadOnly() ) {
-                               $this->setPasswordInternal( $password );
-                       }
-
-                       return true;
-               } else {
-                       $manager = AuthManager::singleton();
-                       $reqs = AuthenticationRequest::loadRequestsFromSubmission(
-                               $manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN ),
-                               [
-                                       'username' => $this->getName(),
-                                       'password' => $password,
-                               ]
-                       );
-                       $res = AuthManager::singleton()->beginAuthentication( $reqs, 'null:' );
-                       switch ( $res->status ) {
-                               case AuthenticationResponse::PASS:
-                                       return true;
-                               case AuthenticationResponse::FAIL:
-                                       // Hope it's not a PreAuthenticationProvider that failed...
-                                       \MediaWiki\Logger\LoggerFactory::getInstance( 'authentication' )
-                                               ->info( __METHOD__ . ': Authentication failed: ' . $res->message->plain() );
-                                       return false;
-                               default:
-                                       throw new BadMethodCallException(
-                                               'AuthManager returned a response unsupported by ' . __METHOD__
-                                       );
-                       }
+                       default:
+                               throw new BadMethodCallException(
+                                       'AuthManager returned a response unsupported by ' . __METHOD__
+                               );
                }
        }
 
@@ -4334,43 +4167,8 @@ class User implements IDBAccessObject {
         * @return bool True if matches, false otherwise
         */
        public function checkTemporaryPassword( $plaintext ) {
-               global $wgNewPasswordExpiry, $wgDisableAuthManager;
-
-               if ( $wgDisableAuthManager ) {
-                       $this->load();
-
-                       $passwordFactory = new PasswordFactory();
-                       $passwordFactory->init( RequestContext::getMain()->getConfig() );
-                       $db = ( $this->queryFlagsUsed & self::READ_LATEST )
-                               ? wfGetDB( DB_MASTER )
-                               : wfGetDB( DB_SLAVE );
-
-                       $row = $db->selectRow(
-                               'user',
-                               [ 'user_newpassword', 'user_newpass_time' ],
-                               [ 'user_id' => $this->getId() ],
-                               __METHOD__
-                       );
-                       try {
-                               $newPassword = $passwordFactory->newFromCiphertext( $row->user_newpassword );
-                       } catch ( PasswordError $e ) {
-                               wfDebug( 'Invalid password hash found in database.' );
-                               $newPassword = PasswordFactory::newInvalidPassword();
-                       }
-
-                       if ( $newPassword->equals( $plaintext ) ) {
-                               if ( is_null( $row->user_newpass_time ) ) {
-                                       return true;
-                               }
-                               $expiry = wfTimestamp( TS_UNIX, $row->user_newpass_time ) + $wgNewPasswordExpiry;
-                               return ( time() < $expiry );
-                       } else {
-                               return false;
-                       }
-               } else {
-                       // Can't check the temporary password individually.
-                       return $this->checkPassword( $plaintext );
-               }
+               // Can't check the temporary password individually.
+               return $this->checkPassword( $plaintext );
        }
 
        /**
@@ -4401,6 +4199,8 @@ class User implements IDBAccessObject {
         * login credentials aren't being hijacked with a foreign form
         * submission.
         *
+        * The $salt for 'edit' and 'csrf' tokens is the default (empty string).
+        *
         * @since 1.19
         * @param string|array $salt Array of Strings Optional function-specific data for hashing
         * @param WebRequest|null $request WebRequest object to use or null to use $wgRequest
@@ -4710,7 +4510,7 @@ class User implements IDBAccessObject {
                if ( $this->getId() == 0 ) {
                        return false; // anons
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $time = $dbr->selectField( 'revision', 'rev_timestamp',
                        [ 'rev_user' => $this->getId() ],
                        __METHOD__,
@@ -5115,17 +4915,25 @@ class User implements IDBAccessObject {
                // Lazy initialization check...
                if ( $dbw->affectedRows() == 0 ) {
                        // Now here's a goddamn hack...
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        if ( $dbr !== $dbw ) {
-                               // If we actually have a slave server, the count is
+                               // If we actually have a replica DB server, the count is
                                // at least one behind because the current transaction
                                // has not been committed and replicated.
-                               $this->initEditCount( 1 );
+                               $this->mEditCount = $this->initEditCount( 1 );
                        } else {
-                               // But if DB_SLAVE is selecting the master, then the
+                               // But if DB_REPLICA is selecting the master, then the
                                // count we just read includes the revision that was
                                // just added in the working transaction.
-                               $this->initEditCount();
+                               $this->mEditCount = $this->initEditCount();
+                       }
+               } else {
+                       if ( $this->mEditCount === null ) {
+                               $this->getEditCount();
+                               $dbr = wfGetDB( DB_REPLICA );
+                               $this->mEditCount += ( $dbr !== $dbw ) ? 1 : 0;
+                       } else {
+                               $this->mEditCount++;
                        }
                }
                // Edit count in user cache too
@@ -5139,9 +4947,9 @@ class User implements IDBAccessObject {
         * @return int Number of edits
         */
        protected function initEditCount( $add = 0 ) {
-               // Pull from a slave to be less cruel to servers
+               // Pull from a replica DB to be less cruel to servers
                // Accuracy isn't the point anyway here
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $count = (int)$dbr->selectField(
                        'revision',
                        'COUNT(rev_user)',
@@ -5239,45 +5047,10 @@ class User implements IDBAccessObject {
         *     - false will be converted to 'create' if this object is the same as
         *       $wgUser and to 'create2' otherwise
         * @param string $reason User supplied reason
-        * @return int|bool True if not $wgNewUserLog or not $wgDisableAuthManager;
-        *   otherwise ID of log item or 0 on failure
+        * @return bool true
         */
        public function addNewUserLogEntry( $action = false, $reason = '' ) {
-               global $wgUser, $wgNewUserLog, $wgDisableAuthManager;
-               if ( !$wgDisableAuthManager || empty( $wgNewUserLog ) ) {
-                       return true; // disabled
-               }
-
-               if ( $action === true ) {
-                       $action = 'byemail';
-               } elseif ( $action === false ) {
-                       if ( $this->equals( $wgUser ) ) {
-                               $action = 'create';
-                       } else {
-                               $action = 'create2';
-                       }
-               }
-
-               if ( $action === 'create' || $action === 'autocreate' ) {
-                       $performer = $this;
-               } else {
-                       $performer = $wgUser;
-               }
-
-               $logEntry = new ManualLogEntry( 'newusers', $action );
-               $logEntry->setPerformer( $performer );
-               $logEntry->setTarget( $this->getUserPage() );
-               $logEntry->setComment( $reason );
-               $logEntry->setParameters( [
-                       '4::userid' => $this->getId(),
-               ] );
-               $logid = $logEntry->insert();
-
-               if ( $action !== 'autocreate' ) {
-                       $logEntry->publish( $logid );
-               }
-
-               return (int)$logid;
+               return true; // disabled
        }
 
        /**
@@ -5334,7 +5107,7 @@ class User implements IDBAccessObject {
                                // Load from database
                                $dbr = ( $this->queryFlagsUsed & self::READ_LATEST )
                                        ? wfGetDB( DB_MASTER )
-                                       : wfGetDB( DB_SLAVE );
+                                       : wfGetDB( DB_REPLICA );
 
                                $res = $dbr->select(
                                        'user_properties',
@@ -5400,7 +5173,7 @@ class User implements IDBAccessObject {
                        [ 'up_property', 'up_value' ], [ 'up_user' => $userId ], __METHOD__ );
 
                // Find prior rows that need to be removed or updated. These rows will
-               // all be deleted (the later so that INSERT IGNORE applies the new values).
+               // all be deleted (the latter so that INSERT IGNORE applies the new values).
                $keysDelete = [];
                foreach ( $res as $row ) {
                        if ( !isset( $saveOptions[$row->up_property] )
@@ -5544,7 +5317,7 @@ class User implements IDBAccessObject {
         * Get a new instance of this user that was loaded from the master via a locking read
         *
         * Use this instead of the main context User when updating that user. This avoids races
-        * where that user was loaded from a slave or even the master but without proper locks.
+        * where that user was loaded from a replica DB or even the master but without proper locks.
         *
         * @return User|null Returns null if the user was not found in the DB
         * @since 1.27
index a4d4356..dddc850 100644 (file)
@@ -46,7 +46,7 @@ abstract class UserArray implements Iterator {
                        // Database::select() doesn't like empty arrays
                        return new ArrayIterator( [] );
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select(
                        'user',
                        User::selectFields(),
@@ -67,7 +67,7 @@ abstract class UserArray implements Iterator {
                        // Database::select() doesn't like empty arrays
                        return new ArrayIterator( [] );
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $res = $dbr->select(
                        'user',
                        User::selectFields(),
index 4a27a13..b7d5058 100644 (file)
@@ -39,7 +39,7 @@ class UserNamePrefixSearch {
        public static function search( $audience, $search, $limit, $offset = 0 ) {
                $user = User::newFromName( $search );
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $prefix = $user ? $user->getName() : '';
                $tables = [ 'user' ];
                $cond = [ 'user_name ' . $dbr->buildLike( $prefix, $dbr->anyString() ) ];
index 916e2f8..395ce37 100644 (file)
  *     $gen = new AutoloadGenerator( __DIR__ );
  *     $gen->readDir( __DIR__ . '/includes' );
  *     $gen->readFile( __DIR__ . '/foo.php' )
- *     $gen->generateAutoload();
+ *     $gen->getAutoload();
  */
 class AutoloadGenerator {
+       const FILETYPE_JSON = 'json';
+       const FILETYPE_PHP = 'php';
+
        /**
         * @var string Root path of the project being scanned for classes
         */
@@ -122,11 +125,11 @@ class AutoloadGenerator {
         * Updates the AutoloadClasses field at the given
         * filename.
         *
-        * @param {string} $filename Filename of JSON
+        * @param string $filename Filename of JSON
         *  extension/skin registration file
+        * @return string Updated Json of the file given as the $filename parameter
         */
        protected function generateJsonAutoload( $filename ) {
-               require_once __DIR__ . '/../../includes/json/FormatJson.php';
                $key = 'AutoloadClasses';
                $json = FormatJson::decode( file_get_contents( $filename ), true );
                unset( $json[$key] );
@@ -148,10 +151,8 @@ class AutoloadGenerator {
                // Sorting the list of autoload classes.
                ksort( $json[$key] );
 
-               // Update file, using constants for the required
-               // formatting.
-               file_put_contents( $filename,
-                       FormatJson::encode( $json, true ) . "\n" );
+               // Return the whole JSON file
+               return FormatJson::encode( $json, true ) . "\n";
        }
 
        /**
@@ -198,8 +199,7 @@ class AutoloadGenerator {
                }
 
                $output = implode( "\n\t", $content );
-               file_put_contents(
-                       $filename,
+               return
                        <<<EOD
 <?php
 // This file is generated by $commandName, do not adjust manually
@@ -210,36 +210,60 @@ global \${$this->variableName};
        {$output}
 ];
 
-EOD
-               );
+EOD;
 
        }
 
        /**
-        * Write out all known classes to autoload.php, extension.json, or skin.json in
-        * the provided basedir
+        * Returns all known classes as a string, which can be used to put into a target
+        * file (e.g. extension.json, skin.json or autoload.php)
         *
         * @param string $commandName Value used in file comment to direct
         *  developers towards the appropriate way to update the autoload.
+        * @return string
         */
-       public function generateAutoload( $commandName = 'AutoloadGenerator' ) {
+       public function getAutoload( $commandName = 'AutoloadGenerator' ) {
 
                // We need to check whether an extenson.json or skin.json exists or not, and
                // incase it doesn't, update the autoload.php file.
 
-               $jsonFilename = null;
-               if ( file_exists( $this->basepath . "/extension.json" ) ) {
-                       $jsonFilename = $this->basepath . "/extension.json";
-               } elseif ( file_exists( $this->basepath . "/skin.json" ) ) {
-                       $jsonFilename = $this->basepath . "/skin.json";
-               }
+               $fileinfo = $this->getTargetFileinfo();
 
-               if ( $jsonFilename !== null ) {
-                       $this->generateJsonAutoload( $jsonFilename );
+               if ( $fileinfo['type'] === self::FILETYPE_JSON ) {
+                       return $this->generateJsonAutoload( $fileinfo['filename'] );
                } else {
-                       $this->generatePHPAutoload( $commandName, $this->basepath . '/autoload.php' );
+                       return $this->generatePHPAutoload( $commandName, $fileinfo['filename'] );
+               }
+       }
+
+       /**
+        * Returns the filename of the extension.json of skin.json, if there's any, or
+        * otherwise the path to the autoload.php file in an array as the "filename"
+        * key and with the type (AutoloadGenerator::FILETYPE_JSON or AutoloadGenerator::FILETYPE_PHP)
+        * of the file as the "type" key.
+        *
+        * @return array
+        */
+       public function getTargetFileinfo() {
+               $fileinfo = [
+                       'filename' => $this->basepath . '/autoload.php',
+                       'type' => self::FILETYPE_PHP
+               ];
+               if ( file_exists( $this->basepath . '/extension.json' ) ) {
+                       $fileinfo = [
+                               'filename' => $this->basepath . '/extension.json',
+                               'type' => self::FILETYPE_JSON
+                       ];
+               } elseif ( file_exists( $this->basepath . '/skin.json' ) ) {
+                       $fileinfo = [
+                               'filename' => $this->basepath . '/skin.json',
+                               'type' => self::FILETYPE_JSON
+                       ];
                }
+
+               return $fileinfo;
        }
+
        /**
         * Ensure that Unix-style path separators ("/") are used in the path.
         *
@@ -249,6 +273,24 @@ EOD
        protected static function normalizePathSeparator( $path ) {
                return str_replace( '\\', '/', $path );
        }
+
+       /**
+        * Initialize the source files and directories which are used for the MediaWiki default
+        * autoloader in {mw-base-dir}/autoload.php including:
+        *  * includes/
+        *  * languages/
+        *  * maintenance/
+        *  * mw-config/
+        *  * /*.php
+        */
+       public function initMediaWikiDefault() {
+               foreach ( [ 'includes', 'languages', 'maintenance', 'mw-config' ] as $dir ) {
+                       $this->readDir( $this->basepath . '/' . $dir );
+               }
+               foreach ( glob( $this->basepath . '/*.php' ) as $file ) {
+                       $this->readFile( $file );
+               }
+       }
 }
 
 /**
index 419ee47..9fc2431 100644 (file)
@@ -31,7 +31,7 @@ class BatchRowIterator implements RecursiveIterator {
        protected $db;
 
        /**
-        * @var string $table The name of the table to read from
+        * @var string|array $table The name or names of the table to read from
         */
        protected $table;
 
@@ -79,7 +79,7 @@ class BatchRowIterator implements RecursiveIterator {
 
        /**
         * @param IDatabase $db The database to read from
-        * @param string       $table      The name of the table to read from
+        * @param string|array $table      The name or names of the table to read from
         * @param string|array $primaryKey The name or names of the primary key columns
         * @param integer      $batchSize  The number of rows to fetch per iteration
         * @throws MWException
@@ -217,7 +217,7 @@ class BatchRowIterator implements RecursiveIterator {
         * `=` conditions while the final key uses a `>` condition
         *
         * Example output:
-        *        array( '( foo = 42 AND bar > 7 ) OR ( foo > 42 )' )
+        *        [ '( foo = 42 AND bar > 7 ) OR ( foo > 42 )' ]
         *
         * @return array The SQL conditions necessary to select the next set
         *  of rows in the batched query
index 549ad41..1e7eda8 100644 (file)
@@ -4,7 +4,7 @@
  * method of batch updating rows in a database. To use create a class
  * implementing the RowUpdateGenerator interface and configure the
  * BatchRowIterator and BatchRowWriter for access to the correct table.
- * The components will handle reading, writing, and waiting for slaves
+ * The components will handle reading, writing, and waiting for replica DBs
  * while the generator implementation handles generating update arrays
  * for singular rows.
  *
index ffb7053..a6e47c8 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  * @ingroup Maintenance
  */
+use \MediaWiki\MediaWikiServices;
+
 class BatchRowWriter {
        /**
         * @var IDatabase $db The database to write to
@@ -54,7 +56,8 @@ class BatchRowWriter {
         *  names to update values to apply to the row.
         */
        public function write( array $updates ) {
-               $this->db->begin();
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
 
                foreach ( $updates as $update ) {
                        $this->db->update(
@@ -65,7 +68,6 @@ class BatchRowWriter {
                        );
                }
 
-               $this->db->commit();
-               wfGetLBFactory()->waitForReplication();
+               $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket );
        }
 }
index f3d72e8..dd3ea1b 100644 (file)
@@ -324,7 +324,7 @@ class MWCryptRand {
                                ": Falling back to using a pseudo random state to generate randomness.\n" );
                }
                while ( strlen( $buffer ) < $bytes ) {
-                       $buffer .= MWCryptHash::hmac( $this->randomState(), mt_rand() );
+                       $buffer .= MWCryptHash::hmac( $this->randomState(), strval( mt_rand() ) );
                        // This code is never really cryptographically strong, if we use it
                        // at all, then set strong to false.
                        $this->strong = false;
index 6a4792c..342dffd 100644 (file)
@@ -26,10 +26,10 @@ interface RowUpdateGenerator {
         * updated value within the database row.
         *
         * Sample Response:
-        *   return array(
+        *   return [
         *       'some_col' => 'new value',
         *       'other_col' => 99,
-        *   );
+        *   ];
         *
         * @param stdClass $row A row from the database
         * @return array Map of column names to updated value within the
index ee769da..a9e8042 100644 (file)
@@ -42,9 +42,6 @@ class ComplexTitleInputWidget extends \OOUI\Widget {
                $this->title = new TitleInputWidget( array_merge(
                        $config['title'],
                        [
-                               // The inner TitleInputWidget shouldn't be infusable,
-                               // only the ComplexTitleInputWidget itself can be.
-                               'infusable' => false,
                                'relative' => true,
                                'namespace' => isset( $config['namespace']['value'] ) ?
                                        $config['namespace']['value'] :
index dcd5415..0d71629 100644 (file)
@@ -30,7 +30,6 @@ class SearchInputWidget extends TitleInputWidget {
         */
        public function __construct( array $config = [] ) {
                $config = array_merge( [
-                       'infusable' => true,
                        'maxLength' => null,
                        'type' => 'search',
                        'icon' => 'search',
index 886afa6..da2e94b 100644 (file)
@@ -32,7 +32,7 @@ class TitleInputWidget extends \OOUI\TextInputWidget {
        public function __construct( array $config = [] ) {
                // Parent constructor
                parent::__construct(
-                       array_merge( [ 'infusable' => true, 'maxLength' => 255 ], $config )
+                       array_merge( [ 'maxLength' => 255 ], $config )
                );
 
                // Properties, which are ignored in PHP and just shipped back to JS
index 86c2bc3..d591ad1 100644 (file)
@@ -17,7 +17,7 @@ class UserInputWidget extends \OOUI\TextInputWidget {
         */
        public function __construct( array $config = [] ) {
                // Parent constructor
-               parent::__construct( array_merge( [ 'infusable' => true ], $config ) );
+               parent::__construct( $config );
 
                // Initialization
                $this->addClasses( [ 'mw-widget-userInputWidget' ] );
index 37f4137..169e0ff 100644 (file)
  * @defgroup Language Language
  */
 
-if ( !defined( 'MEDIAWIKI' ) ) {
-       echo "This file is part of MediaWiki, it is not a valid entry point.\n";
-       exit( 1 );
-}
-
 use CLDRPluralRuleParser\Evaluator;
 
 /**
@@ -1041,6 +1036,8 @@ class Language {
         *    xin  n (month number) in Iranian calendar
         *    xiy  y (two digit year) in Iranian calendar
         *    xiY  Y (full year) in Iranian calendar
+        *    xit  t (days in month) in Iranian calendar
+        *    xiz  z (day of the year) in Iranian calendar
         *
         *    xjj  j (day number) in Hebrew calendar
         *    xjF  F (month name) in Hebrew calendar
@@ -1336,6 +1333,20 @@ class Language {
                                        }
                                        $num = substr( $iranian[0], -2 );
                                        break;
+                               case 'xit':
+                                       $usedIranianYear = true;
+                                       if ( !$iranian ) {
+                                               $iranian = self::tsToIranian( $ts );
+                                       }
+                                       $num = self::$IRANIAN_DAYS[$iranian[1] - 1];
+                                       break;
+                               case 'xiz':
+                                       $usedIranianYear = true;
+                                       if ( !$iranian ) {
+                                               $iranian = self::tsToIranian( $ts );
+                                       }
+                                       $num = $iranian[3];
+                                       break;
                                case 'a':
                                        $usedAMPM = true;
                                        $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'am' : 'pm';
@@ -1594,6 +1605,8 @@ class Language {
                        $jDayNo = floor( ( $jDayNo - 1 ) % 365 );
                }
 
+               $jz = $jDayNo;
+
                for ( $i = 0; $i < 11 && $jDayNo >= self::$IRANIAN_DAYS[$i]; $i++ ) {
                        $jDayNo -= self::$IRANIAN_DAYS[$i];
                }
@@ -1601,7 +1614,7 @@ class Language {
                $jm = $i + 1;
                $jd = $jDayNo + 1;
 
-               return [ $jy, $jm, $jd ];
+               return [ $jy, $jm, $jd, $jz ];
        }
 
        /**
@@ -2577,9 +2590,6 @@ class Language {
         * @return string
         */
        public function iconv( $in, $out, $string ) {
-               # This is a wrapper for iconv in all languages except esperanto,
-               # which does some nasty x-conversions beforehand
-
                # Even with //IGNORE iconv can whine about illegal characters in
                # *input* string. We just ignore those too.
                # REF: http://bugs.php.net/bug.php?id=37166
@@ -2929,46 +2939,29 @@ class Language {
                }
        }
 
+       /**
+        * @deprecated No-op since 1.28
+        */
        function initEncoding() {
-               # Some languages may have an alternate char encoding option
-               # (Esperanto X-coding, Japanese furigana conversion, etc)
-               # If this language is used as the primary content language,
-               # an override to the defaults can be set here on startup.
+               // No-op.
        }
 
        /**
         * @param string $s
         * @return string
+        * @deprecated No-op since 1.28
         */
        function recodeForEdit( $s ) {
-               # For some languages we'll want to explicitly specify
-               # which characters make it into the edit box raw
-               # or are converted in some way or another.
-               global $wgEditEncoding;
-               if ( $wgEditEncoding == '' || $wgEditEncoding == 'UTF-8' ) {
-                       return $s;
-               } else {
-                       return $this->iconv( 'UTF-8', $wgEditEncoding, $s );
-               }
+               return $s;
        }
 
        /**
         * @param string $s
         * @return string
+        * @deprecated No-op since 1.28
         */
        function recodeInput( $s ) {
-               # Take the previous into account.
-               global $wgEditEncoding;
-               if ( $wgEditEncoding != '' ) {
-                       $enc = $wgEditEncoding;
-               } else {
-                       $enc = 'UTF-8';
-               }
-               if ( $enc == 'UTF-8' ) {
-                       return $s;
-               } else {
-                       return $this->iconv( $enc, 'UTF-8', $s );
-               }
+               return $s;
        }
 
        /**
diff --git a/languages/classes/LanguageEo.php b/languages/classes/LanguageEo.php
deleted file mode 100644 (file)
index 3fec5fc..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-<?php
-/**
- * Esperanto (Esperanto) specific code.
- *
- * 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 Brion Vibber <brion@pobox.com>
- * @ingroup Language
- */
-
-/**
- * Esperanto (Esperanto)
- *
- * @ingroup Language
- */
-class LanguageEo extends Language {
-       /**
-        * Wrapper for charset conversions.
-        *
-        * In most languages, this calls through to standard system iconv(), but
-        * for Esperanto we're also adding a special pseudo-charset to convert
-        * accented characters to/from the ASCII-friendly "X" surrogate coding:
-        *
-        *     cx = ĉ     cxx = cx
-        *     gx = ĝ     gxx = gx
-        *     hx = ĥ     hxx = hx
-        *     jx = ĵ     jxx = jx
-        *     sx = ŝ     sxx = sx
-        *     ux = ŭ     uxx = ux
-        *     xx = x
-        *
-        *   http://en.wikipedia.org/wiki/Esperanto_orthography#X-system
-        *   http://eo.wikipedia.org/wiki/X-sistemo
-        *
-        * X-conversion is applied, in either direction, between "utf-8" and "x" charsets;
-        * this comes into effect when input is run through $wgRequest->getText() and the
-        * $wgEditEncoding is set to 'x'.
-        *
-        * In the long run, this should be moved out of here and into the client-side
-        * editor behavior; the original server-side translation system dates to 2002-2003
-        * when many browsers with really bad Unicode support were still in use.
-        *
-        * @param string $in Input character set
-        * @param string $out Output character set
-        * @param string $string Text to be converted
-        * @return string
-        */
-       public function iconv( $in, $out, $string ) {
-               if ( strcasecmp( $in, 'x' ) == 0 && strcasecmp( $out, 'utf-8' ) == 0 ) {
-                       return preg_replace_callback(
-                               '/([cghjsu]x?)((?:xx)*)(?!x)/i',
-                               [ $this, 'strrtxuCallback' ], $string );
-               } elseif ( strcasecmp( $in, 'UTF-8' ) == 0 && strcasecmp( $out, 'x' ) == 0 ) {
-                       # Double Xs only if they follow cxapelutaj literoj.
-                       return preg_replace_callback(
-                               '/((?:[cghjsu]|\xc4[\x88\x89\x9c\x9d\xa4\xa5\xb4\xb5]|\xc5[\x9c\x9d\xac\xad])x*)/i',
-                               [ $this, 'strrtuxCallback' ], $string );
-               }
-               return parent::iconv( $in, $out, $string );
-       }
-
-       /**
-        * @param array $matches
-        * @return string
-        */
-       function strrtuxCallback( $matches ) {
-               static $ux = [
-                       'x' => 'xx', 'X' => 'Xx',
-                       "\xc4\x88" => "Cx", "\xc4\x89" => "cx",
-                       "\xc4\x9c" => "Gx", "\xc4\x9d" => "gx",
-                       "\xc4\xa4" => "Hx", "\xc4\xa5" => "hx",
-                       "\xc4\xb4" => "Jx", "\xc4\xb5" => "jx",
-                       "\xc5\x9c" => "Sx", "\xc5\x9d" => "sx",
-                       "\xc5\xac" => "Ux", "\xc5\xad" => "ux",
-               ];
-               return strtr( $matches[1], $ux );
-       }
-
-       /**
-        * @param array $matches
-        * @return string
-        */
-       function strrtxuCallback( $matches ) {
-               static $xu = [
-                       'xx' => 'x', 'xX' => 'x',
-                       'Xx' => 'X', 'XX' => 'X',
-                       "Cx" => "\xc4\x88", "CX" => "\xc4\x88",
-                       "cx" => "\xc4\x89", "cX" => "\xc4\x89",
-                       "Gx" => "\xc4\x9c", "GX" => "\xc4\x9c",
-                       "gx" => "\xc4\x9d", "gX" => "\xc4\x9d",
-                       "Hx" => "\xc4\xa4", "HX" => "\xc4\xa4",
-                       "hx" => "\xc4\xa5", "hX" => "\xc4\xa5",
-                       "Jx" => "\xc4\xb4", "JX" => "\xc4\xb4",
-                       "jx" => "\xc4\xb5", "jX" => "\xc4\xb5",
-                       "Sx" => "\xc5\x9c", "SX" => "\xc5\x9c",
-                       "sx" => "\xc5\x9d", "sX" => "\xc5\x9d",
-                       "Ux" => "\xc5\xac", "UX" => "\xc5\xac",
-                       "ux" => "\xc5\xad", "uX" => "\xc5\xad",
-               ];
-               return strtr( $matches[1], $xu ) . strtr( $matches[2], $xu );
-       }
-
-       function initEncoding() {
-               global $wgEditEncoding;
-               $wgEditEncoding = 'x';
-       }
-}
index 73f57e4..836fb86 100644 (file)
@@ -374,6 +374,7 @@ class Names {
                'shi' => 'Tašlḥiyt/ⵜⴰⵛⵍⵃⵉⵜ', # Tachelhit (multiple scripts - defaults to Latin)
                'shi-tfng' => 'ⵜⴰⵛⵍⵃⵉⵜ', # Tachelhit (Tifinagh script)
                'shi-latn' => 'Tašlḥiyt', # Tachelhit (Latin script)
+               'shn' => 'ၽႃႇသႃႇတႆး ', # Shan
                'si' => 'සිංහල', # Sinhalese
                'simple' => 'Simple English', # Simple English
                'sk' => 'slovenčina', # Slovak
index 87e957d..a15d910 100644 (file)
@@ -421,7 +421,6 @@ public static $zh2Hant = [
 '埘' => '塒',
 '埙' => '塤',
 '埚' => '堝',
-'埯' => '垵',
 '堑' => '塹',
 '堕' => '墮',
 '墙' => '牆',
@@ -3176,6 +3175,7 @@ public static $zh2Hant = [
 '上系上' => '上繫上',
 '上课钟' => '上課鐘',
 '上面糊' => '上面糊',
+'下文里' => '下文裡',
 '下于' => '下於',
 '下梁' => '下樑',
 '下注解' => '下注解',
@@ -3205,7 +3205,7 @@ public static $zh2Hant = [
 '不好干预' => '不好干預',
 '不嫌母丑' => '不嫌母醜',
 '不寒而栗' => '不寒而慄',
-'不吊' => '不弔',
+'不吊,' => '不弔,',
 '不卷' => '不捲',
 '不采' => '不採',
 '不斗胆' => '不斗膽',
@@ -3232,7 +3232,6 @@ public static $zh2Hant = [
 '丑表功' => '丑表功',
 '丑角' => '丑角',
 '且于' => '且於',
-'世田谷' => '世田谷',
 '世界杯' => '世界盃',
 '世纪里' => '世紀裡',
 '世纪钟' => '世紀鐘',
@@ -3363,6 +3362,7 @@ public static $zh2Hant = [
 '干性' => '乾性',
 '干打雷' => '乾打雷',
 '干折' => '乾折',
+'干拌面' => '乾拌麵',
 '干撂台' => '乾撂台',
 '干撇下' => '乾撇下',
 '干擦' => '乾擦',
@@ -3478,6 +3478,7 @@ public static $zh2Hant = [
 '乱发生' => '亂發生',
 '乱发脾气' => '亂發脾氣',
 '乱发' => '亂髮',
+'乱斗' => '亂鬥',
 '乱哄哄' => '亂鬨鬨',
 '了然后' => '了然後',
 '事有斗巧' => '事有鬥巧',
@@ -3817,6 +3818,7 @@ public static $zh2Hant = [
 '并入' => '併入',
 '并兼' => '併兼',
 '并到' => '併到',
+'并力' => '併力',
 '并合' => '併合',
 '并名' => '併名',
 '并吞' => '併吞',
@@ -4036,7 +4038,7 @@ public static $zh2Hant = [
 '其次辟地' => '其次辟地',
 '其余' => '其餘',
 '典范' => '典範',
-'兼并' => '兼',
+'兼并' => '兼',
 '冉有仆' => '冉有僕',
 '冗余' => '冗餘',
 '冤仇' => '冤讎',
@@ -4087,8 +4089,6 @@ public static $zh2Hant = [
 '刑余' => '刑餘',
 '划一桨' => '划一槳',
 '划一槳' => '划一槳',
-'划上' => '划上',
-'划下' => '划下',
 '划不來' => '划不來',
 '划不来' => '划不來',
 '划了一会' => '划了一會',
@@ -4204,6 +4204,8 @@ public static $zh2Hant = [
 '北回铁路' => '北迴鐵路',
 '匪干' => '匪幹',
 '匿于' => '匿於',
+'区里有' => '區裡有',
+'区里的' => '區裡的',
 '十个' => '十個',
 '十出家' => '十出家',
 '十出击' => '十出擊',
@@ -4263,6 +4265,7 @@ public static $zh2Hant = [
 '卷须' => '卷鬚',
 '厂部' => '厂部',
 '原子钟' => '原子鐘',
+'原文里' => '原文裡',
 '原钟' => '原鐘',
 '历物之意' => '厤物之意',
 '去山里' => '去山裡',
@@ -4291,6 +4294,7 @@ public static $zh2Hant = [
 '口里' => '口裡',
 '口钟' => '口鐘',
 '古人有云' => '古人有云',
+'古文里' => '古文裡',
 '古书云' => '古書云',
 '古書云' => '古書云',
 '古柯咸' => '古柯鹹',
@@ -4423,6 +4427,7 @@ public static $zh2Hant = [
 '吟游' => '吟遊',
 '吧台' => '吧檯',
 '含齿戴发' => '含齒戴髮',
+'吸干' => '吸乾',
 '吹干' => '吹乾',
 '吹发' => '吹髮',
 '吹胡' => '吹鬍',
@@ -4841,6 +4846,7 @@ public static $zh2Hant = [
 '寡欲' => '寡慾',
 '实干' => '實幹',
 '实累累' => '實纍纍',
+'实验里' => '實驗裡',
 '写字台' => '寫字檯',
 '宽于' => '寬於',
 '宽余' => '寬餘',
@@ -4966,7 +4972,6 @@ public static $zh2Hant = [
 '嶒棱' => '嶒稜',
 '岳岳' => '嶽嶽',
 '岳麓' => '嶽麓',
-'川谷' => '川穀',
 '巡回医疗' => '巡回醫療',
 '巡回' => '巡迴',
 '巡游' => '巡遊',
@@ -5009,7 +5014,6 @@ public static $zh2Hant = [
 '年谷' => '年穀',
 '年里' => '年裡',
 '年鉴' => '年鑑',
-'并力' => '并力',
 '并州' => '并州',
 '并日而食' => '并日而食',
 '并迭' => '并迭',
@@ -5069,6 +5073,7 @@ public static $zh2Hant = [
 '干革命' => '幹革命',
 '干头' => '幹頭',
 '干么' => '幹麼',
+'幽并' => '幽并',
 '几个' => '幾個',
 '几周后' => '幾周後',
 '几天后' => '幾天後',
@@ -5120,7 +5125,6 @@ public static $zh2Hant = [
 '吊孝' => '弔孝',
 '吊客' => '弔客',
 '吊宴' => '弔宴',
-'吊带' => '弔帶',
 '吊影' => '弔影',
 '吊恤' => '弔恤',
 '吊慰' => '弔慰',
@@ -5129,7 +5133,6 @@ public static $zh2Hant = [
 '吊撒' => '弔撒',
 '吊文' => '弔文',
 '吊旗' => '弔旗',
-'吊死' => '弔死',
 '吊民' => '弔民',
 '吊祭' => '弔祭',
 '吊纸' => '弔紙',
@@ -5395,6 +5398,7 @@ public static $zh2Hant = [
 '怒气冲天' => '怒氣衝天',
 '怒火冲天' => '怒火衝天',
 '怒发冲冠' => '怒髮衝冠',
+'怜奈' => '怜奈',
 '思如泉涌' => '思如泉湧',
 '怠于' => '怠於',
 '急于' => '急於',
@@ -5581,6 +5585,7 @@ public static $zh2Hant = [
 '抱素怀朴' => '抱素懷樸',
 '抵御' => '抵禦',
 '抹干' => '抹乾',
+'抽干' => '抽乾',
 '抽公签' => '抽公籤',
 '抽签' => '抽籤',
 '抿发' => '抿髮',
@@ -5599,6 +5604,7 @@ public static $zh2Hant = [
 '拉面色' => '拉面色',
 '拉面部' => '拉面部',
 '拉面' => '拉麵',
+'拌面' => '拌麵',
 '拒人于' => '拒人於',
 '拒于' => '拒於',
 '拓朴' => '拓樸',
@@ -5697,7 +5703,10 @@ public static $zh2Hant = [
 '捶炼' => '捶鍊',
 '扫荡' => '掃蕩',
 '授勋' => '授勳',
+'授时历' => '授時曆',
 '掌柜' => '掌柜',
+'排干' => '排乾',
+'排干部' => '排幹部',
 '排骨面' => '排骨麵',
 '挂名' => '掛名',
 '挂历' => '掛曆',
@@ -5910,6 +5919,7 @@ public static $zh2Hant = [
 '斫雕为朴' => '斫雕為樸',
 '新井里美' => '新井里美',
 '新干县' => '新幹縣',
+'新庄子' => '新庄子',
 '新历' => '新曆',
 '新历史' => '新歷史',
 '新扎' => '新紮',
@@ -6074,6 +6084,7 @@ public static $zh2Hant = [
 '村落发' => '村落發',
 '村里有' => '村裡有',
 '村里的' => '村裡的',
+'杜琪峰' => '杜琪峯',
 '杜老志道' => '杜老誌道',
 '杞宋无征' => '杞宋無徵',
 '束发' => '束髮',
@@ -6141,6 +6152,7 @@ public static $zh2Hant = [
 '梁上君子' => '梁上君子',
 '梁启超' => '梁啓超',
 '条干' => '條幹',
+'条文里' => '條文裡',
 '梨干' => '梨乾',
 '梯冲' => '梯衝',
 '械系' => '械繫',
@@ -6232,6 +6244,7 @@ public static $zh2Hant = [
 '欧游' => '歐遊',
 '止于' => '止於',
 '正官庄' => '正官庄',
+'正文里' => '正文裡',
 '正杰' => '正杰',
 '武丑' => '武丑',
 '武后' => '武后',
@@ -6274,8 +6287,6 @@ public static $zh2Hant = [
 '水并流' => '水併流',
 '水来汤里去' => '水來湯裡去',
 '水准' => '水準',
-'水无怜奈' => '水無怜奈',
-'水無怜奈' => '水無怜奈',
 '水表示' => '水表示',
 '水表面' => '水表面',
 '水里' => '水裡',
@@ -6333,7 +6344,6 @@ public static $zh2Hant = [
 '泰山梁木' => '泰山梁木',
 '泱郁' => '泱鬱',
 '泳气钟' => '泳氣鐘',
-'洄游' => '洄遊',
 '洋河大曲' => '洋河大麯',
 '洒家' => '洒家',
 '洒淅' => '洒淅',
@@ -6380,6 +6390,8 @@ public static $zh2Hant = [
 '涂敏恒' => '涂敏恆',
 '涂泽民' => '涂澤民',
 '涂澤民' => '涂澤民',
+'涂尔干' => '涂爾幹',
+'涂爾幹' => '涂爾幹',
 '涂紹煃' => '涂紹煃',
 '涂绍煃' => '涂紹煃',
 '涂羽卿' => '涂羽卿',
@@ -6712,21 +6724,18 @@ public static $zh2Hant = [
 '甜面酱' => '甜麵醬',
 '生力面' => '生力麵',
 '生于' => '生於',
-'生殖洄游' => '生殖洄游',
 '生物钟' => '生物鐘',
 '生发生' => '生發生',
 '生华发' => '生華髮',
 '生姜' => '生薑',
 '生锈' => '生鏽',
 '生发' => '生髮',
-'产卵洄游' => '產卵洄游',
 '苏醒' => '甦醒',
 '用于' => '用於',
 '用法里' => '用法裡',
 '甩发' => '甩髮',
 '田子里' => '田子里',
 '田庄英雄' => '田庄英雄',
-'田谷' => '田穀',
 '田里' => '田裡',
 '由余' => '由余',
 '由于' => '由於',
@@ -6743,6 +6752,7 @@ public static $zh2Hant = [
 '毕于' => '畢於',
 '毕业于' => '畢業於',
 '毕生发展' => '畢生發展',
+'画里' => '畫裡',
 '当准' => '當準',
 '当当丁丁' => '當當丁丁',
 '当当网' => '當當網',
@@ -7110,7 +7120,6 @@ public static $zh2Hant = [
 '吁求' => '籲求',
 '吁请' => '籲請',
 '米沈' => '米瀋',
-'米谷' => '米穀',
 '米团' => '米糰',
 '米余' => '米餘',
 '米面' => '米麵',
@@ -7190,6 +7199,7 @@ public static $zh2Hant = [
 '绑扎' => '綁紮',
 '绥棱' => '綏稜',
 '捆扎' => '綑紮',
+'经文里' => '經文裡',
 '經有云' => '經有云',
 '经有云' => '經有云',
 '综合征' => '綜合徵',
@@ -7248,7 +7258,6 @@ public static $zh2Hant = [
 '系上头' => '繫上頭',
 '系上黑' => '繫上黑',
 '系上,' => '繫上,',
-'系世' => '繫世',
 '系到' => '繫到',
 '系囚' => '繫囚',
 '系心' => '繫心',
@@ -7314,6 +7323,7 @@ public static $zh2Hant = [
 '老板' => '老闆',
 '老面皮' => '老面皮',
 '考征' => '考徵',
+'考试制度' => '考試制度',
 '耍斗' => '耍鬥',
 '耕获' => '耕穫',
 '耳余' => '耳餘',
@@ -7472,6 +7482,7 @@ public static $zh2Hant = [
 '苦里' => '苦裡',
 '苦斗' => '苦鬥',
 '苧麻' => '苧麻',
+'英文里' => '英文裡',
 '茂都淀' => '茂都澱',
 '范文同' => '范文同',
 '范文正公' => '范文正公',
@@ -7622,6 +7633,7 @@ public static $zh2Hant = [
 '药面儿' => '藥麵兒',
 '苏崑' => '蘇崑',
 '苏昆' => '蘇崑',
+'苹婆' => '蘋婆',
 '苹果' => '蘋果',
 '苹果干' => '蘋果乾',
 '兰溪市' => '蘭谿市',
@@ -7841,7 +7853,6 @@ public static $zh2Hant = [
 '西历' => '西曆',
 '西历史' => '西歷史',
 '西湖里' => '西湖里',
-'西米谷' => '西米谷',
 '西西里' => '西西里',
 '西谷米' => '西谷米',
 '西游' => '西遊',
@@ -7911,6 +7922,7 @@ public static $zh2Hant = [
 '注生娘娘' => '註生娘娘',
 '注疏' => '註疏',
 '注脚' => '註腳',
+'注里' => '註裡',
 '注解' => '註解',
 '注记' => '註記',
 '注译' => '註譯',
@@ -8003,6 +8015,7 @@ public static $zh2Hant = [
 '谈征' => '談徵',
 '请君入瓮' => '請君入甕',
 '请托' => '請託',
+'论文里' => '論文裡',
 '咨询' => '諮詢',
 '诸余' => '諸餘',
 '谋干' => '謀幹',
@@ -8026,6 +8039,7 @@ public static $zh2Hant = [
 '警报钟' => '警報鐘',
 '警示钟' => '警示鐘',
 '警钟' => '警鐘',
+'译文里' => '譯文裡',
 '译制' => '譯製',
 '译注' => '譯註',
 '护发' => '護髮',
@@ -8514,6 +8528,7 @@ public static $zh2Hant = [
 '炼金术' => '鍊金術',
 '锲而不舍' => '鍥而不捨',
 '镰仓' => '鎌倉',
+'镇里' => '鎮裡',
 '镜图里' => '鏡圖裡',
 '锈病' => '鏽病',
 '锈菌' => '鏽菌',
@@ -8825,6 +8840,7 @@ public static $zh2Hant = [
 '额我略历' => '額我略曆',
 '额我略历史' => '額我略歷史',
 '颜范' => '顏範',
+'颛顼历' => '顓頊曆',
 '颠干倒坤' => '顛乾倒坤',
 '顛顛仆仆' => '顛顛仆仆',
 '颠颠仆仆' => '顛顛仆仆',
@@ -9428,7 +9444,6 @@ public static $zh2Hant = [
 '龜山庄' => '龜山庄',
 '龟鉴' => '龜鑑',
 ',并力' => ',並力',
-',并力攻' => ',并力攻',
 ',并力討' => ',并力討',
 ',并力讨' => ',并力討',
 ',个中' => ',箇中',
@@ -9863,9 +9878,7 @@ public static $zh2Hans = [
 '圞' => '𪢮',
 '坿' => '附',
 '垜' => '垛',
-'垵' => '埯',
 '埡' => '垭',
-'埰' => '采',
 '執' => '执',
 '堅' => '坚',
 '堊' => '垩',
@@ -13769,6 +13782,7 @@ public static $zh2Hans = [
 '甦醒' => '苏醒',
 '苧烯' => '苧烯',
 '薴烯' => '苧烯',
+'蘋婆' => '苹婆',
 '蘋果' => '苹果',
 '荠苧' => '荠苧',
 '榮陞' => '荣升',
@@ -14088,7 +14102,6 @@ public static $zh2TW = [
 '威士顿康星' => '威斯康辛',
 '威尔士' => '威爾斯',
 '威爾士' => '威爾斯',
-'字库' => '字型檔',
 '存盘' => '存檔',
 '孟德爾遜' => '孟德爾頌',
 '门德尔松' => '孟德爾頌',
@@ -14176,7 +14189,6 @@ public static $zh2TW = [
 '打印度' => '打印度',
 '抽烟' => '抽菸',
 '抽煙' => '抽菸',
-'拉普兰' => '拉布蘭',
 '拒烟' => '拒菸',
 '拒煙' => '拒菸',
 '卷烟' => '捲菸',
@@ -14662,6 +14674,7 @@ public static $zh2TW = [
 '迈凯轮' => '麥拿輪',
 '邁凱輪' => '麥拿輪',
 '马萨诸塞' => '麻薩諸塞',
+'粘膜' => '黏膜',
 '戴安娜' => '黛安娜',
 '狄安娜' => '黛安娜',
 '点烟' => '點菸',
@@ -14690,6 +14703,7 @@ public static $zh2HK = [
 '旧金山' => '三藩市',
 '舊金山' => '三藩市',
 '上台面' => '上枱面',
+'下文里' => '下文裏',
 '下著' => '下着',
 '下著作' => '下著作',
 '下著名' => '下著名',
@@ -15175,6 +15189,8 @@ public static $zh2HK = [
 '動著錄' => '動著錄',
 '包著' => '包着',
 '北朝鲜' => '北韓',
+'区里有' => '區裏有',
+'区里的' => '區裏的',
 '南朝鲜' => '南韓',
 '波札那' => '博茨瓦納',
 '占卜' => '占卜',
@@ -15203,6 +15219,7 @@ public static $zh2HK = [
 '厄利垂亚' => '厄立特里亞',
 '厄利垂亞' => '厄立特里亞',
 '源代码' => '原始碼',
+'原文里' => '原文裏',
 '去山里' => '去山裏',
 '参数里' => '參數裏',
 '受著' => '受着',
@@ -15215,6 +15232,7 @@ public static $zh2HK = [
 '受著錄' => '受著錄',
 '丛林里' => '叢林裏',
 '口里' => '口裏',
+'古文里' => '古文裏',
 '只占' => '只佔',
 '叫著' => '叫着',
 '叫著作' => '叫著作',
@@ -16149,6 +16167,7 @@ public static $zh2HK = [
 '撞球' => '桌球',
 '梅鐸' => '梅鐸',
 '默多克' => '梅鐸',
+'条文里' => '條文裏',
 '梳著' => '梳着',
 '梳著作' => '梳著作',
 '梳著名' => '梳著名',
@@ -16179,6 +16198,7 @@ public static $zh2HK = [
 '機器人' => '機械人',
 '柜台' => '櫃枱',
 '柜里' => '櫃裏',
+'正文里' => '正文裏',
 '历史里' => '歷史裏',
 '死里求生' => '死裏求生',
 '死里逃生' => '死裏逃生',
@@ -16464,6 +16484,7 @@ public static $zh2HK = [
 '畫著名' => '畫著名',
 '畫著稱' => '畫著稱',
 '畫著者' => '畫著者',
+'画里' => '畫裏',
 '當著' => '當着',
 '當著作' => '當著作',
 '過著作' => '當著作',
@@ -16744,6 +16765,7 @@ public static $zh2HK = [
 '綁著者' => '綁著者',
 '綁著述' => '綁著述',
 '綁著錄' => '綁著錄',
+'经文里' => '經文裏',
 '网站里' => '網站裏',
 '網路' => '網絡',
 '网里' => '網裏',
@@ -16908,6 +16930,7 @@ public static $zh2HK = [
 '苦著錄' => '苦著錄',
 '苦里' => '苦裏',
 '英占' => '英佔',
+'英文里' => '英文裏',
 '共和联邦' => '英聯邦',
 '大英國協' => '英聯邦',
 '草丛里' => '草叢裏',
@@ -17057,7 +17080,10 @@ public static $zh2HK = [
 '見著述' => '見著述',
 '見著錄' => '見著錄',
 '視著' => '視着',
+'視著作' => '視著作',
 '視著名' => '視著名',
+'視著稱' => '視著稱',
+'視著者' => '視著者',
 '角落里' => '角落裏',
 '分辨率' => '解像度',
 '解析度' => '解像度',
@@ -17097,8 +17123,10 @@ public static $zh2HK = [
 '說著者' => '說著者',
 '說著述' => '說著述',
 '數據機' => '調制解調器',
+'论文里' => '論文裏',
 '诺曼底' => '諾曼第',
 '警戒著' => '警戒着',
+'譯文里' => '譯文裏',
 '變著' => '變着',
 '變著作' => '變著作',
 '變著名' => '變著名',
@@ -17389,6 +17417,7 @@ public static $zh2HK = [
 '鋪著者' => '鋪著者',
 '鋪著述' => '鋪著述',
 '鋪著錄' => '鋪著錄',
+'镇里' => '鎮裏',
 '镜图里' => '鏡圖裏',
 '钟在寺里' => '鐘在寺裏',
 '狄托' => '鐵托',
@@ -18394,7 +18423,6 @@ public static $zh2CN = [
 '威爾斯' => '威尔士',
 '威斯伐倫' => '威斯特法伦',
 '字型大小' => '字号',
-'字型檔' => '字库',
 '欄位' => '字段',
 '位元組' => '字节',
 '存在著' => '存在着',
@@ -19852,7 +19880,10 @@ public static $zh2CN = [
 '見著述' => '见著述',
 '規畫' => '规划',
 '視著' => '视着',
+'視著作' => '视著作',
 '視著名' => '视著名',
+'視著稱' => '视著称',
+'視著者' => '视著者',
 '占士邦' => '詹姆斯·邦德',
 '警戒著' => '警戒着',
 '計畫' => '计划',
index f63f7c3..cb869d0 100644 (file)
@@ -10,7 +10,8 @@
                        "GeekEmad",
                        "Nemo bis",
                        "Shbib Al-Subaie",
-                       "Macofe"
+                       "Macofe",
+                       "علاء"
                ]
        },
        "tog-usenewrc": ")جمّع التعديلات حسب الصفحة في أحدث التغييرات وقائمة المراقبة (يتطلب جافاسكربت",
        "passwordreset-emailtext-user": "احد ما (قد يكون انت$1)طلب مذكرة تفاصيل الحساب ل{{SITENAME}} ($4).المستخدم الاتي {{PLURAL:$3|الحساب هو|الحسابات هي}} قد قرن بهذا العنوان :\n\n$2\n\n{{PLURAL:$3|كلمة المرور المؤقتة|كلمات المرور المؤقة}}سينتهي في {{PLURAL:$5|يوم|ايام$5 }}\nمن الافضل ان تسجل الدخول وتختار كلمة مرور جديدة الان .\nإذا قام شخص آخر بهذا الطلب، أو إذا  تذكرت كلمة المرور الأصلية الخاصة بك،ولم تعد ترغب في تغييره، يمكنك تجاهل هذه الرسالة ومتابعة استخدام  كلمة المرورالقديمة.",
        "passwordreset-emailelement": "اسم المستخدم: \n$1\n\nكلمة السر المؤقتة: \n$2",
        "passwordreset-emailsentemail": "أرسل بريد إلكتروني تذكيري",
-       "passwordreset-emailsent-capture": "أرسل بريد إلكتروني تذكيري وهو معروض بالأسفل.",
-       "passwordreset-emailerror-capture": "ولّد بريد إلكتروني تذكيري وهو معروض بالأسفل لكن فشل إرساله للمستخدم: $1",
        "changeemail": "تغيير عنوان البريد الإلكتروني",
        "changeemail-header": "تغيير عنوان البريد الإلكتروني للحساب",
        "changeemail-no-info": "يجب تسجيل الدخول للوصول إلى هذه الصفحة مباشرة.",
        "undo-failure": "لم يمكن استرجاع التعديل بسبب تعديلات متعارضة تمت على الصفحة.",
        "undo-norev": "فشل في الرجوع عن التعديل حيث أنه غير موجود أو تم حذفه.",
        "undo-summary": "الرجوع عن التعديل $1 بواسطة [[Special:Contributions/$2|$2]] ([[User talk:$2|نقاش]])",
-       "cantcreateaccounttitle": "لا يمكن إنشاء حساب",
        "cantcreateaccount-text": "إنشاء الحسابات من عنوان الأيبي هذا ('''$1''') تم منعه بواسطة [[User:$3|$3]].\n\nالسبب المعطى بواسطة $3 هو ''$2''",
        "viewpagelogs": "اعرض سجلات هذه الصفحة",
        "nohistory": "لا يوجد تاريخ للتعديلات لهذه الصفحة.",
        "revdel-restore": "تغيير الرؤية",
        "pagehist": "تاريخ صفحة",
        "deletedhist": "التاريخ المحذوف",
-       "revdelete-hide-current": "خطأ Ø¹Ù\86د Ø¥Ø­فاء العنصر المؤرخ في $2 $1: هذه هي المراجعة الحالية.\nلا يمكن إخفاؤها.",
+       "revdelete-hide-current": "خطأ Ø¹Ù\86د Ø¥Ø®فاء العنصر المؤرخ في $2 $1: هذه هي المراجعة الحالية.\nلا يمكن إخفاؤها.",
        "revdelete-show-no-access": "خطأ في إظهار العنصر ذا التاريخ $2 $1: هذا العنصر معلم ك\"مقيد\".\nليس لك صلاحية الوصول إليه.",
        "revdelete-modify-no-access": "خطأ في تعديل العنصر ذا التاريخ $2 $1: هذا العنصر معلم ك\"مقيد\".\nليس لك صلاحية الوصول إليه.",
        "revdelete-modify-missing": "خطأ في تعديل العنصر ذا الهوية $1: العنصر مفقود من قاعدة البيانات!",
        "rc-enhanced-hide": "أخفِ التفاصيل",
        "recentchangeslinked": "تغييرات ذات علاقة",
        "recentchangeslinked-title": "التغييرات المرتبطة ب \"$1\"",
-       "recentchangeslinked-summary": "هذه قائمة بالتغييرات التي تمت حديثا للصفحات الموصولة من صفحة معينة (أو إلى الأعضاء ضمن تصنيف معين).\nالصفحات في [[Special:Watchlist|قائمة مراقبتك]] '''عريضة'''",
+       "recentchangeslinked-summary": "هذه قائمة بالتغييرات التي تمت حديثاً للصفحات الموصولة من صفحة معينة (أو إلى الأعضاء ضمن تصنيف معين).\nالصفحات في [[Special:Watchlist|قائمة مراقبتك]] '''عريضة'''",
        "recentchangeslinked-page": "اسم الصفحة:",
-       "recentchangeslinked-to": "أظهر التغييرات للصفحات الموصولة للصفحة المعطاة عوضا عن ذلك",
+       "recentchangeslinked-to": "أظهر التغييرات للصفحات الموصولة للصفحة المعطاة عوضاً عن ذلك",
        "uploadlogpage": "سجل الرفع",
        "filedesc": "ملخص:",
        "license": "ترخيص:",
index 73c4c8f..d6534b6 100644 (file)
        "october-date": "$1 Oktober",
        "november-date": "$1 November",
        "december-date": "$1 Desember",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Kategorie|Kategorieë}}",
        "category_header": "Bladsye in kategorie \"$1\"",
        "subcategories": "Subkategorieë",
        "morenotlisted": "Die lys is nie volledig nie.",
        "mypage": "Gebruikersblad",
        "mytalk": "Bespreking",
-       "anontalk": "Besprekingsblad vir hierdie IP",
+       "anontalk": "Bespreking",
        "navigation": "Navigasie",
        "and": "&#32;en",
        "qbfind": "Vind",
        "retypenew": "Tik nuwe wagwoord weer in",
        "resetpass_submit": "Stel wagwoord en meld aan",
        "changepassword-success": "U wagwoord is suksesvol gewysig!",
+       "botpasswords-label-create": "Skep",
+       "botpasswords-label-update": "Opdateer",
+       "botpasswords-label-cancel": "Kanselleer",
+       "botpasswords-label-delete": "Skrap",
+       "botpasswords-label-resetpassword": "Herstel wagwoord",
+       "botpasswords-label-grants": "Toepaslike regte:",
        "resetpass_forbidden": "Wagwoorde kannie gewysig word nie.",
        "resetpass-no-info": "U moet ingeteken wees om hierdie bladsy direk te kan gebruik.",
        "resetpass-submit-loggedin": "Verander wagwoord",
        "passwordreset-emailtext-user": "Gebruiker $1 op die webtuiste {{SITENAME}} het u gebruikersgegewens vir {{SITENAME}} ($4) opgevra.\nDie volgende {{PLURAL:$3|gebruiker is|gebruikers is}} aan die e-posadres gekoppel:\n\n$2\n\n{{PLURAL:$3|Die tydelike wagwoord verval|Hierdie tydelike wagwoorde verval}} oor {{PLURAL:$5|een dag|$5 dae}}.\nMeld asseblief aan en verander u wagwoord nou. As u dit nie versoek het nie, of as u die oorspronklike wagwoord nog ken en dit nie wil verander nie, ignoreer die berig en hou aan om u ou wagwoord te gebruik.",
        "passwordreset-emailelement": "Gebruikersnaam: \n$1\n\nTydelike wagwoord: \n$2",
        "passwordreset-emailsentemail": "'n E-pos is gestuur om u wagwoord te herstel.",
-       "passwordreset-emailsent-capture": "'n E-pos vir die herstel van 'n wagwoord is gestuur. Dit word hieronder vertoon.",
-       "passwordreset-emailerror-capture": "'n E-pos vir die herstel van 'n wagwoord is saamgestel. Dit word hieronder vertoon. Die uitstuur daarvan na die {{GENDER:$2|gebruiker}} het egter gefaal: $1",
        "changeemail": "Wysig E-posadres",
        "changeemail-header": "Wysig rekening se e-posadres",
        "changeemail-no-info": "U moet aangemeld wees om regstreeks toegang tot die bladsy te kry.",
        "sig_tip": "Handtekening met datum",
        "hr_tip": "Horisontale streep (selde nodig)",
        "summary": "Opsomming:",
-       "subject": "Onderwerp/opskrif:",
+       "subject": "Onderwerp:",
        "minoredit": "Klein wysiging",
        "watchthis": "Hou bladsy dop",
        "savearticle": "Stoor bladsy",
+       "savechanges": "Stoor wysigings",
+       "publishpage": "Publiseer bladsy",
+       "publishchanges": "Publiseer wysigings",
        "preview": "Voorskou",
        "showpreview": "Wys voorskou",
        "showdiff": "Wys veranderings",
        "undo-nochange": "Die wysiging is klaarblyklik reeds teruggerol.",
        "undo-summary": "Rol weergawe $1 deur [[Special:Contributions/$2|$2]] ([[User talk:$2|bespreek]]) terug.",
        "undo-summary-username-hidden": "Rol weergawe $1 deur 'n versteekte gebruiker terug",
-       "cantcreateaccounttitle": "Kan nie rekening skep nie",
        "cantcreateaccount-text": "Die registrasie van nuwe rekeninge vanaf die IP-adres ('''$1''') is geblok deur [[User:$3|$3]].\n\nDie rede verskaf deur $3 is ''$2''",
        "viewpagelogs": "Bekyk logboeke vir hierdie bladsy",
        "nohistory": "Daar is geen wysigingsgeskiedenis vir hierdie bladsy nie.",
        "recentchanges-label-plusminus": "Bladsy is met die aantal grepe gewysig",
        "recentchanges-legend-heading": "<strong>Sleutel:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (sien ook die [[Special:NewPages|lys van nuwe bladsye]])",
+       "recentchanges-submit": "Wys",
        "rcnotefrom": "Wysigings sedert <strong>$2</strong> (maksimum van <strong>$1</strong> word gewys).",
        "rclistfrom": "Vertoon wysigings vanaf $3 $2",
        "rcshowhideminor": "$1 klein wysigings",
        "mostrevisions": "Artikels met meeste wysigings",
        "prefixindex": "Alle bladsye (voorvoegselindeks)",
        "prefixindex-namespace": "Alle bladsye met die voorvoegsel (naamruimte $1)",
+       "prefixindex-submit": "Wys",
        "shortpages": "Kort bladsye",
        "longpages": "Lang bladsye",
        "deadendpages": "Doodloopbladsye",
        "apisandbox": "API-sandput",
        "apisandbox-api-disabled": "API is afgeskakel op hierdie webwerf.",
        "apisandbox-intro": "Gebruik hierdie bladsy om te eksperimenteer met die '''MediaWiki-API'''.\nSien die [https://www.mediawiki.org/wiki/API:Main_page API-dokumentasie] vir verdere details oor die gebruik van die API. Voorbeeld: [https://www.mediawiki.org/wiki/API#A_simple_example hoe die inhoud van 'n Tuisblad te laai]. Kies 'n handeling om meer voorbeelde te sien.",
+       "apisandbox-unfullscreen": "Wys bladsy",
        "apisandbox-submit": "Maak versoek",
        "apisandbox-reset": "Vee uit",
+       "apisandbox-retry": "Herprobeer",
        "apisandbox-examples": "Voorbeeld",
        "apisandbox-results": "Resultaat",
        "apisandbox-request-url-label": "Versoek-URL:",
        "specialloguserlabel": "Uitvoerende gebruiker:",
        "speciallogtitlelabel": "Teiken (bladsy of gebruiker):",
        "log": "Logboeke",
+       "logeventslist-submit": "Wys",
        "all-logs-page": "Alle openbare logboeke",
        "alllogstext": "Vertoon 'n samestelling van alle boekstawings van {{SITENAME}}.\nU kan die resultate vernou deur 'n boekstaaftipe, gebruikersnaam (kas-sensitief) of spesifieke blad (ook kas-sensitief) te kies.",
        "logempty": "Geen inskrywings in die logboek voldoen aan die kriteria nie.",
        "log-title-wildcard": "Soek bladsye wat met die naam begin",
        "showhideselectedlogentries": "Wys/versteek gekose logboekreëls",
+       "checkbox-select": "Selekteer: $1",
+       "checkbox-all": "Alle",
+       "checkbox-none": "Geen",
+       "checkbox-invert": "Omkeer",
        "allpages": "Alle bladsye",
        "nextpage": "Volgende blad ($1)",
        "prevpage": "Vorige bladsye ($1)",
        "cachedspecial-viewing-cached-ts": "U kyk na 'n gekasde weergawe ban die bladsy, wat moontlik nie volledig bygewerk is nie.",
        "cachedspecial-refresh-now": "Wys nuutste.",
        "categories": "Kategorieë",
+       "categories-submit": "Wys",
        "categoriespagetext": "Die volgende {{PLURAL:$1|kategorie|kategorieë}} bevat bladsye of media.\n[[Special:UnusedCategories|Ongebruikte kategorieë]] word nie gewys nie.\nSien ook [[Special:WantedCategories|nie-bestaande kategorieë met verwysings]].",
        "categoriesfrom": "Wys kategorieë vanaf:",
        "deletedcontributions": "Geskrapte gebruikersbydraes",
        "wlnote": "Hier volg die laaste {{PLURAL:$1|verandering|'''$1''' veranderings}} binne die laaste {{PLURAL:$2|uur|'''$2''' ure}}, soos vanaf $3 om $4.",
        "wlshowlast": "Wys afgelope $1 ure, $2 dae of",
        "watchlist-hide": "Versteek",
+       "watchlist-submit": "Wys",
        "wlshowtime": "Wys laaste:",
        "wlshowhideminor": "klein wysigings",
        "wlshowhidebots": "robotte",
        "delete-confirm": "Skrap \"$1\"",
        "delete-legend": "Skrap",
        "historywarning": "'''Waarskuwing:''' Die bladsy wat u wil verwyder het 'n geskiedenis met ongeveer $1 {{PLURAL:$1|weergawe|weergawes}}:",
+       "historyaction-submit": "Wys",
        "confirmdeletetext": "U staan op die punt om 'n bladsy of prent, insluitende hul geskiedenis, uit die databasis te skrap.\nBevestig asseblief dat u dit wil doen, dat u die gevolge verstaan en dat u dit doen in ooreenstemming met die [[{{MediaWiki:Policy-url}}|beleid]].",
        "actioncomplete": "Aksie uitgevoer",
        "actionfailed": "Aksie het gefaal",
        "sessionfailure-title": "Sessie het gefaal",
        "sessionfailure": "Dit lyk of daar 'n probleem met u sessie is.\nHierdie aksie is gekanselleer omdat dit 'n veiligheidsrisiko is (moontlike kaping van u sessie).\nGaan een bladsy terug, herlaai die bladsy en probeer dan weer.",
        "changecontentmodel-reason-label": "Rede:",
+       "changecontentmodel-submit": "Wysig",
        "protectlogpage": "Beskermingboekstaaf",
        "protectlogtext": "Hieronder is 'n lys van veranderinge wat aan die beveilig van bladsye aangebring is.\nSien die [[Special:ProtectedPages|lys van beveiligde bladsye]] vir alle bladsye wat tans operasioneel beveilig is.",
        "protectedarticle": "het [[$1]] beskerm",
        "confirm-watch-top": "Voeg hierdie bladsy by u dophoulys?",
        "confirm-unwatch-button": "OK",
        "confirm-unwatch-top": "Verwyder hierdie bladsy van u dophoulys?",
+       "confirm-rollback-button": "OK",
        "quotation-marks": "\"$1\"",
        "imgmultipageprev": "← vorige bladsy",
        "imgmultipagenext": "volgende bladsy →",
        "watchlisttools-edit": "Bekyk en wysig dophoulys",
        "watchlisttools-raw": "Wysig bronkode",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|kontak]])",
+       "timezone-local": "Lokaal",
        "duplicate-defaultsort": "Waarskuwing: Die standaardsortering \"$2\" kry voorrang voor die sortering \"$1\".",
        "version": "Weergawe",
        "version-extensions": "Uitbreidings geïnstalleer",
        "tags-activate-reason": "Rede:",
        "tags-activate-submit": "Aktiveer",
        "tags-deactivate-reason": "Rede:",
+       "tags-deactivate-submit": "Deaktiveer",
        "tags-edit-existing-tags-none": "''Geen''",
        "tags-edit-reason": "Rede:",
        "comparepages": "Vergelyk bladsye",
        "pagelang-language": "Taal",
        "pagelang-use-default": "Gebruik standaard taal",
        "pagelang-select-lang": "Kies taal",
+       "pagelang-submit": "Dien in",
        "right-pagelang": "Wysig die taal van die bladsy",
        "log-name-pagelang": "Logboek van taalwysigings",
        "log-description-pagelang": "Hierdie is 'n logboek van wysigings van die taal van bladsye.",
        "logentry-pagelang-pagelang": "$1 wysig die taal van bladsy '$3' van $4 na $5.",
+       "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>afgeskakel</strong>)",
        "mediastatistics": "Mediastatistieke",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 greep|$1 grepe}} ($2; $3%)",
        "mediastatistics-table-mimetype": "MIME-tipe",
        "mediastatistics-header-text": "Tekslêers",
        "mediastatistics-header-executable": "Uitvoerbare lêers",
        "mediastatistics-header-archive": "Gekompakteerde lêers",
+       "mediastatistics-header-total": "Alle lêers",
        "json-error-syntax": "Sintaksfout",
        "headline-anchor-title": "Skakel na die afdeling",
-       "special-characters-group-latin": "Latyns",
-       "special-characters-group-latinextended": "Latyns uitgebreid",
+       "special-characters-group-latin": "Latyn",
+       "special-characters-group-latinextended": "Latyn uitgebrei",
        "special-characters-group-ipa": "IFA",
        "special-characters-group-symbols": "Simbole",
        "special-characters-group-greek": "Grieks",
+       "special-characters-group-greekextended": "Grieks uitgebrei",
        "special-characters-group-cyrillic": "Cyrillies",
        "special-characters-group-arabic": "Arabies",
        "special-characters-group-arabicextended": "Arabies uitgebrei",
        "special-characters-group-gujarati": "Gujarati",
        "special-characters-group-devanagari": "Devanagari",
        "special-characters-group-thai": "Thai",
-       "special-characters-group-lao": "Lao",
+       "special-characters-group-lao": "Laosiaans",
        "special-characters-group-khmer": "Khmer",
        "special-characters-title-minus": "minusteken",
        "mw-widgets-dateinput-no-date": "Geen datum gekies nie",
        "mw-widgets-dateinput-placeholder-month": "JJJJ-MM",
        "mw-widgets-titleinput-description-new-page": "bladsy bestaan nog nie",
        "mw-widgets-titleinput-description-redirect": "aanstuur na $1",
-       "api-error-blacklisted": "Kies asseblief 'n ander, beskrywende titel."
+       "log-action-filter-all": "Alle",
+       "authprovider-resetpass-skip-label": "Slaan oor"
 }
index b414d5e..6bc5553 100644 (file)
                        "Wōdenhelm",
                        "아라",
                        "Dpk",
-                       "Macofe"
+                       "Macofe",
+                       "WhatamIdoing",
+                       "Hogweard",
+                       "Amire80"
                ]
        },
        "tog-underline": "Mearc under hlencan:",
        "tog-hideminor": "Hȳdan lytela adihtunga in nīwra andwendinga getæle",
        "tog-hidepatrolled": "Hȳdan weardoda adihtunga in nīwra andwendinga getæle",
        "tog-newpageshidepatrolled": "Hȳdan weardode trametas in nīwra andwendinga getæle",
+       "tog-hidecategorization": "Behȳd trameta floccas",
        "tog-extendwatchlist": "Sprǣdan behealdungtæl tō īwenne ealla andwendinga, nā synderlīce þā nīwostan",
        "tog-usenewrc": "Settan andwendunga on hēapas on trametum on nīwra andwendunga getæle and behealdungtæle",
        "tog-numberheadings": "Settan rīm on forecwidas selflīce",
@@ -30,6 +34,7 @@
        "tog-watchdefault": "Ēacnian mīn behealdungtæl mid trametum and ymelum þā ic adihte.",
        "tog-watchmoves": "Ēacnian mīn behealdungtæl mid trametum and ymelum þā ic wege.",
        "tog-watchdeletion": "Ēacnian mīn behealdungæl mid trametum and ymelum þā ic forlēose.",
+       "tog-watchuploads": "Eacne nīwa ymelan to mīnum weardgetæle",
        "tog-minordefault": "Mearcian ealla adihtunga lytela tō gewunan",
        "tog-previewontop": "Īwan forebysene ofer adihtunge mearce",
        "tog-previewonfirst": "Īwan forebysene on forman adihtunge",
        "october-date": "$1. Winterfylleðes",
        "november-date": "$1. Blōtmōnaðes",
        "december-date": "$1. Ǣrran Gēolan",
+       "period-am": "on undernmǣl",
+       "period-pm": "on ofernōn",
        "pagecategories": "{{PLURAL:$1|Flocc|Floccas}}",
        "category_header": "Trametas in \"$1\" flocce",
        "subcategories": "Underfloccas",
        "jumptonavigation": "þurhfōr",
        "jumptosearch": "sēcan",
        "view-pool-error": "Wālā, þā þegntōlas nū oferlīce wyrcaþ.\nTō mænige brūcendas gesēcaþ tō sēonne þisne tramet.\nWē biddaþ þæt þū abīde scortne tīman ǣr þū gesēce to sēonne þisne tramet eft.\n\n$1",
+       "pool-queuefull": "Pundfaldes forepenn is full",
        "pool-errorunknown": "Uncūþ wōh",
+       "pool-servererror": "Seo pundfaldgetalere þēgnung nis gearo",
        "aboutsite": "Gecȳþness ymbe {{GRAMMAR:wrēgendlīc|{{SITENAME}}}}",
        "aboutpage": "Project:Gefrǣge",
        "copyright": "Man mæg innunge under $1 findan, būton þǣr hit is elles amearcod.",
        "yourpasswordagain": "Wrītan þafungword eft:",
        "createacct-yourpasswordagain": "Asēð þafungword",
        "createacct-yourpasswordagain-ph": "Wrīt þafungword eft",
-       "remembermypassword": "Gemynan mīne inmeldunge on þissum webbsēcende (oþ $1 {{PLURAL:$1|dæg|daga}} lengest)",
        "userlogin-remembermypassword": "Ætfeolan mīnre inmeldunge",
        "yourdomainname": "Þīn geweald:",
        "password-change-forbidden": "Þū ne canst awendan þafungword on þissum wiki.",
        "minoredit": "Þēos is lytel adihtung",
        "watchthis": "Behealdan þisne tramet",
        "savearticle": "Hordian tramet",
+       "publishpage": "Geswutele tramet",
+       "publishchanges": "Geswutele wrixlung",
        "preview": "Forebysen",
        "showpreview": "Īwan forebysene",
        "showdiff": "Īwan andwendunga",
        "watchlisttools-view": "Sēon andwendunga",
        "watchlisttools-edit": "Sēon and adihtan behealdungtæl",
        "watchlisttools-raw": "Adihtan hrēaw behealdungtæl",
-       "signature": "[[{{ns:user}}:$1|$2]]\n([[{{ns:user_talk}}:$1|mōtung]])",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|mōtung]])",
        "version": "Fadung",
        "version-specialpages": "Syndrige trametas",
        "version-other": "Ōðer",
index 8b64a97..1b6acc9 100644 (file)
                        "Maroen1990",
                        "Sonic N800",
                        "Jdforrester",
-                       "Alaa"
+                       "Alaa",
+                       "Izoozo",
+                       "علاء",
+                       "Hhaboh162002",
+                       "بدارين",
+                       "باسم",
+                       "Moud hosny"
                ]
        },
        "tog-underline": "سطر تحت الوصلات:",
@@ -90,7 +96,7 @@
        "tog-enotifrevealaddr": "أظهر عنوان بريدي الإلكتروني في إشعارات البريد الإلكتروني",
        "tog-shownumberswatching": "اعرض عدد المستخدمين المراقبين",
        "tog-oldsig": "التوقيع الحالي:",
-       "tog-fancysig": "وضع الوصلة يدويا واستعمال نص الويكي",
+       "tog-fancysig": "وضع الوصلة يدوياً واستعمال نص الويكي",
        "tog-uselivepreview": "استعمال المعاينة المباشرة",
        "tog-forceeditsummary": "نبهني عند عدم إدخال ملخص تعديل",
        "tog-watchlisthideown": "أخف تعديلاتي من قائمة المراقبة",
        "october-date": "تشرين الأول/أكتوبر $1",
        "november-date": "تشرين الثاني/نوفمبر $1",
        "december-date": "كانون الأول/ديسمبر $1",
-       "period-am": "صباحا",
+       "period-am": "صباحًا",
        "period-pm": "مساءً",
        "pagecategories": "{{PLURAL:$1|بلا تصنيف|تصنيف|تصنيفان|تصنيفات}}",
        "category_header": "صفحات تصنيف «$1»",
        "subcategories": "تصنيفات فرعية",
        "category-media-header": "ملفات تصنيف \"$1\"",
-       "category-empty": "هذا التصنيف لا يحتوي حاليا على صفحات أو ملفات.",
+       "category-empty": "هذا التصنيف لا يحتوي حالياً على أي صفحات أو ملفات.",
        "hidden-categories": "{{PLURAL:$1|لا تصنيفات مخفية|تصنيف مخفي|تصنيفان مخفيان|تصنيفات مخفية}}",
        "hidden-category-category": "تصنيفات مخفية",
        "category-subcat-count": "{{PLURAL:$2|هذا التصنيف يحوي التصنيف الفرعي التالي|هذا التصنيف يحوي {{PLURAL:$1||التصنيف الفرعي|تصنيفين فرعيين|$1 تصنيفات فرعية}}، من إجمالي $2.}}",
        "tagline": "من {{SITENAME}}",
        "help": "مساعدة",
        "search": "بحث",
+       "search-ignored-headings": "# <!-- اترك هذا السطر كما هو --> <pre>\n# سيتم تجاهل الترويسات خلال عملية البحث\n#التغييرات ستأخذ مجراها ما أن يتم فهرسة الصفحة التي تحتوي على ترويسات\n# يمكنك فرض عملية فهرسة الصفحة من خلال تعديل فارغ\n# الصيغة هي كالأتي:\n# * كل ما يكتب بعد \"#\" إلى آخر السطر يعتبر تعليق\n# * كل سطر غير فارغ سيكون العنوان الذي سيتم تجاهله (سيأخذ العنوان كما هو بالضبط بالتشكيل وخلافه)\nالمراجع\nالوصلات الخارجية\nانظر أيضا\n#</pre><!--أترك هذا السطر كما هو -->",
        "searchbutton": "ابحث",
        "go": "اذهب",
        "searcharticle": "اذهب",
        "databaseerror": "عطل في قاعدة البيانات",
        "databaseerror-text": "حدث خطأ في إستعلام قاعدة البيانات. قد يشير هذا إلى خطأ في البرنامج.",
        "databaseerror-textcl": "حدث خطأ في إستعلام قاعدة البيانات.",
-       "databaseerror-query": "Ø¥ستعلام: $1",
+       "databaseerror-query": "استعلام: $1",
        "databaseerror-function": "دالة: $1",
        "databaseerror-error": "خطأ: $1",
+       "transaction-duration-limit-exceeded": "لتفادي إنشاء تأخير نسخ عالي، هذا الفعل تم إنهاؤه لأن فترة الكتابة ($1) تجاوزت حد $2 ثانية.\nلو أنك تغير عناصر عديدة في نفس الوقت، حاول تجربة عمليا عديدة أصغر بدلا من ذلك.",
        "laggedslavemode": "'''تحذير:''' الصفحة قد لا تحتوي على أحدث التحديثات.",
        "readonly": "قاعدة البيانات مقفلة",
        "enterlockreason": "أدخل سببا للقفل ذاكرا تقديرا لوقت إزالة الغلق",
        "missingarticle-rev": "(رقم المراجعة: $1)",
        "missingarticle-diff": "(فرق: $1، $2)",
        "readonly_lag": "تم قفل قاعدة البيانات تلقائيا حتى تستطيع الخواديم التابعة ملاحقة الخادوم الرئيسي",
+       "nonwrite-api-promise-error": "'Promise-Non-Write-API-Action' HTTP header تم إرساله لكن الطلب كان لAPI write module.",
        "internalerror": "عطل داخلي",
        "internalerror_info": "عطل داخلي: $1",
        "internalerror-fatal-exception": "استثناء مميت من النوع \"$1\"",
        "title-invalid-interwiki": "عنوان الصفحة المطلوب يتضمن وصلة لحلقة لغة وهو ما لا يمكن استخدامه في العناوين.",
        "title-invalid-talk-namespace": "عنوان الصفحة المطلوبة يشير إلى صفحة نقاش غير موجودة.",
        "title-invalid-characters": "عنوان الصفحة المطلوب يتضمن رموزًا غير صالحة: \"$1\"",
+       "title-invalid-relative": "العنوان به مسار نسبي. عنوان الصفحات النسبية (./, ../) هي غير صحيحة، لأنها ستكون غاليا لا يمكن الوصول لها عندما يتم التعامل معها بواسطة متصفح المستخدم.",
+       "title-invalid-magic-tilde": "عنوان الصفحة المطلوب يحتوي على تتابع الشر السحري غير الصحيح (<nowiki>~~~</nowiki>).",
+       "title-invalid-too-long": "عنوان الصفحة المطلوبة طويل جدا. يجب أن يكون ليس أطول من $1 {{PLURAL:$1|بايت}} باستخدام ترميز UTF-8.",
        "title-invalid-leading-colon": "عنوان الصفحة المطلوب يتضمن فاصلة غير صالحة في بدايته.",
        "perfcached": "البيانات التالية مخبأة و قد لا تكون محدثة. {{PLURAL:$1||نتيجة واحدة|نتيجتان|$1 نتائج|$1 نتيجة}} على الأكثر {{PLURAL:$1||مخبّأة|مخبّأتان|مخبّأة}}.",
        "perfcachedts": "البيانات التالية مخزنة، وكان آخر تحديث لها في $1. العدد الأقصى للنتائج المخزنة هو {{PLURAL:$4||نتيجة واحدة|نتيجتان|$4 نتائج|$4 نتيجة}}.",
        "yourpasswordagain": "أعد كتابة كلمة السر:",
        "createacct-yourpasswordagain": "أكد كلمة السر",
        "createacct-yourpasswordagain-ph": "أدخل كلمة المرور مرة أخرى",
-       "remembermypassword": "تذكر دخولي بهذا المتصفح (لمدة أقصاها {{PLURAL:$1||يوم واحد|يومان|$1 أيام|$1 يوما|$1 يوم}})",
        "userlogin-remembermypassword": "أبقني مسجلا للدخول",
        "userlogin-signwithsecure": "الولوج باتصّال مؤمّن",
+       "cannotlogin-title": "لا يمكن تسجيل الدخول",
+       "cannotlogin-text": "تسجيل الدخول غير ممكن.",
        "cannotloginnow-title": "لا يمكن تسجيل الدخول الآن",
        "cannotloginnow-text": "لا يمكن تسجيل الدخول عند استخدام $1.",
+       "cannotcreateaccount-title": "لا يمكن إنشاء الحسابات",
+       "cannotcreateaccount-text": "إنشاء الحسابات المباشر غير مفعل على هذه الويكي.",
        "yourdomainname": "نطاقك:",
        "password-change-forbidden": "أنت لا يمكنك تغيير كلمات السر على هذا الويكي.",
        "externaldberror": "هناك إما خطأ في دخول قاعدة البيانات الخارجية أو أنه غير مسموح لك بتحديث حسابك الخارجي.",
        "createacct-email-ph": "أدخل عنوان بريدك الإلكتروني",
        "createacct-another-email-ph": "أدخل عنوان البريد الإلكتروني",
        "createaccountmail": "استخدم كلمة سر عشوائية مؤقتة وارسلها إلى عنوان البريد الإلكتروني المحدد أدناه",
+       "createaccountmail-help": "يمكن استخدامه لإنشاء حساب لشخص آخر من دون معرفة كلمة المرور.",
        "createacct-realname": "الاسم الحقيقي (اختياري)",
        "createaccountreason": "السبب:",
        "createacct-reason": "السبب",
        "createacct-reason-ph": "لماذا تقوم بإنشاء حساب آخر",
+       "createacct-reason-help": "رسالة تظهر في سجل إنشاء الحسابات",
        "createacct-submit": "افتح الحساب",
        "createacct-another-submit": "أنشئ حسابا",
        "createacct-continue-submit": "مواصلة إنشاء الحساب",
        "nocookiesnew": "تم إنشاء حساب المستخدم، ولكنك لست مسجل الدخول بعد.\nيستخدم {{SITENAME}} كوكيز لتسجيل الدخول.\nلديك الكوكيز معطلة.\nمن فضلك فعلها، ثم سجل الدخول باسم المستخدم وكلمة السر الجديدين.",
        "nocookieslogin": "يستخدم {{SITENAME}} الكوكيز لتسجيل الدخول.\nالكوكيز معطلة لديك.\nمن فضلك فعلها ثم حاول مرة أخرى.",
        "nocookiesfornew": "لم يتم إنشاء حساب المستخدم، لأننا لم نستطع تأكيد مصدره. \nتأكد من أن ملفات تعريف الارتباط (الكوكيز) مفعلة عندك، ثم أعد تحميل الصفحة وحاول مرة أخرى.",
+       "createacct-loginerror": "الحساب تم إنشاؤه تلقائيا لكن لم يمكن تسجيل دخولك تلقائيا. من فضلك اذهب إلى [[Special:UserLogin|تسجيل الدخول اليدوي]].",
        "noname": "لم تحدد اسم مستخدم صحيح.",
        "loginsuccesstitle": "تم الدخول",
        "loginsuccess": "'''لقد سجلت الدخول ل{{SITENAME}} باسم \"$1\".'''",
        "noemail": "لا يوجد عنوان بريد إلكتروني مسجل للمستخدم \"$1\".",
        "noemailcreate": "عليك تقديم عنوان بريد إلكتروني صالح",
        "passwordsent": "تم إرسال كلمة سر جديدة إلى عنوان البريد الإلكتروني المسجل للمستخدم \"$1\".\nمن فضلك حاول تسجيل الدخول مرة ثانية بعد استلامها.",
-       "blocked-mailpassword": "تم منع عنوان الأيبي الخاص بك من التحرير، ولمنع التخريب لا يمكنك أن تستخدم خاصية استرجاع كلمة السر.",
+       "blocked-mailpassword": "تم منع عنوان الأيبي الخاص بك من التحرير، ولمنع التخريب لا يمكنك أن تستخدم خاصية استرجاع كلمة السر من عنوان الآي بي هذا.",
        "eauthentsent": "تم إرسال رسالة تأكيد إلكترونية إلى العنوان المسمى.\nقبل إرسال أي رسالة أخرى لذلك الحساب، عليك أن تتبع التعليمات الواردة في الرسالة، لتأكيد أن هذا الحساب هو لك بالفعل.",
        "throttled-mailpassword": "تم بالفعل إرسال تذكير بكلمة السر، في ال{{PLURAL:$1||ساعة الماضية|ساعتين الماضيتين|$1 ساعات الماضية|$1 ساعة الماضية}}.\nلمنع التخريب، سيتم إرسال تذكير واحد كل {{PLURAL:$1||ساعة|ساعتين|$1 ساعات|$1 ساعة}}.",
        "mailerror": "خطأ أثناء إرسال البريد: $1",
        "changepassword-success": "تم تغيير كلمة السر !",
        "changepassword-throttled": "لديك محاولات تسجيل دخول كثيرة حديثة. من فضلك انتظر $1 قبل المحاولة ثانية.",
        "botpasswords": "كلمات مرور البوت",
+       "botpasswords-summary": "<em>كلمات سر البوت</em> يسمح بالوصول لحساب مستخدم من خلال API بدون استخدام اعتمادات تسجيل الدخول الرئيسية للحساب. صلاحيات المستخدم المتوفرة عند تسجيل الدخول باستخدام كلمة سر بوت ربما تكون مقيدة.\n\nلو أنك لا تعرف لماذا تريد فعل هذا، فأنت ينبغي على الأرجح ألا تففعله. لا أحد ينبغي أن يسألك أبدا أن تولد واحدة من هذه وإعطاؤهم إياها.",
        "botpasswords-disabled": "كلمات السر الخاصة بالبوت معطلة.",
        "botpasswords-no-central-id": "لاستخدام كلمة السر الخاصة بالبوت، يجب أن تقوم بتسجيل الدخول من خلال حساب موحد.",
        "botpasswords-existing": "كلمات مرور البوت الموجودة",
        "botpasswords-label-delete": "احذف",
        "botpasswords-label-resetpassword": "أعد ضبط كلمة السر",
        "botpasswords-label-grants": "المنح التي يمكن تطبيقها:",
+       "botpasswords-help-grants": "كل منحة تعطي وصولا لصلاحيات المستخدم المعروضة التي يمتلكها حساب المستخدم بالفعل. انظر [[Special:ListGrants|جدول المنح]] للمزيد من المعلومات.",
        "botpasswords-label-restrictions": "قيود الاستخدام:",
        "botpasswords-label-grants-column": "الممنوح",
        "botpasswords-bad-appid": "اسم البوت \"$1\" غير صحيح.",
        "botpasswords-insert-failed": "فشل في اضافة  اسم البوت \"$1\".هل اضيف بالفعل؟",
        "botpasswords-update-failed": "فشل في تحديث اسم بوت \"$1\". هل تم حذفه؟",
-       "botpasswords-created-title": "صÙ\86اعة Ù\83Ù\84Ù\85Ø© Ø³Ø± Ø£Ù\84Ù\8aØ©",
-       "botpasswords-created-body": "تم إنشاء كلمة مرور بوت \"$1\".",
-       "botpasswords-updated-title": "تحديث كلمة السر الألية",
-       "botpasswords-updated-body": "كلمة سر البوت\"$1\" قد حذفت.",
+       "botpasswords-created-title": "تÙ\85 Ø¥Ù\86شاء Ù\83Ù\84Ù\85Ø© Ø³Ø± Ø¨Ù\88ت",
+       "botpasswords-created-body": "تم إنشاء كلمة سر بوت \"$1\" للمستخدم \"$2\".",
+       "botpasswords-updated-title": "تم تحديث كلمة سر البوت",
+       "botpasswords-updated-body": "كلمة سر البوت \"$1\" للمستخدم \"$2\" تم تحديثها.",
        "botpasswords-deleted-title": "كلمة سر البوت حذفت",
-       "botpasswords-deleted-body": "كلمة سر البوت\"$1\" قد حذفت.",
+       "botpasswords-deleted-body": "كلمة سر البوت \"$1\" لمستخدم \"$2\" قد حذفت.",
+       "botpasswords-newpassword": "كلمة السر الجديدة لتسجيل الدخول ب <strong>$1</strong> هي <strong>$2</strong>. <em>من فضلك سجل هذه كمرجع في المستقبل .</em>",
        "botpasswords-no-provider": "BotPasswordsSessionProvider غير متاح.",
        "botpasswords-restriction-failed": "قيود كلمة مرور البوت تمنع هذا الولوج.",
+       "botpasswords-invalid-name": "اسم المستخدم الموفر لا يحتوي على فاصل كلمة سر البوت (\"$1\").",
+       "botpasswords-not-exist": "المستخدم \"$1\" لا يمتلك كلمة سر بوت بالاسم \"$2\".",
        "resetpass_forbidden": "كلمات السر لا يمكن تغييرها",
        "resetpass_forbidden-reason": "لا يمكن تغيير كلمة المرور: $1",
        "resetpass-no-info": "يجب أن تكون مسجل الدخول للوصول إلى هذه الصفحة مباشرة.",
        "passwordreset-emailelement": "اسم {{GENDER:$1\n|المستخدم|المستخدمة}}: \n$1\n\nكلمة السر المؤقتة: \n$2",
        "passwordreset-emailsentemail": "إذا كان هذا العنوان البريد مرتبط بحسابك، من ثم سيتم إرسال بريد إلكتروني لإعادة تعيين كلمة السر.",
        "passwordreset-emailsentusername": "إذا كان هناك عنوان بريد إلكتروني مرتبط بهذا المستخدم، ثم سيتم إرسال بريد إلكتروني لإعادة تعيين كلمة السر.",
-       "passwordreset-emailsent-capture": "أُرسل بريد إلكتروني لإعادة ضبط كلمة السر، وهو معروض بالأسفل.",
-       "passwordreset-emailerror-capture": "تم توليد رسالة بريد إلكتروني لتصفير كلمة السر نصّه التالي، إلا أنه تعذّر إرسال الرّسالة إلى {{GENDER:$2|المستخدم|المستخدمة}}: $1",
+       "passwordreset-emailsent-capture2": "{{PLURAL:$1|رسالة|رسائل}} البريد الإلكتروني لضبط كلمة السر تم إرسالها. {{PLURAL:$1|اسم المستخدم وكلمة السر معروضان|قائمة أسماء المستخدمين وكلمات السر معروضة}} بالأسفل.",
+       "passwordreset-emailerror-capture2": "إرسال بريد إلى {{GENDER:$2|المستخدم|المستخدمة}} فشل: $1 {{PLURAL:$3|اسم المستخدم وكلمة السر معروضان|lقائمة أسماء المستخدمين كلمات السر معروضة}} بالأسفل.",
+       "passwordreset-nocaller": "يجب أن يتم توفير مستدعي",
+       "passwordreset-nosuchcaller": "المستدعي غير موجود: $1",
+       "passwordreset-ignored": "إعادة ضبط كلمة السر لم تتم التعامل معها. ربما لا موفر تم ضبطه؟",
        "passwordreset-invalideamil": "عنوان بريد إلكتروني غير صالح",
+       "passwordreset-nodata": "لا اسم مستخدم ولا عنوان بريد الإلكتروي تم توفيره",
        "changeemail": "تغيير أو إزالة عنوان البريد الإلكتروني",
        "changeemail-header": "إكمال هذا النموذج لتغيير عنوان البريد الإلكتروني الخاص بك. إذا كنت ترغب في إزالة جمعية أي عنوان البريد الإلكتروني من حسابك، وترك الفراغ عنوان البريد الإلكتروني الجديد عند تقديم النموذج",
-       "changeemail-passwordrequired": "تحتاج إلى إدخال كلمة السر الخاصة بك لتأكيد هذا التغيير.",
        "changeemail-no-info": "يجب تسجيل الدخول للوصول إلى هذه الصفحة مباشرة.",
        "changeemail-oldemail": "عنوان البريد الإلكتروني الحالي:",
        "changeemail-newemail": "عنوان البريد الإلكتروني الجديد:",
+       "changeemail-newemail-help": "هذا الحقل ينبغي أن يترك فارغا في حالة لو كنت تريد إزالة عنوان البريد الإلكتروني الخاص بك. أنت لن تكون قادرا على إعادة ضبط كلمة سر ضائعة ولن تتلقى رسئل بريد إلكتروني من هذه الويكي لو أزيل عنوان البريد الإلكتروني.",
        "changeemail-none": "(لا شيء)",
        "changeemail-password": "كلمة سر {{SITENAME}} الخاصة بك:",
        "changeemail-submit": "غيّر البريد الإلكتروني",
        "savearticle": "احفظ الصفحة",
        "savechanges": "احفظ التغييرات",
        "publishpage": "نشر الصفحة",
-       "publishchanges": "انشر التغييرات",
+       "publishchanges": "نشر التغييرات",
        "preview": "عرض مسبق",
        "showpreview": "أظهر معاينة",
        "showdiff": "عرض التغييرات",
        "blankarticle": "<strong>تنبيه:</strong> الصفحة التي تريد إنشاءها فارغة. إذا نقرت \"{{int:savearticle}}\" ثانية فستنشأ الصفحة بدون محتوى.",
-       "anoneditwarning": "'''تحذير:''' لم تقم بالدخول.\nسيسجل عنوان الآيبي خاصتك في تاريخ هذه الصفحة.",
+       "anoneditwarning": "<strong>تحذير:</strong> أنت غير مسجل الدخول. عنوان الأيبي الخاص بك سيكون معروضا بشكل علني لو قمت بأي تعديلات. لو أنك <strong>[$1 سجلت الدخول]</strong> أو <strong>[$2 أنشأت حسابا]</strong>، فتعديلاتك ستنسب لاسم المستخدم الخاص بك، بالإضافة إلى فوائد أخرى.",
        "anonpreviewwarning": "''أنت غير مسجل الدخول. الحفظ سيسجل عنوان الأيبي الخاص بك في تاريخ هذه الصفحة.''",
        "missingsummary": "'''تنبيه:''' لم تقم بكتابة ملخص للتعديل.\nإذا قمت بضغط حفظ الصفحة مرة أخرى، فيتم حفظ تعديلك بدون ملخص.",
        "selfredirect": "<strong>تحذير:</strong> أنت تقوم بتحويل الصفحة إلى نفسها.\nربما حددت الهدف الخطأ للتحويلة أو أنك تقوم بتحرير الصفحة الخطأ.\n\nإذا نقرت على «{{int:savearticle}}» مرة أخرى، سيتم إنشاء التحويلة رغم الخطأ.",
        "accmailtext": "أُرسِلت كلمة سر مولدة عشوائيا ل[[User talk:$1|$1]] إلى $2. يمكن تغييرها في صفحة ''[[Special:ChangePassword|تغيير كلمة السر]]'' بعد تسجيل الدخول.",
        "newarticle": "(جديد)",
        "newarticletext": "لقد تبعت وصلة لصفحة لم يتم إنشائها بعد.\nلإنشاء هذه الصفحة ابدأ الكتابة في الصندوق بالأسفل (انظر في [$1 صفحة المساعدة] للمزيد من المعلومات).\nإذا كانت زيارتك لهذه الصفحة بالخطأ، اضغط على زر ''رجوع'' في متصفح الإنترنت لديك.",
-       "anontalkpagetext": "----''هذه صفحة نقاش لمستخدم مجهول لم يقم بإنشاء حساب بعد أو لا يستعمل ذلك الحساب.\nلذا فيجب علينا استعمال رقم الأيبي للتعرف عليه/عليها.\nمثل هذا العنوان يمكن أن يشترك فيه عدة مستخدمين.\nلو كنت مستخدما مجهولا وتشعر بأن تعليقات لا تخصك تم توجيهها إليك، من فضلك [[Special:CreateAccount|أنشئ حسابا]] أو [[Special:UserLogin|سجل الدخول]] لتجنب الارتباك المستقبلي مع مستخدمين مجهولين آخرين.''",
+       "anontalkpagetext": "----\n<em>هذه صفحة نقاش لمستخدم مجهول لم يقم بإنشاء حساب بعد أو لا يستعمل ذلك الحساب.</em>\nلذا فيجب علينا استعمال رقم الأيبي للتعرف عليه/عليها.\nمثل هذا العنوان يمكن أن يشترك فيه عدة مستخدمين.\nلو كنت مستخدما مجهولا وتشعر بأن تعليقات لا تخصك تم توجيهها إليك، من فضلك [[Special:CreateAccount|أنشئ حسابا]] أو [[Special:UserLogin|سجل الدخول]] لتجنب الارتباك المستقبلي مع مستخدمين مجهولين آخرين.",
        "noarticletext": "الصفحة خالية. يمكنك [[Special:Search/{{PAGENAME}}|البحث عن عنوانها]] في الصفحات الأخرى أو\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} البحث في السجلات] (لتعرف إذا حذفت)،\nأو '''[{{fullurl:{{FULLPAGENAME}}|action=edit}} إنشاؤها بنفسك]'''</span>.",
        "noarticletext-nopermission": "لا يوجد حاليا أي نص في هذه الصفحة.\nيمكنك [[Special:Search/{{PAGENAME}}|البحث عن عنوان هذه الصفحة]] في الصفحات الأخرى، أو <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} البحث في السجلات المتعلقة بها]</span>، لكنك لست مخولاً لإنشاء هذه الصفحة.",
        "missing-revision": "المراجعة #$1 من الصفحة المسماة \"{{FULLPAGENAME}}\" غير موجودة.\n\nهذا يحدث عادة عن طريق اتباع وصلة تاريخ قديمة لصفحة تم حذفها.\nالتفاصيل يمكن إيجادها في [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سجل الحذف].",
        "userpage-userdoesnotexist": "حساب المستخدم \"<nowiki>$1</nowiki>\" غير مسجل.\nمن فضلك تأكد أنك تريد إنشاء/تعديل هذه الصفحة.",
        "userpage-userdoesnotexist-view": "حساب المستخدم \"$1\" غير مسجل.",
        "blocked-notice-logextract": "هذا المستخدم ممنوع حاليا.\nآخر مدخلة في سجل المنع موفرة بالأسفل كمرجع:",
-       "clearyourcache": "'''ملاحظة:''' بعد الحفظ، قد تحتاج إلى إفراغ كاش متصفحك لرؤية التغييرات.\n* '''فايرفوكس / سفاري:''' اضغط ''Shift'' أثناء ضغط ''Reload''، أو اضغط أيا من ''Ctrl-F5'' أو ''Ctrl-R'' (''⌘-R'' على ماك)\n* '''جوجل كروم:''' اضغط ''Ctrl-Shift-R'' (''⌘-Shift-R'' على ماك)\n* '''إنترنت إكسبلورر:''' اضغط ''Ctrl'' أثناء ضغط ''Refresh''، أو اضغط ''Ctrl-F5''\n* '''كنكرر:''' اضغط ''Reload'' أو اضغط ''F5''\n* '''أوبرا:''' أفرغ الكاش في ''Tools → Preferences''",
+       "clearyourcache": "<strong>ملاحظة:</strong> بعد الحفظ، أنت قد تحتاج إلى إفراغ الكاش الخاص بمتصفحك لرؤية التغييرات.\n* <strong>فايرفوكس / سافاري:</strong> أمسك <em>Shift</em> أثناء ضغط <em>Reload</em>، أو اضغط على إما <em>Ctrl-F5</em> أو <em>Ctrl-R</em> (<em>⌘-R</em> على ماك)\n* <strong>جوجل كروم:</strong> اضغط <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> على ماك)\n* <strong>إنترنت إكسبلورر:</strong> أمسك <em>Ctrl</em> أثناء ضغط <em>Refresh</em>، أو اضغط <em>Ctrl-F5</em>\n* <strong>أوبرا:</strong> اذهب إلى <em>Menu → Settings</em> (<em>Opera → Preferences</em> على ماك) ثم إلى <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
        "usercssyoucanpreview": "'''ملاحظة:''' استعمل زر \"{{int:showpreview}}\" لتجربة CSS الجديد قبل حفظ الصفحة.",
        "userjsyoucanpreview": "'''ملاحظة:''' استعمل زر \"{{int:showpreview}}\" لتجربة جافاسكربت الجديدة قبل حفظ الصفحة.",
        "usercsspreview": "'''تذكر أنك تقوم بعرض الأنماط المتراصة (CSS) الخاصة بك فقط\nلم يتم حفظها بعد!'''",
        "permissionserrors": "خطأ في السماح",
        "permissionserrorstext": "لا تمتلك الصلاحية لفعل هذا، {{PLURAL:$1||للسبب التالي|للسببين التاليين|للأسباب التالية}}:",
        "permissionserrorstext-withaction": "لا تملك الصلاحيات ل$2، لل{{PLURAL:$1||سبب التالي|سببين التاليين|أسباب التالية}}:",
+       "contentmodelediterror": "أنت لا يمكنك تعديل هذه المراجعة لأن موديل محتواها هو  <code>$1</code>، والذي يختلف عن موديل المحتوى الحالي للصفحة  <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''تحذير: أنت تعيد إنشاء صفحة سبق حذفها.'''\n\nيجب عليك التيقن من أن الاستمرار بتحرير هذه الصفحة ملائم.\nسجلا الحذف والنقل لهذه الصفحة معروضان هنا للتيسير:",
        "moveddeleted-notice": "هذه الصفحة تم حذفها.\nسجلا الحذف والنقل للصفحة معروضان بالأسفل كمرجع.",
+       "moveddeleted-notice-recent": "عذرا، هذه الصفحة تم حذفها مؤخرا (في آخر 24 ساعة).\nسجلا الحذف والنقل للصفحة معروضان بالأسفل كمرجع.",
        "log-fulllog": "أظهر السجل الكامل",
        "edit-hook-aborted": "التعديل تم تركه بواسطة الخطاف.\nلم يعط تفسيرا.",
        "edit-gone-missing": "لم يمكن تحديث الصفحة.\nيبدو أنه تم حذفها.",
        "content-model-text": "نص عادي",
        "content-model-javascript": "جافاسكربت",
        "content-model-css": "CSS",
-       "content-json-empty-object": "غرض فارغ",
+       "content-json-empty-object": "كائن فارغ",
        "content-json-empty-array": "مصفوفة فارغة",
+       "deprecated-self-close-category": "صفحات تستخدم وسوم أتش تي أم أل غير صالحة",
+       "deprecated-self-close-category-desc": "هذه الصفحة تحتوي على وسوم HTML مغلقة ذاتيا، مثل  <code>&lt;b/></code> أو <code>&lt;span/></code>. سلوك هذه سيتغير سريعا ليكون متوافقا مع معيار HTML5، لذا فاستخدامهم في نص الويكي ينبغي أن يتم الاستغناء عنه.",
        "duplicate-args-warning": "<strong>تنبيه:</strong> المدخل \"$3\" ل[[:$1]] المستعمل في [[:$2]] مكرر. آخر قيمة مكرر منه هي المعتمدة.",
        "duplicate-args-category": "صفحات تستعمل قالبا ببيانات مكررة",
        "duplicate-args-category-desc": "تحوي هذه الصفحة استدعاءات قالب تستخدم متغيرات مزدوجة مثل <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> أو <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "التعديل يبدو أنه قد تم الترجع عنه بالفعل.",
        "undo-summary": "الرجوع عن التعديل $1 بواسطة [[Special:Contributions/$2|$2]] ([[User talk:$2|نقاش]])",
        "undo-summary-username-hidden": "الرجوع عن المراجعة $1 التي أجراها مستخدمي مخفي",
-       "cantcreateaccounttitle": "لا يمكن إنشاء حساب",
        "cantcreateaccount-text": "إنشاء الحسابات من عنوان الأيبي هذا ('''$1''') تم منعه بواسطة [[User:$3|$3]].\n\nالسبب المعطى بواسطة $3 هو ''$2''",
        "cantcreateaccount-range-text": "إنشاء الحسابات من عناوين الآيبي في النطاق <strong>$1</strong>، التي تحتوي على الآيبي الخاص بك (<strong>$4</strong>)، قد منعها [[User:$3|$3]].\n\nالسبب المعطى بواسطة $3 هو <em>$2</em>",
        "viewpagelogs": "اعرض سجلات هذه الصفحة",
        "revdel-restore": "تغيير الرؤية",
        "pagehist": "تاريخ الصفحة",
        "deletedhist": "التاريخ المحذوف",
-       "revdelete-hide-current": "خطأ Ø¹Ù\86د Ø¥Ø­فاء العنصر المؤرخ في $2 $1: هذه هي المراجعة الحالية.\nلا يمكن إخفاؤها.",
+       "revdelete-hide-current": "خطأ Ø¹Ù\86د Ø¥Ø®فاء العنصر المؤرخ في $2 $1: هذه هي المراجعة الحالية.\nلا يمكن إخفاؤها.",
        "revdelete-show-no-access": "خطأ في إظهار العنصر ذا التاريخ $2 $1: هذا العنصر معلم ك\"مقيد\".\nليس لك صلاحية الوصول إليه.",
        "revdelete-modify-no-access": "خطأ في تعديل العنصر ذا التاريخ $2 $1: هذا العنصر معلم ك\"مقيد\".\nليس لك صلاحية الوصول إليه.",
        "revdelete-modify-missing": "خطأ في تعديل العنصر ذا الهوية $1: العنصر مفقود من قاعدة البيانات!",
        "mergehistory-go": "عرض التعديلات القابلة للدمج",
        "mergehistory-submit": "دمج المراجعات",
        "mergehistory-empty": "لا مراجعات يمكن دمجها.",
-       "mergehistory-done": "$3 {{PLURAL:$3|مراجعة|مراجعة}} من $1{{PLURAL:$3|كان|اين}} تم دمجها بنجاح في [[:$2]].",
+       "mergehistory-done": "$3 {{PLURAL:$3|مراجعة}} من $1{{PLURAL:$3|كان|اين}} تم دمجها بنجاح في [[:$2]].",
        "mergehistory-fail": "غير قادر على عمل دمج التاريخ، من فضلك أعد التحقق من محددات الصفحة والزمن.",
        "mergehistory-fail-bad-timestamp": "الختم الزمني غير صالح.",
        "mergehistory-fail-invalid-source": "الصفحة المصدر غير صالحة.",
        "preferences": "تفضيلات",
        "mypreferences": "تفضيلات",
        "prefs-edits": "عدد التعديلات:",
-       "prefsnologintext2": "الرجاء $1 لضبط تفضيلات المستخدم.",
+       "prefsnologintext2": "الرجاء تسجيل الدخول لضبط تفضيلات المستخدم.",
        "prefs-skin": "واجهة",
        "skin-preview": "عرض مسبق",
        "datedefault": "لا تفضيل",
        "recentchangesdays-max": "الحد الأقصى {{PLURAL:$1|أقل من يوم|يوم واحد|يومان|$1 أيام|$1 يوما|$1 يوم}}",
        "recentchangescount": "عدد التعديلات الظاهرة مبدئيا:",
        "prefs-help-recentchangescount": "بما في ذلك أحدث التغييرات وتاريخ الصفحات والسجلات.",
-       "prefs-help-watchlist-token2": "Ù\87ذا Ù\87Ù\88 Ø§Ù\84Ù\85Ù\81تاح Ø§Ù\84سرÙ\8a Ù\84تغذÙ\8aØ© Ø§Ù\84Ù\88Ù\90ب لقائمة مراقبتك.\nيمكن لأي شخص يعرفه أن يقرأ قائمة مراقبتك، ولذا لا تتشاركه مع أحد. [[Special:ResetTokens|انقر هنا إذا أردت إعادة ضبطه]].",
+       "prefs-help-watchlist-token2": "Ù\87ذا Ù\87Ù\88 Ø§Ù\84Ù\85Ù\81تاح Ø§Ù\84سرÙ\8a Ù\84تغذÙ\8aØ© Ø§Ù\84Ù\88Ù\8aب لقائمة مراقبتك.\nيمكن لأي شخص يعرفه أن يقرأ قائمة مراقبتك، ولذا لا تتشاركه مع أحد. [[Special:ResetTokens|انقر هنا إذا أردت إعادة ضبطه]].",
        "savedprefs": "تم حفظ تفضيلاتك.",
        "savedrights": "حُفظت الصلاحيات الجديدة {{GENDER:$1|للمستخدم|للمستخدمة}} $1.",
        "timezonelegend": "المنطقة الزمنية:",
        "localtime": "الوقت المحلي:",
        "timezoneuseserverdefault": "استخدام الويكي الافتراضي ($1)",
        "timezoneuseoffset": "آخر (حدد الفرق)",
-       "servertime": "Ù\88Ù\82ت Ø§Ù\84خادÙ\88Ù\85:",
+       "servertime": "وقت الخادم:",
        "guesstimezone": "أدخل التوقيت من المتصفح",
        "timezoneregion-africa": "أفريقيا",
        "timezoneregion-america": "أمريكا",
        "prefs-files": "ملفات",
        "prefs-custom-css": "CSS مخصص",
        "prefs-custom-js": "جافاسكربت مخصص",
-       "prefs-common-css-js": "سي إس إس وجافاسكربت مشترك لجميع المظاهر:",
+       "prefs-common-css-js": "CSS وجافاسكربت مشترك لجميع الواجهات:",
        "prefs-reset-intro": "يمكنك استخدام هذه الصفحة لإعادة تفضيلاتك للحالة الافتراضية للموقع.\nلن تستطيع استرجاع الحالة السابقة.",
        "prefs-emailconfirm-label": "تأكيد البريد الإلكتروني:",
        "youremail": "البريد:",
        "badsig": "توقيع خام غير صحيح؛ تحقق من وسوم HTML.",
        "badsiglength": "توقيعك طويل جدا.\nيجب أن يكون أقل من $1 {{PLURAL:$1|حرف|حروف}}.",
        "yourgender": "كيف تفضل أن توصف؟",
-       "gender-unknown": "عند ذكرك، البرنامج سيستخدم كلمات محايدة النوع متى ما كان ذلك ممكنا",
+       "gender-unknown": "عند ذكرك، البرنامج سيستخدم كلمات محايدة النوع متى ما كان ذلك ممكناً",
        "gender-male": "هو يعدل صفحات الويكي",
        "gender-female": "هي تعدل صفحات الويكي",
-       "prefs-help-gender": "ضبط هذا التفضيل اختياري.\nيستخدم البرنامج هذه القيمة لمخاطبتك ومخاطبة الآخرين عنك وفقا للصيغة النحوية الملائمة للجنس.\nستكون هذه المعلومة علنية.",
+       "prefs-help-gender": "ضبط هذا التفضيل اختياري.\nيستخدم البرنامج هذه القيمة لمخاطبتك ومخاطبة الآخرين عنك وفقاً للصيغة النحوية الملائمة للجنس.\nهذه المعلومة ستكون علنية.",
        "email": "البريد الإلكتروني",
        "prefs-help-realname": "الاسم الحقيقي اختياري.\nلو اخترت أن توفره، فسيستخدم في الإشارة إلى عملك.",
        "prefs-help-email": "تحديد عنوان البريد الإلكتروني اختياري، ولكنه يلزم لإعادة تعيين كلمة المرور في حال نسيت كلمة المرور الخاصة بك.",
        "group-user": "مستخدمون",
        "group-autoconfirmed": "مستخدمون مؤكدون تلقائيا",
        "group-bot": "بوتات",
-       "group-sysop": "مديرو نظام",
+       "group-sysop": "إداريون",
        "group-bureaucrat": "بيروقراطيون",
-       "group-suppress": "Ù\86ظار",
+       "group-suppress": "Ù\85زÙ\8aÙ\84Ù\88Ù\86",
        "group-all": "(الكل)",
        "group-user-member": "{{GENDER:$1|مستخدم|مستخدمة}}",
        "group-autoconfirmed-member": "{{GENDER:$1|مستخدم مؤكد تلقائيًا|مستخدمة مؤكدة تلقائيًا}}",
        "group-bot-member": "{{GENDER:$1|بوت}}",
        "group-sysop-member": "{{GENDER:$1|إداري|إدارية}}",
        "group-bureaucrat-member": "{{GENDER:$1|بيروقراط}}",
-       "group-suppress-member": "{{GENDER:$1|Ù\86اظر|Ù\86اظرة}}",
+       "group-suppress-member": "{{GENDER:$1|Ù\85زÙ\8aÙ\84\85زÙ\8aÙ\84ة}}",
        "grouppage-user": "{{ns:project}}:مستخدمون",
        "grouppage-autoconfirmed": "{{ns:project}}:مستخدمون مؤكدون تلقائيا",
        "grouppage-bot": "{{ns:project}}:بوتات",
        "grouppage-sysop": "{{ns:project}}:إداريون",
        "grouppage-bureaucrat": "{{ns:project}}:بيروقراطيون",
-       "grouppage-suppress": "{{ns:project}}:نظار",
+       "grouppage-suppress": "{{ns:project}}:خاصية الإزالة",
        "right-read": "قراءة الصفحات",
        "right-edit": "تعديل الصفحات",
        "right-createpage": "إنشاء الصفحات (التي ليست صفحات نقاش)",
        "right-move": "نقل الصفحات",
        "right-move-subpages": "نقل الصفحات مع صفحاتها الفرعية",
        "right-move-rootuserpages": "نقل صفحات المستخدمين الأساسية",
-       "right-move-categorypages": "انقل صفحات التصنيف",
+       "right-move-categorypages": "نقل صفحات التصنيف",
        "right-movefile": "نقل الملفات",
        "right-suppressredirect": "عدم إنشاء تحويلة من الاسم القديم عند نقل صفحة",
        "right-upload": "رفع الملفات",
        "right-writeapi": "استخدام API للكتابة",
        "right-delete": "حذف الصفحات",
        "right-bigdelete": "حذف الصفحات ذات التواريخ الكبيرة",
-       "right-deletelogentry": "حذÙ\81 Ù\88اÙ\84غاء Ø­Ø°Ù\81 Ø¥Ø¯Ø®Ø§Ù\84ات Ø³Ø¬Ù\84 Ù\85عÙ\8aÙ\86",
+       "right-deletelogentry": "حذÙ\81 Ù\88Ø¥Ù\84غاء Ø­Ø°Ù\81 Ù\85دخÙ\84ات Ø³Ø¬Ù\84 Ù\85عÙ\8aÙ\86Ø©",
        "right-deleterevision": "حذف واسترجاع مراجعات معينة من الصفحات",
        "right-deletedhistory": "رؤية مدخلات التاريخ المحذوفة، بدون نصوصها المصاحبة",
        "right-deletedtext": "عرض النص المحذوف والتغييرات بين المراجعات المحذوفة",
        "right-browsearchive": "البحث في الصفحات المحذوفة",
        "right-undelete": "استرجاع صفحة",
        "right-suppressrevision": "مراجعة واسترجاع المراجعات المخفية عن مديري النظام",
-       "right-viewsuppressed": "أعرض Ø§Ù\84Ù\85راجعات Ø§Ù\84Ù\85Ø®Ù\81Ù\8aØ© Ø¨Ù\88اسطة Ø£Ù\8a Ù\85ستخدÙ\85",
+       "right-viewsuppressed": "عرض المراجعات المخفية بواسطة أي مستخدم",
        "right-suppressionlog": "رؤية السجلات السرية",
        "right-block": "منع المستخدمين الآخرين من التعديل",
        "right-blockemail": "منع مستخدم من إرسال بريد إلكتروني",
        "right-editinterface": "تعديل واجهة المستخدم",
        "right-editusercssjs": "تعديل ملفات CSS و JS للمستخدمين الآخرين",
        "right-editusercss": "تعديل ملفات CSS للمستخدمين الآخرين",
-       "right-edituserjs": "تعديل ملفات JS للمستخدمين الآخرين",
+       "right-edituserjs": "تعديل ملفات جافاسكريبت للمستخدمين الآخرين",
        "right-editmyusercss": "تعديل ملفات CSS للمستخدم نفسه",
        "right-editmyuserjs": "تعديل ملفات جافاسكربت للمستخدم نفسه",
        "right-viewmywatchlist": "عرض قائمة مراقبتك",
        "right-editmywatchlist": "حرر قائمة مراقبتك. لاحظ أن بعض الإجراءات لا تزال تضيف الصفحات حتى بدون هذا الحق.",
-       "right-viewmyprivateinfo": "إستعرض Ø¨Ù\8aاÙ\86اتÙ\83 Ø§Ù\84شخصÙ\8aØ© (Ù\85Ø«Ù\84 Ø§Ù\84برÙ\8aد Ø§Ù\84Ø¥Ù\84Ù\83ترÙ\88Ù\86Ù\8a Ù\88اÙ\84Ø¥سم الحقيقي)",
-       "right-editmyprivateinfo": "حرر Ø¨Ù\8aاÙ\86اتÙ\83 Ø§Ù\84شخصÙ\8aØ© (Ù\85Ø«Ù\84 Ø§Ù\84برÙ\8aد Ø§Ù\84Ø¥Ù\84Ù\83ترÙ\88Ù\86Ù\8a Ù\88اÙ\84Ø¥سم الحقيقي)",
+       "right-viewmyprivateinfo": "استعراض Ø¨Ù\8aاÙ\86اتÙ\83 Ø§Ù\84شخصÙ\8aØ© (Ù\85Ø«Ù\84 Ø§Ù\84برÙ\8aد Ø§Ù\84Ø¥Ù\84Ù\83ترÙ\88Ù\86Ù\8a Ù\88اÙ\84اسم الحقيقي)",
+       "right-editmyprivateinfo": "تعدÙ\8aÙ\84 Ø¨Ù\8aاÙ\86اتÙ\83 Ø§Ù\84شخصÙ\8aØ© (Ù\85Ø«Ù\84 Ø§Ù\84برÙ\8aد Ø§Ù\84Ø¥Ù\84Ù\83ترÙ\88Ù\86Ù\8a Ù\88اÙ\84اسم الحقيقي)",
        "right-editmyoptions": "تعديل تفضيلاتك",
        "right-rollback": "استرجاع تعديلات آخر مستخدم عدل صفحة معينة سريعا",
        "right-markbotedits": "التعليم على تعديلات الاسترجاع كتعديلات بوت",
        "right-import": "استيراد الصفحات من ويكيات أخرى",
        "right-importupload": "استيراد الصفحات من ملف مرفوع",
        "right-patrol": "تعليم تعديلات الآخرين بعلامة المراجعة",
-       "right-autopatrol": "عÙ\84Ù\85 ØªØ¹Ø¯Ù\8aÙ\84ات Ø§Ù\84Ù\85ستخدÙ\85 مراجعة تلقائيا",
+       "right-autopatrol": "اÙ\84تعÙ\84Ù\8aÙ\85 Ø¹Ù\84Ù\89 ØªØ¹Ø¯Ù\8aÙ\84ات Ø§Ù\84Ù\85ستخدÙ\85 Ù\83مراجعة تلقائيا",
        "right-patrolmarks": "رؤية علامات المراجعة في أحدث التغييرات",
        "right-unwatchedpages": "رؤية قائمة بالصفحات غير المراقبة",
        "right-mergehistory": "دمج تاريخ الصفحات",
        "right-override-export-depth": "تصدير الصفحات متضمنة الصفحات الموصولة حتى عمق 5",
        "right-sendemail": "إرسال رسائل بريد إلكتروني إلى مستخدمين آخرين",
        "right-passwordreset": "عرض رسائل إعادة ضبط كلمات السر",
-       "right-managechangetags": "Ø¥Ù\86شاء Ù\88حذÙ\81 [[Special:Tags|اÙ\84Ù\88سÙ\88Ù\85]] Ù\85Ù\86 Ù\82اعدة Ø§Ù\84بÙ\8aاÙ\86ات",
+       "right-managechangetags": "Ø¥Ù\86شاء Ù\88تعطÙ\8aÙ\84 [[Special:Tags|اÙ\84Ù\88سÙ\88Ù\85]]",
        "right-applychangetags": "تطبيق [[Special:Tags|الوسوم]]  مع التغييرات التي أجريتها.",
+       "right-changetags": "إضافة وإزالة [[Special:Tags|وسوم]] في مراجعات ومدخلات سجل فردية",
+       "right-deletechangetags": "حذف [[Special:Tags|الوسوم]] من قاعدة البيانات",
        "grant-generic": "\"$1\" حزمة الصلاحيات",
        "grant-group-page-interaction": "التفاعل مع الصفحات",
        "grant-group-file-interaction": "التفاعل مع الوسائط",
        "grant-group-high-volume": "أداء نشاط كبير الحجم",
        "grant-group-customization": "التخصيص والتفضيلات",
        "grant-group-administration": "أداء عمليات إدارية",
+       "grant-group-private-information": "الوصول للبيانات السرية المتعلقة بك",
        "grant-group-other": "نشاطات متفرقة",
        "grant-blockusers": "منع ورفع المنع عن المستخدمين",
        "grant-createaccount": "إنشاء حسابات",
        "grant-createeditmovepage": "إنشاء وتعديل ونقل الصفحات",
        "grant-delete": "حذف الصفحات والمراجعات ومدخلات السجلات",
+       "grant-editinterface": "تعديل نطاق ميدياويكي والCSS/JavaScript الخاصة بالمستخدم",
+       "grant-editmycssjs": "تعديل الCSS/JavaScript الخاصة بحسابك",
        "grant-editmyoptions": "تعديل تفضيلاتك",
        "grant-editmywatchlist": "تعديل قائمة مراقبتك",
        "grant-editpage": "تعديل صفحات موجودة",
        "grant-editprotected": "تعديل صفحات محمية",
        "grant-highvolume": "تعديل كبير الحجم",
+       "grant-oversight": "إخفاء المستخدمين وإخفاء المراجعات",
        "grant-patrol": "تغييرات دورية للصفحات",
+       "grant-privateinfo": "الوصول للمعلومات السرية",
        "grant-protect": "حماية وإزالة حماية الصفحات",
        "grant-rollback": "استرجاع التغييرات في الصفحات",
        "grant-sendemail": "إرسال بريد إلكتروني للمستخدمين الآخرين",
        "action-read": "قراءة هذه الصفحة",
        "action-edit": "تعديل هذه الصفحة",
        "action-createpage": "إنشاء هذه الصفحة",
-       "action-createtalk": "Ø¥Ù\86شاء ØµÙ\81حات Ø§Ù\84Ù\86Ù\82اش",
+       "action-createtalk": "Ø¥Ù\86شاء ØµÙ\81حة Ø§Ù\84Ù\86Ù\82اش Ù\87Ø°Ù\87",
        "action-createaccount": "إنشاء حساب المستخدم هذا",
-       "action-autocreateaccount": "تلقائيا إنشاء هذا الحساب مستخدم خارجي",
+       "action-autocreateaccount": "تلقائيا إنشاء هذا الحساب الخارجي للمستخدم",
        "action-history": "اعرض تاريخ هذه الصفحة",
        "action-minoredit": "التعليم على هذا التعديل كطفيف",
        "action-move": "نقل هذه الصفحة",
        "action-viewmyprivateinfo": "مشاهدة معلوماتك الخاصة",
        "action-editmyprivateinfo": "تعديل معلوماتك الخاصة",
        "action-editcontentmodel": "عدل عدل طريقة محتوى صفحة",
-       "action-managechangetags": "Ø¥Ù\86شاء Ù\88حذÙ\81 Ø§Ù\84Ù\88سÙ\88Ù\85 Ù\85Ù\86 Ù\82اعدة Ø§Ù\84بÙ\8aاÙ\86ات",
+       "action-managechangetags": "Ø¥Ù\86شاء Ù\88تعطÙ\8aÙ\84 Ø§Ù\84Ù\88سÙ\88Ù\85",
        "action-applychangetags": "تطبيق الوسوم مع تغييراتك",
+       "action-changetags": "أضف وأزل وسوما في مراجعات ومدخلات سجل فردية",
+       "action-deletechangetags": "حذف الوسوم من قاعدة البيانات",
+       "action-purge": "إفراغ كاش هذه الصفحة",
        "nchanges": "{{PLURAL:$1|لا تغييرات|تغيير واحد|تغييران|$1 تغييرات|$1 تغييرا|$1 تغيير}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|منذ الزيارة الأخيرة}}",
        "enhancedrc-history": "تاريخ",
        "recentchanges-legend-heading": "<strong>شرح:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (راجع أيضا [[Special:NewPages|قائمة الصفحات الجديدة]])",
        "recentchanges-submit": "أظهر",
-       "rcnotefrom": "بالأسفل التغييرات منذ <strong>$2</strong> (إلى <strong>$1</strong> معروضة).",
+       "rcnotefrom": "بالأسفل {{PLURAL:$5|التغيير|التغييرات}} منذ <strong>$2</strong> (إلى <strong>$1</strong> معروضة).",
        "rclistfrom": "أظهر التغييرات بدء من $3 $2",
        "rcshowhideminor": "$1 التعديلات الطفيفة",
        "rcshowhideminor-show": "أظهر",
        "recentchangeslinked-feed": "تغييرات ذات علاقة",
        "recentchangeslinked-toolbox": "تغييرات ذات علاقة",
        "recentchangeslinked-title": "التغييرات المرتبطة بصفحة «$1»",
-       "recentchangeslinked-summary": "هذه قائمة بالتغييرات التي تمت حديثا للصفحات الموصولة من صفحة معينة (أو إلى الأعضاء ضمن تصنيف معين).\nالصفحات في [[Special:Watchlist|قائمة مراقبتك]] '''مغلظة'''",
+       "recentchangeslinked-summary": "هذه قائمة بالتغييرات التي تمت حديثاً للصفحات الموصولة من صفحة معينة (أو إلى الأعضاء ضمن تصنيف معين).\nالصفحات في [[Special:Watchlist|قائمة مراقبتك]] '''عريضة'''",
        "recentchangeslinked-page": "اسم الصفحة:",
-       "recentchangeslinked-to": "أظهر التغييرات للصفحات الموصولة للصفحة المعطاة عوضا عن ذلك",
+       "recentchangeslinked-to": "أظهر التغييرات للصفحات الموصولة للصفحة المعطاة عوضاً عن ذلك",
        "recentchanges-page-added-to-category": "[[:$1]] أضيفت إلى التصنيف",
-       "recentchanges-page-added-to-category-bundled": "أضيفت [[:$1]] و{{PLURAL:$2|صفحة واحدة|صفحتان|$2 صفحات}} إلى التصنيف",
+       "recentchanges-page-added-to-category-bundled": "أضيفت [[:$1]] إلى التصنيف، [[Special:WhatLinksHere/$1|هذه الصفحة مضمنة في صفحات اخرى]]",
        "recentchanges-page-removed-from-category": "أزيلت [[:$1]] من التصنيف",
-       "recentchanges-page-removed-from-category-bundled": "أزÙ\8aÙ\84ت [[:$1]] Ù\88{{PLURAL:$2|صÙ\81حة Ù\88احدة|صÙ\81حتاÙ\86|$2 ØµÙ\81حات}} Ù\85Ù\86 Ø§Ù\84تصÙ\86Ù\8aÙ\81",
+       "recentchanges-page-removed-from-category-bundled": "أزÙ\8aÙ\84ت [[:$1]] Ù\85Ù\86 Ø§Ù\84تصÙ\86Ù\8aÙ\81Ø\8c [[Special:WhatLinksHere/$1|Ù\87Ø°Ù\87 Ø§Ù\84صÙ\81حة Ù\85ضÙ\85Ù\86Ø© Ù\81Ù\8a ØµÙ\81حات Ø£Ø®Ø±Ù\89]]",
        "autochange-username": "تغيير آلي لميدياويكي",
        "upload": "ارفع ملفا",
        "uploadbtn": "ارفع الملف",
        "ignorewarnings": "تجاهل أية تحذيرات",
        "minlength1": "أسماء الملفات يجب أن تتكون من حرف واحد على الأقل.",
        "illegalfilename": "اسم الملف \"$1\" يحتوي على حروف غير مسموح بها في عناوين الصفحات.\nمن فضلك أعد تسمية الملف وحاول رفعه مرة أخرى.",
-       "filename-toolong": "Ø·Ù\88Ù\84 Ø£Ø³Ù\85اء Ø§Ù\84Ù\85Ù\84Ù\81ات Ù\8aجب Ø£Ù\86 Ù\84ا Ù\8aتجاÙ\88ز 240 Ø­Ø±Ù\81 (باÙ\8aت)",
+       "filename-toolong": "Ø·Ù\88Ù\84 Ø£Ø³Ù\85اء Ø§Ù\84Ù\85Ù\84Ù\81ات Ù\8aجب Ø£Ù\86 Ù\84ا Ù\8aتجاÙ\88ز 240 Ø¨Ø§Ù\8aت",
        "badfilename": "تم تغيير اسم الملف إلى \"$1\".",
        "filetype-mime-mismatch": "امتداد الملف \".$1\" لا يطابق نوع MIME للملف ($2).",
        "filetype-badmime": "من غير المسموح به رفع ملفات من النوع \"$1\".",
        "file-thumbnail-no": "يبدأ الملف ب <strong>$1</strong>.\nيبدو أن الملف مصغرا لحجم أعلى ''(تصغير)''.\nإذا كانت لديك الصورة في درجة دقة كاملة قم برفعها، أو قم بتغيير اسم الملف من فضلك.",
        "fileexists-forbidden": "هناك ملف موجود بهذا الاسم بالفعل، ولا يمكن إعادة الكتابة عليه.\nلو أنك مازلت تريد رفع ملفك، من فضلك عد واستخدم اسماً جديداً. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "يوجد ملف بنفس الاسم بالفعل في مستودع الملفات المشترك.\nلو كنت مازلت تريد رفع ملفك، من فضلك ارجع واستخدم اسماً جديداً.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "الملف المرفوع هو نسخة مطابقة تمامًا للنسخة الحالية من <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "الملف المرفوع هو نسخة مطابقة من {{PLURAL:$2|نسخة أقدم|نسخ أقدم}} من <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "هذا الملف مكرر  {{PLURAL:$1|للملف|للملفات}} التالية:",
        "file-deleted-duplicate": "ملف مطابق لهذه الملف ([[:$1]]) تم حذفه من قبل. ينبغي أن تتحقق من تاريخ الحذف لهذا الملف قبل المتابعة بإعادة رفعه.",
        "file-deleted-duplicate-notitle": "سابقا تم حذف ملف مطابق لهذا الملف، وقد تم منع العنوان.\nينبغي أن تسأل شخص ما لديه القدرة على عرض بيانات الملف الممنوع لاستعراض الوضع قبل الشروع في إعادة تحميله.",
        "uploaddisabledtext": "رفع الملفات معطل.",
        "php-uploaddisabledtext": "رفع ملفات PHP معطل. من فضلك تحقق من إعدادات رفع الملفات.",
        "uploadscripted": "هذا الملف يضم كود HTML أو كود آخر يمكن أن يفسره متصفح الوب بطريقة خاطئة.",
+       "upload-scripted-pi-callback": "لا يمكن رفع ملف يحتوي على تعليمة معالجة XML-stylesheet",
+       "uploaded-script-svg": "تم العثور على عنصر سكريبت \"$1\" في ملف الSVG المرفوع.",
+       "uploaded-hostile-svg": "تم العثور على CSS غير آمن في عنصر الشكل في ملف الSVG المرفوع.",
+       "uploaded-event-handler-on-svg": "ضبط سمات معالج الأحداث <code>$1=\"$2\"</code> غير مسموح به في ملفات SVG.",
+       "uploaded-href-attribute-svg": "سمات href في ملفات SVG مسموح بوصلها فقط بأهداف http:// أو https:// ، تم العثور على <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "تم العثور على href لبيانات غير آمنة: هدف URI <code>&lt;$1 $2=\"$3\"&gt;</code> في ملف SVG المرفوع.",
+       "uploaded-animate-svg": "تم العثور على وسم \"animate\" الذي ربما يكون يغير href, باستخدام سمة \"from\" <code>&lt;$1 $2=\"$3\"&gt;</code> في ملف SVG المرفوع.",
+       "uploaded-setting-event-handler-svg": "ضبط سمات معالج الأحداث ممنوع، تم العثور على <code>&lt;$1 $2=\"$3\"&gt;</code> في ملف SVG المرفوع.",
+       "uploaded-setting-href-svg": "استخدام وسم \"set\" لإضافة سمة \"href\" للعنصر الأب ممنوع.",
+       "uploaded-wrong-setting-svg": "استخدام وسم \"set\" لإضافة هدف خارجي/بيانات/سكريبت لأي سمة ممنوع. تم العثور على <code>&lt;set to=\"$1\"&gt;</code> في ملف SVG المرفوع.",
+       "uploaded-setting-handler-svg": "SVG الذي يضبط سمة \"handler\" مع خارجي/بيانات/سكريبت ممنوع. تم العثور على <code>$1=\"$2\"</code> في ملف SVG المرفوع.",
+       "uploaded-remote-url-svg": "SVG الذي يضبط أي سمة شكل مع URL خارجي ممنوع. تم العثور على <code>$1=\"$2\"</code> في ملف SVG المرفوع.",
+       "uploaded-image-filter-svg": "تم العثور على فلتر صورة مع URL: <code>&lt;$1 $2=\"$3\"&gt;</code> في ملف SVG المرفوع.",
        "uploadscriptednamespace": "يحتوي ملف SVG هذا على اسم نطاق غير مشروع \" $1 \"",
        "uploadinvalidxml": "تعذر تحليل XML في الملف المرفوع.",
        "uploadvirus": "الملف يحتوي على فيروس! التفاصيل: $1",
        "upload-options": "خيارات الرفع",
        "watchthisupload": "راقب هذا الملف",
        "filewasdeleted": "تم رفع ثم حذف ملف بهذا الاسم من قبل.\nمن الأفضل مراجعة $1 قبل رفعه مرة أخرى.",
+       "filename-thumb-name": "هذا يبدو وكأنه عنوان صورة مصغرة. من فضلك لا ترفع صورة مصغرة لنفس الويكي مرة ثانية. أو، من فضلك أصلح اسم الملف بحيث يكون معبرا أكثر، ولا يحتوي على بادئة الصورة المصغرة.",
        "filename-bad-prefix": "اسم الملف الذي ترفعه يبدأ ب'''\"$1\"'''، وهو اسم غير وصفي غالباً ما تخصصه الكاميرات الرقمية تلقائياً.\nمن فضلك اختر اسماً يصف ملفك بوضوح أكثر.",
        "filename-prefix-blacklist": " #<!-- اترك هذا السطر تماما كما هو --> <pre>\n# الصيغة كالتالي:\n#   * كل شيء من علامة \"#\" إلى آخر السطر هو تعليق\n#   * كل سطر غير فارغ هو بادئة لأسماء الملفات النمطية التي توضع تلقائيا بواسطة الكاميرات الرقمية\nCIMG # كاسيو\nDSC_ # نيكون\nDSCF # فوجي\nDSCN # نيكون\nDUW # بعض الهواتف المحمولة\nIMG # عام\nJD # جينوبتيك\nMGP # بينتاكس\nPICT # متنوع\n #</pre> <!-- اترك هذا السطر تماما كما هو -->",
        "upload-proto-error": "بروتوكول غير صحيح",
        "upload-too-many-redirects": "احتوى المسار تحويلات كثيرة جدا",
        "upload-http-error": "صودف خطأ HTTP: $1",
        "upload-copy-upload-invalid-domain": "رفع النسخ غير متاح من هذا الموقع",
+       "upload-foreign-cant-upload": "هذه الويكي ليست مضبوطة لرفع الملفات لمستودع الملفات الخارجي المطلوب.",
+       "upload-foreign-cant-load-config": "فشل تحميل الإعدادات للملفات المرفوعة لمستودع الملفات الخارجي.",
+       "upload-dialog-disabled": "رفع الملفات باستخدام هذا الحوار معطلة على هذه الويكي.",
        "upload-dialog-title": "رفع الملف",
        "upload-dialog-button-cancel": "إلغاء",
        "upload-dialog-button-done": "تم",
        "upload-dialog-button-upload": "رفع",
        "upload-form-label-infoform-title": "التفاصيل",
        "upload-form-label-infoform-name": "الاسم",
+       "upload-form-label-infoform-name-tooltip": "عنوان وصفي فريد للملف، والذي سيكون اسم الملف. يمكنك أن تستخدم لغة عادية مع مسافات. لا تضمن امتداد الملف.",
        "upload-form-label-infoform-description": "الوصف",
+       "upload-form-label-infoform-description-tooltip": "باختصار صف كل شيء ملحوظ حول العمل.\nلصورة، اذكر الأشياء الأساسية المصورة، المناسبة أو المكان.",
        "upload-form-label-usage-title": "الاستخدام",
        "upload-form-label-usage-filename": "اسم الملف",
        "upload-form-label-own-work": "هذا عملي الخاص",
        "upload-form-label-infoform-categories": "تصنيفات",
        "upload-form-label-infoform-date": "التاريخ",
+       "upload-form-label-own-work-message-generic-local": "أنا أؤكد أنني أقوم برفع هذا الملف مع مراعاة شروط الخدمة وسياسات الترخيص في {{SITENAME}}.",
+       "upload-form-label-not-own-work-message-generic-local": "لو أنك غير قادر على رفع هذا الملف تحت سياسات {{SITENAME}}، من فضلك أغلق هذا الحوار وجرب طريقة أخرى.",
+       "upload-form-label-not-own-work-local-generic-local": "أنت ربما تريد تجربة [[Special:Upload|صفحة الرفع الافتراضية]].",
+       "upload-form-label-own-work-message-generic-foreign": "أنا أفهم أنني أقوم برفع هذا الملف إلى مستودع مشترك. أنا أؤكد أنني أقوم بهذا بالتوافق مع شروط الخدمة وسياسات الترخيص هناك.",
+       "upload-form-label-not-own-work-message-generic-foreign": "لو أنك غير قادر على رفع هذا الملف تحت سياسات المستودع المشترك، من فضلك أغلق هذا الحوار وجرب طريقة أخرى.",
+       "upload-form-label-not-own-work-local-generic-foreign": "أنت ربما تريد أيضا تجربة [[Special:Upload|صفحة الرفع على {{SITENAME}}]]، لو أن هذا الملف يمكن رفعه هناك تحت سياساتهم.",
        "backend-fail-stream": "لا يمكن عرض الملف $1.",
        "backend-fail-backup": "لا يمكن صنع نسخة أحتياطية للملف $1.",
        "backend-fail-notexists": "الملف $1 غير موجود.",
-       "backend-fail-hashes": "لم يمكن الحصول على هاش الملف من أجل المقارنة",
-       "backend-fail-notsame": "يوجد بالفعل ملف غير متطابق في $1.",
-       "backend-fail-invalidpath": "$1 ليس مساراً صالحاً للتخزين.",
-       "backend-fail-delete": "لم يمكن حذف الملف $1.",
-       "backend-fail-describe": "Ù\84ا Ù\8aÙ\85Ù\83Ù\86 ØªØºÙ\8aÙ\8aر Ø§Ù\84بÙ\8aاÙ\86ات Ø§Ù\84تعرÙ\8aÙ\81 (metadata) Ù\84Ù\84Ù\85Ù\84Ù\81 \" $1 \".",
-       "backend-fail-alreadyexists": "الملف $1 موجود بالفعل.",
-       "backend-fail-store": "لا يمكن تخزين الملف $1 في $2 .",
-       "backend-fail-copy": "لا يمكن نسخ الملف  $1  إلى  $2 .",
-       "backend-fail-move": "تعذر نقل ملف $1 إلى $2 .",
+       "backend-fail-hashes": "لم يمكن الحصول على hashes الملفات من أجل المقارنة",
+       "backend-fail-notsame": "يوجد بالفعل ملف غير متطابق في \"$1\".",
+       "backend-fail-invalidpath": "\"$1\" ليس مساراً صالحاً للتخزين.",
+       "backend-fail-delete": "لم يمكن حذف الملف \"$1\".",
+       "backend-fail-describe": "Ù\84ا Ù\8aÙ\85Ù\83Ù\86 ØªØºÙ\8aÙ\8aر Ø¨Ù\8aاÙ\86ات Ø§Ù\84تعرÙ\8aÙ\81 metadata Ù\84Ù\84Ù\85Ù\84Ù\81 \"$1\".",
+       "backend-fail-alreadyexists": "الملف \"$1\" موجود بالفعل.",
+       "backend-fail-store": "لا يمكن تخزين الملف \"$1\" في \"$2\".",
+       "backend-fail-copy": "لا يمكن نسخ الملف  \"$1\"  إلى  \"$2\".",
+       "backend-fail-move": "تعذر نقل ملف \"$1\" إلى \"$2\".",
        "backend-fail-opentemp": "تعذّر فتح ملف مؤقت.",
        "backend-fail-writetemp": "تعذّرت كتابة ملف مؤقت.",
        "backend-fail-closetemp": "تعذّر إغلاق ملف مؤقت.",
        "backend-fail-read": "لا يمكن قراءة الملف $1.",
        "backend-fail-create": "تعذر كتابة الملف $1.",
        "backend-fail-maxsize": "تعذر كتابة الملف $1 لأنه أكبر من  {{PLURAL:$2|بايت واحد|$2 بايت}}.",
-       "backend-fail-readonly": "خلفية التخزين \"$1\" في وضعية القراءة فقط حاليا. السبب في ذلك هو: \"$2\"",
+       "backend-fail-readonly": "خلفية التخزين \"$1\" في وضعية القراءة فقط حاليا. السبب في ذلك هو:\n<em>$2</em>",
        "backend-fail-synced": "الملف \"$1\" في حالة غير متناسقة ضمن خلفية التخزين الداخلية",
        "backend-fail-connect": "تعذر ربط الإتصال بخلفية التخزين \"$1\".",
        "backend-fail-internal": "وقع خطأ غير معروف في خلفية التخزين \"$1\".",
        "uploadstash-summary": " توفر هذه الصفحة الوصول إلى الملفات التي يتم تحميلها (أو في أثناء عملية التحميل) ولكنها لم تنشر بعد. هذه الملفات هي غير مرئية لأحد إلا للمستخدم الذين تم الرفع لهم.",
        "uploadstash-clear": "مسح الملفات المخبأة",
        "uploadstash-nofiles": "ليس لديك أي ملفات مخبأة.",
-       "uploadstash-badtoken": "Ù\84Ù\85 Ù\8aÙ\86جح Ø£Ø¯Ø§Ø¡ Ø°Ù\84Ù\83 Ø§Ù\84عÙ\85Ù\84Ø\8c Ø±Ø¨Ù\85ا Ù\84Ø£Ù\86 Ù\88ثائÙ\82 ØªÙ\81Ù\88Ù\8aض Ø§Ù\84تحرÙ\8aر Ø§Ù\84خاصة Ø¨Ù\83 Ù\85Ù\86تÙ\87Ù\8aØ© Ø§Ù\84صÙ\84احÙ\8aØ©. حاول مرة أخرى.",
+       "uploadstash-badtoken": "Ù\81Ø´Ù\84 Ø£Ø¯Ø§Ø¡ Ø°Ù\84Ù\83 Ø§Ù\84عÙ\85Ù\84Ø\8c Ø±Ø¨Ù\85ا Ù\84Ø£Ù\86 Ù\88ثائÙ\82 ØªÙ\81Ù\88Ù\8aض Ø§Ù\84تحرÙ\8aر Ø§Ù\84خاصة Ø¨Ù\83 Ù\85Ù\86تÙ\87Ù\8aØ© Ø§Ù\84صÙ\84احÙ\8aØ©. Ù\85Ù\86 Ù\81ضÙ\84Ù\83 حاول مرة أخرى.",
        "uploadstash-errclear": "فشلت عملية مسح الملفات.",
        "uploadstash-refresh": "تحديث قائمة الملفات",
        "uploadstash-thumbnail": "اعرض صورة مصغرة",
+       "uploadstash-exception": "لم يمكن تخزين الرفع في الstash ($1): \"$2\".",
        "invalid-chunk-offset": "قطعة أوفست غير صالحة",
        "img-auth-accessdenied": "رفض الوصول",
        "img-auth-nopathinfo": "PATH_INFO مفقود.\nخادومك ليس مضبوطاً لتمرير هذه المعلومة.\nقد يكون مبنياً على نظام CGI ولا يمكنه دعم img_auth.\nراجع https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "img-auth-notindir": "المسار المطلوب غير موجود في مجلد الرفع المضبوط.",
        "img-auth-badtitle": "تعذر تشكيل عنوان صالح من \"$1\".",
-       "img-auth-nologinnWL": "لست والجاً و\"$1\" ليست في القائمة البيضاء.",
+       "img-auth-nologinnWL": "لست والجا و\"$1\" ليست في القائمة البيضاء.",
        "img-auth-nofile": "الملف \"$1\" غير موجود.",
        "img-auth-isdir": "أنت تحاول الوصول إلى الدليل \"$1\".\nيسمح بوصول الملفات فقط.",
        "img-auth-streaming": "عرض \"$1\".",
        "filerevert-submit": "استرجع",
        "filerevert-success": "'''[[Media:$1|$1]]''' تم استرجاعها [$4 للنسخة بتاريخ $3، $2].",
        "filerevert-badversion": "لا توجد نسخة محلية سابقة لهذا الملف بالتاريخ المعطى.",
+       "filerevert-identical": "الإصدار الحالي من الملف بالفعل مطابق للإصدار المحدد.",
        "filedelete": "احذف $1",
        "filedelete-legend": "احذف الملف",
        "filedelete-intro": "أنت على وشك حذف الملف '''[[Media:$1|$1]]''' مع كل تاريخه.",
        "apisandbox": "ملعب API",
        "apisandbox-jsonly": "الجافا سكريبت مطلوبة لاستخدام ملعب API",
        "apisandbox-api-disabled": "واجهة برمجة التطبيق API معطلة في هذا الموقع.",
+       "apisandbox-intro": "استخدم هذه الصفحة للتجربة ب<strong>MediaWiki web service API</strong>.\nارجع إلى [[mw:API:Main page|توثيق الAPI]] للمزيد من التفاصيل حول استخدام الAPI. مثال: [https://www.mediawiki.org/wiki/API#A_simple_example احصل على محتوى صفحة رئيسية]. اختر فعلا لترى المزيد من الأمثلة.\n\nلاحظ أنه، على الرغم من أن هذا ملعب، فالأفعال التي تقوم بها على هذه الصفحة ربما تعدل الويكي.",
        "apisandbox-fullscreen": "وسع اللوحة",
+       "apisandbox-fullscreen-tooltip": "وسع صفحة الملعب لتملأ نافذة المتصفح.",
        "apisandbox-unfullscreen": "أظهر الصفحة",
+       "apisandbox-unfullscreen-tooltip": "قلل صفحة الملعب، حتى تكون وصلات التصفح لميدياويكي متوفرة.",
        "apisandbox-submit": "عمل الطلب",
        "apisandbox-reset": "إفراغ",
        "apisandbox-retry": "أعد المحاولة",
+       "apisandbox-loading": "تحميل المعلومات لAPI module \"$1\"...",
+       "apisandbox-load-error": "حدث خطأ أثناء تحميل المعلومات لAPI module \"$1\": $2",
        "apisandbox-no-parameters": "وحدة API هذه ليس بها معاملات.",
        "apisandbox-helpurls": "وصلات المساعدة",
        "apisandbox-examples": "أمثلة",
        "apisandbox-dynamic-parameters-add-placeholder": "اسم المعامل",
        "apisandbox-dynamic-error-exists": "يوجد بالفعل معامل باسم \"$1\".",
        "apisandbox-deprecated-parameters": "معاملات مهملة",
+       "apisandbox-fetch-token": "املأ التوكين تلقائيا",
        "apisandbox-submit-invalid-fields-title": "بعض الحقول غير صالحة",
        "apisandbox-submit-invalid-fields-message": "برجاء تصحيح الحقل المعلم والمحاولة مرة أخرى.",
        "apisandbox-results": "النتائج",
        "apisandbox-sending-request": "إرسال طلب API ...",
        "apisandbox-loading-results": "استقبال طلبات API ...",
+       "apisandbox-results-error": "حدث خطأ أثناء تحميل رد استعدلام الAPI: $1.",
        "apisandbox-request-url-label": "مسار الطلب:",
        "apisandbox-request-time": "وقت الطلب: {{PLURAL:$1|$1 ms}}",
        "apisandbox-results-fixtoken": "رمز الصحيح وإعادة الموافقة",
+       "apisandbox-results-fixtoken-fail": "فشل جلب توكين \"$1\"",
        "apisandbox-alert-page": "هناك حقول غير صالحة في هذه الصفحة.",
        "apisandbox-alert-field": "قيمة هذا الحقل غير صالحة.",
        "booksources": "مصادر كتاب",
        "booksources-text": "توجد أدناه قائمة بوصلات لمواقع أخرى تبيع الكتب الجديدة والمستعملة، أيضا يمكنك أن تحصل على معلومات إضافية عن الكتب التي تبحث عنها من هناك:",
        "booksources-invalid-isbn": "رقم ISBN المعطى لا يبدو صحيحا؛ تحقق من أخطاء النسخ من المصدر الأصلي.",
        "specialloguserlabel": "المؤدي:",
-       "speciallogtitlelabel": "الهدف (عنوان أو مستخدم):",
+       "speciallogtitlelabel": "الهدف (عنوان أو {{ns:user}}:اسم المستخدم للمستخدم):",
        "log": "سجلات",
        "logeventslist-submit": "أظهر",
        "all-logs-page": "كل السجلات العامة",
        "listusers-noresult": "لم يتم إيجاد مستخدم.",
        "listusers-blocked": "(ممنوع)",
        "activeusers": "قائمة المستخدمين النشطين",
-       "activeusers-intro": "هذه قائمة بالمستخدمين الذين مارسوا نوعاً من النشاط خلال {{PLURAL:$1||اليوم الماضي|اليومين الماضيين|ال$1 أيام الماضية|ال$1 يوماً ماضياً|ال$1 يوم ماضي}}.",
-       "activeusers-count": "{{PLURAL:$1|لا أفعال|فعل واحد|فعلان اثنان|$1 أفعال|$1 فعلاً|$1 فعل}} منذ {{PLURAL:$3||يوم|يومين|$3 أيام|$3 يوماً|$1 يوم}}",
+       "activeusers-intro": "هذه قائمة بالمستخدمين الذين مارسوا نوعا من النشاط خلال {{PLURAL:$1||اليوم الماضي|اليومين الماضيين|ال$1 أيام الماضية|ال$1 يوما ماضيا|ال$1 يوم ماضي}}.",
+       "activeusers-count": "{{PLURAL:$1|لا أفعال|فعل واحد|فعلان اثنان|$1 أفعال|$1 فعلا|$1 فعل}} منذ {{PLURAL:$3||يوم|يومين|$3 أيام|$3 يوما|$1 يوم}}",
        "activeusers-from": "اعرض المستخدمين ابتداء من:",
        "activeusers-hidebots": "أخف البوتات",
        "activeusers-hidesysops": "أخف الإداريين",
        "activeusers-noresult": "لم يعثر على أي مستخدمين",
-       "activeusers-submit": "لعرض المستخدمين النشطين",
+       "activeusers-submit": "عرض المستخدمين النشطين",
        "listgrouprights": "صلاحيات مجموعات المستخدمين",
        "listgrouprights-summary": "التالي قائمة بمجموعات المستخدمين المعرفة في هذا الويكي، بصلاحياتهم المصاحبة.\nربما تكون هناك [[{{MediaWiki:Listgrouprights-helppage}}|معلومات إضافية]] حول الصلاحيات المنفردة.",
        "listgrouprights-key": "عنوان:\n* <span class=\"listgrouprights-granted\">صلاحية ممنوحة</span>\n* <span class=\"listgrouprights-revoked\">صلاحية مسحوبة</span>",
        "listgrouprights-namespaceprotection-namespace": "النطاق",
        "listgrouprights-namespaceprotection-restrictedto": "الصلاحيات التي تسمح للمستخدم بالتعديل",
        "listgrants": "المنح",
+       "listgrants-summary": "التالي هو قائمة بالمنح بعمليات الوصول لصلاحيات المستخدم المصاحبة لها. المستخدمون يمكنهم إعطاء صلاحية للتطبيقات لاستخدام حساباتهم، ولكن بسماحات محدودة بناء على المنح التي أعطاها المستخدم للتطبيق. تطبيق يعمل بالنيابة عن مستخدم لا يمكنه استخدام الصلاحيات التي لا يمتلكها المستخدم بالفعل.\nربما تكون هناك [[{{MediaWiki:Listgrouprights-helppage}}|معلومات إضافية]] حول الصلاحيات الفردية.",
        "listgrants-grant": "المنحة",
        "listgrants-rights": "الصلاحيات",
        "trackingcategories": "تصانيف التتبع",
        "trackingcategories-msg": "تصانيف التتبع",
        "trackingcategories-name": "اسم الرسالة",
        "trackingcategories-desc": "معايير إدراج تصنيف",
+       "restricted-displaytitle-ignored": "الصفحات بعناوين عرض تم تجاهلها",
+       "restricted-displaytitle-ignored-desc": "هذه الصفحة تحتوي على <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> تم تجاهله لأنه لا يساوي عنوان الصفحة الفعلي.",
        "noindex-category-desc": "هذه الصفحة لا تفهرسها الروبوتات لأن فيها الكلمة السحرية <code><nowiki>__NOINDEX__</nowiki></code> ولأنها في نطاق يسمح بهذا العلم.",
        "index-category-desc": "الصفحة فيها <code><nowiki>__INDEX__</nowiki></code> (وهي في نطاق يسمح بهذا العلم) ولذا فالروبوتات تفهرسها بينما الأصل ألا تفعل.",
        "post-expand-template-inclusion-category-desc": "بعد توسيع جميع القوالب حجم الصفحة أكبر من <code><nowiki>__INDEX__</nowiki></code> ولذا فبعض القوالب لا تُوسّع.",
        "emailccsubject": "نسخة من رسالتك إلى $1: $2",
        "emailsent": "أُرسل البريد الإلكتروني",
        "emailsenttext": "أُرسلت رسالتك الإلكترونية.",
-       "emailuserfooter": "هذا البريد الإلكتروني تم إرساله بواسطة $1 إلى $2 بواسطة وظيفة \"{{int:emailuser}}\" في {{SITENAME}}.",
+       "emailuserfooter": "هذا البريد الإلكتروني {{GENDER:$1|تم إرساله}} بواسطة $1 إلى {{GENDER:$2|$2}} بواسطة وظيفة \"{{int:emailuser}}\" في {{SITENAME}}.",
        "usermessage-summary": "ترك رسالة نظام.",
        "usermessage-editor": "مراسل النظام",
        "watchlist": "قائمة مراقبتي",
        "mywatchlist": "قائمة مراقبتي",
        "watchlistfor2": "ل$1 $2",
        "nowatchlist": "لا توجد مدخلات في قائمة مراقبتك.",
-       "watchlistanontext": "الرجاء $1 لعرض أو تعديل الصفحات في قائمة مراقبتك.",
+       "watchlistanontext": "الرجاء تسجيل الدخول لعرض أو تعديل الصفحات في قائمة مراقبتك.",
        "watchnologin": "غير مسجل الدخول",
        "addwatch": "إضافة إلى قائمة المراقبة",
        "addedwatchtext": "\"[[:$1]]\" وصفحة نقاشها أضيفتا إلى [[Special:Watchlist|قائمة مراقبتك]].",
+       "addedwatchtext-talk": "\"[[:$1]]\" وصفحتها المرافقة تمت إضافتها إلى [[Special:Watchlist|قائمة مراقبتك]].",
        "addedwatchtext-short": "أضيفت صفحة \"$1\" إلى قائمة مراقبتك.",
        "removewatch": "إزالة من قائمة المراقبة",
        "removedwatchtext": "\"[[:$1]]\" وصفحة نقاشها أزيلتا من [[Special:Watchlist|قائمة مراقبتك]].",
+       "removedwatchtext-talk": "\"[[:$1]]\" وصفحتها المرافقة تمت إزالتها من [[Special:Watchlist|قائمة مراقبتك]].",
        "removedwatchtext-short": "أزيلت صفحة \"$1\" من قائمة مراقبتك.",
        "watch": "راقب",
        "watchthispage": "راقب هذه الصفحة",
        "watching": "يراقب...",
        "unwatching": "إزالة المراقبة...",
        "watcherrortext": "حدث خطأ أثناء تغيير إعدادات الرصد الخاصة بك \"$1\".",
-       "enotif_reset": "علم على كل الصفحات كمَزُورة",
+       "enotif_reset": "علم على كل الصفحات كمرئية",
        "enotif_impersonal_salutation": "مستخدم {{SITENAME}}",
        "enotif_subject_deleted": "الصفحة {{SITENAME}} $1 حذفها {{الجنس: $2 | $2 }}",
        "enotif_subject_created": "الصفحة {{SITENAME}}  $1  أنشأها {{الجنس: $2 | $2 }}",
        "deletepage": "حذف الصفحة",
        "confirm": "أكد",
        "excontent": "المحتوى كان: '$1'",
-       "excontentauthor": "المحتوى كان: '$1' (والمساهم الوحيد كان '[[Special:Contributions/$2|$2]]')",
+       "excontentauthor": "المحتوى كان: \"$1\" والمساهم الوحيد كان \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|talk]])",
        "exbeforeblank": "المحتوى قبل الإفراغ كان: '$1'",
        "delete-confirm": "حذف \"$1\"",
        "delete-legend": "حذف",
        "delete-toobig": "لهذه الصفحة تاريخ تعديل طويل، أكثر من {{PLURAL:$1||مراجعة واحدة|مراجعتين|$1 مراجعات|$1 مراجعة}}.\nقُيّد محذف مثل هذه الصفحات لمنع الاضطراب المفاجئة في {{SITENAME}}.",
        "delete-warning-toobig": "لهذه الصفحة تاريخ تعديل طويل، أكثر من {{PLURAL:$1||مراجعة واحدة|مراجعتين|$1 مراجعات|$1 مراجعة}}.\nقد يؤدي حذفها إلى اضطراب عمليات قاعدة البيانات في {{SITENAME}}؛\nاستمر مع الحذر.",
        "deleteprotected": "لا يمكنك حذف هذه الصفحة لأنها محمية.",
-       "deleting-backlinks-warning": "[[Special:WhatLinksHere/{{FULLPAGENAME}}|تتصل صفحات أخرى]] بالصفحة التي تريد حذفها.",
+       "deleting-backlinks-warning": "<strong>تحذير:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|صفحات أخرى]] تصل إلى أو تضمن الصفحة التي أنت على وشك حذفها.",
        "rollback": "التراجع عن التعديلات",
        "rollbacklink": "استرجع",
        "rollbacklinkcount": "استرجع {{PLURAL:$1|لا تعديلات|تعديلا واحدا|تعديلين|$1 تعديلات|$1 تعديلاً|تعديل}}",
        "rollbacklinkcount-morethan": "استرجاع أكثر من {{PLURAL:$1|تعديل|تعديل|تعديلين|$1 تعديلات|$1 تعديلاً|$1 تعديل}}",
        "rollbackfailed": "لم ينجح الاسترجاع",
+       "rollback-missingparam": "محددات مطلوبة مفقودة عند الطلب.",
+       "rollback-missingrevision": "غير قادر على تحميل بيانات المراجعة.",
        "cantrollback": "لم يمكن استرجاع التعديل؛\nآخر مساهم هو المؤلف الوحيد لهذه الصفحة.",
        "alreadyrolled": "لم يمكن استرجاع آخر تعديل ل[[$1]] بواسطة [[User:$2|$2]] ([[User talk:$2|نقاش]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]])؛\nشخص آخر عدل أو استرجع الصفحة بالفعل.\n\nآخر تعديل كان بواسطة [[User:$3|$3]] ([[User talk:$3|نقاش]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "ملخص التعديل كان:<em>$1</em>.",
        "revertpage": "استرجع تعديلات [[Special:Contributions/$2|$2]] ([[User talk:$2|نقاش]]) حتى آخر مراجعة ل[[User:$1|$1]]",
-       "revertpage-nouser": "استرجع تعديلات مستخدم مخفيّ حتى آخر مراجعة ل{{GENDER:$1|[[User:$1|$1]]}}",
+       "revertpage-nouser": "استرجع تعديلات مستخدم مخفي حتى آخر مراجعة ل{{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "استرجع تعديلات $1؛\nاسترجع حتى آخر نسخة بواسطة $2.",
+       "rollback-success-notify": "تم استرجاع التعديلات بواسطة $1;\nتم التغيير إلى آخر مراجعة بواسطة $2. [$3 عرض التغييرات]",
        "sessionfailure-title": "فشل في الجلسة",
        "sessionfailure": "يبدو أنه هناك مشكلة في جلسة الدخول الخاصة بك؛\nلذلك فقد ألغيت هذه العملية كإجراء احترازي ضد الاختراق.\nمن فضلك اضغط على مفتاح \"رجوع\" لتحميل الصفحة التي جئت منها، ثم حاول مرة أخرى.",
        "changecontentmodel": "غير نموذج المحتوى لصفحة",
        "changecontentmodel-success-text": "نوع المحتوى ل[[:$1]] تم تغييره.",
        "changecontentmodel-cannot-convert": "المحتوى على [[:$1]] لا يمكن تحويله لنوع من $2.",
        "changecontentmodel-nodirectediting": "نموذج المحتوى $1 لا يدعم التعديل المباشر",
+       "changecontentmodel-emptymodels-title": "لا موديلات محتوى متوفرة",
+       "changecontentmodel-emptymodels-text": "المحتوى على[[:$1]] لا يمكن تغييره لأي نوع.",
        "log-name-contentmodel": "سجل تغيير نموذج المحتوى",
        "log-description-contentmodel": "الأحداث المرتبطة بنماذج المحتوى لصفحة",
+       "logentry-contentmodel-new": "$1 {{GENDER:$2|أنشأ|أنشأت}} الصفحة $3 باستخدام موديل محتوى غير قياسي \"$5\"",
+       "logentry-contentmodel-change": "$1 {{GENDER:$2|غير|غيرت}} موديل المحتوى للصفحة $3 من \"$4\" إلى \"$5\"",
        "logentry-contentmodel-change-revertlink": "استرجع",
        "logentry-contentmodel-change-revert": "استرجع",
        "protectlogpage": "سجل الحماية",
        "undeletehistorynoadmin": "هذه الصفحة تم حذفها.\nالسبب للحذف معروض في الملخص بالأسفل، إلى جانب تفاصيل المستخدمين الذين قاموا بالتعديل على هذه الصفحة قبل حذفها.\nنص المراجعات المحذوفة هذه متوفر فقط للإداريين.",
        "undelete-revision": "المراجعة المحذوفة ل$1 (بتاريخ $4، الساعة $5) بواسطة $3:",
        "undeleterevision-missing": "مراجعة غير صحيحة أو مفقودة.\nربما لديك وصلة سيئة، أو ربما المراجعة تم استرجاعها أو إزالتها من الأرشيف.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|مراجعة واحدة|$1 مراجعة}} لم يمكن استعادتها, لأن ال<code>rev_id</code> {{PLURAL:$1|الخاص بها}}  كان مستخدما بالفعل.",
        "undelete-nodiff": "لم يتم العثور على مراجعة سابقة.",
        "undeletebtn": "استرجاع",
        "undeletelink": "اعرض/استعد",
        "undeletedrevisions": "تم استرجاع {{PLURAL:$1||تعديل واحد|تعديلين|$1 تعديلات|$1 تعديلا|$1 تعديل}}",
        "undeletedrevisions-files": "أسترجعت {{PLURAL:$1||مراجعة واحدة|مراجعتان|$1 مراجعات|$1 مراجعة}}  و{{PLURAL:$2||ملف واحد|ملفان|$2 ملفات|$2 ملفًا|$2 ملف}}",
        "undeletedfiles": "أسترجع {{PLURAL:$1||ملف واحد|ملفان|$1 ملفات|$1 ملفًا|$1 ملف}}",
-       "cannotundelete": "فشل الاسترجاع؛\n$1",
-       "undeletedpage": "'''تÙ\85 Ø§Ø³ØªØ±Ø¬Ø§Ø¹ $1'''\n\nراجع [[Special:Log/delete|سجÙ\84 Ø§Ù\84حدف]] لمعاينة عمليات الحذف والاسترجاعات الحديثة.",
+       "cannotundelete": "بعض أو كل عملية الاسترجاع فشلت:\n$1",
+       "undeletedpage": "'''تÙ\85 Ø§Ø³ØªØ±Ø¬Ø§Ø¹ $1'''\n\nراجع [[Special:Log/delete|سجÙ\84 Ø§Ù\84حذف]] لمعاينة عمليات الحذف والاسترجاعات الحديثة.",
        "undelete-header": "انظر الصفحات المحذوفة حديثا في [[Special:Log/delete|سجل الحذف]].",
        "undelete-search-title": "البحث في الصفحات المحذوفة",
        "undelete-search-box": "ابحث في الصفحات المحذوفة",
        "sp-contributions-newbies-sub": "للحسابات الجديدة",
        "sp-contributions-newbies-title": "مساهمات المستخدم للحسابات الجديدة",
        "sp-contributions-blocklog": "سجل المنع",
-       "sp-contributions-suppresslog": "مساهمات المستخدم المحذوفة",
-       "sp-contributions-deleted": "مساهمات المستخدم المحذوفة",
+       "sp-contributions-suppresslog": "مساهمات {{GENDER:$1|المستخدم|المستخدمة}} المخفية",
+       "sp-contributions-deleted": "مساهمات {{GENDER:$1|المستخدم|المستخدمة}} المحذوفة",
        "sp-contributions-uploads": "مرفوعات",
        "sp-contributions-logs": "سجلات",
        "sp-contributions-talk": "نقاش",
        "whatlinkshere-prev": "{{PLURAL:$1|السابق|ال$1 السابقة}}",
        "whatlinkshere-next": "{{PLURAL:$1|التالية|ال$1 التالية}}",
        "whatlinkshere-links": "وصلات",
-       "whatlinkshere-hideredirs": "$1 Ø§Ù\84تحÙ\88Ù\8aÙ\84ات",
-       "whatlinkshere-hidetrans": "$1 Ø§Ù\84تضÙ\85Ù\8aÙ\86ات",
-       "whatlinkshere-hidelinks": "$1 الوصلات",
+       "whatlinkshere-hideredirs": "$1 تحويلات",
+       "whatlinkshere-hidetrans": "$1 تضمينات",
+       "whatlinkshere-hidelinks": "$1 وصلات",
        "whatlinkshere-hideimages": "$1 وصلات الملفات",
        "whatlinkshere-filters": "مرشحات",
        "whatlinkshere-submit": "اذهب",
        "autoblockid": "منع تلقائي #$1",
        "block": "امنع المستخدم",
        "unblock": "إلغاء منع مستخدم",
-       "blockip": "منع المستخدم",
+       "blockip": "منع {{GENDER:$1|المستخدم|المستخدمة}}",
        "blockip-legend": "منع المستخدم",
-       "blockiptext": "استخدم النموذج التالي لمنع مستخدم، أو عنوان آيبي، معين من التعديل أو إنشاء حسابات جديدة. تُستخدم هذه العملية لمنع التخريب فقط، ويجب أن تتماشى مع [[{{MediaWiki:Policy-url}}|سياسة المنع]]. أدخل تعليلاً واضحًا لسبب المنع في الخانة المخصصة لذلك (مثلاً: ذكر صفحات محددة تمّ تخريبها من قبل المستخدم).",
+       "blockiptext": "استخدم النموذج التالي لمنع مستخدم، أو عنوان آيبي، معين من التعديل أو إنشاء حسابات جديدة. تُستخدم هذه العملية لمنع التخريب فقط، ويجب أن تتماشى مع [[{{MediaWiki:Policy-url}}|سياسة المنع]]. أدخل تعليلاً واضحًا لسبب المنع في الخانة المخصصة لذلك (مثلاً: ذكر صفحات محددة تمّ تخريبها من قبل المستخدم).\nيمكنك منع نطاقات عناوين IP باستخدام [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] قواعد; أكبر نطاق مسموح به هو /$1 إلى IPv4 و /$2 إلى IPv6.",
        "ipaddressorusername": "عنوان الأيبي أو اسم المستخدم:",
        "ipbexpiry": "مدة المنع:",
        "ipbreason": "السبب:",
        "ipbreason-dropdown": "*أسباب المنع الشائعة\n** كتابة معلومات زائفة\n** إزالة المحتوى من الصفحات\n** سبام وصلات لمواقع خارجية\n** كتابة كلام لا معنى له في الصفحات\n** سلوك عدواني\n** إساءة استخدام حسابات متعددة\n** اسم مستخدم غير مقبول",
-       "ipb-hardblock": "اÙ\85Ù\86ع Ø§Ù\84Ù\85ستخدÙ\85Ù\8aÙ\86 Ø§Ù\84Ù\88اÙ\84جÙ\8aÙ\86 Ù\85Ù\86 Ø§Ù\84تعدÙ\8aÙ\84 Ø¨Ø¹Ù\86Ù\88اÙ\86 Ø§Ù\84Ø¢يبي هذا",
+       "ipb-hardblock": "اÙ\85Ù\86ع Ø§Ù\84Ù\85ستخدÙ\85Ù\8aÙ\86 Ø§Ù\84Ù\88اÙ\84جÙ\8aÙ\86 Ù\85Ù\86 Ø§Ù\84تعدÙ\8aÙ\84 Ø¨Ø¹Ù\86Ù\88اÙ\86 Ø§Ù\84Ø£يبي هذا",
        "ipbcreateaccount": "امنع إنشاء الحسابات",
        "ipbemailban": "امنع المستخدم من إرسال بريد إلكتروني",
        "ipbenableautoblock": "تلقائيا امنع آخر عنوان أيبي تم استعماله بواسطة هذا المستخدم، وأي عناوين أيبي أخرى يحاول التحرير من خلالها",
        "ipb-unblock": "رفع المنع عن مستخدم أو عنوان أيبي",
        "ipb-blocklist": "عرض حالات المنع الحالية",
        "ipb-blocklist-contribs": "مساهمات $1",
+       "ipb-blocklist-duration-left": "يتبقى $1",
        "unblockip": "رفع المنع عن المستخدم",
        "unblockiptext": "استخدم الاستمارة أدناه لاسترجاع صلاحية الكتابة الخاصة بعنوان أيبي أو مستخدم تم سحبها منه مسبقا.",
        "ipusubmit": "ارفع هذا المنع",
        "lockdbsuccesstext": "لقد أغلقت قاعدة البيانات.<br />\nتذكر أن [[Special:UnlockDB|تزيل الغلق]] بعد اكتمال أعمال الصيانة.",
        "unlockdbsuccesstext": "تم إعادة فتح قاعدة البيانات",
        "lockfilenotwritable": "ملف غلق قاعدة البيانات لا يمكن الكتابة عليه.\nلغلق قاعدة البيانات أو إزالة الغلق يجب أن يكون هذا الملف قابلاً للكتابة من قبل خادوم الويب.",
+       "databaselocked": "قاعدة البيانات مقفلة بالفعل.",
        "databasenotlocked": "قاعدة البيانات ليست مغلقة.",
        "lockedbyandtime": "(من $1 على $2 في $3 )",
        "move-page": "نقل $1",
        "move-page-legend": "نقل صفحة",
-       "movepagetext": "باستخدام  الاستمارة بالأسفل بإمكانك أن تغير اسم الصفحة، وأن تنقل تاريخها إلى الاسم الجديد.\nالعنوان القديم سيصبح تحويلة للعنوان الجديد.\nيمكنك أن تترك التحويلات التي تشير إلى العنوان الأصلي كما هي لتقوم البوتات بتحديثها تلقائياً.\nإذا اخترت أن تقوم بالتحديث يدوياً، فتأكد من عدم وجود تحويلات [[Special:DoubleRedirects|مزدوجة]] أو [[Special:BrokenRedirects|مكسورة]] وقم بتصحيحها.\nأنت المسؤول عن التأكد من أن الوصلات تصل إلى الصفحات التي يفترض أن تصل إليها.\n\nلاحظ أنه '''لن يتم''' نقل الصفحة إذا وجدت صفحة في العنوان الجديد، إلا إذا كانت صفحة تحويل، ولا تاريخ لها.\nهذا يعني أنك تستطيع استرجاع الصفحة إلى مكانها لو قمت بخطأ، وأنك لا يمكنك نسخ هذه الصفحة فوق صفحة موجودة.\n\n'''تحذير!'''\nهذا قد يكون تغييراً كارثياً وغير متوقع لصفحة مشهورة؛\nمن فضلك تأكد أنك تفهم عواقب هذا الفعل قبل أن تستمر.",
-       "movepagetext-noredirectfixer": "باستخدام  الاستمارة بالأسفل بإمكانك أن تغير اسم الصفحة، وأن تنقل تاريخها إلى الاسم الجديد.\nالعنوان القديم سيصبح تحويلة للعنوان الجديد.\nيمكنك تحديث التحويلات التي تشير إلى العنوان الأصلي تلقائياً.\nلو اخترت ألا تفعل، تأكد من عدم وجود تحويلات [[Special:DoubleRedirects|مزدوجة]] أو [[Special:BrokenRedirects|مكسورة]].\nأنت المسؤول عن التأكد من أن الوصلات تصل إلى الصفحات التي يفترض أن تصل إليها.\n\nلاحظ أنه '''لن يتم''' نقل الصفحة إذا كان هناك صفحة بنفس العنوان الجديد، إلا إذا كانت فارغة، أو تحويلة لا تاريخ لها.\nهذا يعني أنك تستطيع استرجاع الصفحة إلى مكانها لو قمت بخطأ، وأنك لا يمكنك الكتابة على صفحة موجودة.\n\n'''تحذير!'''\nهذا قد يكون تغييراً كارثياً وغير متوقع لصفحة مشهورة؛\nمن فضلك تأكد أنك تفهم عواقب هذا الفعل قبل أن تستمر.",
-       "movepagetalktext": "صفحة النقاش المرفقة سيتم نقلها كذلك، '''إلا في حالة''':\n* توجد صفحة نقاش غير فارغة تحت العنوان الجديد، أو\n* قمت بإزالة اختيار الصندوق بالأسفل.\n\nوفي هذه الحالات، يجب عليك نقل أو دمج محتويات الصفحة يدويا، إذا رغب في ذلك.",
+       "movepagetext": "باستخدام  الاستمارة بالأسفل بإمكانك أن تغير اسم الصفحة، وأن تنقل تاريخها إلى الاسم الجديد.\nالعنوان القديم سيصبح تحويلة للعنوان الجديد.\nيمكنك أن تترك التحويلات التي تشير إلى العنوان الأصلي كما هي لتقوم البوتات بتحديثها تلقائياً.\nإذا اخترت أن تقوم بالتحديث يدوياً، فتأكد من عدم وجود تحويلات [[Special:DoubleRedirects|مزدوجة]] أو [[Special:BrokenRedirects|مكسورة]] وقم بتصحيحها.\nأنت المسؤول عن التأكد من أن الوصلات تصل إلى الصفحات التي يفترض أن تصل إليها.\n\nلاحظ أنه <strong>لن يتم</strong> نقل الصفحة إذا وجدت صفحة في العنوان الجديد، إلا إذا كانت صفحة تحويل، ولا تاريخ لها.\nهذا يعني أنك تستطيع استرجاع الصفحة إلى مكانها لو قمت بخطأ، ولا يمكنك نسخ هذه الصفحة فوق صفحة موجودة.\n\n<strong>ملاحظة:</strong>\n\nهذا قد يكون تغييراً كارثياً وغير متوقع لصفحة مشهورة؛\nمن فضلك تأكد أنك تفهم عواقب هذا الفعل قبل أن تستمر.",
+       "movepagetext-noredirectfixer": "باستخدام  الاستمارة بالأسفل بإمكانك أن تغير اسم الصفحة، وأن تنقل تاريخها إلى الاسم الجديد.\nالعنوان القديم سيصبح تحويلة للعنوان الجديد.\nيمكنك تحديث التحويلات التي تشير إلى العنوان الأصلي تلقائياً.\nلو اخترت ألا تفعل، تأكد من عدم وجود تحويلات [[Special:DoubleRedirects|مزدوجة]] أو [[Special:BrokenRedirects|مكسورة]].\nأنت المسؤول عن التأكد من أن الوصلات تصل إلى الصفحات التي يفترض أن تصل إليها.\n\nلاحظ أنه <strong>لن يتم</strong>  نقل الصفحة إذا كان هناك صفحة بنفس العنوان الجديد، إلا إذا كانت فارغة، أو تحويلة لا تاريخ لها.\nهذا يعني أنك تستطيع استرجاع الصفحة إلى مكانها لو قمت بخطأ، وأنك لا يمكنك الكتابة على صفحة موجودة.\n\n<strong>ملاحظة</strong> \n\nهذا قد يكون تغييراً كارثياً وغير متوقع لصفحة مشهورة؛\nمن فضلك تأكد أنك تفهم عواقب هذا الفعل قبل أن تستمر.",
+       "movepagetalktext": "لو علمت على هذا الصندوق، فصفحة النقاش المرفقة يتم نقلها أوتوماتيكيا للعنوان الجديد، إلا لو كانت صفحة نقاش غير فارغة هناك بالفعل.\n\nفي هذه الحالة، فسيتعين عليك نقل أو دمج الصفحة يدويا لو رغبت في ذلك.",
        "moveuserpage-warning": "'''تحذير: أنت على وشك نقل صفحة مستخدم. من فضلك لاحظ أن الصفحة وحدها سوف تنقل وأن المستخدم لن يعاد تسميته.'''",
        "movecategorypage-warning": "<strong>تحذير:</strong> أنت على وشك نقل صفحة التصنيف إلى عنوان جديد؛ <em>لن</em> تنقل الصفحات المندرجة تحت التصنيف إلى العنوان الجديد.",
        "movenologintext": "يجب أن تكون مستخدماً مسجلاً وأن  [[Special:UserLogin|تسجل دخولك]] لكي تنقل صفحة.",
        "movenosubpage": "ليس لهذه الصفحة صفحات فرعية.",
        "movereason": "السبب:",
        "revertmove": "استرجع",
-       "delete_and_move_text": "==الحذف مطلوب==\nالصفحة الهدف \"[[:$1]]\" موجودة بالفعل.\nهل تريد حذفها لإفساح المجال للنقل؟",
+       "delete_and_move_text": "الصفحة الهدف \"[[:$1]]\" موجودة بالفعل.\nهل تريد حذفها لإفساح المجال للنقل؟",
        "delete_and_move_confirm": "نعم، احذف الصفحة",
        "delete_and_move_reason": "حُذِفت لإفساح مجال لنقل \"[[$1]]\"",
        "selfmove": "لا يوجد اختلاف في عنوان المصدر والهدف؛\nلا يمكن نقل الصفحة على نفسها.",
        "move-leave-redirect": "اترك تحويلة خلفك",
        "protectedpagemovewarning": "'''تحذير:''' هذه الصفحة قد تم حمايتها، فقط المستخدمون الذين يمتلكون امتيازات الإدارة يمكنهم نقلها.\nآخر مدخلة سجل موفرة بالأسفل كمرجع:",
        "semiprotectedpagemovewarning": "'''ملاحظة:''' هذه الصفحة تمت حمايتها ليتمكن المستخدمون المسجلون وحدهم من نقلها.\nآخر مدخلة سجل موفرة بالأسفل كمرجع:",
-       "move-over-sharedrepo": "== الملف موجود ==\n[[:$1]] موجود في مستودع مشترك. نقل الملف إلى هذا العنوان سوف يلغي الملف المشترك.",
+       "move-over-sharedrepo": "[[:$1]] موجود في مستودع مشترك. نقل الملف إلى هذا العنوان سوف يلغي الملف المشترك.",
        "file-exists-sharedrepo": "اسم الملف الذي اخترته مستخدم من قبل في مستودع مشترك.\nمن فضلك اختر اسماً آخر.",
        "export": "تصدير صفحات",
        "exporttext": "يمكنك تصدير النص وتاريخ تعديلات صفحة أو مجموعة صفحات في صيغة XML.\nهذا يمكن استيراده إلى ويكي آخر يستعمل ميدياويكي بواسطة [[Special:Import|صفحة الاستيراد]].\n\nلتصدير الصفحات، أدخل عناوينها في الصندوق أسفله، عنواناً واحداً في كل سطر، مع اختيار ما إذا كنت ترغب بتصدير النسخة الحالية مع جميع النسخ القديمة، أي مع كامل معلومات تاريخ الصفحة، أو فقط النسخة الحالية مع معلومات عن التعديل الأخير.\n\nفي الحالة الأخيرة يمكنك أيضاً استخدام وصلة، على سبيل المثال [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] للصفحة «[[{{MediaWiki:Mainpage}}]]».",
        "import-nonewrevisions": "لا مراجعات تم استيرادها (كل المراجعات إما أنها كانت موجودة بالفعل، وأو تم تجاوزها نتيجة أخطاء).",
        "xml-error-string": "$1 عند السطر $2، العمود $3 (بايت $4): $5",
        "import-upload": "رفع بيانات XML",
-       "import-token-mismatch": "فقد لبيانات الجلسة. من فضلك حاول مرة أخرى.",
+       "import-token-mismatch": "فقد لبيانات الجلسة.\n\nأنت لابد من أنه قد تم تسجيل خروجك. <strong>من فضلك تأكد من أنك مازلت مسجل الدخول وحاول مرة أخرى</strong>.\nلو كان مازال لا يعمل، فحاول [[Special:UserLogout|تسجيل الخروج]] وتسجيل الدخول مرة أخرى، وتحقق من أن متصفحك يسمح بالكوكيز من هذا الموقع.",
        "import-invalid-interwiki": "لم يمكن الاستيراد من الويكي المحدد.",
        "import-error-edit": "الصفحة \"$1\" لم يتم استيرادها لأنه لا يمكن لك تحريرها.",
        "import-error-create": "الصفحة \"$1\" لم يتم استيرادها لأنه لا يمكن لك استحداثها أصلا.",
        "tooltip-ca-nstab-category": "رؤية صفحة التصنيف",
        "tooltip-minoredit": "علم على هذا كتعديل طفيف",
        "tooltip-save": "احفظ تغييراتك",
+       "tooltip-publish": "انشر تغييراتك",
        "tooltip-preview": "اعرض تغييراتك، من فضلك استخدم هذا قبل الحفظ!",
        "tooltip-diff": "اعرض التغييرات التي قمت بها للنص.",
        "tooltip-compareselectedversions": "شاهد الفروق بين النسختين المختارتين من هذه الصفحة.",
        "pageinfo-article-id": "معرف الصفحة (ID)",
        "pageinfo-language": "لغة محتوى الصفحة",
        "pageinfo-content-model": "نموذج محتوى الصفحة",
+       "pageinfo-content-model-change": "تغيير",
        "pageinfo-robot-policy": "فهرسة الروبوتات",
        "pageinfo-robot-index": "مسموح بها",
        "pageinfo-robot-noindex": "غير مسموح بها",
        "hours-ago": "منذ {{PLURAL:$1|ساعة|$1 ساعات}}",
        "minutes-ago": "منذ {{PLURAL:$1|دقيقة|$1 دقائق}}",
        "seconds-ago": "منذ {{PLURAL:$1|ثانية|$1 ثوان}}",
-       "monday-at": "Ù\8aÙ\88Ù\85 Ø§Ù\84اثنين الساعة $1",
+       "monday-at": "Ù\8aÙ\88Ù\85 Ø§Ù\84Ø¥ثنين الساعة $1",
        "tuesday-at": "يوم الثلاثاء الساعة $1",
        "wednesday-at": "يوم الأربعاء الساعة $1",
        "thursday-at": "يوم الخميس الساعة $1",
        "exif-artist": "المؤلف",
        "exif-copyright": "مالك الحقوق",
        "exif-exifversion": "نسخة Exif",
-       "exif-flashpixversion": "نسخة فلاش بكس المدعومة",
+       "exif-flashpixversion": "نسخة Flashpix المدعومة",
        "exif-colorspace": "فضاء الألوان",
        "exif-componentsconfiguration": "معنى كل مكونة",
        "exif-compressedbitsperpixel": "طور ضغط الصورة",
        "exif-compression-34712": "جيه بي إي جي2000",
        "exif-copyrighted-true": "محفوظ الحقوق",
        "exif-copyrighted-false": "حالة حقوق النشر غير مُعرّفة",
+       "exif-photometricinterpretation-0": "أسود وأبيض (الأبيض هو 0)",
        "exif-photometricinterpretation-1": "أسود وأبيض (الأسود 0)",
        "exif-photometricinterpretation-2": "آر جي بي",
+       "exif-photometricinterpretation-3": "لوح الألوان",
+       "exif-photometricinterpretation-4": "قناع الشفافية",
+       "exif-photometricinterpretation-5": "مفصول (ربما CMYK)",
        "exif-photometricinterpretation-6": "واي سب سر",
+       "exif-photometricinterpretation-32803": "مصفوفة فلترة الألوان",
+       "exif-photometricinterpretation-34892": "خام خطي",
        "exif-unknowndate": "تاريخ غير معروف",
        "exif-orientation-1": "عادي",
        "exif-orientation-2": "مقلوبة أفقياً",
        "exif-iimcategory-edu": "تعليم",
        "exif-iimcategory-evn": "بيئة",
        "exif-iimcategory-hth": "صحة",
-       "exif-iimcategory-hum": "اهتمام البشرية",
+       "exif-iimcategory-hum": "اهتمام البشرية",
        "exif-iimcategory-lab": "عمل",
        "exif-iimcategory-lif": "أسلوب الحياة وأوقات الفراغ",
        "exif-iimcategory-pol": "سياسة",
        "confirmemail_body_set": "شخص ما -من المحتمل أن يكون أنت- من عنوان الأيبي $1 غيّر عنوان البريد\nالإلكتروني للحساب \"$2\" إلى عنوان البريد الإلكتروني هذا في\n{{SITENAME}}.\n\nلتأكيد أن هذا الحساب لك فعلا ولتنشيط خواص البريد الإلكتروني في\n{{SITENAME}}، افتح هذه الوصلة في متصفحك:\n\n$3\n\nإذا كان هذا الحساب *ليس* لك، اتبع هذه الوصلة لإلغاء تأكيد عنوان البريد\nالإلكتروني:\n\n$5\n\nسينتهي رمز التفعيل هذا في $4.",
        "confirmemail_invalidated": "تأكيد عنوان البريد الإلكتروني تم إلغاؤه",
        "invalidateemail": "إلغاء تأكيد البريد الإلكتروني",
+       "notificationemail_subject_changed": "عنوان البريد الإلكتروني المسجل ل{{SITENAME}} تم تغييره",
+       "notificationemail_subject_removed": "عنوان البريد الإلكتروني المسجل ل{{SITENAME}} تمت إزالته",
+       "notificationemail_body_changed": "شخص ما، غالبا أنت،  من عنوان الأيبي $1،\nقام بتغيير عنوان البريد الإلكتروني للحساب \"$2\" إلى \"$3\" في {{SITENAME}}.\n\nلو أن هذا لم يكن أنت، فاتصل بإداري للموقع حالا.",
+       "notificationemail_body_removed": "شخص ما، غالبا أنت،  من عنوان الأيبي $1،\nقام بإزالة عنوان البريد الإلكتروني للحساب \"$2\" في {{SITENAME}}.\n\nلو أن هذا لم يكن أنت، فاتصل بإداري للموقع حالا.",
        "scarytranscludedisabled": "[التضمين بالإنترويكي معطل]",
        "scarytranscludefailed": "[البحث عن القالب فشل ل$1]",
        "scarytranscludefailed-httpstatus": "[فشل جلب القالب لـ $1: HTTP $2]",
        "scarytranscludetoolong": "[المسار طويل للغاية]",
        "deletedwhileediting": "'''تحذير''': هذه الصفحة تم حذفها بعد أن بدأت أنت بتعديلها!",
-       "confirmrecreate": "حذف المستخدم [[User:$1|$1]] ([[User talk:$1|نقاش]]) هذه الصفحة بعد أن بدأت أنت بتحريرها للسبب التالي:\n:''$2''\nالرجاء التأكد من أنك تريد إعادة إنشاء هذه الصفحة.",
-       "confirmrecreate-noreason": "حذف المستخدم [[User:$1|$1]] ([[User talk:$1|نقاش]]) هذه الصفحة بعد أن بدأت أنت بتحريرها. الرجاء التأكد من أنك تريد إعادة إنشاء هذه الصفحة.",
+       "confirmrecreate": "{{GENDER:$1|حذف المستخدم|حذفت المستخدمة}} [[User:$1|$1]] ([[User talk:$1|نقاش]]) هذه الصفحة بعد أن بدأت أنت بتحريرها للسبب التالي:\n: <em>$2</em>\nالرجاء التأكد من أنك تريد إعادة إنشاء هذه الصفحة.",
+       "confirmrecreate-noreason": "{{GENDER:$1|حذف المستخدم|حذفت المستخدمة}} [[User:$1|$1]] ([[User talk:$1|نقاش]]) هذه الصفحة بعد أن بدأت أنت بتحريرها. الرجاء التأكد من أنك تريد إعادة إنشاء هذه الصفحة.",
        "recreate": "إعادة إنشاء",
        "unit-pixel": "بك",
        "confirm_purge_button": "موافق",
        "confirm-watch-top": "إضافة هذه الصفحة إلى قائمة مراقبتك؟",
        "confirm-unwatch-button": "موافق",
        "confirm-unwatch-top": "إزالة هذه الصفحة من قائمة مراقبتك؟",
+       "confirm-rollback-button": "موافق",
+       "confirm-rollback-top": "استرجاع التعديلات لهذه الصفحة؟",
        "semicolon-separator": "؛&#32;",
        "comma-separator": "،&#32;",
        "quotation-marks": "«$1»",
        "autoredircomment": "تحويل إلى [[$1]]",
        "autosumm-new": "أنشأ الصفحة ب'$1'",
        "autosumm-newblank": "أنشأ صفحة فارغة",
-       "size-bytes": "$1 بايت",
+       "size-bytes": "$1 {{PLURAL:$1|بايت}}",
        "size-kilobytes": "$1 كيلوبايت",
        "size-megabytes": "$1 ميجابايت",
        "size-gigabytes": "$1 جيجابايت",
        "size-exabytes": "$1 إكسابايت",
        "size-zetabytes": "$1 زيتابايت",
        "size-yottabytes": "$1 يوتابايت",
+       "size-pixel": "$1 {{PLURAL:$1|بكسل}}",
        "bitrate-bits": "$1بيت لكل ثانية",
        "bitrate-kilobits": "$1كيلوبيت لكل ثانية",
        "bitrate-megabits": "$1ميجابيت لكل ثانية",
        "timezone-local": "محلي",
        "duplicate-defaultsort": "'''تحذير:''' مفتاح الترتيب الافتراضي \"$2\" يتجاوز مفتاح الترتيب الافتراضي السابق \"$1\".",
        "duplicate-displaytitle": "<strong>تحذير:</strong> أعرض عنوان \"$2\" تجاهل العنوان المعروض سابقا \"$1\".",
+       "restricted-displaytitle": "<strong>تحذير:</strong> عنوان العرض \"$1\" تم تجاهله بما أنه لا يساوي عنوان الصفحة الفعلي.",
        "invalid-indicator-name": "<strong>خطأ:</strong> لا يجوز أن تبقى خاصية <code>name</code> لمؤشرات وضع الصفحة فارغةً.",
        "version": "نسخة",
        "version-extensions": "الامتدادات المثبتة",
        "version-libraries-description": "الوصف",
        "version-libraries-authors": "المؤلفون",
        "redirect": "تحويل حسب الملف أو المستخدم أو الصفحة أو معرف الدخول",
-       "redirect-summary": "هذه الصفحة الخاصة تحوّل إلى ملف (باسمه) أو صفحة (برقم إحدى مراجعاتها) أو إلى صفحة مستخدم (برقمه التعريفي). الاستخدام [[{{#Special:Redirect}}/file/Example.jpg]] أو [[{{#Special:Redirect}}/revision/328429]] أو [[{{#Special:Redirect}}/user/101]].",
-       "redirect-submit": "Ø­Ù\88Ù\91Ù\84",
+       "redirect-summary": "هذه الصفحة الخاصة تحوّل إلى ملف (باسمه) أو صفحة (برقم إحدى مراجعاتها) أو إلى صفحة مستخدم (برقمه التعريفي) أو إلى مدخلة سجل (برقم السجل). الاستخدام [[{{#Special:Redirect}}/file/Example.jpg]] أو [[{{#Special:Redirect}}/revision/328429]] أو [[{{#Special:Redirect}}/user/101]] أو [[{{#Special:Redirect}}/logid/186]].",
+       "redirect-submit": "اذÙ\87ب",
        "redirect-lookup": "ابحث في:",
        "redirect-value": "الوجهة",
        "redirect-user": "رقم مستخدم",
        "tags-create-reason": "السبب:",
        "tags-create-submit": "أنشئ",
        "tags-create-no-name": "عليك أن تحدد اسم الوسم.",
+       "tags-create-invalid-chars": "أسماء الوسوم ينبغي ألا تحتوي على فواصل (<code>,</code>) أو forward slashes (<code>/</code>).",
        "tags-create-invalid-title-chars": "يجب أن لا تحتوي أسماء العلامات الأحرف التي لا يمكن استخدامها في عناوين الصفحات.",
        "tags-create-already-exists": "الوسم \"$1\" موجود بالفعل.",
+       "tags-create-warnings-above": "{{PLURAL:$2|التحذير التالي حدث|التحذيرات التالية حدثت}} عند محاولة إنشاء الوسم \"$1\":",
        "tags-create-warnings-below": "هل تود متابعة إنشاء الوسم؟",
        "tags-delete-title": "احذف الوسم",
        "tags-delete-explanation-initial": "أنت على وشك حذف الوسم \"$1\" من قاعدة البيانات.",
+       "tags-delete-explanation-in-use": "ستتم إزالته من {{PLURAL:$2|$2 مراجعة او مدخلة سجل|كل $2 مراجعات و/أو مدخلات سجل}} الذي هو مطبق عليهم حاليا.",
+       "tags-delete-explanation-warning": "هذا الفعل <strong>غير رجعي</strong> و <strong>لا يمكن التراجع عنه</strong>، ولا حتى بواسطة إداريي قاعدة البيانات. كن متاكدا من أن هذا هو الوسم الذي تريد حذفه.",
+       "tags-delete-explanation-active": "<strong>الوسم \"$1\" مازال نشطا، وسيتم الاستمرار في تطبيقه في المستقبل.</strong> لمنع هذا من الحدوث، اذهب إلى المكان(الأمكنة) حيث الوسم مضبوط ليتم تطبيقه، وعطله هناك.",
        "tags-delete-reason": "سبب:",
-       "tags-delete-submit": "إزاÙ\84Ø© هذا الوسم دون رجعة (ستدمر بعض البيانات)",
+       "tags-delete-submit": "حذÙ\81 هذا الوسم دون رجعة (ستدمر بعض البيانات)",
        "tags-delete-not-allowed": "الوسوم التي يعرفها امتداد لا يمكن حذفها إلا إذا أتاح الامتداد ذلك.",
        "tags-delete-not-found": "الوسم \"$1\" غير موجود.",
+       "tags-delete-too-many-uses": "الوسم \"$1\" مطبق في أكثر من $2 {{PLURAL:$2|مراجعة|مراجعات}}، مما يعني أنه لا يمكن حذفه.",
+       "tags-delete-warnings-after-delete": "الوسم \"$1\" تم حذفه، لكن {{PLURAL:$2|التحذير التالي حدث|التحذيرات التالية حدثت}}:",
+       "tags-delete-no-permission": "أنت لا تمتلك الصلاحية لحذف وسوم التغيير.",
        "tags-activate-title": "نشط الوسم",
        "tags-activate-question": "أنت على وشك تنشيط الوسم \"$1\".",
        "tags-activate-reason": "السبب:",
        "tags-deactivate-submit": "عطل",
        "tags-apply-no-permission": "ليس لديك إذن لتطبيق علامات التغيير جنبا إلى جنب مع التغييرات.",
        "tags-apply-blocked": "لا يمكنك تطبيق علامات التغيير جنبا إلى جنب مع التغييرات في حين منعت.",
-       "tags-apply-not-allowed-one": "السوم \"$1\" غير مسموح أن يتم تطبيقه يدويا.",
+       "tags-apply-not-allowed-one": "الوسم \"$1\" غير مسموح أن يتم تطبيقه يدويا.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|الوسم|الوسوم}} التالية غير مسموح أن يتم تطبيقها يدويا: $1",
        "tags-update-no-permission": "أنت لا تمتلك السماح لإضافة أو إزالة وسوم التغيير من المراجعات أو مدخلات السجل الفردية.",
        "tags-update-blocked": "لا يمكنك إضافة أو إزالة العلامات التغيير بينماهي محظورة.",
        "tags-edit-revision-selected": "{{PLURAL:$1|مراجعة مختارة|مراجعات مختارة}} من [[:$2]]:",
        "tags-edit-logentry-selected": "{{PLURAL:$1|حدث سجل مختار|أحداث سجل مختارة}}:",
        "tags-edit-revision-legend": "أضف أو أزل الوسوم من {{PLURAL:$1|هذه المراجعة|كل $1 المراجعات}}",
+       "tags-edit-logentry-legend": "أضف أو أزل الوسوم من {{PLURAL:$1|مدخلة السجل هذه|كل مدخلات السجل ال$1}}",
        "tags-edit-existing-tags": "الوسوم الموجودة:",
        "tags-edit-existing-tags-none": "</em>لا وسوم</em>",
        "tags-edit-new-tags": "وسوم جديدة:",
        "tags-edit-success": "طبقت التغييرات.",
        "tags-edit-failure": "التغييرات لم تطبق: $1",
        "tags-edit-nooldid-title": "مراجعة هدف غير صالحة",
-       "tags-edit-nooldid-text": "لم تحدد النسخة التي تريد تطبيق العملية عليها أو لا توجد تلك النسخة",
+       "tags-edit-nooldid-text": "لم تحدد النسخة التي تريد تطبيق العملية عليها أو لا يوجد نسخة من الأصل.",
        "tags-edit-none-selected": "من فضلك اختر على الأقل وسما واحدا للإضافة أو الإزالة.",
        "comparepages": "قارن صفحات",
        "compare-page1": "صفحة 1",
        "logentry-block-unblock": "$1 {{GENDER:$2|رفع منع}} {{GENDER:$4|$3}}",
        "logentry-block-reblock": " {{GENDER:$2|غير|غيرت}} $1 إعدادات المنع ل{{GENDER:$4|$3}} بتاريخ انتهاء $5 $6",
        "logentry-suppress-block": "{{GENDER:$2|منع|منعت}} $1 {{GENDER:$4|$3}} لفترة زمنية مدتها $5 $6",
+       "logentry-suppress-reblock": "$1 {{GENDER:$2|غير|غيرت}} إعدادات المنع ل{{GENDER:$4|$3}} بتاريخ انتهاء $5 $6",
        "logentry-import-upload": "$1 {{GENDER:$2|استورد}} $3 بواسطة رفع ملف",
+       "logentry-import-upload-details": "$1 {{GENDER:$2|استورد|استوردت}} $3 بواسطة رفع الملف ($4 {{PLURAL:$4|مراجعة|مراجعات}})",
        "logentry-import-interwiki": "$1 {{GENDER:$2|استورد|استوردت}} $3 من ويكي أخرى",
+       "logentry-import-interwiki-details": "$1 {{GENDER:$2|استورد|استوردت}} $3 من $5 ($4 {{PLURAL:$4|مراجعة|مراجعات}})",
        "logentry-merge-merge": "{{GENDER:$2|دمج|دمجت}} $1 $3 إلى $4 (المراجعات حتى $5).",
        "logentry-move-move": "{{GENDER:$2|نقل|نقلت}} $1 صفحة $3 إلى $4",
        "logentry-move-move-noredirect": "{{GENDER:$2|نقل|نقلت}} $1 صفحة $3 إلى $4 دون ترك تحويلة",
        "logentry-protect-unprotect": "{{GENDER:$2|رفع|رفعت}} $1 الحماية عن $3",
        "logentry-protect-protect": "$1 {{GENDER:$2|حمى|حمت}} $3 $4",
        "logentry-protect-protect-cascade": "$1 {{GENDER:$2|حمى|حمت}} $3 $4 [مضمنة]",
-       "logentry-protect-modify": "{{GENDER:$2|غير|غيرت}} $1 مستوى الحماية ل$3 $4",
-       "logentry-rights-rights": "{{GENDER:$2|غيّر|غيّرت}} $1 عضوية $3 من $4 إلى $5",
+       "logentry-protect-modify": "{{GENDER:$2|غير|غيرت}} $1 مستوى حماية $3 $4",
+       "logentry-protect-modify-cascade": "$1 {{GENDER:$2|غير|غيرت}} مستوى الحماية ل$3 $4 [مضمن]",
+       "logentry-rights-rights": "{{GENDER:$2|غيّر|غيّرت}} $1 عضوية {{GENDER:$6|$3}} من $4 إلى $5",
        "logentry-rights-rights-legacy": "{{GENDER:$2|غيّر|غيّرت}} $1 عضوية $3",
        "logentry-rights-autopromote": "تمت تلقائيا ترقية {{GENDER:$2|المستخدم|المستخدمة}} $1 من  $4 إلى $5",
        "logentry-upload-upload": " {{GENDER:$2|رفع|رفعت}} $1 $3",
        "logentry-upload-overwrite": "{{GENDER:$2|رفع|رفعت}} $1 نسخة جديدة من  $3",
        "logentry-upload-revert": "{{GENDER:$2|رفع|رفعت}} $1 $3",
        "log-name-managetags": "سجل إدارة الوسوم",
+       "log-description-managetags": "هذه الصفحة تعرض مهام الإدارة المرتعلقة ب[[Special:Tags|الوسوم]]. السجل يحتوي فقط على الافعال التي تم عملها يدويا بواسطة إداري؛ الوسوم ربما يتم إنشاؤها او حذفها بواسطة برنامج الويكي بدون تسجيل مدخلة في هذا السجل.",
        "logentry-managetags-create": "$1 {{GENDER:$2|أنشأ|أنشأت}} الوسم \"$4\"",
+       "logentry-managetags-delete": "$1 {{GENDER:$2|حذف|حذفت}} الوسم \"$4\" (أزيل من $5 {{PLURAL:$5|مراجعة أو مدخلة سجل|مراجعات و/أو مدخلات سجل}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|فعل|فعلت}} الوسم \"$4\" للاستخدام بواسطة البوتات والمستخدمين",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|ألغى تفعيل|ألغت تفعيل}} الوسم \"$4\" للاستخدام بواسطة البوتات والمستخدمين",
        "log-name-tag": "سجل الوسوم",
+       "log-description-tag": "هذه الصفحة تعرض متى قام المستخدمون بإضافة أو إزالة [[Special:Tags|الوسوم]] من المراجعات أو مدخلات السجل الفردية. السجل لا يعرض أفعال الوسم عندما يحدثوا كجزء من تعديل، حذف، أو فعل مماثل.",
+       "logentry-tag-update-add-revision": "$1 {{GENDER:$2|أضاف|أضافت}} {{PLURAL:$7|الوسم|الوسوم}} $6 للمراجعة $4 للصفحة $3",
+       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|أضاف|أضافت}} {{PLURAL:$7|الوسم|الوسوم}} $6 لمدخلة السجل $5 للصفحة $3",
+       "logentry-tag-update-remove-revision": "$1 {{GENDER:$2|أزال|أزالت}} {{PLURAL:$9|الوسم|الوسوم}} $8 من المراجعة $4 للصفحة $3",
+       "logentry-tag-update-remove-logentry": "$1 {{GENDER:$2|أزال|أزالت}} {{PLURAL:$9|الوسم|الوسوم}} $8 من مدخلة السجل $5 للصفحة $3",
+       "logentry-tag-update-revision": "$1 {{GENDER:$2|حدث|حدثت}} الوسوم للمراجعة $4 للصفحة $3 ({{PLURAL:$7|أضاف}} $6; {{PLURAL:$9|أزال}} $8)",
+       "logentry-tag-update-logentry": "$1 {{GENDER:$2|حدث|حدثت}} الوسوم على مدخلة السجل $5 للصفحة $3 ({{PLURAL:$7|أضاف}} $6; {{PLURAL:$9|أزال}} $8)",
        "rightsnone": "(لا شيء)",
        "revdelete-summary": "ملخص التعديل",
        "feedback-adding": "إضافة تعليقات إلى الصفحة...",
        "feedback-close": "تم",
        "feedback-external-bug-report-button": "أرسل تقرير علة تقنية",
        "feedback-dialog-title": "أرسل تغذية راجعة",
+       "feedback-dialog-intro": "أنت يمكنك استخدام الاستمارة السهلة بالأسفل لإرسال تعليقك. تعليقك ستتم إضافته للصفحة \"$1\"، مع اسم المستخدم الخاص بك.",
        "feedback-error-title": "خطأ",
        "feedback-error1": "خطأ: لا يمكن التعرف عليها من API",
        "feedback-error2": "خطأ: فشل في تحرير",
        "feedback-message": "الرسالة:",
        "feedback-subject": "الموضوع:",
        "feedback-submit": "إرسال",
+       "feedback-terms": "أنا أفهم أن معلومات وكيل المستخدم الخاص بي تشمل معلومات حول نسخة متصفحي ونظام التشغيل الخاص بي بالضبط وستتم مشاركتها علنيا بجانب تعليقي.",
+       "feedback-termsofuse": "أنا أوافق على توفير تعليقات بالتوافق مع شروط الاستخدام.",
        "feedback-thanks": "شكرا! أُرسلت ملاحظاتك لصفحة \"[$2 $1]\".",
        "feedback-thanks-title": "شكرا لك!",
        "feedback-useragent": "وكيل المستخدم:",
        "searchsuggest-search": "بحث",
        "searchsuggest-containing": "يحتوي...",
+       "api-error-autoblocked": "عنوان الأيبي الخاص بك تم منعه تلقائيا، لأنه تم استخدامه بواسطة مستخدم ممنوع",
        "api-error-badaccess-groups": "لا يسمح لك بتحميل الملفات إلى هذه الويكي.",
        "api-error-badtoken": "خطأ داخلي: رمز مميز غير صحيح.",
        "api-error-blocked": "لقد منعت من التحرير.",
        "api-error-copyuploaddisabled": "تم تعطيل تحميل من رابط على هذا الخادم.",
-       "api-error-duplicate": "Ù\87Ù\86اÙ\83 {{PLURAL:$1|Ù\87Ù\88 Ù\85Ù\84Ù\81 Ø¢Ø®Ø±|Ù\83Ø°Ù\84Ù\83$2 Ø¨Ø¹Ø¶ Ø§Ù\84Ù\85Ù\84Ù\81ات Ø§Ù\84أخرÙ\89}} Ù\85سبÙ\82اÙ\8b Ø¹Ù\84Ù\89 Ø§Ù\84Ù\85Ù\88Ù\82ع Ø¨Ù\86Ù\81س Ø§Ù\84Ù\85ضÙ\85Ù\88Ù\86.",
+       "api-error-duplicate": "Ù\87Ù\86اÙ\83 {{PLURAL:$1|Ù\85Ù\84Ù\81 Ø¢Ø®Ø±|بعض Ø§Ù\84Ù\85Ù\84Ù\81ات Ø§Ù\84أخرÙ\89}} Ù\85سبÙ\82ا Ø¹Ù\84Ù\89 Ø§Ù\84Ù\85Ù\88Ù\82ع Ø¨Ù\86Ù\81س Ø§Ù\84Ù\85حتÙ\88Ù\89.",
        "api-error-duplicate-archive": "هناك {{PLURAL:$1|كان ملف آخر |كذلك بعض الملفات الأخرى}} مسبقاً على الموقع بنفس المضمون، ولكن {{PLURAL:$1|أنه تم | إجراء}} الحذف لها.",
        "api-error-empty-file": "كان ملف الذي قمت بإرسال فارغة.",
        "api-error-emptypage": "إنشاء صفحات فارغة جديدة، غير مسموح به.",
        "api-error-filetype-banned-type": "$1 {{PLURAL:$4|ليس نوع ملف مسموح به|ليست أنواع ملفات مسموح بها}}. {{PLURAL:$3|نوع الملف المسموح به هو|أنواع الملفات المسموح بها هي}} $2.",
        "api-error-filetype-missing": "يفتقد الملفّ ملحق نوعيّته.",
        "api-error-hookaborted": "التعديل الذي تحاول أن تقوم به تم إحباطه",
-       "api-error-http": "خطأ Ø¯Ø§Ø®Ù\84Ù\8a: ØªØ¹Ø°Ø± Ø§Ù\84اتصاÙ\84 Ø¨Ø§Ù\84خادÙ\88Ù\85.",
+       "api-error-http": "خطأ داخلي: تعذر الاتصال بالخادم.",
        "api-error-illegal-filename": "اسم الملف غير مسموح به.",
        "api-error-internal-error": "خطأ داخلي: حدث خطأ عند معالجة التحميل الخاص بك على الويكي.",
        "api-error-invalid-file-key": "خطأ داخلي: لم يتم العثور على الملف في التخزين المؤقت.",
        "api-error-missingparam": "خطأ داخلي : متغيرات مفقودة ضمن الطلب.",
        "api-error-missingresult": "خطأ داخلي : لا يمكن التحديد ما إذا كان النسخ ناجحاً.",
        "api-error-mustbeloggedin": "يجب أن تكون مسجلا في لتحميل الملفات.",
-       "api-error-mustbeposted": "خطأ داخلي: يتطلب طلب HTTP POST.",
+       "api-error-mustbeposted": "خطأ داخلي: الطلب يتطلب HTTP POST.",
        "api-error-noimageinfo": "نجح التحميل، ولكن الخادم لم يقدم لنا أي معلومات حول الملف.",
        "api-error-nomodule": "خطأ داخلي: لم يتم تعيين تحميل الوحدة النمطية.",
        "api-error-ok-but-empty": "خطأ داخلي : لم يكن هناك استجابة من الملقم.",
        "api-error-overwrite": "لا يسمح بالكتابة فوق ملف موجود.",
+       "api-error-ratelimited": "أنت تحاول رفع الكثير من الملفات في فترة زمنية قصيرة أقصر مما تسمح به هذه الويكي.\nمن فضلك حاول مرة ثانية خلال عدة دقائق.",
        "api-error-stashfailed": "خطأ داخلي: فشل الملقم في تخزين الملفات المؤقتة.",
        "api-error-publishfailed": "خطأ داخلي: لم ينجح الخادوم في نشر ملف مؤقت",
        "api-error-stasherror": "حدث خطأ أثناء رفع الملف لتخزينه.",
        "api-error-stashnosuchfilekey": "الملف الذي كنت تحاول الوصول اليه في مخبوائتك غير موجود.",
        "api-error-timeout": "لم يستجب الملقم في الوقت المتوقع.",
        "api-error-unclassified": "حدث خطأ غير معروف",
-       "api-error-unknown-code": "خطأ غير معروف : \" $1 \"",
+       "api-error-unknown-code": "خطأ غير معروف: \"$1\"",
        "api-error-unknown-error": "خطأ داخلي: قد حدث خطأ عند محاولة تحميل الملف الخاص بك.",
-       "api-error-unknown-warning": "تحذير غير معروف:$1",
-       "api-error-unknownerror": "خطأ غير معروف : \" $1 \"",
-       "api-error-uploaddisabled": "تم تعطيل تحميل على هذا الويكي.",
-       "api-error-verification-error": "هذا الملف قد يكون معطوباً أو يحتوي على ملحق غير صحيح.",
+       "api-error-unknown-warning": "تحذير غير معروف: \"$1\"",
+       "api-error-unknownerror": "خطأ غير معروف: \"$1\"",
+       "api-error-uploaddisabled": "تم تعطيل الرفع على هذه الويكي.",
+       "api-error-verification-error": "هذا الملف قد يكون معطوباً أو يحتوي على امتداد غير صحيح.",
+       "api-error-was-deleted": "تم رفع ملف بهذا الاسم سابقا ثم تم حذفه بعد هذا.",
        "duration-seconds": "{{PLURAL:$1|أقل من ثانية|ثانية واحدة|ثانيتان|$1 ثوانٍ|$1 ثانية}}",
        "duration-minutes": "{{PLURAL:$1|أقل من دقيقة|دقيقة واحدة|دقيقتان|$1 دقائق|$1 دقيقة}}",
        "duration-hours": "({{PLURAL:$1||ساعة واحدة|ساعتان|$1 ساعات|$1 ساعة}})",
        "expand_templates_generate_rawhtml": "أظهر خام HTML",
        "expand_templates_preview": "عرض مسبق",
        "expand_templates_preview_fail_html": "<em>عذرا! لم نستطع معالجة تعديلك بسبب فقدان بيانات الجلسة.\n\nلأن {{SITENAME}} بها HTML الخام مفعلة، العرض المسبق مخفي كاحتياط ضد هجمات الجافا سكريبت.</em>\n\n<strong>إذا كانت هذه محاولة تعديل صادقة، من فضلك حاول مرة أخرى.</strong>\nإذا كانت مازالت لا تعمل، حاول [[Special:UserLogout|تسجيل الخروج]] ثم تسجيل الدخول مجددا.و تاكد في  متصفحك من الكوكيز  الخاصة  بهذا الموقع.",
+       "expand_templates_preview_fail_html_anon": "<em>لأن {{SITENAME}} لديه الHTML الخام مفعل وأنت غير مسجل الدخول، فعملية المعاينة مخفية كاحتياط ضد عمليات هجوم الجافاسكريبت.</em>\n\n<strong>لو أن هذه عملية معاينة صحيحة، فمن فضلك  [[Special:UserLogin|سجل الدخول]] وحاول مرة أخرى.</strong>",
        "expand_templates_input_missing": "يجب تقديم بعض المدخلات النصية على الأقل.",
        "pagelanguage": "تغيير لغة الصفحة",
        "pagelang-name": "صفحة",
        "log-name-pagelang": "سجل تغيير اللغة",
        "log-description-pagelang": "هذا سجل تغيرات في صفحة اللغات.",
        "logentry-pagelang-pagelang": " {{GENDER:$2|غيَّر|غيَّرت}} $1 لغة الصفحة «$3» من $4 إلى $5.",
+       "default-skin-not-found": "أخ! الواجهة الافتراضية للويكي الخاصة بك، والمعرفة في <code dir=\"ltr\">$wgDefaultSkin</code> ك<code>$1</code>، غير متوفرة.\n\nعملية تنصيبك يبدو أنها تحتوي على {{PLURAL:$4|الواجهة|الواجهات}} التالية. انظر [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] للمعلومات حول كيف تفعل  {{PLURAL:$4|ها|هم وتختار الافتراضي}}.\n\n$2\n\n; لو أنك قمت بتنصيب ميدياويكي حالا:\n: أنت ربما قمت بالتنصيب من git, أو مباشرة من الكود المصدري باستخدام طريقة أخرى. هذا متوقع. حاول تنصيب بعض الواجهات من [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory]، عن طريق تحميل:\n:* تحميل [https://www.mediawiki.org/wiki/Download tarball installer]، والذي يأتي مع واجهات وامتدادات عديدة. أنت يمكنك نسخ مجلد ال<code>skins/</code> مباشرة منه.\n:* تحميل ال  واجهات الtarballs الفردية من [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins استخدام Git لتحميل الواجهات].\n: فعل هذا ينبغي ألا يتعارض مع مستودع git الخاص بك لو أنك مطور ميدياويكي.\n\n; لو أنك قمت بترقية ميدياويكي حالا:\n: MediaWiki 1.24 وأحدث لم يعد يقوم بتفعيل الواجهات المنصبة تلقائيا (انظر [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). أنت يمكنك نسخ {{PLURAL:$5|السطر التالي|السطور التالية}} إلى <code>LocalSettings.php</code> لتفعيل {{PLURAL:$5||كل}}  {{PLURAL:$5|الواجهة|الواجهات}} المنصبة:\n\n<pre dir=\"ltr\">$3</pre>\n\n; لو أنك قمت بتعديل <code>LocalSettings.php</code> حالا:\n: فتحقق مجددا من أسماء الواجهات للأخطاء الإملائية.",
+       "default-skin-not-found-no-skins": "أخ! الواجهة الافتراضية للويكي الخاصة بك، والمعرفة في <code>$wgDefaultSkin</code> ك<code>$1</code>، غير متوفرة.\n\nأنت ليس لديك أي واجهات منصبة.\n\n; لو أنك قد قمت بتنصيب أو ترقية ميدياويكي حالا:\n: أنت على الأرجح قد قمت بالتنصيب من git، أو مباشرة من الكود المصدري باستخدام طريقة أخرى. هذا متوقع. MediaWiki 1.24 وأحدث لا يحتوي على أي واجهات في المستودع الرئيسي. حاول تنصيب بعض الواجهات من  [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory]، عن طريق تحميل [https://www.mediawiki.org/wiki/Download tarball installer], والذي يأتي مع واجهات وامتداات عديدة. أنت يمكنك نسخ مجلد ال<code>skins/</code> منه.\n:* تحميل tarballs الواجهات الفردية من [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins استخدام Git لتحميل الواجهات].\n: فعل هذا ينبغي ألا يتداخل مع مستودع git الخاص بك لو أنك مطور ميدياويكي. انظر [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] للمعلومات حول كيفية تفعيل الواجهات واختيار الافتراضي.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (مفعل)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>ملغاة</strong>)",
        "mediastatistics": "إحصاءات الميديا",
        "mw-widgets-dateinput-no-date": "لا تاريخ تم اختياره",
        "mw-widgets-titleinput-description-new-page": "الصفحة غير موجودة بعد",
        "mw-widgets-titleinput-description-redirect": "تحويل إلى $1",
-       "api-error-blacklisted": "اختر عنوانا مختلفا ومفهوما.",
        "sessionmanager-tie": "لا يمكن جمع أنواع استيثاق متعددة: $1.",
        "sessionprovider-generic": "جلسات $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "جلسات قائمة على ملفات تعريف الارتباط (كوكيز)",
        "sessionprovider-nocookies": "قد يتم تعطيل الكوكيز. تأكد من تمكين ملفات تعريف الأرتباط وأبد مرةأخرى.",
        "randomrootpage": "صفحة جذر عشوائية",
        "log-action-filter-block": "نوع المنع:",
+       "log-action-filter-contentmodel": "نوع تغيير موديل المحتوى:",
        "log-action-filter-delete": "نوع الحذف:",
        "log-action-filter-import": "نوع الاستيراد:",
        "log-action-filter-managetags": "نوع فعل إدارة الوسوم:",
        "log-action-filter-move": "نوع النقل:",
        "log-action-filter-newusers": "نوع إنشاء الحساب:",
+       "log-action-filter-patrol": "نوع الخفر:",
        "log-action-filter-protect": "نوع الحماية:",
        "log-action-filter-rights": "نوع تغيير الصلاحية",
+       "log-action-filter-suppress": "نوع الإخفاء:",
        "log-action-filter-upload": "نوع الرفع:",
        "log-action-filter-all": "الكل",
        "log-action-filter-block-block": "منع",
        "log-action-filter-block-reblock": "منع التعديل",
        "log-action-filter-block-unblock": "رفع المنع",
+       "log-action-filter-contentmodel-change": "تغيير موديل المحتوى",
+       "log-action-filter-contentmodel-new": "إنشاء صفحة بموديل محتوى غير قياسي",
        "log-action-filter-delete-delete": "حذف الصفحات",
+       "log-action-filter-delete-restore": "استرجاع الصفحات",
        "log-action-filter-delete-event": "حذف السجلات",
        "log-action-filter-delete-revision": "حذف المراجعات",
        "log-action-filter-import-interwiki": "استيراد عابر للويكي",
+       "log-action-filter-import-upload": "استيراد بواسطة رفع XML",
        "log-action-filter-managetags-create": "إنشاء الوسوم",
        "log-action-filter-managetags-delete": "حذف الوسوم",
+       "log-action-filter-managetags-activate": "تفعيل الوسم",
+       "log-action-filter-managetags-deactivate": "تعطيل الوسم",
+       "log-action-filter-move-move": "نقل بدون الكتابة على التحويلات",
+       "log-action-filter-move-move_redir": "نقل مع الكتابة على التحويلات",
+       "log-action-filter-newusers-create": "إنشاء بواسطة مستخدم مجهول",
+       "log-action-filter-newusers-create2": "إنشاء بواسطة مستخدم مسجل",
        "log-action-filter-newusers-autocreate": "إنشاء آلي",
        "log-action-filter-newusers-byemail": "الإنشاء بكلمة مرور مرسلة عبر البريد الإلكتروني",
+       "log-action-filter-patrol-patrol": "خفر يدوي",
+       "log-action-filter-patrol-autopatrol": "خفر أوتوماتيكي",
        "log-action-filter-protect-protect": "حماية",
        "log-action-filter-protect-modify": "تعديل الحماية",
        "log-action-filter-protect-unprotect": "رفع الحماية",
+       "log-action-filter-protect-move_prot": "نقل الحماية",
        "log-action-filter-rights-rights": "تغيير يدوي",
+       "log-action-filter-rights-autopromote": "تغيير أوتوماتيكي",
+       "log-action-filter-suppress-event": "إخفاء السجل",
+       "log-action-filter-suppress-revision": "إخفاء المراجعة",
+       "log-action-filter-suppress-delete": "إخفاء الصفحة",
+       "log-action-filter-suppress-block": "إخفاء المستخدم بواسطة المنع",
+       "log-action-filter-suppress-reblock": "إخفاء المستخدم بواسطة إعادة المنع",
        "log-action-filter-upload-upload": "رفع جديد",
        "log-action-filter-upload-overwrite": "إعادة الرفع",
+       "authmanager-authn-not-in-progress": "عملية التحقق ليست جارية أو بينات الجلسة تم فقدها. من فضلك ابدأ مرة ثانية من البداية.",
+       "authmanager-authn-no-primary": "الاعتماد الموفر لم يمكن التحقق منه.",
+       "authmanager-authn-no-local-user": "الاعتماد الموفر غير مرتبط بأي مستخدم على هذه الويكي.",
+       "authmanager-authn-no-local-user-link": "الاعتمادات الموفرة صحيحة لكن غير مرتبطة بأي مستخدم على هذه الويكي. سجل الدخول باستخدام طريقة أخرى، أو أنشيء مستخدما جديدا، وستمتلك الاختيار لوصل اعتماداتك السابقة لذلك الحساب.",
+       "authmanager-authn-autocreate-failed": "الإنشاء التلقائي لحساب محلي فشل: $1",
+       "authmanager-change-not-supported": "الاعتمادات الموفرة لم يمكن تغييرها، حيث أن لا شيء سيستخدمها.",
        "authmanager-create-disabled": "إنشاء الحسابات معطل.",
        "authmanager-create-from-login": "لإنشاء حساب، برجاء ملء الحقول أدناه.",
+       "authmanager-create-not-in-progress": "إنشاء الحساب ليس جاريا أو بيانات الجلسة تم فقدها. من فضلك ابدأ ثانية من البداية.",
+       "authmanager-create-no-primary": "الاعتمادات الموفرة لم يمكن استخدامها لإنشاء الحساب.",
+       "authmanager-link-no-primary": "الاعتماد الموفر لم يمكن استخدامه لوصل الحسابات.",
+       "authmanager-link-not-in-progress": "وصل الحساب ليس جاريا أو بيانات الجلسة تم فقدها. من فضلك ابدأ ثانية منذ البداية.",
+       "authmanager-authplugin-setpass-failed-title": "تغيير كلمة السر فشل",
+       "authmanager-authplugin-setpass-failed-message": "إضافة التحقق رفضت تغيير كلمة السر.",
+       "authmanager-authplugin-create-fail": "إضافة التحقق رفضت إنشاء الحساب.",
+       "authmanager-authplugin-setpass-denied": "إضافة التحقق لا تسمح بتغيير كلمات السر.",
+       "authmanager-authplugin-setpass-bad-domain": "نطاق غير صحيح.",
+       "authmanager-autocreate-noperm": "إنشاء الحساب التلقائي غير مسموح به.",
+       "authmanager-autocreate-exception": "إنشاء الحسابات التلقائي تم تعطيله مؤقتا نظرا للأخطاء السابقة.",
+       "authmanager-userdoesnotexist": "حساب المستخدم \"$1\" غير مسجل.",
+       "authmanager-userlogin-remembermypassword-help": "ما إذا كانت كلمة السر ينبغي أن يتم تذكرها لأطول من مدة الجلسة.",
+       "authmanager-username-help": "اسم المستخدم للتوثيق.",
+       "authmanager-password-help": "كلمة السر للتوثيق",
+       "authmanager-domain-help": "النطاق للتوثيق الخارجي.",
+       "authmanager-retype-help": "كلمة المرور مرة أخرى للتأكيد.",
        "authmanager-email-label": "البريد الإلكتروني",
        "authmanager-email-help": "عنوان البريد الإلكتروني",
        "authmanager-realname-label": "الاسم الحقيقي",
        "authmanager-provider-password": "توثيق مبني على كلمة المرور",
        "authmanager-provider-password-domain": "توثيق مبني على كلمة المرور والنطاق",
        "authmanager-provider-temporarypassword": "كلمة مرور مؤقتة",
+       "authprovider-confirmlink-message": "بناء على محاولات تسجيل الدخول الحديثة الخاصة بك، فالحسابات التالية يمكن وصلها بحساب الويكي الخاص بك. وصلهم يفعل تسجيل الدخول عبر هذه الحسابات. من فضلك اختر أيهم ينبغي أن يتم وصلها.",
+       "authprovider-confirmlink-request-label": "الحسابات التي ينبغي أن يتم وصلها",
+       "authprovider-confirmlink-success-line": "$1: تم الوصل بشكل صحيح.",
+       "authprovider-confirmlink-failed": "وصل الحساب لم ينجح بشكل كامل: $1",
+       "authprovider-confirmlink-ok-help": "الاستمرار بعد عرض رسائل فشل الوصل.",
        "authprovider-resetpass-skip-label": "تخطى",
        "authprovider-resetpass-skip-help": "تخطي إعادة تعيين كلمة المرور",
+       "authform-nosession-login": "عملية التحقق كانت ناجحة، لكن متصفحك لا يمكنه \"تذكر\" أنك مسجل الدخول.\n\n$1",
+       "authform-nosession-signup": "الحساب تم إنشاؤه، لكن متصفحك لا يمكنه cannot \"تذكر\" أنك مسجل الدخول.\n\n$1",
+       "authform-newtoken": "توكين مفقود. $1",
+       "authform-notoken": "توكين مفقود",
+       "authform-wrongtoken": "توكين خاطئ",
+       "specialpage-securitylevel-not-allowed-title": "غير مسموح به",
+       "specialpage-securitylevel-not-allowed": "عذرا، أنت غير مسموح لك باستخدام هذه الصفة لأن هويتك لا يمكن التحقق منها.",
+       "authpage-cannot-login": "غير قادر على بدء عملية تسجيل الدخول.",
+       "authpage-cannot-login-continue": "غير قادر على الاستمرار في تسجيل الدخول. جلستك على الأرجح انتهت صلاحيتها.",
+       "authpage-cannot-create": "غير قادر على بدء عملية إنشاء الحساب.",
+       "authpage-cannot-create-continue": "غير قادر على الاستمرار في إنشاء الحساب. جلستك على الأرجح انتهت صلاحيتها.",
+       "authpage-cannot-link": "غير قادر على بدء عملية وصل الحسابات.",
+       "authpage-cannot-link-continue": "غير قادر على الاستمرار في وصل الحساب. جلستك على الأرجح انتهت صلاحيتها.",
        "cannotauth-not-allowed-title": "الإذن مرفوض",
+       "cannotauth-not-allowed": "أنت غير مسموح لك باستخدام هذه الصفحة",
+       "changecredentials": "تغيير الاعتماد",
+       "changecredentials-submit": "تغيير الاعتماد",
+       "changecredentials-invalidsubpage": "$1 ليس نوع اعتماد صحيح.",
+       "changecredentials-success": "اعتماداتك تم تغييرها.",
+       "removecredentials": "إزالة الاعتماد",
+       "removecredentials-submit": "إزالة الاعتماد",
+       "removecredentials-invalidsubpage": "$1 ليس نوع اعتمادات صحيح.",
+       "removecredentials-success": "اعتماداتك تمت إزالتها.",
+       "credentialsform-provider": "نوع الاعتمادات:",
        "credentialsform-account": "اسم الحساب:",
        "cannotlink-no-provider-title": "لا توجد حسابات قابلة للربط",
        "cannotlink-no-provider": "لا توجد حسابات قابلة للربط",
        "linkaccounts": "ربط الحسابات",
+       "linkaccounts-success-text": "الحساب تم وصله.",
        "linkaccounts-submit": "اربط الحسابات",
-       "unlinkaccounts": "إزالة ربط الحسابات"
+       "unlinkaccounts": "إزالة ربط الحسابات",
+       "unlinkaccounts-success": "الحساب تم فك وصله.",
+       "authenticationdatachange-ignored": "تغيير بيانات التحقق لم يتم التعامل معه. ربما لم يتم ضبط موفر؟",
+       "userjsispublic": "من فضلك لاحظ: صفحات الجافاسكريبت الفرعية لا ينبغي أن تحتوي غلى بيانات سرية بما أنها يمكن رؤيتها بواسطة المستخدمين الآخرين.",
+       "usercssispublic": "من فضل لاحظ: صفحات الCSS الفرعية لا ينبغي أن تحتوي على بيانات سرية بما أنها يمكن رؤيتها بواسطة المستخدمين الآخرين."
 }
index 38bba71..0e51642 100644 (file)
        "undo-failure": "الرجوع فى التعديل ما نفعش علشان فى تعديلات متعاكسة حصلت فى الصفحة.",
        "undo-norev": "الرجوع فى التعديل ما نفعش علشان هو يا إما مش موجود أو انه إتمسح.",
        "undo-summary": "الرجوع فى التعديل $1 بتاع [[Special:Contributions/$2|$2]] ([[User talk:$2|نقاش]])",
-       "cantcreateaccounttitle": "مش ممكن فتح حساب",
        "cantcreateaccount-text": "فتح الحسابات من عنوان الأيبى دا ('''$1''') منعه [[User:$3|$3]].\n\nالسبب إللى إداه $3 هو ''$2''",
        "viewpagelogs": "عرض السجلات للصفحه دى",
        "nohistory": "الصفحة دى ما لهاش تاريخ تعديل.",
        "recentchanges-label-unpatrolled": "التعديل ده مإتراجعش لسه",
        "recentchanges-legend-heading": "<strong>شرح</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (بص كمان على [[Special:NewPages|قايمه الصفحات الجديده]])",
-       "rcnotefrom": "دى التعديلات من '''$2''' (ل '''$1''' معروضه).",
+       "rcnotefrom": "{{PLURAL:$5|ده التعديل|دى التعديلات}} من اول <strong>$3, $4</strong> (لغايه<strong>$1</strong> معروضه).",
        "rclistfrom": "اظهر التعديلات بدايه من $3 $2",
        "rcshowhideminor": "$1 تعديلات صغيره",
        "rcshowhideminor-show": "اعرض",
        "upload_directory_read_only": "مجلد التحميل ($1) مش ممكن الكتابة عليه بواسطة سيرڨر الويب.",
        "uploaderror": "غلطه فى التحميل",
        "uploadtext": "استخدم الاستمارة علشان تحميل الملفات.\nلعرض أو البحث ف الملفات المتحملة سابقا، راجع عمليات المسح [[Special:Log/delete|deletion log]] [[Special:FileList|لستة الملفات المتحملة]]، عمليات التحميل  موجودة فى [[Special:Log/upload|سجل التحميل]].\n\nعلشان تحط صورة فى صفحة، استخدم الوصلات فى الصيغ التالية:\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></code>''' علشان استخدام النسخة الكاملة لملف\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|نص بديل]]</nowiki></code>''' لاستخدام صورة عرضها 200 بكسل فى صندوق فى الجانب الأيسر مع 'نص بديل' كوصف\n* '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code>''' للوصل للملف مباشرة بدون عرض الملف.",
-       "upload-permitted": "أنواع الملفات المسموحة: $1.",
-       "upload-preferred": "أنواع الملفات المفضلة: $1.",
-       "upload-prohibited": "أنواع الملفات الممنوعة: $1.",
+       "upload-permitted": "{{PLURAL:$2|نوع|انواع}} الملفات اللى مسموح بيها: $1.",
+       "upload-preferred": "{{PLURAL:$2|نوع|انواع}} الملفات المفضله: $1.",
+       "upload-prohibited": "{{PLURAL:$2|نوع|انواع}} الملفات الممنوعه: $1.",
        "uploadlogpage": "سجل التحميل",
        "uploadlogpagetext": "تحت فية لستة بأحدث عمليات تحميل الملفات.\nانظر [[Special:NewFiles|معرض الملفات الجديدة]] لعرض بصرى أكتر",
        "filename": "اسم الملف",
        "mywatchlist": "لستة  الصفح اللى باراقبها",
        "watchlistfor2": "لليوزر $1 ($2)",
        "nowatchlist": "مافيش حاجة فى لستة مراقبتك.",
-       "watchlistanontext": "لو سمحت $1 لعرض أو تعديل الصفحات فى لستة مراقبتك.",
+       "watchlistanontext": "لو سمحت اعمل لوجين لعرض أو تعديل الصفحات فى لستة مراقبتك.",
        "watchnologin": "مش متسجل",
        "addedwatchtext": "تمت إضافة الصفحه  \"$1\"  [[Special:Watchlist|للستة الصفحات اللى بتراقبها]].\nالتعديلات اللى بعد كده ها تتحط على الصفحه دى، وصفحة المناقش الخاصه بها ها تتحط هناك. واسم الصفحة هايظهر  بخط <b>عريض</b> فى صفحة [[Special:RecentChanges|أحدث التعديلات]] لتسهيل تحديدها واكتشافها.",
        "removedwatchtext": "الصفحه دى اتشالت \"[[:$1]]\" من [[Special:Watchlist|لستة الصفحات اللى بتراقبها]].",
        "whatlinkshere-hidelinks": "$1 لينكات",
        "whatlinkshere-hideimages": "$1 وصلة صورة",
        "whatlinkshere-filters": "فلاتر",
-       "blockip": "منع يوزر",
+       "blockip": "بلوك {{GENDER:$1|اليوزر|اليوزره}}",
        "blockip-legend": "منع اليوزر",
        "blockiptext": "استخدم الاستمارة اللى تحت لمنع عنوان أيبى أو يوزر معين من الكتابة.\nدا لازم يحصل بس علشان تمنع التخريب ،و على حسب\n[[{{MediaWiki:Policy-url}}|السياسة]].\nاكتب سبب محدد تحت (يعنى مثلا، اكتب الصفحات المعينة اللى اتخربت بسببه).",
        "ipaddressorusername": "عنوان الأيبى أو اسم اليوزر:",
index fb709a3..bd2a0a3 100644 (file)
                        "아라",
                        "Fitoschido",
                        "Macofe",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Tokvo",
+                       "Crucifunked"
                ]
        },
        "tog-underline": "Sorrayar enllaces:",
        "tog-hideminor": "Anubrir les ediciones menores nos cambeos recientes",
        "tog-hidepatrolled": "Anubrir les ediciones vixilaes nos cambeos recientes",
        "tog-newpageshidepatrolled": "Anubrir les páxines vixilaes na llista de páxines nueves",
-       "tog-hidecategorization": "Tapecer la categorización de páxines",
+       "tog-hidecategorization": "Anubrir la categorización de páxines",
        "tog-extendwatchlist": "Espander la llista de siguimientu p'amosar tolos cambeos, non solo los más recientes",
        "tog-usenewrc": "Agrupar los cambeos por páxina nos cambeos recientes y na llista de siguimientu",
        "tog-numberheadings": "Autonumberar los encabezaos",
        "period-am": "AM",
        "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categoría|Categoríes}}",
-       "category_header": "Páxines na categoría «$1»",
+       "category_header": "Páxines na categoría \"$1\"",
        "subcategories": "Subcategoríes",
-       "category-media-header": "Ficheros multimedia na categoría «$1»",
-       "category-empty": "''Anguaño esta categoría nun tien nengún artículu nin ficheru multimedia.''",
+       "category-media-header": "Ficheros multimedia na categoría \"$1\"",
+       "category-empty": "<em>Anguaño esta categoría nun tien nengún artículu nin ficheru multimedia.</em>",
        "hidden-categories": "{{PLURAL:$1|Categoría anubrida|Categoríes anubríes}}",
        "hidden-category-category": "Categoríes anubríes",
        "category-subcat-count": "{{PLURAL:$2|Esta categoría tien namái la subcategoría siguiente.|Esta categoría tien {{PLURAL:$1|la siguiente subcategoría|les siguientes $1 subcategoríes}}, d'un total de $2.}}",
        "tagline": "De {{SITENAME}}",
        "help": "Ayuda",
        "search": "Buscar",
+       "search-ignored-headings": " #<!-- dexar esta llinia exactamente como ta --> <pre>\n# Testeres que nun se tendrán en cuenta na gueta.\n# Los cambios fechos equí son efeutivos nel momentu que s'indexa la páxina cola testera.\n# Pue forzar el reindexáu d'una páxina faciendo una edición nula.\n# La sintaxis ye la siguiente:\n#   * Tolo qu'hai dende un caráuter \"#\" al fin de llinia ye un comentariu\n#   * Cada llinia nun-balera ye'l títulu exactu a descartar, incluyendo mayúscules y demás\nReferencies\nEnllaces esternos\nVer tamién\n #</pre> <!-- dexar esta llinia exactamente como ta -->",
        "searchbutton": "Guetar",
        "go": "Dir",
        "searcharticle": "Dir",
        "yourpasswordagain": "Escribi otra vuelta la contraseña:",
        "createacct-yourpasswordagain": "Confirmar la contraseña",
        "createacct-yourpasswordagain-ph": "Escriba nuevamente la contraseña",
-       "remembermypassword": "Recordar la mio identificación nesti restolador (un máximu {{PLURAL:$1|d'un día|de $1 díes}})",
        "userlogin-remembermypassword": "Caltener abierta la sesión",
        "userlogin-signwithsecure": "Usar una conexón segura",
        "cannotloginnow-title": "Nun puede aniciase sesión agora",
        "passwordreset-emailelement": "Nome d'usuariu: \n$1\n\nContraseña temporal: \n$2",
        "passwordreset-emailsentemail": "Si esta direición de corréu electrónicu ta asociada cola to cuenta, unviaráse un corréu pa reaniciar la contraseña.",
        "passwordreset-emailsentusername": "Si hai una direición de corréu electrónicu asociada con esti nome d'usuariu, unviaráse un corréu electrónicu pa reaniciar la contraseña.",
-       "passwordreset-emailsent-capture": "Unvióse un corréu electrónicu pa reaniciar la contraseña, que s'amuesa abaxo.",
-       "passwordreset-emailerror-capture": "Unvióse un corréu electrónicu pa reaniciar la contraseña, que s'amuesa abaxo, pero falló l'unviu {{GENDER:$2|al usuariu|a la usuaria}}: $1",
+       "passwordreset-emailsent-capture2": "{{PLURAL:$1|Unvióse'l corréu|Unviáronse los correos}} de reaniciu de contraseña. {{PLURAL:$1|El nome d'usuariu y la contraseña|La llista de nomes d'usuarios y contraseñes}} amuésase de siguío.",
+       "passwordreset-emailerror-capture2": "Nun foi posible mandar un corréu electrónicu {{Gender:$2|al usuariu|a la usuaria}}: $1 {{PLURAL:$3|El nome d'usuariu y la contraseña|La llista de nomes d'usuarios y contraseñes}} amuésase de siguío.",
+       "passwordreset-nocaller": "Tien d'apurrise un llamador",
+       "passwordreset-nosuchcaller": "El llamador nun esiste: $1",
+       "passwordreset-ignored": "Nun se llogró'l reaniciu de la contraseña. ¿Seique nun se configuró un proveedor?",
        "passwordreset-invalideamil": "Direición de corréu inválida",
        "passwordreset-nodata": "Nun s'apurrió nin un nome d'usuariu nin una dirección de corréu electrónicu",
        "changeemail": "Camudar o desaniciar la dirección de corréu electrónicu",
        "changeemail-header": "Completa esti formulariu pa camudar la dirección de corréu electrónicu. Si quies desaniciar l'asociación de cualquier dirección de corréu electrónicu de la to cuenta, dexa en blancu la nueva dirección de corréu electrónicu cuando unvies el formulariu.",
-       "changeemail-passwordrequired": "Vas tener qu'escribir la contraseña pa confirmar esti cambéu.",
        "changeemail-no-info": "Tien d'aniciar sesión pa entrar direutamente a esta páxina.",
        "changeemail-oldemail": "Direición de corréu electrónicu actual:",
        "changeemail-newemail": "Direición de corréu electrónicu nueva:",
        "minoredit": "Esta ye una edición menor",
        "watchthis": "Vixilar esta páxina",
        "savearticle": "Guardar la páxina",
+       "savechanges": "Guardar los cambios",
        "publishpage": "Publicar la páxina",
+       "publishchanges": "Publicar los cambios",
        "preview": "Vista previa",
        "showpreview": "Amosar previsualización",
        "showdiff": "Amosar cambeos",
        "content-model-css": "CSS",
        "content-json-empty-object": "Oxetu baleru",
        "content-json-empty-array": "Matriz balera",
+       "deprecated-self-close-category": "Páxines qu'utilicen etiquetes HTML autozarraes inválides",
+       "deprecated-self-close-category-desc": "Esta páxina contien etiquetes HTML autozarraes inválides, tales como <code>&lt;b/></code> o <code>&lt;span/></code>. El comportamientu d'estes va camudar llueu pa ser coherente cola especificación d'HTML5, polo qu'el so usu nel testu wiki ta en desusu.",
        "duplicate-args-warning": "<strong>Avisu:</strong> [[:$1]] llama a [[:$2]] con más d'un valor pal parámetru «$3». Sólo va usase l'últimu valor dau.",
        "duplicate-args-category": "Páxines con argumentos duplicaos nes llamaes a plantíes",
        "duplicate-args-category-desc": "La páxina contien llamaes a plantíes qu'usen argumentos duplicaos, como <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> o <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Paez que la edición yá ta desfecha.",
        "undo-summary": "Esfacer la revisión $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|alderique]])",
        "undo-summary-username-hidden": "Desfacer la revisión $1 d'un usuariu tapecíu",
-       "cantcreateaccounttitle": "Nun pue crease la cuenta",
        "cantcreateaccount-text": "[[User:$3|$3]] bloquió la creación de cuentes dende esta direición IP (<strong>$1</strong>).\n\nEl motivu dau por $3 ye <em>$2</em>",
        "cantcreateaccount-range-text": "La creación de cuentes dende direiciones IP del rangu <strong>$1</strong>, qu'incluye la so direición IP (<strong>$4</strong>), ta bloquiada pol usuariu [[User:$3|$3]].\n\nLa razón dada por $3 ye <em>$2</em>.",
        "viewpagelogs": "Ver los rexistros d'esta páxina",
        "grant-group-high-volume": "Facer una actividá d'altu volume",
        "grant-group-customization": "Personalización y preferencies",
        "grant-group-administration": "Facer aiciones alministratives",
+       "grant-group-private-information": "Acceder a datos privaos sobre ti",
        "grant-group-other": "Actividaes variaes",
        "grant-blockusers": "Bloquiar y desbloquiar usuarios",
        "grant-createaccount": "Crear cuentes",
        "grant-highvolume": "Ediciones de gran volume",
        "grant-oversight": "Tapecer usuarios y desaniciar revisiones",
        "grant-patrol": "Patrullar los cambios fechos nes páxines",
+       "grant-privateinfo": "Acceder a información privada",
        "grant-protect": "Protexer y desprotexer páxines",
        "grant-rollback": "Desfacer los cambios fechos nes páxines",
        "grant-sendemail": "Unviar corréu a otros usuarios",
        "rightslogtext": "Esti ye un rexistru de los cambeos nos permisos d'usuariu.",
        "action-read": "lleer esta páxina",
        "action-edit": "editar esta páxina",
-       "action-createpage": "crear páxines",
-       "action-createtalk": "crear páxines d'alderique",
+       "action-createpage": "crear esta páxina",
+       "action-createtalk": "crear esta páxina d'alderique",
        "action-createaccount": "crear esta cuenta d'usuariu",
        "action-autocreateaccount": "crear automáticamente esta cuenta d'usuariu esterna",
        "action-history": "ver l'historial d'esta páxina",
        "action-applychangetags": "aplicar etiquetes xunto colos cambios",
        "action-changetags": "amestar y desaniciar etiquetes arbitraries en revisiones individuales y entraes del rexistru",
        "action-deletechangetags": "desaniciar etiquetes de la base de datos",
+       "action-purge": "purgar esta páxina",
        "nchanges": "{{PLURAL:$1|un cambiu|$1 cambios}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|dende la última visita}}",
        "enhancedrc-history": "historial",
        "file-thumbnail-no": "El ficheru entama con <strong>$1</strong>.\nPaez ser una imaxe de tamañu menguáu ''(miniatura)''.\nSi tienes esta imaxe a resolución completa xúbila; si non, por favor camuda'l nome del ficheru.",
        "fileexists-forbidden": "Yá esiste un ficheru con esti nome, y nun se pue renomar.\nSi tovía asina quies xubir el ficheru, por favor vuelvi atrás y usa otru nome.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Yá esiste un ficheru con esti nome nel direutoriu de ficheros compartíos.\nSi tovía asina quies xubir el ficheru, por favor vuelvi atrás y usa otru nome.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "La carga ye un duplicáu exautu de la versión actual de <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "La carga ye un duplicáu exautu {{PLURAL:$2|d'una versión más vieya|de versiones más vieyes}} de <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Esti ficheru ye un duplicáu {{PLURAL:$1|del siguiente ficheru|de los siguientes ficheros}}:",
        "file-deleted-duplicate": "Yá se desanició enantes un ficheru idénticu a esti ([[:$1]]).\nDeberíes revisar el historial de desaniciu del ficheru enantes de xubilu otra vuelta.",
        "file-deleted-duplicate-notitle": "Un ficheru idénticu a esti desanicióse anteriormente, y suprimióse'l títulu. Tendría de pidir a dalguién que pueda ver los datos del ficheru desaniciáu que revise la situación enantes de volver a xubilu.",
        "upload-http-error": "Hebo un error HTTP: $1",
        "upload-copy-upload-invalid-domain": "La xubida de copies nun ta disponible dende esti dominiu.",
        "upload-foreign-cant-upload": "Esta wiki nun ta configurada pa xubir ficheros al estoyu de ficheros esternu solicitáu.",
-       "upload-foreign-cant-load-config": "Falló la carga de la configuración de xuba de ficheros pal estoyu esternu de ficheros.",
+       "upload-foreign-cant-load-config": "Falló la carga de la configuración pa xubir ficheros al estoyu esternu.",
        "upload-dialog-disabled": "Nesta wiki tán desactivaes les xubíes de ficheros por aciu d'esti diálogu.",
        "upload-dialog-title": "Xubir ficheru",
        "upload-dialog-button-cancel": "Encaboxar",
        "uploadstash-errclear": "Falló'l desaniciu de los ficheros.",
        "uploadstash-refresh": "Anovar la llista de ficheros",
        "uploadstash-thumbnail": "ver miniatura",
+       "uploadstash-exception": "Nun pudo guardase la subida nel almacén ($1): «$2».",
        "invalid-chunk-offset": "Allugamientu inválidu del fragmentu",
        "img-auth-accessdenied": "Accesu denegáu",
        "img-auth-nopathinfo": "Falta PATH_INFO.\nEl to sirvidor nun ta configuráu pa pasar esta información.\nPue tar basáu en CGI y nun tener sofitu pa img_auth.\nVer https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization",
        "filerevert-submit": "Revertir",
        "filerevert-success": "'''[[Media:$1|$1]]''' foi revertida a la [$4 versión del $3 a les $2].",
        "filerevert-badversion": "Nun hai nenguna versión llocal previa d'esti archivu cola fecha conseñada.",
+       "filerevert-identical": "La versión actual del ficheru ye igual que la seleicionada.",
        "filedelete": "Desaniciar $1",
        "filedelete-legend": "Esborrar archivu",
        "filedelete-intro": "Tas a piques d'esborrar el ficheru '''[[Media:$1|$1]]''' xunto con tol so historial.",
        "trackingcategories-msg": "Categoría de siguimientu",
        "trackingcategories-name": "Nome del mensaxe",
        "trackingcategories-desc": "Criterios d'inclusión de categoría",
+       "restricted-displaytitle-ignored": "Páxines con títulos de vista inoraos",
+       "restricted-displaytitle-ignored-desc": "Esta páxina tien un <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> inoráu porque nun ye equivaliente al títulu verdaderu de la páxina.",
        "noindex-category-desc": "La páxina contien una pallabra máxica <code><nowiki>__NOINDEX__</nowiki></code> (y ta nun espaciu de nomes nel que se permite esta marca) y, poro, los robós nun la indexarán.",
        "index-category-desc": "La páxina contien una pallabra máxica <code><nowiki>__INDEX__</nowiki></code> (y ta nun espaciu de nomes nel que se permite esta marca) y, poro, los robós la indexarán anque normalmente nun lo faigan.",
        "post-expand-template-inclusion-category-desc": "El tamañu de la páxina ye mayor que <code>$wgMaxArticleSize</code> después de espander toles plantíes, de mou qu'algunes plantíes nun s'espandieron.",
        "watchnologin": "Non identificáu",
        "addwatch": "Amestar a la llista de siguimientu",
        "addedwatchtext": "«[[:$1]]» y la so páxina d'alderique amestáronse a la to [[Special:Watchlist|llista de siguimientu]].",
+       "addedwatchtext-talk": "«[[:$1]]» y la so páxina asociada amestáronse a la to [[Special:Watchlist|llista de siguimientu]].",
        "addedwatchtext-short": "Amestóse la páxina «$1» a la to llista de siguimientu.",
        "removewatch": "Desaniciar de la llista de siguimientu",
        "removedwatchtext": "«[[:$1]]» y la so páxina d'alderique desaniciáronse de la to [[Special:Watchlist|llista de siguimientu]].",
+       "removedwatchtext-talk": "«[[:$1]]» y la so páxina asociada desaniciáronse de la to [[Special:Watchlist|llista de siguimientu]].",
        "removedwatchtext-short": "Desanicióse la páxina «$1» de la to llista de siguimientu.",
        "watch": "Vixilar",
        "watchthispage": "Vixilar esta páxina",
        "rollbacklinkcount-morethan": "revertir más de $1 {{PLURAL:$1|edición|ediciones}}",
        "rollbackfailed": "Falló la reversión",
        "rollback-missingparam": "Faltan parámetros riquíos na solicitú.",
+       "rollback-missingrevision": "Nun pueden cargase los datos de la revisión.",
        "cantrollback": "Nun se pue revertir la edición; el postrer collaborador ye l'únicu autor d'esta páxina.",
        "alreadyrolled": "Nun se pue revertir la postrer edición de [[:$1]] fecha por [[User:$2|$2]] ([[User talk:$2|alderique]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\ndaquién más yá editó o revirtió la páxina.\n\nLa postrer edición foi fecha por [[User:$3|$3]] ([[User talk:$3|alderique]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "El resume de la edición yera: <em>$1</em>.",
        "undeletehistorynoadmin": "Esta páxina foi esborrada. El motivu del esborráu amuésase\nnel resume d'embaxo, amás de detalles de los usuarios qu'editaron esta páxina enantes\nde ser esborrada. El testu actual d'estes revisiones esborraes ta disponible namái pa los alministradores.",
        "undelete-revision": "Revisión esborrada de $1 ($4, a les $5) fecha por $3:",
        "undeleterevision-missing": "Falta la revisión o nun ye válida. Sieque l'enllaz nun seya correutu, o que la\nrevisión fuera restaurada o eliminada del archivu.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|Una revisión nun pudo|$1 revisiones nun pudieron}} restaurase porque {{PLURAL:$1|taba usándose la so|taben usándose les sos}} <code>rev_id</code>.",
        "undelete-nodiff": "Nun s'atopó revisión previa.",
        "undeletebtn": "Restaurar",
        "undeletelink": "ver/restaurar",
        "undeletedrevisions": "{{PLURAL:$1|1 revisión restaurada|$1 revisiones restauraes}}",
        "undeletedrevisions-files": "{{PLURAL:$1|1 revisión|$1 revisiones}} y {{PLURAL:$2|1 archivu|$2 archivos}} restauraos",
        "undeletedfiles": "{{PLURAL:$1|1 archivu restauráu|$1 archivos restauraos}}",
-       "cannotundelete": "Falló la restauración:\n$1",
+       "cannotundelete": "Falló total o parcialmente la restauración:\n$1",
        "undeletedpage": "'''Restauróse $1'''\n\nConsulta'l [[Special:Log/delete|rexistru d'esborraos]] pa ver los esborraos y restauraciones de recién.",
        "undelete-header": "Mira nel [[Special:Log/delete|rexistru d'esborraos]] les páxines esborraes recién.",
        "undelete-search-title": "Buscar páxines desaniciaes",
        "sp-contributions-newbies-sub": "Namái les cuentes nueves",
        "sp-contributions-newbies-title": "Contribuciones d'usuariu pa cuentes nueves",
        "sp-contributions-blocklog": "rexistru de bloqueos",
-       "sp-contributions-suppresslog": "collaboraciones del usuariu desaniciaes",
-       "sp-contributions-deleted": "Contribuciones d'usuariu desaniciaes",
+       "sp-contributions-suppresslog": "collaboraciones {{GENDER:$1|del usuariu|de la usuaria}} suprimíes",
+       "sp-contributions-deleted": "collaboraciones {{GENDER:$1|del usuariu|de la usuaria}} desaniciaes",
        "sp-contributions-uploads": "xubes",
        "sp-contributions-logs": "rexistros",
        "sp-contributions-talk": "alderique",
        "table_pager_limit_label": "Elementos por páxina:",
        "table_pager_limit_submit": "Dir",
        "table_pager_empty": "Nun hai resultaos",
-       "autosumm-blank": "Desaniciáu el conteníu de la páxina",
+       "autosumm-blank": "Desaniciáu'l conteníu de la páxina",
        "autosumm-replace": "Sustituyendo la páxina por '$1'",
        "autoredircomment": "Redirixendo a [[$1]]",
        "autosumm-new": "Páxina creada con «$1»",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
        "mw-widgets-titleinput-description-new-page": "la páxina inda nun esiste",
        "mw-widgets-titleinput-description-redirect": "redirixir a $1",
-       "api-error-blacklisted": "Escueyi un títulu distintu, más descriptivu.",
        "sessionmanager-tie": "Nun puen combinase dellos tipos de solicitú d'identificación: $1.",
        "sessionprovider-generic": "sesiones $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sesiones basaes en cookies",
        "log-action-filter-newusers": "Tipu de creación de cuenta:",
        "log-action-filter-patrol": "Tipu de patrulla:",
        "log-action-filter-protect": "Tipu de proteición:",
-       "log-action-filter-rights": "Tipu de cambéu de permisos",
-       "log-action-filter-suppress": "Tipu de supresión",
+       "log-action-filter-rights": "Tipu de cambéu de permisos:",
+       "log-action-filter-suppress": "Tipu de supresión:",
        "log-action-filter-upload": "Tipu de carga:",
        "log-action-filter-all": "Too",
        "log-action-filter-block-block": "Bloquéu",
        "log-action-filter-suppress-reblock": "Supresión d'usuariu por rebloquéu",
        "log-action-filter-upload-upload": "Nueva carga",
        "log-action-filter-upload-overwrite": "Recargar",
+       "authmanager-authn-not-in-progress": "L'autenticación nun ta en cursu o los datos de sesión perdiéronse. Por favor, vuelve de nueves al principiu.",
        "authmanager-authn-no-primary": "Les credenciales apurríes nun pudieron autentificase.",
        "authmanager-authn-no-local-user": "Les credenciales apurríes nun tán asociaes con nengún usuariu nesta wiki.",
+       "authmanager-authn-no-local-user-link": "Les credenciales suministraes son válides, pero nun tán venceyaes con nengún usuariu d'esta wiki. Empecipia sesión d'otra miente, o crea un usuariu nuevu, y podrás venceyar los tos credenciales anteriores a esa cuenta.",
        "authmanager-authn-autocreate-failed": "Falló la creación automática d'una cuenta local: $1",
+       "authmanager-change-not-supported": "Les credenciales apurríes nun pueden camudase porque nun hai nada que les use.",
+       "authmanager-create-disabled": "Ta desactivada la creación de cuentes.",
+       "authmanager-create-from-login": "Pa crear la cuenta, rellena los campos de más abaxo.",
+       "authmanager-create-not-in-progress": "La creación de la cuenta nun ta progresando, o perdiéronse los datos de la sesión. Por favor, vuelve de nueves al principiu.",
+       "authmanager-create-no-primary": "Les credenciales apurríes nun pueden usase pa crear cuentes.",
+       "authmanager-link-no-primary": "Les credenciales apurríes nun pueden usase pa enllazar cuentes.",
+       "authmanager-link-not-in-progress": "L'enllazáu de la cuenta nun ta progresando, o perdiéronse los datos de la sesión. Por favor, vuelve de nueves al principiu.",
+       "authmanager-authplugin-setpass-failed-title": "Falló'l cambiu de contraseña",
+       "authmanager-authplugin-setpass-failed-message": "El complementu d'autenticación refugó'l cambéu de contraseña.",
+       "authmanager-authplugin-create-fail": "El complementu d'autenticación refugó la creación de la cuenta.",
+       "authmanager-authplugin-setpass-denied": "El complementu de autenticación nun permite cambiar contraseñes.",
        "authmanager-authplugin-setpass-bad-domain": "Dominiu inválidu.",
        "authmanager-autocreate-noperm": "Nun se permite la creación automática de cuentes.",
        "authmanager-autocreate-exception": "La creación automática de cuentes desactivóse temporalmente por cuenta d'errores previos.",
        "authmanager-provider-password": "Autenticación basada en contraseña",
        "authmanager-provider-password-domain": "Autenticación basada en contraseña y dominiu",
        "authmanager-provider-temporarypassword": "Contraseña temporal",
+       "authprovider-confirmlink-message": "Basándose nos tos últimos intentos d'aniciar sesión, les siguientes cuentes pueden venceyase a la to cuenta na wiki. Venceyales permite aniciar sesión al traviés d'eses cuentes. Escueye cuáles tienen de venceyase.",
+       "authprovider-confirmlink-request-label": "Cuentes que tienen de venceyase",
+       "authprovider-confirmlink-success-line": "$1: Enllazada correutamente.",
+       "authprovider-confirmlink-failed": "L'enllaz de cuentes nun foi totalmente correutu: $1",
+       "authprovider-confirmlink-ok-help": "Siguir depués d'amosar los mensaxes de fallu nel enllazáu.",
        "authprovider-resetpass-skip-label": "Saltar",
        "authprovider-resetpass-skip-help": "Saltar el reaniciu de la contraseña.",
+       "authform-nosession-login": "La autenticación foi correuta, pero'l navegador nun \"s'alcuerda\" de que tuvo coneutáu.\n\n$1",
+       "authform-nosession-signup": "Creóse la cuenta, pero'l navegador nun \"s'alcuerda\" de que tuvo coneutáu.\n\n$1",
+       "authform-newtoken": "Falta token. $1",
+       "authform-notoken": "Falta token",
+       "authform-wrongtoken": "Token incorreutu",
+       "specialpage-securitylevel-not-allowed-title": "Nun ta permitío",
+       "specialpage-securitylevel-not-allowed": "Sentímoslo, nun tienes permisu pa usar esta páxina porque nun pudo comprobase la to identidá.",
+       "authpage-cannot-login": "Nun pudo empecipiase l'aniciu de sesión.",
+       "authpage-cannot-login-continue": "Nun pudo siguise col aniciu de sesión. Probablemente la sesión caducó.",
+       "authpage-cannot-create": "Nun pudo empecipiase la creación de la cuenta.",
+       "authpage-cannot-create-continue": "Nun pudo siguise la creación de cuenta. Probablemente la sesión caducó.",
+       "authpage-cannot-link": "Nun pudo empecipiase l'enllazáu de la cuenta.",
+       "authpage-cannot-link-continue": "Nun pudo siguise col enllazáu de cuenta. Probablemente la sesión caducó.",
+       "cannotauth-not-allowed-title": "Permisu refugáu",
+       "cannotauth-not-allowed": "Nun tienes permisu pa usar esta páxina",
+       "changecredentials": "Camudar les credenciales",
+       "changecredentials-submit": "Camudar credenciales",
+       "changecredentials-invalidsubpage": "$1 nun ye un tipu de credencial válidu.",
+       "changecredentials-success": "Camudáronse les tos credenciales.",
+       "removecredentials": "Desaniciar credenciales",
+       "removecredentials-submit": "Desaniciar credenciales",
+       "removecredentials-invalidsubpage": "$1 nun ye un tipu de credencial válidu.",
+       "removecredentials-success": "Desaniciáronse les tos credenciales.",
+       "credentialsform-provider": "Tipu de credenciales:",
        "credentialsform-account": "Nome de la cuenta:",
        "cannotlink-no-provider-title": "Nun hai cuentes enllazables",
        "cannotlink-no-provider": "Nun hai cuentes enllazables.",
        "linkaccounts-success-text": "Enllazóse la cuenta.",
        "linkaccounts-submit": "Enllazar cuentes",
        "unlinkaccounts": "Desenllazar cuentes",
-       "unlinkaccounts-success": "Desenllazóse la cuenta."
+       "unlinkaccounts-success": "Desenllazóse la cuenta.",
+       "authenticationdatachange-ignored": "Nun se xestionó'l cambéu de los datos d'autentificacion. ¿Seique, nun se configuró un fornidor?",
+       "userjsispublic": "Atención: les subpáxines JavaScript nun tendríen de contener datos acutaos porque son visibles pa otros usuarios.",
+       "usercssispublic": "Atención: les subpáxines CSS nun tendríen de contener datos acutaos porque son visibles pa otros usuarios."
 }
index 16bf5bc..add8bea 100644 (file)
        "minoredit": "Kiçik redaktə",
        "watchthis": "Bu səhifəni izlə",
        "savearticle": "Səhifəni qeyd et",
+       "publishpage": "Səhifəni yayımla",
+       "publishchanges": "Dəyişiklikləri yayımla",
        "preview": "Sınaq görüntüsü",
        "showpreview": "Sınaq göstərişi",
        "showdiff": "Dəyişiklikləri göstər",
        "undo-failure": "Dəyişikliklərin toqquşması nəticəsində geriyə qaytarma işi uğursuz oldu.",
        "undo-norev": "Düzəlişlər geri qaytarıla bilinmir, çünki onlar ya mövcüd deyil, ya da silinib.",
        "undo-summary": "$1 dəyişikliyi [[Special:Contributions/$2|$2]] ([[User talk:$2|Müzakirə]]) tərəfindən geri alındı.",
-       "cantcreateaccounttitle": "Hesab açılmır.",
        "cantcreateaccount-text": "Bu IP ünvanından ('''$1''') istifadəçi hesabı yaradılması [[User:$3|$3]] tərəfindən əngəllənmişdir.\n\n$3 tərəfindən verilən səbəb ''$2''",
        "viewpagelogs": "Bu səhifə ilə bağlı qeydlərə bax",
        "nohistory": "Bu səhifənin dəyişikliklər tarixçəsi mövcud deyil.",
        "alllogstext": "{{SITENAME}} üçün bütün mövcud qeydlərin birgə göstərişi.\nQeyd növü, istifadəçi adı və ya təsir edilmiş səhifəni seçməklə daha spesifik ola bilərsiniz.",
        "logempty": "Jurnalda uyğun qeyd tapılmadı.",
        "log-title-wildcard": "Bu mətnlə başlayan başlıqları axtar",
+       "checkbox-select": "Seçin: $1",
+       "checkbox-all": "Hamısı",
+       "checkbox-none": "Heç biri",
+       "checkbox-invert": "Çevir",
        "allpages": "Bütün səhifələr",
        "nextpage": "Sonrakı səhifə ($1)",
        "prevpage": "Əvvəlki səhifə ($1)",
        "unwatching": "İzlənilmir...",
        "enotif_reset": "Baxılmış bütün səhifələri işarələ.",
        "enotif_impersonal_salutation": "{{SITENAME}} istifadəçisi",
+       "enotif_subject_moved": "{{SITENAME}} səhifəsi $1 $2 tərəfindən {{GENDER:$2|köçürüldü}}.",
+       "enotif_subject_changed": "{{SITENAME}} səhifəsi $1 $2 tərəfindən {{GENDER:$2|dəyişdirildi}}.",
        "enotif_body_intro_moved": "{{SITENAME}}da  $1 səhifəsinin adı $PAGEEDITDATE tarixində $2 tərəfindən {{GENDER:$2|dəyişdirilib}}, son versiya üçün bura baxın: $3.",
        "enotif_lastvisited": "Sonuncu ziyarətinizdən indiyədək olan bütün dəyişiklikləri görmək üçün baxın: $1.",
        "enotif_lastdiff": "Bu dəyişikliyi görmək üçün $1 səhifəsinə baxın.",
        "delete-confirm": "Silinən səhifə: \"$1\"",
        "delete-legend": "Sil",
        "historywarning": "'''Xəbərdarlıq:''' Silinəcək səhifənin tarixçəsində qeyd olunmuş $1 {{PLURAL:$1|redaktə|redaktə}} var:",
+       "historyaction-submit": "Göstər",
        "confirmdeletetext": "Bu səhifə və ya fayl bütün tarixçəsi ilə birlikdə birdəfəlik silinəcək. Bunu [[{{MediaWiki:Policy-url}}|qaydalara]] uyğun etdiyinizi və əməliyyatın nəticələrini başa düşdüyünüzü təsdiq edin.",
        "actioncomplete": "Fəaliyyət tamamlandı",
        "actionfailed": "Əməliyyat yerinə yetirilmədi",
index d8c9209..76c2859 100644 (file)
        },
        "tog-underline": "باغلانتی‌لارین آلتینی خطله:",
        "tog-hideminor": "سوْن دییشیکلیکلرده کیچیکلری گیزلت",
-       "tog-hidepatrolled": "سوْن دییشیکلیکلرده نظارتلنمیش دَییشیکلیکلری گیزلت",
+       "tog-hidepatrolled": "سوْن دییشیکلیک‌لرده گؤزدن گئچیریلمیش دَییشیکلیکلری گیزلت",
        "tog-newpageshidepatrolled": "یوْخلانمیش صفحه‌لری یئنی صفحه‌لر لیستیندن گیزلت",
-       "tog-extendwatchlist": "ایزله‌دیک‌لری، یالنیز یئنی‌لر اۆچون یوْخ، بۆتون دییشیک‌لیک‌لری گؤرستمک اۆچون، گنیشلندیر.",
+       "tog-hidecategorization": "صفحه بؤلمه‌لرینی گیزلت",
+       "tog-extendwatchlist": "ایزله‌دیک‌لری تکجه یئنی‌لر اۆچون دئییل، بۆتون دییشیک‌لیک‌لری گؤسترمک اۆچون گئنیشلندیر.",
        "tog-usenewrc": "دَییشیک‌لیک‌لری سوْن دَییشیک‌لیک‌لر صفحه‌سینده ایزله‌دیک‌لر صفحه‌سینده قروپ‌لا (جاوااسکریپت گرک‌دیر)",
        "tog-numberheadings": "باشلیق‌لاری اوْتوماتیک نۆمره‌له",
        "tog-showtoolbar": "دَییشدیرمه آراج-چۇبوغونو گؤستر",
@@ -36,6 +37,7 @@
        "tog-watchdefault": "دَییشدیردیگیم صفحه‌‌لری و فايل‌لاری، ایزله‌دیک‌لریمه آرتیر",
        "tog-watchmoves": "داشیدیغیم صفحه‌‌لری و فايللاری ایزله‌دیکلریمه آرتیر",
        "tog-watchdeletion": "سیلدیگیم صفحه‌‌لری و فايللاری ایزله‌دیکلریمه آرتیر",
+       "tog-watchuploads": "یئنی یۆکله‌دیگیم فایل‌لاری ایزله‌دیک‌لریمه آرتیر",
        "tog-watchrollback": "قایتاریلمیش صفحه‌لری ایزله‌دیکلریمه آرتیر",
        "tog-minordefault": "دیفالت اوْلاراق، بۆتون دَییشدیر‌مه‌لری کیچیک کیمی علامتله",
        "tog-previewontop": "اؤن‌گؤستریشی، يازماق قۇتوسوندان قاباق گؤستر",
        "tog-uselivepreview": "دیری اؤن‌گؤستریش ایشلت (تِست مرحله‌سینده)",
        "tog-forceeditsummary": "دَییشیکلیک قیساسی بوْش قالمیشسا منه بیلیندیر",
        "tog-watchlisthideown": "منیم دَییشیک‌لیک‌لریمی ایزله‌دیک‌لردن گیزلت",
-       "tog-watchlisthidebots": "بÙ\88Ù\92ت دَییشیک‌لیک‌لرینی ایزله‌دیک‌لردن گیزلت",
+       "tog-watchlisthidebots": "رÙ\88Ù\92بات دَییشیک‌لیک‌لرینی ایزله‌دیک‌لردن گیزلت",
        "tog-watchlisthideminor": "کیچیک دَییشیک‌لیک‌لری ایزله‌دیک‌لردن گیزلت",
        "tog-watchlisthideliu": "گیریش ائتمیش ایشلدن‌لرین دَییشیک‌لیک‌لرینی ایزله‌دیک‌لردن گیزلت",
        "tog-watchlisthideanons": "تانینمامیش ایشلدن‌لرین دَییشیک‌لیک‌لرینی ایزله‌دیک‌لردن گیزلت",
        "tog-watchlisthidepatrolled": "نظارتلنمیش دَییشیکلیکلری گؤزله‌دیکلردن گیزلت",
+       "tog-watchlisthidecategorization": "صفحه‌لرین بؤلمه‌‌لرینی گیزلت",
        "tog-ccmeonemails": "باشقا ایشلدن‌لره گؤندردیگیم ایمئیل‌لرین کوْپی‌لرینی منه گؤندر",
        "tog-diffonly": "مۆقایسه‌لر آلتیندا صفحه‌نین ایچینده‌کیلرینی گؤسترمه",
        "tog-showhiddencats": "گیزلی بؤلمه‌لری گؤستر",
        "october-date": "اوْکتوبرون $1-ی",
        "november-date": "نوْوامبرین $1-ی",
        "december-date": "دسامبرین $1-ی",
+       "period-am": "صۆبح",
+       "period-pm": "آخشام",
        "pagecategories": "{{PLURAL:$1|بؤلمه|بؤلمه‌لر}}",
        "category_header": "«$1» بؤلمه‌سینده صفحه‌لر",
        "subcategories": "آلت‌بؤلمه‌لر",
        "site-atom-feed": "$1 آتوم فید",
        "page-rss-feed": "«$1» آراِس‌اِس فید",
        "page-atom-feed": "«$1» آتوم فید",
-       "red-link-title": "$1 (صفحه یوْخدور)",
+       "red-link-title": "$1 (صفحه یوْخدور)",
        "sort-descending": "آزالان سیرالاماق",
        "sort-ascending": "چوْخالان سیرالاماق",
        "nstab-main": "صفحه",
        "virus-scanfailed": "یوخلاماق باشا چاتمادی (کود $1)",
        "virus-unknownscanner": "تانینمامیش آنتی‌ویروس:",
        "logouttext": "<strong>سیز ایندی سیستِم‌دن چیخدینیز.</strong>\n\nبونا دیقت ائدین کی وب حافیظه نیزی سیلمه ین،بعضی صحیفه‌لر کَش-ینیزی سیلمه‌میش کیمی، هله ده سیزین گیریش ائتدیگینیز کیمی گؤستریله‌جکلر.",
+       "cannotlogoutnow-title": "ایندی چیخیش اوْلونمازدیر",
+       "cannotlogoutnow-text": "$1-ی ایشلدرکن چیخیش اوْلونمازدیر.",
        "welcomeuser": "خوش گلمیسینیز، $1!",
        "welcomecreation-msg": "حسابینیز آچیلدی.\n[[Special:Preferences|{{SITENAME}}ترجیحلر]] دییشدیرمیی اونوتمایین.",
        "yourname": "ایشلدن آدی:",
        "remembermypassword": "بو بیلگی‌سایاردا منیم گیریشیمی (چوخو $1 {{PLURAL:$1|گون}}ه قدر) یاددا ساخلا",
        "userlogin-remembermypassword": "منی ایچری‌ده ساخلا",
        "userlogin-signwithsecure": "آرخایین باغلانتی ایشلدین",
+       "cannotloginnow-title": "ایندی گیریش اوْلونمازدیر",
+       "cannotloginnow-text": "$1-ی ایشلدرکن گیریش اوْلونمازدیر",
        "yourdomainname": "سیزین دامنه:",
        "password-change-forbidden": "بو ویکی‌ده رمزلری دَییشه بیلنمه‌سینیز.",
        "externaldberror": "بیر دیتابیس دوغرولاما خطاسی اولدو، یوخسا سیزین ائشیک حسابینیزی گونجل‌لدمگه ایجازه‌نیز یوخدور.",
        "userlogin-resetpassword-link": "رمزینیزی اونوتموسوز مو؟",
        "userlogin-helplink2": "گیریش ایله کؤمک",
        "userlogin-loggedin": "سیر حال حاضیردا {{GENDER:$1|$1}} عونوانیندا گیریش ائدیب سیز.\nآشاغیداکی فورمودان بیر آیری ایشلدن عونوانیندا گیریش اوچون ایشلدین.",
+       "userlogin-reauth": "{{GENDER:$1|$1}} اوْلدوغونوزو تأیید ائتمک اۆچون یئنه گیرمه‌لیسینیز.",
        "userlogin-createanother": "بیر باشقا حساب یارات",
-       "createacct-emailrequired": "ایمیل آدرسی",
+       "createacct-emailrequired": "ایمئیل آدرسی",
        "createacct-emailoptional": "ایمیل آدرسی (ایستگه باغلی)",
        "createacct-email-ph": "ایمیل آدرسینیزی یازین",
        "createacct-another-email-ph": "ایمیل آدرسینیزی یازین",
        "createaccountreason": "نَدَن‌لیک:",
        "createacct-reason": "سبب",
        "createacct-reason-ph": "ندن سیز باشقا حساب یارادیرسینیز",
+       "createacct-reason-help": "حساب یاراتماق لیستینده گؤستریلن مساژ",
        "createacct-submit": "حسابینیزی یارادین",
        "createacct-another-submit": "حساب یارات",
+       "createacct-continue-submit": "حساب یاراتماغین دالی‌سینی توت",
+       "createacct-another-continue-submit": "حساب یاراتماغی داوام ائت",
        "createacct-benefit-heading": "{{SITENAME}} سیزین کیمی آدام‌لارین الی ایله یارانیب‌دیر.",
        "createacct-benefit-body1": "{{PLURAL:$1|دَییشیکلیک}}",
        "createacct-benefit-body2": "{{PLURAL:$1|صفحه}}",
        "nocookieslogin": "{{SITENAME}} ایشلدنلری گیردیرمک اوچون، کوکی‌لری ایشلدیر.\nسیزین کوکی‌لریز باغلانیب‌دیر.\nلوطفا اونلاری آچین و یئنی‌دن چالیشین.",
        "nocookiesfornew": "قایناغینی دوغرو اولدوغونو بیلمه‌مک اوچون، ایشلدن حسابی یارادیلمادی.\nکوکی‌لرینیزین آچیق اولدوغون دان آرخایین اولوب، بو یارپاغی یئنی‌دن یوکله‌ییب، یئنی‌دن چالیشین.",
        "noname": "گئچرلی ایستیفاده‌چی آدی وئرمه‌دینیز.",
-       "loginsuccesstitle": "گیریش اوغورلو",
+       "loginsuccesstitle": "گیریلدینیز",
        "loginsuccess": "'''سیز ایندی {{SITENAME}} سایتینا، «$1» آدی‌له گیرمیسینیز.'''",
        "nosuchuser": "«$1» آدلا ایستیفاده‌چی یوخدور.\nایستیفاده‌چی آدلاری، حرفلرین بؤیوک/کیچیک‌لیگینه حساس‌دیلار.\nیازدیغینیزا یئنی‌دن باخین، یوخسا [[Special:CreateAccount|یئنی بیر حساب آچین]].",
        "nosuchusershort": "\"$1\" آدلا ایستیفاده‌چی یوخدور.\nدوزگون یازدیغینیزدان آرخایین اولون.",
        "createacct-another-realname-tip": "اصلی آد ایستگینیزه باغلی‌دیر.\nاگر اونو وئرماغی سئچسز، سیزین ایشلرینیزی سیزه مونتسب ائدن‌ده، بو اصلی آد ایشلنه‌جک‌دیر.",
        "pt-login": "گیریش ائت",
        "pt-login-button": "گیریش ائت",
+       "pt-login-continue-button": "گیریشین دالینی توت",
        "pt-createaccount": "حساب یارات",
        "pt-userlogout": "چیخیش",
        "php-mail-error-unknown": "پی‌اچ‌پی‌نین mail() فونکسیاسیندا تانینمامیش خطا.",
        "changepassword-success": "رمزینیز باجارییلا دَییشدیرلدی!",
        "changepassword-throttled": "سیزین چوخ گیریش چالیشماغینیز اولوب‌دور.\nلوطفاً یئنی‌دن چالیشماق‌دان اؤنجه $1 دؤزون.",
        "botpasswords": "روبات رمزی",
+       "botpasswords-disabled": "روبات پسووْردلاری ایشلدن سالینیبدیر.",
        "botpasswords-createnew": "روبات رمزی یارات",
        "botpasswords-label-appid": "روبات آدی:",
        "botpasswords-label-create": "یارات",
+       "botpasswords-label-update": "آپدیت ائت",
        "botpasswords-label-cancel": "وازگئچ",
        "botpasswords-label-delete": "سیل",
+       "botpasswords-label-resetpassword": "رمزی یئنی‌له",
+       "botpasswords-label-grants-column": "وئریلدی",
+       "botpasswords-bad-appid": "\"$1\" روْبات آدی اعتیبارلی دئییلدیر.",
+       "botpasswords-created-title": "روْبات پسووْردو یارادیلدی.",
+       "botpasswords-updated-title": "روْبات پسووْردو آپدئیت اوْلوندو.",
+       "botpasswords-deleted-title": "روْبات پسووْردو سیلیندی",
        "resetpass_forbidden": "رمزلر دَییشیلمز",
        "resetpass-no-info": "بو صحیفه‌نی دوغرو گؤردوگونوز اوچون سیستمه گیرمه‌لیسینیز.",
        "resetpass-submit-loggedin": "رمزی دَییشدیر",
        "passwordreset-emailtext-user": "{{SITENAME}} سایتیندا، $1 ایستیفاده‌چی، سیزین اوردا ($4) حسابینیزین رمزینی یئنیله‌مک ایستگی وئریب‌دیر. آشاغیداکی {{PLURAL:$3|ایستیفاده‌چی|ایستیفاده‌چیلر}} بو ایمیل ایله ایلگیلیدیرلر:\n\n$2\n\nبو گئچیجی {{PLURAL:$3|رمز|رمزلر}}، {{PLURAL:$5|بیر|$5گون}} سونرا واختلاری قورتاراجاق‌دیر. \nسیز گرک ایندی گیریب و بیر یئنی رمز سئچه‌سینیز. باشقا آدام بو ایستَگی وئرمیش‌سه، یوخسا سیز اسکی رمزینیزی یادا گتیرمیشسینیزسه، و داها اونو چئویرمک ایسته‌میرسینیزسه، بو مئساژی سایماییب و اسکی رمزینیزی ایشلدمگه داوام ائده بیلرسینیز.",
        "passwordreset-emailelement": "ایشلدن آدی: \n$1\n\nگئچیجی رمز: \n$2",
        "passwordreset-emailsentemail": "بۇ ایمئیل آدرسی حسابینیزا ثبت اوْلونموشسا٬ بیر رمز یئنیله‌مه ایمئیلی گؤندریله‌جکدیر.",
-       "passwordreset-emailsent-capture": "آشاغیدا گؤستریلن کیمی بیر رمز یئنیله‌مه ایمیلی گؤندریلیب‌دیر.",
-       "passwordreset-emailerror-capture": "آشاغیدا گؤستریلن کیمی بیر رمز یئنیله‌مه ایمیلی یارادیلیب‌دیر، اما {{GENDER:$2ایستیفاده‌چی}}‌یه گؤندرمگی باشاریلی اولمادی: $1",
        "changeemail": "ایمیل آدرسینی دَییشدیر یا سیل",
        "changeemail-header": "ایمیل آدرسیزی دَییشدیرمک اوچون بو فورمو دولدورون. حسابیزا بوتون ایمیل‌لرین ارتباطینی کسمک اوچون، یئنی ایمیل آدرسینی بوش ساخلایین.",
-       "changeemail-passwordrequired": "بو دَییشمه‌نی دوغرولاماق اوچون، رمزینیزی یازمالیسیز.",
        "changeemail-no-info": "بو صحیفه‌نی دوغرو گؤردوگونوز اوچون سیستمه گیرمه‌لیسینیز.",
        "changeemail-oldemail": "ایندیکی ایمیل آدرس:",
        "changeemail-newemail": "یئنی ایمیل آدرسی:",
        "watchthis": "بو صفحه‌نی ایزله",
        "savearticle": "صفحه‌نی ذخیره ائت",
        "savechanges": "دَییشیکلیکلری ذخیره ائت",
-       "publishchanges": "دَییشیکلیکلری یای",
+       "publishpage": "صفحه‌نی یاییملا",
+       "publishchanges": "دَییشیک‌لیک‌لری یاییملا",
        "preview": "اؤن‌گؤستریش",
        "showpreview": "سیناق گؤستریش",
        "showdiff": "دَییشیکلیکلری گؤستر",
        "undo-nochange": "نظره گلیر دَییشدیرمه قاباغجادان قایتاریلیب.",
        "undo-summary": "$1 دییشیک‌لیک [[Special:Contributions/$2|$2]] ([[User talk:$2|دانیشیق]]) طرفین‌دن قایتاریلدی.",
        "undo-summary-username-hidden": "گیزلی ایستیفاده‌چی ایله ائدیلمیش $1 نوسخه‌سینی قایتارماق",
-       "cantcreateaccounttitle": "حساب یارادماق اولمور",
        "cantcreateaccount-text": "بو ای پی عنوانین‌دان ('$1) ایستیفاده‌چی حسابی یارادیلماسی [[User:$3|$3]] طرفین‌دن انگللنمیش‌دیر.\n\n$3 طرفین‌دن وئریلن سبب '$2",
        "cantcreateaccount-range-text": "<strong>$1</strong> آی‌پی آدرس آرالیغیندان حساب یارانماق، [[User:$3|$3]] ایشلدنی طرفیندن یاساقلانیب‌دیر. سیزین‌ده آی‌پی آدرسیز (<strong>$4</strong>) بو آرادادیر.\n\n$3 طرفین‌دن وئریلن سبب بودور: «$2»",
        "viewpagelogs": "بۇ صفحه‌نین قئیدلرینه باخ",
        "prefs-skin": "قابیق",
        "skin-preview": "اؤن‌گؤستریش",
        "datedefault": "سئچیم‌سیز",
-       "prefs-labs": "آزÙ\85اÛ\8cÛ\8cØ´Û\8c Ø§Ø¤Ø²Ù\84â\80\8cÙ\84Û\8cÚ©لر",
+       "prefs-labs": "تست Ø®Û\86صÛ\87صÛ\8cتلر",
        "prefs-user-pages": "ایستیفاده‌چی صحیفه‌لری",
        "prefs-personal": "ایشلدن پروفایلی",
        "prefs-rc": "سوْن دَییشیکلیکلر",
        "prefs-editwatchlist-edit": "ایزلدیکلرینیزدن گورمک هابئله باشلیق لارین سیلمک",
        "prefs-editwatchlist-raw": "ایزله‌دیگیم خام لیستی دَییشدیر",
        "prefs-editwatchlist-clear": "ایزله دیگیم لیستی سیلمک",
-       "prefs-watchlist-days": "اÛ\8cزÙ\84Ù\87â\80\8cدÛ\8cÚ©â\80\8cÙ\84رÛ\8cÙ\85دÙ\87 Ú¯Ø¤Ø±Ø³Ø¯Û\8cÙ\84Ù\86 Ú¯Û\86Ù\86â\80\8cÙ\84ر",
+       "prefs-watchlist-days": "اÛ\8cزÙ\84Ù\87â\80\8cدÛ\8cÚ©â\80\8cÙ\84رÛ\8cÙ\85دÙ\87 Ú¯Ø¤Ø³ØªØ±Û\8cÙ\84Ù\86 Ú¯Û\86Ù\86Ù\84ر:",
        "prefs-watchlist-days-max": "چوخو {{PLURAL:$1|بیر|$1}} گون",
-       "prefs-watchlist-edits": "گنیشلنمیش ایزله‌ دیک لرده گؤرسدیلن دَییشیک‌لیک‌لرین چوْخو:",
+       "prefs-watchlist-edits": "گئنیشلنمیش ایزله‌‌دیک‌لرده گؤستریلن دَییشیک‌لیک‌لرین ان چوْخو:",
        "prefs-watchlist-edits-max": "چوخ سایی: ۱۰۰۰",
        "prefs-watchlist-token": "ایزله‌دیک‌لر آدرسی:",
        "prefs-misc": "باشقا",
        "columns": "سوتون‌لار",
        "searchresultshead": "آختار",
        "stub-threshold": "<a href=\"#\" class=\"stub\">باغلانتی‌سیز لینکی</a> دییشدیرمک اوچون حدود (بایت‌لارلا):",
+       "stub-threshold-sample-link": "میثال",
        "stub-threshold-disabled": "چالیشمایان",
-       "recentchangesdays": "سÙ\88Ù\92Ù\86 Ø¯Û\8cÛ\8cØ´Û\8cÚ©â\80\8câ\80\8cÙ\84ردÙ\87 Ú¯Ø¤Ø±Ø³Ø¯Û\8cÙ\84Ù\86 Ú¯Û\86Ù\86â\80\8cلر:",
+       "recentchangesdays": "سÙ\88Ù\92Ù\86 Ø¯Û\8cÛ\8cØ´Û\8cÚ©â\80\8câ\80\8cÙ\84ردÙ\87 Ú¯Ø¤Ø³ØªØ±Û\8cÙ\84Ù\86 Ú¯Û\86Ù\86لر:",
        "recentchangesdays-max": "ماکسیموم $1 {{PLURAL:$1|گون |گون}}",
        "recentchangescount": "سوْن ديَیشیک‌لیک‌لرده باشلیق سايی:",
        "prefs-help-recentchangescount": "بورایا یئنی دییشیک‌لیک‌لر، صحیفه‌لرین و ژورنال‌لارین تاریخچه‌سی داخیل‌دیر.",
        "timezoneregion-europe": "اوروپا",
        "timezoneregion-indian": "هیند اوقیانوسو",
        "timezoneregion-pacific": "بؤیوک اوقیانوس",
-       "allowemail": "باشÙ\82ا Ø§Û\8cØ´Ù\84دÙ\86â\80\8cÙ\84ردÙ\86 Ø§Û\8cÙ\85یل آلماغی آچ",
+       "allowemail": "Ø¢Û\8cرÛ\8c Ø§Û\8cØ´Ù\84دÙ\86Ù\84ردÙ\86 Ø§Û\8cÙ\85ئیل آلماغی آچ",
        "prefs-searchoptions": "آختار",
        "prefs-namespaces": "آدلار فضاسی:",
        "default": "فرض ائدیلن",
        "yourvariant": "دیل واریانتی:",
        "prefs-help-variant": "بو ویکی‌نین ایچینده‌کیلری‌نین گؤستریلدیگی اوچون سئچدیگینیز واریانت یوخسا اورتوقرافی.",
        "yournick": "یئنی ایمضا:",
-       "prefs-help-signature": "دانیشیق صحیفه‌لرینده یاخیشلار گرک «<nowiki>~~~~</nowiki>» ایله ایمضالانالار. بو نیشان اوتوماتیک‌جه سیزین آدینیز و تاریخه دؤنه‌جک‌دیر.",
+       "prefs-help-signature": "دانیشیق صفحه‌لرینده یازیلان باخیشلار گرک «<nowiki>~~~~</nowiki>» ایله ایمضالانا. بۇ علامت اوتوماتیک‌ شیکلده سیزین آدینیزلا تاریخه دؤنه‌جکدیر.",
        "badsig": "یانلیش خام ایمضا.\nاچ‌تی‌ام‌ال تگ‌لرینی یوخلایین.",
        "badsiglength": "ایمضانیز چوخ اوزون‌دور.\nاو گرک {{PLURAL:$1|بیر|$1}} حرف‌دن اوزون اولمایا.",
        "yourgender": "ترجیح وئریرسینیز نئجه توصیف اولونسون؟",
        "gender-unknown": "ترجیح وئریرم بیلیندیرمییم",
        "gender-male": "کیشی",
-       "gender-female": "خانیم",
+       "gender-female": "قادین",
        "prefs-help-gender": "بو ترجیح ایستگه باغلی‌دیر.\nبونون میقداری، سیزی آیریلارلا دوزگون آدرس وئرمک اوچون ایشلنه‌جکدیر.\nبو ایطلاعات، عمومی اولاجاق‌دیر.",
        "email": "ایمیل",
        "prefs-help-realname": "اصلی آد ایختیاری دیر.\nاگر اونو وئرماغی سئچسز، سیزین ایشلرینیزی سیزه مونتسب ائدن‌ده، بو اصلی آد ایشلنه‌جک‌دیر.",
        "prefs-help-email": "ایمیل آدرسی ایستگه باغلی‌دیر، آنجاق رمزینیزی اونوتدوغونوز واخت، سیزه يئنی رمز گؤندرمگه گرکلی‌دیر.",
        "prefs-help-email-others": "سیز هم‌ده آیری ایستیفاده‌چیلره ایجازه وئره بیلرسیز، سیزین ایستیفاده‌چی یا دانیشیق صحیفه‌نیزده بیر باغلانتی ایله، سیزله ایلگی قورسونلار.\nآیری ایستیفاده‌چیلر سیزینله ایلگی قوراندا، سیزین ایمیل آدرسیز گؤستریلمه‌یه‌جک‌دیر.",
        "prefs-help-email-required": "ایمیل آدرس گرکلی‌دیر.",
-       "prefs-info": "کؤک Ø§Û\8cØ·Ù\84اعات",
+       "prefs-info": "کؤک Ø¨Û\8cÙ\84Ú¯Û\8câ\80\8cÙ\84ر",
        "prefs-i18n": "میلتلرآراسیلاشدیرماق",
        "prefs-signature": "ایمضا",
        "prefs-dateformat": "تاریخ فورمتی",
        "right-override-export-depth": "ایچری باغلانتیلاری اولان صحیفه‌لری، چوخو ۵ درجه درینلیگی ایله، ایخراج ائت",
        "right-sendemail": "باشقا ایستیفاده‌چیلره ایمیل گؤندر",
        "right-passwordreset": "رمز یئنی‌له‌مه ایمیل‌لرینه باخ",
+       "grant-group-email": "ایمئیل گؤندر",
        "newuserlogpage": "ایشلدن یارادیلما ژورنالی",
        "newuserlogpagetext": "بۇ ایشلدن یارادماق لیستی‌دیر.",
        "rightslog": "ایشلدن حاقلاری ژورنالی",
        "number_of_watching_users_pageview": "[{{PLURAL:$1|بیر|$1}} ایزله‌ین ایستیفاده‌چی]",
        "rc_categories": "بؤلمه‌لره محدودلاشدیر («|» ایله آییر)",
        "rc_categories_any": "سئچیلمیشلرین هر بیریسی",
-       "rc-change-size-new": "دَییشیکلیک‌دن سونرا {{PLURAL:|بیر|$1}} بایت",
+       "rc-change-size-new": "دÙ\8eÛ\8cÛ\8cØ´Û\8cÚ©Ù\84Û\8cÚ©â\80\8cدÙ\86 Ø³Ù\88Ù\92Ù\86را {{PLURAL:|بÛ\8cر|$1}} Ø¨Ø§Û\8cت",
        "newsectionsummary": "/* $1 */ یئنی بؤلمه",
        "rc-enhanced-expand": "تفصیل‌لری گؤستر",
        "rc-enhanced-hide": "تفصیل‌لری گیزلت",
        "rc-old-title": "ایلک‌جه «$1» آدی‌له یارانمیشدیر",
-       "recentchangeslinked": "باغلی دَییشیکلیکلر",
-       "recentchangeslinked-feed": "باغلی دَییشیکلیکلر",
-       "recentchangeslinked-toolbox": "باغلی دَییشیکلیکلر",
+       "recentchangeslinked": "باغلی دَییشیکلیکلر",
+       "recentchangeslinked-feed": "باغلی دَییشیکلیکلر",
+       "recentchangeslinked-toolbox": "باغلی دَییشیکلیکلر",
        "recentchangeslinked-title": "''$1'' ایله باغلی دییشیکلر",
        "recentchangeslinked-summary": "آشاغیداکی سیياهی، قئيد اوْلونان صحیفه‌‌يه (و يا قئيد اوْلونان کاتئقوْرياداکی صحیفه‌‌لره) داخیلی کئچید وئرن صحیفه‌‌لرده ائدیلمیش سوْن ديَیشیکلیکلرین سیياهیسیدیر. \n[[Special:Watchlist|ایزله‌مه سیياهینیزداکی]] صحیفه‌‌لر '''قالین''' شریفتله گؤستریلمیشدیر.",
        "recentchangeslinked-page": "صفحه آدی:",
        "apihelp": "API یاردیمی",
        "apihelp-no-such-module": "«$1» ماژول تاپیلمادی.",
        "apisandbox-reset": "تمیزله",
-       "apisandbox-examples": "میثال",
+       "apisandbox-examples": "میثاللار",
        "apisandbox-results": "نتیجه",
        "apisandbox-request-time": "زامان ایستمک:$1",
        "booksources": "کیتاب قایناقلاری",
        "emailuser": "بو ایستیفاده‌چی‌یه ایمیل گؤندر",
        "emailuser-title-target": "بو {{GENDER:$1|ایستیفاده‌چی}}‌یه ایمیل گؤندر",
        "emailuser-title-notarget": "ایستیفاده‌چی‌یه ایمیل گؤندر",
-       "emailpagetext": "آشغÛ\8cداکÛ\8c Ù\81Ù\88رÙ\85â\80\8cداÙ\86Ø\8c Ø¨Ù\88 {{GENDER:$1|اÛ\8cستÛ\8cÙ\81ادÙ\87â\80\8cÚ\86Û\8c}}â\80\8cÛ\8cÙ\87 Ø§Û\8cÙ\85Û\8cÙ\84 Ú¯Ø¤Ù\86درÙ\85Ú© Ø§Ù\88Ú\86Ù\88Ù\86 Ø§Û\8cستÛ\8cÙ\81ادÙ\87 Ø§Ø¦Ø¯Ù\87 Ø¨Û\8cÙ\84رسÛ\8cÙ\86Û\8cز.\n[[Special:Preferences|اؤز ØªØ±Ø¬Û\8cØ­Ù\84رÛ\8cÙ\86Û\8cز]]دÙ\87 Ù\88ئرÙ\86 Ø§Û\8cÙ\85Û\8cÙ\84 Ø¢Ø¯Ø±Ø³Û\8cØ\8c Ø¨Ù\88 Ø§Û\8cÙ\85Û\8cÙ\84Û\8cÙ\86 \"From\" Û\8cئرÛ\8cÙ\86دÙ\87 Ú¯Ø¤Ø³ØªØ±Û\8cÙ\84Ù\87â\80\8cجکâ\80\8cدÛ\8cر Ù\88 Ø¨Ù\88Ù\86ا Ú¯Ø¤Ø±Ù\87 Ø§Û\8cÙ\85یلی آلان سیزه موستقیم جاواب گؤندره بیلر.",
+       "emailpagetext": "آشاغÛ\8cداکÛ\8c Ù\81Ù\88رÙ\85داÙ\86Ø\8c Ø¨Ù\88 {{GENDER:$1|اÛ\8cستÛ\8cÙ\81ادÙ\87â\80\8cÚ\86Û\8c}}â\80\8cÛ\8cÙ\87 Ø§Û\8cÙ\85ئÛ\8cÙ\84 Ú¯Ø¤Ù\86درÙ\85Ú© Ø§Û\86Ú\86Ù\88Ù\86 Ø§Û\8cستÛ\8cÙ\81ادÙ\87 Ø§Ø¦Ø¯Ù\87 Ø¨Û\8cÙ\84رسÛ\8cÙ\86Û\8cز.\n[[Special:Preferences|اؤز ØªØ±Ø¬Û\8cØ­Ù\84رÛ\8cÙ\86Û\8cز]]دÙ\87 Ù\88ئرÙ\86 Ø§Û\8cÙ\85ئÛ\8cÙ\84 Ø¢Ø¯Ø±Ø³Û\8cØ\8c Ø¨Ù\88 Ø§Û\8cÙ\85ئÛ\8cÙ\84Û\8cÙ\86 \"From\" Û\8cئرÛ\8cÙ\86دÙ\87 Ú¯Ø¤Ø³ØªØ±Û\8cÙ\84Ù\87â\80\8cرک Ø§Û\8cÙ\85ئیلی آلان سیزه موستقیم جاواب گؤندره بیلر.",
        "defemailsubject": "«$1» آدلی ایستیفاده‌چی‌دن، {{SITENAME}} ایمیلی",
        "usermaildisabled": "ایستیفاده‌چی ایمیلی باغلی‌دیر",
        "usermaildisabledtext": "بو ویکی‌ده باشقا ایستیفاده‌چیلره ایمیل گؤندره بیلنمه‌سینیز",
        "tooltip-undo": "ائدیلمیش ديَیشیکلیگی گئری قايتار و گئری قايتارما سببینی قئيد ائتمک اۆچون سێناق گؤستریشینی آچ",
        "tooltip-preferences-save": "ترجیحلری ساخلا",
        "tooltip-summary": "قیسا بیر خولاصه‌‌ یازین",
-       "anonymous": "{{SITENAME}} Ø³Ø§Û\8cتÛ\8cÙ\86Û\8cÙ\86 Ø¢Ù\86Ù\88Ù\86Û\8cÙ\85 {{PLURAL:$1|Û\8cستÛ\8cÙ\81ادÚ\86Û\8cسÛ\8c\8cستÛ\8cÙ\81ادÙ\87â\80\8cÚ\86Û\8câ\80\8cلری}}",
+       "anonymous": "{{SITENAME}} Ø³Ø§Û\8cتÛ\8cÙ\86Û\8cÙ\86 ØªØ§Ù\86Û\8cÙ\86Ù\85اÙ\85Û\8cØ´ {{PLURAL:$1|Û\8cØ´Ù\84دÙ\86\8cØ´Ù\84دÙ\86لری}}",
        "siteuser": "{{SITENAME}} ایستیفاده‌چی‌سی $1",
-       "anonuser": "{{SITENAME}} Ø¢Ù\86Ù\88Ù\86Û\8cÙ\85 Ø§Û\8cستÛ\8cÙ\81ادÙ\87â\80\8cÚ\86Û\8câ\80\8cسÛ\8c $1",
+       "anonuser": "{{SITENAME}} Ø¢Ù\86Ù\88Ù\86Û\8cÙ\85 Ø§Û\8cØ´Ù\84دÙ\86 $1",
        "lastmodifiedatby": "بۇ صحیفه‌‌ سوْنونجو دفعه‌‌ $1، $2 تاریخینده دَییشیلیب.",
        "othercontribs": "$1-این ایشینه اساسلانیب.",
        "others": "آیریلار",
        "pageinfo-hidden-categories": "گیزلی {{PLURAL:$1|بؤلمه|بؤلمه‌لر}} ($1)",
        "pageinfo-templates": "ایشله‌دیلمیش {{PLURAL:$1|بیر|$1}} شابلون ($1)",
        "pageinfo-transclusions": "ایچینده گلن {{PLURAL:$1|صحیفه|صحیفه‌لر}} ($1)",
-       "pageinfo-toolboxlink": "صÙ\81Ø­Ù\87 Ø§Û\8cØ·Ù\84اعاتی",
+       "pageinfo-toolboxlink": "صÙ\81Ø­Ù\87 Ø¨Û\8cÙ\84Ú¯Û\8câ\80\8cسی",
        "pageinfo-redirectsto": "ایستیقامتلن‌دیریلن",
        "pageinfo-redirectsto-info": "بیلگی",
        "pageinfo-contentpage": "بیر مضمون صفحه‌سی ساییلیر",
index 7d76b81..1dd05c7 100644 (file)
        "tagline": "{{SITENAME}} проектынан",
        "help": "Белешмә",
        "search": "Эҙләү",
+       "search-ignored-headings": " #<!-- был юлды нисек бар шулай ҡалдырығыҙ --> <pre>\n# Эҙләүҙәр инҡар иткән атамалар.\n# Атамаһы булған бит индексланғас та, үҙгәртмәләр үҙ көсөнә инәсәк.\n# Буш төҙәтеү менән һеҙ битте яңынан индекслата алаһығыҙ\n# Синтаксис шулай күренә:\n#   * Ошо символға «#» башланған юлдың аҙағына тиклем комментарий була\n#   * Һәр буш булмаған юл - инҡар ителгәндең атамаһы, быға регистр ҙа инә\nИҫкәрмәләр\nҺылтанмалар\nҠарағыҙ шулай уҡ\n#</pre> <!-- был юлды шул көйө ҡалдырығыҙ -->",
        "searchbutton": "Эҙләү",
        "go": "Күсеү",
        "searcharticle": "Күсеү",
        "passwordreset-emailelement": "Ҡулланыусы исеме: \n$1\n\nВаҡытлыса серһүҙ: \n$2",
        "passwordreset-emailsentemail": "Серһүҙҙе ташлау тураһындағы мәғлүмәт менән электрон почта аша хат ебәрелде.",
        "passwordreset-emailsentusername": "Әгәр был ҡатнашыусының исеменә бәйле  электрон почтаһының адресы булһа, ул саҡта  серһүҙҙе тергеҙеү өсөн  хат ебәреләсәк.",
-       "passwordreset-emailsent-capture": "Серһүҙҙе ташлау тураһындағы мәғлүмәт менән электрон хат ебәрелде, уның тексы түбәндә бирелә:",
-       "passwordreset-emailerror-capture": "Серһүҙҙе ташлау тураһында хәбәр итеүсе электрон хат булдырылғайны, ләкин уны  {{GENDER:$2|kullanıcıya}} түбәндәге сәбәп арҡаһында ебәреп булманы: $1",
        "passwordreset-invalideamil": "Электрон почта адресы ҡабул ителмәй",
        "changeemail": "Электрон почта адресын үҙгәртергә",
        "changeemail-header": "Электрон почта адресын үҙгәртеү",
-       "changeemail-passwordrequired": "Әлеге үҙгәрештәрҙе раҫлау өсөн, Һеҙгә ҡулланыусының серһүҙен яҙырға кәрәк",
        "changeemail-no-info": "Был биткә туранан ирешеү өсөн һеҙгә системала танылыу кәрәк.",
        "changeemail-oldemail": "Хәҙерге электрон почта адресы:",
        "changeemail-newemail": "Яңы электрон почта адресы:",
        "undo-nochange": "Төҙәтеү кире ҡайтарылған.",
        "undo-summary": "[[Special:Contributions/$2|$2]] ҡулланыусыһының ([[User talk:$2|фекер алышыу]]) $1 үҙгәртеүенән баш тартыу",
        "undo-summary-username-hidden": "Исеме йәшерелгән ҡатнашыусының төҙәтеүен  $1 кире ҡағыу",
-       "cantcreateaccounttitle": "Иҫәп яҙыуын яһап булмай",
        "cantcreateaccount-text": "Был IP-адрестан (<b>$1</b>) иҫәп яҙыуҙары булдырыу [[User:$3|$3]] тарафынан тыйылған.\n\n$3 белдергән сәбәп: ''$2''",
        "cantcreateaccount-range-text": "{{GENDER:$3|Ҡатнашыусы}} [[User:$3|$3]] һеҙҙең IP-адрес ингән (<strong>$4</strong>) <strong>$1</strong> диапозонында иҫәп яҙмаһын булдырмаҫҡа {{GENDER:$3|тыйыу}} ҡуйҙы.\n\nОшо сәбәп күһәтелгән: $2.",
        "viewpagelogs": "Был биттең яҙмаларын ҡарарға",
        "wlshowtime": "Күрһәтеү өсөн ваҡыт арауығы",
        "wlshowhideminor": "Әҙ генә үҙгәрештәр",
        "wlshowhidebots": "Бот",
-       "wlshowhideliu": "Ñ\82анÑ\8bлÒ\93ан Ò¡Ñ\83лланÑ\8bÑ\83Ñ\81Ñ\8bлаÑ\80",
+       "wlshowhideliu": "Ñ\82еÑ\80кÓ\99лгÓ\99н Ò¡Ð°Ñ\82наÑ\88Ñ\8bÑ\83Ñ\81Ñ\8b",
        "wlshowhideanons": "Аноним ҡулланыусылар",
        "wlshowhidepatr": "Тикшерелгән төҙәтеүҙәр",
        "wlshowhidemine": "Минең үҙгәртеүҙәр",
        "exif-webstatement": "Интернеттағы авторлыҡ хоҡуҡтары тураһындағы белдереү",
        "exif-originaldocumentid": "Сығанаҡ документтың уникаль идентификаторы",
        "exif-licenseurl": "Авторлыҡ рөхсәтнәмәһенең URL",
-       "exif-morepermissionsurl": "Альтернатив рөхсәтнәмә мәғлүмәттәре",
+       "exif-morepermissionsurl": "Альтернатив рөхсәтнамә мәғлүмәттәре",
        "exif-attributionurl": "Был эште ҡулланғанда, зинһар, ошонда һылтанма яһағыҙ",
        "exif-preferredattributionname": "Был эште ҡулланғанда, зинһар, ошоларҙы белдерегеҙ",
        "exif-pngfilecomment": "PNG файл өсөн иҫкәрмә",
        "logentry-delete-delete": "$1 $3 битен {{GENDER:$2|юйҙы}}",
        "logentry-delete-restore": "$1 $3 битен {{GENDER:$2|тергеҙҙе}}",
        "logentry-delete-event": "$1 журналдағы {{PLURAL:$5|яҙманы}} $3: $4 {{GENDER:$2|үҙгәртте}}",
-       "logentry-delete-revision": "$1 {{PLURAL:$5|$5 версияның}} күренеүсәнлеген   $3: $4 битендә {{GENDER:$2|үҙгәртте}}",
+       "logentry-delete-revision": "Ҡатнашыусы $1 $3: $4 битенең {{PLURAL:$5|$5 версиялары|$5 версияларының|1=версиялар}} күренешен {{GENDER:$2|үҙгәртте|үҙгәртте}}.",
        "logentry-delete-event-legacy": "$1  $3 журналы яҙмаларының күренеүсәнлеген {{GENDER:$2|үҙгәртте}}",
        "logentry-delete-revision-legacy": "$1  $3 битендә версияларҙың күренеүсәнлеген {{GENDER:$2|үҙгәртте}}",
        "logentry-suppress-delete": "$1 $3 битен {{GENDER:$2|баҫырылдырҙы}}",
        "mw-widgets-dateinput-no-date": "Дата һайланмаған",
        "mw-widgets-titleinput-description-new-page": "Был бит юҡ",
        "mw-widgets-titleinput-description-redirect": "$1 йүнәлтеү",
-       "api-error-blacklisted": "Башҡа аңлайышлы исем һайлағыҙ.",
        "sessionmanager-tie": "Бер юлы бер нисә ғаризаның төп нөсхәһен тикшереп булмай: $1.",
        "sessionprovider-generic": "$1 сессия",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "куки нигеҙендә сессиялар",
index 2eba576..fceddbc 100644 (file)
@@ -16,7 +16,8 @@
                        "아라",
                        "Matthias Klostermayr",
                        "Macofe",
-                       "George Animal"
+                       "George Animal",
+                       "Lokal Profil"
                ]
        },
        "tog-underline": "Links unterstreichen:",
        "accountcreated": "Benytzerkonto is erstöid worn",
        "accountcreatedtext": "'s Benytzerkonto $1 is aigrichtt worn.",
        "loginlanguagelabel": "Sproch: $1",
+       "pt-login": "Eilogga",
        "changepassword": "Posswort ändern",
        "oldpassword": "Oids Posswort:",
        "newpassword": "Neichs Posswort:",
        "post-expand-template-argument-warning": "'''Obocht:''' De Seitn enthoit mindastns oa Argument in da Vorlog, wo expandiat z grouss is. \nDe Parameta wean ignoriad.",
        "post-expand-template-argument-category": "Seitn mid ignoriadn Voalognparametan",
        "undo-summary": "Änderrung $1 voh [[Special:Contributions/$2|$2]] ([[User talk:$2|Diskussión]]) ryckgängig gmocht.",
-       "cantcreateaccounttitle": "Benutzerkonto kå ned erstöd wern",
        "viewpagelogs": "Logbiacha fia de Datei ozoagn",
        "currentrev": "Aktuelle Versión",
        "currentrev-asof": "Letzte Version vo $1",
index 839d286..abacb5a 100644 (file)
        "tagline": "Gikan sa {{SITENAME}}",
        "help": "Katabangan",
        "search": "Maghanap",
+       "search-ignored-headings": " #<!-- walaton ining linya eksaktong siring sana kaini --> <pre> \n# Mga Kapamayuhanan na pinagpapabayaan sa paghahanap. \n# Mga Kaliwatan kaini magkaka-epekto matapos na an pahina na igwang kapamayuhanan maipaghukdo. \n# Ika makakapagpuwersa sa pahina na maihuhukdo otro sa paagi nin paghimo nin sarong blangko na pagliwat. # An Sintaks iyo ining minasunod: \n# * An gabos magpoon sa sarong karakter na \"#\" sagkod sa tapos kan linya iyo an sarong komento \n# * An lambang linya na bakong blangko iyo an eksaktong titulo na pababayaan, kaso asin gabos na bagay \nMga Panultulan\nPanluwas na mga sugpon\nHilingon man \n#</pre> <!-- walaton ining linya eksaktong siring sana kaini -->",
        "searchbutton": "Maghanap",
        "go": "Dumani",
        "searcharticle": "Lakaw",
        "passwordreset-emailtext-user": "Paragamit $1 sa {{SITENAME}} naghahagad nin sarong pagiromdom kan detalye nin saimong panindog para sa {{SITENAME}}\n($4). An minasunod na paragamit {{PLURAL:$3|panindog iyo an|mga panindog iyo an}} na asosyado kaining e-koreong address:\n\n$2\n\n\n{{PLURAL:$3|Ining temporaryong sekretong panlaog|Ining mga temporaryong panlaog}} mapapaso sa {{PLURAL:$5|sarong aldaw|$5 mga aldaw}}.\nIka dapat na maglaog asin magpili nin sarong bagong sekretong panlaog ngunyan. Kun ibang tawo an naghimo kaining kahagadan, o kun saimo nang nagiromdoman an saimong orihinal na sekretong panlaog, asin habo mo nang ribayan ini, ipasapara mo na sana an mensaheng ini asin ipadagos mo nang gamiton an saimong lumang sekretong panlaog.",
        "passwordreset-emailelement": "Paragamit-ngaran: \n$1\n\nTemporaryong sekretong panlaog: \n$2",
        "passwordreset-emailsentemail": "Sarong e-surat sa pagliliwat kan sekretong panlaog an ipinadara na.",
-       "passwordreset-emailsent-capture": "Sarong e-surat sa pagliliwat kan sekretong panlaog an ipinadara na, yaon ipinapahiling sa ibaba.",
-       "passwordreset-emailerror-capture": "Sarong e-surat sa pagliliwat kan sekretong panlaog an pinagpuyos na, yaon ipinapahiling sa ibaba, alagad sa pagpapadara kaini sa {{GENDER:$2|paragamit}} nagpalya: $1",
        "changeemail": "Ribayan an e-koreong address",
        "changeemail-header": "Ribayan an panindog na e-koreong address",
        "changeemail-no-info": "Ika dapat nakalaog na tanganing direktang makagamit kaining pahina.",
        "undo-norev": "An pagliwat dae tabi magigibo nin huli ta ini bakong eksistido o pinagpura na.",
        "undo-summary": "Dae idagos an rebisyon $1 sa [[Special:Contributions/$2|$2]] ([[User talk:$2|olay]])",
        "undo-summary-username-hidden": "Dae idagos an rebisyon sa $1 nin sarong nakatago na paragamit",
-       "cantcreateaccounttitle": "Dai makagibo nin account",
        "cantcreateaccount-text": "An pagbukas nin account halì sa IP na ('''$1''') binágat ni [[User:$3|$3]].\n\n''$2'' an rason na pigtao ni $3",
        "viewpagelogs": "\nHilingon an mga katalaanan para sa pahinang ini",
        "nohistory": "Mayong paghirá nin uusipón sa pahinang ini.",
        "expand_templates_generate_xml": "Ipahiling an panlunhay na kahoy nin XML",
        "expand_templates_preview": "Patânaw",
        "mw-widgets-dateinput-placeholder-day": "TTTT-BB-AA",
-       "mw-widgets-dateinput-placeholder-month": "TTTT-BB",
-       "api-error-blacklisted": "Pakipili tabi nin sarong nalalaen, deskriptibong titulo."
+       "mw-widgets-dateinput-placeholder-month": "TTTT-BB"
 }
index 6bca06b..69ff42b 100644 (file)
        "tagline": "Зьвесткі з {{GRAMMAR:родны|{{SITENAME}}}}",
        "help": "Дапамога",
        "search": "Пошук",
+       "search-ignored-headings": " #<!-- не зьмяняйце гэты радок --> <pre>\n# Загалоўкі, якія мусіць ігнараваць пошукавы рухавік.\n# Зьмены будуць ужытыя па наступным індэксаваньні старонкі.\n# Вы можаце змусіць пераіндэксаваць старонку пустым рэдагаваньнем.\n# Сынтакс наступны:\n#   * Усё, што пачынаецца з «#» і да канца радку — камэнтар\n#   * Усякі непусты радок — загаловак, які трэба ігнараваць\nКрыніцы\nВонкавыя спасылкі\nГлядзіце таксама\n #</pre> <!-- не зьмяняйце гэты радок -->",
        "searchbutton": "Пошук",
        "go": "Старонка",
        "searcharticle": "Старонка",
        "yourpasswordagain": "Паўтарыце пароль:",
        "createacct-yourpasswordagain": "Пацьвердзіце пароль",
        "createacct-yourpasswordagain-ph": "Увядзіце пароль зноў",
-       "remembermypassword": "Запомніць мяне на гэтым кампутары (ня больш за $1 {{PLURAL:$1|дзень|дні|дзён}})",
        "userlogin-remembermypassword": "Запомніць мяне",
        "userlogin-signwithsecure": "Скарыстацца бясьпечным злучэньнем",
        "cannotloginnow-title": "Цяпер немагчыма ўвайсьці",
        "pt-createaccount": "Стварыць рахунак",
        "pt-userlogout": "Выйсьці",
        "php-mail-error-unknown": "Невядомая памылка ў PHP-функцыі mail().",
-       "user-mail-no-addy": "Спроба даслаць электронны ліст без адрасу дастаўкі",
+       "user-mail-no-addy": "Спроба даслаць электронны ліст бяз адрасу дастаўкі.",
        "user-mail-no-body": "Спроба даслаць ліст з пустым або надзвычай кароткім зьместам.",
        "changepassword": "Зьмяніць пароль",
        "resetpass_announce": "Для завяршэньня ўваходу ў сыстэму Вы мусіце ўвесьці новы пароль.",
        "botpasswords-label-update": "Абнавіць",
        "botpasswords-label-cancel": "Скасаваць",
        "botpasswords-label-delete": "Выдаліць",
-       "botpasswords-label-resetpassword": "Ð\90Ñ\87Ñ\8bÑ\81Ñ\8cÑ\86Ñ\96ць пароль",
+       "botpasswords-label-resetpassword": "СкÑ\96нÑ\83ць пароль",
        "botpasswords-label-grants": "Прыдатныя дазволы:",
        "botpasswords-help-grants": "Кожны дазвол дае доступ да правоў удзельніка, якія ўжо мае рахунак удзельніка. Глядзіце [[Special:ListGrants|табліцу дазволаў]] дзеля дадатковых зьвестак.",
        "botpasswords-label-restrictions": "Абмежаваньні на выкарыстаньне:",
        "resetpass-expired-soft": "Тэрмін дзеяньня вашага паролю скончыўся і ён патрабуе замены. Калі ласка, абярыце новы пароль цяпер або націсьніце «{{int:authprovider-resetpass-skip-label}}», каб зьмяніць яго пазьней.",
        "resetpass-validity-soft": "Ваш пароль зьяўляецца некарэктным: $1\n\nКалі ласка, абярыце зараз новы пароль або націсьніце «{{int:authprovider-resetpass-skip-label}}», каб скінуць яго пазьней.",
        "passwordreset": "Ачыстка паролю",
-       "passwordreset-text-one": "Запоўніце гэтую форму, каб скінуць пароль.",
+       "passwordreset-text-one": "Запоўніце гэтую форму, каб атрымаць часовы пароль электроннай поштай.",
        "passwordreset-text-many": "{{PLURAL:$1|Запоўніце адно з палёў, каб атрымаць часовы пароль праз электронную пошту.}}",
-       "passwordreset-disabled": "Ð\9cагÑ\87Ñ\8bмаÑ\81Ñ\8cÑ\86Ñ\8c Ð°Ñ\87Ñ\8bÑ\81Ñ\82кÑ\96 Ð¿Ð°Ñ\80олÑ\8e Ð±Ñ\8bла Ð·Ð°Ð±Ð°Ñ\80оненаÑ\8f Ñ\9e {{GRAMMAR:меÑ\81нÑ\8b|{{SITENAME}}}}.",
-       "passwordreset-emaildisabled": "Функцыі e-mail у гэтай вікі былі адключаныя.",
+       "passwordreset-disabled": "Ð\9cагÑ\87Ñ\8bмаÑ\81Ñ\8cÑ\86Ñ\8c Ð°Ñ\87Ñ\8bÑ\81Ñ\82кÑ\96 Ð¿Ð°Ñ\80олÑ\8e Ð±Ñ\8bла Ð°Ð´ÐºÐ»Ñ\8eÑ\87анаÑ\8f Ñ\9e Ð³Ñ\8dÑ\82ай Ð²Ñ\96кÑ\96.",
+       "passwordreset-emaildisabled": "Функцыі электроннай пошты ў гэтай вікі былі адключаныя.",
        "passwordreset-username": "Імя ўдзельніка:",
        "passwordreset-domain": "Дамэн:",
-       "passwordreset-capture": "Ð\9fаказаÑ\86Ñ\8c ÐºÐ°Ð½Ñ\87аÑ\82ковы электронны ліст?",
-       "passwordreset-capture-help": "Калі Вы пазначыце гэтае поле, электронны ліст (з часовым паролем), будзе паказаны Вам як толькі ён будзе дасланы ўдзельніку.",
+       "passwordreset-capture": "Ð\9fаказаÑ\86Ñ\8c Ð²Ñ\8bнÑ\96ковы электронны ліст?",
+       "passwordreset-capture-help": "Калі Вы пазначыце гэтае поле, электронны ліст (з часовым паролем) будзе паказаны Вам, як толькі ён будзе дасланы ўдзельніку.",
        "passwordreset-email": "Адрас электроннай пошты:",
        "passwordreset-emailtitle": "Падрабязнасьці рахунку ў {{GRAMMAR:месны|{{SITENAME}}}}",
        "passwordreset-emailtext-ip": "Нехта (магчыма Вы, з IP-адрасу $1) зрабіў запыт на скіданьне вашага паролю ў {{GRAMMAR:месны|{{SITENAME}}}} ($4). {{PLURAL:$3|1=Наступны рахунак удзельніка зьвязаны|Наступныя рахункі ўдзельнікаў зьвязаныя}} з гэтым адрасам электроннай пошты:\n\n$2\n\n{{PLURAL:$3|1=Гэты часовы пароль будзе|Гэтыя часовыя паролі будуць}} дзейнічаць $5 {{PLURAL:$5|дзень|дні|дзён}}.\nЦяпер Вам неабходна ўвайсьці і выбраць новы пароль. Калі нехта іншы зрабіў гэты запыт, ці Вы ўспомнілі Ваш пачатковы пароль, які ня хочаце мяняць, Вы можаце праігнараваць гэтае паведамленьне, і працягваць выкарыстоўваць стары пароль.",
        "passwordreset-emailelement": "Імя ўдзельніка: \n$1\n\nЧасовы пароль: \n$2",
        "passwordreset-emailsentemail": "Калі гэты адрас электроннай пошты далучаны да вашага рахунку, тады будзе дасланы ліст пра скідваньне паролю.",
        "passwordreset-emailsentusername": "Калі ёсьць адрас электроннай пошты, злучаны з гэтым імем удзельніка, тады будзе дасланы ліст пра скідваньне паролю.",
-       "passwordreset-emailsent-capture": "Ліст пра скіданьне паролю быў дасланы, што паказана ніжэй.",
-       "passwordreset-emailerror-capture": "Ліст пра скіданьне паролю быў створаны і паказаны ніжэй, але не ўдалося адправіць яго {{GENDER:$2|ўдзельніку|ўдзельніцы}}: $1",
        "passwordreset-emailsent-capture2": "{{PLURAL:$1|Электронны ліст|Электронныя лісты}} скіданьня паролю {{PLURAL:$1|быў дасланы|былі дасланыя}}. {{PLURAL:$1|Імя ўдзельніка і пароль|Сьпіс імёнаў удзельнікаў і паролі}} паказаныя ніжэй.",
        "passwordreset-emailerror-capture2": "Не атрымалася даслаць {{GENDER:$2|удзельніку|удзельніцы}} ліст электроннай поштай: $1 {{PLURAL:$3|Імя ўдзельніка і пароль|Сьпіс імёнаў удзельнікаў і паролі}} паказаныя ніжэй.",
        "passwordreset-nocaller": "Мусіць быць пададзены той, хто робіць выклік",
        "passwordreset-nodata": "Не былі пададзеныя ні імя ўдзельніка, ні адрас электроннай пошты",
        "changeemail": "Зьмяніць або выдаліць адрас электроннай пошты",
        "changeemail-header": "Запоўніце гэтую форму, каб зьмяніць ваш адрас электроннай пошты. Калі вы жадаеце выдаліць адрас электроннай пошты, далучаны да вашага рахунку, пакіньце поле новага адрасу электроннай пошты пустым пры запаўненьні формы.",
-       "changeemail-passwordrequired": "Вам трэба будзе ўвесьці ваш пароль, каб пацьвердзіць гэтую зьмену.",
        "changeemail-no-info": "Для непасрэднага доступу да гэтай старонкі Вам неабходна ўвайсьці ў сыстэму.",
        "changeemail-oldemail": "Цяперашні адрас электроннай пошты:",
        "changeemail-newemail": "Новы адрас электроннай пошты:",
        "minoredit": "Гэта дробная праўка",
        "watchthis": "Назіраць за гэтай старонкай",
        "savearticle": "Захаваць старонку",
+       "savechanges": "Захаваць зьмены",
        "publishpage": "Апублікаваць старонку",
+       "publishchanges": "Апублікаваць зьмены",
        "preview": "Папярэдні прагляд",
        "showpreview": "Праглядзець",
        "showdiff": "Паказаць зьмены",
        "content-model-css": "CSS",
        "content-json-empty-object": "Пусты аб’ект",
        "content-json-empty-array": "Пусты масіў",
+       "deprecated-self-close-category": "Старонкі зь няслушнымі самазакрытымі HTML-тэгамі",
+       "deprecated-self-close-category-desc": "Старонка ўтрымлівае няслушныя самазакрытыя HTML-тэгі, такія як <code>&lt;b/></code> ці <code>&lt;span/></code>. Іх паводзіны ў хуткім часе будуць зьмененыя ў адпаведнасьці з спэцыфікацыяй HTML5, таму іх ўжываньне ў вікітэксьце лічыцца састарэлым.",
        "duplicate-args-warning": "<strong>Папярэджаньне:</strong> [[:$1]] выклікае [[:$2]] з больш чым адным значэньнем парамэтру «$3». Толькі апошняе з пададзеных значэньняў будзе ўжытае.",
        "duplicate-args-category": "Старонкі, на якіх у шаблёнах выкарыстоўваюцца парамэтры-дублікаты",
        "duplicate-args-category-desc": "Старонкі, якія ўтрымліваюць шаблёны з парамэтрамі-дублікатамі, напрыклад, <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> або <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Выглядае, што праўка ўжо была адмененая.",
        "undo-summary": "Скасаваньне праўкі $1 {{GENDER:$2|удзельніка|удзельніцы}} [[Special:Contributions/$2|$2]] ([[User talk:$2|гутаркі]])",
        "undo-summary-username-hidden": "Вэрсія $1 скасаваная схаваным удзельнікам",
-       "cantcreateaccounttitle": "Немагчыма стварыць рахунак",
        "cantcreateaccount-text": "Стварэньне рахункаў з гэтага IP-адрасу ('''$1''') было заблякаванае [[User:$3|$3]].\n\nПрычына блякаваньня пададзеная $3: ''$2''",
        "cantcreateaccount-range-text": "Стварэньне рахункаў з IP-адрасоў у дыяпазоне <strong>$1</strong>, у які ўваходзіць ваш IP-адрас (<strong>$4</strong>), было забароненае {{GENDER:$3|ўдзельнікам|ўдзельніцай}} [[User:$3|$3]].\n\n{{GENDER:$3|Удзельнікам|Удзельніцай}} $3 была пададзеная наступная прычына: <em>$2</em>.",
        "viewpagelogs": "Паказаць журналы падзеяў для гэтай старонкі",
        "grant-group-high-volume": "Выкананьне дзеяньняў з высокай інтэнсіўнасьцю",
        "grant-group-customization": "Налады і перавагі",
        "grant-group-administration": "Выкананьне адміністрацыйных дзеяньняў",
+       "grant-group-private-information": "Доступ да прыватных зьвестак пра вас",
        "grant-group-other": "Розная актыўнасьць",
        "grant-blockusers": "Блякаваньне і разблякаваньне ўдзельнікаў",
        "grant-createaccount": "Стварыць рахункі",
        "grant-highvolume": "Рэдагаваньне з высокай інтэнсіўнасьцю",
        "grant-oversight": "Хаваньне ўдзельнікаў і вэрсіяў старонак",
        "grant-patrol": "Патруляваньне зьменаў старонак",
+       "grant-privateinfo": "Доступ да прыватных зьвестак",
        "grant-protect": "Абарона і зьняцьце абароны старонак",
        "grant-rollback": "Адкат зьменаў старонак",
        "grant-sendemail": "Адпраўка лістоў электроннай пошты іншым удзельнікам",
        "action-applychangetags": "дадаваньне метак пры рэдагаваньні",
        "action-changetags": "дадаваньне і выдаленьне адвольных метак да асобных вэрсіяў і запісаў у журнале падзеяў",
        "action-deletechangetags": "выдаленьне метак з базы зьвестак",
+       "action-purge": "ачыстку кэшу гэтай старонкі",
        "nchanges": "$1 {{PLURAL:$1|зьмена|зьмены|зьменаў}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|з апошняга візыту}}",
        "enhancedrc-history": "гісторыя",
        "file-thumbnail-no": "Назва файла пачынаецца з <strong>$1</strong>.\nВерагодна гэта паменшаная копія выявы ''(мініятура)''.\nКалі Вы маеце гэтую выяву ў поўным памеры, загрузіце яе, альбо зьмяніце назву файла.",
        "fileexists-forbidden": "Файл з такой назвай ужо існуе і ня можа быць перапісаны.\nКалі ласка, вярніцеся назад і загрузіце гэты файл з новай назвай. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Файл з такой назвай ужо існуе ў агульным сховішчы файлаў.\nКалі Вы жадаеце загрузіць Ваш файл, вярніцеся назад і загрузіце гэты файл з новай назвай. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Гэтая загрузка зьяўляецца дакладнай копіяй цяперашняй вэрсіі <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Гэтая загрузка зьяўляецца дакладнай копіяй {{PLURAL:$2|1=старой вэрсіі|старых вэрсіяў}} файлу <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Гэты файл дублюе {{PLURAL:$1|1=наступны файл|наступныя файлы}}:",
        "file-deleted-duplicate": "Падобны файл ([[:$1]]) ужо выдаляўся. Калі ласка, паглядзіце гісторыю выдаленьняў гэтага файла перад яго паўторнай загрузкай.",
        "file-deleted-duplicate-notitle": "Файл, ідэнтычны гэтаму файлу, раней ужо быў выдалены, а назва файла была забароненая.\nВам трэба зьвярнуцца да некага з правамі прагляду зьвестак забароненых файлаў, каб прааналізаваць сытуацыю перад тым, як загружаць файл ізноў.",
        "uploadstash-errclear": "Не атрымалася ачысьціць файлы.",
        "uploadstash-refresh": "Абнавіць сьпіс файлаў.",
        "uploadstash-thumbnail": "прагляд мініятуры",
+       "uploadstash-exception": "Не магу захаваць загрузку ў сховішчы ($1): «$2».",
        "invalid-chunk-offset": "Няслушнае зрушэньне фрагмэнту",
        "img-auth-accessdenied": "Доступ забаронены",
        "img-auth-nopathinfo": "Адсутнічае PATH_INFO.\nВаш сэрвэр не ўстаноўлены на пропуск гэтай інфармацыі.\nМагчма, ён працуе праз CGI і не падтрымлівае img_auth.\nГлядзіце https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "filerevert-submit": "Вярнуць",
        "filerevert-success": "'''[[Media:$1|$1]]''' быў вернуты да [вэрсіі $4 ад $3, $2].",
        "filerevert-badversion": "Не існуе папярэдняй лякальнай вэрсіі гэтага файла з пазначанай датай.",
+       "filerevert-identical": "Цяперашняя вэрсія файлу ўжо ідэнтычная абранай.",
        "filedelete": "Выдаліць $1",
        "filedelete-legend": "Выдаліць файл",
        "filedelete-intro": "Вы выдаляеце файл '''[[Media:$1|$1]]''' з усёй яго гісторыяй.",
        "listgrouprights-namespaceprotection-namespace": "Прастора назваў",
        "listgrouprights-namespaceprotection-restrictedto": "Правы, якія дазваляюць удзельніку рэдагаваць",
        "listgrants": "Дазволы",
+       "listgrants-summary": "Ніжэй прыведзены сьпіс дазволаў і адпаведных правоў карыстальнікаў, да якіх яны даюць доступ. Удзельнікі могуць дазваляць праграмам ужываць іх рахунак, але з абмежаванымі правамі, якія грунтуюцца на дазволах, дадзеных удзельнікам. Праграмы, якія дзейнічаюць ад імя ўдзельніка, ня могуць карыстацца правамі, якіх ня мае ўдзельнік. Пра асобныя правы можна атрымаць [[{{MediaWiki:Listgrouprights-helppage}}|дадатковую інфармацыю]].",
        "listgrants-grant": "Дазвол",
        "listgrants-rights": "Правы",
        "trackingcategories": "Катэгорыі, якія патрабуюць увагі",
        "trackingcategories-msg": "Катэгорыя, якая патрабуе ўвагі",
        "trackingcategories-name": "Назва паведамленьня",
        "trackingcategories-desc": "Крытэр уключэньня ў катэгорыю",
+       "restricted-displaytitle-ignored": "Старонкі, дзе ігнаруюцца назвы для адлюстраваньня",
+       "restricted-displaytitle-ignored-desc": "Старонка ігнаруе <code><nowiki>{{DISPLAYTITLE}}</nowiki></code>, бо ён не супадае зь цяперашняй назвай старонкі.",
        "noindex-category-desc": "Гэтая старонка не індэксуецца пошукавымі робатамі, таму што на ёй маецца магічнае слова <code><nowiki>__NOINDEX__</nowiki></code>, а старонка знаходзіцца ў прасторы назваў, дзе дазволны гэты сьцяг.",
        "index-category-desc": "На старонцы знаходзіцца магічнае слова <code><nowiki>__INDEX__</nowiki></code> (пры гэтым старонка знаходзіцца ў прасторы назваў, дзе дазволены гэты сьцяг), таму яна індэксуецца пошукавымі робатамі ў тых выпадках, калі звычайна гэтага не адбываецца.",
        "post-expand-template-inclusion-category-desc": "Памер старонкі перавысіў <code>$wgMaxArticleSize</code> пасьля разгортваньня ўсіх шаблёнаў, таму некаторыя шаблёны не былі паказаныя цалкам.",
        "watchnologin": "Вы не ўвайшлі ў сыстэму",
        "addwatch": "Дадаць ў сьпіс назіраньня",
        "addedwatchtext": "Старонка «[[:$1]]» і яе старонка абмеркаваньня былі дададзеная да Вашага [[Special:Watchlist|сьпісу назіраньня]].",
+       "addedwatchtext-talk": "«[[:$1]]» і зьвязаная зь ёй старонка дададзеныя да вашага [[Special:Watchlist|сьпісу назіраньня]].",
        "addedwatchtext-short": "Старонка «$1» была дададзеная ў ваш сьпіс назіраньня.",
        "removewatch": "Выдаліць са сьпісу назіраньня",
        "removedwatchtext": "Старонка «[[:$1]]» і яе старонка абмеркаваньня былі выдаленыя з Вашага [[Special:Watchlist|сьпісу назіраньня]].",
+       "removedwatchtext-talk": "«[[:$1]]» і зьвязаная зь ёй старонка выдаленыя з вашага [[Special:Watchlist|сьпісу назіраньня]].",
        "removedwatchtext-short": "Старонка «$1» была выдаленая з вашага сьпісу назіраньня.",
        "watch": "Назіраць",
        "watchthispage": "Назіраць за гэтай старонкай",
        "rollbacklinkcount": "адкаціць $1 {{PLURAL:$1|рэдагаваньне|рэдагаваньні|рэдагаваньняў}}",
        "rollbacklinkcount-morethan": "адкаціць больш за $1 {{PLURAL:$1|рэдагаваньне|рэдагаваньні|рэдагаваньняў}}",
        "rollbackfailed": "Памылка адкату",
+       "rollback-missingparam": "У запыце адсутнічаюць абавязковыя парамэтры.",
+       "rollback-missingrevision": "Не атрымалася загрузіць зьвесткі вэрсіі.",
        "cantrollback": "Немагчыма адкаціць зьмену; апошні рэдактар — адзіны аўтар гэтай старонкі.",
        "alreadyrolled": "Немагчыма адкаціць апошнюю зьмену [[:$1]], якую {{GENDER:$2|зрабіў|зрабіла}} [[User:$2|$2]] ([[User talk:$2|гутаркі]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); нехта іншы ўжо зьмяніў старонку альбо адкаціў зьмены.\n\nАпошнія зьмены зробленыя [[User:$3|$3]] ([[User talk:$3|гутаркі]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Кароткае апісаньне зьменаў было: <em>$1</em>.",
        "revertpage": "Рэдагаваньні [[Special:Contributions/$2|$2]] ([[User talk:$2|гутаркі]]) скасаваныя да папярэдняй вэрсіі [[User:$1|$1]]",
        "revertpage-nouser": "Рэдагаваньні схаванага ўдзельніка скасаваныя да папярэдняй вэрсіі {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Адмененыя рэдагаваньні $1;\nвернутая папярэдняя вэрсія $2.",
+       "rollback-success-notify": "Адмененыя праўкі $1;\nвернутая папярэдняя вэрсія $2. [$3 Паказаць зьмены]",
        "sessionfailure-title": "Памылка сэсіі",
        "sessionfailure": "Магчыма ўзьніклі праблемы ў Вашым цяперашнім сэансе працы;\nгэта дзеяньне было скасавана для прадухіленьня перахопу сэансу.\nКалі ласка, націсьніце «назад» і перазагрузіце старонку, зь якой Вы прыйшлі, і паспрабуйце ізноў.",
        "changecontentmodel": "Зьмена мадэлі зьместу старонкі",
        "changecontentmodel-success-text": "Тып зьместу [[:$1]] быў зьменены.",
        "changecontentmodel-cannot-convert": "Зьмест [[:$1]] ня можа быць ператвораны ў тып $2.",
        "changecontentmodel-nodirectediting": "Мадэль зьместу $1 не падтрымлівае наўпроставае рэдагаваньне",
+       "changecontentmodel-emptymodels-title": "Няма даступных мадэляў зьместу",
+       "changecontentmodel-emptymodels-text": "Зьмест на [[:$1]] ня можа быць ператвораны ні ў які тып.",
        "log-name-contentmodel": "Журнал зьменаў мадэляў зьместу",
        "log-description-contentmodel": "Падзеі, зьвязаныя з мадэлямі зьместу старонак",
+       "logentry-contentmodel-new": "$1 {{GENDER:$2|стварыў|стварыла}} старонку $3 з дапамогай нестандартнай мадэлі зьместу «$5»",
        "logentry-contentmodel-change": "$1 {{GENDER:$2|зьмяніў|зьмяніла}} мадэль зьместу старонкі $3 з «$4» на «$5»",
        "logentry-contentmodel-change-revertlink": "адкаціць",
        "logentry-contentmodel-change-revert": "адкат",
        "undeletehistorynoadmin": "Гэтая старонка была выдаленая.\nПрычына выдаленьня пададзена ніжэй, разам са зьвесткамі ўдзельніка, які рэдагаваў старонку перад выдаленьнем.\nТэкст выдаленай старонкі могуць глядзець толькі адміністратары.",
        "undelete-revision": "Выдаленая вэрсія $1 (ад $5 $4) ўдзельніка $3:",
        "undeleterevision-missing": "Некарэктная ці неіснуючая вэрсія.\nВерагодна Вы карысталіся няслушнай спасылкай, альбо, магчыма, вэрсія была выдаленая з архіву.",
+       "undeleterevision-duplicate-revid": "$1 {{PLURAL:$1|вэрсія ня можа быць адноўленая|вэрсіі ня могуць быць адноўленыя|вэрсіяў ня могуць быць адноўленыя}}, бо {{PLURAL:$1|1=яе|іх}} <code>rev_id</code> ужо выкарыстоўваецца.",
        "undelete-nodiff": "Папярэдняя вэрсія ня знойдзеная.",
        "undeletebtn": "Аднавіць",
        "undeletelink": "паглядзець/аднавіць",
        "undeletedrevisions": "{{PLURAL:$1|адноўленая $1 вэрсія|адноўленыя $1 вэрсіі|адноўленыя $1 вэрсіяў}}",
        "undeletedrevisions-files": "адноўленыя $1 {{PLURAL:$1|вэрсія|вэрсіі|вэрсіяў}} і $2 {{PLURAL:$2|файл|файлы|файлаў}}",
        "undeletedfiles": "{{PLURAL:$1|адноўлены $1 файл|адноўленыя $1 файлы|адноўленыя $1 файлаў}}",
-       "cannotundelete": "Ð\9fамÑ\8bлка Ð°Ð´Ð½Ð°Ñ\9eленÑ\8cня:\n$1",
+       "cannotundelete": "Ð\9dекаÑ\82оÑ\80Ñ\8bÑ\8f Ð°Ð±Ð¾ Ñ\9eÑ\81е Ð°Ð´Ð½Ð°Ñ\9eленÑ\8cнÑ\96 Ð½Ðµ Ð±Ñ\8bлÑ\96 Ð²Ñ\8bкананÑ\8bя:\n$1",
        "undeletedpage": "'''Старонка $1 была адноўленая'''\n\nГлядзіце [[Special:Log/delete|журнал выдаленьняў]] для прагляду апошніх выдаленьняў і аднаўненьняў.",
        "undelete-header": "Глядзіце [[Special:Log/delete|журнал выдаленьняў]] для прагляду апошніх выдаленьняў.",
        "undelete-search-title": "Пошук выдаленых старонак",
        "sp-contributions-newbies-sub": "Унёсак пачынаючых",
        "sp-contributions-newbies-title": "Унёсак удзельнікаў з новых рахункаў",
        "sp-contributions-blocklog": "журнал блякаваньняў",
-       "sp-contributions-suppresslog": "выдалены ўнёсак удзельніка",
-       "sp-contributions-deleted": "выдалены ўнёсак удзельніка",
+       "sp-contributions-suppresslog": "выдалены ўнёсак {{GENDER:$1|удзельніка|удзельніцы}}",
+       "sp-contributions-deleted": "выдалены ўнёсак {{GENDER:$1|удзельніка|удзельніцы}}",
        "sp-contributions-uploads": "загрузкі",
        "sp-contributions-logs": "журналы падзеяў",
        "sp-contributions-talk": "гутаркі",
        "sp-contributions-username": "IP-адрас альбо імя ўдзельніка/ўдзельніцы:",
        "sp-contributions-toponly": "Паказваць толькі зьмены, якія зьяўляюцца апошнімі вэрсіямі",
        "sp-contributions-newonly": "Паказваць толькі праўкі, якімі былі створаныя старонкі",
+       "sp-contributions-hideminor": "Схаваць дробныя праўкі",
        "sp-contributions-submit": "Шукаць",
        "whatlinkshere": "Спасылкі на старонку",
        "whatlinkshere-title": "Старонкі, якія спасылаюцца на $1",
        "ipb-unblock": "Разблякаваць рахунак ўдзельніка ці IP-адрас",
        "ipb-blocklist": "Паказаць існуючыя блякаваньні",
        "ipb-blocklist-contribs": "Унёсак $1",
+       "ipb-blocklist-duration-left": "засталося $1",
        "unblockip": "Разблякаваць удзельніка",
        "unblockiptext": "Карыстайцеся пададзенай ніжэй формай для аднаўленьня магчымасьці рэдагаваньня для раней заблякаванага IP-адрасу альбо рахунку ўдзельніка.",
        "ipusubmit": "Зьняць гэта блякаваньне",
        "block-log-flags-hiddenname": "імя ўдзельніка схаванае",
        "range_block_disabled": "Адміністратарам забаронена блякаваць дыяпазоны.",
        "ipb_expiry_invalid": "Няслушны тэрмін блякаваньня.",
+       "ipb_expiry_old": "Час сканчэньня ўжо мінуў.",
        "ipb_expiry_temp": "Блякаваньні са схаваньнем імя ўдзельніка павінны быць бестэрміновымі.",
        "ipb_hide_invalid": "Немагчыма схаваць гэты рахунак; зь яго зроблена больш чым {{PLURAL:$1|$1 рэдагаваньне|$1 рэдагаваньні|$1 рэдагаваньняў}}.",
        "ipb_already_blocked": "«$1» ужо заблякаваны",
        "lockdbsuccesstext": "База зьвестак была заблякаваная.<br />\nНе забудзьцеся [[Special:UnlockDB|зьняць блякаваньне]] пасьля сканчэньня абслугоўваньня.",
        "unlockdbsuccesstext": "База зьвестак была разблякаваная.",
        "lockfilenotwritable": "Немагчыма запісаць у файл блякаваньняў базы зьвестак.\nБлякаваньне ці разблякаваньне базы зьвестак патрабуе, каб вэб-сэрвэр меў дазвол на запіс у гэты файл.",
+       "databaselocked": "База зьвестак ужо заблякаваная.",
        "databasenotlocked": "База зьвестак не заблякаваная.",
        "lockedbyandtime": "($1 $2 у $3)",
        "move-page": "Перанесьці $1",
        "tooltip-ca-nstab-category": "Паказаць старонку катэгорыі",
        "tooltip-minoredit": "Пазначыць гэтую зьмену як дробную",
        "tooltip-save": "Захаваць Вашы зьмены",
+       "tooltip-publish": "Апублікаваць вашыя зьмены",
        "tooltip-preview": "Праглядзець Вашы зьмены. Калі ласка, выкарыстоўвайце гэтую магчымасьць перад тым, як захаваць старонку!",
        "tooltip-diff": "Паказаць зробленыя Вамі зьмены ў тэксьце",
        "tooltip-compareselectedversions": "Пабачыць розьніцу паміж дзьвюма абранымі вэрсіямі гэтай старонкі.",
        "pageinfo-article-id": "Ідэнтыфікатар старонкі",
        "pageinfo-language": "Мова зьместу старонкі",
        "pageinfo-content-model": "Мадэль зьместу старонкі",
+       "pageinfo-content-model-change": "зьмяніць",
        "pageinfo-robot-policy": "Індэксацыя пашукавікамі",
        "pageinfo-robot-index": "Дазволеная",
        "pageinfo-robot-noindex": "Не дазволеная",
        "confirmemail_body_set": "Нехта, магчыма Вы, з IP-адраса $1,\nустанавіў адрас электроннай пошты для рахунку «$2» у {{GRAMMAR:месны|{{SITENAME}}}}.\n\nКаб пацьвердзіць, што гэты рахунак сапраўды належыць Вам, і каб актывізаваць падтрымку электроннай пошты ў {{GRAMMAR:месны|{{SITENAME}}}}, адкрыйце гэтую спасылку у Вашым браўзэры:\n\n$3\n\nКалі рахунак Вам *не* належыць, перайдзіце па гэтай спасылцы\nкаб скасаваць пацьверджаньне па электроннай пошце:\n\n$5\n\nГэты код пацьверджаньня дзейнічае да $4.",
        "confirmemail_invalidated": "Пацьверджаньне адрасу электроннай пошты адмененае",
        "invalidateemail": "Скасаваць пацьверджаньне адрасу электроннай пошты",
+       "notificationemail_subject_changed": "Адрас электроннай пошты на сайце {{SITENAME}} быў зьменены",
+       "notificationemail_subject_removed": "Адрас электроннай пошты на сайце {{SITENAME}} быў выдалены",
+       "notificationemail_body_changed": "Некім, магчыма вамі, з IP-адрасу $1,\nбыў зьменены адрас электроннай пошты «$2» на «$3» на сайце {{SITENAME}}.\n\nКалі гэта былі ня вы, неадкладна зьвяжыцеся з адміністратарам.",
        "scarytranscludedisabled": "[Улучэньне інтэрвікі было адключанае]",
        "scarytranscludefailed": "[Памылка атрыманьня шаблёну $1]",
        "scarytranscludefailed-httpstatus": "[Памылка атрыманьня шаблёну $1: HTTP $2]",
        "confirm-watch-top": "Дадаць гэтую старонку ў Ваш сьпіс назіраньня?",
        "confirm-unwatch-button": "Добра",
        "confirm-unwatch-top": "Выдаліць гэтую старонку з Вашага сьпісу назіраньня?",
+       "confirm-rollback-button": "Так",
+       "confirm-rollback-top": "Адкаціць праўкі на гэтай старонцы?",
        "quotation-marks": "«$1»",
        "imgmultipageprev": "← папярэдняя старонка",
        "imgmultipagenext": "наступная старонка →",
        "timezone-local": "Мясцовы",
        "duplicate-defaultsort": "Папярэджаньне: Ключ сартыроўкі па змоўчваньні «$2» замяняе папярэдні ключ сартыроўкі па змоўчваньні «$1».",
        "duplicate-displaytitle": "<strong>Папярэджаньне:</strong> назва для адлюстраваньня «$2» перапісвае ранейшую назву для адлюстраваньня «$1».",
+       "restricted-displaytitle": "<strong>Увага:</strong> назва для адлюстраваньня «$1» была праігнараваная, бо яна не супадае зь цяперашняй назвай старонкі.",
        "invalid-indicator-name": "<strong>Памылка:</strong> атрыбут <code>name</code> індыкатараў статусу старонкі ня мусіць быць пустым.",
        "version": "Вэрсія",
        "version-extensions": "Усталяваныя пашырэньні",
        "redirect-page": "Ідэнтыфікатар старонкі",
        "redirect-revision": "Вэрсіі старонкі",
        "redirect-file": "Імя файла",
+       "redirect-logid": "ID журнала",
        "redirect-not-exists": "Значэньне ня знойдзена",
        "fileduplicatesearch": "Пошук дублікатаў файлаў",
        "fileduplicatesearch-summary": "Пошук аднолькавых файлаў на падставе іх хэшаў.",
        "tags-delete-not-found": "Метка «$1» не існуе.",
        "tags-delete-too-many-uses": "Метка «$1» выкарыстаная ў больш чым $2 {{PLURAL:$2|вэрсіі|вэрсіях}}, адпаведна, яна ня можа быць выдаленая.",
        "tags-delete-warnings-after-delete": "Метка «$1» была выдаленая, але {{PLURAL:$2|1=атрыманае наступнае папярэджаньне|атрыманыя наступныя папярэджаньні}}:",
+       "tags-delete-no-permission": "У вас няма дазволу на выдаленьне метак зьменаў.",
        "tags-activate-title": "Актываваць метку",
        "tags-activate-question": "Вы зьбіраецеся актываваць метку «$1».",
        "tags-activate-reason": "Прычына:",
        "feedback-useragent": "Агент удзельніка:",
        "searchsuggest-search": "Пошук",
        "searchsuggest-containing": "утрымлівае...",
+       "api-error-autoblocked": "Ваш IP-адрас быў аўтаматычна заблякаваны, бо ён быў выкарыстаны заблякаваным удзельнікам.",
        "api-error-badaccess-groups": "У Вас няма дазволу загружаць файлы ў гэтую вікі.",
        "api-error-badtoken": "Унутраная памылка: няслушны ключ.",
+       "api-error-blocked": "Рэдагаваньне было для вас заблякаванае.",
        "api-error-copyuploaddisabled": "Загрузка з URL-адрасу забароненая на гэтым сэрвэры.",
        "api-error-duplicate": "На сайце ўжо {{PLURAL:$1|1=існуе іншы файл|існуюць іншыя файлы}} з такім жа зьместам.",
        "api-error-duplicate-archive": "Раней на сайце {{PLURAL:$1|1=быў файл|былі файлы}} з дакладна такім жа зьместам, але {{PLURAL:$1|1=ён быў выдалены|яны былі выдаленыя}}.",
        "api-error-nomodule": "Унутраная памылка: ня выбраны модуль загрузкі.",
        "api-error-ok-but-empty": "Унутраная памылка: няма адказу ад сэрвэра.",
        "api-error-overwrite": "Замена існуючага файла забароненая.",
+       "api-error-ratelimited": "Вы спрабуеце загрузіць за кароткі час болей файлаў, чым дазваляе вікі.\nКалі ласка, паспрабуйце яшчэ раз празь некалькі хвілінаў.",
        "api-error-stashfailed": "Унутраная памылка: сэрвэр ня змог захаваць часовы файл.",
        "api-error-publishfailed": "Унутраная памылка: сэрвэр ня змог захаваць часловы файл.",
        "api-error-stasherror": "Падчас загрузкі файла ў сховішча адбылася памылка.",
        "api-error-unknownerror": "Невядомая памылка: «$1».",
        "api-error-uploaddisabled": "Загрузка ў гэтую вікі адключаная.",
        "api-error-verification-error": "Гэты файл можа быць пашкоджаны, ці мае няслушнае пашырэньне.",
+       "api-error-was-deleted": "Файл з такой назвай ужо загружаўся раней і быў потым выдалены.",
        "duration-seconds": "$1 {{PLURAL:$1|сэкунда|сэкунды|сэкундаў}}",
        "duration-minutes": "$1 {{PLURAL:$1|хвіліна|хвіліны|хвілінаў}}",
        "duration-hours": "$1 {{PLURAL:$1|гадзіна|гадзіны|гадзінаў}}",
        "expand_templates_preview": "Папярэдні прагляд",
        "expand_templates_preview_fail_html": "<em>Праз тое што {{SITENAME}} мае ўключаным неапрацаваны HTML і адбылася страта зьвестак сэсіі, папярэдні прагляд схаваны, як засьцярога ад атакаў з дапамогай JavaScript.</em>\n\n<strong>Калі гэта слушная спроба перадпрагляду, калі ласка, паспрабуйце яшчэ раз.</strong>\nКалі гэта не спрацуе, паспрабуйце [[Special:UserLogout|выйсьці]] і ўвайсьці яшчэ раз, а таксама праверце, што ваш браўзэр дазваляе кукі-файлы з гэтага сайту.",
        "expand_templates_preview_fail_html_anon": "<em>Праз тое што {{SITENAME}} мае ўключаным сыры HTML і вы не ўвайшлі ў сыстэму, папярэдні прагляд схаваны, як засьцярога ад атакаў з дапамогай JavaScript.</em>\n\n<strong>Калі гэта слушная спроба перадпрагляду, калі ласка, [[Special:UserLogin|увайдзіце ў сыстэму]] і паспрабуйце яшчэ раз.</strong>",
+       "expand_templates_input_missing": "Вам трэба ўвесьці хоць нейкі тэкст.",
        "pagelanguage": "Зьмена мовы старонкі",
        "pagelang-name": "Старонка",
        "pagelang-language": "Мова",
        "pagelang-use-default": "Ужываць мову па змоўчаньні",
        "pagelang-select-lang": "Абярыце мову",
+       "pagelang-submit": "Адправіць",
        "right-pagelang": "Зьмяніць мову старонкі",
        "action-pagelang": "зьмену мовы старонкі",
        "log-name-pagelang": "Журнал зьменаў мовы",
        "mediastatistics": "Статыстыка мэдыяфайлаў",
        "mediastatistics-summary": "Статыстыка тыпаў загружаных файлаў. Яна ўключае толькі актуальныя вэрсіі файлаў. Старыя і выдаленыя вэрсіі ня ўлічваюцца.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 байт|$1 байты|$1 байтаў}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Агульны памер файлу для гэтага разьдзелу: {{PLURAL:$1|$1 байт|$1 байты|$1 байтаў}} ($2; $3%).",
+       "mediastatistics-allbytes": "Агульны памер усіх файлаў: {{PLURAL:$1|$1 байт|$1 байты|$1 байтаў}} ($2).",
        "mediastatistics-table-mimetype": "MIME-тып",
        "mediastatistics-table-extensions": "Магчымыя пашырэньні",
        "mediastatistics-table-count": "Колькасьць файлаў",
        "mediastatistics-header-text": "Тэкст",
        "mediastatistics-header-executable": "Выконваныя файлы",
        "mediastatistics-header-archive": "Сьціснутыя фарматы",
+       "mediastatistics-header-total": "Усе файлы",
        "json-warn-trailing-comma": "$1 {{PLURAL:$1|залішняя коска ў канцы была выдаленая|залішнія коскі ў канцы былі выдаленыя|залішніх косак у канцы былі выдаленыя}} з JSON",
        "json-error-unknown": "Узьнікла праблема з JSON. Памылка: $1",
        "json-error-depth": "Перавышаная максымальная глыбіня стэку",
        "special-characters-group-ipa": "МФА (IPA)",
        "special-characters-group-symbols": "Сымбалі",
        "special-characters-group-greek": "Грэцкія",
+       "special-characters-group-greekextended": "Пашыраная грэцкая",
        "special-characters-group-cyrillic": "Кірылічныя",
        "special-characters-group-arabic": "Арабскія",
        "special-characters-group-arabicextended": "Арабскія пашыраныя",
        "mw-widgets-dateinput-placeholder-month": "ГГГГ-ММ",
        "mw-widgets-titleinput-description-new-page": "старонка яшчэ не існуе",
        "mw-widgets-titleinput-description-redirect": "перанакіраваньне на $1",
-       "api-error-blacklisted": "Калі ласка, выбярыце іншую, апісальную назву.",
-       "randomrootpage": "Выпадковая карэнная старонка"
+       "sessionmanager-tie": "Немагчыма выкарыстаць адначасова некалькі тыпаў аўтэнтыфікацыі: $1.",
+       "sessionprovider-generic": "$1 сэсіі",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "сэсіі на падставе файлаў-кукі",
+       "sessionprovider-nocookies": "Файлы-кукі могуць быць адключаныя. Упэўніцеся, што ў вас уключаныя файлы-кукі і пачніце спачатку.",
+       "randomrootpage": "Выпадковая карэнная старонка",
+       "log-action-filter-block": "Тып блякаваньня:",
+       "log-action-filter-delete": "Тып выдаленьня:",
+       "log-action-filter-import": "Тып імпарту:",
+       "log-action-filter-move": "Тып пераносу:",
+       "log-action-filter-all": "Усе",
+       "log-action-filter-block-block": "Заблякаваць",
+       "log-action-filter-block-reblock": "Зьмяненьне блякаваньня",
+       "log-action-filter-block-unblock": "Разблякаваць",
+       "log-action-filter-contentmodel-change": "Зьмена мадэлі зьместу",
+       "log-action-filter-delete-delete": "Выдаленьне старонкі",
+       "log-action-filter-delete-restore": "Аднаўленьне старонкі",
+       "log-action-filter-delete-event": "Выдаленьне журналу",
+       "log-action-filter-delete-revision": "Выдаленьне вэрсіі",
+       "log-action-filter-managetags-create": "Стварэньне метак",
+       "log-action-filter-managetags-delete": "Выдаленьне метак",
+       "log-action-filter-managetags-activate": "Актывацыя метак",
+       "log-action-filter-managetags-deactivate": "Дэактывацыя метак",
+       "log-action-filter-newusers-autocreate": "Аўтаматычнае стварэньне",
+       "log-action-filter-patrol-autopatrol": "Аўтаматычнае патруляваньне",
+       "log-action-filter-protect-protect": "Абарона",
+       "log-action-filter-protect-unprotect": "Зьняцьце абароны",
+       "log-action-filter-rights-autopromote": "Аўтаматычнае зьмяненьне",
+       "log-action-filter-upload-upload": "Новая загрузка",
+       "authmanager-realname-label": "Сапраўднае імя",
+       "authmanager-provider-temporarypassword": "Часовы пароль",
+       "changecredentials": "Зьмена ўліковых зьвестак",
+       "removecredentials": "Выдаленьне ўліковых зьвестак",
+       "removecredentials-submit": "Выдаліць уліковыя зьвесткі",
+       "credentialsform-provider": "Тып уліковых зьвестак:",
+       "credentialsform-account": "Назва рахунку:"
 }
index fcf7f32..a30b31a 100644 (file)
        "tagline": "З пляцоўкі {{SITENAME}}",
        "help": "Даведка",
        "search": "Знайсці",
+       "search-ignored-headings": " #<!-- не змяняйце гэты радок --> <pre>\n# Загалоўкі, якія будзе ігнараваць рухавік пошуку.\n# Змены набудуць моц па наступным індэксаванні старонкі.\n# Вы можаце змусіць пераіндэксаванне старонкі, зрабіўшы пустое рэдагаванне.\n# Сінтаксіс наступны:\n#   * Усё ад сімвала \"#\" да канца радка - каментарый.\n#   * Кожны непусты радок - дакладны загаловак, які трэба ігнараваць, з рэгістрам і інш.\nКрыніцы\nСпасылкі\nГл. таксама\n #</pre> <!-- не змяняйце гэты радок -->",
        "searchbutton": "Знайсці",
        "go": "Пераход",
        "searcharticle": "Артыкул",
        "yourpasswordagain": "Паўтарыце пароль:",
        "createacct-yourpasswordagain": "Пацвердзіце пароль",
        "createacct-yourpasswordagain-ph": "Увядзіце пароль яшчэ раз",
-       "remembermypassword": "Памятаць мяне на гэтым камп'ютары (не даўжэй за $1 {{PLURAL:$1|дзень|дні|дзён}})",
        "userlogin-remembermypassword": "Заставацца ў сістэме",
        "userlogin-signwithsecure": "Выкарыстоўваць абароненае злучэнне",
        "cannotloginnow-title": "Зараз немагчыма ўвайсці",
        "password-change-forbidden": "Вы не можаце змяняць паролі на гэтай Вікі.",
        "externaldberror": "Або памылка вонкавай аўтэнтыкацыі ў базе дадзеных, або вам не дазволена абнаўляць свой вонкавы рахунак.",
        "login": "Увайсці ў сістэму",
+       "login-security": "Праверыць вашу асобу",
        "nav-login-createaccount": "Увайсці ў сістэму / стварыць рахунак",
        "userlogin": "Увайсці ў сістэму / стварыць рахунак",
        "userloginnocreate": "Увайсці",
        "userlogin-resetpassword-link": "Забылі пароль?",
        "userlogin-helplink2": "Даведка па прадстаўленні сістэме",
        "userlogin-loggedin": "Вы ўжо ўвайшлі ў сістэму як {{GENDER:$1|$1}}.\nСкарыстайцеся формай ніжэй, каб прадставіцца сістэме як іншы ўдзельнік.",
+       "userlogin-reauth": "Вы павінны ўвайсці ў сістэму зноў, каб пераканацца, што Вы {{GENDER:$1|$1}}",
        "userlogin-createanother": "Стварыць яшчэ адзін уліковы запіс",
        "createacct-emailrequired": "Адрас электроннай пошты",
        "createacct-emailoptional": "Адрас электроннай пошты (неабавязкова)",
        "createacct-email-ph": "Увядзіце ваш адрас электроннай пошты",
        "createacct-another-email-ph": "Увядзіце адрас электроннай пошты",
        "createaccountmail": "Ужыць часовы выпадковы пароль і даслаць яго праз эл.пошту",
+       "createaccountmail-help": "Можа быць выкарыстаны для стварэння ўліковага запісу для іншага чалавека без пазнання пароля.",
        "createacct-realname": "Сапраўднае імя (неабавязкова)",
        "createaccountreason": "Прычына:",
        "createacct-reason": "Прычына",
        "createacct-reason-ph": "Чаму вы ствараеце іншы ўліковы запіс",
+       "createacct-reason-help": "Паведамленне паказана ў логу стварэння ўліковага запісу",
        "createacct-submit": "Стварыць уліковы запіс",
        "createacct-another-submit": "Стварыць уліковы запіс",
        "createacct-continue-submit": "Працягнуць стварэнне ўліковага запісу",
+       "createacct-another-continue-submit": "Працягнуць стварэнне ўліковага запісу",
        "createacct-benefit-heading": "{{SITENAME}} зроблены такімі ж людзьмі, як вы.",
        "createacct-benefit-body1": "{{PLURAL:$1|праўка|праўкі|правак}}",
        "createacct-benefit-body2": "{{PLURAL:$1|старонка|старонкі|старонак}}",
        "nocookiesnew": "Рахунак быў створаны, але ў сістэму вы не ўвайшлі. {{SITENAME}} карыстаецца квіткамі (кукі), каб апрацоўваць уваходы ўдзельнікаў, а гэтая функцыянальнасць адключана ў вашым браўзеры. Уключыце квіткі ў браўзеры, тады ўваходзьце са сваімі новымі імем удзельніка і паролем.",
        "nocookieslogin": "{{SITENAME}} карыстаецца квіткамі (кукі), каб пазнаваць удзельнікаў. У вашым браўзеры квіткі не дазволены. Дазвольце іх працу і паспрабуйце ізноў.",
        "nocookiesfornew": "Уліковы запіс карыстальніка не быў створаны, бо мы не змаглі пацвердзіць яго крыніцы. \nУпэўніцеся, што кукі ўключаныя, абнавіце старонку і паспрабуйце яшчэ раз.",
+       "createacct-loginerror": "Уліковы запіс быў паспяхова створаны, але Вы не змаглі аўтарызавацца аўтаматычна. Калі ласка, перайдзіце да старонкі [[Special:UserLogin|ручной аўтарызацыі]].",
        "noname": "Вы не вызначылі правільнага імя ўдзельніка.",
        "loginsuccesstitle": "Паспяховы ўваход у сістэму",
        "loginsuccess": "<strong>Цяпер Вы ўвайшлі на {{SITENAME}} як \"$1\".</strong>",
        "passwordreset-emailelement": "Імя ўдзельніка: \n$1\n\nЧасовы пароль: \n$2",
        "passwordreset-emailsentemail": "Калі гэты адрас электроннай пошты злучаны з вашым уліковым запісам, будзе адпраўлены ліст пра скід пароля.",
        "passwordreset-emailsentusername": "Калі ёсць адрас электроннай пошты, злучаны з гэтым імем удзельніка, то будзе дасланы ліст пра скід пароля.",
-       "passwordreset-emailsent-capture": "Ніжэй прыведзены адпраўлены ліст пра скід пароля.",
-       "passwordreset-emailerror-capture": "Ніжэй прыведзены створаны ліст пра скід пароля, яго адпраўка не атрымалася па прычыне: $1",
+       "passwordreset-emailsent-capture2": "{{PLURAL:$1|Электронны ліст|электронныя лісты}} скіду пароля адпраўлены. {{PLURAL:$1|Імя ўдзельніка і пароль|Спіс імён удзельнікаў і паролі}} паказаны ніжэй.",
+       "passwordreset-emailerror-capture2": "Не ўдалося даслаць {{GENDER:$2|удзельніку|удзельніцы}} ліст электроннай поштай: $1 {{PLURAL:$3|Імя ўдзельніка і пароль|Спіс імён удзельнікаў і паролі}} паказаны ніжэй.",
+       "passwordreset-nocaller": "Мусіць быць указана, хто выклікае",
+       "passwordreset-nosuchcaller": "Аўтар выкліку не існуе: $1",
+       "passwordreset-ignored": "Скід пароля не быў апрацаваны. Магчыма, не настроены пастаўшчык?",
        "passwordreset-invalideamil": "Няслушны адрас электроннай пошты",
+       "passwordreset-nodata": "Не былі пададзены ні імя ўдзельніка, ні адрас электроннай пошты",
        "changeemail": "Змяніць або выдаліць адрас электроннай пошты",
        "changeemail-header": "Запоўніце гэтую форму, каб змяніць свой адрас электроннай пошты. Калі хочаце выдаліць адрас электроннай пошты, злучаны з вашым уліковым запісам, пакіньце поле новага адраса электроннай пошты пустым пры адпраўцы формы.",
-       "changeemail-passwordrequired": "Вам трэба будзе ўвесці свой пароль, каб пацвердзіць гэта змяненне.",
        "changeemail-no-info": "Каб звяртацца непасрэдна да гэтай старонкі, вам варта прадставіцца сістэме.",
        "changeemail-oldemail": "Бягучы адрас электроннай пошты:",
        "changeemail-newemail": "Новы адрас электроннай пошты:",
        "minoredit": "Дробная праўка",
        "watchthis": "Назіраць за гэтай старонкай",
        "savearticle": "Запісаць",
+       "savechanges": "Запісаць змены",
        "publishpage": "Апублікаваць старонку",
+       "publishchanges": "Апублікаваць змены",
        "preview": "Перадпаказ",
        "showpreview": "Як будзе",
        "showdiff": "Розніца",
        "userpage-userdoesnotexist": "Рахунак удзельніка \"<nowiki>$1</nowiki>\" не зарэгістраваны. Праверце, ці вы жадаеце стварыць або паправіць гэтую старонку.",
        "userpage-userdoesnotexist-view": "Уліковы запіс удзельніка \" $1 \"не зарэгістраваны.",
        "blocked-notice-logextract": "Гэты карыстальнік у дадзены момант заблакаваны. \n Апошні запіс журнала блакіровак прыводзіцца ніжэй для даведкі:",
-       "clearyourcache": "<strong>Заўвага:</strong> Пасля замацоўвання, вам можа спатрэбіцца ачыстка кэшу браўзера, каб убачыць унесеныя змяненні. \n*<strong>Firefox / Safari:</strong> націсніце <em>Reload</em>, утрымліваючы <em>Shift</em>, або націсніце <em>Ctrl-F5</em> ці <em>Ctrl-R</em> (<em>⌘-R</em> на Макінтошах)\n* <strong>Google Chrome</strong>: Націсніце <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Mac)\n* <strong>Internet Explorer</strong>: націсніце <em>Refresh</em>, утрымліваючы <em>Ctrl</em>, або націсніце <em>Ctrl-F5</em>\n* <strong>Opera</strong>: увайдзіце ў настройкі карыстальніка (меню <em>Tools</em>, падпункт <em>Preferences</em>), там ачысціце кэш;",
+       "clearyourcache": "<strong>Заўвага:</strong> Пасля замацоўвання, вам можа спатрэбіцца ачыстка кэшу браўзера, каб убачыць унесеныя змяненні. \n*<strong>Firefox / Safari:</strong> націсніце <em>Reload</em>, утрымліваючы <em>Shift</em>, або націсніце <em>Ctrl-F5</em> ці <em>Ctrl-R</em> (<em>⌘-R</em> на Макінтошах)\n* <strong>Google Chrome</strong>: Націсніце <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Mac)\n* <strong>Internet Explorer</strong>: націсніце <em>Refresh</em>, утрымліваючы <em>Ctrl</em>, або націсніце <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Увайдзіце <em>Menu → Settings</em> (<em>Opera → Preferences</em> на Mac), далей <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
        "usercssyoucanpreview": "'''Парада:''' Карыстайцеся кнопкай \"''{{int:showpreview}}''\" для выпрабоўвання новага коду CSS ''перад тым'', як яго запісваць.",
        "userjsyoucanpreview": "'''Парада:''' Карыстайцеся кнопкай \"''{{int:showpreview}}''\" для выпрабоўвання новага коду JS ''перад тым'', як яго запісваць.",
        "usercsspreview": "'''Памятайце, што гэта толькі папярэдні паказ вашага ўласнага CSS. Праўкі яшчэ не замацаваныя!'''",
        "content-model-css": "CSS",
        "content-json-empty-object": "Пусты аб'ект",
        "content-json-empty-array": "Пусты масіў",
+       "deprecated-self-close-category": "Старонкі з недапушчальнымі самазакрытымі HTML-тэгамі",
+       "deprecated-self-close-category-desc": "Старонка ўтрымлівае недапушчальныя самазакрытыя HTML-тэгі, такія як <code>&lt;b/></code> ці <code>&lt;span/></code>. Іх паводзіны ў хуткім часе будуць зменены ў адпаведнасці з спецыфікацыяй HTML5, таму іх ужыванне ў вікітэксце лічыцца састарэлым.",
        "duplicate-args-warning": "<strong>Увага:</strong> [[:$1]] выклікае [[:$2]] з больш чым адным значэннем для параметра \"$3\". Толькі апошняе з пададзеных значэнняў будзе ўжытае.",
        "duplicate-args-category": "Старонкі, якія выкарыстоўваюць задубляваныя параметры ў шаблонах",
        "duplicate-args-category-desc": "Старонка ўтрымлівае шаблоны з задубляванымі параметрамі, напрыклад, <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> або <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Выглядае на тое, што праўка ўжо была адкочаная.",
        "undo-summary": "Адкат версіі $1 аўтарства [[Special:Contributions/$2|$2]] ([[User talk:$2|размова]])",
        "undo-summary-username-hidden": "Адкат версіі $1 схаванага ўдзельніка",
-       "cantcreateaccounttitle": "Немагчыма стварыць рахунак",
        "cantcreateaccount-text": "Стварэнне рахункаў было забаронена для гэтага адрасу IP ('''$1''').\n\nЗабарона зроблена ўдзельнікам [[User:$3|$3]], з такім тлумачэннем: ''$2''",
        "cantcreateaccount-range-text": "Стварэнне ўліковага запісу ўдзельніка з IP-адрасоў у дыяпазоне <strong>$1</strong>, што ўключае ваш адрас IP (<strong>$4</strong>), было забаронена {{GENDER:$3|ўдзельнікам|ўдзельніцай}} [[User:$3|$3]].\n\n$3 {{GENDER:$3|патлумачыў|патлумачыла}} гэта так: <em>$2</em>",
        "viewpagelogs": "Паказаць журналы для гэтай старонкі",
        "badsig": "Недапушчальны зыходны тэкст подпісу; праверце тэгі HTML.",
        "badsiglength": "Занадта доўгі подпіс. Трэба, каб ён быў карацейшым за $1 {{PLURAL:$1|знак|знакаў}}.",
        "yourgender": "Пол:",
-       "gender-unknown": "Ð\9dÑ\8fвÑ\8bзнаÑ\87аны",
+       "gender-unknown": "Ð\97гадваÑ\8eÑ\87Ñ\8b Ð²Ð°Ñ\81, Ð¿Ñ\80агÑ\80ама Ð±Ñ\83дзе Ð¿Ð° Ð¼Ð°Ð³Ñ\87Ñ\8bмаÑ\81Ñ\86Ñ\96 Ñ\9eжÑ\8bваÑ\86Ñ\8c Ð³ÐµÐ½Ð´Ð°Ñ\80на-нейÑ\82Ñ\80алÑ\8cнÑ\8bÑ\8f Ñ\81ловы",
        "gender-male": "М",
        "gender-female": "Ж",
        "prefs-help-gender": "Неабавязкова: ужываецца дзеля пола-карэктнага звяртання з боку праграм. Гэтыя звесткі могуць стацца публічна вядомымі.",
        "right-override-export-depth": "Экспартаваць старонкі, у тым ліку звязаныя, да глыбіні спасылак 5.",
        "right-sendemail": "Адпраўляць электронныя лісты іншым удзельнікам",
        "right-passwordreset": "Бачыць электронныя лісты аб змяненні пароля",
-       "right-managechangetags": "Ствараць і выдаляць [[Special:Tags|біркі]] з базы даных",
+       "right-managechangetags": "Ствараць і (дэ)актываваць [[Special:Tags|біркі]]",
        "right-applychangetags": "Прымяняць [[Special:Tags|біркі]] са сваімі праўкамі",
        "right-changetags": "Дадаваць і выдаляць адвольныя [[Special:Tags|біркі]] да асобных версій і запісаў у журнале падзей",
        "right-deletechangetags": "Выдаляць [[Special:Tags|біркі]] з базы даных",
        "grant-group-high-volume": "Выконваць вялікі аб'ём дзейнасці",
        "grant-group-customization": "Настройкі і перавагі",
        "grant-group-administration": "Выконваць адміністрацыйныя дзеянні",
+       "grant-group-private-information": "Доступ да прыватных звестак пра вас",
        "grant-group-other": "Розная актыўнасць",
        "grant-blockusers": "Блакаваць і разблакаваць удзельнікаў",
        "grant-createaccount": "Ствараць уліковыя запісы",
        "grant-highvolume": "Вялікі аб'ём рэдагавання",
        "grant-oversight": "Утойваць удзельнікаў і версіі старонак",
        "grant-patrol": "Патруляваць змены старонак",
+       "grant-privateinfo": "Доступ да прыватных звестак",
        "grant-protect": "Ахоўваць і здымаць ахову старонак",
        "grant-rollback": "Адкатваць змяненні старонак",
        "grant-sendemail": "Адпраўляць электронную пошту іншым удзельнікам",
        "rightslogtext": "Журнал змяненняў у дазволах, прыпісаных удзельнікам.",
        "action-read": "чытаць гэтую старонку",
        "action-edit": "правіць гэтую старонку",
-       "action-createpage": "ствараць старонкі",
-       "action-createtalk": "ствараць размоўныя старонкі",
+       "action-createpage": "стварыць гэту старонку",
+       "action-createtalk": "стварыць гэту размоўную старонку",
        "action-createaccount": "ствараць гэты рахунак удзельніка",
        "action-autocreateaccount": "аўтаматычна ствараць гэты вонкавы ўліковы запіс удзельніка",
        "action-history": "глядзець гісторыю гэтай старонкі",
        "action-applychangetags": "прымяняць біркі з сваімі праўкамі",
        "action-changetags": "дадаваць і выдаляць адвольныя біркі да асобных версій і запісаў у журнале падзей",
        "action-deletechangetags": "выдаляць біркі з базы даных",
+       "action-purge": "ачысціць кэш гэтай старонкі",
        "nchanges": "$1 {{PLURAL:$1|змена|змены|змен}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|з часу апошняга наведвання}}",
        "enhancedrc-history": "гісторыя",
        "file-thumbnail-no": "Назва файла пачынаецца з <strong>$1</strong>.\nТак можа называцца выява зменшанага памеру ''(драбніца)''.\nКалі гэтая выява сапраўды запісаная ў найлепшым разрозненні, якое ёсць, то ўкладайце яе, а іначай лепей памяняць назву файла.",
        "fileexists-forbidden": "Файл з такой назвай ужо ёсць, і нельга запісаць паўзверх яго. Калі вы жадаеце абавязкова ўкласці свой файл, то выберыце новую назву. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "У агульным сховішчы ўжо існуе файл з такою назвай.\nКалі вы жадаеце ўсё ж укласці свой файл, паўтарыце працэдуру ўкладання, але з іншай назвай. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Укладанне з'яўляецца дакладнай копіяй бягучай версіі <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Укладанне з'яўляецца дакладнай копіяй {{PLURAL:$2|старой версіі|старых версій}} файла <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Гэты файл з'яўляецца дублікатам наступн{{PLURAL:$1|ага файла|ых файлаў}}:",
        "file-deleted-duplicate": "Файл, падобны да гэтага ([[:$1]]), быў сцёрты некалі раней. Трэба праверыць гісторыю таго файла перад тым, як укладваць яго нанова.",
        "file-deleted-duplicate-notitle": "Файл, ідэнтычны гэтаму, быў сцёрты раней, а назва файла заглушана.\nВы мусіце спытаць каго-небудзь з магчымасцю бачыць заглушаныя звесткі па файлах пераглядзець сітуацыю перад тым, як укладваць яго нанова.",
        "uploadstash-badtoken": "Не атрымалася выканаць названыя дзеянні. Магчыма, скончыўся тэрмін дзеяння вашага жэтона бяспекі. Паспрабуйце яшчэ раз.",
        "uploadstash-errclear": "Ачыстка файлаў не ўдалася",
        "uploadstash-refresh": "Абнавіць спіс файлаў",
+       "uploadstash-thumbnail": "паказаць мініяцюру",
        "invalid-chunk-offset": "Недапушчальнае зрушэнне фрагмента",
        "img-auth-accessdenied": "Доступ забаронены",
        "img-auth-nopathinfo": "Адсутнічае PATH_INFO.\nВаш сервер не настроены на перадачу гэтых звестак.\nМагчыма, ён на аснове CGI і не падтрымлівае img_auth.\nГл. https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "rollback-success": "Адкочаны праўкі $1; вернута апошняя версія $2.",
        "sessionfailure-title": "Памылка сеансу",
        "sessionfailure": "Магчыма, ёсць праблемы з вашым сеансам працы ў сістэме. Таму вам было адмоўлена ў выкананні дзеяння, каб засцерагчыся ад захопу сеанса.\n\nВярніцеся на папярэднюю старонку, перазагрузіце яе і тады паспрабуйце зноў.",
+       "changecontentmodel": "Змяніць мадэль змесціва старонкі",
+       "changecontentmodel-legend": "Змяніць мадэль змесціва",
        "changecontentmodel-title-label": "Назва старонкі",
        "changecontentmodel-model-label": "Новая мадэль змесціва",
        "changecontentmodel-reason-label": "Прычына:",
        "changecontentmodel-submit": "Змяніць",
+       "changecontentmodel-success-title": "Мадэль змесціва была зменена",
+       "changecontentmodel-emptymodels-title": "Няма даступных мадэляў змесціва",
        "logentry-contentmodel-change-revertlink": "адкаціць",
        "logentry-contentmodel-change-revert": "адкат",
        "protectlogpage": "Журнал аховы",
        "sp-contributions-username": "Адрас IP або імя ўдзельніка:",
        "sp-contributions-toponly": "Паказваць толькі праўкі, якія з'яўляюцца апошнімі версіямі",
        "sp-contributions-newonly": "Паказваць толькі праўкі, якімі створаны старонкі",
+       "sp-contributions-hideminor": "Схаваць дробныя праўкі",
        "sp-contributions-submit": "Пошук",
        "whatlinkshere": "Сюды спасылаюцца",
        "whatlinkshere-title": "Старонкі, якія спасылаюцца на \"$1\"",
        "whatlinkshere-hidelinks": "$1 спасылкі",
        "whatlinkshere-hideimages": "$1 спасылкі на выявы",
        "whatlinkshere-filters": "Фільтры",
+       "whatlinkshere-submit": "Далей",
        "autoblockid": "Аўтаблакіроўка #$1",
        "block": "Заблакаваць удзельніка",
        "unblock": "Разблакаваць удзельніка",
        "lockdbsuccesstext": "База даных была зачынена.\n<br />Памятайце, каб [[Special:UnlockDB|сцерці файл-замок]] пасля завяршэння абслугоўвання.",
        "unlockdbsuccesstext": "База дадзеных была адмыкнутая.",
        "lockfilenotwritable": "Немагчыма запісаць у файл-замок базы даных. Каб зачыняць базу, трэба, каб веб-сервер мог запісваць у гэты файл.",
+       "databaselocked": "База звестак ужо заблакаваная.",
        "databasenotlocked": "База дадзеных не замкнутая.",
        "lockedbyandtime": "($1 $2 $3)",
        "move-page": "Перанесці $1",
        "htmlform-cloner-delete": "Сцерці",
        "htmlform-cloner-required": "Неабходна хаця б адно значэнне.",
        "htmlform-title-badnamespace": "[[:$1]] не ў прасторы назваў \"{{ns:$2}}\".",
+       "htmlform-title-not-creatable": "\"$1\" - немагчымы загаловак для старонкі",
        "htmlform-title-not-exists": "$1 не існуе.",
        "htmlform-user-not-exists": "<strong>$1</strong> не існуе.",
+       "htmlform-user-not-valid": "<strong>$1</strong> - недапушчальная назва уліковага запісу.",
        "sqlite-has-fts": "$1 з падтрымкай поўна-тэкставага пошуку",
        "sqlite-no-fts": "$1 без падтрымкі поўна-тэкставага пошуку",
        "logentry-delete-delete": "$1 {{GENDER:$2|сцёр|сцёрла}} старонку $3",
        "logentry-block-block": "$1 заблакірава{{GENDER:$2|ў|ла}} {{GENDER:$4|$3}} на перыяд $5 $6",
        "logentry-block-unblock": "$1 {{GENDER:$2|разблакаваў|разблакавала}} {{GENDER:$4|$3}}",
        "logentry-block-reblock": "$1 {{GENDER:$2|памяняў|памяняла}} настройкі блакіроўкі {{GENDER:$4|$3}} на перыяд $5 $6",
+       "logentry-suppress-block": "$1 {{GENDER:$2|заблакіраваў|заблакіравала}} {{GENDER:$4|$3}} на перыяд $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|памяняў|памяняла}} параметры блакіроўкі {{GENDER:$4|$3}} на перыяд $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|імпартаваў|імпартавала}} $3 праз укладанне файла",
+       "logentry-import-upload-details": "$1 {{GENDER:$2|імпартаваў|імпартавала}} $3 праз укладанне файла ($4 {{PLURAL:$4|версія|версіі|версій}})",
        "logentry-import-interwiki": "$1 {{GENDER:$2|імпартаваў|імпартавала}} $3 з іншай вікі",
+       "logentry-import-interwiki-details": "$1 {{GENDER:$2|імпартаваў|імпартавала}} $3 з $5 ($4 {{PLURAL:$4|версія|версіі|версій}})",
        "logentry-move-move": "$1 {{GENDER:$2|перанёс|перанесла}} старонку $3 у $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|перанёс|перанесла}} старонку $3 у $4, не пакінуўшы перасылкі",
        "logentry-move-move_redir": "$1 {{GENDER:$2|перанёс|перанесла}} старонку $3 у $4 па-над перасылкаю",
        "logentry-upload-overwrite": "$1 {{GENDER:$2|уклаў|уклала}} новую версію $3",
        "logentry-upload-revert": "$1 {{GENDER:$2|уклаў|уклала}} $3",
        "log-name-managetags": "Журнал кіравання біркамі",
+       "logentry-managetags-create": "$1 {{GENDER:$2|стварыў|стварыла}} бірку \"$4\"",
+       "logentry-managetags-delete": "$1 {{GENDER:$2|выдаліў|выдаліла}} бірку \"$4\" (выдалена з $5 {{PLURAL:$5|версіі ці запісу ў журнале|версій і/або запісаў у журнале}})",
+       "logentry-managetags-activate": "$1 {{GENDER:$2|актываваў|актывавала}} бірку \"$4\" для выкарыстання ўдзельнікамі і робатамі",
+       "logentry-managetags-deactivate": "$1 {{GENDER:$2|дэактываваў|дэактывавала}} бірку \"$4\" для выкарыстання ўдзельнікамі і робатамі",
        "log-name-tag": "Журнал бірак",
+       "log-description-tag": "На гэтай старонцы паказана, калі ўдзельнікі дадавалі ці выдалялі [[Special:Tags|біркі]] ў асобных версіях ці запісах журнала. Журнал не захоўвае дзеянні з біркамі, калі яны былі часткай рэдагавання, выдалення ці падобных дзеянняў.",
+       "logentry-tag-update-add-revision": "$1 {{GENDER:$2|дадаў|дадала}} {{PLURAL:$7|1=бірку|біркі}} $6 да версіі $4 старонкі $3",
+       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|дадаў|дадала}} {{PLURAL:$7|1=бірку|біркі}} $6 да запісу ў журнале $5 старонкі $3",
+       "logentry-tag-update-remove-revision": "$1 {{GENDER:$2|выдаліў|выдаліла}} {{PLURAL:$9|1=бірку|біркі}} $8 з версіі $4 старонкі $3",
        "rightsnone": "(няма)",
        "revdelete-summary": "тлумачэнне праўкі",
        "feedback-adding": "Даданне водгуку на старонку…",
        "special-characters-group-khmer": "Кхмерскія",
        "special-characters-title-endash": "кароткі працяжнік",
        "special-characters-title-emdash": "доўгі працяжнік",
-       "special-characters-title-minus": "мінус"
+       "special-characters-title-minus": "мінус",
+       "mw-widgets-dateinput-no-date": "Дата не выбрана",
+       "mw-widgets-titleinput-description-new-page": "старонка яшчэ не існуе",
+       "mw-widgets-titleinput-description-redirect": "перанакіраванне на $1",
+       "log-action-filter-all": "Усе",
+       "log-action-filter-block-block": "заблакаваць",
+       "log-action-filter-block-reblock": "Змена блакіроўкі",
+       "log-action-filter-block-unblock": "Разблакаваць",
+       "log-action-filter-contentmodel-change": "Змяненне Contentmodel",
+       "log-action-filter-contentmodel-new": "Стварэнне старонкі з нестандартным Contentmodel",
+       "log-action-filter-delete-delete": "Выдаленне старонкі",
+       "log-action-filter-delete-restore": "Узнаўленне старонкі",
+       "log-action-filter-delete-event": "Выдаленне лога",
+       "log-action-filter-delete-revision": "Выдаленне перагляду",
+       "log-action-filter-import-interwiki": "Імпарт Transwiki",
+       "log-action-filter-import-upload": "Імпарт выгрузкай XML",
+       "log-action-filter-managetags-create": "Стварэнне тэгаў",
+       "log-action-filter-managetags-delete": "Выдаленне тэгаў",
+       "log-action-filter-managetags-activate": "Актывацыя тэга",
+       "log-action-filter-managetags-deactivate": "Адключэнне тэга",
+       "log-action-filter-move-move": "Перайсці без перазапісу рэдырэктаў",
+       "log-action-filter-move-move_redir": "Перамясціць з заменай рэдырэктаў",
+       "log-action-filter-newusers-create": "Стварэнне ананімным удзельнікам",
+       "log-action-filter-newusers-create2": "Стварэнне зарэгістраваным удзельнікам",
+       "log-action-filter-newusers-autocreate": "Аўтаматычнае стварэнне",
+       "log-action-filter-newusers-byemail": "Стварэнне паролем, высланым па электроннай пошце",
+       "log-action-filter-patrol-patrol": "Ручны догляд",
+       "log-action-filter-patrol-autopatrol": "Аўтаматычны догляд",
+       "log-action-filter-protect-protect": "Абарона",
+       "log-action-filter-protect-modify": "Мадыфікацыя абароны",
+       "log-action-filter-protect-unprotect": "Зняцце абароны",
+       "log-action-filter-protect-move_prot": "Ахова старонкі",
+       "log-action-filter-rights-rights": "Ручное змяненне",
+       "log-action-filter-rights-autopromote": "Аўтаматычнае змяненне",
+       "log-action-filter-upload-upload": "Новая перадача",
+       "log-action-filter-upload-overwrite": "Выкладванне",
+       "authmanager-authn-not-in-progress": "Праверка сапраўднасці не выконваецца або сесія перадачы дадзеных была страчана. Калі ласка, пачніце зноў з самага пачатку.",
+       "authmanager-authn-no-primary": "Прадастаўленыя ўліковыя дадзеныя не могуць быць завераны.",
+       "authmanager-authn-no-local-user": "Прадастаўленыя ўліковыя дадзеныя не звязаныя з нікодным удзельнікам на гэтай Вікі.",
+       "authmanager-authn-no-local-user-link": "Прадастаўленыя ўліковыя дадзеныя сапраўдныя, але не звязаныя з ніводным удзельнікам гэтай Вікі. Увайдзіце іншым спосабам або стварыце новы акаўнт і ў вас з'явіцца магчымасць звязаць свае папярэднія ўліковыя дадзеныя для гэтага ўліковага запісу.",
+       "authmanager-authn-autocreate-failed": "Аўтаматычнае стварэнне лакальнага ўліковага запісу не ўдалося: $1",
+       "authmanager-change-not-supported": "Прадастаўленыя ўліковыя дадзеныя не могуць быць зменены, як нішто не будзе іх выкарыстоўваць.",
+       "authmanager-create-disabled": "стварэнне рахунка не дазволена",
+       "authmanager-create-from-login": "Каб стварыць уліковы запіс, калі ласка, запоўніце палі ніжэй.",
+       "authmanager-create-not-in-progress": "Праверка сапраўднасці не выконваецца або сесія перадачы дадзеных была страчана. Калі ласка, пачніце зноў з самага пачатку.",
+       "authmanager-create-no-primary": "Прадастаўленыя ўліковыя дадзеныя не могуць быць выкарыстаны для стварэння ўліковага запісу.",
+       "authmanager-link-no-primary": "Прадастаўленыя ўліковыя дадзеныя не могуць быць выкарыстаны для прывязкі рахунку.",
+       "authmanager-link-not-in-progress": "Звязванне ўліковага запісу не выконваецца або сесія перадачы дадзеных была страчана. Калі ласка, пачніце зноў з самага пачатку.",
+       "authmanager-authplugin-setpass-failed-title": "Памылка змены пароля",
+       "authmanager-authplugin-setpass-failed-message": "Убудова аўтэнтыфікацыі адмоўлена па змене пароля.",
+       "authmanager-authplugin-create-fail": "Убудова аўтэнтыфікацыі адмоўлена ў рэгістрацыі.",
+       "authmanager-authplugin-setpass-denied": "Убудова праверкі сапраўднасці не дазваляе змяняць паролі.",
+       "authmanager-authplugin-setpass-bad-domain": "Недапушчальны дамен.",
+       "authmanager-autocreate-noperm": "Аўтаматычнае стварэнне уліковых запісаў не дапускаецца.",
+       "authmanager-autocreate-exception": "Аўтаматычнае стварэнне уліковых запісаў часова адключана з-за памылак папярэдніх.",
+       "authmanager-userdoesnotexist": "Уліковы запіс удзельніка \"$1\" не зарэгістраваны.",
+       "authmanager-username-help": "Імя карыстальніка для праверкі сапраўднасці.",
+       "authmanager-password-help": "Пароль для праверкі сапраўднасці.",
+       "authmanager-domain-help": "Дамен для знешняй праверкі сапраўднасці.",
+       "authmanager-retype-help": "Пароль яшчэ раз для пацверджання.",
+       "authmanager-email-label": "Эл.пошта",
+       "authmanager-email-help": "Адрас электроннай пошты:",
+       "authmanager-realname-label": "Сапраўднае імя:",
+       "authmanager-realname-help": "Рэальнае імя карыстальніка",
+       "authmanager-provider-password": "Праверка сапраўднасці на аснове пароляў",
+       "authmanager-provider-temporarypassword": "Тымчасовы пароль:",
+       "authprovider-confirmlink-message": "Грунтуючыся на апошніх спробах падлучэння, наступныя уліковыя запісы могуць быць звязаныя з Вашым ўліковым запісам Вікі. Калі ласка, выберыце, якія з іх павінны быць звязаныя паміж сабой.",
+       "authprovider-confirmlink-request-label": "Рахункі, якія павінны быць звязаныя",
+       "authprovider-confirmlink-success-line": "$1: звязаны паспяхова.",
+       "authprovider-confirmlink-failed": "Прывязка акаўнта не атрымалася ў поўнай меры: $1",
+       "authprovider-confirmlink-ok-help": "Працягнуць пасля вываду паведамленняў аб памылках прывязкі.",
+       "authprovider-resetpass-skip-label": "Прапусціць",
+       "authprovider-resetpass-skip-help": "Прапусціць скід пароля.",
+       "authform-nosession-login": "Праверка сапраўднасці прайшла паспяхова, але ваш браўзэр не можа \"запомніць\" уваход у сістэму.\n\n$1",
+       "authform-nosession-signup": "Уліковы запіс быў створаны, але ваш браўзэр не можа \"запомніць\" у сістэму.\n\n$1",
+       "authform-newtoken": "Адсутнічае маркер. \n\n$1",
+       "authform-notoken": "Адсутнічае маркер",
+       "authform-wrongtoken": "Няправільны маркер",
+       "specialpage-securitylevel-not-allowed-title": "Не дапускаецца",
+       "specialpage-securitylevel-not-allowed": "Прабачце, вам не дазволена выкарыстоўваць гэтую старонку, таму што ваша асоба не можа быць праверана.",
+       "authpage-cannot-login": "Не атрымалася запусціць лагін.",
+       "authpage-cannot-login-continue": "Можаце працягваць аўтарызацыю. Сеанс хутчэй за ўсе скончыўся.",
+       "authpage-cannot-create": "Немагчыма пачаць стварэнне рахунку.",
+       "authpage-cannot-create-continue": "Немагчыма працягнуць стварэнне акаўнта. Сеанс хутчэй за ўсе скончыўся.",
+       "authpage-cannot-link": "Не атрымалася запусціць прывязку акаўнта.",
+       "authpage-cannot-link-continue": "Немагчыма працягваць звязванне ўліковага запісу. Сеанс хутчэй за ўсе скончыўся.",
+       "cannotauth-not-allowed-title": "Доступ забаронены.",
+       "cannotauth-not-allowed": "Вам не дазволена выкарыстоўваць гэтую старонку",
+       "changecredentials": "Змяненне уліковых дадзеных",
+       "changecredentials-submit": "Змяненне уліковых дадзеных",
+       "changecredentials-invalidsubpage": "$1 не з'яўляецца дапушчальным тыпам уліковых дадзеных.",
+       "changecredentials-success": "Вашы дадзеныя былі змененыя.",
+       "removecredentials": "Выдаліць ўліковыя дадзеныя",
+       "removecredentials-submit": "Выдаліць ўліковыя дадзеныя",
+       "removecredentials-invalidsubpage": "$1 не з'яўляецца дапушчальным тыпам уліковых дадзеных.",
+       "removecredentials-success": "Вашы ўліковыя дадзеныя былі выдаленыя.",
+       "credentialsform-provider": "Калі ласка, увядзіце ўліковыя дадзеныя:",
+       "credentialsform-account": "Імя ўліковага запісу:",
+       "cannotlink-no-provider-title": "Няма рахункаў, якія трэба звязваць",
+       "cannotlink-no-provider": "Няма рахункаў, якія трэба звязваць",
+       "linkaccounts": "Звязаць акаўнты",
+       "linkaccounts-success-text": "Акаўнт быў звязаны.",
+       "linkaccounts-submit": "Звязаць акаўнты",
+       "unlinkaccounts": "Адмяніць сувязь уліковых запісаў",
+       "unlinkaccounts-success": "Сувязь акаўнтаў была выдалена."
 }
index dc49adf..c8eb76b 100644 (file)
@@ -34,7 +34,9 @@
                        "Xð",
                        "Miroslav35232",
                        "Ket",
-                       "Ricordo.tenerissimo"
+                       "Ricordo.tenerissimo",
+                       "Plamen",
+                       "Iliev"
                ]
        },
        "tog-underline": "Подчертаване на препратките:",
@@ -52,6 +54,7 @@
        "tog-watchdefault": "Добавяне на страниците, които редактирам, в списъка ми за наблюдение",
        "tog-watchmoves": "Добавяне на преместените от мен страници и файлове към списъка ми за наблюдение",
        "tog-watchdeletion": "Добавяне на изтритите от мен страници и файлове към списъка ми за наблюдение",
+       "tog-watchuploads": "Добавяне на новите качени от мен файлове към списъка ми за наблюдение",
        "tog-watchrollback": "Добавяне на страници, в които съм {{GENDER:$1|извършвал|извършвала}} отмяна на редакции в списъка ми за наблюдениe",
        "tog-minordefault": "Отбелязване на всички промени като малки по подразбиране",
        "tog-previewontop": "Показване на предварителния преглед преди текстовата кутия",
@@ -76,7 +79,7 @@
        "tog-ccmeonemails": "Получаване на копия на писмата, които пращам на другите потребители",
        "tog-diffonly": "Без показване на съдържанието на страницата при преглед на разлики",
        "tog-showhiddencats": "Показване на скритите категории",
-       "tog-norollbackdiff": "Ð\9fÑ\80опÑ\83Ñ\81кане Ð½Ð° Ñ\80азликоваÑ\82а Ð²Ñ\80Ñ\8aзка Ñ\81лед Ð¸Ð·Ð²Ñ\8aÑ\80Ñ\88ване Ð½Ð° отмяна на редакции",
+       "tog-norollbackdiff": "Ð\9dе Ð¿Ð¾ÐºÐ°Ð·Ð²Ð°Ð¹ Ñ\80азликаÑ\82а Ð¼ÐµÐ¶Ð´Ñ\83 Ñ\80едакÑ\86ииÑ\82е Ñ\81лед отмяна на редакции",
        "tog-useeditwarning": "Предупреждаване при опит за напускане на страница, отворена в режим на редактиране, без да са запазени промените",
        "tog-prefershttps": "Да се използва винаги защитена връзка след влизане",
        "underline-always": "Винаги",
        "protectedinterface": "Тази страница съдържа текст, нужен за работата на системата. Тя е защитена против редактиране, за да се предотвратят възможни злоупотреби.\nЗа извършване на промяна за всички уикита, посетете [https://translatewiki.net/ translatewiki.net], проектът за локализация на MediaWiki.",
        "editinginterface": "<strong>Внимание:</strong> Редактирате страница, която се използва за текстовия интерфейс на софтуера. Промяната й ще повлияе на външния вид на уикито.",
        "translateinterface": "За да добавите или промените преводи за всички уикита, моля, използвайте [https://translatewiki.net/ translatewiki.net], локализиращия проект на МедияУики.",
-       "cascadeprotected": "Тази страница е защитена против редактиране, защото е включена в {{PLURAL:$1|следната страница, която от своя страна има|следните страници, които от своя страна имат}} каскадна защита:\n$2",
+       "cascadeprotected": "Тази страница е защитена против редактиране, защото е включена в {{PLURAL:$1|следната страница, която от своя страна има|следните страници, които от своя страна имат}} „каскадна“ защита:\n$2",
        "namespaceprotected": "Нямате права за редактиране на страници в именно пространство '''$1'''.",
        "customcssprotected": "Нямате права за редактиране на тази CSS страница, защото тя съдържа чужди потребителски настройки.",
        "customjsprotected": "Нямате права за редактиране на тази JavaScript страница, защото тя съдържа чужди потребителски настройки.",
        "password-change-forbidden": "Не можете да променяте пароли в това уики.",
        "externaldberror": "Или е станала грешка в базата от данни при външното удостоверяване, или не ви е позволено да обновявате външната си сметка.",
        "login": "Влизане",
+       "login-security": "Потвърдете идентичността си",
        "nav-login-createaccount": "Регистриране или влизане",
        "userlogin": "Влизане / създаване на сметка",
        "userloginnocreate": "Влизане",
        "createacct-reason-ph": "Защо създавате друга сметка",
        "createacct-submit": "Създаване на сметката",
        "createacct-another-submit": "Създаване на сметка",
+       "createacct-continue-submit": "Продължаване създаването на акаунт",
+       "createacct-another-continue-submit": "Продължаване създаването на акаунт",
        "createacct-benefit-heading": "{{SITENAME}} се създава от хора като вас.",
        "createacct-benefit-body1": "{{PLURAL:$1|редакция|редакции}}",
        "createacct-benefit-body2": "{{PLURAL:$1|страница|страници}}",
        "noname": "Не указахте валидно потребителско име.",
        "loginsuccesstitle": "Успешно влизане",
        "loginsuccess": "'''Влязохте в {{SITENAME}} като „$1“.'''",
-       "nosuchuser": "Ð\9dе Ñ\81Ñ\8aÑ\89еÑ\81Ñ\82вÑ\83ва Ð¿Ð¾Ñ\82Ñ\80ебиÑ\82ел Ñ\81 Ð¸Ð¼Ðµ â\80\9e$1â\80\9c.\nÐ\9fоÑ\82Ñ\80ебиÑ\82елÑ\81киÑ\82е Ð¸Ð¼ÐµÐ½Ð° Ñ\81а Ñ\87Ñ\83вÑ\81Ñ\82виÑ\82елни Ð½Ð° Ð¼Ð°Ð»ÐºÐ¸ Ð¸ Ð³Ð»Ð°Ð²Ð½Ð¸ Ð±Ñ\83кви.\nПроверете изписването или [[Special:CreateAccount|създайте нова сметка]].",
+       "nosuchuser": "Ð\9dе Ñ\81Ñ\8aÑ\89еÑ\81Ñ\82вÑ\83ва Ð¿Ð¾Ñ\82Ñ\80ебиÑ\82ел Ñ\81 Ð¸Ð¼Ðµ â\80\9e$1â\80\9c.\nÐ\9fоÑ\82Ñ\80ебиÑ\82елÑ\81киÑ\82е Ð¸Ð¼ÐµÐ½Ð° Ñ\81а Ñ\87Ñ\83вÑ\81Ñ\82виÑ\82елни ÐºÑ\8aм Ñ\80егиÑ\81Ñ\82Ñ\8aÑ\80а Ð½Ð° Ð±Ñ\83квиÑ\82е.\nПроверете изписването или [[Special:CreateAccount|създайте нова сметка]].",
        "nosuchusershort": "Не съществува потребител с името „$1“. Проверете изписването.",
        "nouserspecified": "Необходимо е да се посочи потребителско име.",
        "login-userblocked": "Този потребител е блокиран. Влизането в системата не е позволено.",
        "newpassword": "Нова парола:",
        "retypenew": "Повтори новата парола:",
        "resetpass_submit": "Избиране на парола и влизане",
-       "changepassword-success": "Ð\9fаÑ\80олаÑ\82а Ð²Ð¸ Ð±ÐµÑ\88е Ð¿Ñ\80оменена Ñ\83Ñ\81пеÑ\88но!",
+       "changepassword-success": "Паролата беше променена успешно!",
        "changepassword-throttled": "Направили сте твърде много опити да въведете паролата за тази сметка.\nНеобходимо е да изчакате $1 преди да опитате отново.",
        "botpasswords-createnew": "Създаване на нова бот парола",
        "botpasswords-editexisting": "Редактиране на съществуваща бот парола",
        "botpasswords-label-cancel": "Отказване",
        "botpasswords-label-delete": "Изтриване",
        "botpasswords-label-resetpassword": "Възстановяване на парола",
+       "botpasswords-label-restrictions": "Ограничения на употребата:",
+       "botpasswords-created-title": "Паролата на бота е създадена",
+       "botpasswords-created-body": "Паролата на бот „$1“ на потребител „$2“ е създадена.",
+       "botpasswords-updated-title": "Паролата на бота е обновена",
+       "botpasswords-updated-body": "Паролата на бот „$1“ на потребител „$2“ е обновена.",
+       "botpasswords-deleted-title": "Паролата на бота е изтрита",
+       "botpasswords-deleted-body": "Паролата на бот „$1“ на потребител „$2“ е премахната.",
        "resetpass_forbidden": "Не е разрешена смяна на паролата",
+       "resetpass_forbidden-reason": "Паролите не могат да се променят: $1",
        "resetpass-no-info": "За да достъпвате тази страница директно, необходимо е да влезете в системата.",
        "resetpass-submit-loggedin": "Промяна на паролата",
        "resetpass-submit-cancel": "Отказ",
-       "resetpass-wrong-oldpass": "Невалидна временна или текуща парола.\nВъзможно е вече успешно да сте сменили паролата си или да сте поискали нова временна парола.",
+       "resetpass-wrong-oldpass": "Невалидна временна или текуща парола.\nВъзможно е вече да сте сменили паролата си или да сте поискали нова временна парола.",
        "resetpass-recycled": "Моля, променете паролата си с такава, различна от текущата.",
        "resetpass-temp-emailed": "Вие сте влезли в системата с помощта на временен код, който е изпратен чрез електронна поща.\nЗа да завършите влизането в системата, трябва да въведете нова парола тук:",
        "resetpass-temp-password": "Временна парола:",
        "resetpass-abort-generic": "Промяната на паролата беше прекъсната от използвано разширение.",
        "resetpass-expired": "Срокът на валидност на паролата е изтекъл. Изберете нова парола за влизане.",
        "resetpass-expired-soft": "Вашата парола е изтекла и трябва да бъде променена. Моля, въведете нова парола сега или натиснете бутона \"{{int:authprovider-resetpass-skip-label}}\" за да я промените по-късно.",
-       "resetpass-validity-soft": "Ð\9cолÑ\8f, Ð²Ñ\8aведеÑ\82е Ð½Ð¾Ð²Ð° Ð¿Ð°Ñ\80ола Ñ\81ега Ð¸Ð»Ð¸ Ð½Ð°Ñ\82иÑ\81неÑ\82е Ð±Ñ\83Ñ\82она \"{{int:authprovider-resetpass-skip-label}}\" за да я промените по-късно.",
+       "resetpass-validity-soft": "Ð\92аÑ\88аÑ\82а Ð¿Ð°Ñ\80ола Ðµ Ð½ÐµÐ²Ð°Ð»Ð¸Ð´Ð½Ð°: $1\n\nÐ\9cолÑ\8f, Ð²Ñ\8aведеÑ\82е Ð½Ð¾Ð²Ð° Ð¿Ð°Ñ\80ола Ð¸Ð»Ð¸ Ð½Ð°Ñ\82иÑ\81неÑ\82е Ð±Ñ\83Ñ\82она â\80\9e{{int:authprovider-resetpass-skip-label}}â\80\9c за да я промените по-късно.",
        "passwordreset": "Възстановяване на парола",
        "passwordreset-text-one": "Попълването на формуляра ще доведе до възстановяване на паролата.",
        "passwordreset-text-many": "{{PLURAL:$1|За получаване на временна парола по електронна поща е необходимо да попълните едно от полетата.}}",
        "passwordreset-emailelement": "Потребителско име: \n$1\n\nВременна парола: \n$2",
        "passwordreset-emailsentemail": "Ако електронната Ви поща е свързана със сметката Ви, на нея е изпратено писмо за възстановяване на паролата.",
        "passwordreset-emailsentusername": "Ако това потребителско име е свързано с електронна поща, е изпратено писмо за възстановяване на паролата.",
-       "passwordreset-emailsent-capture": "По-долу е показано електронното писмо за възстановяване на паролата, което беше изпратено.",
-       "passwordreset-emailerror-capture": "По-долу е показано създадено електронно писмо за възстановяване на паролата, което не беше изпратено на {{GENDER:$2|потребителя}}: $1",
        "passwordreset-invalideamil": "Неправилен email адрес",
        "changeemail": "Промяна или премахване на адреса за е-поща",
        "changeemail-header": "Промяна на адреса за е-поща на сметката",
-       "changeemail-passwordrequired": "Трябва да въведете паролата си, за да потвърдите тази промяна.",
        "changeemail-no-info": "За да достъпвате тази страница директно, необходимо е да влезете в системата.",
        "changeemail-oldemail": "Текущ адрес за е-поща:",
        "changeemail-newemail": "Нов адрес за е-поща:",
        "minoredit": "Това е малка промяна",
        "watchthis": "Наблюдаване на страницата",
        "savearticle": "Съхраняване",
+       "savechanges": "Съхраняване на промените",
        "publishpage": "Публикуване на страницата",
+       "publishchanges": "Публикуване на промените",
        "preview": "Предварителен преглед",
        "showpreview": "Предварителен преглед",
        "showdiff": "Показване на промените",
        "accmailtext": "Случайно генерирана парола за [[User talk:$1|$1]] беше изпратена на $2. Паролата може да бъде променена от страницата ''[[Special:ChangePassword|„Промяна на паролата“]]'' след влизане в системата.",
        "newarticle": "(нова)",
        "newarticletext": "Последвахте препратка към страница, която все още не съществува.\nЗа да я създадете, просто започнете да пишете в долната текстова кутия\n(вижте [$1 помощната страница] за повече информация).",
-       "anontalkpagetext": "----''Това е дискусионната страница на анонимен потребител, който все още няма регистрирана сметка или не я използва, затова се налага да използваме IP-адрес, за да го идентифицираме. Такъв адрес може да се споделя от няколко потребители.''\n\n''Ако сте анонимен потребител и мислите, че тези неуместни коментари са отправени към вас, [[Special:CreateAccount|регистрирайте се]] или [[Special:UserLogin|влезте в системата]], за да избегнете евентуално бъдещо объркване с други анонимни потребители.''",
+       "anontalkpagetext": "----\n<em>Това е дискусионната страница на анонимен потребител, който все още няма регистрирана сметка или не я използва</em>\nЗатова се налага да използваме IP-адрес, за да го идентифицираме.\nТакъв адрес може да се споделя от няколко потребители.\nАко сте анонимен потребител и мислите, че тези неуместни коментари са отправени към вас, [[Special:CreateAccount|регистрирайте се]] или [[Special:UserLogin|влезте в системата]], за да избегнете евентуално бъдещо объркване с други анонимни потребители.",
        "noarticletext": "Понастоящем няма текст на тази страница. Можете да [[Special:Search/{{PAGENAME}}|потърсите за заглавието на страницата]] в други страници, да <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} потърсите в съответните дневници] или [{{fullurl:{{FULLPAGENAME}}|action=edit}} да я създадете]</span>.",
        "noarticletext-nopermission": "Текущо в тази страница няма текст.\nМожете да [[Special:Search/{{PAGENAME}}|потърсите заглавието на тази страница ]] в други страници или да <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} потърсите в съответните дневници]</span>, но нямате права да създадете тази страница.",
        "missing-revision": "Версия #$1 на страницата „{{FULLPAGENAME}}“ не съществува.\n\nТова обикновено се дължи на препратка от историята на страницата, която е била изтрита.\nПодробности могат да бъдат открити в [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дневника на изтриванията].",
        "userpage-userdoesnotexist": "Няма регистрирана потребителска сметка за „<nowiki>$1</nowiki>“. Изисква се потвърждение, че желаете да създадете/редактирате тази страница?",
        "userpage-userdoesnotexist-view": "Не е регистрирана потребителска сметка на име „$1“.",
        "blocked-notice-logextract": "В момента този потребител е блокиран.\nПо-долу за справка е показан последният запис от Дневника на блокиранията:",
-       "clearyourcache": "'''Забележка:''' За да се видят промените, необходимо е след съхраняване на страницата, кешът на браузъра да бъде изтрит.\n* '''Firefox / Safari:''' Задържа се клавиш ''Shift'' и се щраква върху ''Презареждане'' (''Reload'') или чрез клавишната комбинация ''Ctrl-Shift-R'' (''⌘-Shift-R'' за Mac);\n* '''Google Chrome:''' клавишна комбинация ''Ctrl-Shift-R'' (''⌘-Shift-R'' за Mac)\n* '''Internet Explorer:''' Задържа се клавиш ''Ctrl'' и се щраква върху ''Refresh'' или чрез клавишната комбинация ''CTRL-F5'';\n* '''Opera:''' кешът се изчиства през менюто ''Tools → Preferences''.",
+       "clearyourcache": "<strong>Забележка:</strong> За да се видят промените, необходимо е след съхраняване на страницата, кешът на браузъра да бъде изтрит.\n* <strong>Firefox / Safari:</strong> Задържа се клавиш <em>Shift</em> и се щраква върху <em>Презареждане</em> (<em>Reload</em>) или чрез клавишната комбинация <em>Ctrl-F5</em> or <em>Ctrl-R</em> (<em>⌘-R</em> за Mac);\n* <strong>Google Chrome:</strong> клавишна комбинация <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> за Mac)\n* <strong>Internet Explorer:</strong> Задържа се клавиш <em>Ctrl</em> и се щраква върху <em>Refresh</em> или чрез клавишната комбинация <em>Ctrl-F5</em>;\n* <strong>Opera:</strong> кешът се изчиства през менюто <em>Tools → Settings</em> (<em>Opera → Preferences</em> за Mac) след което <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
        "usercssyoucanpreview": "'''Съвет:''' Използвайте бутона „{{int:showpreview}}“, за да изпробвате новия код на CSS преди съхранението.",
        "userjsyoucanpreview": "'''Съвет:''' Използвайте бутона „{{int:showpreview}}“, за да изпробвате новия код на Джаваскрипт преди съхранението.",
        "usercsspreview": "'''Не забравяйте, че това е само предварителен преглед на кода на CSS. Страницата все още не е съхранена!'''",
        "undo-failure": "Редакцията не може да бъде върната поради конфликтни междинни редакции.",
        "undo-norev": "Редакцията не може да бъде върната, тъй като не съществува или е била изтрита.",
        "undo-summary": "Премахната редакция $1 на [[Special:Contributions/$2|$2]] ([[User talk:$2|беседа]])",
-       "cantcreateaccounttitle": "Невъзможно е да бъде създадена потребителска сметка.",
+       "undo-summary-username-hidden": "Отмяна на редакция $1 от скрит потребител",
        "cantcreateaccount-text": "[[User:$3|Потребител:$3]] е блокирал(а) създаването на сметки от този IP-адрес ('''$1''').\n\nПричината, изложена от $3, е ''$2''",
        "viewpagelogs": "Преглед на извършените административни действия по страницата",
        "nohistory": "Няма редакционна история за тази страница.",
        "history-feed-description": "Редакционна история на страницата в {{SITENAME}}",
        "history-feed-item-nocomment": "$1 в $2",
        "history-feed-empty": "Исканата страница не съществува — може да е била изтрита или преименувана. Опитайте да [[Special:Search|потърсите]] нови страници, които биха могли да са ви полезни.",
+       "history-edit-tags": "Редактиране етикетите на избраните редакции",
        "rev-deleted-comment": "(резюмето е премахнато)",
        "rev-deleted-user": "(името на автора е изтрито)",
        "rev-deleted-event": "(записът е изтрит)",
        "revdelete-unsuppress": "Премахване на ограниченията за възстановените версии",
        "revdelete-log": "Причина:",
        "revdelete-submit": "Прилагане към {{PLURAL:$1|избраната версия|избраните версии}}",
-       "revdelete-success": "'''Видимостта на версията беше променена успешно.'''",
+       "revdelete-success": "Видимостта на версията беше променена успешно.",
        "revdelete-failure": "'''Видимостта на редакцията не може да бъде обновена:'''\n$1",
-       "logdelete-success": "Видимостта на събитието беше променена.",
+       "logdelete-success": "Видимостта на дневника е установена.",
        "logdelete-failure": "'''Видимостта на дневника не може да бъде променяна:'''\n$1",
        "revdel-restore": "промяна на видимостта",
        "pagehist": "История на страницата",
        "mergehistory-go": "Показване на редакциите, които могат да се слеят",
        "mergehistory-submit": "Сливане на редакции",
        "mergehistory-empty": "Няма редакции, които могат да бъдат слети.",
-       "mergehistory-done": "$3 {{PLURAL:$3|версия|версии}} от $1 бяха успешно слети с редакционната история на [[:$2]].",
+       "mergehistory-done": "$3 {{PLURAL:$3|версия|версии}} от $1 {{PLURAL:$3|беше успешно слята|бяха успешно слети}} с редакционната история на [[:$2]].",
        "mergehistory-fail": "Невъзможно е да се извърши сливане на редакционните истории; проверете страницата и времевите параметри.",
        "mergehistory-no-source": "Изходната страница $1 не съществува.",
        "mergehistory-no-destination": "Целевата страница $1 не съществува.",
        "userrights": "Управление на потребителските права",
        "userrights-lookup-user": "Управляване на потребителските групи",
        "userrights-user-editname": "Потребителско име:",
-       "editusergroup": "Редактиране на потребителските групи",
-       "editinguser": "Промяна на потребителските права на потребител '''[[User:$1|$1]]''' $2",
+       "editusergroup": "Редактиране на {{GENDER:$1|потребителските}} групи",
+       "editinguser": "Промяна на потребителските права на {{GENDER:$1|потребител }} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Редактиране на потребителските групи",
        "saveusergroups": "Съхраняване на потребителските групи",
        "userrights-groupsmember": "Член на:",
        "grouppage-sysop": "{{ns:project}}:Администратори",
        "grouppage-bureaucrat": "{{ns:project}}:Бюрократи",
        "grouppage-suppress": "{{ns:project}}:Ревизори",
-       "right-read": "четене на страници",
+       "right-read": "Четене на страници",
        "right-edit": "редактиране на страници",
        "right-createpage": "Създаване на страници (които не са беседи)",
        "right-createtalk": "Създаване на дискусионни страници",
        "right-move-rootuserpages": "Преместване на основни потребителски страници",
        "right-move-categorypages": "Преместване на категорийни страници",
        "right-movefile": "Преместване на файлове",
-       "right-suppressredirect": "без създаване на пренасочване от старото име при преместване на страница",
+       "right-suppressredirect": "Ð\91ез създаване на пренасочване от старото име при преместване на страница",
        "right-upload": "Качване на файлове",
        "right-reupload": "Презаписване на съществуващ файл",
        "right-reupload-own": "Презаписване на съществуващ файл, качен от същия потребител",
        "right-reupload-shared": "Предефиниране на едноименните файлове от общото мултимедийно хранилище с локални",
        "right-upload_by_url": "качване на файл от URL адрес",
        "right-purge": "изчистване на складираното съдържание на страниците без показване на страница за потвърждение",
-       "right-autoconfirmed": "редактиране на полузащитени страници",
-       "right-bot": "третиране като автоматизиран процес",
+       "right-autoconfirmed": "Редактиране на полузащитени страници",
+       "right-bot": "Третиране като автоматизиран процес",
        "right-nominornewtalk": "Малките промени по дискусионните страници не предизвикват известието за ново съобщение",
-       "right-apihighlimits": "използване на крайните предели в API заявките",
+       "right-apihighlimits": "Ð\98зползване на крайните предели в API заявките",
        "right-writeapi": "Употреба на API за писане",
-       "right-delete": "изтриване на страници",
+       "right-delete": "Ð\98зтриване на страници",
        "right-bigdelete": "изтриване на страници с големи редакционни истории",
-       "right-deleterevision": "изтриване и възстановяване на отделни версии на страниците",
+       "right-deletelogentry": "Изтриване и възстановяване на отделни записи в дневника",
+       "right-deleterevision": "Изтриване и възстановяване на отделни версии на страниците",
        "right-deletedhistory": "преглеждане на записи от изтрити редакционни истории без асоциирания към тях текст",
        "right-deletedtext": "Преглед на изтрития текст и промените между изтритите версии",
        "right-browsearchive": "търсене на изтрити страници",
-       "right-undelete": "възстановяване на страници",
-       "right-suppressrevision": "преглед и възстановяване на версии, скрити от администраторите",
+       "right-undelete": "Ð\92ъзстановяване на страници",
+       "right-suppressrevision": "Ð\9fреглед и възстановяване на версии, скрити от администраторите",
        "right-viewsuppressed": "Преглеждане на версии, скрити от който и да е потребител",
-       "right-suppressionlog": "преглеждане на тайните дневници",
+       "right-suppressionlog": "Ð\9fреглеждане на тайните дневници",
        "right-block": "спиране на достъпа до редактиране",
-       "right-blockemail": "блокиране на потребители да изпращат писма по е-поща",
+       "right-blockemail": "Ð\91локиране на потребители да изпращат писма по е-поща",
        "right-hideuser": "блокиране и скриване на потребителско име",
        "right-ipblock-exempt": "пренебрегване на блокирания по IP blocks, автоматични блокирания и блокирани IP интервали",
        "right-unblockself": "Собствено отблокиране",
        "right-protect": "променяне на нивото на защита и редактиране на защитени страници",
-       "right-editprotected": "редактиране на защитени страници (без каскадна защита)",
-       "right-editinterface": "редактиране на потребителския интерфейс",
+       "right-editprotected": "Редактиране на страници защитени като „{{int:protect-level-sysop}}“",
+       "right-editinterface": "Редактиране на потребителския интерфейс",
        "right-editusercssjs": "редактиране на CSS и JS файловете на други потребители",
        "right-editusercss": "редактиране на CSS файловете на други потребители",
        "right-edituserjs": "редактиране на JS файловете на други потребители",
        "right-import": "внасяне на страници от други уикита",
        "right-importupload": "внасяне на страници от качен файл",
        "right-patrol": "отбелязване на редакциите като проверени",
-       "right-autopatrol": "автоматично отбелязване на редакции като проверени",
+       "right-autopatrol": "Ð\90втоматично отбелязване на редакции като проверени",
        "right-patrolmarks": "Показване на отбелязаните като патрулирани последни промени",
        "right-unwatchedpages": "преглеждане на списъка с ненаблюдаваните страници",
        "right-mergehistory": "сливане на редакционни истории на страници",
        "right-sendemail": "Изпращане на е-писма до другите потребители",
        "right-passwordreset": "Преглеждане на е-писма за възстановяване на парола",
        "grant-group-email": "Изпращане на е-писмо",
+       "grant-createaccount": "Създаване на сметки",
+       "grant-createeditmovepage": "Създаване, редактиране и преместване на страници",
        "grant-delete": "Изтриване на страници, редакции и записи в дневника",
+       "grant-editmycssjs": "Редактиране на личния CSS/JavaScript",
        "grant-editmyoptions": "Редактиране на вашите потребителски настройки",
        "grant-editmywatchlist": "редактиране на списъка ви за наблюдение",
        "grant-editpage": "Редактиране на съществуващи страници",
        "grant-editprotected": "Редактиране на защитени страници",
+       "grant-uploadfile": "Качване на нови файлове",
        "grant-basic": "Основни права",
        "grant-viewdeleted": "Преглед на изтрити файлове и страници",
        "grant-viewmywatchlist": "преглед на списъка ви за наблюдение",
        "action-viewmywatchlist": "преглед на списъка ви за наблюдение",
        "action-viewmyprivateinfo": "преглеждане на личните данни",
        "action-editmyprivateinfo": "редактиране на личната си информация",
+       "action-purge": "почисти кеша на тази страница",
        "nchanges": "$1 {{PLURAL:$1|промяна|промени}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|от последното посещение}}",
        "enhancedrc-history": "история",
        "recentchanges-legend-heading": "<strong>Легенда:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (вижте също [[Special:NewPages|списъка с нови страници]])",
        "recentchanges-submit": "Покажи",
-       "rcnotefrom": "Дадени са промените от <strong>$2</strong> (до <strong>$1</strong> показани).",
+       "rcnotefrom": "{{PLURAL:$5|Дадена е промяната|Дадени са промените}} от <strong>$3, $4</strong> (до <strong>$1</strong> показани).",
        "rclistfrom": "Показване на промени, като се започва от $3 $2",
        "rcshowhideminor": "$1 на малки промени",
        "rcshowhideminor-show": "Показване",
        "recentchangeslinked-summary": "Тук се показват последните промени на страниците, към които се препраща от дадена страница. При избиране на категория, се показват промените по страниците, влизащи в нея. ''Пример:'' Ако изберете страницата '''А''', която съдържа препратки към '''Б''' и '''В''', тогава ще можете да прегледате промените по '''Б''' и '''В'''.\n\nАко пък сложите отметка пред '''Обръщане на релацията''', ще можете да прегледате промените в обратна посока: ще се включат тези страници, които съдържат препратки към посочената страница.\n\nСтраниците от списъка ви за наблюдение се показват в '''получер'''.",
        "recentchangeslinked-page": "Име на страницата:",
        "recentchangeslinked-to": "Обръщане на релацията, така че да се показват промените на страниците, сочещи към избраната страница",
+       "recentchanges-page-added-to-category": "[[:$1]] е добавена към категория",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] е добавена към категория, [[Special:WhatLinksHere/$1|към страницата сочат други страници]]",
+       "recentchanges-page-removed-from-category": "[[:$1]] е премахната от категория",
        "upload": "Качи файл",
        "uploadbtn": "Качване",
        "reuploaddesc": "Връщане към формуляра за качване.",
        "php-uploaddisabledtext": "Качванията на файлове са спрени през PHP. Проверете настройката file_uploads.",
        "uploadscripted": "Файлът съдържа HTML или скриптов код, който може да бъде погрешно  интерпретиран от браузъра.",
        "uploadscriptednamespace": "Този SVG файл съдържа неправилно именно пространство „$1“",
+       "uploadinvalidxml": "XML-кода в качения файл не може да бъде анализиран.",
        "uploadvirus": "Файлът съдържа вирус! Подробности: $1",
        "uploadjava": "Файлът е ZIP файл, който съдържа Java .class файл.\nКачването на Java файлове не е позволено, тъй като могат да причинят заобикаляне на ограниченията за сигурност.",
        "upload-source": "Изходен файл",
        "uploadstash-summary": "Тази страница предоставя достъп до файловете, които са качени (или са в процес на качване), но все още не са публикувани в уикито. Тези файлове не са достъпни само за потребителя, който ги е качил.",
        "uploadstash-clear": "Изчистване на скритите качвания",
        "uploadstash-nofiles": "Нямате скрити файлове",
-       "uploadstash-badtoken": "Ð\98звÑ\8aÑ\80Ñ\88ване Ð½Ð° Ñ\82ова Ð´ÐµÐ¹Ñ\81Ñ\82вие Ðµ Ð½ÐµÑ\83Ñ\81пеÑ\88но, Ð²ÐµÑ\80оÑ\8fÑ\82но Ð·Ð°Ñ\80ади Ð¸Ð·Ñ\82екла Ñ\81еÑ\81иÑ\8f. Ð\9eпитайте отново.",
+       "uploadstash-badtoken": "Ð\98звÑ\8aÑ\80Ñ\88ване Ð½Ð° Ñ\82ова Ð´ÐµÐ¹Ñ\81Ñ\82вие Ðµ Ð½ÐµÑ\83Ñ\81пеÑ\88но, Ð²ÐµÑ\80оÑ\8fÑ\82но Ð·Ð°Ñ\80ади Ð¸Ð·Ñ\82екла Ñ\81еÑ\81иÑ\8f. Ð\9cолÑ\8f, Ð¾питайте отново.",
        "uploadstash-errclear": "Изчистването на файловете беше неуспешно.",
        "uploadstash-refresh": "Обновяване на списъка с файлове",
+       "uploadstash-thumbnail": "преглед на миниатюра",
        "img-auth-accessdenied": "Достъпът е отказан",
        "img-auth-nopathinfo": "Липсва PATH_INFO.\nВашият сървър не е конфигуриран да предава тази информация.\nТой може да е базиран на CGI и да не може да поддържа img_auth.\nВижте https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "img-auth-notindir": "Търсеният път не е в настроената директория за качвания.",
        "pager-older-n": "{{PLURAL:$1|по-стара 1|по-стари $1}}",
        "suppress": "Премахване от публичния архив",
        "querypage-disabled": "Тази специална страница е изключена, защото затруднява производителността на уикито.",
+       "apihelp": "Помощ за API",
        "apihelp-no-such-module": "Модул \"$1\" не беше намерен.",
+       "apisandbox": "Пясъчник за API",
        "apisandbox-fullscreen": "Разшири полето",
        "apisandbox-reset": "Изчистване",
-       "apisandbox-examples": "Пример",
+       "apisandbox-retry": "Повторен опит",
+       "apisandbox-examples": "Примери",
+       "apisandbox-dynamic-parameters-add-label": "Добавяне на параметър:",
        "apisandbox-dynamic-parameters-add-placeholder": "Име на параметъра",
        "apisandbox-results": "Резултати",
+       "apisandbox-request-url-label": "URL-адрес на заявката:",
        "booksources": "Източници на книги",
        "booksources-search-legend": "Търсене на информация за книга",
        "booksources-search": "Търсене",
        "activeusers-hidesysops": "Скриване на администраторите",
        "activeusers-noresult": "Няма намерени потребители.",
        "listgrouprights": "Права по потребителски групи",
-       "listgrouprights-summary": "По-долу на тази страница е показан списък на групите потребители в това уики с асоциираните им права за достъп. Допълнителна информация за отделните права може да бъде намерена [[{{MediaWiki:Listgrouprights-helppage}}|тук]].",
+       "listgrouprights-summary": "По-долу на тази страница е показан списък на групите потребители в това уики и права им за достъп. Допълнителна информация за отделните права може да бъде намерена [[{{MediaWiki:Listgrouprights-helppage}}|тук]].",
        "listgrouprights-key": "Легенда:\n* <span class=\"listgrouprights-granted\">Поверено право</span>\n* <span class=\"listgrouprights-revoked\">Отнето право</span>",
        "listgrouprights-group": "Група",
        "listgrouprights-rights": "Права",
        "sessionfailure": "Изглежда има проблем със сесията ви; действието беше отказано като предпазна мярка срещу крадене на сесията. Натиснете бутона за връщане на браузъра, презаредете страницата, от която сте дошли, и опитайте отново.",
        "changecontentmodel-title-label": "Заглавие на страницата",
        "changecontentmodel-reason-label": "Причина:",
+       "changecontentmodel-success-text": "Типът на съдържанието на [[:$1]] е успешно променен.",
+       "logentry-contentmodel-change-revertlink": "връщане",
+       "logentry-contentmodel-change-revert": "връщане",
        "protectlogpage": "Дневник на защитата",
        "protectlogtext": "Списък на промените в защитата за страницата.\nМожете да прегледате и [[Special:ProtectedPages|списъка на текущо защитените страници]].",
        "protectedarticle": "защити „[[$1]]“",
        "sp-contributions-username": "IP-адрес или потребителско име:",
        "sp-contributions-toponly": "Показване само на последните редакции",
        "sp-contributions-newonly": "Показване само на редакции за създаването на страници",
+       "sp-contributions-hideminor": "Скриване на малки промени",
        "sp-contributions-submit": "Търсене",
        "whatlinkshere": "Какво сочи насам",
        "whatlinkshere-title": "Страници, които сочат към „$1“",
        "whatlinkshere-prev": "{{PLURAL:$1|предишна|предишни $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|следваща|следващи $1}}",
        "whatlinkshere-links": "← препратки",
-       "whatlinkshere-hideredirs": "$1 Ð½Ð° Ð¿Ñ\80енаÑ\81оÑ\87ваÑ\89и Ñ\81Ñ\82Ñ\80аниÑ\86и",
-       "whatlinkshere-hidetrans": "$1 Ð½Ð° Ð²ÐºÐ»Ñ\8eÑ\87ени Ñ\81Ñ\82Ñ\80аниÑ\86и",
-       "whatlinkshere-hidelinks": "$1 Ð½Ð° Ð¿Ñ\80епÑ\80аÑ\82ки",
+       "whatlinkshere-hideredirs": "$1 Ð¿Ñ\80енаÑ\81оÑ\87ваниÑ\8f",
+       "whatlinkshere-hidetrans": "$1 Ð²ÐºÐ»Ñ\8eÑ\87ваниÑ\8f",
+       "whatlinkshere-hidelinks": "$1 препратки",
        "whatlinkshere-hideimages": "$1 на препратки към файла",
        "whatlinkshere-filters": "Филтри",
        "whatlinkshere-submit": "Отваряне",
        "unblocked-ip": "[[Special:Contributions/$1|$1]] е отблокиран.",
        "blocklist": "Блокирани потребители",
        "ipblocklist": "Блокирани потребители",
-       "ipblocklist-legend": "Ð\9eÑ\82кÑ\80иване на блокиран потребител",
+       "ipblocklist-legend": "ТÑ\8aÑ\80Ñ\81ене на блокиран потребител",
        "blocklist-userblocks": "Скриване на блокирани потребителски сметки",
-       "blocklist-tempblocks": "Скриване на срочните блокирания",
+       "blocklist-tempblocks": "Скриване на срочни блокирания",
        "blocklist-addressblocks": "Скриване на отделни блокирани IP адреси",
        "blocklist-rangeblocks": "Скриване на блокиранията по IP диапазон",
        "blocklist-timestamp": "Дата и час",
        "ipblocklist-submit": "Търсене",
        "ipblocklist-localblock": "Локално блокиране",
        "ipblocklist-otherblocks": "{{PLURAL:$1|Друго блокиране|Други блокирания}}",
-       "infiniteblock": "неограничено",
+       "infiniteblock": "неограничен",
        "expiringblock": "изтича на $1 в $2",
        "anononlyblock": "само анон.",
        "noautoblockblock": "автоблокировката е изключена",
        "movenosubpage": "Тази страница няма подстраници.",
        "movereason": "Причина:",
        "revertmove": "връщане",
-       "delete_and_move_text": "== Наложително изтриване ==\n\nЦелевата страница „[[:$1]]“ вече съществува. Искате ли да я изтриете, за да освободите място за преместването?",
+       "delete_and_move_text": "Целевата страница „[[:$1]]“ вече съществува.\nИскате ли да я изтриете, за да освободите място за преместването?",
        "delete_and_move_confirm": "Да, искам да изтрия тази страница.",
        "delete_and_move_reason": "Изтрита, за да се освободи място за преместване от „[[$1]]“",
        "selfmove": "Страницата не може да бъде преместена, тъй като целевото име съвпада с първоначалното ѝ заглавие.",
        "pageinfo-toolboxlink": "Информация за страницата",
        "pageinfo-redirectsto": "Пренасочване към",
        "pageinfo-redirectsto-info": "инфо",
+       "pageinfo-contentpage": "Отчита се като страница със съдържание",
        "pageinfo-contentpage-yes": "Да",
        "pageinfo-protect-cascading": "Каскадни защити, започващи от тази страница",
        "pageinfo-protect-cascading-yes": "Да",
        "patrol-log-page": "Дневник на патрула",
        "patrol-log-header": "Тази страница съдържа дневник на проверените версии.",
        "log-show-hide-patrol": "$1 на Дневника на патрула",
+       "log-show-hide-tag": "$1 на дневника на отбелязванията",
        "deletedrevision": "Изтрита стара версия $1",
        "filedeleteerror-short": "Грешка при изтриване на файл: $1",
        "filedeleteerror-long": "Възникнаха грешки при изтриването на файла:\n\n$1",
        "exif-dc-contributor": "Сътрудници",
        "exif-dc-date": "Дата(и)",
        "exif-dc-rights": "Права",
+       "exif-dc-type": "Вид медия",
        "exif-isospeedratings-overflow": "По-голяма от 65535",
        "exif-iimcategory-ace": "Изкуствa, култура и забавление",
        "exif-iimcategory-clj": "Престъпност и право",
        "scarytranscludefailed": "[Зареждането на шаблона за $1 не сполучи]",
        "scarytranscludetoolong": "[Адресът е твърде дълъг]",
        "deletedwhileediting": "'''Внимание''': Страницата е била изтрита, след като сте започнали да я редактирате!",
-       "confirmrecreate": "Потребителят [[User:$1|$1]] ([[User talk:$1|беседа]]) е изтрил страницата, откакто сте започнали да я редактирате, като е посочил следното обяснение:\n: ''$2''\nПотвърдете, че наистина желаете да създадете страницата отново.",
-       "confirmrecreate-noreason": "Потребител [[User:$1|$1]] ([[User talk:$1|беседа]]) изтри тази страница след като сте започнали да я редактирате.  Необходимо е потвърждение, че наистина желаете да създадете страницата отново.",
+       "confirmrecreate": "Потребител [[Потребител:$1|$1]] ([[Потребител беседа:$1|беседа]]) е {{GENDER:$1|изтрил}} страницата, откакто сте започнали да я редактирате, като е посочил следното обяснение:\n: <em>$2</em>\nПотвърдете, че наистина желаете да създадете страницата отново.",
+       "confirmrecreate-noreason": "Потребител [[User:$1|$1]] ([[User talk:$1|беседа]]) {{GENDER:$1|изтри}} тази страница след като сте започнали да я редактирате. Необходимо е потвърждение, че наистина желаете да създадете страницата отново.",
        "recreate": "Ново създаване",
        "confirm_purge_button": "Добре",
        "confirm-purge-top": "Изчистване на складираното копие на страницата?",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 страница беше добавена|$1 страници бяха добавени}}:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|Една страница беше премахната|$1 страници бяха премахнати}}:",
        "watchlistedit-clear-legend": "Изчистване на списъка за наблюдение",
+       "watchlistedit-clear-explain": "Всички заглавия ще бъдат премахнати от списъка ви за наблюдение",
        "watchlistedit-clear-titles": "Заглавия:",
        "watchlistedit-clear-submit": "Изчистване на списъка за наблюдение (Необратимо!)",
        "watchlistedit-clear-done": "Списъкът за наблюдение беше изчистен.",
+       "watchlistedit-clear-removed": "{{PLURAL:$1|1 заглавие беше премахнато|$1 заглавия бяха премахнати}}:",
        "watchlisttools-clear": "Изчистване на списъка за наблюдение",
        "watchlisttools-view": "Преглед на списъка за наблюдение",
        "watchlisttools-edit": "Преглед и редактиране на списъка за наблюдение",
        "revdelete-uname-hid": "скрито потребителско име",
        "revdelete-restricted": "добавени ограничения за администраторите",
        "revdelete-unrestricted": "премахнати ограничения за администраторите",
-       "logentry-block-block": "$1 блокира {{GENDER:$4|$3}} със срок на изтичане $5 $6",
-       "logentry-block-reblock": "$1 промени настройките на блокиране на {{GENDER:$4|$3}} със срок на изтичане $5 $6",
+       "logentry-block-block": "$1 {{GENDER:$2|блокира }} {{GENDER:$4|$3}} със срок на изтичане $5 $6",
+       "logentry-block-reblock": "$1 {{GENDER:$2|промени }} настройките на блокиране на {{GENDER:$4|$3}} със срок на изтичане $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|блокира}} {{GENDER:$4|$3}} със срок на изтичане $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|промени}} настройките на блокиране на {{GENDER:$4|$3}} със срок на изтичане $5 $6",
        "logentry-move-move": "$1 {{GENDER:$2|премести}} страница „$3“ като „$4“",
        "logentry-newusers-create2": "$1 {{GENDER:$2|създаде}} потребителска сметка $3",
        "logentry-newusers-byemail": "$1 {{GENDER:$2|създаде}} потребителската сметка $3, като паролата за нея беше изпратена по е-поща",
        "logentry-newusers-autocreate": "Сметката $1 беше {{GENDER:$2|създадена}} автоматично",
-       "logentry-rights-rights": "$1 {{GENDER:$2|промени}} потребителската група на $3 от $4 на $5",
+       "logentry-rights-rights": "$1 {{GENDER:$2|промени}} потребителската група на {{GENDER:$6|$3}} от $4 на $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|промени}} потребителската група на $3",
        "logentry-rights-autopromote": "$1 е автоматично {{GENDER:$2|повишен|повишена}} от $4 до $5",
        "logentry-upload-upload": "$1 {{GENDER:$2|качи}} $3",
        "api-error-badaccess-groups": "Нямате необходимите права, за да качвате файлове в това уики.",
        "api-error-badtoken": "Вътрешна грешка: неправилен маркер.",
        "api-error-copyuploaddisabled": "Качването през URL е забранено на този сървър.",
-       "api-error-duplicate": "На сайта вече има качени {{PLURAL:$1|друг файл|други файла}} с идентично съдържание.",
+       "api-error-duplicate": "На сайта вече има {{PLURAL:$1|качен друг файл|качени други файлове}} с идентично съдържание.",
        "api-error-duplicate-archive": "На сайта вече е имало {{PLURAL:$1|качен друг файл|качени други файла}} с идентично съдържание, {{PLURAL:$1|който е бил изтрит|които са били изтрити}}.",
        "api-error-empty-file": "Заявеният за качване файл беше празен.",
        "api-error-emptypage": "Създаването на нови, празени страници, не е разрешено.",
        "mw-widgets-dateinput-placeholder-month": "ГГГГ-ММ",
        "mw-widgets-titleinput-description-new-page": "страницата все още не съществува",
        "mw-widgets-titleinput-description-redirect": "пренасочване към $1",
-       "api-error-blacklisted": "Моля, изберете различно, описателно заглавие.",
-       "randomrootpage": "Случайна начална страница",
+       "randomrootpage": "Случайна основна страница",
+       "log-action-filter-block": "Вид на блокирането:",
+       "log-action-filter-delete": "Вид на изтриването:",
+       "log-action-filter-newusers": "Вид на създаването на акаунт:",
        "log-action-filter-protect": "Тип защита:",
+       "log-action-filter-rights": "Вид на промяната на правата за достъп:",
        "log-action-filter-upload": "Тип качване:",
        "log-action-filter-all": "Всички",
        "log-action-filter-block-block": "Блокиране",
        "log-action-filter-upload-upload": "Ново качване",
        "log-action-filter-upload-overwrite": "Повторно качване",
        "authmanager-authplugin-setpass-bad-domain": "Невалиден домейн.",
+       "authmanager-retype-help": "Парола (повторно) за потвърждение.",
        "authmanager-email-label": "Е-поща",
        "authmanager-email-help": "Електронна поща",
        "authmanager-realname-label": "Истинско име",
+       "authmanager-realname-help": "Истинско име на потребителя",
+       "authmanager-provider-temporarypassword": "Временна парола",
        "authprovider-resetpass-skip-label": "Пропусни",
        "specialpage-securitylevel-not-allowed-title": "Не е позволено",
        "cannotauth-not-allowed-title": "Достъпът е отказан"
index 6f85ebb..5b12678 100644 (file)
        "passwordreset-emailtext-user": "{{SITENAME}} ($4) पर सदस्य $1 राउर {{PLURAL:$3|खाता}} के गुप्तशब्द के पुनर्स्थापित करे के अनुरोध कइले बानी। इ ई-मेल पता से निम्न {{PLURAL:$3|खाता जुड़ल बा}}:\n\n$2\n\n{{PLURAL:$3|इ}} अस्थायी गुप्तशब्द {{PLURAL:$5|एक दिन|$5 दिन}} के बाद काम ना करी।\nरउआ खाता में प्रवेश करके एगो नया गुप्तशब्द अभीये चुन लेवे के चाहीं। यदि इ अनुरोध केहु अउर कइले बा, या फिर रउआ आपन मूल गुप्तशब्द याद आ गईल बा, अउर रउआ {{PLURAL:$3|आपन}} गुप्तशब्द नईखीं बदले के चाहत त, रउआ इ संदेश के अनदेखा कर के आपन पुरनका गुप्तशब्द के प्रयोग जारी रख सकत बानीं।",
        "passwordreset-emailelement": "सदस्यनाम: \n$1\n\nअस्थायी गुप्तशब्द: \n$2",
        "passwordreset-emailsentemail": "एगो गुप्तशब्द रिसेट ई-मेल भेजल जा चुकल बा।",
-       "passwordreset-emailsent-capture": "नीचे दिखावल गईल गुप्तशब्द पुनर्स्थापना ई-मेल भेज दिहल गईल बा।",
-       "passwordreset-emailerror-capture": "नीचे दिखावल गईल गुप्तशब्द पुनर्स्थापना ई-मेल उत्पन्न करल गईल रहल, परंतु उ के {{GENDER:$2|सदस्य}} के भेजे के क्रिया असफल रहल।\nत्रुटि: $1",
        "changeemail": "ई-मेल पता बदलीं",
        "changeemail-header": "खाता के ई-मेल पता बदलीं",
        "changeemail-no-info": "इ पन्ना के सिधे प्रयोग करे खातिर रउआ पहिले खाता में प्रवेश करे के पड़ी।",
        "newarticle": "(नया)",
        "newarticletext": "रउआ एगो अइसन कड़ी के पन्ना के अनुसरण कइले बानी जवन अभी तक उपलब्ध नइखे।\nपन्ना बनावे खातिर, नीचे के बाकस में टाइप करे के शुरु करीं (ज्यादा जानकारी खातिर देखीं [$1 मदद पन्ना])।\nयदि रउआ अहिजा गलती से आ गइल बानी त, आपन ब्राउजर के '''बैक''' (Back) बटन दबाईं!",
        "anontalkpagetext": "----''इ वार्ता पन्ना उन अनाम सदस्यन खातिर बा जिन्हन के या त खाता नइखे खोलल गइल या खाता के प्रयोग नइखन करत।\nएहि खातिर उन्हन के पहिचान खातिर हमनी के उनकर आइ॰पी पता के प्रयोग करे के पड़ेला।\nआइ॰पी पता कई सदस्यन खातिर साझा हो सकत बा।\nयदि आप एगो अनाम सदस्य बानी अउर आपके लागत बा कि आपके बारे में अप्रासंगिक टीका टिप्पणी करल गइल बा त कृपया [[Special:CreateAccount|सदस्यता लिहीं]] या [[Special:UserLogin|सत्रारंभ करीं]] ताकि अन्य अनाम सदस्यन में से आपके अलग से पहिचानल जा सके।''",
-       "noarticletext": "à¤\8f à¤ªà¤¨à¥\8dना à¤®à¥\87 à¤\85भà¥\80 à¤²à¥\87 à¤\95à¥\8cनà¥\8bà¤\82 à¤¸à¤¾à¤®à¤\97à¥\8dरà¥\80 à¤¨à¤\87à¤\96à¥\87। \nरà¤\89à¤\86 à¤¦à¥\81सरा à¤ªà¤¨à¥\8dना à¤®à¥\87à¤\82 [[Special:Search/{{PAGENAME}}|à¤\8f à¤\9fाà¤\87à¤\9fिल à¤\95à¥\87 à¤\96à¥\8bà¤\9c]] à¤\95र à¤¸à¤\95त à¤¬à¤¾à¤¨à¥\80à¤\82।",
+       "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 à¤\95à¥\87 à¤\88 à¤ªà¤¨à¥\8dना à¤¬à¤¨à¤¾à¤µà¥\87 à¤\95à¥\87 à¤ªà¤°à¤®à¥\80शन à¤¨à¤\87à¤\96à¥\87।",
        "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कृपया जाँच लीं कि आप इ पन्ना संपादित अथवा निर्मित करे के चाहत बानी कि ना।",
        "undo-nochange": "लागत बा की ई संपादन पहिलहीं वापस लिहल जा चुकल बाटे।",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|वार्ता]]) के द्वारा कइल $1 बदलाव के वापस कइल गइल",
        "undo-summary-username-hidden": "एगो छिपल सदस्य द्वारा कइल बदलाव $1 वापस कइल गइल",
-       "cantcreateaccounttitle": "खाता खुल नईखे सकत",
        "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": "ए पन्ना खातिर लॉग कुल देखीं",
        "revisionasof": "$1 ले भइल नया बदलाव",
        "revision-info": "{{GENDER:$6|$2}}$7 के द्वारा $1 के संशोधन",
        "previousrevision": "← पुरान संशोधन",
-       "nextrevision": "नया à¤¸à¤\82à¤ोधन →",
-       "currentrevisionlink": "हाल à¤\95à¥\87 à¤¸à¤\82à¤ोधन",
+       "nextrevision": "नया à¤¸à¤\82सोधन →",
+       "currentrevisionlink": "हाल à¤\95à¥\87 à¤¸à¤\82सोधन",
        "cur": "हाल",
        "next": "अगिला",
        "last": "पछिला",
        "contributions": "{{GENDER:$1|सदस्य}} योगदान",
        "contributions-title": " $1 खातिर प्रयोगकर्ता योगदान",
        "mycontris": "हमार योगदान",
+       "anoncontribs": "योगदान",
        "nocontribs": "ई मानदंड से मिलत जुलत कौनो बदलाव ना मिलल।",
        "uctop": "(हाल के)",
        "month": "महीना से (आ ओ से पहिले):",
        "whatlinkshere-next": "{{PLURAL:$1|अगिला|अगिला $1}}",
        "whatlinkshere-links": "← कड़ी",
        "whatlinkshere-hideredirs": "$1 अनुप्रेषण",
-       "whatlinkshere-hidetrans": "$1 à¤\9fà¥\8dरानà¥\8dसà¥\8dà¤\95à¥\8dलà¥\8dयà¥\81à¤\9cनà¥\8dस",
+       "whatlinkshere-hidetrans": "$1 à¤\9fà¥\8dराà¤\82सà¤\95à¥\8dलà¥\82à¤\9cन",
        "whatlinkshere-hidelinks": "$1 कड़ी",
        "whatlinkshere-hideimages": "$1 फ़ाइल लिंक",
        "whatlinkshere-filters": "छननी",
        "importbadinterwiki": "खराब इंटरविकि कड़ी",
        "importsuccess": "आयात पूरा भइल!",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|संशोधन|संशोधनसभ}} लावल गइल",
-       "tooltip-pt-userpage": "हमार सदस्य पन्ना",
-       "tooltip-pt-mytalk": "हमार बातचीत पन्ना",
-       "tooltip-pt-preferences": "हमार सेटिंग",
+       "tooltip-pt-userpage": "{{GENDER:|राउर सदस्य}} पन्ना",
+       "tooltip-pt-mytalk": "{{GENDER:|राउर}} बातचीत पन्ना",
+       "tooltip-pt-preferences": "{{GENDER:|राउर}} पसंद",
        "tooltip-pt-watchlist": "राउर धियानसूची में पन्ना सब के लिस्ट",
-       "tooltip-pt-mycontris": "राउर सब योगदान के लिस्ट",
+       "tooltip-pt-mycontris": "{{GENDER:|राउर}} योगदान के एगो लिस्ट",
        "tooltip-pt-login": "रउआ के खाता मे प्रवेश (लॉग इन) खातिर उत्साहित कइल जात बा, बाकि ई जरूरी नइखे",
        "tooltip-pt-logout": "लॉग आउट",
        "tooltip-pt-createaccount": "हमनी के सुझाव बा की आप खाता बनाईं आ लॉग इन करीं, बाकी ई जरूरी नइखे",
        "tooltip-ca-talk": "सामग्री पन्ना की बारे में बात-चीत",
-       "tooltip-ca-edit": "रà¤\89à¤\86 à¤\8f à¤ªà¤¨à¥\8dना à¤\95à¥\87 à¤¸à¤\82पादन à¤\95र à¤¸à¤\95त à¤¬à¤¾à¤¨à¥\80। à¤ªà¤¨à¥\8dना à¤¸à¤¹à¥\87à¤\9cà¥\87 à¤¸à¥\87 à¤ªà¤¹à¤¿à¤²à¥\87 à¤¨à¤®à¥\82ना  à¤¦à¥\87à¤\96ाà¤\88à¤\82 à¤¬à¤\9fन à¤\95à¥\87 à¤\87सà¥\8dतà¥\87माल à¤\95रà¥\80à¤\82।",
+       "tooltip-ca-edit": "à¤\8f à¤ªà¤¨à¥\8dना à¤\95à¥\87 à¤¸à¤\82पादन à¤\95रà¥\80à¤\82",
        "tooltip-ca-addsection": "एगो नया खंड शुरु करीं",
        "tooltip-ca-viewsource": "ए पन्ना के सुरक्षित कइ दिहल गइल बा। आप एकर स्रोत देख सकत बानी।",
        "tooltip-ca-history": "ए पन्ना के पछिला संशोधन",
        "tooltip-t-whatlinkshere": "इहाँ जुड़े वाला सब विकि पन्नवन के लिस्ट",
        "tooltip-t-recentchangeslinked": "ए पन्ना से जुड़ल पन्नवन पर तुरंत भइल बदलाव",
        "tooltip-feed-atom": "ए पन्ना खातिर एटम फीड",
-       "tooltip-t-contributions": "ए सदस्य के कुल योगदान के लिस्ट",
+       "tooltip-t-contributions": "{{GENDER:$1|एह सदस्य}} के योगदान के एक ठो लिस्ट",
        "tooltip-t-upload": "फाइल अपलोड करीं",
        "tooltip-t-specialpages": "खास पन्नवन के लिस्ट",
        "tooltip-t-print": "ए पन्ना के छापे लायक संस्करण",
        "tooltip-t-permalink": "ए पन्ना के संशोधन खातिर स्थायी कड़ी।",
        "tooltip-ca-nstab-main": "सामग्री पन्ना देखीं",
        "tooltip-ca-nstab-user": "सदस्य-पन्ना देखीं",
-       "tooltip-ca-nstab-special": "à¤\88 à¤\8fà¤\97à¥\8b à¤\96ास à¤ªà¤¨à¥\8dना à¤¹, à¤°à¤\89à¤\86à¤\81 à¤\8f à¤ªà¤¨à¥\8dना à¤\95à¥\87 à¤¸à¤\82पादन à¤¨à¤\87à¤\96à¥\80à¤\82 à¤\95र à¤¸à¤\95त",
+       "tooltip-ca-nstab-special": "à¤\88 à¤\8fà¤\97à¥\8b à¤\96ास à¤ªà¤¨à¥\8dना à¤¹, à¤\8fà¤\95र à¤¸à¤\82पादन à¤¨à¤¾ à¤¹à¥\8b à¤¸à¤\95à¥\87ला",
        "tooltip-ca-nstab-image": "फाइल के पन्ना देखीं",
-       "tooltip-ca-nstab-template": "साà¤\81à¤\9aा देखीं",
+       "tooltip-ca-nstab-template": "à¤\9fà¥\87मà¥\8dपलà¥\87à¤\9f देखीं",
        "tooltip-ca-nstab-category": "श्रेणी के पन्ना देखीं",
        "tooltip-save": "बदलाव के सहेजीं",
        "tooltip-preview": "आपन द्वारा कियल गइल बदलाव के देखीं, संजोये से पहले ईका इस्तेमाल करीं!",
        "logentry-delete-delete": "$1 द्वारा पन्ना $3 {{GENDER:$2|हटा}} दिहल गइल",
        "revdelete-restricted": "प्रबंधक पर प्रतिबंध लागू",
        "revdelete-unrestricted": "प्रबंधक पर से प्रतिबंध समाप्त",
+       "logentry-move-move": "$1 पन्ना $3 के $4 पर {{GENDER:$2|स्थानांतरण कइलें}}",
        "logentry-newusers-create": "खाता $1 {{GENDER:$2|बनावल गइल}}",
        "revdelete-summary": "सारांश संपादन",
        "searchsuggest-search": "खोजीं",
index 4d7614f..3d9a515 100644 (file)
@@ -28,7 +28,9 @@
                        "Sayma Jahan",
                        "Macofe",
                        "Bodhisattwa",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "আজিজ",
+                       "Kayser Ahmad"
                ]
        },
        "tog-underline": "সংযোগগুলির নিচে দাগ দেখানো হোক:",
        "missingarticle-rev": "(সংস্করণ#: $1)",
        "missingarticle-diff": "(পার্থক্য: $1, $2)",
        "readonly_lag": "ডাটাবেজ স্বয়ংক্রিয়ভাবে বন্ধ করে দেয়া হয়েছে, যাতে অধীন ডাটাবেজ সার্ভারগুলি প্রধান ডাটাবেজ সার্ভারের অবস্থায় আসতে পারে।",
+       "nonwrite-api-promise-error": "'Promise-Non-Write-API-Action' HTTP শিরলেখে পাঠানো হয়েছিল কিন্তু অনুরোধটি একটি API লিখন মডিউলে ছিল।",
        "internalerror": "আভ্যন্তরীণ ত্রুটি",
        "internalerror_info": "আভ্যন্তরীণ ত্রুটি: $1",
        "internalerror-fatal-exception": "\"$1\" ধরনের মারাত্মক ব্যতিক্রম",
        "yourpasswordagain": "পাসওয়ার্ড আবার লিখুন:",
        "createacct-yourpasswordagain": "পাসওয়ার্ড নিশ্চিত করুন",
        "createacct-yourpasswordagain-ph": "আবারও পাসওয়ার্ড লিখুন",
-       "remembermypassword": "এই ব্রাউজারে আমার প্রবেশ মনে রাখা হোক (সর্বোচ্চ $1 {{PLURAL:$1|দিনের}} জন্য)",
        "userlogin-remembermypassword": "আমাকে প্রবেশ অবস্থায় রাখো",
        "userlogin-signwithsecure": "নিরাপদ সংযোগ ব্যবহার করুন",
+       "cannotlogin-title": "প্রবেশ করতে পারবেন না",
        "cannotloginnow-title": "এখন প্রবেশ করা যাবে না",
        "cannotloginnow-text": "$1 ব্যবহার করার সময় প্রবেশ করা সম্ভব নয়।",
        "yourdomainname": "আপনার ডোমেইন:",
        "createacct-email-ph": "আপনার ইমেইল ঠিকানা যোগ করুন",
        "createacct-another-email-ph": "আপনার ইমেইল ঠিকানা প্রবেশ করান",
        "createaccountmail": "একটি র‌্যান্ডম পাসওয়ার্ড নির্বাচন করুন এবং নির্ধারিত ইমেইল ঠিকানায় পাঠিয়ে দিন",
+       "createaccountmail-help": "পাসওয়ার্ড জানা ছাড়াই অন্য ব্যক্তির জন্য অ্যাকাউন্ট তৈরি করতে ব্যবহার করা যেতে পারে।",
        "createacct-realname": "আসল নাম (ঐচ্ছিক)",
        "createaccountreason": "কারণ:",
        "createacct-reason": "কারণ",
        "nocookiesnew": "ব্যবহারকারীর অ্যাকাউন্টটি সৃষ্টি করা হয়েছে, কিন্তু আপনি এখনও অ্যাকাউন্টে প্রবেশ করেননি। {{SITENAME}}-তে কুকি ব্যবহার করে ব্যবহারকারীদের অ্যাকাউন্টে প্রবেশ করানো হয়। আপনার ব্রাউজারে কুকিগুলি নিষ্ক্রিয় করা আছে। অনুগ্রহ করে কুকিগুলি সক্রিয় করুন এবং আপনার নতুন ব্যবহারকারী নাম ও পাসওয়ার্ড ব্যবহার করে অ্যাকাউন্টে প্রবেশ করুন।",
        "nocookieslogin": "ব্যবহারকারীদের প্রবেশ সম্পন্ন করতে {{SITENAME}} কুকি ব্যবহার করে। আপনার ব্রাউজারে কুকি নিষ্ক্রিয় করা আছে। কুকি চালু করে আবার চেষ্টা করুন।",
        "nocookiesfornew": "ব্যবহারকারীর অ্যাকাউন্ট তৈরি হয়নি, কারণ এর উৎস সম্পর্কে আমরা নিশ্চিত নই।\nনিশ্চিত করুন আপনার কুকি সক্রিয় রয়েছে, পাতাটি পুনরায় লোড করে আবার চেষ্টা করুন।",
+       "createacct-loginerror": "অ্যাকাউন্ট সফলভাবে তৈরি করা হয়েছে কিন্তু আপনি স্বয়ংক্রিয়ভাবে প্রবেশ করতে পারবেন না। দয়া করে [[Special:UserLogin|ম্যানুয়াল প্রবেশ]] করতে এগিয়ে যান।",
        "noname": "আপনি সঠিক ব্যবহারকারী নাম নির্দিষ্ট করেননি।",
        "loginsuccesstitle": "প্রবেশ করেছেন",
        "loginsuccess": "<strong>আপনি এইমাত্র \"$1\" নামে {{SITENAME}}-তে প্রবেশ করেছেন।</strong>",
        "passwordreset-emailelement": "ব্যবহারকারী নাম: \n$1\n\nঅস্থায়ী পাসওয়ার্ড: \n$2",
        "passwordreset-emailsentemail": "যদি এই ই-মেইল ঠিকানা আপনার অ্যাকাউন্টের সাথে সংযুক্ত করা থাকে, তাহলে একটি পাসওয়ার্ড বদলের ইমেইল পাঠানো হবে।",
        "passwordreset-emailsentusername": "যদি এই ব্যবহারকারী নামের সাথে ই-মেইল ঠিকানা সংযুক্ত করা থাকে, তাহলে একটি পাসওয়ার্ড বদলের ইমেইল পাঠানো হবে।",
-       "passwordreset-emailsent-capture": "স্মরণ করিয়ে দেয়ার জন্য একটি ইমেইল করা হয়েছে, যা নিচে দেখানো হচ্ছে।",
-       "passwordreset-emailerror-capture": "স্মরণ করিয়ে দেয়ার জন্য একটি ইমেইল তৈরী করা হয়েছিল, যা নিচে দেখানো হচ্ছে, তবে $1 {{GENDER:$2|ব্যবহারকারীকে}} এটি পাঠানো যায়নি!",
        "passwordreset-nocaller": "একটি আহ্বানকারী প্রদান করা আবশ্যক",
        "passwordreset-nosuchcaller": "আহ্বানকারীর অস্তিত্ব নেই: $1",
        "passwordreset-invalideamil": "ভুল ইমেইল ঠিকানা",
+       "passwordreset-nodata": "একটি ব্যবহারকারীর নাম বা একটি ইমেল ঠিকানা দুটির একটিও সরবরা দেয়া হয়নি",
        "changeemail": "ই-মেইল ঠিকানা পরিবর্তন বা বাতিল",
        "changeemail-header": "আপনার ইমেল ঠিকানা পরিবর্তন করতে এই ফরমটি পূরণ করুন। আপনি যদি আপনার একাউন্ট থেকে যে কোন ইমেল ঠিকানার এসোসিয়েশন অপসারণ করতে চান, তাহলে ফরমটি জমা দেওয়ার সময় নতুন ইমেইল ঠিকানা খালি রাখুন।",
-       "changeemail-passwordrequired": "এই পরিবর্তন নিশ্চিত করতে আপনাকে আপনার পাসওয়ার্ড লিখতে হবে।",
        "changeemail-no-info": "এই পাতাটিতে সরাসরি প্রবেশাধিকার পেতে আপনাকে অবশ্যই প্রবেশ করতে হবে।",
        "changeemail-oldemail": "বর্তমান ই-মেইল ঠিকানা:",
        "changeemail-newemail": "নতুন ই-মেইল ঠিকানা:",
        "sectioneditnotsupported-text": "এই সম্পাদনা পাতায় অনুচ্ছেদ সম্পাদনা সমর্থন করে না",
        "permissionserrors": "অনুমতি ত্রুটিসমূহ",
        "permissionserrorstext": "আপনার এটা করার অনুমতি নেই, নিচের {{PLURAL:$1|টি কারণের|টি কারণের}} জন্য:",
-       "permissionserrorstext-withaction": "à¦\86পনার $2 à¦\95রার à¦\85নà§\81মতি à¦¨à§\87à¦\87, à¦¯à¦¾à¦° {{PLURAL:$1|à¦\95ারণ|à¦\95ারণসমà§\82হ}} à¦¹à¦²:",
+       "permissionserrorstext-withaction": "আপনার $2 অনুমতি নেই, যার {{PLURAL:$1|কারণ|কারণসমূহ}} হল:",
        "recreate-moveddeleted-warn": "'''সতর্কীকরণ: আপনি এমন একটি পাতা পুনরায় তৈরি করছেন যা পূর্বে অপসারণ করা হয়েছিল।'''\n\nআপনি পাতাটি সম্পাদনা চালিয়ে যাওয়া ঠিক হবে কিনা, তা বিবেচনা করুন।\nআপনার সুবিধার্থে পাতাটির অপলুপ্তি লগ এখানে দেয়া হলো:",
        "moveddeleted-notice": "এই পাতাটি অপসারণ করা হয়েছে।\nসূত্র হিসেবে নিচে এ পাতার অবলুপ্তি লগ দেওয়া হলো।",
        "moveddeleted-notice-recent": "দুঃখিত, এই পাতাটি সাম্প্রতি অপসারিত হয়েছে (সর্বশেষ ২৪ ঘণ্টায়)।\nসূত্র হিসেবে নিচে এই পাতা অপসারণ ও স্থানান্তর লগ দেয়া হয়েছে।",
        "content-model-css": "সিএসএস",
        "content-json-empty-object": "খালি বস্তু",
        "content-json-empty-array": "খালি অ্যারে",
+       "deprecated-self-close-category": "অবৈধ স্ব-বন্ধকৃত এইচটিএমএল ট্যাগ ব্যবহার করা পাতা",
+       "deprecated-self-close-category-desc": "এই পাতাটি অবৈধ স্ব-বন্ধকৃত এইচটিএমএল ট্যাগ ধারন করে, যেমন <code>&lt;b/></code> বা <code>&lt;span/></code>। এইচটিএমএল৫-এর নির্দিষ্টকরণের সঙ্গে সামঞ্জস্যপূর্ণ হতে এইসবের আচরণ শীঘ্রই পরিবর্তন করা হবে, তাই উইকিপাঠ্যে তাদের ব্যবহার অবচিত হয়েছে।",
        "duplicate-args-warning": "<strong>সতর্কীকরণ:</strong> \"$3\" প্যারামিটারের জন্য একের অধিক মানসহ [[:$1]] [[:$2]] কে আহ্বান করছে। শুধুমাত্র প্রদত্ত শেষ মান ব্যবহৃত হবে।",
        "duplicate-args-category": "টেমপ্লেট আহ্বানে সদৃশ আর্গুমেন্ট ব্যবহার করা পাতা",
        "duplicate-args-category-desc": "এই পাতায় টেমপ্লেট আহ্বান উপস্থিত রয়েছে যা সদৃশ আর্গুমেন্ট ব্যবহার করেছে, যেমন <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> বা <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>।",
        "undo-nochange": "সম্পাদনাটি পূর্বেই বাতিল করা হয়েছে।",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|আলাপ]])-এর সম্পাদিত $1 নম্বর সংশোধনটি বাতিল করা হয়েছে",
        "undo-summary-username-hidden": "একজন লুকানো ব্যবহারকারী $1 সংশোধন পুনরায় ফিরিয়ে এনেছেন",
-       "cantcreateaccounttitle": "অ্যাকাউন্ট তৈরি করা যাবে না",
        "cantcreateaccount-text": "[[User:$3|$3]] এই আইপি ঠিকানা('''$1''') থেকে অ্যাকাউন্ট সৃষ্টিতে বাধা দিয়েছেন।\n\n$3-এর দেয়া কারণ হল ''$2''",
        "cantcreateaccount-range-text": "[[User:$3|$3]] কর্তৃক আইপি ঠিকানার ব্যাপ্তি <strong>$1</strong>-এর মধ্যে অ্যাকাউন্ট তৈরি করা অবরুদ্ধ করা হয়েছে। যাতে আপনার আইপি ঠিকানাও (<strong>$4</strong>) রয়েছে। \n\n$3 কর্তৃক <em>$2</em> কারণ দেখানো হয়েছে।",
        "viewpagelogs": "এই পাতার জন্য লগগুলো দেখুন",
        "right-editmyprivateinfo": "আপনার ব্যক্তিগত তথ্য সম্পাদনা করুন (যেমন ইমেইল ঠিকানা, আসল নাম)",
        "right-editmyoptions": "আপনার পছন্দসমূহ পরিবর্তন করুন",
        "right-rollback": "একটি নির্দিষ্ট পাতার সর্বশেষ ব্যবহারকারীর সম্পদনা পূর্বাবস্থায় ফিরিয়ে আনুন",
-       "right-markbotedits": "রà§\8bলড-বà§\8dযাà¦\95 à¦¸à¦®à§\8dপাদনাসমà§\82হà¦\95à§\87 à¦¬à¦\9f à¦¸à¦®à§\8dপাদনা à¦¹à¦¿à¦¸à§\87বà§\87 à¦\9aিহà§\8dনিত à¦\95রà§\8b",
+       "right-markbotedits": "ফà§\87রত à¦\86না à¦¸à¦®à§\8dপাদনাসমà§\82হà¦\95à§\87 à¦¬à¦\9f à¦¸à¦®à§\8dপাদনা à¦¹à¦¿à¦¸à§\87বà§\87 à¦\9aিহà§\8dনিত à¦\95রà§\87",
        "right-noratelimit": "রেট লিমিটের ভিত্তিতে পরিবর্তন হবে না",
        "right-import": "অন্য উইকি থেকে পাতা আমদানী করো",
        "right-importupload": "ফাইল আপলোড থেকে এই পাতাগুলো আমদানী করো",
        "right-managechangetags": "[[Special:Tags|ট্যাগ]] তৈরি ও সক্রিয়/নিষ্ক্রিয় করুন",
        "right-applychangetags": "সম্পাদনার সাথে [[Special:Tags|ট্যাগ]] যুক্ত করুন",
        "right-changetags": "নির্দিষ্ট সংস্করণ এবং দীর্ঘ সম্পাদনাগুলোতে [[Special:Tags|ট্যাগ]] সংযোজন ও অপসারণ করুন",
+       "right-deletechangetags": "ডাটাবেজ থেকে [[Special:Tags|ট্যাগ]] অপসারণ করা",
        "grant-group-email": "ইমেইল পাঠান",
+       "grant-group-private-information": "আপনার সম্পর্কিত ব্যক্তিগত তথ্যে প্রবেশাধিকার পায়",
+       "grant-group-other": "বিবিধ কার্যকলাপ",
        "grant-createaccount": "অ্যাকাউন্ট তৈরি করুন",
        "grant-createeditmovepage": "পাতা তৈরি, সম্পাদনা এবং স্থানান্তর করুন",
        "grant-editmycssjs": "আপনার সিএসএস/জাভাস্ক্রিপ্ট সম্পাদনা করুন",
        "grant-editmyoptions": "আপনার ব্যবহারকারী পছন্দসমূহ সম্পাদনা করুন",
        "grant-editmywatchlist": "আপনার নজরতালিকা সম্পাদনা করুন",
        "grant-editprotected": "সংরক্ষিত পাতা সম্পাদনা করুন",
+       "grant-privateinfo": "ব্যক্তিগত তথ্যে প্রবেশাধিকার",
        "grant-sendemail": "অন্য ব্যবহারকারীকে ইমেইল পাঠান",
+       "grant-uploadeditmovefile": "ফাইল আপলোড, প্রতিস্থাপন এবং স্থানান্তর",
        "grant-uploadfile": "নতুন ফাইল আপলোড করুন",
        "grant-basic": "মৌলিক অধিকার",
        "grant-viewdeleted": "অপসারিত ফাইল ও পাতাগুলি দেখুন",
        "newuserlogpagetext": "এটি নতুন ব্যবহারকারী সৃষ্টির লগ",
        "rightslog": "ব্যবহারকারীর অধিকার লগ",
        "rightslogtext": "এটি ব্যবহারকারী অধিকারে আনা পরিবর্তনগুলির একটি লগ।",
-       "action-read": "এই পাতাটি পড়ুন",
-       "action-edit": "এই পাতাটি সম্পাদনা",
-       "action-createpage": "এই পাতাটি তৈরি",
-       "action-createtalk": "এই আলাপের পাতাটি তৈরি",
-       "action-createaccount": "এই ব্যবহারকারী একাউন্টটি তৈরি করো",
-       "action-history": "এই পাতার ইতিহাস দেখাও",
-       "action-minoredit": "এই সম্পাদনাটি অনুল্লেখ্য হিসেবে চিহ্নিত করো",
+       "action-read": "এই পাতাটি পড়ার",
+       "action-edit": "এই পাতাটি সম্পাদনা করার",
+       "action-createpage": "এই পাতাটি তৈরি করার",
+       "action-createtalk": "এই আলাপ পাতাটি তৈরি করার",
+       "action-createaccount": "এই ব্যবহারকারী একাউন্টটি তৈরি করার",
+       "action-autocreateaccount": "স্বয়ংক্রিয়ভাবে এই বাহ্যিক ব্যবহারকারী অ্যাকাউন্ট তৈরি করার",
+       "action-history": "এই পাতার ইতিহাস দেখার",
+       "action-minoredit": "এই সম্পাদনাটি অনুল্লেখ্য হিসেবে চিহ্নিত করার",
        "action-move": "পাতাটি সরিয়ে ফেলুন",
        "action-move-subpages": "পাতাটি এবং এর উপপাতাগুলো সরিয়ে ফেলুন",
        "action-move-rootuserpages": "root ব্যবহারকারীর পাতাগুলো সরিয়ে ফেলুন",
        "action-move-categorypages": "বিষয়শ্রেণী পাতাসমূহ স্থানান্তর করুন",
-       "action-movefile": "এই ফাইলটি সরিয়ে ফেলুন",
-       "action-upload": "à¦\8fà¦\87 à¦«à¦¾à¦\87ল à¦\86পলà§\8bড à¦\95রà§\8b",
+       "action-movefile": "এই ফাইল স্থানান্তর করার",
+       "action-upload": "à¦\8fà¦\87 à¦«à¦¾à¦\87ল à¦\86পলà§\8bড à¦\95রার",
        "action-reupload": "বিদ্যমান ফাইল প্রতিস্থাপন করো",
-       "action-reupload-shared": "শà§\87য়ারà§\8dড à¦°à¦¿à¦ªà§\8bà¦\9cিà¦\9fরà§\80তà§\87 à¦\8fà¦\87 à¦«à¦¾à¦\87লà¦\9fি à¦¹à¦¾à¦²à¦¨à¦¾à¦\97াদ à¦\95রà§\81ন",
+       "action-reupload-shared": "শà§\87য়ারà§\8dড à¦°à¦¿à¦ªà§\8bà¦\9cিà¦\9fরà§\80তà§\87 à¦\8fà¦\87 à¦«à¦¾à¦\87লà¦\9fি à¦¹à¦¾à¦²à¦¨à¦¾à¦\97াদ à¦\95রার",
        "action-upload_by_url": "কোন ইউআরএল থেকে ফাইলটি আপলোড করো",
        "action-writeapi": "রাইট এপিআই ব্যবহার করুন",
        "action-delete": "পাতাটি মুছে ফেলো",
-       "action-deleterevision": "à¦\8fà¦\87 à¦¸à¦\82শà§\8bধনà¦\9fি à¦®à§\81à¦\9bà§\87 à¦«à§\87লà§\8b",
+       "action-deleterevision": "à¦\8fà¦\87 à¦¸à¦\82শà§\8bধনà¦\9fি à¦®à§\81à¦\9bà§\87 à¦«à§\87লার",
        "action-deletedhistory": "পাতার মুছে ফেলা ইতিহাস দেখাও",
        "action-browsearchive": "অপসারিত পাতায় অনুসন্ধান করুন",
        "action-undelete": "পাতাটি পুনরুদ্ধার করো",
        "action-suppressrevision": "লুকানো সংস্করণগুলো পর্যালোচনা এবং পুনঃস্থাপন করুন",
-       "action-suppressionlog": "à¦\8fà¦\87 à¦¬à§\8dযà¦\95à§\8dতিà¦\97ত à¦²à¦\97 à¦¦à§\87à¦\96াà¦\93",
-       "action-block": "à¦\8fà¦\87 à¦¬à§\8dযবহারà¦\95ারà§\80à¦\95à§\87 à¦¸à¦®à§\8dপাদনা à¦\95রতà§\87 à¦¬à¦¾à¦\81ধা à¦¦à¦¾à¦\93",
-       "action-protect": "à¦\8fà¦\87 à¦ªà¦¾à¦¤à¦¾à¦° à¦¸à§\81রà¦\95à§\8dষার à¦®à¦¾à¦¤à§\8dরা à¦ªà¦°à¦¿à¦¬à¦°à§\8dতন à¦\95রà§\8b",
+       "action-suppressionlog": "à¦\8fà¦\87 à¦¬à§\8dযà¦\95à§\8dতিà¦\97ত à¦²à¦\97 à¦¦à§\87à¦\96ার",
+       "action-block": "à¦\8fà¦\87 à¦¬à§\8dযবহারà¦\95ারà§\80à¦\95à§\87 à¦¸à¦®à§\8dপাদনা à¦\95রতà§\87 à¦¬à¦¾à¦\81ধা à¦¦à§\87য়ার",
+       "action-protect": "à¦\8fà¦\87 à¦ªà¦¾à¦¤à¦¾à¦° à¦¸à§\81রà¦\95à§\8dষার à¦®à¦¾à¦¤à§\8dরা à¦ªà¦°à¦¿à¦¬à¦°à§\8dতন à¦\95রার",
        "action-rollback": "একটি নির্দিষ্ট পাতার সর্বশেষ ব্যবহারকারীর সম্পদনা পূর্বাবস্থায় ফিরিয়ে আনুন",
        "action-import": "অন্য উইকি থেকে পাতা আমদানী করো",
        "action-importupload": "ফাইল আপলোড থেকে পাতা আমদানী করো",
        "action-patrol": "অন্যদের সম্পাদনা পরীক্ষিত বলে চিহ্নিত করো",
        "action-autopatrol": "পরীক্ষিত বলে চিহ্নিত কি আপনি সম্পাদনা করেছেন",
        "action-unwatchedpages": "নজরতালিকা বহির্ভূত পাতাগুলির তালিকা দেখাও",
-       "action-mergehistory": "à¦\8fà¦\87 à¦ªà¦¾à¦¤à¦¾à¦° à¦\87তিহাস à¦\8fà¦\95তà§\8dরিত à¦\95রà§\81ন",
+       "action-mergehistory": "à¦\8fà¦\87 à¦ªà¦¾à¦¤à¦¾à¦° à¦\87তিহাস à¦\8fà¦\95তà§\8dরিত à¦\95রার",
        "action-userrights": "সকল ব্যবহারকারীর অধিকার সম্পাদনা করুন",
        "action-userrights-interwiki": "অন্যান্য উইকির ব্যবহারকারীদের অধিকারসমূহ সম্পাদনা করুন",
        "action-siteadmin": "ডাটাবেজ বন্ধ অথবা খুলুন",
        "action-managechangetags": "ট্যাগ তৈরি ও সক্রিয়/নিষ্ক্রিয়",
        "action-applychangetags": "আপনার পরিবর্তনগুলোর সাথে ট্যাগ সংযোজন করুন",
        "action-changetags": "নির্দিষ্ট সংস্করণ এবং দীর্ঘ সম্পাদনাগুলোতে ট্যাগ সংযোজন ও অপসারণ করুন",
+       "action-deletechangetags": "ডাটাবেজ থেকে ট্যাগ অপসরণ করার",
+       "action-purge": "এই পাতা হালনাগাদ করার",
        "nchanges": "$1টি {{PLURAL:$1|পরিবর্তন}}",
        "enhancedrc-since-last-visit": "{{PLURAL:$1|সর্বশেষ প্রদর্শনের পর}} $1টি",
        "enhancedrc-history": "ইতিহাস",
        "file-thumbnail-no": "ফাইলের নামটি <strong>$1</strong> দিয়ে শুরু হয়েছে।\nমনে হচ্ছে এটি একটি সংকুচিত আকারের ছবি  ''(থাম্বনেইল)''।\nআপনার কাছে যদি পূর্ণ রেজোলিউশনের ছবিটি থাকে, তবে সেটি আপলোড করুন, নতুবা অনুগ্রহ করে ফাইলের নামটি পরিবর্তন করুন।",
        "fileexists-forbidden": "এই নামের একটি ফাইল ইতিমধ্যেই বিদ্যমান, এবং এটি প্রতিস্থাপনযোগ্য নয়।\nআপনি যদি এখনো ফাইলটি আপলোড করতে চান, তবে অনুগ্রহপূর্বক পেছনে গিয়ে একটি নতুন নামে ফাইলটি আপলোড করুন।\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "অংশীদারী ফাইল ভাণ্ডারে এই নামের একটি ফাইল ইতিমধ্যেই বিদ্যমান।\nআপনি যদি এখনো ফাইলটি আপলোড করতে চান, তবে অনুগ্রহপূর্বক পেছনে গিয়ে একটি নতুন নামে ফাইলটি আপলোড করুন।[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "আপলোডটি <strong>[[:$1]]</strong>-এর বর্তমান সংস্করণের হুবহু প্রতিলিপি।",
+       "fileexists-duplicate-version": "এই আপলোডটি <strong>[[:$1]]</strong>-এর একটি {{PLURAL:$2|পুরনো সংস্করণের}} হুবহু প্রতিলিপি।",
        "file-exists-duplicate": "এই ফাইলটি নিচের {{PLURAL:$1|ফাইল|ফাইলগুলির}} অনুলিপি:",
        "file-deleted-duplicate": "এই ফাইলটির মত একটি ফাইল ([[:$1]]) পূর্বে অপসারণ করা হয়েছে।\nপুনরায় আপলোড করার পূর্বে আপনার উচিত আগের ফাইলটির অপসারণের কারণ জানা।",
        "uploadwarning": "আপলোড সতর্কবাণী",
        "uploadscripted": "এই ফাইলে এমন HTML বা স্ক্রিপ্ট কোড আছে যা একটি ওয়েব ব্রাউজার ভুল বুঝতে পারে।",
        "uploaded-script-svg": "আপলোডকৃত SVG ফাইলে স্ক্রিপ্টযোগ্য উপাদান \"$1\" পাওয়া গেছে।",
        "uploaded-hostile-svg": "আপলোড করা SVG ফাইলের শৈলী উপাদানে অনিরাপদ সিএসএস পাওয়া গেছে।",
+       "uploaded-href-unsafe-target-svg": "অনিরাপদ উপাত্তে href পাওয়া গেছে: আপলোডকৃত SVG ফাইলে URI লক্ষ্য ছিল <code>&lt;$1 $2=\"$3\"&gt;</code>।",
        "uploaded-image-filter-svg": "আপলোডকৃত SVG ফাইলে URL: <code>&lt;$1 $2=\"$3\"&gt;</code> সহ ছবি পরিশোধক পাওয়া গেছে।",
        "uploadscriptednamespace": "এই SVG ফাইলে অবৈধ নামস্থান \"$1\" রয়েছে",
        "uploadinvalidxml": "আপলোডকৃত ফাইলে XML পার্স করা যাবে না।",
        "upload-too-many-redirects": "এই ইউআরএলটিতে অনেক বেশি পুনর্নির্দেশনা রয়েছে",
        "upload-http-error": "একটি এইচটিটিপি ত্রুটি দেখা দিয়েছে: $1",
        "upload-copy-upload-invalid-domain": "এই ডোমেইন থেকে আপলোড সম্ভব নয়।",
+       "upload-dialog-disabled": "এই ডায়ালগ ব্যবহার করে ফাইল আপলোড করা এই উইকিতে নিষ্ক্রিয় করা হয়েছে।",
        "upload-dialog-title": "ফাইল আপলোড করুন",
        "upload-dialog-button-cancel": "বাতিল",
        "upload-dialog-button-done": "সম্পন্ন",
        "upload-dialog-button-upload": "আপলোড",
        "upload-form-label-infoform-title": "বিস্তারিত",
        "upload-form-label-infoform-name": "নাম",
+       "upload-form-label-infoform-name-tooltip": "ফাইলের জন্য একটি অদ্বিতীয় বিবরণমূলক শিরোনাম, যেটি ফাইলের নাম হিসাবে পরিবেশন হবে। আপনি ফাঁকা স্থান সহ সমতল ভাষা ব্যবহার করতে পারেন। ফাইল এক্সটেনশন অন্তর্ভুক্ত করবেন না।",
        "upload-form-label-infoform-description": "বিবরণ",
+       "upload-form-label-infoform-description-tooltip": "সংক্ষেপে কাজটি সম্পর্কে উল্লেখযোগ্য সবকিছু বর্ণনা করুন।\nএকটি ছবির জন্য, এতে থাকা প্রধান বিষয়, অনুষ্ঠান, বা জায়গা উল্লেখ করুন।",
        "upload-form-label-usage-title": "ব্যবহার",
        "upload-form-label-usage-filename": "ফাইলের নাম",
        "upload-form-label-own-work": "এটি আমার নিজের কাজ",
        "upload-form-label-infoform-categories": "বিষয়শ্রেণীসমূহ",
        "upload-form-label-infoform-date": "তারিখ",
+       "upload-form-label-own-work-message-generic-local": "আমি নিশ্চিত করছি যে আমি {{SITENAME}}-এর পরিষেবা এবং লাইসেন্সকরণ নীতির শর্তাবলী অনুসরণ করে এই ফাইল আপলোড করছি।",
        "upload-form-label-not-own-work-local-generic-local": "এছাড়াও আপনি [[Special:Upload|ডিফল্ট আপলোডের পাতা]] চেষ্টা করতে পারেন।",
        "upload-form-label-not-own-work-local-generic-foreign": "এছাড়াও আপনি [[Special:Upload|{{SITENAME}}-এর আপলোডের পাতা]] ব্যবহার করার চেষ্টা করতে পারেন, যদি এই ফাইলটি তাদের নীতিমালা অধীনে সেখানে আপলোড করা যায়।",
        "backend-fail-stream": "\"$1\" ফাইলের স্ট্রিম দেখানো যাচ্ছে না।",
        "backend-fail-read": "$1 ফাইলটি ওপেন করা যাচ্ছে না।",
        "backend-fail-create": "$1 ফাইলটি তৈরী করা যাচ্ছে না।",
        "backend-fail-maxsize": "\"$1\" ফাইলে লেখা যাচ্ছে না কারণ এটি {{PLURAL:$2|এক বাইট|$2 বাইট}} থেকে বড়।",
-       "backend-fail-readonly": "\"$1\" à¦¸à§\8dà¦\9fà§\8bরà§\87à¦\9c à¦¬à§\8dযাà¦\95à¦\8fনà§\8dড à¦¥à§\87à¦\95à§\87 à¦¬à¦°à§\8dতমানà§\87 à¦²à§\87à¦\96া à¦¯à¦¾à¦\9aà§\8dà¦\9bà§\87 à¦¨à¦¾à¥¤ à¦\95ারণ: \"''$2''\"",
+       "backend-fail-readonly": "\"$1\" à¦¸à§\8dà¦\9fà§\8bরà§\87à¦\9c à¦¬à§\8dযাà¦\95à¦\8fনà§\8dড à¦¥à§\87à¦\95à§\87 à¦¬à¦°à§\8dতমানà§\87 à¦¶à§\81ধà§\81-পঠনà§\87 à¦°à¦¯à¦¼à§\87à¦\9bà§\87। à¦ªà§\8dরদতà§\8dত à¦\95ারণ: <em>$2</em>",
        "backend-fail-synced": "\"$1\" ফাইলটি ইন্টারনাল স্টোরেজ ব্যকএন্ডের সাথে অসামঞ্জস্যপূর্ণ",
        "backend-fail-connect": "স্টোরেজ ব্যাকেন্ড \"$1\" এর সাথে যোগাযোগ করা যাচ্ছে না।",
        "backend-fail-internal": "\"$1\" স্টোরেজ ব্যাকেন্ডে কোনো অজানা ত্রুটি হয়েছে।",
        "uploadstash-errclear": "ফাইলগুলো পরিষ্কারকরণ ব্যর্থ হয়েছে।",
        "uploadstash-refresh": "ফাইলের তালিকা রিফ্রেশ করুন",
        "uploadstash-thumbnail": "থাম্বনেইল দেখুন",
+       "uploadstash-exception": "স্টাসে আপলোড সঞ্চয় করা যায়নি ($1): \"$2\"।",
        "invalid-chunk-offset": "ত্রুটিপূর্ণ চাংক অফসেট",
        "img-auth-accessdenied": "প্রবেশাধিকার নাই",
        "img-auth-nopathinfo": "PATH_INFO পাওয়া যাচ্ছে না।\nআপনার সার্ভার থেকে এই তথ্য পাঠানোর জন্য কনফিগার করা হয়নি।\nএটি হয়তো CGI ভিত্তিক এবং img_auth সমর্থন করে না।\nবিস্তারিত দেখুন https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization।",
        "apihelp": "এপিআই সাহায্য",
        "apihelp-no-such-module": "মডিউল \"$1\" পাওয়া যায়নি।",
        "apisandbox": "এপিআই খেলাঘর",
+       "apisandbox-jsonly": "API খেলাঘর ব্যবহার করতে জাভাস্ক্রিপ্ট প্রয়োজন।",
        "apisandbox-api-disabled": "এপিআই এই সাইটে নিষ্ক্রিয় করা আছে।",
        "apisandbox-fullscreen": "প্যানেল সম্প্রসারণ করুন",
+       "apisandbox-fullscreen-tooltip": "ব্রাউজারের উইন্ডো পূরণ করতে খেলাঘরের প্যানেল প্রসারিত করুন।",
        "apisandbox-unfullscreen": "পাতা দেখাও",
+       "apisandbox-unfullscreen-tooltip": "খেলাঘরের প্যানেল হ্রাস করুন, তাহলে মিডিয়াউইকি পরিভ্রমণ করার সংযোগগুলি পাওয়া যাবে।",
        "apisandbox-submit": "অনুরোধ রাখুন",
        "apisandbox-reset": "পরিস্কার",
        "apisandbox-retry": "পুনঃচেষ্টা করুন",
        "listgrouprights-removegroup-self-all": "নিজের অ্যাকাউন্ট থেকে সকল দল অপসারণ",
        "listgrouprights-namespaceprotection-header": "নামস্থান নিষেধাজ্ঞাসমূহ",
        "listgrouprights-namespaceprotection-namespace": "নামস্থান",
+       "listgrants": "কার্যভার",
+       "listgrants-summary": "নিম্নে ব্যবহারকারী অধিকারের সাথে যুক্ত প্রবেশাধিকারসহ তাদের কার্যভারের একটি তালিকা দেয়া হয়েছে। ব্যবহারকারীরা তাদের অ্যাকাউন্ট ব্যবহার করতে অ্যাপ্লিকেশনকে অনুমোদন দিতে পারে, কিন্তু কার্যভারের উপর ভিত্তি করে সীমিত অনুমতি ব্যবহারকারীরা অ্যাপ্লিকেশনকে দিতে পারবেন। মূলত, একটি অ্যাপ্লিকেশন একজন ব্যবহারকারীর দেয়া অধিকারের অতিরিক্ত অধিকার ব্যবহার করতে পারবে না। পৃথক অধিকার সম্পর্কে [[{{MediaWiki:Listgrouprights-helppage}}|অতিরিক্ত তথ্য]] দেখুন।",
+       "listgrants-grant": "কার্যভার",
        "listgrants-rights": "অধিকারসমূহ",
        "trackingcategories": "বিষয়শ্রেণীসমূহ অনুসরণ করা হচ্ছে",
        "trackingcategories-msg": "বিষয়শ্রেণী অনুসরণ করা হচ্ছে",
        "restricted-displaytitle-ignored": "উপেক্ষিত প্রদর্শন শিরোনামসহ পাতা",
        "restricted-displaytitle-ignored-desc": "পাতাটি একটি <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> উপেক্ষা করেছে কারণ এটা পাতাটির আসল শিরোনামের সাথে সমতুল্য নয়।",
        "broken-file-category-desc": "এই পাতায় একটি ভাঙ্গা ফাইলের লিঙ্ক রয়েছে (একটি ফাইল এম্বেড করার জন্য একটি লিঙ্ক যখন ফাইলটির অস্তিত্ব নেই)",
+       "hidden-category-category-desc": "এই বিষয়শ্রেণীটির পাতার বিষয়বস্তুর মধ্যে <code><nowiki>__HIDDENCAT__</nowiki></code> উপস্থিত রয়েছে, যা পূর্ব-নির্ধারিতভাবে পাতার বিষয়শ্রেণীর সংযোগে দেখায় না।",
        "trackingcategories-nodesc": "কোন বর্ণনা নেই।",
        "trackingcategories-disabled": "বিষয়শ্রেণীটি বিকল",
        "mailnologin": "প্রাপকের ঠিকানা নেই",
        "watchnologin": "আপনি প্রবেশ করেননি",
        "addwatch": "নজরতালিকায় যোগ করো",
        "addedwatchtext": "\"[[:$1]]\" ও এর আলোচনা পাতাটি আপনার [[Special:Watchlist|নজরতালিকাতে]] যোগ করা হয়েছে।",
+       "addedwatchtext-talk": "\"[[:$1]]\" ও এর সাথে জড়িত পাতা আপনার [[Special:Watchlist|নজরতালিকাতে]] যোগ করা হয়েছে।",
        "addedwatchtext-short": "\"$1\" পাতাটি আপনার নজরতালিকায় যোগ করা হয়েছে।",
        "removewatch": "নজরতালিকা থেকে অপসারণ",
        "removedwatchtext": "\"[[:$1]]\" ও এর আলোচনা পাতাটি আপনার [[Special:Watchlist|নজরতালিকা]] থেকে সরানো হয়েছে।",
+       "removedwatchtext-talk": "\"[[:$1]]\" ও এর সাথে জড়িত পাতা আপনার [[Special:Watchlist|নজরতালিকা]] থেকে সরানো হয়েছে।",
        "removedwatchtext-short": "\"$1\" পাতাটি আপনার নজরতালিকা থেকে সরিয়ে নেয়া হয়েছে।",
        "watch": "নজর রাখুন",
        "watchthispage": "নজরে রাখুন",
        "enotif_body": "প্রিয় $WATCHINGUSERNAME,\n\n$PAGEINTRO $NEWPAGE\n\nসম্পাদকের সারাংশ: $PAGESUMMARY $PAGEMINOREDIT\n\nসম্পাদকের সাথে যোগাযোগের উপায়:\nইমেইল: $PAGEEDITOR_EMAIL\nউইকি: $PAGEEDITOR_WIKI\n\nপাতাটির পরবর্তী পরিবর্তনগুলো জন্য আর কোন বিজ্ঞপ্তি পাঠানো হবে না, যতক্ষণ না আপনি প্রবেশ করার সময় এই পাতায় ব্রাউজ করবেন। এছাড়া আপনি আপনার নজরতালিকায় রাখা সবগুলি পাতা জন্য বিজ্ঞপ্তি ফ্ল্যাগ শুরুর অবস্থায় ফিরিয়ে নিতে পারেন।\n\nআপনার বন্ধুত্বপূর্ণ {{SITENAME}} বিজ্ঞপ্তি ব্যবস্থা\n\n--\nইমেইল বিজ্ঞপ্তি সেটিং পরিবর্তনের জন্য দেখুন\n{{canonicalurl:{{#special:Preferences}}}}\n\nআপনার নজরতালিকা সেটিংস পরিবর্তন করতে চাইলে দেখুন, \n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nনজরতালিকা থেকে কোন পাতা অপসারণ করতে চাইলে এখানে যান:\n$UNWATCHURL\n\nপ্রতিক্রিয়া জানাতে ও আরও সহযোগিতার জন্য:\n$HELPPAGE",
        "created": "তৈরী হয়েছিল",
        "changed": "পরিবর্তিত",
-       "deletepage": "পাতাটি মুছে ফেলা হোক",
+       "deletepage": "পাতা মুছুন",
        "confirm": "নিশ্চিত করুন",
        "excontent": "বিষয়বস্তু ছিল: '$1'",
        "excontentauthor": "বিষয়বস্তু ছিল: \"$1\", এবং একমাত্র অবদানকারী ছিলেন \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|আলাপ]])",
        "rollbacklinkcount": "$1টি {{PLURAL:$1|সম্পাদনা}} রোলব্যাক করুন",
        "rollbacklinkcount-morethan": "$1টির বেশি {{PLURAL:$1|সম্পাদনা}} রোলব্যাক করুন",
        "rollbackfailed": "রোলব্যাক ব্যর্থ",
+       "rollback-missingparam": "অনুরোধে প্রয়োজনীয় প্যারামিটারগুলি অনুপস্থিত।",
+       "rollback-missingrevision": "সংশোধনের উপাত্ত লোড করতে অক্ষম।",
        "cantrollback": "পূর্বের সংস্করণে ফেরত যাওয়া সম্ভব হল না, সর্বশেষ সম্পাদনাকারী এই নিবন্ধটির একমাত্র লেখক।",
        "alreadyrolled": "[[User:$2|$2]] ([[User talk:$2|talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) দ্বারা সম্পাদিত সর্বশেষ [[:$1]] সম্পাদনাটি পুনর্বহাল করা যাচ্ছে না;\nঅন্য কোন ব্যবহারকারী এই পাতা ইতিমধ্যে সম্পাদনা বা পুনর্বহাল করেছেন।\n\nএই পাতায় সর্বোশেষে [[User:$3|$3]] ([[User talk:$3|talk]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) দ্বারা সম্পাদিত।",
        "editcomment": "সম্পাদনা সারাংশ ছিল: \"''$1''\"।",
        "undeletedrevisions": "{{PLURAL:$1|১টি সংশোধন|$1টি সংশোধন}} পুনরুদ্ধার করা হয়েছে",
        "undeletedrevisions-files": "{{PLURAL:$1|১টি সংশোধন|$1টি সংশোধন}} এবং {{PLURAL:$2|১টি ফাইল|$2টি ফাইল}} পুনরুদ্ধার করা হয়েছে",
        "undeletedfiles": "{{PLURAL:$1|১টি ফাইল|$1টি ফাইল}} পুনরুদ্ধার করা হয়েছে",
-       "cannotundelete": "মà§\81à¦\9bà§\87 à¦«à§\87লা à¦¬à¦¾à¦¤à¦¿à¦² করা যায়নি:\n$1",
+       "cannotundelete": "à¦\95িà¦\9bà§\81 à¦¬à¦¾ à¦¸à¦¬ à¦ªà§\81নরà§\81দà§\8dধার করা যায়নি:\n$1",
        "undeletedpage": "'''$1 পুনরুদ্ধার করা হয়েছে'''\n\nসাম্প্রতিক মুছে ফেলা ও পুনরুদ্ধারের ঘটনাগুলির জন্য [[Special:Log/delete|অবলুপ্তি লগ]] দেখুন।",
        "undelete-header": "সাম্প্রতিক সময়ে মুছে ফেলা পাতাগুলি দেখতে [[Special:Log/delete|অবলুপ্তি লগ]] দেখুন।",
        "undelete-search-title": "অপসারিত পাতা অনুসন্ধান করো",
        "sp-contributions-newbies-sub": "নতুন অ্যাকাউন্টের জন্য",
        "sp-contributions-newbies-title": "নতুন অ্যাকাউন্টের ব্যবহারকারী অবদান",
        "sp-contributions-blocklog": "বাধা দানের লগ",
-       "sp-contributions-suppresslog": "মà§\81à¦\9bà§\87 à¦«à§\87লা à¦¬à§\8dযবহারà¦\95ারà§\80 অবদান",
-       "sp-contributions-deleted": "মুছে ফেলা ব্যবহারকারী অবদান",
+       "sp-contributions-suppresslog": "à¦\97à§\8bপনà¦\95à§\83ত {{GENDER:$1|বà§\8dযবহারà¦\95ারà§\80র}} অবদান",
+       "sp-contributions-deleted": "মুছে ফেলা {{GENDER:$1|ব্যবহারকারীর}} অবদান",
        "sp-contributions-uploads": "আপলোডসমূহ",
        "sp-contributions-logs": "লগসমূহ",
        "sp-contributions-talk": "আলোচনা",
        "move-page-legend": "পাতা স্থানান্তর",
        "movepagetext": "নিচের ফর্মটি ব্যবহার করে একটি পাতার শিরোনাম পরিবর্তন করা যাবে, এবং সেই সাথে নতুন শিরোনামে এর সমগ্র ইতিহাস স্থানান্তর করা যাবে।\nপুরনো শিরোনামটি নতুন শিরোনামটির প্রতি একটি পুনর্নির্দেশনা ধারণ করবে।\nযেসমস্ত পুনর্নির্দেশনা পুরনো শিরোনামটির দিকে নির্দেশ করছিল, সেগুলি স্বয়ংক্রিয়ভাবে হালনাগাদ করতে পারবেন।\nযদি তা না চান, তবে [[Special:DoubleRedirects|দ্বি-পুনর্নির্দেশনা]] বা [[Special:BrokenRedirects|অচল পুনর্নির্দেশনাগুলি]] পরীক্ষা করে দেখতে ভুলবেন না।\nসংযোগগুলি যাতে তাদের লক্ষ্যে পৌঁছায়, তা নিশ্চিত করার দায়িত্ব আপনার।\n\nলক্ষ্য করুন যে যদি নতুন শিরোনামে ইতিমধ্যেই একটি পাতা থেকে থাকে, তবে উৎস পাতাটি সেই শিরোনামে স্থানান্তর করা হবে <strong>না</strong>, যদি না নতুন শিরোনামের পাতাটি খালি থাকে বা একটি পুননির্দেশনা হয় এবং এর কোন অতীত সম্পাদনা ইতিহাস না থাকে।\nঅর্থাৎ আপনি ভুল করে নাম পরিবর্তন করলে সহজেই পুরনো নামে ফেরত যেতে পারবেন, কিন্তু ইতিমধ্যে বিদ্যমান কোন পাতার উপরে লিখতে পারবেন না।\n\n<strong>টীকা:</strong>\nকোন জনপ্রিয় পাতার ক্ষেত্রে এই পরিবর্তনটি খুবই আকস্মিক হতে পারে; অগ্রসর হবার আগে এই কাজটির ফলাফল কী হতে পারে, সে ব্যাপারে অনুগ্রহ করে নিশ্চিত হোন।",
        "movepagetext-noredirectfixer": "নিচের ফর্মটি ব্যবহার করে একটি পাতার শিরোনাম পরিবর্তন করা যাবে, এবং সেই সাথে নতুন শিরোনামে এর সমগ্র ইতিহাস স্থানান্তর করা যাবে।\nপুরনো শিরোনামটি নতুন শিরোনামটির প্রতি একটি পুনর্নির্দেশনা ধারণ করবে।\n[[Special:DoubleRedirects|দ্বি-পুনর্নির্দেশনা]] বা [[Special:BrokenRedirects|অচল পুনর্নির্দেশনাগুলি]] পরীক্ষা করে দেখতে ভুলবেন না।\nসংযোগগুলি যাতে তাদের লক্ষ্যে পৌঁছায়, তা নিশ্চিত করার দায়িত্ব আপনার।\n\nলক্ষ্য করুন যে যদি নতুন শিরোনামে ইতিমধ্যেই একটি পাতা থেকে থাকে, তবে উৎস পাতাটি সেই শিরোনামে স্থানান্তর করা হবে <strong>না</strong>, যদি না নতুন শিরোনামের পাতাটি খালি থাকে বা একটি পুননির্দেশনা হয় এবং এর কোন অতীত সম্পাদনা ইতিহাস না থাকে। \nঅর্থাৎ আপনি ভুল করে নাম পরিবর্তন করলে সহজেই পুরনো নামে ফেরত যেতে পারবেন, কিন্তু ইতিমধ্যে বিদ্যমান কোন পাতার উপরে লিখতে পারবেন না।\n\n<strong>টীকা:</strong>\nকোন জনপ্রিয় পাতার ক্ষেত্রে এই পরিবর্তনটি খুবই আকস্মিক হতে পারে;\nঅগ্রসর হবার আগে এই কাজটির ফলাফল কী হতে পারে, সে ব্যাপারে অনুগ্রহ করে নিশ্চিত হোন।",
-       "movepagetalktext": "পাতাà¦\9fির à¦¸à¦¾à¦¥à§\87 à¦¸à¦¾à¦¥à§\87 à¦¸à¦\82শà§\8dলিষà§\8dà¦\9f à¦\86লà§\8bà¦\9aনা à¦ªà¦¾à¦¤à¦¾à¦\9fিà¦\93 à¦¸à§\8dবয়à¦\82à¦\95à§\8dরিয়ভাবà§\87 à¦¸à¦°à¦¾à¦¨à§\8b à¦¹à¦¬à§\87 '''যদি à¦¨à¦¾:'''\n*à¦\96ালি à¦¨à¦¯à¦¼ à¦\8fমন à¦\8fà¦\95à¦\9fি à¦\86লাপ à¦ªà¦¾à¦¤à¦¾ à¦¨à¦¤à§\81ন à¦¶à¦¿à¦°à§\8bনামà¦\9fির à¦\85ধà§\80নà§\87 à¦\87তিমধà§\8dযà§\87à¦\87 à¦¬à¦¿à¦¦à§\8dযমান à¦¥à¦¾à¦\95à§\87, à¦\85থবা\n*à¦\86পনি à¦¨à¦¿à¦\9aà§\87র à¦¬à¦¾à¦\95à§\8dসà¦\9fি à¦¥à§\87à¦\95à§\87 à¦\9fিà¦\95 à¦¸à¦°à¦¿à¦¯à¦¼à§\87 à¦¨à¦¿à¦¤à§\87 à¦ªà¦¾à¦°à§\87ন।\n\nএসব ক্ষেত্রে আপনি চাইলে নিজের হাতে পাতাটিকে সরাতে বা একত্রীকরণ করতে পারেন।",
+       "movepagetalktext": "à¦\86পনি à¦¯à¦¦à¦¿ à¦\8fà¦\87 à¦¬à¦¾à¦\95à§\8dসà§\87 à¦\9fিà¦\95 à¦¦à§\87ন, à¦¤à¦¾à¦¹à¦²à§\87 à¦ªà¦¾à¦¤à¦¾à¦\9fির à¦¸à¦¾à¦¥à§\87 à¦¸à¦\82শà§\8dলিষà§\8dà¦\9f à¦\86লà§\8bà¦\9aনা à¦ªà¦¾à¦¤à¦¾à¦\9fিà¦\93 à¦¸à§\8dবয়à¦\82à¦\95à§\8dরিয়ভাবà§\87 à¦¨à¦¤à§\81ন à¦¶à¦¿à¦°à§\8bনামà§\87 à¦¸à¦°à¦¾à¦¨à§\8b à¦¹à¦¬à§\87 à¦¯à¦¦à¦¿ à¦¨à¦¾ à¦¨à¦¤à§\81ন à¦¶à¦¿à¦°à§\8bনামà¦\9fির à¦\85ধà§\80নà§\87 à¦\87তিমধà§\8dযà§\87à¦\87 à¦\8fà¦\95à¦\9fি à¦¬à¦¿à¦¦à§\8dযমান à¦¥à¦¾à¦\95à§\87।\n\nএসব ক্ষেত্রে আপনি চাইলে নিজের হাতে পাতাটিকে সরাতে বা একত্রীকরণ করতে পারেন।",
        "moveuserpage-warning": "'''সতর্কতা:''' আপনি একটি ব্যবহারকারী পাতা স্থানান্তর করছেন। অনুগ্রহ করে লক্ষ্য করুন যে এর মাধ্যমে কেবলমাত্র পাতাটি স্থানান্তর হবে, কিন্তু পাতার নাম পরিবর্তন হবে ''না''।",
        "movecategorypage-warning": "<strong>সতর্কীকরণ:</strong> আপনি একটি বিষয়শ্রেণীর পাতা স্থানান্তর করতে চলেছেন। দয়া করে মনে রাখবেন যে এতে শুধুমাত্র পাতাটি স্থানান্তরিত হবে এবং পুরাতন বিষয়শ্রেণীতে থাকা কোন পাতা নতুনটিতে পুনঃশ্রেণীকরণ করা হবে <em>না</em>।",
        "movenologintext": "কোন পাতা সরিয়ে ফেলতে চাইলে আপনাকে অবশ্যই একজন নিবন্ধিত ব্যবহারকারী হতে হবে ও অ্যাকাউন্টে [[Special:UserLogin|প্রবেশ]] করতে হবে।",
        "movereason": "কারণ:",
        "revertmove": "পূর্বাবস্থায় ফেরত নেওয়া হোক",
        "delete_and_move_text": "\"[[:$1]]\" শিরোনামের গন্তব্য পাতাটি ইতিমধ্যেই বিদ্যমান। আপনি কি স্থানান্তর সফল করার জন্য পাতাটি মুছে দিতে চান?",
-       "delete_and_move_confirm": "হà§\8dযাà¦\81, à¦ªà¦¾à¦¤à¦¾à¦\9fি à¦®à§\81à¦\9bà§\87 à¦«à§\87লা à¦¹à§\8bà¦\95",
+       "delete_and_move_confirm": "হà§\8dযাà¦\81, à¦ªà¦¾à¦¤à¦¾à¦\9fি à¦®à§\81à¦\9bà§\81ন",
        "delete_and_move_reason": "\"[[$1]]\" থেকে স্থানান্তরের স্বার্থে মুছে ফেলা হয়েছে",
        "selfmove": "উৎস ও গন্তব্য পাতা একই শিরোনামের; কোন পাতা একই শিরোনামের আরেক পাতায় সরানো যাবে না।",
        "immobile-source-namespace": "পাতাটি \"$1\" নামস্থানে স্থানান্তর সম্ভব নয়",
        "pageinfo-article-id": "পাতার আইডি",
        "pageinfo-language": "পাতার তথ্যের ভাষা",
        "pageinfo-content-model": "পাতার বিষয়বস্তুর মডেল",
+       "pageinfo-content-model-change": "পরিবর্তন",
        "pageinfo-robot-policy": "রোবটের মাধ্যমে ইন্ডেক্স করা হচ্ছে",
        "pageinfo-robot-index": "অনুমোদিত",
        "pageinfo-robot-noindex": "অনুনমোদিন",
        "mediastatistics-header-office": "অফিস",
        "mediastatistics-header-archive": "সংকুচিত বিন্যাস",
        "mediastatistics-header-total": "সকল ফাইল",
+       "json-warn-trailing-comma": "JSON থেকে $1টি সর্বশেষ {{PLURAL:$1|কমা}} সরানো হয়েছে",
        "json-error-unknown": "JSON-এ একটি সমস্যা রয়েছে। ত্রুটি: $1",
+       "json-error-depth": "সর্বাধিক স্ট্যাকের গভীরতা অতিক্রম হয়েছে",
        "json-error-state-mismatch": "অকার্যকর বা ত্রুটিপূর্ণ JSON",
        "json-error-ctrl-char": "অক্ষর নিয়ন্ত্রণ ত্রুটি, সম্ভবত ভুল এনকোডকৃত",
        "json-error-syntax": "সিনট্যাক্স ত্রুটি",
        "mw-widgets-dateinput-placeholder-month": "বববব-মম",
        "mw-widgets-titleinput-description-new-page": "পাতা এখনো বিদ্যমান নয়",
        "mw-widgets-titleinput-description-redirect": "$1-এ পুনঃনির্দেশিত",
-       "api-error-blacklisted": "অনুগ্রহ করে অপর কোনো বর্ণনামূলক নাম ব্যবহার করুন।",
        "sessionmanager-tie": "একাধিক অনুরোধের প্রমাণীকরণের ধরন একত্রিত করা যাবে না: $1।",
        "sessionprovider-generic": "$1টি সেশন",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "কুকি-ভিত্তিক সেশন",
        "log-action-filter-block": "বাধাদানের ধরন:",
        "log-action-filter-delete": "অপসারণের ধরন:",
        "log-action-filter-import": "আমদানির ধরন:",
+       "log-action-filter-managetags": "ট্যাগ ব্যবস্থাপনা কার্যের ধরন:",
+       "log-action-filter-move": "স্থানান্তরের ধরন:",
+       "log-action-filter-newusers": "অ্যাকাউন্ট তৈরির ধরন:",
        "log-action-filter-patrol": "টহলের ধরন:",
        "log-action-filter-protect": "সুরক্ষার ধরন:",
+       "log-action-filter-rights": "অধিকার পরিবর্তনের ধরন:",
        "log-action-filter-upload": "আপলোডের ধরন:",
        "log-action-filter-all": "সব",
        "log-action-filter-block-block": "বাধাদান",
        "log-action-filter-delete-restore": "পাতা পুনঃরুদ্ধার",
        "log-action-filter-delete-event": "লগ অপসারণ",
        "log-action-filter-delete-revision": "সংশোধন অপসারণ",
+       "log-action-filter-import-interwiki": "আন্তঃউইকি আমদানি",
+       "log-action-filter-import-upload": "XML আপলোড কর্তৃক আমদানি",
+       "log-action-filter-managetags-create": "ট্যাগ সৃষ্টিকরণ",
+       "log-action-filter-managetags-delete": "ট্যাগ অপসারণ",
+       "log-action-filter-managetags-activate": "ট্যাগ সক্রিয়করণ",
+       "log-action-filter-managetags-deactivate": "ট্যাগ নিষ্ক্রিয়করণ",
+       "log-action-filter-newusers-create": "বেনামী ব্যবহারকারী দ্বারা সৃষ্টি",
+       "log-action-filter-newusers-create2": "নিবন্ধিত ব্যবহারকারী দ্বারা সৃষ্টি",
+       "log-action-filter-newusers-autocreate": "স্বয়ংক্রিয় সৃষ্টি",
+       "log-action-filter-newusers-byemail": "ই-মেইলে পাসওয়ার্ড প্রেরণের দ্বারা সৃষ্টি",
        "log-action-filter-patrol-patrol": "ম্যানুয়াল টহল",
        "log-action-filter-patrol-autopatrol": "স্বয়ংক্রিয় টহল",
        "log-action-filter-protect-protect": "সুরক্ষা",
        "log-action-filter-protect-modify": "সুরক্ষা পরিমার্জন",
        "log-action-filter-protect-unprotect": "অসুরক্ষা",
+       "log-action-filter-protect-move_prot": "সুরক্ষা স্থানান্তরিত",
+       "log-action-filter-rights-rights": "ম্যানুয়াল পরিবর্তন",
        "log-action-filter-rights-autopromote": "স্বয়ংক্রিয় পরিবর্তন",
        "log-action-filter-upload-upload": "নতুন আপলোড",
        "log-action-filter-upload-overwrite": "পুনঃআপলোড",
+       "authmanager-authn-no-primary": "সরবরাহকৃত পরিচয়পত্রের অনুমোদন যাচাই করা যায়নি।",
+       "authmanager-create-disabled": "অ্যাকাউন্ট সৃষ্টিকরণ নিষ্ক্রিয় করা হয়েছে।",
+       "authmanager-create-from-login": "আপনার একাউন্ট তৈরি করতে, নীচের ক্ষেত্রগুলি পূরণ করুন।",
+       "authmanager-authplugin-setpass-failed-title": "পাসওয়ার্ড পরিবর্তন ব্যর্থ হয়েছে",
        "authmanager-authplugin-setpass-bad-domain": "অবৈধ ডোমেইন।",
        "authmanager-autocreate-noperm": "স্বয়ংক্রিয় অ্যাকাউন্ট সৃষ্টি মঞ্জুরিপ্রাপ্ত নয়।",
        "authmanager-userdoesnotexist": "ব্যবহারকারী অ্যাকাউন্ট \"$1\" অনিবন্ধিত।",
        "authmanager-realname-label": "প্রকৃত নাম",
        "authmanager-realname-help": "ব্যবহারকারীর প্রকৃত নাম",
        "authmanager-provider-temporarypassword": "অস্থায়ী পাসওয়ার্ড",
+       "authprovider-confirmlink-success-line": "$1: সংযোগ করা সফল হয়েছে।",
        "authprovider-resetpass-skip-label": "উপেক্ষা করো",
        "authprovider-resetpass-skip-help": "পাসওয়ার্ড পুনঃস্থাপন করা উপেক্ষা করুন।",
+       "authform-newtoken": "টোকেন অনুপস্থিত। $1",
+       "authform-notoken": "টোকেন অনুপস্থিত",
        "authform-wrongtoken": "ভুল টোকেন",
        "specialpage-securitylevel-not-allowed-title": "অনুমতি নেই",
+       "specialpage-securitylevel-not-allowed": "দুঃখিত, আপনি এই পাতা ব্যবহার করতে অনুমতিপ্রাপ্ত নন কারণ আপনার পরিচয় যাচাই করা যায়নি।",
        "cannotauth-not-allowed-title": "অনুমতি অস্বীকৃত",
        "cannotauth-not-allowed": "আপনি এই পাতাটি ব্যবহার করতে অনুমতিপ্রাপ্ত নন।",
        "changecredentials": "পরিচয়পত্র পরিবর্তন করুন",
        "removecredentials-success": "আপনার পরিচয়পত্র সরানো হয়েছে।",
        "credentialsform-provider": "পরিচয়পত্রের ধরন:",
        "credentialsform-account": "অ্যাকাউন্টের নাম:",
-       "linkaccounts": "অ্যাকাউন্ট সংযোগ করুন"
+       "linkaccounts": "অ্যাকাউন্ট সংযোগ করুন",
+       "linkaccounts-submit": "অ্যাকাউন্ট সংযুক্ত করুন",
+       "unlinkaccounts": "অ্যাকাউন্ট সংযোগ বিচ্ছিন্ন করুন",
+       "unlinkaccounts-success": "অ্যাকাউন্টের সংযোগ বিচ্ছিন্ন করা হয়েছে।",
+       "userjsispublic": "অনুগ্রহ করে লক্ষ্য করুন: জাভাস্ক্রিপ্টের উপপাতাগুলিতে গোপনীয় তথ্য থাকা উচিত নয় যেহেতু অন্যান্য ব্যবহারকারীও এগুলি দেখতে পান।",
+       "usercssispublic": "অনুগ্রহ করে লক্ষ্য করুন: সিএসএসের উপপাতাগুলিতে গোপনীয় তথ্য থাকা উচিত নয় যেহেতু অন্যান্য ব্যবহারকারীও এগুলি দেখতে পান।"
 }
index 482c3ba..5553398 100644 (file)
        "tagline": "ཡོང་ཁུངས་{{SITENAME}}",
        "help": "ཕན་གྲོགས།",
        "search": "འཚོལ་བ།",
+       "search-ignored-headings": " #<!-- སྟར་པ་འདི་སྔར་གྱི་ཇི་མ་ཇི་འཞིན་དུ་འཇོག་རོགས། --> <pre>\n# འགོ་བརྗོད་དེ་འཚོལ་ཞིབ་ནང་སྣང་མེད་དུ་བསྐྱུར་རོགས།\n# ཤོག་ངོས་འདིའི་འགོ་བརྗོད་ལ་ཐོ་འགོད་རེའུ་མིག་དུ་འགོད་ཚར་མ་ཐག་འགྱུར་བ་རྣམས་མངོན་ཐུབ།\n# ཁྱེད་ཀྱི་རྩོམ་སྒྲིག་སྟོང་པ་དྱས་མ་ཐག་ཤོག་ངོས་འདི་དབང་ཤུགས་ཀྱི་ཐོ་འགོད་རེའུ་མིག་དུ་འགོད་ཐུབ།\n# ཚིག་སྦྱོར་རྣམས་གཤམ་གསལ་ལྟར་དཀོད་རོགས།\n#   * ཡིག་འབྲུ་\"#\" ནས་སྟར་པའི་འཇུག་བར་ཚང་མ་མཆན་འགྲེལ་ཡིན།\n#   * སྟོང་ཆ་མེད་པའི་སྟར་པ་འདི་སྣང་མེད་དུ་བསྐྱུར་དགོས་པའི་འགོ་བརྗོད་ཡིན། \nཟུར་མཆན།\nཕྱི་ཕྱོགས་སྦྲེལ་མཐུད།\nའདིར་ཡང་གཟིགས་རོགས།\n #</pre> <!-- སྟར་པ་འདི་སྔར་གྱི་ཇི་མ་ཇི་འཞིན་དུ་འཇོག་རོགས། -->",
        "searchbutton": "འཚོལ།",
        "go": "སོང་།",
        "searcharticle": "འཚོལ།",
index 8c3c3d8..5bea10a 100644 (file)
        "october-date": "$1 a viz Here",
        "november-date": "$1 a viz Du",
        "december-date": "$1 a viz Kerzu",
+       "period-am": "mintin",
+       "period-pm": "goude-merenn",
        "pagecategories": "{{PLURAL:$1|Rummad |Rummad }}",
        "category_header": "Niver a bennadoù er rummad \"$1\"",
        "subcategories": "Isrummadoù",
        "yourpasswordagain": "Skrivit ho ker-tremen en-dro",
        "createacct-yourpasswordagain": "Kadarnaat ar ger-tremen",
        "createacct-yourpasswordagain-ph": "Skrivit ar ger-tremen adarre",
-       "remembermypassword": "Derc'hel soñj eus ma ger-tremen war an urzhiataer-mañ (evit $1 devezh{{PLURAL:$1||}} d'ar muiañ)",
        "userlogin-remembermypassword": "Derc'hel ac'hanon kevreet",
        "userlogin-signwithsecure": "Implijout ur gevreadenn suraet",
        "yourdomainname": "Ho tomani",
        "passwordreset-emailtext-user": "Goulennet en deus an implijer $1 war  {{SITENAME}} e vefe degaset soñj dezhañ eus titouroù e gont evit {{SITENAME}} ($4). Emañ liammet {{PLURAL:$3|ar gont implijer|ar c'hontoù implijer}} da-heul gant ar chomlec'h postel-mañ :\n\n$2\n\nMont a raio da get {{PLURAL:$3|ar ger-tremen da c'hortoz|ar gerioù-tremen da c'hortoz}} a-benn {{PLURAL:$5|un devezh|$5 deiz}}.\nMat e vefe deoc'h kevreañ ha dibab ur ger-tremen nevez bremañ. Mard eo bet goulennet kement-se gant unan bennak all pe m'hoc'h eus soñj eus ho ker-tremen orin ha mar ne fell ket deoc'h e cheñch ken, na daolit ket evezh ouzh ar gemennadenn-mañ ha dalc'hit d'ober gant ho ker-tremen kozh.",
        "passwordreset-emailelement": "Anv implijer :           \n$1\n\nGer-tremen da c'hortoz : \n$2",
        "passwordreset-emailsentemail": "Kaset ez eus bet ur postel deoc'h da adderaouekaat ho ker-tremen.",
-       "passwordreset-emailsent-capture": "Ur postel evit aderaouekaat ho ker-tremen, evel diskouezet amañ dindan, zo bet kaset.",
-       "passwordreset-emailerror-capture": "Kaset ez eus bet ur postel degas da soñj evel m'emañ diskouezet amañ dindan met c'hwitet eo bet ar gasadenn d'an {{GENDER:$2|implijer|implijerez}} : $1",
        "changeemail": "Kemmañ ar chomlec'h postel",
        "changeemail-header": "Kemmañ chomlec'h postel ar gont",
        "changeemail-no-info": "Ret eo deoc'h bezañ kevreet a-benn mont d'ar bajenn-se war-eeun.",
        "minoredit": "Kemm dister",
        "watchthis": "Evezhiañ ar pennad-mañ",
        "savearticle": "Enrollañ ar bajenn",
+       "savechanges": "Enrollañ ar c'hemmoù",
+       "publishpage": "Embann ar bajenn",
+       "publishchanges": "Embann ar c'hemmoù",
        "preview": "Rakwelet",
        "showpreview": "Rakwelet",
        "showdiff": "Diskouez ar c'hemmoù",
        "undo-nochange": "War a seblant eo bet nullet ar c'hemm dija.",
        "undo-summary": "Dizober kemmoù $1 a-berzh [[Special:Contributions/$2|$2]] ([[User talk:$2|kaozeal]])",
        "undo-summary-username-hidden": "Dizober ar reizhadenn $1 gant un implijer kuzhet",
-       "cantcreateaccounttitle": "Dibosupl krouiñ ar gont",
        "cantcreateaccount-text": "Stanket eo bet ar c'hrouiñ kontoù adal ar chomlec'h IP ('''$1''') gant [[User:$3|$3]].\n\nAn abeg roet gant $3 zo ''$2''",
        "viewpagelogs": "Gwelet ar marilhoù evit ar bajenn-mañ",
        "nohistory": "Ar bajenn-mañ n'he deus tamm istor ebet.",
        "mostrevisions": "Pennadoù bet kemmet ar muiañ",
        "prefixindex": "An holl bajennoù a grog gant...",
        "prefixindex-namespace": "An holl bajennoù enno ur rakger (esaouenn anv $1)",
+       "prefixindex-submit": "Diskouez",
        "prefixindex-strip": "Lemel ar rakger er roll",
        "shortpages": "Pennadoù berr",
        "longpages": "Pennadoù hir",
        "usereditcount": "$1 {{PLURAL:$1|kemm|kemm}}",
        "usercreated": "{{GENDER:$3|Krouet}} d'an $1 da $2",
        "newpages": "Pajennoù nevez",
+       "newpages-submit": "Diskouez",
        "newpages-username": "Anv implijer :",
        "ancientpages": "Pennadoù koshañ",
        "move": "adenvel",
        "apisandbox": "Poull-traezh API",
        "apisandbox-api-disabled": "Diweredekaet eo API war al lec'hienn-mañ.",
        "apisandbox-intro": "Grit gant ar bajenn-mañ evit amprouiñ '''servij Web API MediaWiki'''.\nKit da deuler ur sell war [https://www.mediawiki.org/wiki/API:Main_page titouroù an API] evit gouzout hiroc'h war an doare da embreger API. Da skouer :\n[https://www.mediawiki.org/wiki/API#A_simple_example gwelet danvez ur bennbajenn]. Dibabit un oberiadenn bennak evit gwelet skouerioù all",
+       "apisandbox-unfullscreen": "Diskouez ar bajenn",
        "apisandbox-submit": "Sevel ar goulenn",
        "apisandbox-reset": "Riñsañ",
-       "apisandbox-examples": "Skouer",
-       "apisandbox-results": "Disoc'h",
+       "apisandbox-retry": "Klask en-dro",
+       "apisandbox-examples": "Skouerioù",
+       "apisandbox-dynamic-parameters": "Arventenn ouzhpenn",
+       "apisandbox-dynamic-parameters-add-label": "Ouzhpennañ un arventenn:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Anv an arventenn",
+       "apisandbox-results": "Disoc'hoù",
        "apisandbox-request-url-label": "Goulenn URL :",
        "apisandbox-request-time": "Pad ar goulenn: $1",
        "booksources": "Oberennoù dave",
        "specialloguserlabel": "Implijer :",
        "speciallogtitlelabel": "Bukadenn (titl pe implijer) :",
        "log": "Marilhoù",
+       "logeventslist-submit": "Diskouez",
        "all-logs-page": "An holl varilhoù foran",
        "alllogstext": "Diskwel a-gevret an holl varilhoù hegerz war {{SITENAME}}.\nGallout a rit strishaat ar mod diskwel en ur zibab ar marilh, an anv implijer (diwallit ouzh ar pennlizherennoù) pe ar bajenn a fell deoc'h (memes tra).",
        "logempty": "Goullo eo istor ar bajenn-mañ.",
        "log-title-wildcard": "Klask an titloù a grog gant an destenn-mañ",
        "showhideselectedlogentries": "Diskouez/kuzhat penngerioù ar marilh bet diuzet",
+       "checkbox-select": "Diuzañ : $1",
+       "checkbox-all": "An holl",
+       "checkbox-none": "Hini ebet",
+       "checkbox-invert": "Eilpennañ",
        "allpages": "An holl bajennoù",
        "nextpage": "Pajenn war-lerc'h ($1)",
        "prevpage": "Pajenn gent ($1)",
        "cachedspecial-viewing-cached-ts": "Emaoc'h o sellet ouzh ur stumm krubuilhet eus ar bajenn-mañ a c'hall bezañ dispredet un disterañ.",
        "cachedspecial-refresh-now": "Gwelet an hini nevesañ.",
        "categories": "Roll ar rummadoù",
+       "categories-submit": "Diskouez",
        "categoriespagetext": "Er {{PLURAL:$1|rummad|rummadoù}} da-heul ez eus pajennoù pe restroù media.\nNe ziskouezer ket amañ ar [[Special:UnusedCategories|Rummadoù dizimplij]].\nGwelet ivez ar [[Special:WantedCategories|rummadoù goulennet a vank]].",
        "categoriesfrom": "Diskouez ar rummadoù en ur gregiñ gant :",
        "deletedcontributions": "Degasadennoù diverket un implijer",
        "wlheader-showupdated": "E '''tev''' emañ merket ar pajennoù bet kemmet abaoe ar wezh ziwezhañ hoc'h eus sellet outo",
        "wlnote": "Setu aze {{PLURAL:$1|ar c'hemm diwezhañ|ar '''$1''' kemm diwezhañ}} c'hoarvezet e-kerzh an {{PLURAL:$2|eurvezh|'''$2''' eurvezh}} diwezhañ, d'an $3 da $4.",
        "wlshowlast": "Diskouez an $1 eurvezh $2 devezh diwezhañ",
+       "watchlist-hide": "Kuzhat",
+       "watchlist-submit": "Diskouez",
+       "wlshowhideminor": "kemmoù dister",
+       "wlshowhidebots": "robotoù",
+       "wlshowhideliu": "implijerien enrollet",
+       "wlshowhideanons": "implijerien dizanv",
        "wlshowhidemine": "ma c'hemmoù",
        "watchlist-options": "Dibarzhioù ar roll evezhiañ",
        "watching": "Heuliet...",
        "whatlinkshere-hidelinks": "$1 liamm",
        "whatlinkshere-hideimages": "$1 ar restroù liammet",
        "whatlinkshere-filters": "Siloù",
+       "whatlinkshere-submit": "Mont",
        "autoblockid": "Emstankañ #$1",
        "block": "Stankañ an implijer",
        "unblock": "Distankañ an implijer",
        "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|adweladenn}} enporzhiet eus $2",
        "javascripttest": "Amprouadenn JavaScript",
        "javascripttest-qunit-intro": "Sellet ouzh [$1 an teulioù amprouiñ] e mediawiki.org.",
-       "tooltip-pt-userpage": "Ho pajenn implijer",
+       "tooltip-pt-userpage": "{{GENDER:|Ho pajenn}} implijer",
        "tooltip-pt-anonuserpage": "Ar bajenn implijer evit ar c'homlec'h IP implijet ganeoc'h",
-       "tooltip-pt-mytalk": "Ho pajenn gaozeal",
+       "tooltip-pt-mytalk": "{{GENDER:|Ho}} pajenn gaozeal",
        "tooltip-pt-anontalk": "Kaozeadennoù diwar-benn ar c'hemmoù graet adal ar chomlec'h-mañ",
-       "tooltip-pt-preferences": "Ma fenndibaboù",
+       "tooltip-pt-preferences": "{{GENDER:|Ma}} fenndibaboù",
        "tooltip-pt-watchlist": "Roll ar pajennoù evezhiet ganeoc'h.",
        "tooltip-pt-mycontris": "Roll ho tegasadennoù",
        "tooltip-pt-login": "Daoust ma n'eo ket ret, ec'h aliomp deoc'h kevreañ",
        "tooltip-t-recentchangeslinked": "Roll ar c'hemmoù diwezhañ war ar pajennoù liammet ouzh ar bajenn-mañ",
        "tooltip-feed-rss": "Magañ ar red RSS evit ar bajenn-mañ",
        "tooltip-feed-atom": "Magañ ar red Atom evit ar bajenn-mañ",
-       "tooltip-t-contributions": "Gwelet roll degasadennoù an implijer-mañ",
+       "tooltip-t-contributions": "Gwelet roll degasadennoù {{GENDER:$1|this user}} an implijer-mañ",
        "tooltip-t-emailuser": "Kas ur postel d'an implijer-mañ",
        "tooltip-t-upload": "Enporzhiañ ur skeudenn pe ur restr media war ar servijer",
        "tooltip-t-specialpages": "Roll an holl bajennoù dibar",
        "tooltip-ca-nstab-category": "Gwelet pajenn ar rummad",
        "tooltip-minoredit": "Merkañ ar c'hemm-mañ evel dister",
        "tooltip-save": "Enrollañ ho kemmoù",
+       "tooltip-publish": "Embann ho kemmoù",
        "tooltip-preview": "Rakwelet ar c'hemmoù; trugarez d'ober gantañ a-raok enrollañ!",
        "tooltip-diff": "Diskouez ar c'hemmoù degaset ganeoc'h en destenn.",
        "tooltip-compareselectedversions": "Sellet ouzh an diforc'hioù zo etre daou stumm diuzet ar bajenn-mañ.",
        "confirm-watch-top": "Ouzhpennañ ar bajenn-mañ d'ho roll evezhiañ",
        "confirm-unwatch-button": "Mat eo",
        "confirm-unwatch-top": "Lemel ar bajenn-mañ a-ziwar ho roll evezhiañ",
+       "confirm-rollback-button": "Mat eo",
        "quotation-marks": "« $1 »",
        "imgmultipageprev": "&larr; pajenn gent",
        "imgmultipagenext": "pajenn war-lerc'h &rarr;",
        "iranian-calendar-m11": "11vet miz Jalāli",
        "iranian-calendar-m12": "12vet miz Jalāli",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|kaozeal]])",
+       "timezone-local": "Lec'hel",
        "duplicate-defaultsort": "Diwallit : Frikañ a ra an alc'hwez dre ziouer \"$2\" an hini a oa a-raok \"$1\".",
        "version": "Stumm",
        "version-extensions": "Astennoù staliet",
        "tags-source-header": "Mammenn",
        "tags-active-header": "Oberiant ?",
        "tags-hitcount-header": "Kemmoù balizennet",
+       "tags-actions-header": "Oberoù",
        "tags-active-yes": "Ya",
        "tags-active-no": "Ket",
        "tags-edit": "aozañ",
        "special-characters-group-thai": "Thai",
        "special-characters-group-lao": "Laoseg",
        "special-characters-group-khmer": "Khmer",
-       "api-error-blacklisted": "Dibabit un titl deskrivañ all",
        "randomrootpage": "Pajenn wrizienn dargouezhek"
 }
index 6c710f1..503f347 100644 (file)
@@ -65,7 +65,7 @@
        "tog-ccmeonemails": "Pošalji mi kopije e-pošte koju pošaljem drugim korisnicima",
        "tog-diffonly": "Ne prikazuj sadržaj stranice ispod razlika",
        "tog-showhiddencats": "Prikaži skrivene kategorije",
-       "tog-norollbackdiff": "Izostavi razliku nakon vraćanja",
+       "tog-norollbackdiff": "Ne prikazuj razliku nakon izvršenog vraćanja",
        "tog-useeditwarning": "Upozori me kad napuštam stranicu za izmjene bez sačuvanih promjena",
        "tog-prefershttps": "Uvijek koristi sigurnu konekciju kada sam prijavljen.",
        "underline-always": "Uvijek",
        "yourpasswordagain": "Ponovo upišite lozinku:",
        "createacct-yourpasswordagain": "Potvrdite lozinku",
        "createacct-yourpasswordagain-ph": "Unesite lozinku opet",
-       "remembermypassword": "Zapamti moju lozinku na ovom pregledniku (najduže $1 {{PLURAL:$1|dan|dana}})",
        "userlogin-remembermypassword": "Ostavi me prijavljenog/-u",
        "userlogin-signwithsecure": "Koristite sigurnu konekciju",
        "yourdomainname": "Vaš domen:",
        "passwordreset-emailtext-user": "Korisnik $1 na {{SITENAME}} je zatražio podsjetnik o detaljima Vašeg računa za {{SITENAME}} ($4). Sljedeći {{PLURAL:$3|korisnički račun je|korisnički računi su}} povezani s ovom e-mail adresom:\n\n$2\n\n{{PLURAL:$3|Ova privremena šifra|Ove privremene šifre}} će isteći za {{PLURAL:$5|jedan dan|$5 dana}}.\nTrebate se prijaviti i odabrati novu šifru. Ako je neko drugi napravio ovaj zahtjev, ili ako ste se sjetili Vaše originalne šifre, a ne želite je više promijeniti, možete zanemariti ovu poruku i nastaviti koristiti staru šifru.",
        "passwordreset-emailelement": "Korisničko ime: \n$1\n\nPrivremena šifra: \n$2",
        "passwordreset-emailsentemail": "Ako je ovo adresa e-pošte s kojom ste registrirali ovaj račun, podsjetnik šifre će vam biti poslan na vašu adresu e-pošte.",
-       "passwordreset-emailsent-capture": "Poslan je podsjetnik preko e-pošte (prikazano ispod).",
-       "passwordreset-emailerror-capture": "E-poruka za resetiranje lozinke, prikazano ispod, poslana je, ali slanje {{GENDER:$2|korisniku|korisnici}} nije uspjelo: $1",
        "changeemail": "Promjena ili uklanjanje e-adrese",
        "changeemail-header": "Ispunite sljedeći formular da biste promijenili adresu e-pošte. Ako želite ukloniti postojeću adresu e-pošte s vašeg korisničkog računa, pri ispunjavanju formulara, polje nove adrese e-pošte ostavite prazno.",
-       "changeemail-passwordrequired": "Morat ćete unijeti šifru da biste potvrdili ovu izmjenu.",
        "changeemail-no-info": "Morate biti prijavljeni za direktan pristup ovoj stranici.",
        "changeemail-oldemail": "Trenutna adresa e-pošte:",
        "changeemail-newemail": "Nova adresa e-pošte:",
        "minoredit": "Ovo je manja izmjena",
        "watchthis": "Prati ovu stranicu",
        "savearticle": "Sačuvaj stranicu",
+       "savechanges": "Sačuvaj izmjene",
+       "publishpage": "Objavi stranicu",
+       "publishchanges": "Objavi izmjene",
        "preview": "Pregled stranice",
        "showpreview": "Prikaži izgled",
        "showdiff": "Prikaži izmjene",
        "undo-nochange": "Izgleda da je izmjena već vraćena.",
        "undo-summary": "Poništena izmjena $1 {{GENDER:$2|korisnika|korisnice}} [[Special:Contributions/$2|$2]] ([[User talk:$2|razgovor]])",
        "undo-summary-username-hidden": "Poništi izmjenu $1 od skrivenog korisnika",
-       "cantcreateaccounttitle": "Nije moguće napraviti korisnički račun",
        "cantcreateaccount-text": "Pravljenje korisničkog računa sa ove IP adrese ('''$1''') je blokirano od strane [[User:$3|$3]].\n\nRazlog koji je naveo $3 je ''$2''",
        "cantcreateaccount-range-text": "Pravljenje računa sa IP adresa u rasponu <strong>$1</strong>, koji uključuje i vašu IP adresu (<strong>$4</strong>), je blokirao korisnik [[User:$3|$3]].\n\nNavedeni razlog korisnika $3 je <em>$2</em>",
        "viewpagelogs": "Pogledaj zapisnike ove stranice",
        "history-feed-item-nocomment": "$1 u $2",
        "history-feed-empty": "Tražena stranica ne postoji.\nMoguće da je izbrisana sa wikija, ili preimenovana.\nPokušajte [[Special:Search|pretražiti wiki]] za slične stranice.",
        "history-edit-tags": "Uredi oznake izabranih verzija",
-       "rev-deleted-comment": "(uklonjen sažetak izmjene)",
+       "rev-deleted-comment": "(sažetak izmjene uklonjen)",
        "rev-deleted-user": "(korisničko ime uklonjeno)",
        "rev-deleted-event": "(stavka zapisa obrisana)",
        "rev-deleted-user-contribs": "[korisničko ime ili IP adresa uklonjeni - izmjena sakrivena u spisku doprinosa]",
        "rev-suppressed-text-unhide": "Ova revizija stranice je '''uklonjena'''.\nMožete pogledati detalje u [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zapisu uklanjanja].\nVi je i dalje možete [$1 vidjeti ovu reviziju] ako želite.",
        "rev-deleted-text-view": "Revizija ove stranice je '''obrisana'''.\nVi je možete vidjeti; detalji o tome se mogu vidjeti u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisu brisanja].",
        "rev-suppressed-text-view": "Ova revizija stranice je '''uklonjena'''.\nVi je možete vidjeti; možete pogledati detalje u [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zapisu uklanjanja].",
-       "rev-deleted-no-diff": "Ne možete vidjeti ove razlike jer je jedna od revizija '''obrisana'''.\nMožete pregledati detalje u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisima brisanja].",
+       "rev-deleted-no-diff": "Ne možete vidjeti ovu razliku jer je jedna od izmjena '''obrisana'''.\nDetalji se nalaze u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisniku brisanja].",
        "rev-suppressed-no-diff": "Ne možete vidjeti ove razlike jer je jedna od revizija '''obrisana'''.",
        "rev-deleted-unhide-diff": "Jedna od revizija u ovom pregledu razlika je '''obrisana'''.\nMožete pregledati detalje u [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} zapisniku brisanja].\nVi još uvijek možete [$1 vidjeti ove razlike] ako želite da nastavite.",
        "rev-suppressed-unhide-diff": "edna od revizija ove razlike je '''uklonjena'''.\nMožete pogledati detalje u [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} zapisniku uklanjanja].\nVi i dalje možete [$1 vidjeti ove razlike] ako želite da nastavite.",
        "revdelete-unsuppress": "Ukloni ograničenja na vraćenim revizijama",
        "revdelete-log": "Razlog:",
        "revdelete-submit": "Primijeni na odabrane {{PLURAL:$1|reviziju|revizije}}",
-       "revdelete-success": "'''Vidljivost revizije uspješno ažurirana.'''",
+       "revdelete-success": "'''Vidljivost izmjene je ažurirana.'''",
        "revdelete-failure": "'''Vidljivost revizije nije mogla biti ažurirana:'''\n$1",
        "logdelete-success": "'''Vidljivost evidencije uspješno postavljena.'''",
        "logdelete-failure": "'''Zapisnik vidljivosti nije mogao biti postavljen:'''\n$1",
        "rightslogtext": "Ovo je zapisnik promjena korisničkih prava.",
        "action-read": "čitate ovu stranicu",
        "action-edit": "uređujete ovu stranicu",
-       "action-createpage": "napravite stranicu",
-       "action-createtalk": "kreirate stranice za razgovor",
+       "action-createpage": "napravite ovu stranicu",
+       "action-createtalk": "pravite stranice za razgovor",
        "action-createaccount": "napravite ovaj korisnički račun",
        "action-history": "gledate historiju ove stranice",
        "action-minoredit": "da označite ovu izmjenu kao malu",
        "action-viewmyprivateinfo": "pogledajte svoje privatne informacije",
        "action-editmyprivateinfo": "uredite svoje privatne podatke",
        "action-editcontentmodel": "uredi model sadržaja stranice",
-       "action-managechangetags": "napravite i uklonite oznake iz baze podataka",
+       "action-managechangetags": "pravite i (de)aktivirate oznake",
        "action-applychangetags": "dodate oznake uz vaše izmjene",
        "action-changetags": "dodate ili uklonite razne oznake na pojedinačnim verzijama i unosima u zapisnicima",
        "nchanges": "$1 {{PLURAL:$1|promjena|promjene|promjena}}",
        "listusersfrom": "Prikaži korisnike koji počinju sa:",
        "listusers-submit": "Prikaži",
        "listusers-noresult": "Nije pronađen korisnik.",
-       "listusers-blocked": "(blokiran)",
+       "listusers-blocked": "({{GENDER:$1|blokiran|blokirana|blokiran}})",
        "activeusers": "Spisak aktivnih korisnika",
        "activeusers-intro": "Ovo je spisak korisnika koji su imali neku aktivnost u {{PLURAL:$1|posljednji $1 dan|posljednja $1 dana|posljednjih $1 dana}}.",
        "activeusers-count": "$1 {{PLURAL:$1|izmjena|izmjene|izmjena}} u {{PLURAL:$3|posljednji $3 dan|posljednja $3 dana|posljednjih $3 dana}}",
        "logentry-suppress-revision-legacy": "$1 je tajno {{GENDER:$2|promijenio|promijenila}} vidljivost izmjena na stranici $3",
        "revdelete-content-hid": "sadržaj je sakriven",
        "revdelete-summary-hid": "sažetak izmjene je sakriven",
-       "revdelete-uname-hid": "sažetak izmjene je sakriven",
+       "revdelete-uname-hid": "korisničko ime je sakriveno",
        "revdelete-content-unhid": "sadržaj je otkriven",
        "revdelete-summary-unhid": "sažetak izmjene je otkriven",
        "revdelete-uname-unhid": "korisničko ime je otkriveno",
        "special-characters-group-ipa": "IPA",
        "special-characters-group-symbols": "simboli",
        "special-characters-group-greek": "grčki",
+       "special-characters-group-greekextended": "prošireni grčki",
        "special-characters-group-cyrillic": "ćirilica",
        "special-characters-group-arabic": "arapski",
        "special-characters-group-arabicextended": "prošireni arapski",
        "mw-widgets-dateinput-no-date": "Nikakav datum nije izabran",
        "mw-widgets-titleinput-description-new-page": "stranica još ne postoji",
        "mw-widgets-titleinput-description-redirect": "preusmjerava na $1",
-       "api-error-blacklisted": "Molimo izaberite drugačiji, deskriptivniji naziv.",
        "randomrootpage": "Slučajna root stranica",
        "log-action-filter-block": "Vrsta blokiranja:",
        "log-action-filter-delete": "Vrsta brisanja:",
index 8e1e80a..9e9e6b6 100644 (file)
        "tagline": "De {{SITENAME}}",
        "help": "Ajuda",
        "search": "Cerca",
+       "search-ignored-headings": " #<!-- deixeu aquesta línia tal com està --> <pre>\n# Títols que seran ignorats pel cercador.\n# Els canvis fets aquí tindran efecte tant aviat com la pàgina amb el títol sigui indexada.\n# Podeu forçar que una pàgina s'indexi de nou fent una edició nul·la.\n# La sintaxi és la següent:\n#   * Tot el que hi hagi des d'un caràcter \"#\" fins el final de línia és un comentari\n#   * Tota línia no buida és el títol exacte a ignorar, amb les majúscules i complet\nReferències\nEnllaços externs\nVegeu també\n #</pre> <!-- deixeu aquesta línia tal com està -->",
        "searchbutton": "Cerca",
        "go": "Vés-hi",
        "searcharticle": "Vés-hi",
        "yourpasswordagain": "Escriviu una altra vegada la contrasenya",
        "createacct-yourpasswordagain": "Confirmeu la contrasenya",
        "createacct-yourpasswordagain-ph": "Introduïu de nou la contrasenya",
-       "remembermypassword": "Recorda la contrasenya entre sessions (per un màxim de $1 {{PLURAL:$1|dia|dies}})",
        "userlogin-remembermypassword": "Mantén-me connectat",
        "userlogin-signwithsecure": "Connexió segura",
        "cannotloginnow-title": "Ara no es pot iniciar la sessió",
        "password-change-forbidden": "No podeu canviar les contrasenyes en aquest wiki.",
        "externaldberror": "Hi ha hagut un error en la base de dades d'autenticació o bé no teniu permís per a actualitzar el vostre compte extern.",
        "login": "Inici de sessió",
+       "login-security": "Verificació de la identitat",
        "nav-login-createaccount": "Inicia una sessió / crea un compte",
        "userlogin": "Inicia una sessió / crea un compte",
        "userloginnocreate": "Inici de sessió",
        "createaccountreason": "Motiu:",
        "createacct-reason": "Motiu",
        "createacct-reason-ph": "Per què creeu un altre compte",
+       "createacct-reason-help": "Missatge mostrat en el registre de creació de comptes",
        "createacct-submit": "Crea el meu compte",
        "createacct-another-submit": "Crea un compte",
+       "createacct-continue-submit": "Continua amb la creació del compte",
+       "createacct-another-continue-submit": "Continua amb la creació del compte",
        "createacct-benefit-heading": "{{SITENAME}} és feta per gent com tu.",
        "createacct-benefit-body1": "{{PLURAL:$1|edició|edicions}}",
        "createacct-benefit-body2": "{{PLURAL:$1|pàgina|pàgines}}",
        "nocookieslogin": "{{SITENAME}} utilitza galetes per a enregistrar usuaris. Teniu les galetes desactivades. Activeu-les i torneu a provar.",
        "nocookiesfornew": "No s'ha creat el compte d'usuari, ja que no es podia confirmar el seu origen.\nVerifiqueu que teniu habilitades les galetes al vostre navegador, torneu a carregar aquesta pàgina i intenteu-lo de nou.",
        "nocookiesforlogin": "{{int:nocookieslogin}}",
+       "createacct-loginerror": "El vostre compte ha estat creat correctament, però la vostra sessió podria no iniciar-se automàticament. Si us plau, realitzeu l'inici de [[Special:UserLogin|sessió manual]].",
        "noname": "No heu especificat un nom vàlid d'usuari.",
        "loginsuccesstitle": "Sessió iniciada",
        "loginsuccess": "Heu iniciat la sessió a {{SITENAME}} com a «$1».",
        "botpasswords-created-title": "S'ha creat la contrasenya del bot",
        "botpasswords-created-body": "S'ha creat la contrasenya per al bot «$1» de l'usuari «$2».",
        "botpasswords-updated-title": "Contrasenya de bot actualitzada",
+       "botpasswords-deleted-title": "S'ha eliminat la contrasenya del bot",
+       "botpasswords-deleted-body": "La contrasenya del bot \"$1\", pertanyent a l'usuari \"$2\", ha estat eliminada.",
+       "botpasswords-newpassword": "La nova contrasenya per a iniciar sessió amb <strong>$1</strong> és <strong>$2</strong>. Si us plau, guardeu-la de cara al futur.",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider no està disponible.",
+       "botpasswords-restriction-failed": "Les restriccions de contrasenyes de bots impedeixen aquest inici de sessió.",
        "botpasswords-not-exist": "L'usuari «$1» no té una contrasenya de bot anomenada «$2».",
        "resetpass_forbidden": "No poden canviar-se les contrasenyes",
+       "resetpass_forbidden-reason": "Les contrasenyes no es poden canviar: $1",
        "resetpass-no-info": "Heu d'estar registrats en un compte per a poder accedir directament a aquesta pàgina.",
        "resetpass-submit-loggedin": "Canvia la contrasenya",
        "resetpass-submit-cancel": "Cancel·la",
        "passwordreset-emailtext-user": "L'usuari $1 de {{SITENAME}} ha demanat una reinicialització de la vostra contrasenya per al projecte {{SITENAME}} ($4). {{PLURAL:$3|El següent compte d'usuari està associat|Els següents comptes d'usuari estan associats}} amb aquesta adreça de correu electrònic:\n\n$2\n\n{{PLURAL:$3|Aquesta contrasenya temporal caducarà|Aquestes contrasenyes temporals caducaran}} en {{PLURAL:$5|un dia|$5 dies}}.\nHauríeu d'entrar ara per fixar una nova contrasenya. Si algú que no sou vós és qui ha fet aquesta petició o si heu recordat la contrasenya original i ja no la voleu canviar, podeu ignorar aquest missatge i seguir utilitzant la vostra antiga contrasenya.",
        "passwordreset-emailelement": "Nom d'usuari: \n$1\n\nContrasenya temporal: \n$2",
        "passwordreset-emailsentemail": "Si aquesta adreça electrònica està associada al vostre compte, s’enviarà un missatge de restabliment de contrasenya.",
-       "passwordreset-emailsent-capture": "S'ha enviat un correu electrònic de reinicialització de contrasenya, tal com es mostra a continuació.",
-       "passwordreset-emailerror-capture": "S'ha generat un correu electrònic de renovació de contrasenya, que es mostra a continuació, però ha fallat l'enviament a {{GENDER:$2:l'usuari|la usuària}}: $1",
+       "passwordreset-emailsentusername": "Si existeix una adreça electrònica associada a aquest nom d'usuari, s’hi enviarà un missatge de reestabliment de contrasenya.",
        "passwordreset-emailsent-capture2": "{{PLURAL:$1|S'ha enviat el correu|S'han enviat els correus}} de restabliment de {{PLURAL:$1|contrasenya|contrasenyes}}. A continuació es mostra {{PLURAL:$1|l'usuari i contrasenya|la llista d'usuaris i contrasenyes}}.",
        "passwordreset-invalideamil": "Adreça de correu electrònic no vàlida",
+       "passwordreset-nodata": "No s'ha proporcionat cap nom d'usuari ni adreça electrònica",
        "changeemail": "Canvia o elimina l’adreça electrònica",
        "changeemail-header": "Empleneu aquest formulari per canviar la vostra adreça electrònica. Si voleu eliminar qualssevol associacions d’adreces electròniques del vostre compte, deixeu en blanc el camp i envieu el formulari.",
-       "changeemail-passwordrequired": "Cal que introduïu la vostra contrasenya per confirmar el canvi.",
        "changeemail-no-info": "Heu d'entrar en un compte d'usuari per accedir directament a aquesta pàgina.",
        "changeemail-oldemail": "Adreça electrònica actual:",
        "changeemail-newemail": "Adreça electrònica nova:",
        "minoredit": "Aquesta és una modificació menor",
        "watchthis": "Vigila aquesta pàgina",
        "savearticle": "Desa la pàgina",
+       "savechanges": "Desa els canvis",
        "publishpage": "Publica la pàgina",
+       "publishchanges": "Publica els canvis",
        "preview": "Previsualització",
        "showpreview": "Mostra una previsualització",
        "showdiff": "Mostra els canvis",
        "content-model-json": "JSON",
        "content-json-empty-object": "Objecte buit",
        "content-json-empty-array": "Matriu buida",
+       "deprecated-self-close-category": "Pàgines que usen etiquetes HTML autotancades no vàlides",
        "duplicate-args-warning": "<strong>Avís:</strong> [[:$1]] crida [[:$2]] amb més d'un valor pel paràmetre «$3». Només s'utilitzarà el darrer valor proporcionat.",
        "duplicate-args-category": "Pàgines amb arguments duplicats en utilització de plantilles",
        "duplicate-args-category-desc": "La pàgina conté crides a plantilles que fan servir duplicats d'arguments, com ara <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> o <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Sembla que ja s'ha desfet la modificació.",
        "undo-summary": "Es desfà la revisió $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|Discussió]])",
        "undo-summary-username-hidden": "Desfés la revisió $1 d'un usuari ocult",
-       "cantcreateaccounttitle": "No es pot crear el compte",
        "cantcreateaccount-text": "[[User:$3|$3]] ha bloquejat la creació de comptes des d'aquesta adreça IP ('''$1''').\n\nEl motiu donat per $3 és ''$2''",
        "cantcreateaccount-range-text": "La creació de comptes des de les adreces IP en el rang <strong>$1</strong>, que inclou la vostra adreça IP (<strong>$4</strong>), ha estat blocada per [[User:$3|$3]].\n\nEl motiu donat per $3 és <em>$2</em>",
        "viewpagelogs": "Visualitza els registres d'aquesta pàgina",
        "right-override-export-depth": "Exportar pàgines incloent aquelles enllaçades fins a una fondària de 5",
        "right-sendemail": "Enviar missatges de correu electrònic a altres usuaris",
        "right-passwordreset": "Veure les sol·licituds de restabliment de contrasenya per correu electrònic",
-       "right-managechangetags": "Crear i suprimir [[Special:Tags|etiquetes]] des de la base de dades",
+       "right-managechangetags": "Crear, activar i desactivar [[Special:Tags|etiquetes]]",
        "right-applychangetags": "Aplica les [[Special:Tags|etiquetes]] juntament amb els canvis propis",
        "right-changetags": "Afegeix i suprimeix [[Special:Tags|etiquetes]] en revisions individuals i entrades de registre",
+       "right-deletechangetags": "Suprimir [[Special:Tags|etiquetes]] des de la base de dades",
        "grant-group-page-interaction": "Interacció amb pàgines",
        "grant-group-file-interaction": "Interacció amb fitxes multimèdia",
        "grant-group-watchlist-interaction": "Interacció amb la vostra llista de seguiment",
        "grant-createeditmovepage": "Crea, modifica i reanomena pàgines",
        "grant-delete": "Suprimeix pàgines, revisions i entrades de registre",
        "grant-editinterface": "Modifica l'espai de noms MediaWiki i els CSS/JavaScript d'usuari",
+       "grant-editmyoptions": "Editeu les vostres preferències d'usuari",
        "grant-editmywatchlist": "Modifica la llista de seguiment",
        "grant-editpage": "Modifica les pàgines existents",
        "grant-editprotected": "Modifica pàgines protegides",
        "grant-highvolume": "Edició d'alt volum",
        "grant-oversight": "Amaga usuaris i suprimeix revisions",
+       "grant-patrol": "Patrulla els canvis de les pàgines",
        "grant-protect": "Protecció i desprotecció de pàgines",
        "grant-rollback": "Reversió de canvis en pàgines",
        "grant-sendemail": "Enviament de correus a altres usuaris",
        "rightslogtext": "Aquest és un registre de canvis dels permisos d'usuari.",
        "action-read": "llegir aquesta pàgina",
        "action-edit": "modificar aquesta pàgina",
-       "action-createpage": "crear pàgines",
-       "action-createtalk": "crear pàgines de discussió",
+       "action-createpage": "crear aquesta pàgina",
+       "action-createtalk": "crear aquesta pàgina de discussió",
        "action-createaccount": "crear aquest compte d'usuari",
        "action-autocreateaccount": "crea automàtica aquest compte d'usuari extern",
        "action-history": "mostra l'historial de la pàgina",
        "action-managechangetags": "crear i (des)activar etiquetes",
        "action-applychangetags": "aplica les etiquetes juntament amb els canvis",
        "action-changetags": "afegeix i elimina etiquetes a les revisions i les entrades de registre individuals",
+       "action-deletechangetags": "eliminar etiquetes des de la base de dades",
+       "action-purge": "purga la pàgina",
        "nchanges": "$1 {{PLURAL:$1|canvi|canvis}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|des de la darrera visita}}",
        "enhancedrc-history": "historial",
        "upload-too-many-redirects": "L'URL conté massa redireccions",
        "upload-http-error": "Ha ocorregut un error HTTP: $1",
        "upload-copy-upload-invalid-domain": "Les càrregues de còpia no són disponibles des d'aquest domini.",
+       "upload-dialog-disabled": "La càrrega de fitxers utilitzant aquest quadre de diàleg està desactivada en aquest wiki.",
        "upload-dialog-title": "Carrega un fitxer",
        "upload-dialog-button-cancel": "Cancel·la",
        "upload-dialog-button-done": "Fet",
        "upload-form-label-infoform-name": "Nom",
        "upload-form-label-infoform-name-tooltip": "Un únic títol descriptiu pel fitxer, que servirà com a nom del fitxer. Podeu utilitzar llenguatge senzill amb espais. No hi inclogueu l'extensió del fitxer.",
        "upload-form-label-infoform-description": "Descripció",
+       "upload-form-label-infoform-description-tooltip": "Descriviu breument tot el que sigui notable d'aquesta obra.\nSi es tracta d'una fotografia, esmenteu els principals elements que hi ha representats o bé l'esdeveniment o lloc en qüestió.",
        "upload-form-label-usage-title": "Ús",
        "upload-form-label-usage-filename": "Nom del fitxer",
        "upload-form-label-own-work": "Això és el meu propi treball",
        "upload-form-label-infoform-categories": "Categories",
        "upload-form-label-infoform-date": "Data",
+       "upload-form-label-own-work-message-generic-local": "Confirmo que estic carregant aquest fitxer seguint les condicions d'ús i polítiques de concessió de llicències a {{SITENAME}}.",
+       "upload-form-label-not-own-work-message-generic-local": "Si no podeu carregar aquest fitxer sota les polítiques de {{SITENAME}}, si us plau, tanqueu aquest quadre de diàleg i proveu amb un altre mètode.",
        "upload-form-label-not-own-work-local-generic-local": "També podeu provar [[Special:Upload|la pàgina de càrrega per defecte]].",
        "upload-form-label-own-work-message-generic-foreign": "Entenc que esteu carregant el fitxer en un repositori compartit. Confirmo que ho estic fent seguint les condicions d'ús i les polítiques de llicenciament que s'hi apliquen.",
+       "upload-form-label-not-own-work-message-generic-foreign": "Si no podeu carregar aquest fitxer sota les polítiques del repositori lliure, si us plau, tanqueu aquest quadre de diàleg i proveu amb un altre mètode.",
+       "upload-form-label-not-own-work-local-generic-foreign": "També us recomanem que utilitzeu [[Special:Upload|la pàgina de càrrega a {{SITENAME}}]] si aquest fitxer es pot carregar seguint les seves polítiques.",
        "backend-fail-stream": "No s'ha pogut transmetre el fitxer $1.",
        "backend-fail-backup": "No s'ha pogut fer una còpia de seguretat del fitxer $1.",
        "backend-fail-notexists": "El fitxer $1 no existeix.",
        "uploadstash-summary": "Aquesta pàgina permet accedir als fitxers que han estat carregats (o estan en procés de ser carregats), però que encara no s'han publicat al wiki. Aquests fitxers només són visibles per a l'usuari que els ha carregats.",
        "uploadstash-clear": "Esborra els fitxers en reserva",
        "uploadstash-nofiles": "No teniu fitxers en reserva",
-       "uploadstash-badtoken": "No s'ha pogut realitzar l'acció, possiblement perquè han caducat la vostra identificació. Intenteu-ho de nou.",
+       "uploadstash-badtoken": "No s'ha pogut realitzar l'acció, possiblement perquè ha caducat la vostra identificació. Intenteu-ho de nou.",
        "uploadstash-errclear": "S'estan netejant els fitxers que han fallat.",
        "uploadstash-refresh": "Actualitza la llista de fitxers",
        "uploadstash-thumbnail": "mostra una miniatura",
        "trackingcategories-msg": "Categoria de seguiment",
        "trackingcategories-name": "Nom del missatge",
        "trackingcategories-desc": "Criteris d'inclusió de categoria",
+       "restricted-displaytitle-ignored": "Pàgines amb títols a mostrar ignorats",
        "noindex-category-desc": "La pàgina conté una paraula màgica <code><nowiki>__NOINDEX__</nowiki></code> (i és en un espai de noms on està permesa) i per tant no està indexada per robots.",
        "index-category-desc": "La pàgina conté un <code><nowiki>__INDEX__</nowiki></code> (i és en un espai de noms on està permès) i per tant està indexat per robots quan normalment no ho seria.",
        "post-expand-template-inclusion-category-desc": "La mida de la pàgina és més gran que <code>$wgMaxArticleSize</code> un cop expandides totes les plantilles, per tant algunes plantilles no s'han expandit.",
        "rollbacklinkcount": "reverteix $1 {{PLURAL:$1|edició|edicions}}",
        "rollbacklinkcount-morethan": "reverteix més de $1 {{PLURAL:$1|edició|edicions}}",
        "rollbackfailed": "No s'ha pogut revocar",
+       "rollback-missingrevision": "No es poden carregar les dades de revisió.",
        "cantrollback": "No s'han pogut revertir les edicions; el darrer col·laborador és l'únic autor de la pàgina.",
        "alreadyrolled": "No es pot revertir la darrera modificació de [[:$1]]\nde l'usuari [[User:$2|$2]] ([[User talk:$2|Discussió]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]). Algú altre ja ha modificat o revertit la pàgina.\n\nLa darrera modificació l'ha fet l'usuari [[User:$3|$3]] ([[User talk:$3|Discussió]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "El resum d'edició és: <em>$1</em>.",
        "changecontentmodel-success-text": "S'ha canviat el tipus de contingut de [[:$1]].",
        "changecontentmodel-cannot-convert": "El contingut a [[:$1]] no es pot convertir a un tipus de $2.",
        "changecontentmodel-nodirectediting": "El model de contingut $1 no permet l'edició directa",
+       "changecontentmodel-emptymodels-title": "No hi ha models de contingut",
+       "changecontentmodel-emptymodels-text": "El contingut a [[:$1]] no pot convertir-se a cap tipus.",
        "log-name-contentmodel": "Registre de canvis del model de contingut",
        "log-description-contentmodel": "Esdeveniments relacionats amb els models de contingut d'una pàgina",
        "logentry-contentmodel-change": "$1 {{GENDER:$2|ha canviat}} el model de contingut de la pàgina $3 de «$4» a «$5»",
        "sp-contributions-username": "Adreça IP o nom d'usuari:",
        "sp-contributions-toponly": "Mostra només les darreres revisions",
        "sp-contributions-newonly": "Mostra només modificacions que són creacions de pàgina",
+       "sp-contributions-hideminor": "Amaga les edicions menors",
        "sp-contributions-submit": "Cerca",
        "whatlinkshere": "Què hi enllaça",
        "whatlinkshere-title": "Pàgines que enllacen amb «$1»",
        "lockdbsuccesstext": "S'ha bloquejat la base de dades.<br />\nRecordeu-vos de [[Special:UnlockDB|treure el bloqueig]] quan hàgiu acabat el manteniment.",
        "unlockdbsuccesstext": "S'ha desbloquejat la base de dades del projecte {{SITENAME}}.",
        "lockfilenotwritable": "No es pot modificar el fitxer de la base de dades de bloquejos. Per a blocar o desblocar la base de dades, heu de donar-ne permís de modificació al servidor web.",
+       "databaselocked": "La bases de dades ja està bloquejada.",
        "databasenotlocked": "La base de dades no està bloquejada.",
        "lockedbyandtime": "(per $1 el $2 a les $3)",
        "move-page": "Reanomena $1",
        "tooltip-ca-nstab-category": "Vegeu la pàgina de la categoria",
        "tooltip-minoredit": "Marca-ho com una modificació menor",
        "tooltip-save": "Deseu els canvis",
+       "tooltip-publish": "Publica els canvis",
        "tooltip-preview": "Reviseu els vostres canvis, feu-ho abans de desar res!",
        "tooltip-diff": "Mostra quins canvis heu fet al text",
        "tooltip-compareselectedversions": "Vegeu les diferències entre les dues versions seleccionades d'aquesta pàgina.",
        "pageinfo-category-files": "Nombre d'arxius",
        "markaspatrolleddiff": "Marca com a supervisat",
        "markaspatrolledtext": "Marca la pàgina com a supervisada",
+       "markaspatrolledtext-file": "Marc la versió del fitxer com patrullada",
        "markedaspatrolled": "Marca com a supervisat",
        "markedaspatrolledtext": "S'ha marcat com a patrullada la revisió seleccionada de [[:$1]].",
        "rcpatroldisabled": "S'ha inhabilitat la supervisió dels canvis recents",
        "confirm-watch-top": "Voleu afegir aquesta pàgina a la llista de seguiment?",
        "confirm-unwatch-button": "D'acord",
        "confirm-unwatch-top": "Voleu treure aquesta pàgina de la llista de seguiment?",
+       "confirm-rollback-button": "D'acord",
+       "confirm-rollback-top": "Voleu revertir les modificacions a la pàgina?",
        "quotation-marks": "«$1»",
        "imgmultipageprev": "← pàgina anterior",
        "imgmultipagenext": "pàgina següent →",
        "redirect-page": "ID de pàgina",
        "redirect-revision": "Revisió de la pàgina",
        "redirect-file": "Nom del fitxer",
+       "redirect-logid": "ID de registre",
        "redirect-not-exists": "No s'ha trobat el valor",
        "fileduplicatesearch": "Cerca fitxers duplicats",
        "fileduplicatesearch-summary": "Cerca fitxers duplicats d'acord amb el seu valor de resum.",
        "tags-edit-title": "Modifica les etiquetes",
        "tags-edit-manage-link": "Gestiona les etiquetes",
        "tags-edit-revision-selected": "{{PLURAL:$1|Revisió seleccionada|Revisions seleccionades}} de [[:$2]]:",
+       "tags-edit-revision-legend": "Afegeix o suprimeix etiquetes {{PLURAL:$1|d'aquesta revisió|de totes les $1 revisions}}",
        "tags-edit-logentry-legend": "Afegeix o suprimeix etiquetes {{PLURAL:$1|d'aquesta entrada del registre|de totes les entrades del registre}}",
        "tags-edit-existing-tags": "Etiquetes existents:",
        "tags-edit-existing-tags-none": "<em>Cap</em>",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
        "mw-widgets-titleinput-description-new-page": "la pàgina no existeix encara",
        "mw-widgets-titleinput-description-redirect": "redirigeix a $1",
-       "api-error-blacklisted": "Trieu un títol diferent, més descriptiu.",
        "sessionmanager-tie": "No es poden combinar diferents tipus de sol·licituds d'autenticació: $1.",
        "sessionprovider-generic": "$1 sessions",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sessions basades en galetes",
        "randomrootpage": "Pàgina arrel aleatòria",
        "log-action-filter-block": "Tipus de blocatge:",
        "log-action-filter-delete": "Tipus de supressió:",
+       "log-action-filter-import": "Tipus d'importació:",
+       "log-action-filter-managetags": "Tipus d'acció de gestió d'etiquetes:",
+       "log-action-filter-move": "Tipus de reanomenament:",
+       "log-action-filter-newusers": "Tipus de creació de compte:",
        "log-action-filter-patrol": "Tipus de patrullatge:",
        "log-action-filter-protect": "Tipus de protecció:",
+       "log-action-filter-rights": "Tipus de canvi de permisos:",
+       "log-action-filter-suppress": "Tipus de supressió:",
        "log-action-filter-upload": "Tipus de càrrega:",
        "log-action-filter-all": "Tota",
        "log-action-filter-block-block": "Bloca",
        "log-action-filter-delete-restore": "Restauració de pàgines",
        "log-action-filter-delete-event": "Registre de supressió",
        "log-action-filter-delete-revision": "Supressió de revisions",
+       "log-action-filter-managetags-create": "Creació de l'etiqueta",
+       "log-action-filter-managetags-delete": "Supressió de l'etiqueta",
+       "log-action-filter-managetags-activate": "Activació de l'etiqueta",
+       "log-action-filter-newusers-autocreate": "Creació automàtica",
        "log-action-filter-patrol-patrol": "Patrullatge manual",
        "log-action-filter-patrol-autopatrol": "Patrullatge automàtic",
        "log-action-filter-protect-protect": "Protecció",
        "authmanager-authn-autocreate-failed": "Ha fallat la creació automàtica d'un compte local: $1",
        "authmanager-realname-label": "Nom real",
        "authmanager-realname-help": "Nom real de l'usuari",
+       "authmanager-provider-temporarypassword": "Contrasenya temporal",
        "authprovider-resetpass-skip-label": "Omet",
        "authpage-cannot-login-continue": "No es pot continuar amb l'inicio de sessió. Probablement la vostra sessió ha expirat.",
        "authpage-cannot-create-continue": "No es pot prosseguir la creació del compte. Probablement la vostra sessió ha expirat.",
        "changecredentials": "Canvi de dades credencials",
        "changecredentials-submit": "Canvia les dades credencials",
        "credentialsform-provider": "Tipus de dades credencials:",
-       "credentialsform-account": "Nom del compte:"
+       "credentialsform-account": "Nom del compte:",
+       "linkaccounts-submit": "Enllaça els comptes",
+       "unlinkaccounts": "Desenllaça els comptes",
+       "unlinkaccounts-success": "El compte s'ha desenllaçat.",
+       "authenticationdatachange-ignored": "No s'ha gestionat el canvi de dades d'autenticació. Potser no s'ha configurat cap proveïdor?"
 }
index 0655819..088c746 100644 (file)
        "yourpasswordagain": "Юха язъе пароль:",
        "createacct-yourpasswordagain": "Бакъе пароль",
        "createacct-yourpasswordagain-ph": "Кхин цкъа язъе пароль",
-       "remembermypassword": "Даглаца сан дӀаяздар хӀокху компьютеран тӀехь (цхьан $1 {{PLURAL:$1|дийнахь}})",
        "userlogin-remembermypassword": "Системин чохь Ӏойла",
        "userlogin-signwithsecure": "Ларийна цхьаьнакхетар",
        "cannotloginnow-title": "ХӀинца чудаха таро яц",
        "password-change-forbidden": "Хьан йиш яц хӀокху вики чохь пароль хийца.",
        "externaldberror": "Арахьара хаамийн базан гӀоьнца аутентификаци ечу хенахь гӀалат даьлла я хьа дӀаяздаран хийцам бан бакъонаш яц.",
        "login": "Системин довзийтар",
+       "login-security": "Хьой хилар бакъде",
        "nav-login-createaccount": "Довзийтар / дӀаяздар кхоллар",
        "userlogin": "Довзийтар я декъашхочун дӀаяздар кхоллар",
        "userloginnocreate": "Довзийта",
        "userlogin-resetpassword-link": "Пароль кхоссар?",
        "userlogin-helplink2": "Системин чудахаран гӀодар",
        "userlogin-loggedin": "Хьо {{GENDER:$1|$1}} цӀарца чохь ву/ю.\nЛахара форманца кхин цӀарца чугӀо.",
+       "userlogin-reauth": "{{GENDER:$1|$1}} хьой хилар бакъдан системин чугӀо.",
        "userlogin-createanother": "Кхолла декъашхочун кхин дӀаяздар",
        "createacct-emailrequired": "Электронан поштан адрес",
        "createacct-emailoptional": "Электронан поштан адрес (ца яздича мега)",
        "resetpass-wrong-oldpass": "Нийса йоцу я хана йолу карара пароль. Ахьа кхиамца пароль хийцина я керла хана йолу пароль ехна хила там бу.",
        "resetpass-recycled": "Дехар до, хӀинца йолччул башха пароль хӀотта йе.",
        "resetpass-temp-emailed": "Ахьа чугӀош язйина цкъачунна электронан поште яийтина пароль. Чудахар чекхдалийта язъян еза керла пароль.",
-       "resetpass-temp-password": "ЦÑ\85Ñ\8cан Ñ\85ана пароль:",
+       "resetpass-temp-password": "Ð¥ана пароль:",
        "resetpass-abort-generic": "Пароль хийцар дӀахедар",
        "resetpass-expired": "Хьан паролан хан чекхелла. Дехар до керла пароль хӀоттаяр.",
        "resetpass-expired-soft": "Хьа паролан хан чекхелла. Дехар до, харжа керла пароль я тӀетаӀе «{{int:authprovider-resetpass-skip-label}}», и тӀехо хица.",
        "passwordreset-emailtitle": "{{SITENAME}}: декъашхочун дӀаяздарх лаьцна хаам",
        "passwordreset-emailtext-ip": "{{SITENAME}} ($4) проектехь цхьам я ахьа хӀокху IP-адрес $1 тӀера хьа декъашхочун пароль кхоссар дехна,\nоьцу электронан адресца дихкина ду {{PLURAL:$3|1хӀара декъашхочун дӀаяздар|хӀара декъашхочун дӀаяздар}}:\n\n$2\n\n{{PLURAL:$3|ХӀара хана пароль|ХӀара хана паролаш}} лелар ю {{PLURAL:$5|$5 дийнахь}}.\nСистемин чугӀой харжа керла пароль. \nХьой пароль кхоссар дехна дацахь я хьалхалера пароль дага еънехь хӀума цадеш Ӏад битта хӀара хаам хьа йиш ю шира пароль лелаян.",
        "passwordreset-emailtext-user": "{{SITENAME}} ($4) проектера декъашхочо $1 хьа декъашхочун пароль кхоссар дехна,\nоьцу электронан адресца дихкина ду {{PLURAL:$3|1хӀара декъашхочун дӀаяздар|хӀара декъашхочун дӀаяздар}}:\n\n$2\n\n{{PLURAL:$3|ХӀара хана пароль|ХӀара хана паролаш}} лелар ю {{PLURAL:$5|$5 дийнахь}}.\nСистемин чугӀой харжа керла пароль. \nХьой пароль кхоссар дехна дацахь я хьалхалера пароль дага еънехь хӀума цадеш Ӏад битта хӀара хаам хьа йиш ю шира пароль лелаян.",
-       "passwordreset-emailelement": "Ð\94екÑ\8aаÑ\88Ñ\85оÑ\87Ñ\83н Ñ\86Ó\80е: \n$1\n\nХанна Ð¹Ð¾Ð»Ñ\83 Ð¿Ð°Ñ\80олÑ\8c: \n$2",
+       "passwordreset-emailelement": "Декъашхочун цӀе: \n$1\n\nХанна пароль: \n$2",
        "passwordreset-emailsentemail": "Электронан хаам баийтина кхоьссинчу паролах лаьцна хаам чохь болуш.",
-       "passwordreset-emailsent-capture": "Электронан хаам баийтина кхоьссинчу паролах лаьцна хаам чохь болуш. \nцуна йозане хьажа йиш ю лахахь.",
-       "passwordreset-emailerror-capture": "Пароль кхоссаран хаам чохь болуш электронан кехат кхоьллина, цуна йоза хьажа йиш ю лахахь, амма иза {{GENDER:$2|декъашхочунга}} дӀадахьийта тар цаделира бахьнехь: $1",
        "changeemail": "Хийца электронан пошт",
        "changeemail-header": "Электронан поштан адрес хийцар",
        "changeemail-no-info": "ХӀара агӀо лело системин чугӀо.",
        "minoredit": "Жима хийцам",
        "watchthis": "ХӀара агӀо тергаме могӀанан юкъатоха",
        "savearticle": "АгӀо дӀаязъян",
+       "savechanges": "Ӏалашбе хийцамаш",
+       "publishpage": "АгӀо кхолла",
        "preview": "Хьалххе хьажар",
        "showpreview": "Хьалха хьажар",
        "showdiff": "Бина болу хийцамашка хьажар",
        "undo-nochange": "Нисдар хьалхо юхадяьккхиначух тера ду.",
        "undo-summary": "Юхадаьккхина {{GENDER:$2|декъашхочун}} [[Special:Contributions/$2|$2]] ([[User talk:$2|дийц.]]) нисдар $1",
        "undo-summary-username-hidden": "Юхадаьккхина декъашхочун нисдарш $1, цунна цӀе дӀахьулйина",
-       "cantcreateaccounttitle": "Декъашхочун дӀаяздар кхолла йиш яц",
        "viewpagelogs": "Гайта хӀокху агӀонан тептар",
        "nohistory": "ХӀокху агӀонан хийцамаш ца бина.",
        "currentrev": "Карара верси",
        "page_last": "тlаьххьара",
        "histlegend": "Кхетор: (хӀинцалера.) — йолучу башхон къастам; (хьалх.) — хьалхалерчу башхон къастам; '''ж''' — жимо бозалца болу хийцам.",
        "history-fieldset-title": "АгӀона хийцамаш",
-       "history-show-deleted": "Ð\94Ó\80аÑ\8fÑ\85инарш",
+       "history-show-deleted": "Ð\94Ó\80аÑ\8fÑ\8cÑ\85нарш",
        "histfirst": "ширниш",
        "histlast": "хьалхарниш",
        "historysize": "($1 {{PLURAL:$1|байт}})",
        "searchprofile-articles-tooltip": "$1 чохь лахар",
        "searchprofile-images-tooltip": "Файлаш лахар",
        "searchprofile-everything-tooltip": "Массо агӀонашкахь лахар (дийцаре агӀонашца)",
-       "searchprofile-advanced-tooltip": "Ð\94еÑ\85аÑ\80Ñ\86а Ð¹Ð¾Ð»Ñ\83 Ñ\86Ó\80еÑ\80ийн Ð°Ð½ашкахь лахар",
+       "searchprofile-advanced-tooltip": "Ð\94еÑ\85аÑ\80Ñ\86а Ð¹Ð¾Ð»Ñ\83 Ñ\86Ó\80еÑ\80ийн Ð¼ÐµÑ\82Ñ\82игашкахь лахар",
        "search-result-size": "$1 ({{PLURAL:$2|$2 дош|$2 дешнаш}})",
        "search-result-category-size": "$1 {{PLURAL:$1|юкъаяр}} ($2 {{PLURAL:$2|1=бухара категори|бухара категореш}}, $3 {{PLURAL:$3|1=файл|файлаш}}).",
        "search-redirect": "(дӀасахьажийна $1)",
        "search-showingresults": "{{PLURAL:$4|Карийна <strong>$1</strong> — цхьаъ агӀо|И дош карийна <strong>$3</strong> агӀонашкахь, царех гойту $2 агӀо}}",
        "search-nonefound": "Дехаре терра цхьа хӀума ца карийна.",
        "powersearch-legend": "Шуьйра лахар",
-       "powersearch-ns": "ЦÓ\80еÑ\80ийн Ð°Ð½ашкахь лахар:",
+       "powersearch-ns": "ЦÓ\80еÑ\80ийн Ð¼ÐµÑ\82Ñ\82игашкахь лахар:",
        "powersearch-togglelabel": "Билгалдан:",
        "powersearch-toggleall": "Массо",
        "powersearch-togglenone": "ХӀумма цаоьшу",
        "mostlinkedtemplates": "Массарел дуккха а лелайо агӀонаш",
        "mostcategories": "Дуккха категореш тӀе тоьхна йолу агӀонаш",
        "mostimages": "Массарел дуккха лелайо файлаш",
-       "mostinterwikis": "Ð\94Ñ\83ккÑ\85а Ñ\8eкÑ\8aаÑ\80вики хьажоргаш тӀе тоьхна йолу агӀонаш",
+       "mostinterwikis": "Ð\94Ñ\83ккÑ\85а Ñ\8eкÑ\8aаÑ\80викаÑ\88-хьажоргаш тӀе тоьхна йолу агӀонаш",
        "mostrevisions": "Сих сиха нисйина йолу агӀонаш",
        "prefixindex": "Хьалха агӀонийн цӀерш хӀотто еза",
        "prefixindex-namespace": "Хьалха агӀонийн цӀерш хӀотто еза («{{ns:$1}}»)",
        "activeusers-hidebots": "Къайлабаха боташ",
        "activeusers-hidesysops": "Къайлабаха куьйгалхой",
        "activeusers-noresult": "Декъашхой цакарий.",
+       "activeusers-submit": "Гайта жигара декъашхой",
        "listgrouprights": "Декъашхойн тобанийн бакъонаш",
        "listgrouprights-summary": "Лахахьа гойту декъашхошна яла йиш йолу бакъонаш. [[{{MediaWiki:Listgrouprights-helppage}}|хьажа кхин хааме]].",
        "listgrouprights-key": "Легенда:\n* <span class=\"listgrouprights-granted\">Ела бакъонаш</span>\n* <span class=\"listgrouprights-revoked\">ДӀаяьхна бакъонаш</span>",
        "tooltip-ca-nstab-category": "Категорешан агӀо",
        "tooltip-minoredit": "Къастам бé хӀокху хийцамна кӀеззиг болуш санна",
        "tooltip-save": "Хьан хийцамаш Ӏалашбой",
+       "tooltip-publish": "Гучубаха хьой бина хийцамаш",
        "tooltip-preview": "Дехар до, агӀо Ӏалаш ярал хьалха хьажа муха ю из!",
        "tooltip-diff": "Гайта долуш долу йозанах бина болу хийцам.",
        "tooltip-compareselectedversions": "ХӀокху агӀона шина хаьржина версийн башхалле хьажар.",
        "confirmrecreate-noreason": "Декъашхочо [[User:$1|$1]] ([[User talk:$1|дийцаре]]) хӀара агӀо дӀаяьккхина, ахьа иза тая йолийча. Дехар до, тешал де, хьо иза агӀо меттахӀотто лууш ву/ю але.",
        "recreate": "Юха кхолла",
        "confirm_purge_button": "ХӀаъ",
+       "confirm-purge-top": "ХӀокху агӀона кэш дӀацӀанъян?",
+       "confirm-purge-bottom": "Кэш дӀацӀанйиначул тӀехьа цуна тӀеххьара верси гойтур ю.",
        "confirm-watch-button": "ХӀаъ",
        "confirm-watch-top": "ТӀетоха хӀара агӀо хьан тергаме могӀам юкъа?",
        "confirm-unwatch-button": "ХӀаъ",
        "special-characters-title-minus": "хьаьрк минус",
        "mw-widgets-titleinput-description-redirect": "ДӀасхьажорг $1 тӀе",
        "sessionprovider-generic": "$1 сесси",
-       "randomrootpage": "Цахууш нисъелла ораман агӀо"
+       "randomrootpage": "Цахууш нисъелла ораман агӀо",
+       "authmanager-provider-temporarypassword": "Ханна пароль",
+       "changecredentials": "Декъашхочун дӀаяздаран хийцам",
+       "removecredentials": "ДӀадаха декъашхойн дӀаяздарш",
+       "removecredentials-submit": "ДӀадаха декъашхойн дӀаяздарш",
+       "credentialsform-provider": "ДӀаяздарийн тайпа:",
+       "credentialsform-account": "Декъашхочун цӀе:"
 }
index c8efd12..c6b47a7 100644 (file)
        "about": "سەبارەت",
        "article": "بابەت",
        "newwindow": "(لە پەڕەیەکی نوێدا دەکرێتەوە)",
-       "cancel": "ھەڵیوەشێنەوە",
+       "cancel": "ھەڵوەشاندنەوە",
        "moredotdotdot": "زیاتر",
        "morenotlisted": "ئەم لیستەیە تەواو نییە",
        "mypage": "پەڕە",
        "mainpage": "دەستپێک",
        "mainpage-description": "دەستپێک",
        "policy-url": "Project: سیاسەت",
-       "portal": "دەروازەی کۆمەڵگا",
+       "portal": "دەروازەی کۆمەڵگە",
        "portal-url": "Project:دەروازەی کۆمەڵگا",
        "privacy": "سیاسەتی تایبەتێتی",
        "privacypage": "Project:پاراستنی زانیارییەکان",
        "youhavenewmessagesmulti": "لە $1 دا پەیامی نوێت ھەیە",
        "editsection": "دەستکاری",
        "editold": "دەستکاری",
-       "viewsourceold": "سÛ\95رÚ\86اÙ\88Û\95Ú©Û\95Û\8c Ø¨Ø¨Û\8cÙ\86ە",
+       "viewsourceold": "بÛ\8cÙ\86Û\8cÙ\86Û\8c Ø³Û\95رÚ\86اÙ\88ە",
        "editlink": "دەستکاری",
-       "viewsourcelink": "سÛ\95رÚ\86اÙ\88Û\95Ú©Û\95Û\8c Ø¨Ø¨Û\8cÙ\86ە",
+       "viewsourcelink": "بÛ\8cÙ\86Û\8cÙ\86Û\8c Ø³Û\95رÚ\86اÙ\88ە",
        "editsectionhint": "دەستکاریکردنی بەش: $1",
        "toc": "پێرست",
        "showtoc": "نیشانیبدە",
        "nstab-user": "پەڕەی بەکارھێنەر",
        "nstab-media": "میدیا",
        "nstab-special": "پەڕەی تایبەت",
-       "nstab-project": "پەڕەی پرۆژە",
+       "nstab-project": "پەڕەی پڕۆژە",
        "nstab-image": "پەڕگە",
        "nstab-mediawiki": "پەیام",
        "nstab-template": "داڕێژە",
        "perfcached": "داتای خوارەوە پاشەکەوتکراوەیە و لەوانەیە بەڕۆژنەکرابێتەوە. لانی زۆر {{PLURAL:$1|یەک ئەنجام|$1 ئەنجام}} لە cacheدا لەبەردەستدایە.",
        "perfcachedts": "داتای خوارەوە cacheکراوە و دوایین جار لە $1 نوێ کراوەتەوە. لە cacheدا لانی زۆر {{PLURAL:$4|یەک ئەنجام|$4 ئەنجام}} لەبەردەستە.",
        "querypage-no-updates": "تازەکردنەوەکان بۆ ئەم پەڕە لە حاڵی ئێستادا ناچالاک کراوەتەوە.\nداتای ئێرە دەسبەجێ تازە ناکرێتەوە.",
-       "viewsource": "سÛ\95رÚ\86اÙ\88Û\95Ú©Û\95Û\8c Ø¨Ø¨Û\8cÙ\86ە",
+       "viewsource": "بÛ\8cÙ\86Û\8cÙ\86Û\8c Ø³Û\95رÚ\86اÙ\88ە",
        "viewsource-title": "سەرچاوەی $1 ببینە",
        "actionthrottled": "چالاکی پێشی پێ گیرا",
        "actionthrottledtext": "بە مەبەستی پێشگریی لە سپەم، ڕێگە نادرێت تۆ لە ماوەیەکی کورت دا لە سەر یەک ئەمە زۆر جار ئەنجام بدەی، وە ئیستا تۆ لە ڕادە بەدەرت کردووە.\nتکایە پاش چەند خولەک دووبارە تاقی بکەوە.",
        "yourpasswordagain": "دیسان تێپەڕوشەکە بنووسەوە:",
        "createacct-yourpasswordagain": "تێپەروشە پشتڕاست بکەرەوە",
        "createacct-yourpasswordagain-ph": "تێپەروشە دیسان بنووسەوە",
-       "remembermypassword": "چوونە ژوورەوەم لەسەر ئەم کۆمپیوتەرە پاشەکەوت بکە (ئەو پەڕی $1 {{PLURAL:$1|ڕۆژ}}ە)",
        "userlogin-remembermypassword": "چوونەژوورەوەکەم ڕابگرە",
        "userlogin-signwithsecure": "پەیوەندیی دڵنیا بەکاربھێنە",
        "yourdomainname": "دۆمەینەکەت:",
        "passwordreset-emailtext-user": "‫بەکارھێنەر $1 لە {{SITENAME}} ڕیسێتکردنەوەی تێپەڕوشەکەت لە {{SITENAME}}دا ($4) کردووە. {{PLURAL:$3|ھەژماری بەکارھێنەریی ژێرەوە پەیوەندیی ھەیە|ھەژمارە بەکارھێنەرییەکانی ژێرەوە پەیوەندییان ھەیە}} بەم ناونیشانەی ئیمەیلەوە:\n\n$2\n\n{{PLURAL:$3|ئەم تێپەڕوشە کاتییە|ئەم تێپەڕوشە کاتییانە}} لە {{PLURAL:$5|ڕۆژێک|$5 ڕۆژ}}دا بەسەردەچێت.\nدەبێ بچیتە ژوورەوە و ھەر ئێستا تێپەڕوشەیەکی نوێ ھەڵبژێریت. ئەگەر کەسێکی تر ئەم داواکارییەی کردووە، یان ئەگەر تێپەڕوشە سەرەتاییەکەت ھاتووەتەوە بیرت و ئیتر ناتەوێ بیگۆڕی، \nدەتوانی گوێ بەم پەیامە نەدەیت و ھەر لە تێپەڕوشە کۆنەکەت کەڵک وەربگریت.",
        "passwordreset-emailelement": "ناوی بەکارھێنەری: \n$1\n\nتێپەڕوشەی کاتی: \n$2",
        "passwordreset-emailsentemail": "ئیمەیلێکی ڕیسێتکردنەوەی تێپەڕوشە نێردرا.",
-       "passwordreset-emailsent-capture": "ئیمەیلێکی ڕیسێتکردنەوەی تێپەڕوشە نێردرا، کە لە ژێرەوە نیشان دراوە.",
-       "passwordreset-emailerror-capture": "ئیمەیلێکی ڕیسێتکردنەوەی تێپەڕوشە نێردرا، کە لە ژێرەوە نیشان دراوە، بەڵام ناردنەکەی بۆ {{GENDER:$2|بەکارھێنەر}} سەرکەوتوو نەبوو: $1",
        "changeemail": "گۆڕینی ناونیشانی ئیمەیل",
        "changeemail-header": "ناونیشانی ئیمەیلی ھەژمار بگۆڕە",
        "changeemail-no-info": "بۆ گەیشتنی راستەوخۆ بەم پەڕە دەبێت بچیتە ژوورەوە.",
        "image_tip": "وێنەی نێو دەق",
        "media_sample": "نموونە.ogg",
        "media_tip": "لینکی پەڕگە",
-       "sig_tip": "ئیمزاکەت بە مۆری ڕێکەوتەوە",
+       "sig_tip": "واژووەکەت بە مۆری ڕێکەوتەوە",
        "hr_tip": "هێڵی ئاسۆیی (دەگمەن بەکاری بێنە)",
        "summary": "کورتەی دەستکاری:",
        "subject": "بابەت/سەردێڕ:",
        "minoredit": "ئەمە دەستکارییەکی بچووکە",
        "watchthis": "ئەم پەڕەیە بخە ژێر چاودێری",
        "savearticle": "پەڕەکە پاشەکەوت بکە",
+       "savechanges": "پاشەکەوتکردنی گۆڕانکارییەکان",
        "preview": "پێشبینین",
        "showpreview": "پێشبینین نیشان بدە",
        "showdiff": "گۆڕانکارییەکان نیشان بدە",
        "undo-failure": "لەبەر کێشەی دەست‌تێ‌وەردان، ناتوانی دەستکاریەکە ئەنجام‌نەدراو بکەیت.",
        "undo-norev": "ناتوانی دەستکاریەکە ئەنجام‌نەدراو بکەی لەبەر ئەوەی بوونی نیە یا سڕدراوەتەوە.",
        "undo-summary": "گەڕاندنەوەی پێداچوونەوەی $1 لە لایەن [[Special:Contributions/$2|$2]] ([[User talk:$2|لێدوان]])",
-       "cantcreateaccounttitle": "ناتوانرێت هەژمار دروست بکرێت",
        "cantcreateaccount-text": "درووست‌کردنی هەژمارە بۆ ناونیشانی ئای‌پی ('''$1''') لە لایەن [[User:$3|$3]] داخراوە.<br /><br />\n$3 هۆکاری \"$2\" خستوەتەڕوو",
        "viewpagelogs": "لۆگەکانی ئەم پەڕەیە ببینە",
        "nohistory": "هیچ مێژوویەکی دەستکاری نییە بۆ ئەم پەڕەیە.",
        "upload-misc-error-text": "هەڵەیەکی نەناسراو لە کاتی بارکردن ڕووی‌دا.\nتکایە لە درووست‌بوون و دەست‌پێ گەیشتنی URL ئەرخەیان ببە و دیسان تاقی‌بکەوە.\nگەر کێشەکە هەر بەردەوام بوو پەیوەندی بکە بە [[Special:ListUsers/sysop|بەڕێوبەر]].",
        "upload-too-many-redirects": "URL ڕەوانەکەری زۆری لەخۆ گرتووە",
        "upload-http-error": "هەڵەیەکی HTTP ڕووئ داوە: $1",
+       "upload-dialog-button-cancel": "ھەڵوەشاندنەوە",
        "upload-dialog-button-upload": "بارکردن",
+       "upload-form-label-own-work": "ئەمە کاری خۆمە",
        "backend-fail-stream": "نەکرا پەڕگەی $1 بنێردرێت.",
        "backend-fail-notexists": "پەڕگەی $1 بوونی نییە.",
        "backend-fail-delete": "نەکرا پەڕگەی $1 بسڕدرێتەوە.",
        "license-nopreview": "(پێشبینین ئامادەی کەڵک وەرگرتن نییە)",
        "upload_source_url": " (URLـی بەکار، بۆ دەست‌پێگەیشتنی  گشتی)",
        "upload_source_file": "(پەڕگەی ھەڵبژێرراوت لەسەر کۆمپیوتەرەکەت)",
-       "listfiles-delete": "بÛ\8cسÚ\95ەوە",
+       "listfiles-delete": "سÚ\95Û\8cÙ\86ەوە",
        "listfiles-summary": "ئەم پەڕە تایبەتە ھەموو پەڕگە بارکراوەکان نیشان دەدات.",
        "listfiles_search_for": "بگەڕێ بۆ ناوی میدیای:",
        "imgfile": "پەڕگە",
        "duplicatesoffile": "ئەم {{PLURAL:$1|پەڕگە دووبارەکرنەوەیەکی|پەڕگانە دووبارەکردنەوەی}} ئەم پەڕگەن ([[Special:FileDuplicateSearch/$2|وردەکاری زیاتر]]):",
        "sharedupload": "ئەم پەڕگە لە $1ەوەیە و لەوە دەچێ لە پرۆژەکانی دیکەش بەکار ببرێت.",
        "sharedupload-desc-there": "ئەم پەڕگە لە $1ەوەیە و لەوە دەچێ لە پرۆژەکانی دیکەش بەکار ببرێت.\nتکایە بۆ زانیاریی زیاتر چاو بکە لە [$2 لاپەڕەی ناساندنی پەڕگە].",
-       "sharedupload-desc-here": "ئەم پەڕگە لە $1ەوەیە و لەوانەیە لە پرۆژەکانی دیکەش بەکار ھاتبێت.\nپێناسەکەی لەسەر [$2 پەڕەی وەسفی پەڕگەکە] لە خوارەوە نیشان دراوە.",
+       "sharedupload-desc-here": "ئەم پەڕگە لە $1ەوەیە و لەوانەیە لە پڕۆژەکانی دیکەش بەکار ھاتبێت.\nپێناسەکەی لەسەر [$2 پەڕەی وەسفی پەڕگەکە] لە خوارەوە نیشان دراوە.",
        "filepage-nofile": "پەڕگەیەک بەم ناوە نیە.",
        "filepage-nofile-link": "پەڕگەیەک بەم ناوە نیە بەڵام دەتوانی [$1 باری بکەی].",
        "uploadnewversion-linktext": "وەشانێکی نوێی ئەم پەڕگەیە بار بکە",
        "suppress": "چاودێری",
        "apisandbox-unfullscreen": "نیشاندانی پەڕە",
        "booksources": "سەرچاوەکانی کتێب",
-       "booksources-search-legend": "بۆ سەرچاوەی کتێب بگەڕێ",
+       "booksources-search-legend": "گەڕان بۆ سەرچاوەکانی کتێب",
        "booksources-search": "بگەڕێ",
        "booksources-text": "لە خوارەوە لیستێک لە بەستەر بۆ ماڵپەڕهایەک کە کتێبی نوێ و بەکارهێنراو دەفرۆشێت و لەوانەیە لەوێ زانیاریی زیاترت دەست‌کەوێت سەبارەت بەو کتێبانەی لە دووی دەگەڕیت:",
        "booksources-invalid-isbn": "ISBN دراو لەوە ناچی بەکار بێت، سەرنج بدە لە کاتی کۆپی کردن لە سەرچاوە تووشی هەڵە نوبوبێت.",
        "logempty": "هیچ بابەتێکی هاوتا لە لۆگەکاندا نەدۆزرایەوە.",
        "log-title-wildcard": "گەڕانی ئەو سەرناوانە بەم دەقەوە دەست پێدەکەن",
        "showhideselectedlogentries": "دیاریکردنی بابەتە ھەڵبژێردراوەکانی لۆگ بگۆڕە",
+       "checkbox-all": "ھەموو",
        "checkbox-none": "هیچ",
        "allpages": "ھەموو پەڕەکان",
        "nextpage": "پەڕەی پاشەوە ($1)",
        "usermessage-summary": "بەجێھێشتنی پەیامی سیستەم",
        "usermessage-editor": "پەیامنێری سیستەم",
        "watchlist": "پێرستی چاودێری",
-       "mywatchlist": "پێرستی چاودێری",
+       "mywatchlist": "پێڕستی چاودێری",
        "watchlistfor2": "بۆ $1 $2",
        "nowatchlist": "لە لیستی چاودێڕییەکانتدا ھیچ نیە.",
        "watchlistanontext": "بۆ دیتن و دەستکاریی بابەتەکانی  ناو پێرستی چاودێرییەکەتدا دەبێ $1.",
        "excontentauthor": "ناوەرۆک ئەمە بوو: «$1» (و تەنیا بەشداربوو «[[Special:Contributions/$2|$2]]» بوو)",
        "exbeforeblank": "ناوەرۆک بەر لە واڵاکردنەوە ئەمە بوو: «$1»",
        "delete-confirm": "سڕینەوەی «$1»",
-       "delete-legend": "بÛ\8cسÚ\95ەوە",
+       "delete-legend": "سÚ\95Û\8cÙ\86ەوە",
        "historywarning": "<strong>ھۆشیار بە:</strong> پەڕەیەک کە خەریکیت دەیسڕیتەوە مێژوویەکی ھەیە بە $1 {{PLURAL:$1|پێداچوونەوە|پێداچوونەوە}}وە:",
        "historyaction-submit": "نیشاندان",
        "confirmdeletetext": "تۆ خەریکی پەڕەیەک بە ھەموو مێژووەکەیەوە دەسڕیتەو.\nتکایە پشتڕاستی بکەوە کە دەتەوێت ئەم کارە بکەی، لە ئاکامەکەی تێدەگەی، و ئەم کارە بە پێی [[{{MediaWiki:Policy-url}}|سیاسەتنامە]] ئەنجام دەدەی.",
        "sp-contributions-username": "ناونیشانی ئایپی یان ناوی‌ بەکارھێنەر:",
        "sp-contributions-toponly": "تەنیا ئەو دەستکارییانە نیشان بدە کە دوایین پێداچوونەوەن",
        "sp-contributions-newonly": "تەنیا ئەو دەستکارییانە نیشان بدە کە دروستکردنی پەڕەن",
+       "sp-contributions-hideminor": "شاردنەوەی دەستکارییە بچووکەکان",
        "sp-contributions-submit": "بگەڕێ",
        "whatlinkshere": "بەستەرەکان بە ئێرەوە",
        "whatlinkshere-title": "ئەو پەڕانەی بەستەریان ھەیە بۆ «$1»",
        "javascripttest": "تاقیکردنەوەی جاڤاسکریپت",
        "tooltip-pt-userpage": "پەڕەی {{GENDER:|تۆ}}",
        "tooltip-pt-anonuserpage": "پەڕەی بەکارھێنەری بۆ ئای‌پی یەکە کە بەناویەوە خەریکی دەستکاری کردنی",
-       "tooltip-pt-mytalk": "پەڕەی لێدوانەکەت",
+       "tooltip-pt-mytalk": "پەڕەی لێدوان",
        "tooltip-pt-anontalk": "لێدوان لەسەر دەستکارییەکان لەم ئایپی ئەدرەسەوە",
        "tooltip-pt-preferences": "{{GENDER:|هەڵبژاردەکانت}}",
-       "tooltip-pt-watchlist": "پێرستی ئەو پەڕانە کە چاودێریی گۆڕانکارییەکانیانی دەکەی",
-       "tooltip-pt-mycontris": "پێرستی بەشدارییەکانت",
+       "tooltip-pt-watchlist": "پێڕستی ئەو پەڕانەی کە چاودێریی گۆڕانکارییەکانیان دەکەیت",
+       "tooltip-pt-mycontris": "پێڕستی بەشدارییەکان",
        "tooltip-pt-login": "پێشنیارت پێدەکرێ بچیتە ژوورەوە؛ ھەرچەندە زۆرت لێناکرێ",
        "tooltip-pt-logout": "دەرچوون",
-       "tooltip-ca-talk": "Ù\84Û\8eدÙ\88اÙ\86 Ø¯Û\95ربارÛ\95Û\8c Ù\86اÙ\88Û\95Ú\95Û\86Ú©Û\8c Ù¾Û\95رÛ\95",
+       "tooltip-ca-talk": "Ù\88تÙ\88Ù\88Û\8eÚ\98 Ø³Û\95بارÛ\95ت Ø¨Û\95 Ù¾Û\95Ú\95Û\95Û\8c Ù\86اÙ\88Û\95Ú\95Û\86Ú©",
        "tooltip-ca-edit": "دەستکاری ئەم پەڕەیە بکە‌",
        "tooltip-ca-addsection": "بەشێکی نوێ دەست پێ بکە",
        "tooltip-ca-viewsource": "ئەم پەڕەیە پارێزراوە.\nئەتوانی سەرچاوەکەی ببینیت",
        "tooltip-n-recentchanges": "لیستی دوایین گۆڕانکارییەکان لەم ویکییەدا",
        "tooltip-n-randompage": "پەڕەیەک بە هەڵکەوت نیشان بدە",
        "tooltip-n-help": "شوێنی تێگەیشتن",
-       "tooltip-t-whatlinkshere": "پێرستی ھەموو پەڕەکانی ویکی کە بەستەر دراون بۆ ئێرە",
+       "tooltip-t-whatlinkshere": "پێڕستی ھەموو پەڕەکانی ویکی کە بەستەر دراون بۆ ئێرە",
        "tooltip-t-recentchangeslinked": "دوایین گۆڕانکارییەکان لەو پەڕانە کە بەگرەوە گرێ دراون",
        "tooltip-feed-rss": "RSS feed بۆ ئەم پەڕە",
        "tooltip-feed-atom": "Atom feed بۆ ئەم پەڕە",
-       "tooltip-t-contributions": "پێرستی بەشدارییەکانی {{GENDER:$1|ئەم بەکارھێنەرە}}",
+       "tooltip-t-contributions": "پێڕستی بەشدارییەکانی {{GENDER:$1|ئەم بەکارھێنەرە}}",
        "tooltip-t-emailuser": "ئیمەیلێک بنێرە بۆ ئەم بەکارھێنەرە",
        "tooltip-t-upload": "پەڕگە بار بکە",
-       "tooltip-t-specialpages": "پێرستی ھەموو پەڕە تایبەتەکان",
+       "tooltip-t-specialpages": "پێڕستی ھەموو پەڕە تایبەتەکان",
        "tooltip-t-print": "وەشانی چاپی ئەم پەڕەیە",
        "tooltip-t-permalink": "گرێدەری ھەمیشەیی بۆ ئەم وەشانەی ئەم پەڕەیە",
        "tooltip-ca-nstab-main": "بینینی پەڕەی ناوەڕۆک",
        "tooltip-ca-nstab-user": "پەڕەی بەکارھێنەر تەماشا بکە",
        "tooltip-ca-nstab-media": "پەڕەی میدیا چاو لێ بکە",
        "tooltip-ca-nstab-special": "ئەمە پەڕەیەکی تایبەتە و دەستکاری ناکرێت",
-       "tooltip-ca-nstab-project": "بینینی پەڕەی پرۆژە",
+       "tooltip-ca-nstab-project": "بینینی پەڕەی پڕۆژە",
        "tooltip-ca-nstab-image": "بینینی پەڕەی پەڕگە",
        "tooltip-ca-nstab-mediawiki": "بینینی پەیامی سیستەم",
        "tooltip-ca-nstab-template": "بینینی قاڵبەکە",
        "tooltip-ca-nstab-help": "بینینی پەڕەی رێنمایی",
-       "tooltip-ca-nstab-category": "پەڕەی پۆلەکە ببینە",
+       "tooltip-ca-nstab-category": "بینینی پەڕەی پۆلەکە",
        "tooltip-minoredit": "ئەمە وەک گۆڕانکارییەکی بچووک دیاری بکە",
        "tooltip-save": "گۆڕانکارییەکانی خۆت پاشکەوت بکە",
        "tooltip-preview": "پێش بینینی گۆڕانکارییەکان، تکایە پێش پاشکەوت کردن ئەمە بەکار بھێنە",
        "tags-active-yes": "بەڵێ",
        "tags-active-no": "نا",
        "tags-edit": "دەستکاری",
+       "tags-delete": "سڕینەوە",
        "tags-hitcount": "$1 {{PLURAL:$1|گۆڕان|گۆڕانکاری}}",
        "comparepages": "پەڕەکان ھەڵسەنگێنە",
        "compare-page1": "پەڕەی ١",
        "logentry-upload-overwrite": "$1 وەشانێکی نوێی $3ی {{GENDER:$2|بار کرد}}",
        "rightsnone": "(ھیچ)",
        "revdelete-summary": "پوختەی دەستکاری",
-       "feedback-cancel": "ھەڵیوەشێنەوە",
+       "feedback-back": "گەڕانەوە",
+       "feedback-cancel": "ھەڵوەشاندنەوە",
        "feedback-close": "کرا",
        "feedback-message": "پەیام:",
        "feedback-subject": "بابەت:",
        "special-characters-group-gujarati": "گوجەراتی",
        "special-characters-group-thai": "تایلەندی",
        "special-characters-group-khmer": "خمێری",
-       "api-error-blacklisted": "هەڵبژێرە ناونیشانی جیاوازتر و واتادارتر.",
+       "log-action-filter-all": "ھەموو",
        "log-action-filter-upload-upload": "بارکردنی نوێ"
 }
index c09a07f..3bab099 100644 (file)
                        "Urbanecm",
                        "LordMsz",
                        "Matma Rex",
-                       "Dvorapa"
+                       "Dvorapa",
+                       "Walter Klosse",
+                       "Martin Urbanec",
+                       "Marek Pavlica"
                ]
        },
        "tog-underline": "Podtrhávat odkazy:",
        "tagline": "Z {{grammar:2sg|{{SITENAME}}}}",
        "help": "Nápověda",
        "search": "Hledat",
+       "search-ignored-headings": " #<!-- tento řádek ponechte beze změny --> <pre>\n# Zde uvedené nadpisy budou ignorovány vyhledáváním.\n# Změny této stránky se projeví ve chvíli, kdy je stránka používající příslušný nadpis indexována.\n# Přeindexování stránky můžete vynutit prázdnou editací.\n# Syntaxe je taková:\n#   * Cokoli od znaku „#“ do konce řádky je komentář.\n#   * Každá neprázdná řádka je přesný nadpis, který se má ignorovat, včetně velikosti písmen a tak.\nReference\nExterní odkazy\nSouvisející články\nSouvisející stránky\n #</pre> <!-- tento řádek ponechte beze změny -->",
        "searchbutton": "Hledat",
        "go": "Jít na",
        "searcharticle": "Jít na",
        "yourpasswordagain": "Zopakujte heslo:",
        "createacct-yourpasswordagain": "Potvrzení hesla",
        "createacct-yourpasswordagain-ph": "Zadejte heslo ještě jednou",
-       "remembermypassword": "Zapamatovat si mé přihlášení na tomto počítači (maximálně $1 {{PLURAL:$1|den|dny|dní}})",
        "userlogin-remembermypassword": "Přihlásit trvale",
        "userlogin-signwithsecure": "Používat zabezpečené připojení",
        "cannotloginnow-title": "Momentálně se nelze přihlásit",
        "passwordreset-emailelement": "Uživatelské jméno: \n$1\n\nDočasné heslo: \n$2",
        "passwordreset-emailsentemail": "Pokud je u vašeho účtu nastavena tato e-mailová adresa, bude vám zaslán e-mail pro získání nového hesla.",
        "passwordreset-emailsentusername": "Pokud je u tohoto účtu nastavena e-mailová adresa, bude vám zaslán e-mail pro získání nového hesla.",
-       "passwordreset-emailsent-capture": "Byl odeslán e-mail pro získání nového hesla, který je zobrazen níže.",
-       "passwordreset-emailerror-capture": "Byl vygenerován e-mail pro získání nového hesla, který je zobrazen níže, ale {{GENDER:$2|uživateli|uživatelce}} se ho nepodařilo odeslat: $1",
        "passwordreset-emailsent-capture2": "{{PLURAL:$1|Byl odeslán e-mail|Byly odeslány e-maily}} pro získání nového hesla. {{PLURAL:$1|Uživatelské jméno a heslo jsou zobrazeny|Seznam uživatelských jmen a hesel je zobrazen}} níže.",
        "passwordreset-emailerror-capture2": "{{GENDER:$2|Uživateli|Uživatelce}} se nepodařilo odeslat e-mail: $1 {{PLURAL:$3|Uživatelské jméno a heslo jsou zobrazeny|Seznam uživatelských jmen a hesel je zobrazen}} níže.",
        "passwordreset-nocaller": "Musí být uveden volající",
        "passwordreset-nodata": "Nebylo zadáno uživatelské jméno ani e-mailová adresa",
        "changeemail": "Změna nebo odstranění e-mailové adresy",
        "changeemail-header": "Vyplněním tohoto formuláře můžete změnit svou e-mailovou adresu. Pokud chcete ze svého účtu odstranit vazbu na všechny e-mailové adresy, ponechte při odeslání formuláře novou e-mailovou adresu prázdnou.",
-       "changeemail-passwordrequired": "Pro potvrzení této změny musíte zadat své heslo.",
        "changeemail-no-info": "K této stránce mají přímý přístup jen přihlášení uživatelé.",
        "changeemail-oldemail": "Stávající e-mailová adresa:",
        "changeemail-newemail": "Nová e-mailová adresa:",
        "content-model-css": "CSS",
        "content-json-empty-object": "Prázdný objekt",
        "content-json-empty-array": "Prázdné pole",
+       "deprecated-self-close-category": "Stránky s neplatnými sebeuzavírajícími HTML značkami",
+       "deprecated-self-close-category-desc": "Stránka obsahuje neplatné sebeuzavírající HTML značky, například <code>&lt;b/></code> nebo <code>&lt;span/></code>. Jejich chování se v zájmu konzistence se specifikací HTML5 brzy změní, proto je jejich použití ve wikitextu zastaralé.",
        "duplicate-args-warning": "<strong>Upozornění:</strong> Stránka [[:$1]] volá [[:$2]] s více než jednou hodnotou parametru „$3“. Použije se jen poslední uvedená hodnota.",
        "duplicate-args-category": "Stránky s duplicitními argumenty ve voláních šablon",
        "duplicate-args-category-desc": "Stránka obsahuje volání šablony, které používá duplicitní argumenty, např. <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> nebo <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Zdá se, že editace již byla zrušena.",
        "undo-summary": "Zrušena verze $1 od uživatele [[Special:Contributions/$2|$2]] ([[User talk:$2|diskuse]])",
        "undo-summary-username-hidden": "Zrušena verze $1 od skrytého uživatele",
-       "cantcreateaccounttitle": "Nelze vytvořit uživatelský účet",
        "cantcreateaccount-text": "Zakládání nových účtů z této IP adresy (<strong>$1</strong>) bylo zablokováno {{GENDER:$3|uživatelem|uživatelkou}} [[User:$3|$3]].\n\n$3 uvádí toto zdůvodnění: <em>$2</em>",
        "cantcreateaccount-range-text": "Zakládání nových účtů z IP adres v rozsahu <strong>$1</strong>, který obsahuje i vaši IP adresu (<strong>$4</strong>), bylo zablokováno {{GENDER:$3|uživatelem|uživatelkou}} [[User:$3|$3]].\n\n$3 uvádí toto zdůvodnění: <em>$2</em>",
        "viewpagelogs": "Zobrazit protokolovací záznamy k této stránce",
        "group-suppress-member": "{{GENDER:$1|utajovatel|utajovatelka|utajovatel}}",
        "grouppage-user": "{{ns:project}}:Uživatelé",
        "grouppage-autoconfirmed": "{{ns:project}}:Automaticky schválení uživatelé",
-       "grouppage-bot": "{{ns:project}}:Boti",
+       "grouppage-bot": "{{ns:project}}:Roboti",
        "grouppage-sysop": "{{ns:project}}:Správci",
        "grouppage-bureaucrat": "{{ns:project}}:Byrokraté",
        "grouppage-suppress": "{{ns:project}}:Utajovatelé",
        "grant-group-high-volume": "Velkoobjemové činnosti",
        "grant-group-customization": "Nastavení a přizpůsobení",
        "grant-group-administration": "Provádění správcovských činností",
+       "grant-group-private-information": "Přístup k soukromým údajům o vás",
        "grant-group-other": "Různé činnosti",
        "grant-blockusers": "Blokovat a odblokovávat uživatele",
        "grant-createaccount": "Zakládat účty",
        "grant-highvolume": "Hromadné editace",
        "grant-oversight": "Skrývat uživatele a utajovat revize",
        "grant-patrol": "Patrolovat změny stránek",
+       "grant-privateinfo": "Přístup k soukromým údajům",
        "grant-protect": "Zamykat a odemykat stránky",
        "grant-rollback": "Vracet editace zpět",
        "grant-sendemail": "Posílat e-maily ostatním uživatelům",
        "action-applychangetags": "přidávat značky k vlastním změnám",
        "action-changetags": "přidávat libovolné značky na jednotlivé revize a protokolovací záznamy a odebírat je",
        "action-deletechangetags": "mazat značky z databáze",
+       "action-purge": "vyčistit vyrovnávací paměť této stránky",
        "nchanges": "$1 {{PLURAL:$1|změna|změny|změn}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|od poslední návštěvy}}",
        "enhancedrc-history": "historie",
        "file-thumbnail-no": "Jméno souboru začíná na <strong>$1</strong>.\nMožná to je obrázek ve zmenšené velikosti ''(náhled)''.\nNačtěte soubor v plném rozlišením, pokud je k dispozici, nebo změňte jméno souboru.",
        "fileexists-forbidden": "Soubor s tímto názvem již existuje a není dovoleno ho přepsat.\nPokud chcete přesto soubor načíst, vraťte se a zvolte jiný název.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Soubor s tímto názvem již existuje ve sdíleném úložišti. Pokud přesto chcete váš soubor načíst, vraťte se a zvolte jiný název. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Načítaný soubor je přesným duplikátem aktuální revize souboru <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Načítaný soubor je přesným duplikátem {{PLURAL:$2|starší revize|starších revizí}} souboru <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Tento soubor je duplikát {{PLURAL:$1|následujícího souboru|následujících souborů}}:",
        "file-deleted-duplicate": "Identický soubor k tomuto ([[:$1]]) byl již dříve smazán. Před tím, než soubor znovu nahrajete, byste měli zkontrolovat záznamy o předchozím smazání.",
        "file-deleted-duplicate-notitle": "Identický soubor k tomuto byl již dříve smazán a název byl utajen.\nPřed tím, než soubor znovu nahrajete, byste měli požádat někoho, kdo může prohlížet utajené soubory, aby situaci zkontroloval.",
        "uploadstash-errclear": "Soubory se nepodařilo vymazat.",
        "uploadstash-refresh": "Aktualizovat seznam souborů",
        "uploadstash-thumbnail": "zobrazit náhled",
+       "uploadstash-exception": "Načtený soubor se nepodařilo uložit do skrýše ($1): „$2“.",
        "invalid-chunk-offset": "Neplatný posun bloku",
        "img-auth-accessdenied": "Přístup odepřen",
        "img-auth-nopathinfo": "Chybí PATH_INFO.\nVáš server není nastaven tak, aby tuto informaci poskytoval.\nMožná funguje pomocí CGI a img_auth na něm nemůže fungovat.\nVizte https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "filerevert-submit": "Vrátit zpět",
        "filerevert-success": "Soubor '''[[Media:$1|$1]]''' byl vrácen zpět na [$4 verzi z $3 $2].",
        "filerevert-badversion": "Není dostupná předchozí verze tohoto souboru s odpovídající časovou značkou.",
+       "filerevert-identical": "Aktuální verze souboru se již od vybrané verze neliší.",
        "filedelete": "Smazání souboru $1",
        "filedelete-legend": "Smazat soubor",
        "filedelete-intro": "Chystáte se smazat soubor '''[[Media:$1|$1]]''' i s celou historií.",
        "querypage-disabled": "Tato speciální stránka je z výkonnostních důvodů vypnuta.",
        "apihelp": "Nápověda k API",
        "apihelp-no-such-module": "Modul „$1“ nebyl nalezen.",
-       "apisandbox": "API pískoviště",
-       "apisandbox-jsonly": "Pro použití API pískoviště je nutný JavaScript.",
+       "apisandbox": "Pískoviště API",
+       "apisandbox-jsonly": "Pro použití pískoviště API je nutný JavaScript.",
        "apisandbox-api-disabled": "API je na tomto webu vypnuto.",
        "apisandbox-intro": "Pomocí této stránky můžete experimentovat s <strong>webovými službami MediaWiki API</strong>.\nPodrobnosti využití API najdete v [[mw:API:Main page|jeho dokumentaci]]. Příklad: [https://www.mediawiki.org/wiki/API#A_simple_example získání obsahu Hlavní stránky]. Další příklady uvidíte vybráním parametru action.\n\nUvědomte si, že přestože jste na pískovišti, mohou akce provedené na této stránce wiki změnit.",
        "apisandbox-fullscreen": "Rozbalit panel",
        "listgrouprights-namespaceprotection-header": "Omezení jmenných prostorů",
        "listgrouprights-namespaceprotection-namespace": "Jmenný prostor",
        "listgrouprights-namespaceprotection-restrictedto": "Oprávnění umožňující uživateli editovat",
-       "listgrants": "Svolení",
-       "listgrants-summary": "Následující seznam obsahuje svolení a jim odpovídající přístup k uživatelským právům. Uživatelé mohou aplikace autorizovat k využití jejich účtu, ale s omezenými právy na základě svolení, která uživatel aplikaci poskytl. Aplikace konající jménem uživatele ale nemůže využít oprávnění, která uživatel nemá.\nK jednotlivým oprávněním mohou existovat [[{{MediaWiki:Listgrouprights-helppage}}|doplňující informace]].",
-       "listgrants-grant": "Svolení",
+       "listgrants": "Skupiny oprávnění",
+       "listgrants-summary": "Následující seznam obsahuje skupiny oprávnění a jim odpovídající přístup k uživatelským právům. Uživatelé mohou aplikace autorizovat k využití jejich účtu, ale s omezenými právy na základě skupin oprávnění, která uživatel aplikaci dovolil použít. Aplikace konající jménem uživatele ale nemůže využít oprávnění, která uživatel nemá.\nK jednotlivým oprávněním mohou existovat [[{{MediaWiki:Listgrouprights-helppage}}|doplňující informace]].",
+       "listgrants-grant": "Skupina oprávnění",
        "listgrants-rights": "Oprávnění",
        "trackingcategories": "Sledovací kategorie",
        "trackingcategories-summary": "Tato stránka obsahuje seznam sledovacích kategorií, které automaticky přidává software MediaWiki. Jejich jména lze změnit úpravou příslušných systémových hlášení ve jmenném prostoru {{ns:8}}.",
        "trackingcategories-name": "Název hlášení",
        "trackingcategories-desc": "Kritéria pro vložení do kategorie",
        "restricted-displaytitle-ignored": "Stránky s ignorovanými zobrazovanými názvy",
-       "restricted-displaytitle-ignored-desc": "Stránka obsahuje příkaz <code><nowiki>{{DISPLAYTITLE}}</nowiki></code>, který se ignoruje, protože není ekvivalentní skutečnému názvu stránky.",
+       "restricted-displaytitle-ignored-desc": "Stránka obsahuje příkaz <code><nowiki>{{DISPLAYTITLE}}</nowiki></code>, který se ignoruje, neboť není ekvivalentní skutečnému názvu stránky.",
        "noindex-category-desc": "Stránka není indexována roboty, protože obsahuje kouzelné slovo <code><nowiki>__NOINDEX__</nowiki></code> a je ve jmenném prostoru, ve kterém je tento příznak dovolen.",
        "index-category-desc": "Stránka obsahuje kouzelné slovo <code><nowiki>__INDEX__</nowiki></code> (a je ve jmenném prostoru, ve kterém je tento příznak dovolen), takže je indexována roboty, přestože by normálně nebyla.",
        "post-expand-template-inclusion-category-desc": "Stránka je po rozbalení všech šablon větší než <code>$wgMaxArticleSize</code>, takže některé šablony rozbaleny nebyly.",
        "watchnologin": "Nejste přihlášen(a)",
        "addwatch": "Přidat do sledovaných stránek",
        "addedwatchtext": "Stránka „[[:$1]]“ a její diskusní stránka byly přidány mezi [[Special:Watchlist|stránky, které sledujete]].",
+       "addedwatchtext-talk": "„[[:$1]]“ a související stránka byly přidány mezi [[Special:Watchlist|stránky, které sledujete]].",
        "addedwatchtext-short": "Stránka „$1“ byla přidána mezi stránky, které sledujete.",
        "removewatch": "Vyřadit ze sledovaných stránek",
-       "removedwatchtext": "Stránka „[[:$1]]“ a její diskusní stránka byly vyřazeny z vašeho [[Special:Watchlist|seznamu sledovaných stránek]].",
+       "removedwatchtext": "Stránka „[[:$1]]“ a její diskusní stránka byly vyřazeny ze [[Special:Watchlist|stránek, které sledujete]]",
+       "removedwatchtext-talk": "„[[:$1]]“ a související stránka byly vyřazeny ze [[Special:Watchlist|stránek, které sledujete]].",
        "removedwatchtext-short": "Stránka „$1“ byla vyřazena z vašeho seznamu sledovaných stránek.",
        "watch": "Sledovat",
        "watchthispage": "Sledovat tuto stránku",
        "rollbacklinkcount-morethan": "vrácení více než $1 {{PLURAL:$1|editace|editací}} zpět",
        "rollbackfailed": "Nešlo vrátit zpět",
        "rollback-missingparam": "V požadavku chybí povinné parametry.",
+       "rollback-missingrevision": "Nepodařilo se načíst data revize.",
        "cantrollback": "Nelze vrátit zpět poslední editaci, neboť poslední přispěvatel je jediným autorem této stránky.",
        "alreadyrolled": "Nelze vrátit zpět poslední editaci [[:$1]] od uživatele [[User:$2|$2]] ([[User talk:$2|diskuse]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]), protože někdo jiný již stránku editoval nebo vrátil tuto změnu zpět.\n\nPoslední editaci této stránky {{GENDER:$3|provedl|provedla|provedl uživatel}} [[User:$3|$3]] ([[User talk:$3|diskuse]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Shrnutí editace bylo: <em>$1</em>.",
        "changecontentmodel-nodirectediting": "Model obsahu $1 nepodporuje přímou editaci",
        "changecontentmodel-emptymodels-title": "Nejsou k dispozici žádné modely obsahu",
        "changecontentmodel-emptymodels-text": "Obsah stránky [[:$1]] nelze zkonvertovat na žádný typ.",
-       "log-name-contentmodel": "Kniha změny modelů obsahu",
+       "log-name-contentmodel": "Kniha změn modelů obsahu",
        "log-description-contentmodel": "Události týkající se modelů obsahu stránek",
        "logentry-contentmodel-new": "$1 {{GENDER:$2|založil|založila}} stránku $3 za použití nestandardního modelu obsahu „$5“",
        "logentry-contentmodel-change": "$1 {{GENDER:$2|změnil|změnila}} model obsahu stránky $3 z „$4“ na „$5“",
        "undeletehistorynoadmin": "Tato stránka byla smazána. Důvod smazání je uveden níže, spolu s informacemi o uživatelích, kteří tuto stránku před smazáním editovali. Samotný text stránky je dostupný pouze správcům.",
        "undelete-revision": "Smazaná verze stránky $1 (z $4 dne $5) od uživatele $3:",
        "undeleterevision-missing": "Nesprávná nebo chybějící revize. Možná máte špatný odkaz, nebo revize byla obnovena či odstraněna z archivu.",
+       "undeleterevision-duplicate-revid": "Nebylo možné obnovit {{PLURAL:$1|jednu revizi|$1 revize|$1 revizí}}, protože {{PLURAL:$1|její|jejich}} <code>rev_id</code> již {{PLURAL:$1|bylo obsazeno|byla obsazena}}.",
        "undelete-nodiff": "Nebyla nalezena žádná předchozí verze.",
        "undeletebtn": "Obnovit",
        "undeletelink": "prohlédnout/obnovit",
        "undeletedrevisions": "{{PLURAL:$1|Obnovena jedna verze|Obnoveny $1 verze|Obnoveno $1 verzí}}",
        "undeletedrevisions-files": "{{PLURAL:$1|Obnovena jedna verze|Obnoveny $1 verze|Obnoveno $1 verzí}} a {{PLURAL:$2|jeden soubor|$2 soubory|$2 souborů}}.",
        "undeletedfiles": "{{PLURAL:$1|Obnoven jeden soubor|Obnoveny $1 soubory|Obnoveno $1 souborů}}",
-       "cannotundelete": "Obnovení se nezdařilo:\n$1",
+       "cannotundelete": "Některá nebo všechna obnovení se nezdařila:\n$1",
        "undeletedpage": "<strong>Stránka „$1“ byla obnovena</strong>\n\nZáznam o posledních mazáních a obnoveních najdete v [[Special:Log/delete|knize smazaných stránek]].",
        "undelete-header": "Vizte nedávno smazané stránky v [[Special:Log/delete|knize smazaných stránek]].",
        "undelete-search-title": "Hledání smazaných stránek",
        "tooltip-namespace_association": "Zaškrtnutím tohoto políčka zahrnete i diskusní či obsahový jmenný prostor příslušný k vybranému jmennému prostoru",
        "blanknamespace": "(Hlavní)",
        "contributions": "Příspěvky {{GENDER:$1|uživatele|uživatelky}}",
-       "contributions-title": "Příspěvky uživatele $1",
+       "contributions-title": "Příspěvky {{GENDER:$1|uživatele|uživatelky|uživatele/uživatelky}} $1",
        "mycontris": "Příspěvky",
        "anoncontribs": "Příspěvky",
        "contribsub2": "{{GENDER:$3|uživatele|uživatelky}} $1 ($2)",
        "sp-contributions-newbies-sub": "Noví uživatelé",
        "sp-contributions-newbies-title": "Příspěvky nových uživatelů",
        "sp-contributions-blocklog": "kniha zablokování",
-       "sp-contributions-suppresslog": "utajené příspěvky uživatele",
-       "sp-contributions-deleted": "smazané editace uživatele",
+       "sp-contributions-suppresslog": "utajené příspěvky {{GENDER:$1|uživatele|uživatelky}}",
+       "sp-contributions-deleted": "smazané editace {{GENDER:$1|uživatele|uživatelky}}",
        "sp-contributions-uploads": "načtené soubory",
        "sp-contributions-logs": "protokolovací záznamy",
        "sp-contributions-talk": "diskuse",
        "pageinfo-article-id": "ID stránky",
        "pageinfo-language": "Jazyk obsahu stránky",
        "pageinfo-content-model": "Model obsahu stránky",
+       "pageinfo-content-model-change": "změnit",
        "pageinfo-robot-policy": "Indexování roboty",
        "pageinfo-robot-index": "Dovoleno",
        "pageinfo-robot-noindex": "Zakázáno",
        "mw-widgets-dateinput-placeholder-month": "RRRR-MM",
        "mw-widgets-titleinput-description-new-page": "stránka zatím neexistuje",
        "mw-widgets-titleinput-description-redirect": "přesměrování na $1",
-       "api-error-blacklisted": "Zvolte prosím jiný, popisný název.",
        "sessionmanager-tie": "Nelze kombinovat několik typů autentizace požadavků: $1.",
        "sessionprovider-generic": "relace pomocí $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "relace pomocí cookies",
        "linkaccounts-submit": "Propojit účty",
        "unlinkaccounts": "Zrušení propojení účtů",
        "unlinkaccounts-success": "Propojení účtu bylo zrušeno.",
-       "authenticationdatachange-ignored": "Změna autentizačních údajů nebyla zpracována. Možná není nakonfigurován žádný poskytovatel?"
+       "authenticationdatachange-ignored": "Změna autentizačních údajů nebyla zpracována. Možná není nakonfigurován žádný poskytovatel?",
+       "userjsispublic": "Uvědomte si prosím, že podstránky s JavaScriptem by neměly obsahovat tajné údaje, protože jsou viditelné ostatním uživatelům.",
+       "usercssispublic": "Uvědomte si prosím, že podstránky s CSS by neměly obsahovat tajné údaje, protože jsou viditelné ostatním uživatelům."
 }
index c643580..aca71bb 100644 (file)
@@ -17,7 +17,8 @@
                        "Matma Rex",
                        "Diafol",
                        "Nemo bis",
-                       "Dafyddt"
+                       "Dafyddt",
+                       "Jdforrester"
                ]
        },
        "tog-underline": "Tanlinellu cysylltiadau:",
        "passwordreset-emailtext-user": "Gofynodd y defnyddiwr $1 ar {{SITENAME}} am gael ailosod ei gyfrinair ar {{SITENAME}}\n($4). Mae'r {{PLURAL:$3||cyfrif|cyfrifon}} canlynol ynghlwm wrth y cyfeiriad e-bost hwn:\n\n$2\n\nBydd y {{PLURAL:$3||cyfrinair|cyfrineiriau}} dros dro hyn yn dod i ben ymhen {{PLURAL:$5||diwrnod|deuddydd|tridiau|$5 diwrnod}}.\nDylech fewngofnodi nawr a dewis cyfrinair newydd. Os mai rhywun arall a ofynodd am ailosod y cyfrinair, neu os ydych wedi cofio eich cyfrinair gwreiddiol, neu os nad ydych am ei newid bellach, gallwch anwybyddu'r neges hon a pharhau i ddefnyddio eich hen gyfrinair.",
        "passwordreset-emailelement": "Enw'r defnyddiwr: \n$1\n\nY cyfrinair dros dro: \n$2",
        "passwordreset-emailsentemail": "Anfonwyd e-bost i ailosod eich cyfrinair atoch.",
-       "passwordreset-emailsent-capture": "Anfonwyd e-bost i ailosod cyfrinair, ac fe'i ddangosir isod.",
-       "passwordreset-emailerror-capture": "Lluniwyd e-bost i ailosod cyfrinair fel ag a welir isod, ond ni lwyddwyd i'w anfon at y {{GENDER:$2|defnyddiwr}}: $1",
        "changeemail": "Newid y cyfeiriad e-bost",
        "changeemail-header": "Cwbwlhewch y ffurflen hon i newid cyfeiriad e-bost y cyfrifi. I ddileu pob cysylltiad i bob cyfeiriad ebost, gadewch e'n wag.",
        "changeemail-no-info": "Ni allwch fynd at y dudalen hon heblaw eich bod wedi mewngofnodi.",
        "minoredit": "Golygiad bychan yw hwn",
        "watchthis": "Gwylier y dudalen hon",
        "savearticle": "Cadw'r dudalen",
+       "publishpage": "Cyhoeddi tudalen",
+       "publishchanges": "Cyhoeddi newidiadau",
        "preview": "Rhagolwg",
        "showpreview": "Dangos rhagolwg",
        "showdiff": "Dangos newidiadau",
        "undo-nochange": "Ymddengys fod y golygiad wedi ei ddadwneud yn barod.",
        "undo-summary": "Dadwneud y golygiad $1 gan [[Special:Contributions/$2|$2]] ([[User talk:$2|Sgwrs]] | [[Special:Contributions/$2|{{MediaWiki:Contribslink}}]])",
        "undo-summary-username-hidden": "Dadwneud y golygiad $1 gan ddefnyddiwr cudd",
-       "cantcreateaccounttitle": "Yn methu creu cyfrif",
        "cantcreateaccount-text": "Rhwystrwyd y gallu i greu cyfrif ar gyfer y cyfeiriad IP hwn, ('''$1'''), gan [[User:$3|$3]].\n\nY rheswm a roddwyd dros y bloc gan $3 yw ''$2''.",
        "cantcreateaccount-range-text": "Gosododd [[User:$3|$3]] floc ar agor cyfrifon o gyfeiriadau IP yn yr ystod <strong>$1</strong>, sy'n cynnwys eich cyfeiriad IP chi (<strong>$4</strong>).\n\nY rheswm a roddwyd gan $3 yw \"$2\"",
        "viewpagelogs": "Dangos logiau'r dudalen hon",
        "rightslogtext": "Lòg y newidiadau i alluoedd defnyddwyr yw hwn.",
        "action-read": "darllen y dudalen",
        "action-edit": "golygu'r dudalen",
-       "action-createpage": "creu tudalennau",
-       "action-createtalk": "creu tudalennau sgwrs",
+       "action-createpage": "crewch y ddalen hon",
+       "action-createtalk": "crewch y ddalen Sgwrs",
        "action-createaccount": "creu'r cyfrif defnyddiwr hwn",
+       "action-autocreateaccount": "crewch y cyfri defnyddiwr allanol yma",
        "action-history": "gweld hanes y dudalen",
        "action-minoredit": "marcio'r golygiad yn un bach",
        "action-move": "symud y dudalen",
        "action-viewmywatchlist": "gweld eich rhestr wylio",
        "action-viewmyprivateinfo": "gweld eich manylion personol preifat",
        "action-editmyprivateinfo": "golygu eich manylion personol preifat",
+       "action-editcontentmodel": "golygwch y cynnwys",
        "nchanges": "$1 {{PLURAL:$1|newid|newid|newid|newid|newid|o newidiadau}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ers eich ymweliad diwethaf}}",
        "enhancedrc-history": "hanes",
        "apisandbox-submit": "Gwnewch gais",
        "apisandbox-reset": "Clirio",
        "apisandbox-retry": "Ailgeisio",
+       "apisandbox-helpurls": "Dolennau cymorth",
+       "apisandbox-examples": "Engreifftiau",
+       "apisandbox-results": "Canlyniadau",
        "booksources": "Ffynonellau llyfrau",
        "booksources-search-legend": "Chwilier am lyfrau",
        "booksources-search": "Chwilio",
        "logempty": "Does dim eitemau yn cyfateb yn y lòg.",
        "log-title-wildcard": "Chwilio am deitlau'n dechrau gyda'r geiriau hyn",
        "showhideselectedlogentries": "Dewis dangos neu guddio cofnodion lòg",
+       "checkbox-all": "Y cyfan",
+       "checkbox-none": "Dim",
        "allpages": "Pob tudalen",
        "nextpage": "Y bloc nesaf gan ddechrau gyda ($1)",
        "prevpage": "Y bloc cynt gan ddechrau gyda ($1)",
        "activeusers-hidebots": "Cuddio botiau",
        "activeusers-hidesysops": "Cuddio gweinyddwyr",
        "activeusers-noresult": "Dim defnyddwyr i'w cael.",
+       "activeusers-submit": "Dangos defnyddwyr byw",
        "listgrouprights": "Galluoedd grwpiau defnyddwyr",
        "listgrouprights-summary": "Dyma restr o'r grwpiau defnyddwyr sydd i'w cael ar y wici hon, ynghyd â galluoedd aelodau'r gwahanol grwpiau. Cewch wybodaeth pellach am y gwahanol alluoedd ar y [[{{MediaWiki:Listgrouprights-helppage}}|dudalen gymorth]].",
        "listgrouprights-key": "Allwedd:\n* <span class=\"listgrouprights-granted\">Gallu sydd wedi ei roi</span>\n* <span class=\"listgrouprights-revoked\">Gallu sydd wedi ei dynnu yn ôl</span>",
        "listgrouprights-namespaceprotection-header": "Cyfyngiadau parth",
        "listgrouprights-namespaceprotection-namespace": "Parth",
        "listgrouprights-namespaceprotection-restrictedto": "Gallu(oedd) yn caniatau i'r defnyddiwr olygu",
+       "listgrants": "Nawdd",
+       "listgrants-rights": "Hawliau",
        "trackingcategories": "Categoriau tracio",
        "trackingcategories-summary": "Mae'r dudalen hon yn rhestru categoriau tracio sy'n cael eu creu'n otomatig gan feddalwedd MediaWiki. Gellir newid eu henwau drwy addasu'r negeseuon ym mharthenw {{ns:8}}.",
        "trackingcategories-msg": "Categori tracio",
        "sessionfailure": "Mae'n debyg fod yna broblem gyda'ch sesiwn mewngofnodi; diddymwyd y weithred er mwyn diogelu'r sustem rhag ddefnyddwyr maleisus. Gwasgwch botwm 'nôl' eich porwr ac ail-lwythwch y dudalen honno, yna ceisiwch eto.",
        "changecontentmodel-title-label": "Teitl y ddalen",
        "changecontentmodel-reason-label": "Rheswm:",
+       "changecontentmodel-submit": "Newid",
        "protectlogpage": "Lòg diogelu",
        "protectlogtext": "Isod mae rhestr o bob gweithred diogelu (a dad-ddiogelu) tudalen.\nMae'r tudalennau sydd wedi eu diogelu ar hyn o bryd wedi eu rhestri ar y [[Special:ProtectedPages|rhestr tudalennau wedi eu diogelu]].",
        "protectedarticle": "wedi diogelu '[[$1]]'",
        "whatlinkshere-links": "← cysylltiadau",
        "whatlinkshere-hideredirs": "$1 ailgyfeiriadau",
        "whatlinkshere-hidetrans": "$1 cynhwysion",
-       "whatlinkshere-hidelinks": "$1 cysylltau",
+       "whatlinkshere-hidelinks": "$1 dolennau",
        "whatlinkshere-hideimages": "$1 cysylltau ffeiliau",
        "whatlinkshere-filters": "Hidlau",
        "autoblockid": "Awtoflocio #$1",
        "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",
-       "api-error-blacklisted": "Dewiswch deitl gwahanol sy'n disgrifio'r gwaith, os gwelwch yn dda."
+       "mw-widgets-dateinput-placeholder-month": "BBBB-MM"
 }
index aa6b2f0..c6bd656 100644 (file)
        "tagline": "Fra {{SITENAME}}",
        "help": "Hjælp",
        "search": "Søg",
+       "search-ignored-headings": " #<!-- lad denne linje være præcis som den er --> <pre>\n# Overskrifter, der vil blive ignoreret af søgning.\n# Ændringer til dette træder i kraft så snart siden med overskriften er indekseret.\n# Du kan tvinge siden til genindeksering ved at lave en nul redigering.\n# syntaksen er som følger:\n#   * Alt fra en tegnet \"#\" til slutningen af linjen er en kommentar\n#   * Hver ikke-tomme linje er den nøjagtige titel der skal ignoreres, der skelnes også mellem store og små bogstaver\nReferencer\nEksterne henvisninger\nSe også\nKilder og henvisninger\nEksterne kilder/henvisninger\nKilder\n #</pre> <!-- lad denne linje være præcis som den er -->",
        "searchbutton": "Søg",
        "go": "Gå til",
        "searcharticle": "Gå til",
        "yourpasswordagain": "Gentag adgangskode",
        "createacct-yourpasswordagain": "Bekræft adgangskode",
        "createacct-yourpasswordagain-ph": "Indtast adgangskode igen",
-       "remembermypassword": "Husk mit brugernavn i denne browser (højst $1 {{PLURAL:$1|dag|dage}})",
        "userlogin-remembermypassword": "Husk mig",
        "userlogin-signwithsecure": "Brug sikker forbindelse",
        "cannotloginnow-title": "Kan ikke logge ind på nuværende tidspunkt",
        "passwordreset-emailelement": "Brugernavn: \n$1\n\nMidlertidig adgangskode: \n$2",
        "passwordreset-emailsentemail": "Hvis denne e-mailadresse er knyttet til din konto, så vil en e-mail om nulstilling af adgangskoden blive sendt.",
        "passwordreset-emailsentusername": "Hvis der er en e-mailadresse forbundet med dette brugernavn, så vil en e-mail om nulstilling af adgangskoden blive sendt.",
-       "passwordreset-emailsent-capture": "En e-mail om nulstilling af adgangskode, som vist nedenfor, er blevet sendt.",
-       "passwordreset-emailerror-capture": "En mail om nulstilling af adgangskode, som vist nedenfor, blev genereret, men det lykkedes ikke at sende den til {{GENDER:$2|bruger}}: $1",
        "changeemail": "Ændr eller fjern e-mailadresse",
        "changeemail-header": "Udfyld denne formular for at ændre din e-mailadresse. Hvis du gerne vil fjerne forbindelsen af en e-mailadresse fra din konto, så lad den nye e-mailadresse være blank, når du sender formularen.",
-       "changeemail-passwordrequired": "Du er nødt til at indtaste din adgangskode for at bekræfte denne ændring.",
        "changeemail-no-info": "Du skal være logget på for at komme direkte til denne side.",
        "changeemail-oldemail": "Nuværende e-mailadresse:",
        "changeemail-newemail": "Ny e-mailadresse:",
        "minoredit": "Dette er en mindre ændring",
        "watchthis": "Overvåg denne side",
        "savearticle": "Gem side",
+       "savechanges": "Gem ændringer",
+       "publishpage": "Offentliggør side",
+       "publishchanges": "Offentliggør ændringer",
        "preview": "Forhåndsvisning",
        "showpreview": "Forhåndsvisning",
        "showdiff": "Vis ændringer",
        "undo-nochange": "Ændringen ser ud til allerede at være blevet fjernet.",
        "undo-summary": "Fjerner version $1 af [[Special:Contributions/$2|$2]] ([[User talk:$2|diskussion]])",
        "undo-summary-username-hidden": "Fortryde revision $1 af en skjult bruger",
-       "cantcreateaccounttitle": "Brugerkontoen kan ikke oprettes.",
        "cantcreateaccount-text": "Oprettelsen af en brugerkonto fra IP-adressen ('''$1''') er blevet blokeret af [[User:$3|$3]].\n\nÅrsagen til blokeringen er angivet af $3 som ''$2''",
        "cantcreateaccount-range-text": "Oprettelsen af en brugerkonto fra IP-adresser i intervallet <strong>$1</strong>, som omfatter din IP-adresse (<strong>$4</strong>), er blevet blokeret af [[User:$3|$3]].\n\nÅrsagen angivet af $3 er <em>$2</em>",
        "viewpagelogs": "Vis loglister for denne side",
        "rightslogtext": "Dette er en log over ændringer i brugeres rettigheder.",
        "action-read": "se denne side",
        "action-edit": "redigere denne side",
-       "action-createpage": "oprette sider",
-       "action-createtalk": "oprette diskussionssider",
+       "action-createpage": "oprette denne side",
+       "action-createtalk": "oprette denne diskussionsside",
        "action-createaccount": "Oprette denne brugerkonto",
        "action-history": "se historik for denne side",
        "action-minoredit": "markere denne redigering som mindre",
        "upload-form-label-infoform-title": "Detaljer",
        "upload-form-label-infoform-name": "Navn",
        "upload-form-label-infoform-description": "Beskrivelse",
+       "upload-form-label-usage-title": "Anvendelse",
        "upload-form-label-usage-filename": "Filnavn",
        "upload-form-label-infoform-categories": "Kategorier",
        "upload-form-label-infoform-date": "Dato",
        "log-title-wildcard": "Søg i titler som begynder med teksten",
        "showhideselectedlogentries": "Vis/skjul de markerede loghændelser",
        "log-edit-tags": "Rediger tags i valgte logposter",
+       "checkbox-all": "Alle",
+       "checkbox-none": "Ingen",
        "allpages": "Alle sider",
        "nextpage": "Næste side ($1)",
        "prevpage": "Forrige side ($1)",
        "listgrouprights-namespaceprotection-header": "Navnerumsbegrænsninger",
        "listgrouprights-namespaceprotection-namespace": "Navnerum",
        "listgrouprights-namespaceprotection-restrictedto": "Rettighed(er) der giver brugeren mulighed for at redigere",
+       "listgrants-rights": "Rettigheder",
        "trackingcategories": "Sporingskategorier",
        "trackingcategories-summary": "Denne side viser sporing af de kategorier, som er automatisk udfyldt af MediaWiki-softwaren. Deres navne kan ændres ved at ændre de relevante system-beskeder i {{ns:8}}-navnerummet.",
        "trackingcategories-msg": "Sporingskategori",
        "mw-widgets-dateinput-placeholder-month": "ÅÅÅÅ-MM",
        "mw-widgets-titleinput-description-new-page": "side eksisterer ikke endnu",
        "mw-widgets-titleinput-description-redirect": "omdiriger til $1",
-       "api-error-blacklisted": "Vælg venligst en anden, beskrivende titel.",
        "randomrootpage": "Tilfældig stamside",
        "log-action-filter-block": "Blokeringstype:",
        "log-action-filter-move": "Flyttetype:",
index a0160e8..8f38fa2 100644 (file)
@@ -99,7 +99,7 @@
        "tog-extendwatchlist": "Alle und nicht nur die aktuellsten Änderungen in der Beobachtungsliste anzeigen",
        "tog-usenewrc": "Änderungen auf „Letzte Änderungen“ und der Beobachtungsliste nach Seite gruppieren",
        "tog-numberheadings": "Überschriften automatisch nummerieren",
-       "tog-showtoolbar": "Bearbeiten-Werkzeugleiste anzeigen",
+       "tog-showtoolbar": "Bearbeiten-Werkzeugleiste aktivieren",
        "tog-editondblclick": "Seiten mit Doppelklick bearbeiten",
        "tog-editsectiononrightclick": "Einzelne Abschnitte per Rechtsklick bearbeiten",
        "tog-watchcreations": "Selbst erstellte Seiten und hochgeladene Dateien automatisch beobachten",
        "category-empty": "''Diese Kategorie enthält zurzeit keine Seiten oder Medien.''",
        "hidden-categories": "{{PLURAL:$1|Versteckte Kategorie|Versteckte Kategorien}}",
        "hidden-category-category": "Versteckte Kategorien",
-       "category-subcat-count": "{{PLURAL:$2|Diese Kategorie enthält folgende Unterkategorie:|{{PLURAL:$1|Folgende Unterkategorie ist eine von insgesamt $2 Unterkategorien in dieser Kategorie:|Es werden $1 von insgesamt $2 Unterkategorien in dieser Kategorie angezeigt.}}}}",
+       "category-subcat-count": "{{PLURAL:$2|Diese Kategorie enthält nur folgende Unterkategorie.|Diese Kategorie enthält {{PLURAL:$1|folgende Unterkategorie|die folgende $1 Unterkategorien}}, von $2 insgesamt.}}",
        "category-subcat-count-limited": "Diese Kategorie enthält folgende {{PLURAL:$1|Unterkategorie|$1 Unterkategorien}}:",
-       "category-article-count": "{{PLURAL:$2|Diese Kategorie enthält folgende Seite:|{{PLURAL:$1|Folgende Seite ist eine von insgesamt $2 Seiten in dieser Kategorie:|Es werden $1 von insgesamt $2 Seiten in dieser Kategorie angezeigt.}}}}",
-       "category-article-count-limited": "Folgende {{PLURAL:$1|Seite ist|$1 Seiten sind}} in dieser Kategorie enthalten:",
-       "category-file-count": "{{PLURAL:$2|Diese Kategorie enthält folgende Datei:|{{PLURAL:$1|Folgende Datei ist eine von insgesamt $2 Dateien in dieser Kategorie:|Es werden $1 von insgesamt $2 Dateien in dieser Kategorie angezeigt:}}}}",
+       "category-article-count": "{{PLURAL:$2|Diese Kategorie enthält nur die folgende Seite.|Folgende {{PLURAL:$1|Seite ist| $1 Seiten sind}} in dieser Kategorie, von $2 insgesamt.}}",
+       "category-article-count-limited": "{{PLURAL:$1|Folgende Seite ist|Die folgenden $1 Seiten sind}} in dieser Kategorie enthalten:",
+       "category-file-count": "{{PLURAL:$2|Diese Kategorie enthält nur folgende Datei.|Folgende {{PLURAL:$1|Datei ist|$1 Dateien sind}} in dieser Kategorie, von $2 insgesamt.}}",
        "category-file-count-limited": "Folgende {{PLURAL:$1|Datei ist|$1 Dateien sind}} in dieser Kategorie enthalten:",
        "listingcontinuesabbrev": "(Fortsetzung)",
        "index-category": "Indexierte Seiten",
        "tagline": "Aus {{SITENAME}}",
        "help": "Hilfe",
        "search": "Suche",
+       "search-ignored-headings": " #<!-- diese Zeile nicht verändern --> <pre>\n# Überschriften, die von der Suche ignoriert werden.\n# Diese Änderungen werden wirksam, sobald die Seite mit der Überschrift indexiert wurde.\n# Du kannst die Seitenindexierung erzwingen, indem du einen Nulledit durchführst.\n# Syntax:\n#   * Alles, was einer Raute („#“) bis zum Zeilenende folgt, ist ein Kommentar.\n#   * Jede nicht-leere Zeile ist der exakte zu ignorierende Titel.\nEinzelnachweise\nWeblinks\nSiehe auch\n #</pre> <!-- diese Zeile nicht verändern -->",
        "searchbutton": "Suchen",
        "go": "Ausführen",
        "searcharticle": "Seite",
        "databaseerror-query": "Abfrage: $1",
        "databaseerror-function": "Funktion: $1",
        "databaseerror-error": "Fehler: $1",
-       "transaction-duration-limit-exceeded": "Um eine große Verzögerung in der Datenreplikation zu vermeiden, wurde diese Transaktion abgebrochen. Die Schreibdauer ($1) hat die Grenze von {{PLURAL:$2|einer Sekunde|$2 Sekunden}} überschritten. Falls du viele Objekte auf einmal änderst, versuche stattdessen, die Änderungen auf mehrere Operationen aufzuteilen.",
+       "transaction-duration-limit-exceeded": "Um eine große Verzögerung in der Datenreplikation zu vermeiden, wurde diese Transaktion abgebrochen. Die Schreibdauer ($1) hat die Grenze von $2 Sekunden überschritten. Falls du viele Objekte auf einmal änderst, versuche stattdessen, die Änderungen auf mehrere Operationen aufzuteilen.",
        "laggedslavemode": "<strong>Achtung:</strong> Die angezeigte Seite könnte unter Umständen nicht die letzten Bearbeitungen enthalten.",
        "readonly": "Datenbank gesperrt",
        "enterlockreason": "Bitte gib einen Grund ein, warum die Datenbank gesperrt werden soll und eine Abschätzung über die Dauer der Sperrung",
        "yourpasswordagain": "Passwort wiederholen:",
        "createacct-yourpasswordagain": "Passwort bestätigen",
        "createacct-yourpasswordagain-ph": "Gib das Passwort erneut ein",
-       "remembermypassword": "Mit diesem Browser dauerhaft angemeldet bleiben (maximal $1 {{PLURAL:$1|Tag|Tage}})",
        "userlogin-remembermypassword": "Angemeldet bleiben",
        "userlogin-signwithsecure": "Sichere Verbindung verwenden",
+       "cannotlogin-title": "Die Anmeldung ist nicht möglich.",
+       "cannotlogin-text": "Die Anmeldung ist nicht möglich.",
        "cannotloginnow-title": "Anmeldung nicht erfolgreich",
        "cannotloginnow-text": "Eine Anmeldung ist mit Verwendung von $1 nicht möglich.",
+       "cannotcreateaccount-title": "Die Erstellung von Benutzerkonten ist nicht möglich.",
+       "cannotcreateaccount-text": "Die direkte Erstellung von Benutzerkonten ist auf diesem Wiki nicht aktiviert.",
        "yourdomainname": "Deine Domain:",
        "password-change-forbidden": "Du kannst auf diesem Wiki keine Passwörter ändern.",
        "externaldberror": "Entweder liegt ein Fehler bei der externen Authentifizierung vor oder du darfst dein externes Benutzerkonto nicht aktualisieren.",
        "passwordreset-emailelement": "Benutzername: \n$1\n\nTemporäres Passwort: \n$2",
        "passwordreset-emailsentemail": "Falls diese E-Mail-Adresse mit deinem Benutzerkonto verknüpft ist, wird eine Passwort-Zurücksetzungs-E-Mail versandt.",
        "passwordreset-emailsentusername": "Falls es eine E-Mail-Adresse gibt, die mit diesem Benutzernamen verknüpft ist, wird eine Passwort-Zurücksetzungs-E-Mail versandt.",
-       "passwordreset-emailsent-capture": "Eine Passwortzurücksetzungs-E-Mail wurde versandt, die unten angezeigt wird.",
-       "passwordreset-emailerror-capture": "Die unten angezeigte Passwortzurücksetzungs-E-Mail wurde generiert, allerdings ist der Versand an {{GENDER:$2|den Benutzer|die Benutzerin}} gescheitert: $1",
        "passwordreset-emailsent-capture2": "Die Passwort-Zurücksetzungs-{{PLURAL:$1|E-Mail wurde|E-Mails wurden}} versandt. {{PLURAL:$1|Der Benutzername und das Passwort|Die Liste der Benutzernamen und Passwörter}} wird unten angezeigt.",
        "passwordreset-emailerror-capture2": "Das Senden der E-Mail an {{GENDER:$2|den Benutzer|die Benutzerin}} ist fehlgeschlagen: $1 {{PLURAL:$3|Der Benutzername und das Passwort|Die Liste der Benutzernamen und Passwörter}} wird unten angezeigt.",
        "passwordreset-nocaller": "Es muss ein Rufer angegeben werden",
        "passwordreset-nodata": "Weder ein Benutzername noch eine E-Mail-Adresse wurde angegeben",
        "changeemail": "E-Mail-Adresse ändern oder entfernen",
        "changeemail-header": "Fülle dieses Formular vollständig aus, um deine E-Mail-Adresse zu ändern. Falls du die Zuweisung einer E-Mail-Adresse zu deinem Benutzerkonto entfernen möchtest, lasse beim Übermitteln des Formulars das Feld für die neue E-Mail-Adresse leer.",
-       "changeemail-passwordrequired": "Du musst dein Passwort eingeben, um diese Änderung zu bestätigen.",
        "changeemail-no-info": "Du musst angemeldet sein, um direkt auf diese Seite zugreifen zu können.",
        "changeemail-oldemail": "Aktuelle E-Mail-Adresse:",
        "changeemail-newemail": "Neue E-Mail-Adresse:",
        "content-model-css": "CSS",
        "content-json-empty-object": "Leeres Objekt",
        "content-json-empty-array": "Leeres Array",
+       "deprecated-self-close-category": "Seiten, die ungültige selbstschließende HTML-Tags verwenden",
+       "deprecated-self-close-category-desc": "Die Seite enthält ungültige selbstschließende HTML-Tags wie <code>&lt;b/></code> oder <code>&lt;span/></code>. Das Verhalten dieser Tags wird bald geändert, um mit der HTML5-Spezifikation konsistent zu sein, so dass ihre Verwendung im Wikitext veraltet ist.",
        "duplicate-args-warning": "<strong>Warnung:</strong> [[:$1]] ruft [[:$2]] mit mehr als einem Wert für den Parameter „$3“ auf. Nur der letzte angegebene Wert wird verwendet.",
        "duplicate-args-category": "Seiten, die doppelte Argumente in Vorlagenaufrufen verwenden",
        "duplicate-args-category-desc": "Die Seite enthält Vorlagenaufrufe, die Duplikate von Argumenten verwenden, wie <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> oder <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Anscheinend wurde diese Bearbeitung bereits rückgängig gemacht.",
        "undo-summary": "Änderung $1 von [[Special:Contributions/$2|$2]] ([[User talk:$2|Diskussion]]) rückgängig gemacht.",
        "undo-summary-username-hidden": "Änderung $1 eines versteckten Benutzers rückgängig gemacht.",
-       "cantcreateaccounttitle": "Das Benutzerkonto kann nicht erstellt werden",
        "cantcreateaccount-text": "Die Erstellung eines Benutzerkontos von der IP-Adresse '''($1)''' aus wurde durch [[User:$3|$3]] gesperrt.\n\nGrund der Sperre: ''$2''",
        "cantcreateaccount-range-text": "Das Erstellen von Benutzerkonten von IP-Adressen im Bereich <strong>$1</strong>, der deine IP-Adresse (<strong>$4</strong>) enthält, wurde von [[User:$3|$3]] gesperrt.\n\nDer angegebene Grund von $3 lautet: <em>$2</em>",
        "viewpagelogs": "Logbücher dieser Seite anzeigen",
        "nextn-title": "{{PLURAL:$1|Folgendes Ergebnis|Folgende $1 Ergebnisse}}",
        "shown-title": "Zeige $1 {{PLURAL:$1|Ergebnis|Ergebnisse}} pro Seite",
        "viewprevnext": "Zeige ($1 {{int:pipe-separator}} $2) ($3)",
-       "searchmenu-exists": "<strong>Es gibt eine Seite, die den Namen „[[:$1]]“ hat.</strong> {{PLURAL:$2|0=|Weitere Suchergebnisse anzeigen.}}",
+       "searchmenu-exists": "<strong>Es gibt eine Seite, die den Namen „[[:$1]]“ hat.</strong> {{PLURAL:$2|0=|Weitere Suchergebnisse:}}",
        "searchmenu-new": "<strong>Erstelle die Seite „[[:$1]]“ in diesem Wiki.</strong> {{PLURAL:$2|0=|Siehe auch die über deine Suche gefundene Seite.|Siehe auch die gefundenen Suchergebnisse.}}",
        "searchprofile-articles": "Inhaltsseiten",
        "searchprofile-images": "Multimedia",
        "prefs-rc": "Letzte Änderungen",
        "prefs-watchlist": "Beobachtungsliste",
        "prefs-editwatchlist": "Beobachtungsliste bearbeiten",
-       "prefs-editwatchlist-label": "Einträge auf der Beobachtungsliste bearbeiten:",
+       "prefs-editwatchlist-label": "Einträge auf der Beobachtungsliste:",
        "prefs-editwatchlist-edit": "ansehen und selektiv entfernen",
        "prefs-editwatchlist-raw": "unformatiert bearbeiten",
        "prefs-editwatchlist-clear": "vollständig entfernen",
        "prefs-namespaces": "Namensräume",
        "default": "Voreinstellung",
        "prefs-files": "Dateien",
-       "prefs-custom-css": "Benutzerdefinierte CSS",
+       "prefs-custom-css": "Benutzerdefiniertes CSS",
        "prefs-custom-js": "Benutzerdefiniertes JavaScript",
        "prefs-common-css-js": "Gemeinsames CSS/JavaScript aller Benutzeroberflächen:",
        "prefs-reset-intro": "Du kannst diese Seite verwenden, um die Einstellungen auf die Standards zurückzusetzen.\nDies kann nicht mehr rückgängig gemacht werden.",
        "grant-group-high-volume": "Massenaktivitäten ausführen",
        "grant-group-customization": "Anpassung und Einstellungen",
        "grant-group-administration": "Administrative Aktionen ausführen",
+       "grant-group-private-information": "Auf private Daten über dich zugreifen",
        "grant-group-other": "Verschiedene Aktivitäten",
        "grant-blockusers": "Benutzer sperren und freigeben",
        "grant-createaccount": "Benutzerkonten erstellen",
        "grant-highvolume": "Massenbearbeitungen",
        "grant-oversight": "Benutzer verstecken und Versionen unterdrücken",
        "grant-patrol": "Änderungen an Seiten kontrollieren",
+       "grant-privateinfo": "Auf private Informationen zugreifen",
        "grant-protect": "Seiten schützen und freigeben",
        "grant-rollback": "Änderungen an Seiten zurücksetzen",
        "grant-sendemail": "E-Mails an andere Benutzer versenden",
        "action-applychangetags": "Markierungen zusammen mit deinen Änderungen anzuwenden",
        "action-changetags": "beliebige Markierungen zu einzelnen Versionen und Logbucheinträgen hinzuzufügen und zu entfernen",
        "action-deletechangetags": "Markierungen aus der Datenbank zu löschen",
+       "action-purge": "den Cache dieser Seite zu leeren",
        "nchanges": "$1 {{PLURAL:$1|Änderung|Änderungen}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|seit dem letzten Besuch}}",
        "enhancedrc-history": "Versionsgeschichte",
        "file-thumbnail-no": "Der Dateiname beginnt mit <strong>$1</strong>. Dies deutet auf ein Bild verringerter Größe ''(Minitatur)'' hin.\nBitte prüfe, ob du das Bild in voller Auflösung vorliegen hast und lade dieses unter dem Originalnamen hoch.",
        "fileexists-forbidden": "Unter diesem Namen existiert bereits eine Datei und sie kann nicht überschrieben werden. Bitte gehe zurück und lade die Datei unter einem anderen Namen hoch. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Unter diesem Namen existiert bereits eine Datei im zentralen Medienarchiv.\nWenn du diese Datei trotzdem hochladen möchtest, gehe bitte zurück und ändere den Namen.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Die hochgeladene Datei ist ein exaktes Duplikat der aktuellen Version von <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Die hochgeladene Datei ist ein exaktes Duplikat {{PLURAL:$2|einer älteren Version|von älteren Versionen}} von <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Diese Datei ist ein Duplikat der folgenden {{PLURAL:$1|Datei|$1 Dateien}}:",
        "file-deleted-duplicate": "Eine mit dieser identische Datei ([[:$1]]) wurde früher gelöscht. Sieh das Lösch-Logbuch ein, bevor du sie hochlädst.",
        "file-deleted-duplicate-notitle": "Eine identische Datei wurde kürzlich gelöscht und der Titel wurde unterdrückt.\nDu solltest jemanden fragen, der die Möglichkeit hat, die unterdrückten Dateidaten anzusehen, um die Situation vor dem erneuten Hochladen zu überprüfen.",
        "backend-fail-read": "Die Datei $1 konnte nicht gelesen werden.",
        "backend-fail-create": "Die Datei $1 konnte nicht gespeichert werden.",
        "backend-fail-maxsize": "Die Datei $1 konnte nicht gespeichert werden, da sie größer als {{PLURAL:$2|ein Byte|$2 Bytes}} ist.",
-       "backend-fail-readonly": "Das Speicher-Backend „$1“ befindet sich derzeit im Lesemodus. Der angegebene Grund lautet: <em>$2</em>",
-       "backend-fail-synced": "Die Datei „$1“ befindet sich, innerhalb des internen Speicher-Backends, in einem inkonsistenten Zustand.",
-       "backend-fail-connect": "Es konnte keine Verbindung zum Speicher-Backend „$1“ hergestellt werden.",
-       "backend-fail-internal": "Im Speicher-Backend „$1“ ist ein unbekannter Fehler aufgetreten.",
+       "backend-fail-readonly": "Die Datenbank „$1“ befindet sich derzeit im Lesemodus. Der Grund hierfür ist: <em>$2</em>",
+       "backend-fail-synced": "Die Datei „$1“ befindet sich, innerhalb des internen Datenbanksystems, in einem inkonsistenten Zustand.",
+       "backend-fail-connect": "Es konnte keine Verbindung zur Datenbank „$1“ hergestellt werden.",
+       "backend-fail-internal": "In der Datenbank „$1“ ist ein unbekannter Fehler aufgetreten.",
        "backend-fail-contenttype": "Der Inhaltstyp, der im Pfad „$1“ zu speichernden Datei, konnte nicht bestimmt werden.",
-       "backend-fail-batchsize": "Eine Stapelverarbeitungsdatei, die {{PLURAL:$1|eine Operation|$1 Operationen}} enthält, wurde an das Speicher-Backend gesandt. Die Begrenzung liegt allerdings bei {{PLURAL:$2|einer Operation|$2 Operationen}}.",
+       "backend-fail-batchsize": "Der Datenbank wurde eine Stapelverarbeitungsdatei mit {{PLURAL:$1|einem Verarbeitungsschritt|$1 Verarbeitungsschritten}} übermittelt. Die zulässige Obergrenze liegt indes bei {{PLURAL:$2|einem Verarbeitungsschritt|$2 Verarbeitungsschritten}}.",
        "backend-fail-usable": "Die Datei „$1“ konnte entweder aufgrund eines nicht vorhandenen Verzeichnisses oder wegen unzureichender Berechtigungen weder abgerufen noch gespeichert werden.",
-       "filejournal-fail-dbconnect": "Es konnte keine Verbindung zur Journaldatenbank des Speicher-Backends „$1“ hergestellt werden.",
-       "filejournal-fail-dbquery": "Die Journaldatenbank des Speicher-Backends „$1“ konnte nicht aktualisiert werden.",
+       "filejournal-fail-dbconnect": "Es konnte keine Verbindung zur Journaldatenbank des Datenbanksystems „$1“ hergestellt werden.",
+       "filejournal-fail-dbquery": "Die Journaldatenbank des Datenbanksystems „$1“ konnte nicht aktualisiert werden.",
        "lockmanager-notlocked": "„$1“ konnte nicht entsperrt werden, da keine Sperrung besteht.",
        "lockmanager-fail-closelock": "Die Sperrdatei für „$1“ konnte nicht geschlossen werden.",
        "lockmanager-fail-deletelock": "Die Sperrdatei für „$1“ konnte nicht gelöscht werden.",
        "uploadstash-errclear": "Das Entfernen der vorab gespeicherten Dateien ist fehlgeschlagen.",
        "uploadstash-refresh": "Liste der Dateien aktualisieren",
        "uploadstash-thumbnail": "Vorschaubild ansehen",
+       "uploadstash-exception": "Upload konnte nicht gespeichert werden ($1): „$2“.",
        "invalid-chunk-offset": "Ungültiger Startpunkt",
        "img-auth-accessdenied": "Zugriff verweigert",
        "img-auth-nopathinfo": "Die Angabe PATH_INFO fehlt.\nDer Server ist nicht dafür eingerichtet, diese Information weiterzugeben.\nSie könnte CGI-gestützt sein und kann daher „img_auth“ (Authentifizierung des Dateiaufrufs) nicht unterstützen.\nSiehe auch https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization (englisch).",
        "licenses-edit": "Lizenzoptionen bearbeiten",
        "license-nopreview": "(es ist keine Vorschau verfügbar)",
        "upload_source_url": "(deine ausgewählte Datei von einer gültigen, öffentlich zugänglichen URL)",
-       "upload_source_file": "(deine ausgewählte Datei von deinem Computer)",
+       "upload_source_file": "(die von dir auf deinem Computer ausgewählte Datei)",
        "listfiles-delete": "löschen",
        "listfiles-summary": "Diese Spezialseite listet alle hochgeladenen Dateien auf.",
        "listfiles_search_for": "Suche nach Datei:",
        "filerevert-submit": "Zurücksetzen",
        "filerevert-success": "'''[[Media:$1|$1]]''' wurde auf die [$4 Version vom $2, $3 Uhr] zurückgesetzt.",
        "filerevert-badversion": "Es gibt keine Version der Datei zu dem angegebenen Zeitpunkt.",
+       "filerevert-identical": "Die aktuelle Version der Datei ist bereits mit der ausgewählten Version identisch.",
        "filedelete": "Lösche „$1“",
        "filedelete-legend": "Lösche Datei",
        "filedelete-intro": "Du löschst die Datei '''„[[Media:$1|$1]]“''' inklusive ihrer Versionsgeschichte.",
        "watchnologin": "Du bist nicht angemeldet",
        "addwatch": "Zur Beobachtungsliste hinzufügen",
        "addedwatchtext": "„[[:$1]]“ und die Diskussionsseite wurden zu deiner [[Special:Watchlist|Beobachtungsliste]] hinzugefügt.",
+       "addedwatchtext-talk": "„[[:$1]]“ und ihre dazugehörige Seite wurden zu deiner [[Special:Watchlist|Beobachtungsliste]] hinzugefügt.",
        "addedwatchtext-short": "Die Seite „$1“ wurde zu deiner Beobachtungsliste hinzugefügt.",
        "removewatch": "Von der Beobachtungsliste entfernen",
        "removedwatchtext": "„[[:$1]]“ und die Diskussionsseite wurden von deiner [[Special:Watchlist|Beobachtungsliste]] entfernt.",
+       "removedwatchtext-talk": "„[[:$1]]“ und ihre dazugehörige Seite wurden von deiner [[Special:Watchlist|Beobachtungsliste]] entfernt.",
        "removedwatchtext-short": "Die Seite „$1“ wurde von deiner Beobachtungsliste entfernt.",
        "watch": "Beobachten",
        "watchthispage": "Seite beobachten",
        "rollbacklinkcount-morethan": "Mehr als {{PLURAL:$1|eine Version|$1 Versionen}} zurücksetzen",
        "rollbackfailed": "Zurücksetzen gescheitert",
        "rollback-missingparam": "In der Anfrage fehlen erforderliche Parameter.",
+       "rollback-missingrevision": "Die Versionsdaten konnten nicht geladen werden.",
        "cantrollback": "Die Änderung kann nicht zurückgesetzt werden, da es keine früheren Autoren gibt.",
        "alreadyrolled": "Das Zurücksetzen der Änderungen von [[User:$2|$2]] ([[User talk:$2|Diskussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) an [[:$1]] ist gescheitert, da in der Zwischenzeit ein anderer Benutzer die Seite geändert hat.\n\nDie letzte Änderung stammt von [[User:$3|$3]] ([[User talk:$3|Diskussion]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Die Änderungszusammenfassung lautet: <em>$1</em>.",
        "undeletehistorynoadmin": "Diese Seite wurde gelöscht. Der Löschgrund ist in der Zusammenfassung angegeben,\ngenauso wie Details zum letzten Benutzer, der diese Seite vor der Löschung bearbeitet hat.\nDer aktuelle Text der gelöschten Seite ist nur Administratoren zugänglich.",
        "undelete-revision": "Gelöschte Version von $1 (vom $4 um $5 Uhr), $3:",
        "undeleterevision-missing": "Ungültige oder fehlende Version. Entweder ist der Link falsch oder die Version wurde aus dem Archiv wiederhergestellt oder entfernt.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|Eine Version konnte|$1 Versionen konnten}} nicht wiederhergestellt werden, da ihre <code>rev_id</code> bereits in Verwendung {{PLURAL:$1|ist|sind}}.",
        "undelete-nodiff": "Keine vorhergehende Version vorhanden.",
        "undeletebtn": "Wiederherstellen",
        "undeletelink": "ansehen/wiederherstellen",
        "undeletedrevisions": "{{PLURAL:$1|1 Version wurde|$1 Versionen wurden}} wiederhergestellt",
        "undeletedrevisions-files": "{{PLURAL:$1|1 Version|$1 Versionen}} und {{PLURAL:$2|1 Datei|$2 Dateien}} wurden wiederhergestellt",
        "undeletedfiles": "{{PLURAL:$1|1 Datei wurde|$1 Dateien wurden}} wiederhergestellt",
-       "cannotundelete": "Die Wiederherstellung ist fehlgeschlagen:\n$1",
+       "cannotundelete": "Einige oder alle Wiederherstellungen sind fehlgeschlagen:\n$1",
        "undeletedpage": "'''„$1“''' wurde wiederhergestellt.\n\nIm [[Special:Log/delete|Lösch-Logbuch]] findest du eine Übersicht der gelöschten und wiederhergestellten Seiten.",
        "undelete-header": "Siehe das [[Special:Log/delete|Lösch-Logbuch]] für kürzlich gelöschte Seiten.",
        "undelete-search-title": "Nach gelöschten Seiten suchen",
        "sp-contributions-newbies-sub": "Von neuen Benutzern",
        "sp-contributions-newbies-title": "Benutzerbeiträge von neuen Benutzern",
        "sp-contributions-blocklog": "Sperr-Logbuch",
-       "sp-contributions-suppresslog": "Unterdrückte Benutzerbeiträge",
-       "sp-contributions-deleted": "Gelöschte Beiträge",
+       "sp-contributions-suppresslog": "Unterdrückte {{GENDER:$1|Benutzerbeiträge}}",
+       "sp-contributions-deleted": "Gelöschte {{GENDER:$1|Benutzerbeiträge}}",
        "sp-contributions-uploads": "Hochgeladene Dateien",
        "sp-contributions-logs": "Logbücher",
        "sp-contributions-talk": "Diskussion",
        "revertmove": "zurück verschieben",
        "delete_and_move_text": "Die Seite „[[:$1]]“ existiert bereits.\nMöchtest du diese löschen, um die Seite verschieben zu können?",
        "delete_and_move_confirm": "Ja, Seite löschen",
-       "delete_and_move_reason": "gelöscht, um Platz für die Verschiebung von „[[$1]]“ zu machen",
+       "delete_and_move_reason": "Gelöscht, um Platz für die Verschiebung von „[[$1]]“ zu machen",
        "selfmove": "Ursprungs- und Zielname sind gleich.\nEine Seite kann nicht auf sich selbst verschoben werden.",
        "immobile-source-namespace": "Seiten des „$1“-Namensraums können nicht verschoben werden",
        "immobile-target-namespace": "Seiten können nicht in den „$1“-Namensraum verschoben werden",
        "creditspage": "Seitenzuschreibung",
        "nocredits": "Für diese Seite sind keine Zuschreibungen vorhanden.",
        "spamprotectiontitle": "Spamschutzfilter",
-       "spamprotectiontext": "Der Text, die du speichern willst, wurde vom Spamschutzfilter blockiert.\nDas liegt wahrscheinlich an einem Link auf eine externe Seite.",
+       "spamprotectiontext": "Der Text, den du speichern möchtest, wurde vom Spamschutzfilter blockiert.\nDas liegt wahrscheinlich an einem Link auf eine externe Seite.",
        "spamprotectionmatch": "'''Der folgende Text wurde vom Spamfilter gefunden: ''$1'''''",
        "spambot_username": "MediaWiki-Spam-Säuberung",
        "spam_reverting": "Letzte Version ohne Links zu $1 wiederhergestellt.",
        "pageinfo-article-id": "Seitenkennnummer",
        "pageinfo-language": "Seiteninhaltssprache",
        "pageinfo-content-model": "Seiteninhaltsmodell",
+       "pageinfo-content-model-change": "ändern",
        "pageinfo-robot-policy": "Indizierung durch Suchmaschinen",
        "pageinfo-robot-index": "Erlaubt",
        "pageinfo-robot-noindex": "Nicht erlaubt",
        "mw-widgets-dateinput-placeholder-month": "JJJJ-MM",
        "mw-widgets-titleinput-description-new-page": "Seite ist noch nicht vorhanden",
        "mw-widgets-titleinput-description-redirect": "Weiterleitung nach $1",
-       "api-error-blacklisted": "Bitte einen anderen, aussagekräftigen Titel wählen.",
        "sessionmanager-tie": "Mehrere Anfrageauthentifikationstypen konnten nicht kombiniert werden: $1.",
        "sessionprovider-generic": "$1-Sitzungen",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "cookiebasierten Sitzungen",
        "log-action-filter-newusers": "Typ der Benutzerkontenerstellung:",
        "log-action-filter-patrol": "Kontrolltyp:",
        "log-action-filter-protect": "Schutztyp:",
-       "log-action-filter-rights": "Typ der Rechteänderung",
-       "log-action-filter-suppress": "Unterdrückungstyp",
+       "log-action-filter-rights": "Typ der Rechteänderung:",
+       "log-action-filter-suppress": "Unterdrückungstyp:",
        "log-action-filter-upload": "Hochladetyp:",
        "log-action-filter-all": "Alle",
        "log-action-filter-block-block": "Sperren",
        "linkaccounts-submit": "Benutzerkonten verknüpfen",
        "unlinkaccounts": "Benutzerkonten trennen",
        "unlinkaccounts-success": "Das Benutzerkonto wurde getrennt.",
-       "authenticationdatachange-ignored": "Die Änderung der Authentifizierungsdaten wurde nicht bearbeitet. Vielleicht wurde kein Anbieter konfiguriert?"
+       "authenticationdatachange-ignored": "Die Änderung der Authentifizierungsdaten wurde nicht bearbeitet. Vielleicht wurde kein Anbieter konfiguriert?",
+       "userjsispublic": "Bitte beachten: JavaScript-Unterseiten sollten keine vertraulichen Daten enthalten, da sie von anderen Benutzern eingesehen werden können.",
+       "usercssispublic": "Bitte beachten: CSS-Unterseiten sollten keine vertraulichen Daten enthalten, da sie von anderen Benutzern eingesehen werden können."
 }
index edab232..4de026d 100644 (file)
@@ -23,7 +23,8 @@
                        "Calak",
                        "Macofe",
                        "Matma Rex",
-                       "Kumkumuk"
+                       "Kumkumuk",
+                       "Gırd"
                ]
        },
        "tog-underline": "Bınê gırey de xete bance:",
@@ -75,7 +76,7 @@
        "editfont-monospace": "Terzê nusteyê sabıtcagırewtoği",
        "editfont-sansserif": "Fontê Sans-serifi",
        "editfont-serif": "Font (çêşıdê nuştey) Serif",
-       "sunday": "Kırê (Bazar)",
+       "sunday": "Kırê",
        "monday": "Dışeme",
        "tuesday": "Sêşeme",
        "wednesday": "Çarşeme",
        "oct": "Tşv",
        "nov": "Tşp",
        "dec": "Kan",
-       "january-date": "Çele  $1",
-       "february-date": "Sıbate $1",
-       "march-date": "Adar $1",
-       "april-date": "Nisane $1",
-       "may-date": "Gulane $1",
-       "june-date": "{{PLURAL:$1|1=1ᵉ|$1}} Heziran",
-       "july-date": "Temuze $1",
-       "august-date": "Tebaxe $1",
-       "september-date": "Keşkelun $1",
-       "october-date": "Tışrino Verên $1",
-       "november-date": "Tışrino Peyên $1",
-       "december-date": "Kanun $1",
+       "january-date": "$1 Çele",
+       "february-date": "$1 Sıbate",
+       "march-date": "$1 Adar",
+       "april-date": "$1 Nisane",
+       "may-date": "$1 Gulane",
+       "june-date": "$1 Heziran",
+       "july-date": "$1 Temuze",
+       "august-date": "$1 Tebaxe",
+       "september-date": "$1 Keşkelun",
+       "october-date": "$1 Tışrino Verên",
+       "november-date": "$1 Tışrino Peyên",
+       "december-date": "$1 Kanun",
        "period-am": "AM",
        "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Kategori|Kategoriy}}",
        "category-empty": "''Ena kategoriye de hewna qet nuştey ya zi medya çıniyê.''",
        "hidden-categories": "{{PLURAL:$1|Kategoriya nımıtiye|Kategoriyê nımıtey}}",
        "hidden-category-category": "Kategoriyê nımıtey",
-       "category-subcat-count": "{{PLURAL:$2|Na kategoriya de $1 bınkategoriyay estê.|$2 kategoriyan ra $1 bınkategoriyay asenê.}}",
-       "category-subcat-count-limited": "Na kategoriya de {{PLURAL:$1|ena kategoriya bınên est a|enê $1 kategoriyay bınêni est ê}}.",
+       "category-subcat-count": "{{PLURAL:$2|Na kategoriye de $1 bınkategoriy estê.|$2 kategoriyan ra $1 kategoriyê bınêni asenê.}} \n{| border=\"1\" cellpadding=\"2\" cellspacing=\"0\" align=\"left\" style=\"margin-left:1em; background:peru; border: 1px #aaa solid; border-collapse: collapse; font-size: 95%;\"\n| align=\"center\" |(K) Kategoriye (D) Dosya (P) Peli (M)  Medya\n|}",
+       "category-subcat-count-limited": "Na kategoriye de {{PLURAL:$1|na kategoriya bınêne esta|nê $1 kategoriyê bınêni estê}}.",
        "category-article-count": "{{PLURAL:$2|Na kategoriye de teyna ena pele esta.|Ebe $2 ra pêro piya {{PLURAL:$1|ena pela na kategoriye dera|$1 enê peli na kategoriye derê.}}}}",
        "category-article-count-limited": "{{PLURAL:$1|Pela cêrêne|$1 Pelê cêrêni}} na kategoriye derê.",
-       "category-file-count": "<noinclude>{{PLURAL:$2|Na kategoriya tenya dosyanê cêrênan muhtewa kena.}}</noinclude>\n*Na kategoriya de $2 dosyan ra {{PLURAL:$1|yew dosya tenêka esta| $1 dosyay asenê}}.",
+       "category-file-count": "{{PLURAL:$2|Na kategori tenya dosya ya cêri muhtewa kena.|Na kategori de $2 ra pêro piya {{PLURAL:$1|1 dosya est a|$1 dosyey est ê}}.}}",
        "category-file-count-limited": "{{PLURAL:$1|Dosya cêrêne|$1 Dosyê cêrêni}} na kategoriye derê.",
        "listingcontinuesabbrev": "dewam...",
        "index-category": "Pelê endeksıni",
        "faq": "PZP",
        "faqpage": "Project: PZP",
        "actions": "Hereketi",
-       "namespaces": "Heruna naman",
+       "namespaces": "Heruna nameyan",
        "variants": "Varyanti",
        "navigation-heading": "Menuyê navigasyoni",
        "errorpagetitle": "Xeta",
        "search": "Cı geyre",
        "searchbutton": "Cı geyre",
        "go": "Şo",
-       "searcharticle": "So",
+       "searcharticle": "Şo",
        "history": "Tarixê pele",
        "history_short": "Tarix",
        "updatedmarker": "cıkewtena mına peyêne ra dıme biyo rocane",
-       "printableversion": "Versiyonê nusterin",
-       "permalink": "Gıreyo jûqere",
+       "printableversion": "Asayışê çapkerdışi",
+       "permalink": "Gıreyo daimi",
        "print": "Çap ke",
        "view": "Bıvêne",
-       "view-foreign": "$1'i bıvin",
+       "view-foreign": "$1 de bıvêne",
        "edit": "Bıvurne",
        "edit-local": "Şınasnayışê lokali bıvurne",
        "create": "Vıraze",
        "talkpagelinktext": "werênayış",
        "specialpage": "Pela xısusiye",
        "personaltools": "Hacetê şexsiy",
-       "articlepage": "Pela zerreki bıvêne",
-       "talk": "Werênayış",
+       "articlepage": "Pera zerreki bıvin",
+       "talk": "Vaten",
        "views": "Asayışi",
        "toolbox": "Haceti",
        "userpage": "Pela karberi bıvêne",
        "imagepage": "Pera dosya bıasne",
        "mediawikipage": "Pera mesaci bıasne",
        "templatepage": "Pera şabloni bıasne",
-       "viewhelppage": "Pela peşti bıvêne",
+       "viewhelppage": "Pera peşti bıvin",
        "categorypage": "Pela kategoriya bıasne",
        "viewtalkpage": "Werênayışi bıvêne",
        "otherlanguages": "Zıwananê binan de",
        "redirectedfrom": "($1 ra kırışı yê)",
        "redirectpagesub": "Pela berdışi",
        "redirectto": "Beno hetê:",
-       "lastmodifiedat": "Ena pele tewr peyên roca $2, $1 de biya rocaniye.",
+       "lastmodifiedat": "Per roca $1, sehat $2 de biya anewe.",
        "viewcount": "Ena pele {{PLURAL:$1|rae|$1 rey}} vêniya.",
        "protectedpage": "Pela pawıtiye",
        "jumpto": "Şo be:",
        "copyright": "Zerrekacı $1 bındı not biya.",
        "copyrightpage": "{{ns:project}}:Heqa telifi",
        "currentevents": "Hediseyê rocaneyi",
-       "currentevents-url": "Project:Rocani hadisey",
+       "currentevents-url": "Project:Hediseyê rocaneyi",
        "disclaimers": "Redê mesuliyeti",
        "disclaimerpage": "Project:Reddê mesuliyetê bıngey",
        "edithelp": "Peştdariya vurnayışi",
        "policy-url": "Project:Terzê hereketi",
        "portal": "Portalê cemaeti",
        "portal-url": "Project:Portalê cemaeti",
-       "privacy": "Politikay Nımnayışi",
-       "privacypage": "Project:Xısusiyetê nımtışi",
+       "privacy": "Politikaya nımıteyiye",
+       "privacypage": "Project:Xısusiyetê nımıtışi",
        "badaccess": "Xeta mısadey",
        "badaccess-group0": "Heqa şıma çıniya, karo ke şıma waşt, bıkerê.",
        "badaccess-groups": "No fealiyeto ke şıma waşt, tenya karberanê {{PLURAL:$2|grubi|gruban ra yewi}} rê akerdeyo: $1.",
        "toc": "Sernameyê meselan",
        "showtoc": "bıasene",
        "hidetoc": "bınımne",
-       "collapsible-collapse": "Teng ke",
+       "collapsible-collapse": "Teng kı",
        "collapsible-expand": "Hera ke",
-       "confirmable-confirm": "{{GENDER:$1|Şıma }} do emeli?",
+       "confirmable-confirm": "{{GENDER:$1|Şıma}} pêbawerê?",
        "confirmable-yes": "Eya",
        "confirmable-no": "Nê",
        "thisisdeleted": "Bıvêne ya zi $1 peyser biya?",
-       "viewdeleted": "$1 bıvêne?",
+       "viewdeleted": "$1 bıvin?",
        "restorelink": "{{PLURAL:$1|jew vurnayış besteriya|$1 vurnayışi besteriyaye}}",
        "feedlinks": "Warikerdış:",
        "feed-invalid": "Qeydey cıresnayışê  beğşi nêvêreno.",
        "nstab-main": "Pele",
        "nstab-user": "Pela karberi",
        "nstab-media": "Pela medya",
-       "nstab-special": "Pela xase",
+       "nstab-special": "Pela xısusiye",
        "nstab-project": "Pela proceyi",
        "nstab-image": "Dosya",
        "nstab-mediawiki": "Mesac",
        "cannotdelete-title": "Şıma nêşenê pela \"$1\" besterê",
        "delete-hook-aborted": "Esterıtışi terefê çengeli ra ibtal bi.\nQet tesrih beyan nêbi.",
        "no-null-revision": "Qandé \"$1\" zew rewizyono newe névıraziya.",
-       "badtitle": "Sernameo xırabın",
+       "badtitle": "Sernameyo xırabın",
        "badtitletext": "Sernameyê pela ke şıma waşt, nêvêrd, vengo ya zi zıwano miyanêno ğelet gırêdaye ya zi sernameyê wiki.\nBeno ke, tede yew ya zi zêdê işareti estê ke sernameyan de nêxebetiyenê.",
        "perfcached": "Datay cı ver hazır biye. No semedê ra nıkayin niyo! tewr zaf {{PLURAL:$1|netice|$1 netice}} debêno de",
        "perfcachedts": "Cêr de malumatê nımıteyi esti, demdê newe kerdışo peyın: $1. Tewr zaf {{PLURAL:$4|netice|$4 neticey cı}} debyayo de",
        "querypage-no-updates": "Rocanebiyayışê na pele nıka cadayiyê.\nDayiyi tiya nıka newe nêbenê.",
-       "viewsource": "Çımey bıvêne",
+       "viewsource": "Çemi bıvin",
        "viewsource-title": "Cı geyrayışê $1'i bıvin",
        "actionthrottled": "Kerden peysnaya",
        "actionthrottledtext": "Riyê tedbirê anti-spami ra,  wextê do kılmek de şıma nê fealiyeti nêşkenê zaf zêde bıkerê, şıma ki no hedi viyarna ra.\nÇend deqey ra tepeya reyna bıcerrebnên.",
        "protectedpagetext": "Na per qey nêvuriyayiş ho pawyeno ya zi kerdışe bini.",
-       "viewsourcetext": "To şikinay çımey na pele bıvêne u kopya kerê:",
+       "viewsourcetext": "To şikinay çımey na pele bıvêne u kopya kerê:{{MediaWiki Wesiqe malumat}}",
        "viewyourtext": "Na pela '''Vurnayışê ke kerdê''' re şıma şenê kopya kerê:",
        "protectedinterface": "Na pela qandê nusnerin destegê verri dana u kes xırabin nêqero deye kerda kılit.",
        "editinginterface": "'''İqaz:''' Şıma hayo yew pela ke seba nuşteyê meqalanê cayanê bırnayeyan dana, vurnenê.\nVurnayışê na pele karberanê binan rê serpela karberi kena ke bımocno.\nSeba çarnayışi, yardımê [https://translatewiki.net/wiki/Main_Page?setlang=diq translatewiki.net]i ra procêdoşkerdışi rê diqet kerên.",
        "cannotlogoutnow-title": "Enewke ronıştışo nêracneyêno",
        "welcomeuser": "Ğeyr amey, $1!",
        "welcomecreation-msg": "Hesabê şıma abiyo.\n[[Special:Preferences|{{SITENAME}} vurnayişê tercihanê xo]], xo vir ra mekere.",
-       "yourname": "Nameyê karberi:",
-       "userlogin-yourname": "Nameyê karberi",
+       "yourname": "Namey karberi:",
+       "userlogin-yourname": "Namey karberi",
        "userlogin-yourname-ph": "Nameyê xoyê karberi cı kewe",
        "createacct-another-username-ph": "Nameyê karberi cı kewe",
        "yourpassword": "Parola",
        "yourpasswordagain": "Parola reyna bınusne:",
        "createacct-yourpasswordagain": "Parola tesdiq ke",
        "createacct-yourpasswordagain-ph": "Parola fına cıkewe",
-       "remembermypassword": "Parola mı nê cıgeyrayoği de biya xo viri (seba tewr zêde $1 {{PLURAL:$1|roce|rocan}})",
        "userlogin-remembermypassword": "Mı biya xo viri",
        "userlogin-signwithsecure": "Ebe teqdimkerê asayişın cıkewe",
-       "yourdomainname": "Nameyê şıma yo meydani",
+       "cannotloginnow-title": "Enewke ronıştışo nêabeno",
+       "cannotloginnow-text": "$1 karkerdışa ronıştış akerdış mıkum niyo.",
+       "yourdomainname": "Yewdestê şıma:",
        "password-change-forbidden": "Şıma na wiki de nêşenê parola bıvurnê.",
        "externaldberror": "Ya database de xeta esta ya zi heqê şıma çino şıma no hesab bıvurni.",
        "login": "Cı kewe",
+       "login-security": "Kamiya xo araşt kerê",
        "nav-login-createaccount": "Dekew de / hesab vıraze",
        "userlogin": "Cıkewtış / hesab vıraze",
-       "userloginnocreate": "Cı kewe",
+       "userloginnocreate": "Dekewtış",
        "logout": "Bıveciye",
        "userlogout": "Bıveciye",
        "notloggedin": "Şıma cıkewtış nêvıraşto",
        "nologinlink": "Yew hesab ake",
        "createaccount": "Hesab vıraze",
        "gotaccount": "Hesabê şıma esto? '''$1'''.",
-       "gotaccountlink": "Cı kewe",
+       "gotaccountlink": "Cıkewtış",
        "userlogin-resetlink": "Melumatê cıkewtışi xo vira kerdê?",
        "userlogin-resetpassword-link": "Parola xo kerda xo vira?",
        "userlogin-helplink2": "Heqa qeydbiyayışi de peşti bıgêrên",
        "userlogin-loggedin": "Tı xora namey {{GENDER:$1|$1}} ra kewtê/kewtay cı.\nFormê cêrêni bıgureyne ke namey karberio bin ra cı kewê.",
+       "userlogin-reauth": "Eger ke {{GENDER:$1|$1}} şımayê se xo araşt kerdışirê fına ronıştış akerê.",
        "userlogin-createanother": "Zewbi hesab vıraz",
        "createacct-emailrequired": "Adresa e-postey",
        "createacct-emailoptional": "Adresa e-postey (mecburi niya)",
        "loginlanguagelabel": "Zıwan: $1",
        "suspicious-userlogout": "Waştişê tu ya veciyayişi kebul nibiya cunki ihtimal o ke waştiş yew browser ya zi proksiyê heripiyaye ra ameya.",
        "createacct-another-realname-tip": "Nameyo raştıkên keyfiyo.\nŞıma nameyo xoyo raştıkên ke bımocnê, seba iştırakanê karberi be ney ra istıfade beno.",
-       "pt-login": "Cı kewe",
-       "pt-login-button": "Cı kewe",
+       "pt-login": "Cıkewtış",
+       "pt-login-button": "Cıkewtış",
        "pt-login-continue-button": "Cıkewten rê dewam ke",
        "pt-createaccount": "Hesab vıraze",
        "pt-userlogout": "Veciyayış",
        "passwordreset-emailtext-user": "$1 enê karberi, {{SITENAME}}  ra ($4) teferuatê hesab dê şıma  va wa biyaro xo viri. Karbero ke cêrdeyo {{PLURAL:$3|hesaba|eno hesaba}} ena e-posta adresiya aleqey cı esto:\n\n$2\n\n{{PLURAL:$3|ena parola idaretena|ena parola idareten}} {{PLURAL:$5|jew roc|$5  roca}}rêya.\nEna parolaya deqewe de u xorê ju parolaya newi bıweçine. Parolaya şıma emaya şıma viri se  yana  ena e-posta şıma nê weştase u şıma qayıl niye parolaya xo bıvurnese, ena mesacer peygoş bıkerê.",
        "passwordreset-emailelement": "Nameyê karberi: \n$1\n\nParolaya vêrdiye: \n$2",
        "passwordreset-emailsentemail": "Eke na seba hesabê şıma yew adresa e-posteyê qeydına, yew e-posteyê parola nênkerdışi rışiyeno.",
-       "passwordreset-emailsent-capture": "Yew e-posteyê esterıtışê parolayo ke rışiya, no cêr mocniyayo.",
-       "passwordreset-emailerror-capture": "Yew e-posteyê esterıtışê parolayo ke rışiya, no cêr mocniyayo, ema {{GENDER:$2|karber}}i rê rıştış de mıwefeq nêbi: $1",
        "passwordreset-invalideamil": "Adresê eposta raşt niya",
        "changeemail": "E-posta adresa xo wedarne",
        "changeemail-header": "E-posya adresta hesabdê xo bıvurnê",
        "hr_tip": "Xeta verardiye (teserrufın bıgureyne/bıxebetne)",
        "summary": "Xulasa:",
        "subject": "Mewzu:",
-       "minoredit": "No yew vurnayışo werdiyo",
-       "watchthis": "Ena pele seyr ke",
-       "savearticle": "Pele qeyd ke",
+       "minoredit": "Vurriyayışo werdiyo",
+       "watchthis": "Na pele seyr ke",
+       "savearticle": "Qeyd ke",
+       "savechanges": "Vurnayışan qeyd ke",
        "publishpage": "Perer bıhesırne",
+       "publishchanges": "Vurnayışa vıla ke",
        "preview": "Verqayt",
-       "showpreview": "Verqayti bıasne",
-       "showdiff": "Vurriyayışan bıasne",
+       "showpreview": "Verqayti bıne",
+       "showdiff": "Vurriyayışan bımocne",
        "anoneditwarning": "<strong>İqaz:</strong> Şıma be hesabê xo nêkewtê cı. \nAdresê şımayê IP tarixê vırnayışê na pele de do qeyd bo. Eke şıma <strong>[$1 cıkewê]</strong> ya zi <strong>[$2 hesab vırazê]</strong>, vurnayışê şıma be zewbina kare ra nameyê şıma rê bar beno.",
        "anonpreviewwarning": "\"Şıma be hesabê xo nêkewtê cı. Eke qeyd kerê, adresê şımaê IP tarixê vırnayışê na pele de do qeyd bo.\"",
        "missingsummary": "'''DİQET:''' Şıma jû xulasa nênuşte.\nEke şıma \"{{int:savearticle}}\" reyna bıtıknê, vırnayışê şıma bê xulasa qeyd beno.",
        "accmailtext": "[[User talk:$1|$1]] parolayo ke raşt ameyo şırawiyo na adres $2.\n\nQey na hesabê newe parola, cıkewtış dıma şıma eşkeni na qısım de ''[[Special:ChangePassword|parola bıvurn]]'' bıvurni.",
        "newarticle": "(Newe)",
        "newarticletext": "To yew gıre tıkna be ra yew pela ke hewna çıniya.\nSeba afernayışê pele ra, qutiya metnê cêrêni bıgurene (seba melumati qaytê [$1 pela peşti] ke).\nEke be ğeletine ameya tiya, wa gocega <strong>peyser</strong>i programê xo de bıtıkne.",
-       "anontalkpagetext": "----''No pel, pel o karbero hesab a nêkerdeyan o, ya zi karbero hesab akerdeyan o labele pê hesabê xo nêkewto de. No sebeb ra ma IP adres şuxulneni û ney IP adresan herkes eşkeno bıvino. Eke şıma qayil niye ina bo xo ri [[Special:CreateAccount|yew hesab bıvıraze]] veyaxut [[Special:UserLogin|hesab akere]].''",
-       "noarticletext": "Ena pele de hewna theba çıni yo.\nTı şenê zerreyê pelanê binan de [[Special:Search/{{PAGENAME}}|qandê  sernameyê ena pele cı geyre]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} qeydan miyan de cı geyre],\nya zi [{{fullurl:{{FULLPAGENAME}}|action=edit}} ena pele vıraze]</span>.",
+       "anontalkpagetext": "----''Na per, perêk kı karbero hesab a nêkerdeyan o, ya zi karbero hesab akerdeyan o labele pê hesabê xo nêkewto de. No sebeb ra ma IP adres xebetneno û ney IP adresan herkes nêşeno bıvino. Eke şıma qayil niye ina bo xorê [[Special:CreateAccount|yew hesab bıvıraze]] veya xut [[Special:UserLogin|hesab akere]].''",
+       "noarticletext": "Ena pele de hewna theba çıniyo.\nTı şenê zerreyê pelanê binan de [[Special:Search/{{PAGENAME}}|qandê  sernameyê ena pele cı geyre]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} qeydan miyan de cı geyre],\nya zi [{{fullurl:{{FULLPAGENAME}}|action=edit}} ena pele vıraze]</span>.{{MediaWiki mesaca pera newi}}",
        "noarticletext-nopermission": "Ena pele de hewna theba çıniyo.\nTı şenay zerreyê pelanê binan de [[Special:Search/{{PAGENAME}}|seba sernameyê na pele cı geyre]], ya zi <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} qeydan miyan de cı geyre]</span>, ema destur çıniyo ke na pele vırazê.",
        "missing-revision": "Rewizyonê name dê pela da #$1 \"{{FULLPAGENAME}}\" dı çıniyo.\n\nNo normal de tarix dê pelanê besterneyan dı ena xırabin asena.\nDetayê besternayışi [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} tiya dı] aseno.",
        "userpage-userdoesnotexist": "Hesabê karberi \"<nowiki>$1</nowiki>\" qeyd nêbiyo.\nKerem ke, tı ke wazenay na pele bafernê/bıvurnê, qontrol ke.",
        "template-protected": "(kılit biyo)",
        "template-semiprotected": "(nimey ena pele kılit biya)",
        "hiddencategories": "Ena per de {{PLURAL:$1|1 kategoriyo nımıte|$1 kategoriyê nımıtey}} muhtewa benê:",
-       "edittools": "<!-- Text here will be shown below edit and upload forms. -->",
+       "edittools": "<div id=\"specialcharss\" class=\"toccolours specialchars\" style=\"margin-top:.5em; padding: .3em .5em; font-size: 100%; color:#aaa; text-align:left;\" title=\"{{int:bw-edittools-tooltip}}\">\n<p class=\"specialbasic\" id=\"Standard\">\n'''{{int:bw-edittools-lead-in}}''' \n<charinsert>Á á É é Í í Ó ó Ú ú Ý ý</charinsert> –\n<charinsert>À à È è Ì ì Ò ò Ù ù </charinsert> –\n<charinsert> â Ê ê Î î Ô ô Û û </charinsert> –\n<charinsert>Ä ä Ë ë Ï ï Ö ö Ü ü Ÿ ÿ</charinsert> –\n<charinsert>Æ æ Ø ø Œ œ ẞ ß </charinsert> –\n<charinsert>Å å Ů ů </charinsert> –\n<charinsert>àã Ẽ ẽ ɛ̃ Ĩ ĩ Ñ ñ Õ õ ɔ̃ Ũ ũ </charinsert> –\n<charinsert>Рð Þ þ </charinsert> –\n<charinsert>Ç ç Ģ ģ Ķ ķ Ļ ļ Ņ ņ Ŗ ŗ Ş ş Ţ ţ </charinsert> –\n<charinsert>Ć ć Ĺ ĺ Ń ń Ŕ ŕ Ś ś Ý ý Ź ź </charinsert> –\n<charinsert>Č č Ď ď Ľ ľ Ň ň Ř ř Š š Ť ť Ž ž </charinsert> –\n<charinsert>Ǎ ǎ Ě ě Ǐ ǐ Ǒ ǒ Ǔ ǔ </charinsert> –\n<charinsert>Ā ā Ē ē Ī ī Ō ō Ū ū </charinsert> –\n<charinsert>ǖ ǘ ǚ ǜ </charinsert> –\n<charinsert>Ĉ ĉ Ĝ ĝ Ĥ ĥ Ĵ ĵ Ŝ ŝ Ŵ ŵ Ŷ ŷ </charinsert> –\n<charinsert>Ă ă Ğ ğ Ŭ ŭ </charinsert> –\n<charinsert>Ċ ċ Ė ė Ġ ġ Għ għ İ ı Ż ż </charinsert> –\n<charinsert>Ą ą Ę ę Į į Ų ų </charinsert> –\n<charinsert>Ő ő Ű ű </charinsert> –\n<charinsert>Đ đ Ħ ħ Ł ł Ŀ ŀ </charinsert> –\n<charinsert>Ɖ ɖ Ɛ ɛ Ƒ ƒ Ɣ ɣ Ŋ ŋ Ɔ ɔ Ʋ ʋ </charinsert> -\n<charinsert>Ə ə </charinsert> –\n<charinsert>– — ’</charinsert> –\n<charinsert>~ | ° ¹ ² ³ ⅛ ¼ ⅓ ⅜ ½ ⅝ ¾ ⅔ ⅞ € $ ¥ £ † × ← → ↔ ↑ ± ≠ © ® ™ ‰ «+» ‹+› „+“ „+” ‚+‘ ¡ ¿ …</charinsert> –\n<charinsert>&amp;nbsp; &nbsp; [[Category:+]] #REDIRECT[[+]] {{msg-mw|+|notext=1}} &#33;!FUZZY!! ~~~~  &lt;nowiki>+</nowiki></charinsert>\n<charinsert>ڈ ڑ ٹ </charinsert>\n<charinsert>ټ څ ځ ډ ړ ږ ښ ڼ ؤ ي ې ۍ ئ </charinsert>\n<charinsert>{{{+}}} {{+}} {{subst:+}} <noinclude>+</noinclude></charinsert>\n<charinsert>&lt;!--&nbsp;+&nbsp;--> &lt;br&nbsp;/></charinsert>\n</p></div>",
        "edittools-upload": "-",
        "nocreatetext": "{{SITENAME}}, Pelê neweyi vıraştış re destur çino.\nşıma eşkeni tepiya şêri u eke şıma qayd biyaye yê [[Special:UserLogin|şıma eşkeni hesab akeri]], eke niye [[Special:UserLogin|şıma eşkeni qayd bıbiy]].",
        "nocreate-loggedin": "Desturê şıma çıniyo ke pelanê neweyan vırazê.",
        "parser-unstrip-recursion-limit": "Sinorê limit dê qayış dê ($1) ravêrya",
        "converter-manual-rule-error": "Rehberê zıwan açarnayışi dı xırabin tesbit biya",
        "undo-success": "No vurnayiş tepeye geryeno. pêverronayişêyê cêrıni kontrol bıkeri.",
-       "undo-failure": "Sebayê pêverameyişê vurnayişan karo tepêya gırewtış nêbı.",
+       "undo-failure": "Poxta pëverameyişa vurnayişan ra  peyd grotışë kari në bı",
        "undo-norev": "Vurnayiş tepêya nêgeryeno çunke ya vere cû hewna biyo ya zi ca ra çino.",
        "undo-summary": "Vırnayışê $1'i [[Special:Contributions/$2|$2i]] ([[User talk:$2|Werênayış]]) peyser gırot",
        "undo-summary-username-hidden": "Rewizyona veri $1'i hewada",
-       "cantcreateaccounttitle": "Nêşenay hesab rakerê",
        "cantcreateaccount-text": "Hesabvıraştışê na IP adrese ('''$1''') terefê [[User:$3|$3]] kılit biyo.\n\nSebebo ke terefê $3 ra diyao ''$2''",
-       "viewpagelogs": "Seba na pele rê qeydan bımocne",
+       "viewpagelogs": "Qeydanê na pele bımocne",
        "nohistory": "Verê vurnayışanê na pele çıniyo.",
        "currentrev": "Çımraviyarnayışo rocane",
        "currentrev-asof": "$1 ra tepya mewcud weziyeta pela",
        "page_first": "verên",
        "page_last": "peyên",
        "histlegend": "Ferqê weçinıtışi: Qutiya versiyonan seba têversanayış işaret ke û dest be ''enter''i ya zi gocega cêrêne ro ne.<br />\nCedwel: <strong>({{int:ferq}})</strong> = ferqê verziyonê peyêni, <strong>({{int:peyên}})</strong> = ferqê versiyonê verêni, <strong>{{int:q}}</strong> = vurnayışo werdi.",
-       "history-fieldset-title": "Tarixi bıvêne",
+       "history-fieldset-title": "Çımberz verori",
        "history-show-deleted": "Tenya esterıtey",
        "histfirst": "Verênêr",
        "histlast": "Peyênêr",
        "historysize": "({{PLURAL:$1|1 bayt|$1 bayti}})",
        "historyempty": "(veng)",
        "history-feed-title": "Tarixê çımraviyarnayışi",
-       "history-feed-description": "Wiki de tarixê çımraviyarnayışê na pele",
+       "history-feed-description": "Wiki de tarixê çım ra viyarnayışë na perer",
        "history-feed-item-nocomment": "$1 miyanê $2i de",
        "history-feed-empty": "Pela cıgeyrayiye çıniya.\nBeno ke ena esteriya, ya zi namê cı vuriyo.\nSeba pelanê muhimanê newan [[Special:Search|cıgeyrayışê wiki de]] bıcerebne.",
        "history-edit-tags": "Etiketa weçinaye rewizyoni timar ke",
        "rev-suppressed-unhide-diff": "Nê Timarkerdışi ra yewi '''çap biyo'''.\n[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} rocaneyê vındertışi] de teferru'ati esti.\nEke şıma serkari u devam bıkeri [$1 no vurnayiş şıma eşkeni bıvini].",
        "rev-deleted-diff-view": "Jew timarkerdışê ena versiyon '''wedariyayo''.\nÎdarekarî şenê ena versiyon bivîne; belki tiya de [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} wedarnayişî] de teferruat esto.",
        "rev-suppressed-diff-view": "Jew timarkerdışê ena versiyon '''Ploxneyış'' biyo.\nÎdarekarî eşkeno ena dif bivîne; belki tiya de [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ploxnayış] de teferruat esto.",
-       "rev-delundel": "bıasne/bınımne",
+       "rev-delundel": "bımocne/bınımne",
        "rev-showdeleted": "bıasene",
        "revisiondelete": "Çımraviyarnayışan bestere/peyser biya",
        "revdelete-nooldid-title": "Çımraviyarnayışo waşte nêvêreno",
        "mergehistory-box": "revizyonê pelanî yew bike:",
        "mergehistory-from": "Pela çımey:",
        "mergehistory-into": "Pela destinasyonî",
-       "mergehistory-list": "tarixê vurnayîşî ke eşkeno yew bi.",
+       "mergehistory-list": "Tarixê vurnayışiyo yewbiyaye",
        "mergehistory-merge": "[[:$1]] qey ney revizyonê cêrini [[:$2]] şıma ekeni piyawani. Benatê wexto muwaqqet de piyayanayişê rezizyonan de tuşa radyo bıxebitne.",
        "mergehistory-go": "Yew bıyaye vurriyayışa bıasne",
        "mergehistory-submit": "revizyonî yew bike",
        "mergehistory-empty": "Revizyonî yew nibenê.",
        "mergehistory-done": "$1 ra $3 {{PLURAL:$3|revizyon|revizyoni}} [[:$2]] de {{PLURAL:$3|biyo|biyê}} têmiyan.",
        "mergehistory-fail": "Tarixê pele yew nibeno, ma rica kenê ke pel u wext control bike.",
+       "mergehistory-fail-bad-timestamp": "Mora zemani ravêrdi niya",
+       "mergehistory-fail-invalid-source": "Çime per ravêrdi niya",
+       "mergehistory-fail-invalid-dest": "Hedef per ravêrdi niya",
+       "mergehistory-fail-permission": "Verori yewkerdışi rê mısade çıni yo.",
+       "mergehistory-fail-self-merge": "Çıne u hedef pera zey pê yê",
        "mergehistory-no-source": "Pela çımeyê $1 çıniya.",
        "mergehistory-no-destination": "Pela destinasyoni $1 çini yo.",
        "mergehistory-invalid-source": "Pela çime gani yew seroğê raşt biy.",
        "mergelog": "Qeydé zew kerdışi",
        "revertmerge": "Abırnê",
        "mergelogpagetext": "Cêr de yew liste esta ke mocnena ra, raya tewr peyêne kamci pela tarixi be a bine ra şanawa pê.",
-       "history-title": "Revizyona pela \"$1\"",
+       "history-title": "Tarixê çımraviyarnayışê \"$1\"",
        "difference-title": "Pela \"$1\" ferqê çım ra viyarnayışan",
        "difference-title-multipage": "Ferkê pelan dê \"$1\" u \"$2\"",
        "difference-multipage": "(Ferqê pelan)",
-       "lineno": "Xeta $1i:",
+       "lineno": "Xeta $1:",
        "compareselectedversions": "Rewizyonanê weçineyan pêver ke",
        "showhideselectedversions": "Revizyonanê weçinıtan bımocne/bınımne",
-       "editundo": "peyser bıgê",
+       "editundo": "peyser biya",
        "diff-empty": "(Babetna niyo)",
        "diff-multi-sameuser": "(Terefê eyni karberi ra {{PLURAL:$1|yew revizyono miyanên nêmocno|$1 revizyonê miyanêni nêmocnê}})",
        "diff-multi-otherusers": "(Terefê {{PLURAL:$2|yew karberi|$2 karberan}} ra {{PLURAL:$1|yew revizyono miyanên nêmocno|$1 revizyonê miyanêni nêmocnê}})",
        "next-page": "Pela peyên",
        "prevn-title": "$1o verên  {{PLURAL:$1|netice|neticeyan}}",
        "nextn-title": "$1o ke yeno {{PLURAL:$1|netice|neticey}}",
-       "shown-title": "bimocne $1î  {{PLURAL:$1|netice|neticeyan}} ser her pel",
+       "shown-title": "Herg per sero $1 {{PLURAL:$1|netici|netica}} bıasne",
        "viewprevnext": "($1 {{int:pipe-separator}} $2) ($3) bıvênên",
        "searchmenu-exists": "''Ena 'Wikipediya de ser \"[[:$1]]\" yew pel esto'''",
        "searchmenu-new": "<strong>Na wiki de pela \"[[:$1]]\" vıraze!</strong> {{PLURAL:$2|0=|Sewbina pela ke şıma geyrayê cı aye bıvênê.|Yew zi neticanê cıgeyrayışê xo bıvênê.}}",
        "searchprofile-images": "Multimedya",
        "searchprofile-everything": "Heme çi",
        "searchprofile-advanced": "Raverşiyaye",
-       "searchprofile-articles-tooltip": "$1 de cı geyré",
-       "searchprofile-images-tooltip": "Dosya cı geyr",
+       "searchprofile-articles-tooltip": "$1 de cı geyre",
+       "searchprofile-images-tooltip": "Dosya cı geyre",
        "searchprofile-everything-tooltip": "Tedeesteyan hemine cı geyre (pelanê mınaqeşeyi zi tey)",
-       "searchprofile-advanced-tooltip": "qe cayê nimeyî bigêre",
+       "searchprofile-advanced-tooltip": "Cayê nameyanê xısusiyan de cı geyre",
        "search-result-size": "$1 ({{PLURAL:$2|1 çeku|$2 çekuy}})",
        "search-result-category-size": "{{PLURAL:$1|1 eza|$1 ezayan}} ({{PLURAL:$2|1 kategoriyê bini|$2 kategirayanê binan}}, {{PLURAL:$3|1 dosya|$3 dosyayan}})",
        "search-redirect": "($1 ra ardış)",
        "search-category": "(kategori $1)",
        "search-file-match": "(zerreyê dosya yewbini gêno)",
        "search-suggest": "To va: $1",
-       "search-rewritten": "Neticey $ ra asenê.  Herunda ney wa neticey $2 ra bıasê",
+       "search-rewritten": "Neticey $ ra asenê.  Herunda ney wa neticey $2 ra bıasê?",
        "search-interwiki-caption": "Proceyê bıray",
        "search-interwiki-default": "$1 ra neticey:",
        "search-interwiki-more": "(véşi)",
        "showingresults": "#<strong>$2</strong> netican ra {{PLURAL:$1|<strong>1</strong> netice cêr dero|<strong>$1</strong> neticey cêr derê}}.",
        "showingresultsinrange": "{{PLURAL:$1|<strong>1</strong> netice|<strong>$1</strong> neticey}} be mabeynê #<strong>$2</strong> ra be #<strong>$3</strong> cêr asenê.",
        "search-showingresults": "{{PLURAL:$4|Netice <strong>$1</strong> be <strong>$3</strong>|Neticeyi <strong>$1 - $2</strong> be <strong>$3</strong>}}",
-       "search-nonefound": "Zey perskerdışê şıma netice nêvêniya.",
+       "search-nonefound": "Zey perskerdışê şıma peyniye çıniya.",
        "search-nonefound-thiswiki": "Ena sita dı zey waşten da şıma theba nêvineya",
        "powersearch-legend": "Cıgeyrayışo hera",
        "powersearch-ns": "Cayanê nameyan de cıgeyrayış:",
        "skin-preview": "Verqayt",
        "datedefault": "Tercih çıniyo",
        "prefs-labs": "Xacetê labs",
-       "prefs-user-pages": "Pela Karberi",
+       "prefs-user-pages": "Pelê karberi",
        "prefs-personal": "Pela karberi",
        "prefs-rc": "Vurriyayışê peyêni",
        "prefs-watchlist": "Lista seyrkerdışi",
        "prefs-help-recentchangescount": "Ney de vurnayışê peyêni, tarixê pelan u cıkewteni asenê.",
        "prefs-help-watchlist-token2": "Na pawıtış nımnayi kılta listada şımaya.\nOke kıliti zano şeno listeya tamaşann bıvino. Poğta coy ra kesiya me hesırne. \n[[Special:ResetTokens|Na kıliti reset kerdışi re tiyay bıploğne]].",
        "savedprefs": "Tecihê şıma qeyd biy.",
+       "savedrights": "Qandé {{GENDER:$1|$1}} heqê karberi qeyd  biye.",
        "timezonelegend": "Warey saete:",
        "localtime": "saeta mehelliye:",
        "timezoneuseserverdefault": "Zey karkerdışê Wiki ($1)",
        "badsig": "Îmzayê tu raşt niyo.\nEtiketê HTMLî kontrol bike.",
        "badsiglength": "İmzaya şıma zaf derga.\nA gani be $1 {{PLURAL:$1|karakter|karakteran}} ra zêde mebo.",
        "yourgender": "Şeklê xitabi?",
-       "gender-unknown": "Ez detay nivana",
+       "gender-unknown": "Çımhal tı kerdê etiket, Nusner do çekuya Nerimaki cinsiyeti de şeno bıkar no",
        "gender-male": "Perané wiki camérd deyne ezo vırnena",
        "gender-female": "Perané wiki cıni deyne eza vırnena",
        "prefs-help-gender": "Na tercih keyfiya.\nNa nustenek ercana qısan de qandé grameri karneyéna, na malumater herkes şeno bıvino .",
        "right-passwordreset": "E-postayanê parola reset kerdışa vineno",
        "right-managechangetags": "[[Special:Tags|Etiketi]] vıraz u aktiv (me)ke",
        "right-applychangetags": "[[Special:Tags|Etiketa]]  vurnayışana piya dezge fi.",
+       "grant-group-page-interaction": "Peran na tesiri",
+       "grant-group-file-interaction": "Medya na tesiri",
+       "grant-group-watchlist-interaction": "Lista da xoya tesir",
+       "grant-group-email": "e-poste bırışe",
+       "grant-group-high-volume": "Performansa aktiviteya vengê berzi",
+       "grant-group-customization": "Şexsi kerdış u tercihi",
+       "grant-group-administration": "Performans hereketa idarey",
+       "grant-group-other": "Enwayi babet aktivitey",
+       "grant-blockusers": "Karberi men ke u meni wedarne",
+       "grant-createaccount": "Hesab vıraze",
+       "grant-createeditmovepage": "Perer vırazê, bıvurnê u berê",
+       "grant-delete": "Besternayış, revizyon  u qeydé peran",
+       "grant-editinterface": "Canameyê MediaWiki u CSS/javScripta karberi bıvurnê",
+       "grant-editmycssjs": "CSS/JavaScripta karberiya xo bıvurnê",
+       "grant-editmyoptions": "Tercihanê xo bıvurne",
+       "grant-editmywatchlist": "Listeyseyran de xo bıvırne",
+       "grant-editpage": "Peran bıvurnê",
+       "grant-editprotected": "Srar bıyaye peran bıvurnê",
+       "grant-highvolume": "Vengê berzi dayış",
+       "grant-oversight": "Karberan u ploğyayê revizyona bınımn",
+       "grant-patrol": "Perer de vurnayışa çımra ravyarn",
+       "grant-protect": "Şeveknayış u wedarıtışê şeveknayışê pelan",
+       "grant-sendemail": "Karberanê binan rê e-posta bırışê",
+       "grant-uploadeditmovefile": "Dosyaya bar ke, bıvurnê  yana berê",
+       "grant-uploadfile": "Dosyaya newi bar ke",
+       "grant-basic": "Heqê basiti",
+       "grant-viewdeleted": "Besteryaya peran u dosyaya bıasne",
+       "grant-viewmywatchlist": "Lista serykerdışê xo bıvêne",
        "newuserlogpage": "Cıkewtışê hesabvıraştışi",
        "newuserlogpagetext": "Ena log de viraştişê karberî esta.",
        "rightslog": "Qeydê heqanê karberi",
        "rightslogtext": "Ena listeyê loganê ke heqqa karbaranî mucneno.",
        "action-read": "ena pela wanayış",
        "action-edit": "ena pela bıvurnê",
-       "action-createpage": "pelan vıraze",
+       "action-createpage": "na perer bıvıraz",
        "action-createtalk": "pelanê werênayışi bıvıraze",
        "action-createaccount": "hesabê nê karberi bıvıraze",
+       "action-autocreateaccount": "nê hesabê karberiyê teberi otomatik vıraze",
+       "action-history": "tarixê na pele bıvêne",
        "action-minoredit": "nê vurnayışi be qıckek işaret ke",
        "action-move": "ena pele bere",
        "action-move-subpages": "ena pele, u pelanê daê bınênan bere",
        "action-move-rootuserpages": "pelanê karberiyê bıngeyan bere",
+       "action-move-categorypages": "Pera kategoriyer ber",
        "action-movefile": "ena dosya bere",
        "action-upload": "ena dosya bar ke",
        "action-reupload": "dosyayê ke database de esto ser ey binuse",
        "action-viewmywatchlist": "Listeyseyran de xo bıvin",
        "action-viewmyprivateinfo": "Xısusi tercihane xo bıvin",
        "action-editmyprivateinfo": "Xısusi malumate xo bıvurne",
-       "nchanges": "$1 {{PLURAL:$1|fın vurna|fıni vurna}}",
-       "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ra yok wazino}}",
+       "action-editcontentmodel": "Zerrekê modela yu perer timar ke",
+       "action-managechangetags": "Vıraz u etiketa aktiv (me) ke",
+       "action-applychangetags": "Vurnayışana piya etiket kerdışi zi dezge fi",
+       "action-purge": "Ane perer newe ke",
+       "nchanges": "$1 {{PLURAL:$1|vurnayış|vurnayışi}}",
+       "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ziyaretê peyêni ra nata}}",
        "enhancedrc-history": "tarix",
        "recentchanges": "Vurriyayışê peyêni",
        "recentchanges-legend": "Tercihê vurnayışanê peyênan",
-       "recentchanges-summary": "Ena pele de wiki sero vurnayışanê peyênan teqib ke.",
+       "recentchanges-summary": "Wiki sero vurriyayışê peyêni asenê.",
        "recentchanges-noresult": "Goreyê kriteranê kıfşkerdeyan ra qet yew vurnayış nêvêniya.",
        "recentchanges-feed-description": "Ena feed dı vurnayişanê tewr peniyan teqip bık.",
-       "recentchanges-label-newpage": "Enê vurnayışi ra yew pela newiye vıraziye",
-       "recentchanges-label-minor": "No yew vurnayışo werdiyo",
+       "recentchanges-label-newpage": "Enê vurnayışi ra yu pera newi vıraziya ya",
+       "recentchanges-label-minor": "Vurriyayışo werdiyo",
        "recentchanges-label-bot": "Eno vurnayış terefê yew boti ra vıraziyo",
        "recentchanges-label-unpatrolled": "Eno vurnayış hewna dewriya nêbiyo",
        "recentchanges-label-plusminus": "Ebadê pele de bazê bayti de vayeyê cı",
-       "recentchanges-legend-heading": "<strong>Kıtabek:</strong>",
-       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|Lista pelanê neweyan]] zi bıvêne)",
+       "recentchanges-legend-heading": "<strong>Kıtabekê Vurriyayışê peyêni:</strong>",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} Şıma şenê ([[Special:NewPages|Listey peranê  newan]] zi bıvinê)",
        "recentchanges-legend-plusminus": "''(±123)''",
-       "recentchanges-submit": "Bıasne",
-       "rcnotefrom": "Cêr de <strong>$2</strong> ra nata vurnayışiyê asenê (tewr vêşi <strong>$1</strong> asenê).",
+       "recentchanges-submit": "Bımocne",
+       "rcnotefrom": "Cêr de <strong>$2</strong> ra nata {{PLURAL:$5|vurnayışiyê}} asenê (tewr vêşi <strong>$1</strong> asenê) <strong>$3, $4</strong>",
        "rclistfrom": "$3 $2 ra tepiya vurnayışanê neweyan bımocne",
-       "rcshowhideminor": "vurnayışê werdiyi $1",
-       "rcshowhideminor-show": "Bıasne",
+       "rcshowhideminor": "vurriyayışê werdi $1",
+       "rcshowhideminor-show": "Bımocne",
        "rcshowhideminor-hide": "Bınımne",
        "rcshowhidebots": "botan $1",
-       "rcshowhidebots-show": "Bıasene",
+       "rcshowhidebots-show": "Bımocne",
        "rcshowhidebots-hide": "Bınımne",
        "rcshowhideliu": "karberê qeydbiyayeyi $1",
-       "rcshowhideliu-show": "Bıasne",
+       "rcshowhideliu-show": "Bımocne",
        "rcshowhideliu-hide": "Bınımne",
        "rcshowhideanons": "karberê bênameyi $1",
-       "rcshowhideanons-show": "Bıasene",
+       "rcshowhideanons-show": "Bımocne",
        "rcshowhideanons-hide": "Bınımne",
        "rcshowhidepatr": "$1 vurnayışê ke dewriya geyrayê",
-       "rcshowhidepatr-show": "Bıasne",
+       "rcshowhidepatr-show": "Bımocne",
        "rcshowhidepatr-hide": "Bınımne",
        "rcshowhidemine": "vurnayışanê mı $1",
-       "rcshowhidemine-show": "Bıasne",
+       "rcshowhidemine-show": "Bımocne",
        "rcshowhidemine-hide": "Bınımne",
        "rcshowhidecategorization": "kategorizasyonê pele $1",
-       "rcshowhidecategorization-show": "Bıasne",
+       "rcshowhidecategorization-show": "Bımocne",
        "rcshowhidecategorization-hide": "Bınımne",
-       "rclinks": "Peyniya $2 rocan de $1 vurnayışan bımocne <br />$3",
+       "rclinks": "Peyniya $2 rocan de $1 vurriyayışan ra <br />$3 asenê",
        "diff": "ferq",
        "hist": "verên",
        "hide": "Bınımne",
-       "show": "Bıasne",
+       "show": "Bımocne",
        "minoreditletter": "q",
        "newpageletter": "N",
        "boteditletter": "b",
        "recentchangeslinked-summary": "Lista cêrêne, pela bêlikerdiye rê (ya zi karberanê kategoriya bêlikerdiye rê) pelanê gırêdayoğan de lista de vurnayışê peyênana.\n[[Special:Watchlist|Lista şımaya seyrkedışi de]] peli be nuşteyo '''qolınd''' bêli kerdê.",
        "recentchangeslinked-page": "Nameyê pele:",
        "recentchangeslinked-to": "Heruna pela ke yena dayene, vurnayışanê pelanê ke daye ra gırêdayiyê inan bımocne",
-       "recentchanges-page-added-to-category": "[[:$1]] kerd be kategoriye",
+       "recentchanges-page-added-to-category": "[[:$1]] kerd kategoriye miyan",
        "recentchanges-page-removed-from-category": "[[:$1]] kategoriye ra vet",
+       "autochange-username": "MediaWiki vurnayışo otomatik",
        "upload": "Dosya bar ke",
        "uploadbtn": "Dosya bar ke",
        "reuploaddesc": "Barkerdışi iptal ke u peyser şo formê barkerdışi",
        "upload-http-error": "Yew ğeletê HTTPî biyo: $1",
        "upload-copy-upload-invalid-domain": "Na domain ra kopyayê barkerdışanê nêbenê.",
        "upload-dialog-title": "Dosya bar ke",
-       "upload-dialog-button-cancel": "Bıtexelne",
+       "upload-dialog-button-cancel": "Bıterkın",
        "upload-dialog-button-done": "Temam",
        "upload-dialog-button-save": "Bışevekne",
        "upload-dialog-button-upload": "Bar ke",
        "backend-fail-read": "Na \"$1\" dosya nê wanêna",
        "backend-fail-create": "Dosyay $1 nê vırazıyê",
        "backend-fail-maxsize": "Dosyay $1 aya nênusneyêna feqet gırdeya cı {{PLURAL:$2|bayta|$2 bayto}}",
-       "backend-fail-readonly": "Depo kerdışê \"$1\" enewke salt wanêno.Sebebê cı zi:\"''$2''\"",
+       "backend-fail-readonly": "Depo kerdışê \"$1\" enewke salt wanêno.Sebebê cı zi:<em>$2</em>",
        "backend-fail-synced": "Dosyay \" $1 \" miyan de depo kerdışeyda cıdı pê nêtepıştey esta",
        "backend-fail-connect": "Depo kerdışê \"$1\" peyni de nêgrêdeya.",
        "backend-fail-internal": "Depo kerdışê \"$1\" peyni de ju xırabin vıcyê.",
        "uploadstash-badtoken": "Karkerdışê cı nêbı, muhtemelen desture şımayê timarkerdışi zeman do şıma ravêrdo. Fına bıcerbnê.",
        "uploadstash-errclear": "Besternayışê dosyayan nêbı",
        "uploadstash-refresh": "Listanê dosyayan aneweke",
+       "uploadstash-thumbnail": "asayışê qıckeki bıvêne",
        "invalid-chunk-offset": "Ofseto nêravyarde",
        "img-auth-accessdenied": "Cıresnayış vındarnayo.",
        "img-auth-nopathinfo": "PATH_INFO kemiyo.\nTeqdimkerê şıma seba ravurnayışê nê melumati eyar nêkerdo.\nBeno ke be CGI-bıngeyın bo u img_auth rê desteg nêbeno.\nhttps://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization Selahiyetê resımi bıvêne.",
        "license": "Lisans:",
        "license-header": "Lisansdayış",
        "nolicense": "Theba nêweçineya",
+       "licenses-edit": "Weçenega lisansi bıvurnê",
        "license-nopreview": "(verqeydî çin o)",
-       "upload_source_url": "(yew URLê raştî, şar rê akerde yo)",
-       "upload_source_file": "(komputerê ti de yew dosya)",
+       "upload_source_url": "(to yew ravêrde dosya weçina,   şar rê akerde URL yo)",
+       "upload_source_file": "(komputer ra dos yu dosya weçina)",
+       "listfiles-delete": "bestere",
        "listfiles-summary": "Na pera bağsiya; heme resima bar biyayeyan mocnena.",
        "listfiles_search_for": "Qe nameyê medyayî bigêre:",
+       "listfiles-userdoesnotexist": "Hesabê karberi \"$1\" qeyd nêbiyo.",
        "imgfile": "dosya",
        "listfiles": "Lista Dosya",
        "listfiles_thumb": "Resmo qıckek",
        "listfiles-latestversion-yes": "Eya",
        "listfiles-latestversion-no": "Nê",
        "file-anchor-link": "Dosya",
-       "filehist": "Ravêrdê dosya",
+       "filehist": "Tarixê dosya",
        "filehist-help": "Seba diyayışê viyarteyê dosya tarixê ke qısımê tarix/zemani derê inan bıtıkne.",
        "filehist-deleteall": "pêro bestere",
        "filehist-deleteone": "bestere",
        "filerevert-legend": "Dosya raçarne",
        "filerevert-intro": "Ti ho ena dosyayê '''[[Media:$1|$1]]'''î  [$4 versiyonê $3, $2] rê reyna anî.",
        "filerevert-comment": "Sebeb:",
-       "filerevert-defaultcomment": "Versiyonê $2, $1 rê reyna ard",
+       "filerevert-defaultcomment": "Versiyonê $2, $1 rê reyna ard ($3)",
        "filerevert-submit": "Reyna biyere",
        "filerevert-success": "'''[[Media:$1|$1]]''' peyser çarna ra [versiyonanê $4, $3, $2].",
        "filerevert-badversion": "Vesiyonê lokalê verniyê eno dosya pê ena pulêwext de çin o.",
        "mimesearch": "MIME bigêre",
        "mimesearch-summary": "Na pele, dosyayanê MIME goreyê tewran ra parzûn kena. Cıkewtış: tewrê zerreki/tewro bınên ya zi tewrê zerreki/*, nımune: <code>image/jpeg</code>.",
        "mimetype": "Babetê NIME",
-       "download": "bar ke",
+       "download": "Bar ke",
        "unwatchedpages": "Pelanê seyrnibiyeyî",
        "listredirects": "Listeya Hetenayışan",
        "listduplicatedfiles": "Lista dosyeyanê ke kopyaya cı vêniyena",
+       "listduplicatedfiles-entry": "[[$3|{{PLURAL:$2|kapyay|$2 kopyey}}]] dosya da [[:File:$1|$1]]'i est a",
        "unusedtemplates": "Şablonê ke nêguriyenê",
        "unusedtemplatestext": "no pel, {{ns:template}} pelê ke pelê binan de nêaseni, ninan keno.",
        "unusedtemplateswlh": "linkanê binî",
        "randomincategory-invalidcategory": "\"$1\" yew nameyê kategoriya vêrdiye niyo.",
        "randomincategory-nopages": "Kategori da [[:Category:$1|$1]] de qet  per çıniya.",
        "randomincategory-category": "Kategoriye:",
+       "randomincategory-legend": "Kategori ra raştamayi per",
        "randomincategory-submit": "Şo",
        "randomredirect": "Serçarnayışo rastameye",
        "randomredirect-nopages": "Cayê nameyê \"$1\" de serşıkıtışi çıniyê.",
        "protectedpages-unknown-performer": "Karbero nêzanaye",
        "protectedtitles": "Sernameyê pawıteyi",
        "protectedtitlesempty": "pê ney parametreyan sernuşteyê pawite çinê",
+       "protectedtitles-submit": "Sernameyan bımocne",
        "listusers": "Listeyê Karberan",
        "listusers-editsonly": "Teyna karberan bimucne ke ey nuştê",
        "listusers-creationsort": "goreyê wextê vıraştışi rêz ker",
        "newpages-submit": "Bıasene",
        "newpages-username": "Nameyê karberi:",
        "ancientpages": "Pelê kehenêri",
-       "move": "Wegi",
-       "movethispage": "Ena peler wegi",
+       "move": "Bıkırışe",
+       "movethispage": "Ena pele bıkırışe",
        "unusedimagestext": "Enê dosyey estê, feqet zerrey yew pele de wedardey niyê.\nXo vira mekerê ke, sıteyê webiê bini şenê direkt ebe URLi yew dosya ra gırê bê, u wına şenê verba gurênayışo feal de tiya hewna lista bê.",
        "unusedcategoriestext": "Kategoriyê ke cêr derê, nê bıbê zi, terefê qet madeyan ya zi kategoriyan ra nêgureniyenê.",
        "notargettitle": "Hedef çini yo",
        "apihelp-no-such-module": "Modulê \"$1\" çıniyo.",
        "apisandbox": "API qumdor",
        "apisandbox-fullscreen": "Panela hera kerdışi",
+       "apisandbox-unfullscreen": "Pele bımocne",
        "apisandbox-submit": "Bıwazê",
        "apisandbox-reset": "Bestere",
        "apisandbox-retry": "Fına",
        "apisandbox-results": "Neticey",
        "apisandbox-sending-request": "API waştış rışêno...",
        "apisandbox-request-url-label": "URL waştış:",
-       "apisandbox-request-time": "Demê waştışi: $1",
+       "apisandbox-request-time": "Demê waştışi: {{PLURAL:$1|$1 ms}}",
        "booksources": "Çımeyê kıtaban",
        "booksources-search-legend": "Seba çımeyanê kıtaban cı geyre",
        "booksources-isbn": "ISBN:",
        "checkbox-select": "Weçinaye: $1",
        "checkbox-all": "Pêro",
        "checkbox-none": "Temam",
-       "checkbox-invert": "Doç kerdış",
+       "checkbox-invert": "Rageyre",
        "allpages": "Peli pêro",
        "nextpage": "Pela badê cû ($1)",
        "prevpage": "Pela verêne ($1)",
        "allpagesfrom": "Pera liste kerdışi bıasne:",
        "allpagesto": "Pelanê ke be ena herfe qediyenê bımocne:",
-       "allarticles": "Wesiqey pêro",
+       "allarticles": "Peli pêro",
        "allinnamespace": "Peli pênro ( $1 cayênameyî)",
        "allpagessubmit": "Şo",
        "allpagesprefix": "herfê ke şıma tiya de nuşti, pê ney herfan pelê ke destpêkenê liste ker:",
        "categoriesfrom": "Kategoriyê ke be ninan dest pêkenê, bımocne:",
        "deletedcontributions": "İştırakê karberi esterdi",
        "deletedcontributions-title": "İştırakê karberi esterdi",
-       "sp-deletedcontributions-contribs": "iştıraqi",
+       "sp-deletedcontributions-contribs": "iştiraki",
        "linksearch": "Gıreyê teberi cı geyrê",
        "linksearch-pat": "bıgêr motif:",
        "linksearch-ns": "Heruna nameyi:",
        "emailsenttext": "e-mailê şıma erşawiya/ruşiya",
        "emailuserfooter": "na e-posta hetê ıney ra $1 erşawiya $2 no/na karberi/e re. pê fonksiyonê \"Karberi/e re e-posta bıerşaw\" no {{SITENAME}} keyepeli erşawiya.",
        "usermessage-summary": "Mesacê sistemi caverde.",
-       "usermessage-editor": "Mesaj berdoxe sistemi",
+       "usermessage-editor": "Xeberdarê sistemi",
        "usermessage-template": "MediaWiki:UserMessage",
        "watchlist": "Lista seyrkerdışi",
        "mywatchlist": "Lista seyrkerdışi",
        "watchlistfor2": "Qandê $1 ($2)",
        "nowatchlist": "listeya temaşa kerdıişê şıma de yew madde zi çina.",
-       "watchlistanontext": "qey vurnayişê maddeya listeya temaşakerdişi $1.",
+       "watchlistanontext": "qey vurnayişê maddeya listeya temaşakerdiş ronıştış akerê",
        "watchnologin": "Şıma cıkewtış nêvıraşto",
        "addwatch": "Lista seyrkerdışi ke",
-       "addedwatchtext": "Ma pele \"[[:$1]]\" zerri [[Special:Watchlist|watchlist]]ê tı kerd de.\nEna deme ra, ma qe vurnayışan ser ena pele tı haberdar keni.",
+       "addedwatchtext": "Ma pele \"[[:$1]]\" zerri [[Special:Watchlist|listeya seyri]] tı kerd de.\nEna deme ra, ma qe vurnayışan ser ena pele tı haberdar keni.",
+       "addedwatchtext-short": "Pera $1`i çebyê listeya seyran de şıma",
        "removewatch": "Lista seyrkerdışi ra wedare",
        "removedwatchtext": "Ena pela \"[[:$1]]\" biya wedariya [[Special:Watchlist|listeyê seyr-kerdışi şıma]].",
+       "removedwatchtext-short": "Pera $1`i listeya seyran de şıma ra wedari yê",
        "watch": "Seyr ke",
-       "watchthispage": "Ena pele seyr ke",
+       "watchthispage": "Na pele seyr ke",
        "unwatch": "Teqib meke",
        "unwatchthispage": "temaşa kerdışê peli vındarn.",
        "notanarticle": "mebhesê peli niyo",
        "wlheader-enotif": "E-mail xeber dayiş abiyo.",
        "wlheader-showupdated": "ziyaretê şıma ye peyini de vuryayişê peli pê '''nuşteyo qalıni''' mocyayo.",
        "wlnote": "$3 saete $4 ra dıme {{PLURAL:$2|yew saete de|'''$2''' saetan de}} {{PLURAL:$1|vurnayışo peyên|vurnayışê '''$1''' peyêni}} cêrderê.",
-       "wlshowlast": "Peyni de vurnayışan ra  $1 seata u $2 roca  bımocnê",
+       "wlshowlast": "Peyni de  $1 seata u $2 roca  bıasne",
        "watchlist-hide": "Bınımne",
        "watchlist-submit": "Bıasene",
-       "wlshowtime": "Peyênan bımocne:",
-       "wlshowhideminor": "vurnayışê werdiyi",
+       "wlshowtime": "Periyoda zemani asenayışi:",
+       "wlshowhideminor": "vurriyayışê werdi",
        "wlshowhidebots": "boti",
        "wlshowhideliu": "karberê qeydıni",
        "wlshowhideanons": "karberê anonimi",
        "delete-edit-reasonlist": "Sebebê vurnayışan bıvurne",
        "delete-toobig": "no pel, pê $1 {{PLURAL:$1|tene vuriyayiş|tene vuriyayiş}}i wayirê yew tarixo kehen o.\nqey hewna nêşiyayişi wina pelani u {{SITENAME}}nêxerebnayişê keyepeli yew hed niyaya ro.",
        "delete-warning-toobig": "no pel wayirê tarixê vurnayiş ê derg o, $1 {{PLURAL:$1|revizyonê|revizyonê}} seri de.\nhewn a kerdışê ıney {{SITENAME}} şuxul bıne gırano;\nbı diqqet dewam kerê.",
+       "deleteprotected": "Şıma nêşenê ena perer esternê,  çıkı per starya ya.",
        "rollback": "vurnayişan tepiya bıger",
        "rollbacklink": "peyser biya",
        "rollbacklinkcount": "$1 {{PLURAL:$1|vurnayış|vurnayışi}} peyd gıroti",
        "maximum-size": "Ebatê maximumî",
        "pagesize": "(bitî)",
        "restriction-edit": "Bıvurne",
-       "restriction-move": "Berr",
-       "restriction-create": "Vıraze",
+       "restriction-move": "Bıkırış",
+       "restriction-create": "Bıvıraz",
        "restriction-upload": "Bar ke",
        "restriction-level-sysop": "tam pawiyayo",
        "restriction-level-autoconfirmed": "nêm pawiyayo",
        "undeleterevision-missing": "revizyonê nemeqbul u vindbiyayeyi.\nRevizyoni ya hewn a biyê ya arşiw ra veciyayê ya zi cıresayişê şımayi şaş o.",
        "undelete-nodiff": "revizyonê verıni nidiya",
        "undeletebtn": "Timar bike",
-       "undeletelink": "bıvêne/peyser biya",
+       "undeletelink": "bıewni/peyser biya",
        "undeleteviewlink": "bıvin",
-       "undeleteinvert": "Weçinıtışi açarne",
+       "undeleteinvert": "Weçinayışi dimlaşt ke",
        "undeletecomment": "Sebeb:",
        "undeletedrevisions": "pêro piya{{PLURAL:$1|1 qeyd|$1 qeyd}} tepiya anciya.",
        "undeletedrevisions-files": "{{PLURAL:$1|1 revizyon|$1 revizyon}} u {{PLURAL:$2|1 dosya|$2 dosya}} ameyê halê xo yê verıni",
        "undelete-show-file-confirm": "\"<nowiki>$1</nowiki>\" şıma emin î dosyaya revizyonê no $2 $3 tarixi bıvini?",
        "undelete-show-file-submit": "Eya",
        "namespace": "Heruna nameyi:",
-       "invert": "Weçinıtışi açarne",
+       "invert": "Weçinayışi dimlaşt ke",
        "tooltip-invert": "nameyo ke nışan biyo (u nameyo elekeyın zi nışanyyayo se) vurnayışan  zerrekan nımtışi re ena dore tesdiqi nışan kerê",
        "namespace_association": "Heruna nameyanê elaqedaran",
        "tooltip-namespace_association": "Herunda canemiya elekeyın nışan kerdışi sero qıse kerdışi yana zerre dekerdışi rê ena dora tesdiqi nışan kerê",
        "blanknamespace": "(Ser)",
-       "contributions": "İştiraqê {{GENDER:$1|karber}}i",
+       "contributions": "İştırakê {{GENDER:$1|karber}}i",
        "contributions-title": "Dekerdenê karber de $1",
-       "mycontris": "İştıraqi",
-       "anoncontribs": "İştıraqi",
+       "mycontris": "İştıraki",
+       "anoncontribs": "İştıraki",
        "contribsub2": "Qandê {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "Hesabê karberi \"$1\" qeyd nêbiyo.",
        "nocontribs": "Ena kriteriya de vurnayîş çini yo.",
        "sp-contributions-newbies-title": "Îştîrakê karberî ser hesabê neweyî",
        "sp-contributions-blocklog": "qeydê kılitbiyayeyi",
        "sp-contributions-deleted": "iştırakê karberi esterdi",
-       "sp-contributions-uploads": "barkerdey",
+       "sp-contributions-uploads": "Barkerdışi",
        "sp-contributions-logs": "qeydi",
        "sp-contributions-talk": "werênayış",
        "sp-contributions-userrights": "idareyê heqanê karberan",
        "sp-contributions-username": "Adresa IPy ya zi nameyê karberi:",
        "sp-contributions-toponly": "Tenya rewizyonanê tewr peyniyan bimocne",
        "sp-contributions-submit": "Cı geyre",
-       "whatlinkshere": "Pele rê gıreyi",
+       "whatlinkshere": "Linkê tedeestey",
        "whatlinkshere-title": "Per da \"$1\" rê perê ke gre danê",
        "whatlinkshere-page": "Pele:",
        "linkshere": "Ena peleyan grey biya '''[[:$1]]''':",
        "whatlinkshere-links": "← gırey",
        "whatlinkshere-hideredirs": "Hetenayışê $1",
        "whatlinkshere-hidetrans": "Açarnayışê $1",
-       "whatlinkshere-hidelinks": "Greyê $1",
+       "whatlinkshere-hidelinks": "Gıreyê $1",
        "whatlinkshere-hideimages": "Gıreyê dosya $1",
-       "whatlinkshere-filters": "Avrêci",
+       "whatlinkshere-filters": "Parzûn",
        "whatlinkshere-submit": "Şo",
        "autoblockid": "Otomatik vındarnayış #$1",
        "block": "Karberi vındarne",
        "ipb-unblock-addr": "$1 a bik",
        "ipb-unblock": "Yew adresê IPî ya zi nameyê karberî blok bike",
        "ipb-blocklist": "Blokî ke hama estê ey bivîne",
-       "ipb-blocklist-contribs": "Qandê {{GENDER:$1|}} ra iştıraqi",
+       "ipb-blocklist-contribs": "İştirakê {{GENDER:$1|$1}}`i",
        "ipb-blocklist-duration-left": "$1 vet",
        "unblockip": "Hesabê karberî a bike",
        "unblockiptext": "Cıreştışê nuştışê IP ya zi karberio ke ver ra gêriyayo, seba peyser barkerdışi dey rê formê cêrêni bıgurenên.",
        "unblocked-id": "Blokê $1î wedariyayo",
        "blocklist": "Karberê kılitbiyayey",
        "ipblocklist": "Karberê kılitbiyayey",
-       "ipblocklist-legend": "Yew karberê kılitbiyayey bıvêne",
+       "ipblocklist-legend": "Karberê kılit biyayey bıvin",
        "blocklist-userblocks": "Kılitkerdışê hesaban bınımne",
        "blocklist-tempblocks": "Kılitkerdışan mıweqet bınımne",
        "blocklist-addressblocks": "Tenya kılitkerdışanê IPy bınımne",
        "blocklink": "kılit ke",
        "unblocklink": "bloki wedare",
        "change-blocklink": "kılitkerdışi bıvurne",
-       "contribslink": "iştıraqi",
+       "contribslink": "iştıraki",
        "emaillink": "e-poste bırışe",
        "autoblocker": "Şıma otomatikmen kılit biy, çıke adresa şımaya ''IP''y terefê \"[[User:$1|$1]]\" gureniyena.\nSebebê kılitbiyayışê $1'i \"$2\"o",
        "blocklogpage": "Qeydê astengi",
        "blocklogtext": "No kuliyatê kılitkerdış u rakerdışê fealiyetê karberano.\nAdresê IP'ya ke otomatikmen kılit biyê lista de çıniya.\nSeba lista karberanê ke heta nıka kılit biyê [[Special:BlockList|lista kılitkerdışê IPy]] bıvinê.",
        "unblocklogentry": "$1 ake",
        "block-log-flags-anononly": "tenya karberê anonimi",
-       "block-log-flags-nocreate": "akerdışê hesabi racneyayo",
+       "block-log-flags-nocreate": "akerdışê hesabi kılit bi",
        "block-log-flags-noautoblock": "Oto-wedariye terkneyayo",
-       "block-log-flags-noemail": "e-posta biya bloqe",
-       "block-log-flags-nousertalk": "Pela verênayişi ke xo nêşeno bıvurno",
+       "block-log-flags-noemail": "e-poste kılit bi",
+       "block-log-flags-nousertalk": "pela werênayışê xo nêşeno ke bıvurno",
        "block-log-flags-angry-autoblock": "oto-wedariye amayen aktivo",
        "block-log-flags-hiddenname": "nameyê karberi nımteyo",
        "range_block_disabled": "Desturê administorî ke viraştişê blokê rangeyî kefiliyo.",
        "lockfilenotwritable": "dosyaya qefılnayişê databaseyi ser ra çiyek nênusyena.",
        "databasenotlocked": "Database a nibiya.",
        "lockedbyandtime": "({{GENDER:$1|$1}} ra $2 tepya $3 biyo)",
-       "move-page": "$1 Bere",
+       "move-page": "$1 Bıkırış",
        "move-page-legend": "Pele bere",
        "movepagetext": "Pe form ki ho bın de, tı eşkeno name yew pele bıvurni u tarixê pele hemi ya zi pyeran beri.\nMa nameyê kıhanyeri keno pele redireksiyoni ser nameyê newe.\nTı eşkeno pele redireksiyoni ki şıno nameyê originali bıvurni.\nEg tı nıwazeno, ma tı ra rica keni tı [[Special:DoubleRedirects|double]] ya zi [[Special:BrokenRedirects|broken redirects]] qontrol bıki.\nTı gani qontrol bıki eg gıreyan şıno peleyanê raşti.\n\nTeme eka ser yew name de yew nuşte esti, sistemê ma '''nıeşkeno''' nuşte tı beri. Eka ser ena name de yew pele vengi esti, sistemê ma eşkeno nuşte tı beri.\nTı nıeşkeni name yew pele reyna bıvurni.\n\n'''Teme!'''\nEna transfer ser peleyanê populari zaf muhumo;\nMa tu ra rica keni, tı en verni dı qontrol bıki u bışıravi.",
        "movepagetext-noredirectfixer": "Pe form ki ho bın de, tı eşkeno name yew pele bıvurni u tarixê pele hemi ya zi pyeran beri.\nMa nameyê kıhanyeri keno pele redireksiyoni ser nameyê newe.\nTı eşkeno pele redireksiyoni ki şıno nameyê originali bıvurni.\nEg tı nıwazeno, ma tı ra rica keni tı [[Special:DoubleRedirects|raçarnayışo dılet]] ya zi [[Special:BrokenRedirects|raçarnayışo xırab]]i qontrol bıke.\nTı gani qontrol bıki eg gıreyan şıno peleyanê raşti.\n\nTeme eka ser yew name de yew nuşte esti, sistemê ma '''nıeşkeno''' nuşte tı beri. Eka ser ena name de yew pele vengi esti, sistemê ma eşkeno nuşte tı beri.\nTı nıeşkeni name yew pele reyna bıvurni.\n\n'''İkaz!'''\nEna transfer ser peleyanê populari zaf muhumo;\nMa tu ra rica keni, tı en verni dı qontrol bıki u bışıravi.",
        "cant-move-user-page": "desturê şıma çino, şıma pelanê karberani bıkırışi (bê pelê cerıni).",
        "cant-move-to-user-page": "desturê şıma çino, şıma yew peli bıkırışi pelê yew karberi.",
        "newtitle": "Sernameyo newe:",
-       "move-watch": "Peler seyr ke",
+       "move-watch": "Na pele seyr ke",
        "movepagebtn": "Pele bere",
        "pagemovedsub": "Berdışi kerd temam",
        "movepage-moved": "'''\"$1\" berd \"$2\"'''",
        "articleexists": "Ena nameyê pela database ma dı esta ya zi tı raşt nınuşt. .\nYewna name bınus.",
        "cantmove-titleprotected": "şıma nêşkeni yew peli bıhewelnê tiya çunke pawıyeno",
        "movetalk": "Pela werênayışiê elaqedare bere",
-       "move-subpages": "pelê bınini bıkırış($1 heta tiya)",
-       "move-talk-subpages": "pelê bınini yê pelê werê ameyeşi bıkırış ($1 heta tiya)",
+       "move-subpages": "Peranê bınênan bıkırış (hetana $1)",
+       "move-talk-subpages": "Bın peranê peranê vatena bıkırış (hetana $1)",
        "movepage-page-exists": "maddeya $1i ca ra esta u newe ra otomatikmen nênusyena.",
        "movepage-page-moved": "pelê $1i kırışiya pelê $2i.",
        "movepage-page-unmoved": "pelê $1i nêkırışiyeno sernameyê $2i.",
        "import-rootpage-nosubpage": "Qan de bınnaman reçe de \"$1\" re mısade nedano.",
        "importlogpage": "Qeydê ragozi",
        "importlogpagetext": "wiki yo ke nişane biyo tera kırıştışê zerredayişi nêbeno.",
-       "import-logentry-upload-detail": "$1 {{PLURAL:$1|çımraviyarnayış|çımraviyarnayışi}}",
-       "import-logentry-interwiki-detail": "$2 ra $1 {{PLURAL:$1|çımraviyarnayış|çımraviyarnayışi}}",
+       "import-logentry-upload-detail": "$1 {{PLURAL:$1|revizyon|revizyon}} debya zere",
+       "import-logentry-interwiki-detail": "$2 per da $1  ra{{PLURAL:$1|revizyon|revizyon}} debya zere",
        "javascripttest": "Cerebnayışê JavaScripti",
        "javascripttest-qunit-intro": "Mediawiki.org dı [dokumanê $1] bıvinê.",
        "tooltip-pt-userpage": "Pela {{GENDER:|şımaya karberi}}",
        "tooltip-pt-login": "Mayê şıma ronıştış akerdışi rê dawet keme; labelê ronıştış mecburi niyo",
        "tooltip-pt-logout": "Bıveciye",
        "tooltip-pt-createaccount": "Şıma rê tewsiyey ma xorê jew hesab akerê. Fına zi hesab akerdış mecburi niyo.",
-       "tooltip-ca-talk": "Zerrekê pele sero werênayış",
+       "tooltip-ca-talk": "Heqa zerrekê pele de werênayış",
        "tooltip-ca-edit": "Ena pele bıvurne",
        "tooltip-ca-addsection": "Zu bınnusteya newi ak",
        "tooltip-ca-viewsource": "Ena pele kılit biya.\nŞıma şenê çımeyê aye bıvênê",
        "tooltip-ca-unprotect": "Starkerdışe ena peler bıvurne",
        "tooltip-ca-delete": "Ena pele bestere",
        "tooltip-ca-undelete": "peli biyarê halê ver hewnakerdışi",
-       "tooltip-ca-move": "Ena peler wegi",
+       "tooltip-ca-move": "Ena pele bıkırışe",
        "tooltip-ca-watch": "Ena pele lista xoya seyrkerdışi ke",
        "tooltip-ca-unwatch": "Ena pele lista xoya seyrkerdışi ra vece",
        "tooltip-search": "{{SITENAME}} de cı geyre",
        "tooltip-n-mainpage-description": "Şo pela seri",
        "tooltip-n-portal": "Heqa proceyi de, çı şenay bıkerê, çı koti vêniyeno",
        "tooltip-n-currentevents": "Vurnayışanê peyênan de melumatê pey bıvêne",
-       "tooltip-n-recentchanges": "Wiki de lista vurnayışanê peyênan",
-       "tooltip-n-randompage": "Perake raşt amé",
+       "tooltip-n-recentchanges": "Wiki de yew lista vurriyayışanê peyênan",
+       "tooltip-n-randompage": "Pelê da raştameyiye bar ke",
        "tooltip-n-help": "Cayê peştigırewtışi",
        "tooltip-t-whatlinkshere": "Lista pelanê wikiya pêroina ke tiya gırê bena",
        "tooltip-t-recentchangeslinked": "Vurnayışê peyênê pelanê ke ena pela ra gırê biyê",
        "tooltip-feed-rss": "RSS feed qe ena pele",
        "tooltip-feed-atom": "Qe ena pele atom feed",
-       "tooltip-t-contributions": "Yew lista iştırakanê {{GENDER:$1|nê karberi}}",
-       "tooltip-t-emailuser": "Ena karber ri yew email bışırav",
+       "tooltip-t-contributions": "{{GENDER:$1|Enê karberi}} ra listey iştirakan",
+       "tooltip-t-emailuser": "Ena karber ri yew email bırış",
        "tooltip-t-upload": "Dosyeyan bar ke",
        "tooltip-t-specialpages": "Yew lista pelanê xasanê pêroyinan",
        "tooltip-t-print": "Hewl versiyona ploğnayışa na perer",
        "tooltip-ca-nstab-main": "Pela zerreki bıvêne",
        "tooltip-ca-nstab-user": "Pela karberi bıvêne",
        "tooltip-ca-nstab-media": "Pela medya bıvêne",
-       "tooltip-ca-nstab-special": "Na pelaya xas a, şıma nêşenê sero vurnayış bıkerê",
+       "tooltip-ca-nstab-special": "Na yew pela xasa, şıma nêşenê sero vurnayış bıkerê",
        "tooltip-ca-nstab-project": "Pela proceyi bıvêne",
        "tooltip-ca-nstab-image": "Pera dosyayer bıvin",
-       "tooltip-ca-nstab-mediawiki": "Mesacê sistemi bıne",
-       "tooltip-ca-nstab-template": "Şabloni bıvin",
+       "tooltip-ca-nstab-mediawiki": "Mesacê sistemi bımocne",
+       "tooltip-ca-nstab-template": "Şabloni bıvêne",
        "tooltip-ca-nstab-help": "Pela peşti bıvêne",
        "tooltip-ca-nstab-category": "Pela kategoriye bıvêne",
        "tooltip-minoredit": "Nay vırnayışa werdi nışan bıkeré",
        "anonymous": "{{PLURAL:$1|karberê|karberê}} anonimi yê keyepelê {{SITENAME}}i",
        "siteuser": "karberê {{SITENAME}}i $1",
        "anonuser": "karberê anonim o {{SITENAME}}i $1",
-       "lastmodifiedatby": "Ena pele tewr peyên roca $2, $1 by $3. de biya rocaniye",
+       "lastmodifiedatby": "Ena per tewr peyên roca $2, $1 de terefê $3 ra vurmaya ya.",
        "othercontribs": "xebatê $1 ıney geriyayo diqqeti/geriyayo nezer.",
        "others": "bini",
-       "siteusers": "{{SITENAME}} {{PLURAL:$2|karberê ey|karberanê ey}} $1",
+       "siteusers": "{{SITENAME}} {{PLURAL:$2|karber|karberan}} $1",
        "anonusers": "{{SITENAME}} {{PLURAL:$2|karberê eyê|karberanê eyê}} anonimi $1",
        "creditspage": "şınasnameyê peli",
        "nocredits": "qey no peli hema/hona yew şınasnameyi mewcud niyo",
        "pageinfo-header-edits": "Veréna timar kerdışi",
        "pageinfo-header-restrictions": "Sıtarkerdışê pele",
        "pageinfo-header-properties": "Xısusiyetê pele",
-       "pageinfo-display-title": "Sernuştey bımocne",
+       "pageinfo-display-title": "Sernuşteyo ke mosneyêno",
        "pageinfo-default-sort": "Hesıbyaye mırfeyo kılm",
        "pageinfo-length": "Derdeya pela (bayti heta)",
        "pageinfo-article-id": "Kamiya pele",
        "svg-long-desc-animated": "SVG dosya, nominalin $1 × $2 piksela, ebatê dosya: $3",
        "svg-long-error": "Nêmeqbul dosyaya SVG'i: $1",
        "show-big-image": "Dosyaya oricinale",
-       "show-big-image-preview": "Verqayd dergiya: $1'i.",
+       "show-big-image-preview": "Verqaytê dergiya: $1.",
        "show-big-image-other": "Zewmi{{PLURAL:$2|Vılêşnayış|Vılêşnayışê}}: $1.",
        "show-big-image-size": "$1 × $2 piksel",
        "file-info-gif-looped": "viyariye biyo",
        "exif-planarconfiguration": "Rezeyê datayi",
        "exif-ycbcrsubsampling": "Subsampleyi ebatê Y heta C",
        "exif-ycbcrpositioning": "pozisyonê Y u C",
-       "exif-xresolution": "Rezulasyanê veriniye",
+       "exif-xresolution": "Qerarê verıniye",
        "exif-yresolution": "Rezulasyanê derganiye",
        "exif-stripoffsets": "Cayê melumatê resımi",
        "exif-rowsperstrip": "Yew reze de teneyê dizeyi",
        "exif-referenceblackwhite": "Çiftyê siya u sipe değerê referansi",
        "exif-datetime": "Zeman u tarixê vurnayişê dosyayi",
        "exif-imagedescription": "Serê resimi",
-       "exif-make": "Viraştoğê kamera",
-       "exif-model": "Modelê kamerayi",
+       "exif-make": "Vıraştoğê kamera",
+       "exif-model": "Modelê kamera",
        "exif-software": "Software ke hebitiyeno",
        "exif-artist": "Nuştoğ",
        "exif-copyright": "Wahirê copyrighti",
        "exif-pixelydimension": "Berzeya resimi",
        "exif-usercomment": "Mışewreyê karberi",
        "exif-relatedsoundfile": "Derhekê dosya yê vengi",
-       "exif-datetimeoriginal": "Zeman u tarixê data varaziyayişi",
-       "exif-datetimedigitized": "Zeman u tarixê dicital kerdişi",
+       "exif-datetimeoriginal": "Demê afernayışê dayeyo sıfteyıni",
+       "exif-datetimedigitized": "Zeman û tarixê dicitalkerdışi",
        "exif-subsectime": "ZemanTarix saniyeyibini",
        "exif-subsectimeoriginal": "ZemanTarixOricinal saniyeyibini",
        "exif-subsectimedigitized": "ZemanTarixDicital saniyeyibini",
        "exif-urgency-low": "($1) Kemiyo",
        "exif-urgency-high": "( $1 ) Vêşiyo",
        "exif-urgency-other": "Sıftê  şınasiya karberi ($1)",
-       "namespacesall": "pêron",
+       "namespacesall": "pêro",
        "monthsall": "pêro",
        "confirmemail": "Adresê e-posta tesdiq ker",
        "confirmemail_noemail": "Yew emaîlê tu raştîyê çin o ke [[Special:Preferences|tercihê karberî]] ayar bike.",
        "scarytranscludefailed-httpstatus": "[Qande $1 şablon nêşa bıgêriyo: HTTP $2]",
        "scarytranscludetoolong": "[Ena URL zaf dergo]",
        "deletedwhileediting": "'''Teme''': Ena pele  verniyê ti de eseteriyaya!",
-       "confirmrecreate": "Karberê [[User:$1|$1]]î ([[User talk:$1|mesac]]), verniyê vurnayîşê ti ra ena pele wedarno, sebeb: ''$2''\nMa rica keno tesdiq bike ke ti raştî wazeno eno pel bivirazo.",
-       "confirmrecreate-noreason": "karbero [[User:$1|$1]] ([[User talk:$1|mesac]]) , dest pêkerdışiena pela sero vurnayışiya tepya ena pela besternê. Şıma qayıli ke ena pela fına vırazê se ena pela tesdiq kerê.",
-       "recreate": "Reyna viraz",
+       "confirmrecreate": "Karberê [[User:$1|$1]]i ([[User talk:$1|mesac]]), verniyê vurnayışê to ra ena pele {{GENDER:$1|wedarna}}, sebeb: ''$2''\nMa rica kem tesdiq kerê ke şıma qayılêena per fına bıvorazi yo.",
+       "confirmrecreate-noreason": "karbero [[User:$1|$1]] ([[User talk:$1|mesac]]) , dest pêkerdışiena pela sero vurnayışiya tepya ena pela {{GENDER:$1|besternê}}. Şıma qayıli ke ena pela fına vırazê se ena pela tesdiq kerê.",
+       "recreate": "Werzayne",
        "unit-pixel": "px",
        "confirm_purge_button": "Temam",
        "confirm-purge-top": "Vervirê na pele bestere?",
        "autosumm-replace": "Maqale pê '$1' vuriya",
        "autoredircomment": "heteneya [[$1]]",
        "autosumm-new": "Pela vıraziyê, '$1' bıvinê",
+       "autosumm-newblank": "Pera veng vıraziyê",
        "size-bytes": "$1 {{PLURAL:$1|bayt|bayti}}",
        "size-kilobytes": "$1 KB",
        "size-megabytes": "$1 MB",
        "watchlistedit-raw-done": "Listeyê tuyê seyrkerdişi rocaniye biyo",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 sername kerd|$1 sernamey kerdi}} cı:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 sername dard|$1 sernamey dardi}} we:",
+       "watchlistedit-clear-title": "Lista serykerdışê pak kerê",
+       "watchlistedit-clear-legend": "Lista serykerdışê pak kerê",
+       "watchlistedit-clear-explain": "Listeya serykerdış da şıma dı sernamey pêro besteryay",
        "watchlistedit-clear-titles": "Sernamey:",
        "watchlisttools-view": "Vurnayışanê elaqedaran bıvêne",
        "watchlisttools-edit": "Lista seyrkerdışi bıvêne û bıvurne",
        "version": "Versiyon",
        "version-extensions": "Ekstensiyonî ke ronaye",
        "version-skins": "Bar kerde bejni",
-       "version-specialpages": "Pelanê xasiyan",
+       "version-specialpages": "Pelê xısusiyi",
        "version-parserhooks": "Çengelê Parserî",
        "version-variables": "Vurnayeyî",
        "version-antispam": "Spam vındarnayış",
        "version-software": "Softwareyê ronayi",
        "version-software-product": "Mal",
        "version-software-version": "Versiyon",
-       "version-entrypoints": "heruna dekewtış de GRE'i",
+       "version-entrypoints": "Heruna cıkewtışê URLi",
        "version-entrypoints-header-entrypoint": "Heruna dekewtışi",
        "version-entrypoints-header-url": "GRE",
        "version-entrypoints-articlepath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgArticlePath Article path]",
        "redirect-value": "Erc:",
        "redirect-user": "Kamiya Karberi:",
        "redirect-page": "Kamiya pele",
-       "redirect-revision": "Çımraviyarnayışê pele",
+       "redirect-revision": "Çım ra viyarnayışê perer",
        "redirect-file": "Namey dosya",
        "redirect-logid": "Qeydé  ID",
        "redirect-not-exists": "Erc nêvineyê",
        "fileduplicatesearch-result-1": "Dosyayê ''$1î'' de hem-kopya çini yo.",
        "fileduplicatesearch-result-n": "Dosyayê ''$1î'' de {{PLURAL:$2|1 hem-kopya|$2 hem-kopyayî'}} esto.",
        "fileduplicatesearch-noresults": "Ebe namey \"$1\" ra dosya nêdiyayê.",
-       "specialpages": "Peleyê xısusiy",
+       "specialpages": "Pelê xısusiyi",
        "specialpages-note-top": "Kıtabek",
        "specialpages-note": "* Pelê xasê normali.\n* <span class=\"mw-specialpagerestricted\">Pelê xasê nımıtey.</span>",
        "specialpages-group-maintenance": "Raporê pawıtışi",
-       "specialpages-group-other": "Pelê xasiyê bini",
-       "specialpages-group-login": "Cı kewe / hesab vıraze",
+       "specialpages-group-other": "Pelê xısusiyê bini",
+       "specialpages-group-login": "Dekew / hesab vıraz",
        "specialpages-group-changes": "Vurnayışê peyêni û qeydi",
        "specialpages-group-media": "Raporê medya û barkerdışi",
        "specialpages-group-users": "Karberi û heqi",
        "tag-filter-submit": "Avrêc",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etiket|Etiketi}}]]: $2)",
        "tags-title": "Etiketan",
-       "tags-intro": "Eno pel de listeyê eyiketî este ke belki software pê ey edit kenî.",
+       "tags-intro": "Ena per şena ju vurnayışa etiketinu maney nustekeni liste  kena",
        "tags-tag": "Nameyê etiketi",
        "tags-display-header": "Listeyê vurnayîşî de esayîş",
        "tags-description-header": "Tam arezekerdışê maneyê cı",
        "tags-actions-header": "Kerdışi",
        "tags-active-yes": "Eya",
        "tags-active-no": "Nê",
-       "tags-source-extension": "Teref dê yo dergeneki ra şınasiyayo",
+       "tags-source-extension": "Kışta ju dergeneki ra şınasêna",
        "tags-edit": "bıvurne",
        "tags-delete": "bestere",
        "tags-activate": "Aktiv ke",
        "logentry-newusers-create2": "Hesabê karberi $1 terefê $3 ra {{GENDER:$2|vıraziya}}",
        "logentry-newusers-byemail": "Karber $1 hesabe $3 {{GENDER:$2|virast}} u parola rist epostadaci",
        "logentry-newusers-autocreate": "Hesabê karberi $1 otomatikmen {{GENDER:$2|vıraşt}}",
-       "logentry-rights-rights": "$1 qandê $3 rê ezayiya grube $4 ra $5 {{GENDER:$2|vuriye}}",
+       "logentry-rights-rights": "$1 qandê {{GENDER:$6|$3}} rê ezayiya grube $4 ra $5 {{GENDER:$2|vuriye}}",
        "logentry-rights-rights-legacy": "$1 qandê $3 rê ezayiya grube {{GENDER:$2|vuriye}}",
        "logentry-rights-autopromote": "$1 otomatikmen $4 ra $5 {{GENDER:$2|terfi bi}}",
        "logentry-upload-upload": "$1 {{GENDER:$2|bar kerd}} $3",
        "expand_templates_generate_xml": "Dara XML arêdayoği bımocne",
        "expand_templates_generate_rawhtml": "Xam HTML'i bıvin",
        "expand_templates_preview": "Verqayt",
-       "pagelanguage": "Weçinıtoğê zıwanê pele",
+       "pagelanguage": "Zıwanê perer bıvırnê",
        "pagelang-name": "Pele",
        "pagelang-language": "Zıwan",
        "pagelang-use-default": "Zıwanê hesabiyayeyi bıgurene",
        "pagelang-select-lang": "Zıwan weçine",
        "right-pagelang": "Zıwanê pele bıvurne",
        "action-pagelang": "zıwanê pele bıvurne",
-       "log-name-pagelang": "Qeydê zıwani bıvurne",
+       "log-name-pagelang": "Qeydê vurriyayışa zıwani",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 bayt|$1 bayti}} ($2; $3%)",
        "mediastatistics-table-mimetype": "Tewrê MIME",
        "special-characters-group-latin": "Latin",
        "special-characters-title-minus": "işaretê kemiye",
        "mw-widgets-dateinput-placeholder-day": "SSSS-AA-RR",
        "mw-widgets-dateinput-placeholder-month": "SSSS-AA",
-       "mw-widgets-titleinput-description-redirect": "berd be $1",
-       "api-error-blacklisted": "Reca keme zewbina weçine, name wa şınasnaye bo."
+       "mw-widgets-titleinput-description-redirect": "berd be $1"
 }
index 179bebb..cf025f0 100644 (file)
        "tagline": "Z {{GRAMMAR:genitiw|{{SITENAME}}}}",
        "help": "Pomoc",
        "search": "Pytaś",
+       "search-ignored-headings": " #<!-- njezměń toś tu smužku --> <pre>\n# Nadpisma, kótarež pytanje ignorěrujo.\n# Toś te změny budu se wustatkowaś, za tym až bok jo se indicěrował.\n# Móžoš indicěrowanje bokow wunuźiś, z tym až pśewjedujoš proznu změnu.\n# Syntaksa:\n#   * Wšykno, což slědujo znamušku \"#\" až do kóńca smužki, jo komentar\n#   * Kuzda njeprozna smužka jo eksaktny titel, kótaryž ma se ignorěrowaś\nŽrědła\nEksterne wótkaze\nGlědaj teke\n #</pre> <!-- njezměń toś tu smužku -->",
        "searchbutton": "Pytaś",
        "go": "Nastawk",
        "searcharticle": "Nastawk",
        "passwordreset-emailtext-user": "Wužywaŕ $1 jo anulěrowanje gronidła za {{GRAMMAR:akuzatiw|{{SITENAME}}}} pominał ($4).  {{PLURAL:$3|Slědujuce wužywarske konto jo|Slědujucej wužywarskej konśe stej|Slědujuce wužywarske konta su}} z toś tej e-mailoweju adresu {{PLURAL:$3|zwězane|zwězanej|zwězane}}:\n\n$2\n\n{{PLURAL:$3|Toś to nachylne gronidło spadnjo|Toś tej nachylnej gronidle spadnjotej|Toś te nachylne gronidła spadnu}} za {{PLURAL:$5|jaden źeń|$5 dnja|$5 dny|$5 dnjow}}.\nTy by měł se něnto pśizjawiś a nowe gronidło wustajiś. Jolic něchten drugi jo toś to napšašowanje pósłał, abo jolic sy se zasej na spócetne gronidło spomnjeł a wěcej njocoš jo změniś, móžoš toś to zdźělenje ignorěrowaś a swójo stare gronidło dalej wužywaś.",
        "passwordreset-emailelement": "Wužywarske mě: \n$1\n\nNachylne gronidło: \n$2",
        "passwordreset-emailsentemail": "E-mail za anulěrowanje gronidła jo se pósłała.",
-       "passwordreset-emailsent-capture": "E-mail za anulěrowanje gronidła jo se pósłała, kótaraž pokazujo se dołojce.",
-       "passwordreset-emailerror-capture": "E-mail za anulěrowanje gronidła jo se napórała, kótaraž se dołojce pokazujo, ale słanje {{GENDER:$2|wužywarjeju|wužywarce}} njejo se raźiło: $1",
        "changeemail": "E-mailowu adresu změniś",
        "changeemail-header": "Kontowu e-mailowu adresu změniś",
        "changeemail-no-info": "Dejš pśizjawjony byś, aby direktny pśistup na toś ten bok měł.",
        "undo-nochange": "Zda se, až změna jo se južo anulěrowała.",
        "undo-summary": "Wersija $1 wót [[Special:Contributions/$2|$2]] ([[User talk:$2|Diskusija]]) jo se anulěrowała",
        "undo-summary-username-hidden": "Změna $1 schowanego wužywarja jo se anulěrowała",
-       "cantcreateaccounttitle": "Njejo móžno wužywarske konto wutwóriś",
        "cantcreateaccount-text": "Wutwórjenje wužywarskego konta z toś teje IP adresy ('''$1''') jo blokěrowane pśez [[User:$3|$3]].\n\nPśicyna, kótaruž $3 jo zapódał, jo ''$2''.",
        "cantcreateaccount-range-text": "Napóranje kontow z IP-adresow we wobcerku <strong>$1</strong>, kótaryž wopśimujo twóju IP-adresu (<strong>$4</strong>), jo se wót [[User:$3|$3]] zablokěrowało.\n\nPśicyna pódana wót $3 jo <em>$2</em>",
        "viewpagelogs": "Protokole boka pokazaś",
index 1fa6a5c..61ae466 100644 (file)
@@ -6,14 +6,15 @@
                        "रमेश सिंह बोहरा",
                        "राम प्रसाद जोशी",
                        "Macofe",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Nirajan pant"
                ]
        },
        "tog-underline": "सम्बन्ध निम्न रेखाङ्कन:",
        "tog-hideminor": "अहिलका मामूली सम्पादनलाई लुकाउन्या",
        "tog-hidepatrolled": "गस्ती(patrolled)सम्पादनलाई लुकाउन्या",
        "tog-newpageshidepatrolled": "गस्ती गरिया पानानलाई नयाँ पाना  सूचीबठेई लुकाउन्या",
-       "tog-hidecategorization": "पà¥\83षà¥\8dठहरà¥\81को श्रेणीकरण हटाया",
+       "tog-hidecategorization": "पà¥\83षà¥\8dठहरà¥\82को श्रेणीकरण हटाया",
        "tog-extendwatchlist": "निगरानी सूचीलाई सबै परिवर्तन धेकुन्या गरी बढुन्या , ऐईलका बाहेक",
        "tog-usenewrc": "पानाका अहिलका  परिवर्तन र अवलोकन सूचीका आधारमी सामूहिक परिवर्तनहरू",
        "tog-numberheadings": "शीर्षकहरूलाई स्वत:अङ्कित गर",
        "tog-watchlisthidebots": "बोट सम्पादनहरू ध्यान सूचीबठेई लुकाउन्या",
        "tog-watchlisthideminor": "मेरा सम्पादनहरू ध्यान सूचीबाट लुकाउन्या",
        "tog-watchlisthideliu": "प्रवेश गरेका प्रयोगकर्ताहरूको सम्पादन ध्यान सूचीबठेई लुकाउन्या",
+       "tog-watchlistreloadautomatically": "जज्ज्याँलै फिल्टर बदेलिन्छ इच्छासूची आफुइ रिलोड अर: (जावास्क्रिप्ट चायीन्छ)",
        "tog-watchlisthideanons": "अज्ञात प्रयोगकर्ताहरूबाट गरिएको सम्पादन ध्यान सूचीबठेई लुकाउन्या",
        "tog-watchlisthidepatrolled": "बोट सम्पादनहरू ध्यान सूचीबठेई लुकाउन्या",
-       "tog-watchlisthidecategorization": "पà¥\83षà¥\8dठहरà¥\81को श्रेणीकरण लुकौन्या",
+       "tog-watchlisthidecategorization": "पà¥\83षà¥\8dठहरà¥\82को श्रेणीकरण लुकौन्या",
        "tog-ccmeonemails": "मुईले अन्य प्रयोगकर्ताहरूलाई पठाउन्या इ-मेलको प्रतिलिपि मुईलाई पठाउन्या",
        "tog-diffonly": "तलका पानाहरुको भिन्नहरू सामग्री नदेखाउन्या",
        "tog-showhiddencats": "लुकाइएका श्रेणीहरू धेखाउन्या",
        "editfont-serif": "सेरिफ फन्ट",
        "sunday": "आइतबार",
        "monday": "सौउबार",
-       "tuesday": "माà¤\82à¤\97लबार",
+       "tuesday": "माà¤\99लबार",
        "wednesday": "बुधबार",
-       "thursday": "बिपैबार",
+       "thursday": "बà¥\80पैबार",
        "friday": "शुकबार",
        "saturday": "छन्चरबार",
        "sun": "आइत",
        "mon": "सौउ",
        "tue": "मांगल",
-       "wed": "बà¥\81ध",
-       "thu": "बिपै",
+       "wed": "बà¥\8bऽ",
+       "thu": "बà¥\80पै",
        "fri": "शुक",
        "sat": "छन्चर",
        "january": "जनवरी",
        "october-gen": "अक्टोबर",
        "november-gen": "नोभेम्बर",
        "december-gen": "डिसेम्बर",
-       "jan": "जनवरी",
+       "jan": "जन",
        "feb": "फेब्रुअरी",
        "mar": "मार्च",
-       "apr": "अप्रि",
+       "apr": "अप्रि",
        "may": "मे",
        "jun": "जुन",
-       "jul": "जुलाई",
-       "aug": "अगस्ट",
-       "sep": "सेप्टेम्बर",
-       "oct": "अक्टोबर",
-       "nov": "नोभेम्बर",
-       "dec": "डिसेम्बर",
+       "jul": "जुल",
+       "aug": "अग",
+       "sep": "सेप्ट",
+       "oct": "अक्ट",
+       "nov": "नोभ",
+       "dec": "डिस",
        "january-date": "जनवरी $1",
        "february-date": "फेब्रुअरी $1",
        "march-date": "मार्च $1",
        "december-date": "डिसेम्बर $1",
        "period-am": "रात १२ बज्या बठे छाकला सम्म",
        "period-pm": "छाकला बठे रात १२ बज्या सम्म",
-       "pagecategories": "{{PLURAL:$1|शà¥\8dरà¥\87णà¥\80|शà¥\8dरà¥\87णà¥\80हरà¥\82}}",
-       "category_header": "\"$1\" श्रेणीमी भया लेखहरू",
+       "pagecategories": "{{PLURAL:$1|शà¥\8dरà¥\87णà¥\80|शà¥\8dरà¥\87णà¥\80न}}",
+       "category_header": "\"$1\" श्रेणीमी भयाऽ लेखअन",
        "subcategories": "उपश्रेणीहरू",
        "category-media-header": "\"$1\" श्रेणीमी भया लेखहरू",
        "category-empty": "''यै श्रेणीमी हाल कोइलै पाना या मिडिया रया नाइँथिन ।''",
        "mypage": "पानो",
        "mytalk": "मेरी कुरडी",
        "anontalk": "कुरडी",
-       "navigation": "à¤\96à¥\8bà¤\9c",
-       "and": "&#32;र",
+       "navigation": "पथपà¥\8dरदरà¥\8dशन",
+       "and": "&#32;र",
        "qbfind": "तम जाण",
        "qbbrowse": "ब्राउज गर्न्या",
        "qbedit": "सम्पादन",
        "faqpage": "Project:भौत सोधिएका प्रश्नहरु",
        "actions": "कार्यहरू",
        "namespaces": "नेमस्पेस",
-       "variants": "बहà¥\81रà¥\81पहरà¥\82",
+       "variants": "बहà¥\81रà¥\81पà¤\85न",
        "navigation-heading": "नेविगेशन मेनू",
        "errorpagetitle": "त्रुटी",
        "returnto": "$1 मी फर्क।",
-       "tagline": "{{SITENAME}}बाट",
-       "help": "सहायता",
-       "search": "खोज",
-       "searchbutton": "खोज",
+       "tagline": "{{SITENAME}} बठेइ",
+       "help": "मद्दत",
+       "search": "खोजी",
+       "search-ignored-headings": " #<!-- leave this line exactly as it is --> <pre>\n# Headings that will be ignored by search.\n# Changes to this take effect as soon as the page with the heading is indexed.\n# You can force page reindexing by doing a null edit.\n# The syntax is as follows:\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.\nReferences\nExternal links\nSee also\n #</pre> <!-- leave this line exactly as it is -->",
+       "searchbutton": "खोज:",
        "go": "जाने",
        "searcharticle": "जाओ",
        "history": "पाना इतिहास",
        "history_short": "पानाको इतिहास",
        "updatedmarker": "मेरो अन्तिम घुमाई पछि अद्यतन गरियाको",
-       "printableversion": "à¤\9bापà¥\8dनसà¤\95िनà¥\87 संस्करण",
+       "printableversion": "à¤\9bापà¥\8dद à¤®à¤¿à¤²à¥\8dलà¥\8dया संस्करण",
        "permalink": "स्थायी लिङ्क",
        "print": "छाप",
        "view": "अवलोकन गर",
        "unprotectthispage": "यै पानाको सुरक्षा परिवर्तन गर",
        "newpage": "नयाँ पाना",
        "talkpage": "यै पानाका बारेमी छलफल गर",
-       "talkpagelinktext": "à¤\95à¥\81रडà¥\80",
+       "talkpagelinktext": "à¤\95à¥\81रणि",
        "specialpage": "खास पानो",
-       "personaltools": "वà¥\8dयà¤\95à¥\8dतिà¤\97त à¤\94à¤\9cारहरà¥\82",
+       "personaltools": "वà¥\8dयà¤\95à¥\8dतिà¤\97त à¤\94à¤\9cारà¤\85न",
        "articlepage": "कन्टेन्ट पानो हेर",
-       "talk": "à¤\95à¥\81रडà¥\80 à¤\95ानी",
-       "views": "अवलोकन गर",
-       "toolbox": "à¤\94à¤\9cारहरà¥\82",
+       "talk": "à¤\95à¥\81रणिà¤\95ाà¤\86नी",
+       "views": "अवलोकन गर",
+       "toolbox": "à¤\94à¤\9cारà¤\85न",
        "userpage": "प्रयोगकर्ता पाना हेर्न्या",
        "projectpage": "प्रोजेक्ट पानो हेर्न्या",
        "imagepage": "चित्र पानो हेर",
        "viewhelppage": "सहायता पानो हेर्ने",
        "categorypage": "श्रेणी पानो हेर",
        "viewtalkpage": "छलफल हेर",
-       "otherlanguages": "à¤\85नà¥\8dय à¤­à¤¾à¤·à¤¾मी",
-       "redirectedfrom": "($1 à¤¬à¤¾à¤\9f à¤ªà¤ à¤¾à¤\87याà¤\95à¥\8b)",
+       "otherlanguages": "à¤\94र à¤­à¤·à¤¾à¤\85नमी",
+       "redirectedfrom": "($1 à¤¬à¤ à¥\87à¤\87 à¤ªà¥\81न:निरà¥\8dदà¥\87शित)",
        "redirectpagesub": "अनुप्रेषित पानो",
        "redirectto": "पठाएको पाना:",
-       "lastmodifiedat": "यà¥\88 à¤ªà¤¾à¤¨à¤¾à¤²à¤¾à¤\88 à¤\86नà¥\8dतिम à¤ªà¤\9fà¤\95 $2, $1 à¤®à¥\80 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रिया थ्यो।",
+       "lastmodifiedat": "यà¥\88 à¤ªà¤¨à¥\8dनालाà¤\88 à¤\9bाडà¥\8dडà¥\80बाऱ $2 à¤¬à¤\9cà¥\87, $1 à¤®à¥\80 à¤¹à¥\87रफà¥\87र à¤\97रियाऽ थ्यो।",
        "viewcount": "यो पाना हेरियाको थियो {{PLURAL:$1|एकपटक|$1 पटक}}",
        "protectedpage": "सुरक्षित गर्याका पानाहरू",
-       "jumpto": "यà¥\88मà¥\80 à¤\9cाà¤\93:",
-       "jumptonavigation": "भ्रमण गर",
-       "jumptosearch": "खोज",
+       "jumpto": "यà¥\88मà¥\80 à¤«à¤\9fà¥\8dà¤\9fाà¤\95:",
+       "jumptonavigation": "भ्रमण गर",
+       "jumptosearch": "खोज",
        "view-pool-error": "माफ गर्या , अहिल सर्भरहरूमी कामको भार भौत रह्या छ।\nभौत भौत प्रयोगकर्ताहरू यै पाना हेद्या प्रयास गरी रह्या छन्।\nकृपया यो पाना पुन: हेर्नु अगाडि थोक्कै पख ।\n\n$1",
        "generic-pool-error": "माफ गर्या , अहिल सर्भरहरूमी कामको भार भौत रह्या छ।\nभौत भौत प्रयोगकर्ताहरू यै पाना हेद्या प्रयास गरी रह्या छन् ।\nकृपया यो पाना पुन: हेर्नु अगाडि थोक्कै पख ।",
        "pool-timeout": "समय सकियो बन्द गर्ने प्रतीक्षामी",
        "pool-errorunknown": "अज्ञात गल्ती",
        "pool-servererror": "पुल काउन्टर सेवा उपलब्ध नाइथिन् ($1)।",
        "poolcounter-usage-error": "प्रयोग गल्ती:$1",
-       "aboutsite": "{{SITENAME}}à¤\95à¥\8b बारेमी",
+       "aboutsite": "{{SITENAME}}à¤\86 बारेमी",
        "aboutpage": "Project:बारेमी",
        "copyright": "सामाग्री $1 अनुसार उपलब्ध छ, खुलाइएको अवस्था बाहेकका हकमी ।",
-       "copyrightpage": "{{ns:project}}:पà¥\8dरतिलिपà¥\80 à¤\85धिà¤\95ारहरà¥\82",
-       "currentevents": "आजभोलका घटनाहरू",
-       "currentevents-url": "Project:आजभोलका घटनाहरू",
-       "disclaimers": "à¤\85सà¥\8dविà¤\95ारà¥\8bà¤\95à¥\8dतिहरà¥\82",
+       "copyrightpage": "{{ns:project}}:पà¥\8dरतिलिपà¥\80 à¤\85धिà¤\95ारà¤\85न",
+       "currentevents": "आजभोलका घटना",
+       "currentevents-url": "Project:आजभोलका घटना",
+       "disclaimers": "à¤\85सà¥\8dविà¤\95ारà¥\8bà¤\95à¥\8dतà¥\80न",
        "disclaimerpage": "Project:सामान्य अस्वीकारोक्ति",
        "edithelp": "सम्पादन सहायता",
        "helppage-top-gethelp": "सहायता",
-       "mainpage": "मà¥\81à¤\96à¥\8dय à¤ªà¤¾à¤¨à¥\8b",
-       "mainpage-description": "मà¥\81à¤\96à¥\8dय à¤ªà¤¾à¤¨à¥\8b",
+       "mainpage": "मà¥\81à¤\96à¥\8dय à¤ªà¤¨à¥\8dना",
+       "mainpage-description": "मà¥\81à¤\96à¥\8dय à¤ªà¤¨à¥\8dना",
        "policy-url": "Project:निति",
        "portal": "सामाजिक पोर्टल",
        "portal-url": "Project:सामाजिक पोर्टल",
        "versionrequired": "MediaWiki संस्करण $1 चाईन्या",
        "versionrequiredtext": "ये पाना प्रयोग गर्नका लागि MediaWiki $1 संस्करण चाहिन्छ ।\nहेर  [[Special:Version|version page]]",
        "ok": "भयो",
-       "retrievedfrom": " \"$1\" बठे निकालिया",
-       "youhavenewmessages": "तमखी लेखा($3)मी $1 ($2) छ ।",
-       "youhavenewmessagesfromusers": "तमखी लेखा {{PLURAL:$3|प्रयोगकर्ता|$3 प्रयोगकर्तान}}($2)बठे$1",
+       "retrievedfrom": " \"$1\" बठे निकालिया",
+       "youhavenewmessages": "{{PLURAL:$3|तम सित छन}} $1 ($2)।",
+       "youhavenewmessagesfromusers": "{{PLURAL:$3|अर्खा प्रयोगकर्ता|$3 प्रयोगकर्ताअन}} ($2) मी है {{PLURAL:$4|तम सित}} $1 छन।",
        "youhavenewmessagesmanyusers": "तमलाई धेरै प्रयोगकर्ताहरू($2) बठे $1 छ ।",
        "newmessageslinkplural": "{{PLURAL:$1|एक नौलो रैबार|999=नौला रैबारहरू}}",
        "newmessagesdifflinkplural": "छाड्डीबारको {{PLURAL:$1|परिवर्तन|999=परिवर्तनहरू}}",
        "viewsourceold": "स्रोत हेर",
        "editlink": "सम्पादन",
        "viewsourcelink": "स्रोत हेर",
-       "editsectionhint": "खण्ड: $1 सम्पादन गर",
+       "editsectionhint": "खण्ड: $1 सम्पादन गर",
        "toc": "विषयसूची",
        "showtoc": "धेकाउन्या",
        "hidetoc": "लुकाउन्या",
        "feed-invalid": "अमान्य फिड प्रकार ग्राह्याता ।",
        "feed-unavailable": "सिन्डीकेसन फिडहरु उपलब्ध नाइथिन्",
        "site-rss-feed": "$1 आरएसएस फिड",
-       "site-atom-feed": "$1 à¤\8fà¤\9fम à¤«à¤¿ड",
+       "site-atom-feed": "$1 à¤\8fà¤\9fम à¤«à¥\80ड",
        "page-rss-feed": "\"$1\" आरएसएस फिड",
        "page-atom-feed": "\"$1\" एटम फिड",
-       "red-link-title": "$1 (पाना उपलब्ध नाइँथिन)",
+       "red-link-title": "$1 (पनà¥\8dना उपलब्ध नाइँथिन)",
        "sort-descending": "अवरोहण क्रममी मिलाउन्या",
        "sort-ascending": "आरोहण क्रममी मिलाउन्या",
-       "nstab-main": "लà¥\87à¤\96",
+       "nstab-main": "पनà¥\8dना",
        "nstab-user": "प्रयोगकर्ता पानो",
        "nstab-media": "माध्यम पाना",
        "nstab-special": "खास पानो",
        "nstab-template": "ढाँचा",
        "nstab-help": "सहायता पानो",
        "nstab-category": "श्रेणी",
-       "mainpage-nstab": "मà¥\81à¤\96à¥\8dय à¤ªà¤¾à¤¨à¥\8b",
+       "mainpage-nstab": "मà¥\81à¤\96à¥\8dय à¤ªà¤¨à¥\8dना",
        "nosuchaction": "यसो काम हैन",
        "nosuchactiontext": "URL ले खुलाएको काम मान्य छैन ।\nतमीले URL गलत टाइपगरेका हौ , वा गलत लिंकक पछाडी लागेका हुनसक्देहौ ।\nयो {{SITENAME}}ले सफ्टवेयरमी भयाको गल्ति देखायाको लै हुनसक्छ ।",
        "nosuchspecialpage": "तसो विशेष पानो छैन",
        "laggedslavemode": "<strong>चेतावनी:</strong> पानामी हालका अद्यतनहरू नहुनस्कदान ।",
        "readonly": "डेटाबेस बन्द गरिया छ",
        "enterlockreason": "ताल्चा मार्नुको कारण दिया, साथै ताल्चा हटाउने समयको अवधि अनुमान लगा।",
-       "readonlytext": "समà¥\8dभवतà¤\83 à¤¨à¤¿à¤¯à¤®à¤¿à¤¤ à¤¡à¥\87à¤\9fाबà¥\87स à¤°à¤\96-रà¤\96ाà¤\89à¤\95à¥\8b à¤\95ारण à¤\85हिलà¥\87लाà¤\88 à¤¨à¤¯à¤¾à¤\81 à¤¡à¥\87à¤\9fाबà¥\87स à¤ªà¥\8dरविषà¥\8dà¤\9fà¥\80 à¤° à¤\85नà¥\8dय à¤¸à¤\82शà¥\8bधनहरà¥\82  à¤¬à¤¨à¥\8dद à¤°à¤¾à¤\96िया à¤\9b, à¤\9cà¤\88लाà¤\88 à¤ªà¤\9bि à¤¬à¤ à¥\87 à¤¸à¤¾à¤®à¤¾à¤¨à¥\8dय à¤\97रिनà¥\8dया à¤\9b। \nपà¥\8dरबनà¥\8dधà¤\95 à¤\9cà¤\88लà¥\87 à¤¯à¥\8b à¤¬à¤¨à¥\8dद à¤\97रà¥\8dयाà¤\9bनà¥\8d, à¤¯à¥\8b à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤¦à¤¿à¤¯à¤¾à¤\95ाà¤\9bनà¥\8d: $1",
+       "readonlytext": "नà¥\8cला à¤ªà¥\8dरविषà¥\8dà¤\9fà¥\80 à¤°à¥\87 à¤\94र à¤¸à¤\82शà¥\8bधनà¤\85न à¤\96िलाà¤\88 à¤¡à¤¾à¤\9fाबà¥\87स à¤\85à¤\87ल à¤¬à¤¨à¥\8dद à¤\85रà¥\80रà¥\88à¤\9b़, à¤¸à¤®à¥\8dभबत: à¤¨à¤¿à¤¯à¤®à¤¿à¤¤ à¤¡à¤¾à¤\9fाबà¥\87स à¤°à¤\96-रà¤\96ाà¤\89 à¤\96िलाà¤\87, à¤\9cà¥\88 à¤ªà¤\9bा à¤¯à¥\8b à¤¸à¤¾à¤®à¤¾à¤¨à¥\8dय à¤\85वसà¥\8dथा à¤®à¥\80 à¤\86सलà¥\8b। \n\nवà¥\8dयवसà¥\8dथापà¤\95 à¤\9cà¤\88लà¥\87 à¤¯à¥\8b à¤¬à¤¨à¥\8dद à¤\85रिराà¤\87à¤\9b à¤\89नलà¥\87 à¤¯à¥\87à¤\87 à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤¦à¥\80राà¤\87à¤\9b़: $1",
        "missing-article": "नाम \"$1\" $2 भयाको भेटिनु पड्डे पानो पाठ डेटाबेसले  भेटाएन, \n\nयिसो प्राय: मिति नाघिसक्याको भिन्न वा इतिहास वा कुनै मेटिसक्याको पानाको लिंक पहिल्याउनाले हुन्छ ।\n\nयदि यसो भया नाइँहो भणे सफ्टवेयरको गल्ती लै हुनसकुन्छ ।\nकृपया यैको url खुलाइ [[Special:ListUsers/sysop|प्रबन्धक]]लाई उजुरी गर",
        "missingarticle-rev": "(संशोधन #: $1)",
        "missingarticle-diff": "(भिन्नता: $1, $2)",
        "readonly_lag": "डेटाबेस स्वतः बन्द गरिया छ जबकि अधिनस्थ डेटाबेस सर्वरले मूल पहिल्याउँनाछ।",
+       "nonwrite-api-promise-error": "'Promise-Non-Write-API-Action' HTTP हेडर पठाइयाथ्यो तर अनुरोध API write module खिलाइ थ्यो।",
        "internalerror": "भित्रका गल्ती",
        "internalerror_info": "भित्रका गल्ती: $1",
        "internalerror-fatal-exception": "प्रकारको गम्भीर अपवाद \"$1\"",
        "title-invalid-interwiki": "अनुरोध गरियाको शिर्षकमी अन्तर विकि लिङ्क छ जइलाई शिर्षकमी प्रयोग गद्द नाइपाइनो ।",
        "title-invalid-talk-namespace": "निवेदन गरियाको पानाको शिर्षकले उपलब्ध नभएका कुरडी पानालाई सन्दर्भको रूपमी राख्याको छ ।",
        "title-invalid-characters": "निवेदन गरियाको यै पानाको शिर्षकमी अवैध अक्षर रयाको छः \"$1\" ।",
+       "title-invalid-magic-tilde": "अनुरोध अरिया: पन्ना: शीर्षकमी अमान्य म्याजिक टिल्ड शृङ्खला छ (<nowiki>~~~</nowiki>)।",
+       "title-invalid-too-long": "अनुरोध अरिया: पन्ना: शीर्षक भौत लामु छ। यो UTF-8 इनकोडिङमी $1 {{PLURAL:$1|byte|bytes}} है लामु हुनु हुनैन।",
+       "title-invalid-leading-colon": "निवेदन गरिया पृष्ठको शिर्षकको शुरूमी अवैध कोलोन रया छ ।",
+       "perfcached": "तलका डाटाहरू क्याचमी रया कुराहरू हुन्। अपटुडेट नहुन लाई सक्दान। बर्ति {{PLURAL:$1|नतिजा|$1 नतिजाहरू}} क्याचमी उपलब्ध छ।",
+       "perfcachedts": "तलतिरको आँकडा क्याच हो र $1 पहिला अद्यतन गरिया थ्यो। येई क्याचमी उपलब्ध {{PLURAL:$4|एउटा कारण हो|$4 कारणहरू हुन्}}।",
+       "querypage-no-updates": "येइ पन्ना: अद्दतन कार्य ऐल़ निस्क्रिय अरीरैछ।\nयाँ को डाटा ऐलखिलाइ ताजो अरिन्या आथिन।",
        "viewsource": "स्रोत हेर",
        "viewsource-title": " $1 को स्रोत हेर",
        "actionthrottled": "कार्य रोकिईयो",
        "viewsourcetext": "तम ये पृष्ठको स्रोत हेद्दु सकुन्छौ और उईको नक्कल उताद्दु सकुन्छौ |",
        "viewyourtext": "यै पानामी रह्याका '''तमरा सम्पादनहरू''' हेद्द या प्रतिलिपी गद्द सक्द्या हौ :",
        "editinginterface": "<strong>चेतावनी:</strong> तमी यै पानालाई सम्पादन गद्द लाग्याछौ, जनले सफ्टवेयरको लागि \nइन्टरफेस सामग्रीहरू प्रदान गरन्छ।\nयै पानामी गरियाको परिवर्तनले यै विकिमी अरु प्रयोगकर्तानको इन्टरफेसको प्रदर्शनमी प्रभाव पडन्छ ।",
+       "translateinterface": "सप्पै विकिइनखिलाइ अनुवाद थप्दाइ या बदेल्लाइ, कृपया [https://translatewiki.net/ translatewiki.net]को प्रयोग अर:, मिडियाविकि क्षेत्रीयकरण परियोजना:।",
        "namespaceprotected": "तमलाई '''$1'''  नेमस्पेसमी रह्याका पानाहरू सम्पादन गद्या अनुमति छैन ।",
        "customcssprotected": "तमलाई यो  पानो सम्पादन गद्दे अनुमति छैन, किनकी यैमी कुनै अर्को प्रयोगकर्ताको व्यक्तिगत अभिरुचीहरू संग्रहित छन् ।",
        "customjsprotected": "तमलाई यो जाभास्कृप्ट पानो सम्पादन गद्दे अनुमति छैन, किनकी यैमी कुनै अर्को प्रयोगकर्ताको व्यक्तिगत अभिरुचीहरू संग्रहित छन् ।",
        "mypreferencesprotected": "तमसंग तमरो अभिरुचीहरू सम्पादन ग्द्दे अनुमती छैन |",
        "ns-specialprotected": "विशेष पृष्ठहरू सम्पादन अद्दु नाइँ सकिनो।",
        "titleprotected": "[[User:$1|$1]]द्वारा ये शीर्षक निर्माणहुनबठे जोगाइया छ।\nकारण <em>$2</em> हो ।",
-       "filereadonlyerror": "फाइल \"$1\" लाई परिवर्तन अद्दु नाइँ सकिनो क्याईकि फाइल भण्डार  \"$2\" केवल पढ्ने स्थिति (read-only mode)मी छ।\n\nयेलाई सुरक्षित गर्ने प्रवन्धकले  यो कारण दियाकाछन् : ''$3''।",
+       "filereadonlyerror": "\"$1\" फाइललाई परिवर्तन अद्दु नाइँ सकिनो क्याईकि फाइल भण्डार \"$2\" केवल पड्ड्या स्थिति (read-only mode)मी छ।\n\nयेलाई सुरक्षित अद्द्या प्रवन्धकले यो कारण दीराइछ: ''$3''।",
+       "invalidtitle-knownnamespace": "\"$2\" नाउँबार रे \"$3\" पाठ भया: अमान्य शीर्षक",
+       "invalidtitle-unknownnamespace": "अपछ्याणो नाउँबार अङ्क $1 रे पाठ \"$2\" भया: अमान्य शीर्षक",
        "exception-nologin": "प्रवेश (लग ईन) नगरिएको",
+       "exception-nologin-text": "येए पन्नालाई चलुनाइ या केयि काम खिलाइ सक्षम हुनाइ लगइन अर:।",
+       "virus-badscanner": "गट्टी मिलजुल: अपछ्याणो भाइरस खोज्ज्या: <em>$1</em>",
        "virus-scanfailed": "जँचाई असफल(कोड $1)",
        "virus-unknownscanner": "थानभया एन्टीभाइरस:",
        "logouttext": "<strong>तमी अहिल बाहिर निस्क्याका  छौ।</strong>\n\nयाद राख्या तमीले ब्राउजरको क्याच खालि नगर्यासम्म कुनै पानाहरूमी तमी अझैं प्रवेश गरिरख्याको धेकाउन सक्छ।",
        "createacct-yourpassword-ph": "पासवर्ड लेख",
        "yourpasswordagain": "पासवर्ड फेरि टाईप गर",
        "createacct-yourpasswordagain": "पासवर्ड निश्चित गर",
-       "createacct-yourpasswordagain-ph": "à¤\86à¤\9cà¥\80 à¤ªà¤¾à¤¸à¤µà¤°à¥\8dड à¤²à¥\87à¤\96",
+       "createacct-yourpasswordagain-ph": "à¤\86à¤\81à¤\9cि à¤ªà¤¾à¤¸à¤µà¤°à¥\8dड à¤­à¤°à¤½",
        "userlogin-remembermypassword": "मुलाई अघाडी झान्या काम गराइराख्या",
        "userlogin-signwithsecure": "सुक्षित जडान प्रयोग गद्द्या",
+       "cannotlogin-title": "लगइन अरिसक्दैन",
+       "cannotlogin-text": "येइमी लगइन सम्भव नाइथिन।",
        "cannotloginnow-title": "अईल भितर झान नाइँ पाईनो",
        "cannotloginnow-text": "भितर जान असंभव छ जब प्रयोग $1|",
+       "cannotcreateaccount-title": "खाता बनौन नाइसक्दो",
+       "cannotcreateaccount-text": "प्रत्यक्ष खाता बनौन एइ विकि मी सक्षम अरीयाऽ आथिन।",
        "yourdomainname": "तमरो ज्ञानक्षेत्र(डोमेन):",
        "password-change-forbidden": "ये विकिमी पासवर्ड परिवर्तन गर्न सक्नुहुन्न।",
+       "externaldberror": "या त याँ प्रमाणीकरण डाटाबेस त्रुटी थी या त तमलाई अफुना बाइल्ला खातालाई अद्यतन अद्देइ अनुमति आथिन।",
        "login": "प्रवेश (लगईन)",
        "login-security": "तमरो पहिचान जाचँ गर",
        "nav-login-createaccount": "प्रवेश गर्ने/नयाँ खाता बनाउन्या",
        "userlogin-resetpassword-link": "पासवर्ड भुलिगया?",
        "userlogin-helplink2": "प्रवेश गद्दलाई सहयोग",
        "userlogin-loggedin": "तमी {{GENDER:$1|$1}}को रूपमी प्रवेश (लग इन) भइ सक्यौ ।\nअर्को प्रयोगकर्ताको रूपमी प्रवेश (लग इन) गर्न तलको फारम प्रयोग गर ।",
+       "userlogin-reauth": "तम {{GENDER:$1|$1}} हो भणिबर पुष्टि अद्दाइ दोसर्‍याँ लगइन अर:।",
        "userlogin-createanother": "दोसरो खाता खोल",
        "createacct-emailrequired": "इमेल ठेगाना",
        "createacct-emailoptional": "इमेल ठेगाना (ऐच्छिक)",
-       "createacct-email-ph": "तमरà¥\8b à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤­à¤°à¤¯à¤¾",
+       "createacct-email-ph": "तमरà¥\8b à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤­à¤°à¤½",
        "createacct-another-email-ph": "इमेल ठेगाना भर",
        "createacct-realname": "वास्तविक नाम (ऐच्छिक)",
        "createaccountreason": "कारण:",
        "createacct-reason": "कारण",
        "createacct-reason-ph": "क्याई तम नयाँ खाता खोल्ला छौ?",
-       "createacct-submit": "तमरà¥\8b à¤\96ाता à¤¸à¤¿à¤°à¥\8dà¤\9cना à¤\97र",
-       "createacct-another-submit": "दà¥\8bसरà¥\8b à¤\96ाता à¤¸à¤¿à¤°à¥\8dà¤\9cना à¤\97र",
+       "createacct-submit": "तमरà¥\8b à¤\96ाता à¤¬à¤¨à¤¾à¤½",
+       "createacct-another-submit": "à¤\96ाता à¤\96à¥\8bल",
        "createacct-continue-submit": "खाता खोल्लु जारि राख",
        "createacct-another-continue-submit": "खाता खोल्लु जारि राख",
        "createacct-benefit-heading": "{{SITENAME}} तम जसाई मान्सुनले सिर्जना गरिया हो ।",
        "mailmypassword": "पासवर्ड पूर्वनिर्धारित गर",
        "passwordremindertitle": "{{SITENAME}}का लागि नयाँ अस्थायी पासवर्ड",
        "passwordremindertext": "कसैले (सायद तमी, IP ठेगाना $1 बाट), {{SITENAME}}($4) को लागि नौलो पासवर्ड अनुरोध गर्या छ । प्रयोगकर्ता \"$2\" को लागि नौलो अस्थायी पासवर्ड \"$3\"तयार पारिया छ । यदि यो तमरो इच्छामी भयाको भया अहिले तमीले लगइन गरीबर नौलो पासवर्ड छान्नु पड्ड्या हुन्छ ।\nतमरो अस्थायी पासवर्ड  {{PLURAL:$5|एक दिन|$5 दिनहरू पछि}} अमान्य हुन्याछ ।\n\nयदि कोही अरुले नै अनुरोध गर्याको हो भण्या , या तमीले आफ्नो पासवर्ड सम्झ्यौ भण्या, अथवा\nत्यैलाई परिवर्तन गर्न चाहन्नौ भण्या, तमीले यो सन्देसको वेवास्ता गद्दसक्द्याहौ र पुरानै पासवर्ड प्रयोग गरिरहन सक्द्याहौ ।",
-       "blocked-mailpassword": "तमरà¥\8b IP à¤ à¥\87à¤\97ानालाà¤\88 à¤¸à¤®à¥\8dपादन à¤\97दà¥\8dद à¤¬à¤ à¥\87 à¤°à¥\8bà¤\95 à¤²à¤\97ायाà¤\95à¥\8b à¤\9b, à¤° à¤¤à¥\8dयसà¥\88लà¥\87 à¤¦à¥\81रà¥\81पयà¥\8bà¤\97 à¤°à¥\8bà¤\95à¥\8dदाà¤\95à¥\8b à¤²à¤¾à¤\97ि à¤ªà¥\8dरवà¥\87सशबà¥\8dद à¤ªà¥\81नरà¥\8dलाभ à¤ªà¥\8dरà¤\95à¥\8dरिया à¤ªà¥\8dरयà¥\8bà¤\97 à¤\97दà¥\8dया à¤\85नà¥\81मति à¤\9bà¥\88न ।",
+       "blocked-mailpassword": "तमरा IP à¤ à¥\87à¤\97ानालाà¤\88 à¤¸à¤®à¥\8dपादन à¤\97दà¥\8dद à¤¬à¤ à¥\87 à¤°à¥\8bà¤\95 à¤²à¤¾à¤\87राà¤\87à¤\9b। à¤¦à¥\81रà¥\81पयà¥\8bà¤\97 à¤°à¥\8bà¤\95à¥\8dदाà¤\87, à¤¤à¤®à¤°à¤¾ IP à¤ à¥\87à¤\97ाना à¤¬à¤ à¥\87à¤\87 à¤ªà¥\8dरवà¥\87सशबà¥\8dद à¤ªà¥\81नरà¥\8dलाभ à¤ªà¥\8dरà¤\95à¥\8dरिया à¤ªà¥\8dरयà¥\8bà¤\97 à¤\85दà¥\8dदà¥\8dया à¤\85नà¥\81मति à¤\86थिन।",
        "mailerror": " चिठी :$1 पठाउँदा गल्ती भयो",
        "noemailprefs": "निम्न सुविधाहरू राम्डरी काम गद्दको लागि तमरो रोजाईमी आफ्नो ई-मेल ठेगाना खुलाओ ।",
        "emailconfirmlink": "तमरो ई-मेल ठेगाना पक्का गर",
        "pt-login": "प्रवेश (लग ईन)",
        "pt-login-button": "प्रवेश",
        "pt-login-continue-button": "प्रवेश जारी राख",
-       "pt-createaccount": "नयाँ खाता खोल",
+       "pt-createaccount": "नयाँ खाता खोल:",
        "pt-userlogout": "बाहिर निस्कन्या (लग आउट)",
        "php-mail-error-unknown": "PHP मेल() क्रियामा अज्ञात गल्ती",
        "user-mail-no-addy": "इमेल ठेगाना बिनाई इमेल पठाउन खोजिया थ्यो।",
        "passwordreset-email": "इमेल ठेगाना:",
        "passwordreset-emailtitle": "{{SITENAME}}मा खाता विवरण",
        "passwordreset-emailelement": "प्रयोगकर्ताको नाम: \n$1\n\nअस्थाई पासवर्ड: \n$2",
-       "passwordreset-emailsentemail": "पासवरà¥\8dड à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनà¤\95ा à¤²à¤¾à¤\97ि à¤\87मà¥\87ल à¤ªà¤ à¤¾à¤\87या à¤\9b।",
+       "passwordreset-emailsentemail": "यदि à¤¯à¥\8b à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤¤à¤® à¤¸à¤¿à¤¤ à¤¸à¤®à¥\8dबनà¥\8dधित à¤\9b à¤­à¤£à¥\8dया, à¤¤à¤¬ à¤¯à¤\95 à¤ªà¤¾à¤¸à¤µà¤°à¥\8dड à¤°à¤¿à¤¸à¥\87à¤\9f à¤\87मà¥\87ल à¤ªà¤ à¤¾à¤\8fलà¥\8b।",
        "passwordreset-invalideamil": "अबैध ई-मेल ठेगाना",
        "changeemail": "इमेल ठेगाना बदेल वा हटा",
-       "changeemail-header": "à¤\86फà¥\8dनà¥\8b à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97दà¥\8dद à¤¯à¥\8b à¤«à¤¾à¤°à¤® à¤­à¤° à¥¤ à¤¯à¥\88लाà¤\88 à¤ªà¥\81षà¥\8dà¤\9fि à¤\97दà¥\8dद à¤¤à¤®à¥\80लà¥\87 à¤\86फà¥\8dनà¥\8b à¤ªà¤¾à¤¸à¤µà¤°à¥\8dड à¤¹à¤¾à¤²à¥\8dनà¥\81 à¤ªà¤¡à¤¨à¥\8dà¤\9b।",
+       "changeemail-header": "तमरà¥\8b à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤¬à¤¦à¥\87लà¥\8dलाà¤\87 à¤\8fà¤\87 à¤«à¤¾à¤°à¤¾à¤® à¤ªà¥\81राà¤\87 à¤­à¤°à¤½à¥¤ à¤¯à¤¦à¤¿ à¤¤à¤® à¤¤à¤®à¤°à¤¾ à¤\96ाता à¤¬à¤ à¥\87à¤\87 à¤\95सà¥\88 à¤²à¥\88 à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤¸à¤¿à¤¤à¥\8bऽ à¤¸à¤®à¥\8dबनà¥\8dध à¤¹à¤\9fà¥\8cन à¤\9aाहनà¥\8dà¤\9bऽ à¤­à¤£à¥\8dया, à¤«à¤¾à¤°à¤¾à¤® à¤¬à¥\81à¤\9cà¥\8cनà¥\8dà¤\9cà¥\8dयाà¤\81 à¤¨à¥\8cलà¥\8b à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤­à¤£à¥\8dणà¥\8dया à¤ à¥\8cर à¤\96ालि à¤\9bाणà¥\8dयाऽ।",
        "changeemail-oldemail": "अईलको इमेल-ठेगाना:",
        "changeemail-newemail": "नयाँ इमेल-ठेगाना:",
        "changeemail-none": "(के लै नाइँ)",
        "changeemail-password": "तमरो {{SITENAME}} पासवर्ड:",
        "changeemail-submit": "इमेल परिवर्तन गद्या",
        "changeemail-throttled": "तमले अलै भौत फेर प्रवेशका निम्ति प्रयास गरया छौ।\nकृपया $1 पर्खेर मात्र प्रयास गर।",
+       "changeemail-nochange": "कृपया नौलो जुदोइ इमेल ठेगाना राख:।",
        "resettokens": "टोकन पूर्वरुपमी फर्काउन्या",
        "resettokens-text": "जो टोकन तमरो खातासँग सम्बद्ध केहि विशिष्ट व्यक्तिगत जानकारी प्रदान गर्छन, तम त्यसलाई यहाँ रिसेट गद्द सक्द्या हौ।\n\nयदि तमले तिनलाई भुलवस कैकनै देखाईदिया छौ वा तमरो खाता ह्याक भइसक्याको छ भन्या तम यसलाई रिसेट गर्या ।",
        "resettokens-no-tokens": "पूर्वरुमी फर्काउन्या कोई लै टोकन नाइथिन् ।",
        "minoredit": "यो नानो सम्पादन हो",
        "watchthis": "यै पानाको ध्यान राख",
        "savearticle": "सङ्ग्रह गर",
+       "savechanges": "परिवर्तन सुरक्षित गर",
        "publishpage": "प्रकाशित पृष्ठ",
+       "publishchanges": "परिवर्तन प्रकाशित गर",
        "preview": "पूर्वावलोकन",
        "showpreview": "पूर्वालोकन धेकाउन्या",
        "showdiff": "परिवर्तन धेकाउन्या",
        "anoneditwarning": "<strong>चेतावनी:</strong> तमले प्रवेश अरेको नाइथिन । तमरो आइपि ठेगाना पाना सम्पादन इतिहासमि दर्ता गरिन्या छ र यो सब्बैले हेद्द सक्कान । यदि तमलाईँ <strong>[$1 लगईन]</strong> वा <strong>[$2 नयाँ खाता बनाउन्या] गर्याभण्या तमबठे गरियाको सम्पादन तमरो प्रयोगकर्तानाममि जोडिन्याछ ।",
        "missingsummary": "'''यादगर्या :''' तमीले सम्पादन सारांश दियाका छैनौ ।\nयदि तमीले \"{{int:savearticle}}\"  थिच्यौ भण्या , सारांश बिना नै सङ्ग्रहित गरिन्या छ ।",
        "selfredirect": "<strong>चेतावनी:</strong> तम यै पानालाई आफुमी पुनः निर्देशित गद्द लाग्याछौ ।\nहुनसक्छ तम अनुप्रेषितको लागि गलत लक्ष्य निर्दिष्ट गद्द लाग्याछौ, वा गलत पानाको सम्पादन गद्द लाग्याछौ ।\nतम पुनः एकपल्ट \"{{int:savearticle}}\" क्लिक गद्दाछौ, पुनः निर्देशित तसै लै बनाइन्याछ।",
-       "missingcommentheader": "'''याद गर :''' तमीले टिप्पणीमी विषय /शीर्ष पंक्ति  दियाका छैनौ ।\nतमीले फेरि \"{{int:savearticle}}\"  थिच्यौ भण्या , तमरो सम्पादन यसै रुपमी संग्रहित हुन्याछ ।",
+       "missingcommenttext": "कृपया तलतिर टिप्पणी राख ।",
+       "missingcommentheader": "'''याद गर :''' तमले टिप्पणीमी विषय /शीर्ष पंक्ति  दियाका छैनौ ।\nतमले फेरि \"{{int:savearticle}}\"  थिच्यौ भण्या , तमरो सम्पादन यसै रुपमी संग्रहित हुन्याछ ।",
        "summary-preview": "सारांश पूर्वालोकन:",
        "subject-preview": "विषय पूर्वरुप:",
        "previewerrortext": "तमरो परिवर्तनको पूर्वावलोकन बनाउन खोज्दा समस्या आयाको छ ।",
        "anontalkpagetext": "----''यो कुरडी पानो अज्ञात प्रयोगकर्ताको हो जनले अहिलसम्म खाता बनायाकै छैन, अथवा जनले यै पानाको उपयोग गर्दैन।\nयस कारण हामीले उनलाई उनरो आइ पी (IP) ठेगानाले चिन्न सकन्छौ। \nयस्तो आइ पी (IP) ठेगाना धेरै प्रयोगकर्तानको साझा हुनसकन्छ ।\nयदि तमी अज्ञात प्रयोगकर्ता हौ र तमलाई अचाहिँदो टिप्पणी भयाको अनुभव गद्दा छौ भण्या भविष्यमी अन्य अज्ञात प्रयोगकर्तासँगको भ्रमबाट बाँच्न कृपया [[Special:CreateAccount|खाता खोल]] अथवा [[Special:UserLogin|प्रवेश गर]] ''",
        "noarticletext": "यै लेखमी अहिल क्यै पन पाठ नाइथी  ।\nतमले और पृष्ठमी\n[[Special:Search/{{PAGENAME}}|यस पृष्ठको शीर्षककी लेखा खोज]] गद्द सकन्छौ ।\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} पाना सम्बन्धित ढड्डामी खोज],\nवा [{{fullurl:{{FULLPAGENAME}}|action=edit}}  यै पानालाई सम्पादन गद्या]</span>.",
        "noarticletext-nopermission": "यै लेखमी अहिल केइ पन पाठ नाइथी  ।\nतमले और पानामी\n[[Special:Search/{{PAGENAME}}|यै पानाको शीर्षककी लेखा खोज]] गद्द सकन्छौ ।\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} पाना सम्बन्धित ढड्डामी खोज्न],\nवा [{{fullurl:{{FULLPAGENAME}}|action=edit}}  यै पानालाई सम्पादन गद्द] सकन्छौ</span>.",
+       "userpage-userdoesnotexist": "\"$1\" प्रयोगकर्ता खाता दर्ता अरीया: आथिन।\nयेइ पान्नो बनुन/सम्पादन अद्द चाहन्छ: भण्या विचार अर:।",
        "userpage-userdoesnotexist-view": "प्रयोगकर्ता खाता \"$1\" दर्ता गरिया छैन।",
+       "blocked-notice-logextract": "यो प्रयोगकर्ता अच्याल प्रतिवन्धित छ।\nसब है पछा: प्रतिबन्ध लग प्रविष्टि सन्दर्भ खिलाइ तल्तिर दियीरैछ:",
+       "sitecsspreview": "<strong>येइ CSSलाई तम पूर्वावलोकन मात्तरी अद्दाछ: भणिबर फाम अर:।\nयो आँजि सङ्ग्रह अरिया: आथिन। </strong>",
+       "sitejspreview": "<strong>येइ जावास्क्रिप्ट कोडलाई तम पूर्वावलोकन मात्तरी अद्दाछ: भणिबर फाम अर:।\nयो आँजि सङ्ग्रह अरिया: आथिन। </strong>",
        "userinvalidcssjstitle": "<strong>चेतावनी:</strong> यहाँ कोइपनि \"$1\" नामको खोल नाइथिन् ।\nप्रचलित .css तथा .js पानाहरूले निम्नपद शीर्षक प्रयोग गद्दान्, जस्तै {{ns:user}}:Foo/Vector.css को सट्टामी {{ns:user}}:Foo/vector.css",
        "updated": "नौला",
        "note": "'''सूचना:'''",
+       "previewnote": "<strong>फाम अर: कि यो यक पूर्वावलोकन मात्तरी हो।</strong>\nतमले अर्‍या फेरबदेली आँजि सङ्ग्रहित भया: आथिन!",
        "continue-editing": "सम्पादन क्षेत्रमी जाओ",
-       "editing": "$1 à¤¸à¤®à¥\8dपादन à¤\97रिà¤\81दà¥\88",
+       "editing": "$1 à¤¸à¤®à¥\8dपादन à¤\85रà¥\80नà¥\8dनाà¤\9b़",
        "creating": "$1 बनाइँदै",
-       "editingsection": "$1 (à¤\96णà¥\8dड) à¤¸à¤®à¥\8dपादन à¤\97रिà¤\81दà¥\88",
+       "editingsection": "$1 (खण्ड) सम्पादन गरिदै",
        "editingcomment": "$1 सम्पादन गर्दै(नयाँ खण्ड)",
        "editconflict": "सम्पादन बाँझ्यो: $1",
        "yourtext": "तमरा पाठहरू",
        "template-protected": "(सुरक्षित)",
        "template-semiprotected": "(अर्ध-सुरक्षित)",
        "hiddencategories": "यो पानो निम्न {{PLURAL:$1|1 लुकाइयाको श्रेणी|$1 लुकाइयाका श्रेणीहरू}}को हिस्सादार(सदस्य) हो :",
+       "nocreate-loggedin": "नौला पन्ना बनुनाइ तम सित अधिकार आथिन।",
        "sectioneditnotsupported-title": "खण्ड सम्पादन असमर्थित",
        "sectioneditnotsupported-text": "ये पृष्ठमी खण्ड सम्पादन असमर्थित",
        "permissionserrors": "अधिकारमी त्रुटी",
+       "permissionserrorstext": "तइ काम अद्दाइ तम सित अधिकार आथिन, यिन {{PLURAL:$1|कारण|कारणअन}}ले अद्दा:",
        "permissionserrorstext-withaction": "$2 कि लेखा तमलाईँ अनुमति नाइथिन , यिन {{PLURAL:$1|कारणले|कारणहरुले}} गद्दा :",
        "moveddeleted-notice": "पानो मेटियाको छ।\nमेटियाका और सारियाका पानाहरूको सूची तल्तिर सन्दर्भखी लेखा दियाको छ।",
        "log-fulllog": "पूरा लग हेर",
        "parser-unstrip-loop-warning": "अनस्ट्रिप लुप धेखिन्छ",
        "parser-unstrip-recursion-limit": "अन्स्ट्रिप पुनरावर्तन सिमा पार गरियो ($1)",
        "converter-manual-rule-error": "म्यानुअल भाषा अनुवाद सिध्दान्तमी समस्या धेखियो",
-       "cantcreateaccounttitle": "खाता बनाउन सकिएन",
        "viewpagelogs": "यै पानाका लगहरू हेर",
        "nohistory": "ये पृष्ठका लागी कोइ सम्पादन इतिहास छैन।",
        "currentrev": "अहिलको पुनरावलोकन",
        "revdelete-unsuppress": "पुनर्स्थापित पुनरावृत्तिबठे बन्देज हटाउन्या",
        "revdelete-log": "कारण:",
        "revdelete-submit": "{{PLURAL:$1|छानिया संशोधन|छान्निया संशोधनहरू}}मी प्रयोग गर्न्या",
-       "revdelete-success": "'''संशोधन दृश्यता सफलतापूर्वक अद्यतन भयो।'''",
+       "revdelete-success": "संशोधन दृश्यता अद्यतन अरियो।",
        "revdelete-failure": "'''संशोधन दृश्यता अद्यतन गर्न सकिएन:'''\n$1",
        "logdelete-success": "लग दृष्टि मिलाइयो ।",
        "logdelete-failure": "'''लग दृष्टि मिलाउन सकिएन :'''\n$1",
        "nextn-title": "यै पछाका $1 {{PLURAL:$1|नतिजा |नतिजाहरू}}",
        "shown-title": "धेखाउने $1 {{PLURAL:$1|नतिजा|नतिजाहरू}} प्रति पाना",
        "viewprevnext": "हेर ($1 {{int:pipe-separator}} $2) ($3)",
-       "searchmenu-exists": "''' \"[[:$1]]\" नाम गरया पाना  ये विकीमी रह्या छ'''",
+       "searchmenu-exists": "<strong>\"[[:$1]]\" नाउँ अरियाऽ पन्ना ये विकीमी छ।</strong>{{PLURAL:$2|0=| पाइयाऽ और खोजी नतिजाअन लै तकऽ।}}",
        "searchmenu-new": "<strong>\"[[:$1]]\"  पानो इसै विकिमी बनाओ !</strong> {{PLURAL:$2|0=|तमले खोज अरी भेटियाको पानो पन सङ्ङै जोड्या काम अर ।|तमरो खोज परिणाम पन हेर।}}",
-       "searchprofile-articles": "सामà¤\97à¥\8dरà¥\80 à¤ªà¤¾à¤¨à¤¾à¤¹à¤°à¥\82",
+       "searchprofile-articles": "सामà¤\97à¥\8dरà¥\80 à¤ªà¤¨à¥\8dनाà¤\85न",
        "searchprofile-images": "मल्टिमिडिया(श्रव्य दृश्य)",
        "searchprofile-everything": "सबै थोक",
        "searchprofile-advanced": "उन्नत",
        "searchprofile-articles-tooltip": "$1 मी खोज्या",
-       "searchprofile-images-tooltip": "फाइलहरू खोज्ज्या",
+       "searchprofile-images-tooltip": "फाइल कि लेखा खोजऽ",
        "searchprofile-everything-tooltip": "सबै सामग्री खोज्या (वार्तालाप लै )",
        "searchprofile-advanced-tooltip": "अनुकुल नेमस्पेसमा खोज्या",
-       "search-result-size": "$1 ({{PLURAL:$2|1 à¤¶à¤¬à¥\8dद|$2 à¤¶à¤¬à¥\8dदहरà¥\82}})",
+       "search-result-size": "$1 ({{PLURAL:$2|1 à¤\86à¤\81à¤\96र|$2 à¤\86à¤\81à¤\96र}})",
        "search-result-category-size": "{{PLURAL:$1|एक सदस्य|$1 सदस्यहरू}} ({{PLURAL:$2|1 उपश्रेणी|$2  उपश्रेणीहरू}}, {{PLURAL:$3|एउटा फाइल|$3 फाइलहरू}})",
        "search-redirect": "(जान्या $1)",
        "search-section": "(खण्ड $1)",
        "search-interwiki-more": "(आजी)",
        "search-relatedarticle": "सम्बन्धित",
        "searchrelated": "सम्बन्धित",
-       "searchall": "सबै",
+       "searchall": "सपà¥\8dपै",
        "showingresults": "धेखाउँदै  {{PLURAL:$1|'''१''' नतिजा|'''$1''' नतिजाहरू }} , #'''$2''' बठे सुरुहुन्या ।",
        "showingresultsinrange": "देखाई रह्या छ{{PLURAL:$1|<strong>1</strong> result|<strong>$1</strong> परिणाम}} सम्म पहुँच  #<strong>$2</strong> देखि #<strong>$3</strong> मी।",
        "search-showingresults": "{{PLURAL:$4|<strong>$3</strong> मै बठे <strong>$1</strong> परिणाम|<strong>$3</strong> मै बठे परिणाम <strong>$1 - $2</strong>}}",
        "prefs-rc": "नौला परिवर्तनहरू",
        "prefs-watchlist": "मेरो ध्यान सूची",
        "prefs-editwatchlist": "अवलोकनसूची सम्पादन",
+       "prefs-editwatchlist-label": "आफना अवलोकनसूचीमी रया इन्ट्रीलाई सम्पादन गर:",
+       "prefs-editwatchlist-edit": "आफना अवलोकनसूचीमी रया शीर्षकलाई धेकाउन्या तथा हटाउन्या",
        "prefs-editwatchlist-raw": "कच्चा अवलोकनसूची सम्पादन गद्दा",
        "prefs-editwatchlist-clear": "तमरो अवलोकनसूची मेटा",
        "prefs-watchlist-days": "ध्यान सूचीमी धेकाउने दिनहरू:",
+       "prefs-watchlist-days-max": "बर्ती है बर्ती $1 {{PLURAL:$1|दिन|दिनअन}}",
+       "prefs-watchlist-edits": "उच्चतम परिवर्तन संख्या बढाइएको निगरानी सूचीमी  धकाउनका लागि :",
        "prefs-watchlist-edits-max": "सबै है ज्यादा संख्या : १०००",
        "prefs-watchlist-token": "अवलोकन सूची टोकन:",
        "prefs-misc": "साधारण",
        "prefs-resetpass": "पासवर्ड परिवर्तन गर",
-       "prefs-changeemail": "à¤\87मà¥\87ल à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dनà¥\8dया",
+       "prefs-changeemail": "à¤\87मà¥\87ल à¤ à¥\87à¤\97ाना à¤¬à¤¦à¥\87ल à¤µà¤¾ à¤¹à¤\9fा",
        "prefs-setemail": "इमेल ठेगाना प्रविष्ट गर्न्या",
        "prefs-email": "इमेल  विकल्पहरू",
        "prefs-rendering": "स्वरुप",
        "saveprefs": "संग्रह",
+       "restoreprefs": "सबै पूर्वनिर्धारित स्थिती कायम गर्ने(सबै खण्डहरूमी)",
        "prefs-editing": "सम्पादन",
        "rows": "हरफहरू :",
        "columns": "स्तम्भहरू :",
        "stub-threshold-sample-link": "उदाहरण",
        "stub-threshold-disabled": "निष्क्रिय",
        "recentchangesdays": "हालको परिवर्तनमी धेकाउने दिनहरू:",
-       "recentchangesdays-max": "à¤\85धिà¤\95तम $1 {{PLURAL:$1|दिन|दिन}}",
+       "recentchangesdays-max": "à¤\9cà¥\87दा à¤¹à¥\88 à¤\9cà¥\87दा $1 {{PLURAL:$1|दिन|दिनà¤\85न}}",
        "timezonelegend": "समय क्षेत्र :",
        "localtime": "स्थानिय समय:",
        "timezoneuseserverdefault": "विकि मूल  ($1) रुपमी प्रयोग गर्ने",
        "prefs-reset-intro": "तम ये पृष्ठलाई आफनो अभिरुचीहरू साइट पूर्वावस्थामी फर्काउनत फर्काउन प्रयोग गद्दु सकन्छौ । तै पाछा ये लाई रद्द गद्दु सकन्छौ ।",
        "prefs-emailconfirm-label": "इ-मेल एकिन प्रक्रिया:",
        "youremail": "ईमेल",
-       "username": "{{लिंग:$1|प्रयोगकर्ता नाम}}:",
-       "prefs-memberingroups": "निम्न {{बहुबचन:$1|समूह | समूहहरू}}को {{लिंग:$2|सदस्य}} :",
+       "username": "{{GENDER:$1|प्रयोगकर्ता नाम}}:",
+       "prefs-memberingroups": "निम्न {{PLURAL:$1|समूह|समूहहरू}}को {{GENDER:$2|सदस्य}} :",
        "prefs-registration": "दर्ता समय:",
        "yourrealname": "वास्तविक नाम:",
        "yourlanguage": "भाषा",
        "yournick": "नयाँ हस्ताक्षर:",
        "prefs-help-signature": "कुरडी पानाका टिप्पणीहरू \"<nowiki>~~~~</nowiki>\" द्वारा दस्तखत गरिनुपडन्छ ,त्यो पछि तमरो दस्तखत र समयरेखामी रुपान्तरित हुनेछ ।",
        "badsiglength": "तमरो दस्तखत मैथै लामो छ।\nयो $1 {{PLURAL:$1|अक्षर|अक्षरहरू}} भन्दा लामो हुनु हुँदैन ।",
+       "gender-male": "उ विकि पृष्ठहरू सम्पादन गरन्छ",
+       "gender-female": "तीन विकि पृष्ठहरु गरिन्छिन",
        "email": "ईमेल",
        "prefs-help-realname": "वास्तविक नाम ऐच्छिक हो ।\nतमीले खुलायौ भण्या तमरो कामको श्रेय दिनको लेखा यैको प्रयोग गरिन्या छ ।",
        "prefs-help-email-required": "इमेल ठेगाना चाहिन्छ ।",
        "userrights": "प्रयोगकर्ता अधिकार व्यवस्थापन",
        "userrights-lookup-user": "प्रयोगकर्ता समूह व्यवस्थापन गर",
        "userrights-user-editname": "प्रयोगकर्ता नाम दिय:",
-       "editusergroup": "सम्पादन{{लिंग:$1}}समूहहरु",
+       "editusergroup": "{{GENDER:$1|प्रयोगकर्ता}}समूहहरू सम्पादन",
        "userrights-editusergroup": "प्रयोगकर्ता समूह सम्पादन गर",
-       "saveusergroups": "सुरक्षित{{लिंग:$1}}समूहहरु",
+       "saveusergroups": "सुरक्षित {{GENDER:$1|प्रयोगकर्ता}} समूहहरू",
        "userrights-groupsmember": "को सदस्य:",
        "userrights-groupsmember-auto": "अंतर्निहित सदस्य:",
        "userrights-reason": "कारण:",
        "userrights-unchangeable-col": "तमीले परिवर्तन गद्द नसक्ने समूहहरू",
        "userrights-conflict": "प्रयोगकर्ताको अधिकार परिवर्तनमी मतभेद भयो ! कृपया तमरो परिवर्तन पुनरावलोकन तथा पुष्टि गर ।",
-       "userrights-removed-self": "तमà¥\80लà¥\87 à¤¸à¤«à¤²à¤¤à¤¾à¤ªà¥\82रà¥\8dवà¤\95 à¤\86फà¥\8dनà¥\8b à¤\85धिà¤\95ारहरà¥\82लाà¤\88 à¤®à¥\87à¤\9fायà¥\8c à¥¤ à¤¤à¥\8dयà¥\88 à¤\95ारण à¤¤à¤®à¥\80 à¤\85ब यो पानो हेद्द नाइसक्दा ।",
+       "userrights-removed-self": "तमलà¥\87 à¤¸à¤«à¤²à¤¤à¤¾à¤ªà¥\82रà¥\8dवà¤\95 à¤\86फनà¥\8b à¤\85धिà¤\95ारहरà¥\82लाà¤\88 à¤®à¥\87à¤\9fाया à¥¤ à¤¤à¥\8dयà¥\88 à¤\95ारण à¤¤à¤® à¤\86ब यो पानो हेद्द नाइसक्दा ।",
        "group": "समूह:",
        "group-user": "प्रयोगकर्ताहरू",
        "group-autoconfirmed": "स्वत स्थापित प्रयोगकर्ताहरू",
        "group-bureaucrat": "प्रशासकहरू",
        "group-suppress": "लुकौन्या वाला",
        "group-all": "(सबै)",
-       "group-user-member": "{{लिङग:$1|प्रयोगकर्ता}}",
-       "group-autoconfirmed-member": "{{लिङग:$1|स्वनिर्धारित प्रयोगकर्ता}}",
-       "group-bot-member": "{{लिङग:$1|बोट}}",
-       "group-sysop-member": "{{लिङग:$1|प्रबन्धक}}",
-       "group-bureaucrat-member": "{{लिङग:$1|प्रशासक}}",
-       "group-suppress-member": "{{लिङग:$1|दबाउन्या}}",
-       "grouppage-user": "{{एनयस:आयोजना}}:प्रयोगकर्ताहरू",
-       "grouppage-autoconfirmed": "{{एनयस:आयोजना}}:स्वनिर्धारित प्रयोगकर्ताहरू",
-       "grouppage-bot": "{{एनयस:आयोजना}}:बोटहरु",
-       "grouppage-sysop": "{{एनयस:आयोजना}}:प्रबन्धकहरु",
-       "grouppage-bureaucrat": "{{एनयस:आयोजना}}:प्रशासकहरु",
-       "grouppage-suppress": "{{एनयस:आयोजना}}:लुकौन्या",
+       "group-user-member": "{{GENDER:$1|प्रयोगकर्ता}}",
+       "group-autoconfirmed-member": "{{GENDER:$1|स्वनिर्धारित प्रयोगकर्ता}}",
+       "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}}:स्वतःपुष्टि भयाऽ प्रयोगकर्ताअन",
+       "grouppage-bot": "{{ns:project}}:बोटअन",
+       "grouppage-sysop": "{{ns:project}}:प्रबन्धकहरू",
+       "grouppage-bureaucrat": "{{ns:project}}:प्रशासकअन",
+       "grouppage-suppress": "{{ns:project}}:लुकौन्या",
        "right-read": "पृष्ठहरू पढ",
        "right-edit": "पृष्ठहरू सम्पादन गर",
        "right-createpage": "पृष्ठ निर्माण गर(छलफल पृष्ठहरू बाहेक)",
        "right-upload_by_url": "URL बठे फाइल उर्ध्वभरण गर्ने",
        "right-purge": "साइटको क्याश( cache) निश्चित नगरिकनै पर्ज(Purge) गर्ने",
        "right-writeapi": "लेखन API प्रयोग गद्य्या",
+       "right-delete": "पृष्ठहरू मेट्ने",
        "right-bigdelete": "लामो इतिहास भयाका पानाहरू मेट्ट्या",
        "right-deleterevision": "खुलाइयाको पानाहरू मेटाउन्या र मेटायाको रद्द गद्या",
        "right-deletedtext": "मेट्याका संशोधन बीचका मेट्याका पाठ र परिवर्तनहरू हेद्या",
+       "right-undelete": "मेट्याको पाना फर्काउने",
        "right-suppressionlog": "व्यक्तिगत लगहरू हेद्या",
        "right-block": "अरु प्रयोगकर्तानलाई सम्पादन गद्दाकी ब्लक गर",
+       "right-unblockself": "आफुलाई खुल्ला गर ।",
        "right-editprotected": "\"{{int:protect-level-sysop}}\" को हैसियतले सुरक्षित पानाहरू सम्पादन गद्या",
        "right-editusercssjs": "अन्य प्रयोगकर्ताको सी.एस.एस. रे जाभास्क्रिप्ट फाइलहरू सम्पादन गद्या",
        "right-editusercss": "अन्य प्रयोगकर्ताको सी. एस. एस. फाइलहरू सम्पादन गद्या",
        "right-userrights-interwiki": "अन्य विकिहरूमी प्रयोगकर्ताहरूको अधिकार सम्पादन गद्या",
        "right-override-export-depth": "गहिराइ ५ सम्म लिंक गरियाका पानाहरू सहित निर्यात गद्या",
        "right-sendemail": "अन्य प्रयोगकर्तानलाई इमेल पठाउन्या",
-       "grant-editmycssjs": "तमरो प्रयोगकर्ता CSS/JavaScript सम्पादन गर",
-       "grant-editmyoptions": "तमरा प्रयोगकर्ता अभिरूचीहरूलाई सम्पादन गर",
+       "grant-editmycssjs": "तमरो प्रयोगकर्ता CSS/JavaScript सम्पादन गरऽ",
+       "grant-editmyoptions": "तमरा प्रयोगकर्ता अभिरुचीइनलाई सम्पादन गरऽ",
+       "grant-editmywatchlist": "तमरो अवलोकनसूची सम्पादन गर",
+       "grant-editpage": "भैरया पृष्ठहरू सम्पादन गर",
+       "grant-editprotected": "सुरक्षित पृष्ठ सम्पादन",
+       "grant-highvolume": "उच्च मात्रा सम्पादन",
+       "grant-basic": "आधारभूत अधिकार",
+       "grant-viewdeleted": "नयाँ फाइलहरू अपलोड\nसम्पादन गर",
+       "grant-viewmywatchlist": "आफनो अबलोकन सुची हेर",
        "newuserlogpage": "प्रयोगकर्ता श्रृजना लग",
+       "action-move": "ये पानालाई अर्खिठौर सार",
        "action-move-subpages": "यै पानाको रे यैका उपपानाको नाम बदल्न्या",
+       "action-move-rootuserpages": "मूल प्रयोगकर्ता पृष्ठहरू सार्ने",
+       "action-move-categorypages": "श्रेणी पृष्ठ सार",
+       "action-movefile": "ये फाईललाइ सार",
+       "action-upload": "ये फाइल अपलोड गर",
        "action-unwatchedpages": "कसैले ध्यान नराख्याका पाननको सूची हेद्या",
        "action-userrights-interwiki": "अन्य विकिका प्रयोगकर्तानको प्रयोगकर्ता अधिकार सम्पादन गद्या",
        "action-applychangetags": "तमरो परिवर्तनसँगै ट्यागहरू लागु गर्न्या",
        "recentchanges-legend": "अच्यालैका परिवर्तन विकल्पहरू",
        "recentchanges-summary": "विकिका यैल्लैका फेरबदललाई यै पानामि पहिल्याउन्या",
        "recentchanges-label-newpage": "यै सम्पादनले नौलो पानो बनायाको छ",
-       "recentchanges-label-minor": "यो नानो सम्पादन हो",
+       "recentchanges-label-minor": "यà¥\8b à¤¨à¤¾à¤½à¤¨à¥\8b à¤¸à¤®à¥\8dपादन à¤¹à¥\8b",
        "recentchanges-label-bot": "यो सम्पादन बोटबठे गरियाको थ्यो",
        "recentchanges-label-unpatrolled": "यो सम्पादन यैलसम्म गस्ती गरियाको नाइथी",
        "recentchanges-label-plusminus": "यति बाइटहरू संख्याले पानाको आकार फेरबदल  भयाको छ",
        "rclistfrom": "$3 $2 देखिका नयाँ परिवर्तनहरू देखाउन्या",
        "rcshowhideminor": "$1 सानतिनो सम्पादन",
        "rcshowhideminor-show": "धेकाइदिय",
-       "rcshowhideminor-hide": "लà¥\81à¤\95ाà¤\89नà¥\8dया",
+       "rcshowhideminor-hide": "लà¥\81à¤\95ाऽ",
        "rcshowhidebots": "$1 बोटहरू",
        "rcshowhidebots-show": "धेकाइदिय",
        "rcshowhidebots-hide": "लुकाइदिय",
        "rcshowhideliu": "$1 दर्ता अर्याका प्रयोगकर्ताहरू",
        "rcshowhideliu-hide": "लुकाउन्या",
        "rcshowhideanons": "$1 नपछेण्याका प्रयोगकर्ता",
-       "rcshowhideanons-show": "धà¥\87à¤\95ाà¤\87दिय",
-       "rcshowhideanons-hide": "लà¥\81à¤\95ाà¤\89नà¥\8dया",
+       "rcshowhideanons-show": "धà¥\87à¤\95ाऽ",
+       "rcshowhideanons-hide": "लà¥\81à¤\95ाऽ",
        "rcshowhidepatr": "$1 पट्रोल गर्याका सम्पादनहरू",
        "rcshowhidemine": "$1 मेरा सम्पादनहरू",
        "rcshowhidemine-show": "धेकाइदिय",
-       "rcshowhidemine-hide": "लà¥\81à¤\95ाà¤\87दिय",
+       "rcshowhidemine-hide": "लà¥\81à¤\95ाऽ",
        "rcshowhidecategorization-show": "धेकाउन्या",
        "rcshowhidecategorization-hide": "लुकाउन्या",
        "rclinks": "पछिल्ला $1 परिवर्तनहरू पछाडिका $2 दिनहरूमी<br />$3",
        "newpageletter": "नौ",
        "boteditletter": "बो",
        "rc_categories": "श्रेणीहरूमी सीमित (\"|\" ले छुट्याओ)",
-       "rc-change-size-new": "$1 {{PLURAL:$1|बाà¤\87à¤\9f|बाà¤\87à¤\9fस}}फà¥\87रबदलपाछा",
+       "rc-change-size-new": "$1 {{PLURAL:$1|बाà¤\87à¤\9f|बाà¤\87à¤\9fà¥\8dस}}फà¥\87रबदल पाछा",
        "recentchangeslinked": "सम्बन्धित फेरबदल",
        "recentchangeslinked-toolbox": "सम्बन्धित फेरबदल",
        "recentchangeslinked-title": "\"$1\" सित सम्बन्धित परिवर्तन",
        "recentchangeslinked-summary": "यो सूची निर्दिष्ट पाना (वा निर्दिष्ट श्रेणी)सित जोडियाका अल्लै परिवर्तन भयाका पानाको  हो। [[Special:Watchlist|तमरो ध्यानसूची]]का पानाहरू <strong>गाढा अक्षरमी</strong> छन्।",
        "recentchangeslinked-page": "पाना नाम:",
        "recentchangeslinked-to": "यैको सट्टा यो पानासित जोडियाका पानानको परिवर्तन धेकाउन्या",
-       "upload": "à¤\9aितà¥\8dर à¤\85पलà¥\8bड à¤\97र",
+       "upload": "फाà¤\87ल à¤\85पलà¥\8bड à¤\97रऽ",
        "uploadbtn": "फाइल अपलोड गर्न्या",
        "upload-recreate-warning": "'''चेतावनी: त्यस नाममी रह्याका फाइलहरू सारियाको या हटायाको छ।'''\n\nयै पानाको सारियाको र हटायाको लग तमरो सहजताको लागि दियाको छ।",
        "filedesc": "सारांश:",
        "large-file": "यो सिफारिस गर्याछकि फाइलहरूको आकार $1 भन्दा ठूला हुनु हुँदैन;\nयै फाइलको आकार $2 छ ।",
        "emptyfile": "तमीले अपलोड गर्याको फाइल रित्तो छ ।\nयो फाइल नाम गलत राख्याका कारणले भयाको हुनसकन्छ\nयो फाइल साँच्चै अपलोड गद्दे कुरडीमी निश्चित होइजाओ ।",
        "fileexists": "यै नामको फाइल पैल्ली नैं छ, यदि तम परिवर्तन गद्या कुरडीमू सुनिश्चित छैनौ भण्या कृपया <strong>[[:$1]]</strong> जाँच गर।\n[[$1|thumb]]",
+       "fileexists-no-change": "अपलोड <strong>[[:$1]]</strong>का अच्यालआ संस्करणो ठ्याक्कै नकल हो।",
+       "fileexists-duplicate-version": "अपलोड <strong>[[:$1]]</strong> को {{PLURAL:$2|पुरानु संस्करण|पुरानु संस्करणअन}}ओ नकल हो।",
        "filewasdeleted": "यै नामको एक फाइल पहिली पनि अपलोड गरिबर पछि हटाई सकियाको छ।\nपुनः अपलोड गद्दु पूर्व तम $1 लाई निक्करी जाँच गर ।",
        "upload-dialog-title": "चित्र अपलोड गर",
        "upload-dialog-button-cancel": "रद्द",
        "uploadstash-nofiles": "तमरा कोइ पनि स्टाश गर्याका फाइलहरू नाइथिन् ।",
        "uploadstash-badtoken": "त्यो कार्य असफलभयो , सायद तमरो सम्पादन अधिकार समाप्त भयो । पुन: प्रयास गर ।",
        "uploadstash-refresh": "फाइलहरूको सूची ताजा गर्न्या",
-       "license-header": "à¤\95à¥\8bà¤\87 à¤\95à¥\87à¤\87 à¤¨à¤¾à¤\87थिन",
+       "license-header": "à¤\86à¤\9cà¥\8dà¤\9eापतà¥\8dर à¤¦à¤¿à¤¨à¥\8dनाà¤\9b़",
        "listfiles-summary": "यै खास पानाले अपलोड गर्याका सबै फाइलहरू धेकाउन्छ ।",
        "imgfile": "चित्र",
        "listfiles_count": "संस्करणहरू",
        "filehist-thumb": "थम्बनेल",
        "filehist-thumbtext": "थम्बनेल $1 संस्करणको रुपमी",
        "filehist-user": "प्रयोगकर्ता",
-       "filehist-dimensions": "à¤\86à¤\95ारहरà¥\82",
+       "filehist-dimensions": "à¤\86याम",
        "filehist-comment": "टिप्पणी",
        "imagelinks": "फाइलको प्रयोगहरु",
        "linkstoimage": "यै चित्रमी निम्न{{PLURAL:$1|पाना जोडिनान{{PLURAL:$1|}}|$1 पानाहरू जोडिनान्}}:",
        "filedelete-intro-old": "तमी <strong>[[Media:$1|$1]]</strong> को संस्करणलाई [$4 $3, $2] हुन्या गरि मेट्ट लाग्याछौ ।",
        "filedelete-maintenance": "रखरखाव चलिरह्याको हुनाले अस्थायी रुपमी फाइलहरू मेट्ट्या र मेट्याकोलाई पुनर्बहाली गर्न निष्क्रिय गरियाकोछ।",
        "mimesearch-summary": "MIME-प्रकार अनुसार फाइलहरू खोज्न यै पानाको प्रयोग गद्द सकिन्याछ ।\nइनपुट: फाइलको प्रकार/उपप्रकार, उदा. <code>image/jpeg</code>।",
-       "randompage": "à¤\95à¥\8bà¤\87 à¤\8fà¤\95 à¤²à¥\87à¤\96",
+       "randompage": "à¤\95à¥\8dरमरहित à¤ªà¤¨à¥\8dना",
        "statistics-header-pages": "पानानको तथ्याङ्क",
        "statistics-header-edits": "सम्पादनहरूको तथ्याङ्क",
        "statistics-files": "अपलोड गर्याका फाइलहरू",
        "deadendpagestext": "निम्न पानाहरू {{SITENAME}}मी रह्याका अरु पानाहरूसँग जोडिदाइनन् ।",
        "protectedpagesempty": "यै बेला यी नियम बठे कुनै पाना लै शुरक्षित नाइथिन्",
        "usereditcount": "$1 {{PLURAL:$1|सम्पादन|सम्पादनहरू}}",
-       "newpages": "नयाà¤\81 à¤ªà¤¾à¤¨à¤¾à¤¹à¤°à¥\82",
+       "newpages": "नà¥\8cला à¤ªà¤¨à¥\8dनाà¤\85न",
        "move": "नाम बदल",
        "movethispage": "पानाको नाम बदल्न्या",
        "notargettext": "यै कार्यका लेखाई तमीले कुनै लक्षित पानो वा प्रयोगकर्ता निर्दिष्ट गर्याको छैनौ ।",
        "booksources-text": "तल दियाको सूची नौला तथा पूराना किताब बेच्न्या लगायत तमीले खोज्याका किताबका बारेमी थप जानकारी भयाका अन्य साइटका लिंकहरू हुन् ।",
        "log": "लगहरू",
        "all-logs-page": "सब्बै सार्वजनिक लगहरू",
-       "allarticles": "सबà¥\8dबà¥\88 à¤²à¥\87à¤\96हरà¥\82",
-       "allpagessubmit": "à¤\9cानà¥\8dया",
+       "allarticles": "सपà¥\8dपà¥\88 à¤ªà¤¨à¥\8dनाà¤\85न",
+       "allpagessubmit": "à¤\9cाऽ",
        "allpagesprefix": "यी सुरुका अक्षरसहितका पानाहरू हेद्या:",
        "categories": "श्रेणीहरू",
        "listusers-noresult": "प्रयोगकर्ता भेटियानन्",
        "blanknamespace": "(मुख्य)",
        "contributions": "{{GENDER:$1|प्रयोगकर्ता}}को योगदान",
        "mycontris": "मेरो योगदानहरू",
+       "anoncontribs": "योगदान",
        "month": "महिना बठे (लै पैल्ली):",
        "year": "वर्ष बठे( लौ पैल्ली):",
        "sp-contributions-toponly": "नवीनतम संशोधनका सम्पादनहरू मात्र धेकाओ",
-       "whatlinkshere": "याà¤\81à¤\96ाà¤\87 à¤\95à¥\80 à¤\9cà¥\81डन्छ",
-       "whatlinkshere-title": "$1 à¤¸à¤¿à¤¤ à¤\9cà¥\8bडियाà¤\95ा à¤ªà¤¾à¤¨à¤¾à¤¹à¤°à¥\82",
+       "whatlinkshere": "याà¤\81à¤\96ाà¤\87 à¤\95ि à¤\9cà¥\8bणà¥\80न्छ",
+       "whatlinkshere-title": "$1 à¤¸à¤¿à¤¤ à¤\9cà¥\8bडियाऽ à¤ªà¤¨à¥\8dनाà¤\85न",
        "whatlinkshere-page": "पानो",
        "linkshere": "निम्न पानाहरू '''[[:$1]]''' मी जुडन्छ :",
        "nolinkshere-ns": "चुनियाको नामस्थानमी '''[[:$1]]''' सित जुड्न्या पानाहरू नाइथिन्।",
        "whatlinkshere-prev": "{{PLURAL:$1|पैलो|पैलो $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|अर्को|अर्को $1}}",
        "whatlinkshere-links": "← लिंकहरू",
-       "whatlinkshere-hideredirs": "$1 à¤\85नà¥\81पà¥\8dरà¥\87षित हुन्छ",
-       "whatlinkshere-hidetrans": "$1 à¤ªà¤¾à¤°à¤¦à¤°à¥\8dशन",
-       "whatlinkshere-hidelinks": "$1 लिङ्कहरु",
-       "whatlinkshere-hideimages": "$1 फाइल लिंकहरू",
+       "whatlinkshere-hideredirs": "$1 à¤ªà¥\81न:निरà¥\8dदà¥\87शित हुन्छ",
+       "whatlinkshere-hidetrans": "$1 à¤¸à¤®à¥\8dमà¥\80ल",
+       "whatlinkshere-hidelinks": "$1 लिङ्क",
+       "whatlinkshere-hideimages": "$1 फाइलआ लिङ्कअन",
        "whatlinkshere-filters": "छानियाका",
        "ipbreason-dropdown": "* ब्लक गर्नुका समान्य कारणहरू\n** झूटो सूचना दियाको\n** पानानबठे सामाग्रीहरू हटायाको\n** बाहिरी जालक्षेत्र (sites)सित नचाहिंदो लिङ्क गर्याको \n** पानानमी बकवास/गाली-गलौच हाल्याको\n** भै धेकाउने व्यवहार/उत्पीडन (सताउने कार्य) गर्याको\n** धेरै गलत खाताहरू बनायाको\n** प्रयोगकर्ता नाम अस्वीकार्य",
        "ipboptions": "२ घण्टाहरू:2 hours,१ दिन :1 day,३ दिनहरू:3 days,१ हप्ता:1 week,२ हप्ताहरू:2 weeks,१ महिना:1 month,३ महिनाहरू:3 months,६ महिनाहरू:6 months,१ वर्ष:1 year,अनगिन्ती:infinite",
        "ipblocklist": "ब्लक गर्याका प्रयोगकर्ताहरू",
        "ipblocklist-legend": "ब्लक गर्याका प्रयोगकर्ताहरू खोज",
        "blocklink": "रोक्न्या",
-       "contribslink": "यà¥\8bà¤\97दानहरà¥\82",
+       "contribslink": "यà¥\8bà¤\97दानà¤\85न",
        "block-log-flags-anononly": "नाम नभयाका प्रयोकर्ताहरू मात्र",
        "proxyblockreason": "तमरो IP ठेगानामी रोक लगायाको छ किनकी यो खुला प्रोक्सी हो ।\nकृपया तमरो इन्टरनेट सेवा प्रदायक या प्राविधिक सहायतासँग सम्पर्क गरीबर यै सुरक्षा समस्याका बारेमी जानकारी गराओ ।",
        "sorbsreason": "तमरो IP ठेगाना खुल्ला प्रोक्सीको रुपमी  DNSBL मा सूचीकरण गरिएको छ यैलाई{{SITENAME}}ले प्रयोगमी ल्यायाको छ।",
        "import-error-edit": "तमलाई सम्पादन गद्या अनुमति नभयाको पानो \"$1\" आयात गरिएन ।",
        "import-error-create": "तमलाई नयाँ बनाउने अनुमति नभयाको पानो \"$1\" आयात गरिएन ।",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|संशोधन|संशोधनहरू}} आयात भयो",
-       "tooltip-pt-userpage": "तमरो प्रयोगकर्ता पानो",
+       "tooltip-pt-userpage": "{{GENDER:|तमरो प्रयोगकर्ता}} पान्नो",
        "tooltip-pt-anonuserpage": "तमी जो IP ठेगानाको रुपमी सम्पादन गद्दै छौ , त्यैको प्रयोगकर्ता पानो निम्न छ :",
-       "tooltip-pt-mytalk": "तमरो कुरडीकानी पानो",
-       "tooltip-pt-preferences": "तमरा अभिरुचिहरू",
+       "tooltip-pt-mytalk": "{{GENDER:|तमरो}} कुरडीकानी पानो",
+       "tooltip-pt-preferences": "{{GENDER:|तमरी}} अभिरुचि",
        "tooltip-pt-watchlist": "पृष्ठहरूको सूची जैका फेरबदलहरुलाई तमले पहरा गरिराखेका छौ ।",
-       "tooltip-pt-mycontris": "तमरो योगदानको सूची",
-       "tooltip-pt-login": "तमलाई प्रवेशगद्द सुझाव दिइन्छ ; याद अर यो जरुरी आथिन भण्या ।",
+       "tooltip-pt-mycontris": "{{GENDER:|तमरा}} योगदानअनऐ सूची",
+       "tooltip-pt-login": "तमलाई प्रवेशगद्द सुझाव दिइन्छ ; याद अर यो जरुरी आथिन भण्या ।",
        "tooltip-pt-logout": "बाहिर निस्कन्या (लग आउट)",
-       "tooltip-pt-createaccount": "तमलाई खाता बनौन लै लग इन अद्द हम हौसला अद्दाउ; काइकि, यो अनिवार्य नाइथी भण्या ।",
-       "tooltip-ca-talk": "सामाà¤\97à¥\8dरà¥\80 à¤ªà¥\83षà¥\8dठबारà¥\87मà¥\80 à¤\9bलफल",
-       "tooltip-ca-edit": "ये पाना सम्पादन गर",
+       "tooltip-pt-createaccount": "तमलाई खाता बनौन लै लगइन अद्द लै हम हौसला अद्दाउ; यद्यपि, यो अनिवार्य नाइथीन।",
+       "tooltip-ca-talk": "सामाà¤\97à¥\8dरà¥\80 à¤ªà¥\83षà¥\8dठबारà¥\87मà¥\80 à¤\95à¥\81रणिà¤\95ाà¤\86नà¥\80",
+       "tooltip-ca-edit": "येइ पन्ना सम्पादन गरऽ",
        "tooltip-ca-addsection": "नयाँ खण्ड सुरु अरिदिय",
        "tooltip-ca-viewsource": "यो पानो सुरक्षित अरियाको छ। यैको श्रोत हेद्द सकन्छौ ।",
-       "tooltip-ca-history": "यà¥\88 à¤ªà¥\83षà¥\8dठà¤\95ा à¤ªà¥\88लà¥\8dलिà¤\95ा à¤ªà¥\81नरावलà¥\8bà¤\95नहरà¥\81",
+       "tooltip-ca-history": "यà¥\88 à¤ªà¤¨à¥\8dनाऽ à¤ªà¥\88लà¥\8dलिà¤\95ा à¤ªà¥\81नरावलà¥\8bà¤\95नà¤\85न",
        "tooltip-ca-undelete": "मेट्याको भया पनि यै पानाको सम्पादनहरू पुन:प्राप्त गर",
        "tooltip-ca-move": "यो पानालाई अर्खिठौर सार",
        "tooltip-ca-watch": "यै पानालाई तमरा ध्यानसूचीमि थपिदिय",
-       "tooltip-search": "{{SITENAME}}मी खोज",
-       "tooltip-search-go": "यदि à¤¯à¥\8b à¤¨à¤¾à¤®à¤\95à¥\8b à¤ªà¥\83षà¥\8dठ à¤°à¤¯à¤¾à¤\95à¥\8b à¤\9b à¤­à¤£à¥\8dया à¤¤à¥\88मà¥\80 à¤\9cानà¥\8dया ।",
-       "tooltip-search-fulltext": "यà¥\88 à¤ªà¤¾à¤ à¤\95ा à¤²à¤¾à¤\97ि à¤ªà¤¾à¤¨à¤¾मी खोज",
-       "tooltip-p-logo": "à¤\96ास à¤ªà¤¾à¤¨à¥\8b",
+       "tooltip-search": "{{SITENAME}}मी खोज",
+       "tooltip-search-go": "यदà¥\80 à¤ à¥\8dयाà¤\95à¥\8dà¤\95à¥\88 à¤¯à¥\87à¤\87 à¤¨à¤¾à¤\89à¤\81 à¤­à¤¯à¤¾: à¤ªà¤¨à¥\8dना à¤°à¥\88à¤\9b à¤­à¤\81णà¥\8dया à¤¤à¥\88 à¤®à¥\80 à¤\9cा:।",
+       "tooltip-search-fulltext": "यà¥\88 à¤ªà¤¾à¤ à¤\95ा à¤²à¤¾à¤\97ि à¤ªà¤¨à¥\8dनाà¤\85नमी खोज",
+       "tooltip-p-logo": "मà¥\81à¤\96à¥\8dय à¤ªà¤¨à¥\8dनामà¥\80 à¤¹à¥\87रऽ",
        "tooltip-n-mainpage": "खास पानामी झान्या",
-       "tooltip-n-mainpage-description": "à¤\96ास à¤ªà¤¾à¤¨à¤¾à¤®à¥\80 à¤\9dा",
+       "tooltip-n-mainpage-description": "à¤\96ास à¤ªà¤¨à¥\8dनामà¥\80 à¤\9dाऽ",
        "tooltip-n-portal": "आयोजनाका बारेमी , तम कि अद्द सकन्छौ , समान काखाइ  भेटौन्या",
        "tooltip-n-currentevents": "हालैका घटनाको बारेमी पृष्ठभूमि जानकारी पत्ता लागाइदिय",
-       "tooltip-n-recentchanges": "विà¤\95िमा à¤\85रियाà¤\95ा à¤¹à¤¾à¤²à¥\88à¤\95ा à¤­à¤¯à¤¾ à¤«à¥\87रबदलà¤\95ा शुचि ।",
-       "tooltip-n-randompage": "à¤\9cà¥\8b à¤\95à¥\8bà¤\87 à¤ªà¤¾à¤¨à¥\8b à¤\96à¥\8bलà¥\8dया",
-       "tooltip-n-help": "खोज्जु पड्या ठौर ।",
-       "tooltip-t-whatlinkshere": "यà¥\8b à¤¸à¤¿à¤¤ à¤\9cà¥\8bडियाà¤\95ा à¤¸à¤¬à¥\8dबà¥\88 à¤µà¤¿à¤\95ि à¤ªà¤¾à¤¨à¤¾à¤¨à¤\95à¥\8b à¤¸à¥\82à¤\9aà¥\80",
+       "tooltip-n-recentchanges": "विà¤\95िमà¥\80 à¤¹à¤¾à¤²à¥\88 à¤\85रियाà¤\95ा à¤«à¥\87रबदलà¥\88 शुचि ।",
+       "tooltip-n-randompage": "à¤\95à¥\8dरमरहित à¤ªà¤¨à¥\8dना à¤\96à¥\8bलऽ",
+       "tooltip-n-help": "à¤\96à¥\8bà¤\9cà¥\8dà¤\9cà¥\81 à¤ªà¤¡à¥\8dडà¥\8dया à¤ à¥\8cर à¥¤",
+       "tooltip-t-whatlinkshere": "सपà¥\8dपà¥\88 à¤µà¤¿à¤\95ि à¤ªà¤¨à¥\8dनाà¤\85नà¥\88 à¤¶à¥\81à¤\9aि à¤\9cà¥\8b à¤¯à¤¾à¤\81à¤\96ाà¤\87 à¤\9cà¥\8bणà¥\80à¤\9cान",
        "tooltip-t-recentchangeslinked": "यै पानामी जोडियाका पानामी अहिलको परिवर्तन",
        "tooltip-feed-atom": "यै पानाकी लेखा एक एटम फिड",
-       "tooltip-t-contributions": "यिन प्रयोगकर्ताका योगदानहरूको सूची हेरपुई",
-       "tooltip-t-upload": "à¤\9aितà¥\8dर à¤\85पà¥\8dलà¥\8bड à¤\85र",
-       "tooltip-t-specialpages": "सब्बै खास खास पानानको शुचि ।",
-       "tooltip-t-print": "यà¥\8b à¤ªà¤¾à¤¨à¤¾à¤\95à¥\8b à¤\9bापिन्या संस्करण",
+       "tooltip-t-contributions": "{{GENDER:$1|यिन प्रयोगकर्ता}}ऽ योगदानन ऐ शुचि",
+       "tooltip-t-upload": "फाà¤\87ल à¤\85पà¥\8dलà¥\8bड à¤\85रऽ",
+       "tooltip-t-specialpages": "सब्बै खास-खास पन्नाअनै सूची",
+       "tooltip-t-print": "यà¥\87à¤\87 à¤ªà¤¨à¥\8dनाऽ à¤\9bापà¥\8dद à¤®à¤¿à¤²à¥\8dल्या संस्करण",
        "tooltip-t-permalink": "पृष्ठको यो पुनरावलोकनकि लेखा स्थाई लिङ्क",
        "tooltip-ca-nstab-main": "सामाग्री पानो हेरिदिय",
        "tooltip-ca-nstab-user": "प्रयोगकर्ता पानो हेरिदिय",
        "siteusers": "{{SITENAME}} {{PLURAL:$2|प्रयोगकर्ता|प्रयोगकर्ताहरू}} $1",
        "anonusers": "{{SITENAME}} का नाम नभयाका {{PLURAL:$2| प्रयोगकर्ता|प्रयोगकर्ताहरू}} $1",
        "simpleantispam-label": "ऐन्टी-स्प्याम जाँच।\nयैलाई <strong>नाइँ</strong> भद्य्या!",
-       "pageinfo-toolboxlink": "यà¥\88 à¤ªà¤¾à¤¨à¤¾à¤\95à¥\8b à¤\9cाणकारी",
+       "pageinfo-toolboxlink": "पनà¥\8dनाà¤\87 à¤\9cानकारी",
        "rcpatroldisabled": "अहिलका परिवर्तनहरू गस्ती निष्क्रिय पार्याको छ ।",
        "rcpatroldisabledtext": "अहिलका परिवर्तनहरू गस्ती गुण अहिलको लागि निष्कृय पारियाको छ ।",
        "markedaspatrollederror-noautopatrol": "तमी आफ्नै सम्पादनलाई गस्ती गरियाको भनि चिनो लगाउन नाइसक्दा ।",
        "watchlistedit-clear-done": "तमरो ध्यान सूची खाली गरीयाको छ।",
        "watchlisttools-view": "आधारित फेरबदलीहरू हेर",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|कुरडी]])",
-       "specialpages": "à¤\96ास à¤ªà¤¾à¤¨à¥\8b",
+       "specialpages": "à¤\96ास à¤ªà¤¨à¥\8dनाà¤\85न",
        "specialpages-note": "* साधारण खास पानाहरू।\n* <span class=\"mw-specialpagerestricted\">निषेधित खास पानाहरू।</span>",
        "specialpages-group-changes": "अल्लैका परिवर्तन लगहरू",
        "tags": "मान्य परिवर्तन ट्यागहरू",
        "logentry-newusers-create": "प्रयोगकर्ता खाता $1 {{GENDER:$2|खोलियो}}",
        "logentry-upload-upload": "$1 ले $3 {{GENDER:$2|अपलोड अरेका छन्}}",
        "feedback-bugornote": "यदि तमी कुनै प्राविधिक समस्यालाई विस्तारले सम्झाउन तयार छौ भण्या कृपया [$1 बग राख]।\nयदि हैन, भण्या तमी तल दियाको सरल फारमको प्रयोग गद्दसक्द्याहौ । तमरो टिप्पणी, तमरो प्रयोगकर्ता नाम र तमरो ब्राउजरको नाम सहित \"[$3 $2]\" पानामी जोडिन्याछ ।",
-       "searchsuggest-search": "खोज",
+       "searchsuggest-search": "खोज:",
        "api-error-duplicate": "यै साइटमी पहिलीबठे यस्तै सामग्री {{PLURAL:$1|भयाको अर्को फाइल छ|भयाका  केहि अरु फाइलहरू छन्}} ।",
        "api-error-duplicate-archive": "यै साइटमी पहिलेबाट यस्तै सामग्री {{PLURAL:$1|भयाको अर्को फाइल थियो|भयाका केहि अरु फाइलहरू थिए}} ।\nतर {{PLURAL:$1|यो मेट्याको थियो|यी मेटायाका थिए}} ।",
        "expand_templates_preview_fail_html": "<em>किनकि {{SITENAME}} सिधै एचटिएमयल सक्षम छ र तमीले लग इन गर्या छैनौ, पूर्वावलोकन लुकाइयाको छ ताकि सम्भावित जाभास्क्रिप्ट आक्रमणलाई रोक्द सकियोस् ।</em>\n\n<strong>यदि यो मान्य पूर्ववावलोकन प्रयास हो भण्या पुन प्रयास गर ।</strong>\nयदि यसले कार्य पूर्ण भएन भण्या [[Special:UserLogout|लग आउट गरिबर]] फेरी लग इन गर्या ।",
index 0c6b8ef..d1d84b0 100644 (file)
        "morenotlisted": "Cl'elèinch ché an n'é mìa finî.",
        "mypage": "Pàgina",
        "mytalk": "Al mē discusiòun",
-       "anontalk": "Discusiòun per cl' IP ché",
+       "anontalk": "Discusiòun",
        "navigation": "Navigasiòun",
        "and": "&#32;e",
        "qbfind": "Câta",
        "laggedslavemode": "'''Atèinti:''' la pàgina la pré avèir mìa al revisiòun pió nōv.",
        "readonly": "'Database' bluchê",
        "enterlockreason": "Scréver al mutîv dal blôch, precişêr quând a 's pèinsa che 'l vègna tôt via.",
-       "readonlytext": "In cól mumèint ché al databêş l'é bluchê e an 's pōlen fêr né zûnti né mudéfichi. Al blôch ed sôlit l'é lighê a 'na revişiòun normêla e quând la srà finîda al gnirà sbluchê. \n\nL'aminitradōr dal sistēma ch' al l'à bluchê l'à dê cla spiegasiòun ché: $1",
+       "readonlytext": "In cól mumèint ché al databêş l'é bluchê e an 's pōlen fêr né zûnti né mudéfichi. Al blôch ed sôlit l'é lighê a 'na revişiòun normêla e quând la srà finîda al gnirà sbluchê. \n\nL'aminitradōr dal sistēma ch' l'à urdnê 'l blôch l'à dê cla spiegasiòun ché: $1",
        "missing-article": "Al datebêş an n'à mìa catê al tèst ed 'na pàgina ch' l' aré duvû catêres sòt' al nòm \"$1\" $2. Ed sôlit còst a sucēd quând a vîn arciamê, a partîr da la stòria dal mudéfichi o dal cunfrûnt tra versiòun, un colegamèint a 'na pàgina scanşlêda, a un cunfrûnt tra versiòun che gh'în mìa o a un cunfrûnt tra versiòun cun la stòria dal mudéfichi scanşlêda. In chês cuntrâri, a s'é pubabilmèint catê un erōr int al prugrâma ed Media Wiki. A se dmânda al piaşèir ed comunichêr còl ch'é sucès a un [[Special:ListUsers/sysop|amministadōr]] e comunichêregh l'indirés (URL) in quistiòun.",
        "missingarticle-rev": "(nómer ed la versiòun: $1)",
        "missingarticle-diff": "(Diff: $1, $2)",
        "viewsource": "Guêrda la surzéia",
        "viewsource-title": "Guêrda la surzéia 'd $1",
        "actionthrottled": "L'asiòun la vîn tardêda.",
-       "actionthrottledtext": "Cme mişûra 'd sicurèsa cûnt'r al spam soquânti operasiòun a vînen limitêdi a 'n nómer mâsim ed vôlti in un precîş peréiod ed tèimp, in cól chêş ché a s'é bèle andê d'ed là 'd cól lémit. A se dmânda ed turnêr a pruvêr tra soquânt minût.",
+       "actionthrottledtext": "Cme mişûra 'd sicurèsa cûntra l'abûş soquânti operasiòun a vînen limitêdi a 'n nómer mâsim ed vôlti in un precîş peréiod ed tèimp, in cól chêş ché a 's é bèle andê d'ed là 'd cól lémit. A 's e-dmânda 'd turnêr a pruvêr tra soquânt minût.",
        "protectedpagetext": "Cla pàgina ché l'é stêda prutèta per impidîr la mudéfica o êtri operasiòun.",
        "viewsourcetext": "L'é pusébil vèder e cupiêr al côdis surzéia ed cla pàgina ché.",
        "viewyourtext": "L'é pusébil vèder e cupiêr al côdis surzéia dal <strong>tō mudéfichi</strong> ed cla pàgina ché.",
        "protectedinterface": "Cla pàgina ché la gh'à 'n elemèint ch' al fa pêrt dal colegamèint tra utèint e al progrâma 'd cól sît ché e l'é prutèta per schivşêr pusébil abûş. Per zuntêr o mudufichêr tradusiòun per tót i sistēma wiki druvêr [https://translatewiki.net/ translatewiki.net], al prugèt 'd adatamèint a ògni léngva 'd MediaWiki.",
        "editinginterface": "<strong>Atèinti:</strong> Al tèst ed cla pàgina ché 'l fa pêrt dal colegamèint tra utèint e 'l prugrâma dal sît.  Tót' al modéfichi fâti a cla pàgina ché a se spècen in sém a i mesâg vést per tót j utèint ed cól wiki ché.",
        "translateinterface": "Per zuntêr o mudifichêr al tradusiòun vâlidi in sém a tót i wiki, drōva [https://translatewiki.net/ translatewiki.net], al prugèt Media Wiki p'r al léngui di divêrs pôst.",
-       "cascadeprotected": "Insém a cla pàgina ché an n'é mìa pusébil fêr dal mudéfichi perchè l'é dèinter {{PLURAL:$1|int la pàgina sgnêda ché  'd sègvit, ch' l'é stêda prutèta|int al pàgini sgnêdi ché  'd sègvit, ch' în stêdi prutèti}} cun la prutesiòun ch' la 's arfà in cuntinvasiòun:\n$2",
+       "cascadeprotected": "In sém a cla pàgina ché an n'é mìa pusébil fêr dal mudéfichi perchè l'é dèinter {{PLURAL:$1|int la pàgina sgnêda ché  'd sègvit, ch' l'é stêda prutèta|int al pàgini sgnêdi ché  'd sègvit, ch' în stêdi prutèti}} cun la prutesiòun ch' la 's arfà in cuntinvasiòun:\n$2",
        "namespaceprotected": "An 's gh'à mìa i permès necesâri per mudifichêr al pàgini dal spâsi di nòm <strong>$1</strong>.",
        "customcssprotected": "An 's gh'à mìa i permès necesâri per mudifichêr cla pàgina CSS ché, perchè la gh'à dèinter al j impustasiòun personêli 'd n' êter utèint.",
        "customjsprotected": "An 's gh'à mìa i permès necesâri per mudifichêr cla pàgina JavaScript ché, perchè la gh'à dèinter al j impustasiòun personêli 'd n' êter utèint.",
        "mypreferencesprotected": "An 's gh'à mìa i permès necesâri per cambiêr al preferèinsi personêli.",
        "ns-specialprotected": "An n'é mìa pusébil mudifichêr al pàgini specêli.",
        "titleprotected": "Al tétol ed cla pagina ché l'é stê bluchê da [[User:$1|$1]].\nCòst l'é al mutîv: <em>$2</em>.",
-       "filereadonlyerror": "An n'é mìa stê pusébil mudifichêr al file \"$1\" perchè al depôsit di file \"$2\" a 's pōl sōl lēzer.\n\nL'aministradōr ch' al l'à bluchê l'à dê cla spiegasiòun ché:\"$3\".",
+       "filereadonlyerror": "An n'é mìa stê pusébil mudifichêr al file \"$1\" perchè al depôsit di file \"$2\" a 's pōl sōl lēzer.\n\nL'aministradōr dal sistēma ch' al l'à bluchê l'à dê cla spiegasiòun ché:\"$3\".",
        "invalidtitle-knownnamespace": "Tétol mìa vâlid cme spâsi di nòm \"$2\" e tèst \"$3\"",
        "invalidtitle-unknownnamespace": "Tétol mìa vâlid cun spâsi di nòm mìa cgnusû \"$1\" e tèst \"$2\"",
        "exception-nologin": "An t'é mìa gnû dèinter",
        "yourpasswordagain": "Scrév incòra la cêva 'd ingrès:",
        "createacct-yourpasswordagain": "Cunfērma la cêva 'd ingrès",
        "createacct-yourpasswordagain-ph": "Tōrna mèter dèinter la cêva 'd ingrès",
-       "remembermypassword": "Tîn a mèint la cêva 'd ingrès insém a cól navigadōr ché (per un mâsim ed $1{{PLURAL:$1|dé}}).",
        "userlogin-remembermypassword": "Sèimper coleghê",
        "userlogin-signwithsecure": "Drōva un colegamèint sicûr",
        "yourdomainname": "Precişêr al duméni:",
        "createacct-reason": "Mutîv",
        "createacct-reason-ph": "Perchè ét drē fêr 'n' êtra utèinsa",
        "createacct-submit": "Fà la tó utèinsa",
-       "createacct-another-submit": "Fà 'n' êtra utèinsa.",
+       "createacct-another-submit": "Fà l' utèinsa.",
        "createacct-benefit-heading": "{{SITENAME}} crès grâsia a persòuni cme té.",
        "createacct-benefit-body1": "{{PLURAL:$1|mudéfica|mudéfichi}}",
        "createacct-benefit-body2": "{{PLURAL:$1|pàgina|pàgini}}",
        "nocookieslogin": "Per fêr l'ingrès a {{SITENAME}} a 's dēv druvêr i cookie che rişûlten bluchê. Tōrna fêr l'ingrès dôp avèir şbluchê i cookie int al tó navigadōr.",
        "nocookiesfornew": "L'iscrisiòun utèint an n'é mìa stêda fâta, perchè òm mìa prû cunfermêr la só urégin. Veréfica 'd avèir şbluchê i cookie, tōrna carghêr cla pàgina ché e prōva incòra.",
        "noname": "Al nòm utèint scrét an n'é mìa vâlid.",
-       "loginsuccesstitle": "Ingrès fât.",
+       "loginsuccesstitle": "Fât l'ingrès.",
        "loginsuccess": "''' T'é stê coleghê al terminêl {{SITENAME}} cun al nòm utèint '''$1'''.",
-       "nosuchuser": "An n'é mìa registrê nisûn utèint cun al nòm \"$1\". I nòm utèin în sensébil al lètri grândi. Veréfica al nòm scrét o [[Special:CreateAccount|fà un nōv ingrès]].",
+       "nosuchuser": "An n'é mìa registrê nisûn utèint cun al nòm \"$1\". I nòm utèin în sensébil al lètri grândi. Veréfica al nòm scrét o [[Special:CreateAccount|fà 'na nōva utèinsa]].",
        "nosuchusershort": "An gh'é mìa registrê un utèint ciamê ''$1''. Veréfica al nòm scrét.",
        "nouserspecified": "L'é necesâri precişêr un nòm utèint.",
        "login-userblocked": "Cl'utèinsa ché l'é bluchêda. An n'é pusébil fêr l'ingrès.",
        "noemail": "Nisûn indirés ed pôsta eletrônica registrê per l'utèint $1.",
        "noemailcreate": "L'é necesâri dêr un 'indirés ed pôsta eletrônica vâlid.",
        "passwordsent": "'Na nōva cêva 'd ingrès l'é stêda spidîda a l'indiré ed pôsta eletrônica per l'utèint \"$1\". Per piaşèir, fà un ingrès apèina 't la ricēv.",
-       "blocked-mailpassword": "Per pervèder abûş, an n'é mìa permès druvêr la funsiòun \"spidés 'na nōva cêva 'd ingrès\" da un indirés IP bluchê.",
+       "blocked-mailpassword": "Al tó indirés IP l’è bluchê int la mudéfica. Per pervèder abûş, an n'é mìa permès druvêr la funsiòun ed recóper ed la cêva ‘d ingrès da cl’indirés IP ché.",
        "eauthentsent": "Un mesâg ed cunfèirma l'é stê spidî a l'indirés ed pôsta eletrônica sgnê ché. L'utèint per prèir inviêr di mesâg ed pôsta eletrônica al dēv andêr a drē al j istrusiòun scréti, in môd da cunfermêr ch' l'é ló al legétim proprietâri 'd l'indirés.",
        "throttled-mailpassword": "Un mesâg ed pôsta eletrônica 'd arnōv ed la cêva 'd ingrès l'é bèle stê inviê da mēno 'd {{PLURAL:$1|1 ōra|$1 ōri}}. Per pervèder abûş, la funziòun 'd arnōv ed la cêva 'd ingrès la pōl èser druvêda sōl 'na vôlta ògni {{PLURAL:$1|1 ōra|$1 ōri}}.",
        "mailerror": "Erōr int la spedisiòun dal mesâg $1",
        "createaccount-title": "Per fêr un inscrisiòun a {{SITENAME}}",
        "createaccount-text": "Quelchidûn l'à fât un inscrisiòun a  {{SITENAME}} ($4) a nòm ed $2 coleghê a cl'indirés ed pôsta eletrônica ché. La cêva 'd ingrès per l'utèint \"$2\" l'é  impustêda a \"$3\". \nÉ necesâri fêr un ingrès préma ch' es pôl e cambiêr subét la cêva 'd ingrès. \nSe l'inscrisiòun l'é stêda fâta per şbâli, es pōl scanşlêr sté mesâg.",
        "login-throttled": "În stê fât trôp tentatîv 'd ingrès in pôch tèimp. Spèta $1 e pó tōrna pruvêr.",
-       "login-abort-generic": "An t'é mìa stê arcgnusû - Scanşlê",
+       "login-abort-generic": "Al tó ingrès an n’è mia stê fât – Al vîn scanşlê",
        "login-migrated-generic": "La tó utèinsa l'é stêda spustêda, e al tó nòm utèint al gh'é mìa pió in sém a cla wiki ché.",
        "loginlanguagelabel": "Léngva: $1",
        "suspicious-userlogout": "La tó dmânda per destachêret l'é stēda rifiutêda perchè la sèmbra spidîda da un navigadōr ch' al funsiòuna mìa o da un proxy di caching.",
        "newpassword": "Nōva cêva 'd ingrès:",
        "retypenew": "Scrév incòra la nōva cêva 'd ingrès:",
        "resetpass_submit": "Scrév la cêva 'd ingrès e và dèinter al sît",
-       "changepassword-success": "La cêva 'd ingrès l'é stêda nudifichêda!",
+       "changepassword-success": "La tó cêva 'd ingrès l'é stêda mudifichêda!",
        "changepassword-throttled": "În stê fât trôp tentatîv 'd ingrès in pôch tèimp. Spèta $1 e pó tōrna pruvêr.",
        "resetpass_forbidden": "An 'né mìa pusébil mudifichêr la cêva 'd ingrès",
        "resetpass-no-info": "Per andêr dèinter a cla pàgina ché 't gh'ê da fêr l'ingrès.",
        "resetpass-submit-loggedin": "Câmbia la cêva 'd ingrès",
        "resetpass-submit-cancel": "Scanşèla",
-       "resetpass-wrong-oldpass": "Cêva 'd ingrès pruvişôria o còla 'd adès mìa vâlida.\nLa cêva 'd ingrès la pré èser stêda bèle cambiêda, opór n'in pré èser stê dmandê 'na nōva pruvişôria.",
+       "resetpass-wrong-oldpass": "Cêva 'd ingrès pruvişôria o còla 'd adès an n'é mìa vâlida.\nLa cêva 'd ingrès la pré èser stêda bèle cambiêda, opór in pré èser stê dmandê 'na nōva pruvişôria.",
        "resetpass-recycled": "Mèt dèinter 'na cêva 'd ingrès divêrsa da còla 'd adès.",
        "resetpass-temp-emailed": "L'ingrès l'é stê fât cun un côdis pruvişôri. Per finîr la registrasiòun, l'é necesâri impustêr 'na nōva cêva 'd ingrès ché:",
        "resetpass-temp-password": "Cêva 'd ingrès pruvişôria:",
        "passwordreset-emailtext-ip": "Quelchidûn (prubabilmèint té, cun l'indirés IP $1) l'à dmandê de spidîregh 'na nōva cêva 'd ingrès per andêr dèinter a {{SITENAME}} ($4). {{PLURAL:$3|L'utèint inscrét| J utèint inscrét}} a sté indirés ed pôsta eletrônica în:\n \n$2 \n\n{{PLURAL:$3|Cla cêva 'd ingrès pruvişôria la scadrà| St' al cêvi 'd ingrès pruvişôri ché scadrân}} dôp {{PLURAL:$5|ûn dé|$5 dé}}. Ét duvrés andêr dèinter e sernîr 'na cêva 'd ingrès nōva adès. \n\nSe t'é mìa stê té a fêr la dmânda, o s' ét t'é ricurdê la cêva 'd ingrès uriginêla e an 't vō mia pió cambiêrla, ét pō scanşlêr cól mesâg ché e cuntinvêr a druvêr la tó cêva 'd ingrès vècia.",
        "passwordreset-emailtext-user": "L'utèint $1 ed {{SITENAME}} l'à dmandê de spidîregh 'na nōva cêva 'd ingrès per andêr dèinter a {{SITENAME}} ($4). {{PLURAL:$3|L'utèint inscrét| J utèint inscrét}} a sté indirés ed pôsta eletrônica în:\n\n$2 \n\n{{PLURAL:$3|Cla cêva 'd ingrès pruvişôria ché la scadrà| St' al cêvi 'd ingrès pruvişôri ché scadrân}} dôp {{PLURAL:$5|ûn dé|$5 dé}}. Ét duvrés andêr dèinter e sernîr 'na cêva 'd ingrès nōva adès. \n\nSe t'é mìa stê té a fêr la dmânda, o s' ét t'é ricurdê la cêva 'd ingrès uriginêla e an 't vō mia pió cambiêrla, ét pō scanşlêr cól mesâg ché e cuntinvêr a druvêr la tó cêva 'd ingrès vècia",
        "passwordreset-emailelement": "Nòm utèint: \n$1\n.\nCêva 'd ingrès pruvişôria: \n$2",
-       "passwordreset-emailsentemail": "É stê spidî un mesâg ed pôsta eletrônica per turnêr a impustêr la cêva 'd ingrès.",
-       "passwordreset-emailsent-capture": "É stê spidî un mesâg ed pôsta eletrônica per turnêr a impustêr la cêva 'd ingrès, ché sòta a gh'é al tèst che gh'é scrét.",
-       "passwordreset-emailerror-capture": "É stê fât un mesâg ed pôsta eletrônica per turnêr a impustêr la cêva 'd ingrès, scréta ché 'd sègvit. La spedisiòun {{GENDER:$2|a l'utèint}} an n'é mia 'riusîda:$1",
-       "changeemail": "Câmbia l'indirés ed la pôsta eletrônica",
-       "changeemail-header": "Câmbia l'indirés ed la pôsta eletrônica 'd la tó inscrisiòun.",
+       "passwordreset-emailsentemail": "Se cl’indirés ed pôsta eletrônica ché l’è unî a la tó utèinsa, alōra a gnirà spidî ‘na lètra per per turnêr a impustêr la cêva ‘d ingrès.",
+       "changeemail": "Mudéfica o tó via l'indirés ed pôsta eletrônica",
+       "changeemail-header": "Finés cól fòj ché per cambiêr al tó indirés ed pôsta eletrônica, 'S an 't vō mia avèir nisûn indirés coleghê a la tó utèinsa lêsa vōd al spâsi per l'indirés nōv quând té spidés al fòj.",
        "changeemail-no-info": "Per andêr dèinter diretamèint a cla pàgina ché 't gh'ê da fêr l'ingrès.",
        "changeemail-oldemail": "L'indirés ed la pôsta eletrànica 'd adès.",
        "changeemail-newemail": "Nōv indirés ed pàsta eletrônica:",
        "sig_tip": "Fîrma cun la dâta e l'ōra",
        "hr_tip": "Rîga spiâna (drōva cun giudési)",
        "summary": "Ogèt:",
-       "subject": "Argumèint (tétol):",
+       "subject": "Argumèint:",
        "minoredit": "Còsta l'é 'na mudéfica céca",
        "watchthis": "Tîn adrē a cla pàgina ché",
        "savearticle": "Sêlva la pàgina",
        "missingsummary": "'''Atensiòun:''' an n'é mìa stê precişê al mutîv de sté mudéfica. S'es tōrna a clichêr insém a \"{{int:savearticle}}\" la mudéfica la gnirà salvêda cun al mutîv vōd.",
        "selfredirect": "<strong>Ateinti:</strong>t'é drē fêr un rinvéi a l'istèsa pàgina. Ét prés avèir şbaliê sgnêr al pôst dal rinvéi o t'é drē mudifichêr la pàgina şbaliêda. S'ét fê cléch incòra in sém a \"{{int:savearticle}}\", al rinvéi al gnirà fât in tót' al manēri.",
        "missingcommenttext": "Scréver un cumèint ché sòta.",
-       "missingcommentheader": "'''Atensiòun:''' an n'é mìa stê precişê al mutîv/al tétol de sté mudéfica. S'es tōrna a clichêr insém a \"{{int:savearticle}}\" la mudéfica la gnirà salvêda sèinsa tétol.",
+       "missingcommentheader": "<strong>Atensiòun:<strong> an n'é mìa stê precişê l'argumèint de sté mudéfica. S'es tōrna a clichêr insém a \"{{int:savearticle}}\" la mudéfica la gnirà salvêda sèinsa.",
        "summary-preview": "Guêrda préma sûnt:",
-       "subject-preview": "Guêrda préma argumèint/tétol:",
+       "subject-preview": "Guêrda préma l'argumèint:",
        "previewerrortext": "A gh'é stê 'n erōr mèinter a s'é serchê ed guardêr al lavōr préma 'd salvêrel.",
        "blockedtitle": "Utèint bluchê",
        "blockedtext": " '''Al tō nòm utèint o indirés IP l'é stê bluchê.'''\n\nAl blôch l'é stê fât da $1. Al mutîv dal blôch l'é còst:  ''$2''.\n\n*Inési dal blôch: $8\n*Scadèinsa dal blôch: $6\n*Intervâl ed blôch: $7\n\nS' ét vō, l'é pusébil mètres in cuntât cun $1 o 'n êter [[{{MediaWiki:Grouppage-sysop}}|aministradōr]] per discóter dal blôch.\n\nGuêrda che la funsiòun 'Scrév a l'utèint' an n'é mìa in ôvra s' an n'é mìa stê registrtê un indirés ed pôsta eletrônica vâlid int al tō [[Special:Preferences| preferèinsi]] o se sté funsiòun l'é stêda bluchêda. L'indirés IP 'd adèsa l'é $3, al nóme ID dal blôch l'é #$5. T'é perghê ed precişêr tót j elemèint ed préma per ògni dmânda de spiegasiòun",
        "accmailtext": "'Na cêva 'd ingrés l'è stêda fâta a chêş per [[User talk:$1|$1]] e l'è stêda spidîda a $2. Cla cêva 'd ingrès ché la pōl èser cambiêda int la pàgina per ''[[Special:ChangePassword|cambiêr la cêva 'd ingrès]]'' subét dôp avèir fât l'ingrès.",
        "newarticle": "(Nōv)",
        "newarticletext": "Al colegamèint apèina fât al cumbîna cun 'na pàgina ch' an n'é mìa incòra stêda fâta. S'ét vō fêr la pàgina adès, l'é asê cumincêr a scréver al tèst int la caşèla ché sòt (per vedèr infurmasiòun pió precîşi guêrda la [$1 pàgina 'd ajót]). Se al colegamèint  l'é stê avêrt per erōr, l'é asê clichêr al pulsânt \"Indrē\" dal tó navigadōr.",
-       "anontalkpagetext": "----'' Còsta l'è la pàgina 'd discusiòun ed 'n utèint sèinsa nòm, ch' an n' à mìa incòra fât 'n' utèinsa o in tót al manēri an n'è mìa drē druvêrla. Per arcgnòsrel l'è dòunca necesâri druvê al só indirés IP. J indirés IP a pōlen èser spartî cun êter utèint. Se t'è un utèint sèinsa nòm e 't pèins che i cumèint in cla pàgina ché an riguêrden mìa tè, [[Special:CreateAccount|fa 'n' utèinsa nōva]] o [[Special:UserLogin|vîn dèinter cun còla ch' ét gh'ê bèle]] per schivşêr, in futûr,  'd èser cunfûş cun 'd j êter utèint sèinsa nòm.''",
-       "noarticletext": "In cól mumèint ché la pàgina serchêda l'é vōda. L'é pusébil [[Special:Search/{{PAGENAME}}|serchêr sté tétol]] int al j êtri pàgini dal sît, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} serchêr int i regéster coleghê] opór  [{{fullurl:{{FULLPAGENAME}}|action=edit}} mudifichêr la pàgina adèsa]</span>.",
+       "anontalkpagetext": "----\n<em>Còsta l'è la pàgina 'd discusiòun ed 'n utèint sèinsa nòm, ch' an n' à mìa incòra fât 'n' utèinsa o in tót al manēri an n'è mìa drē druvêrla.</em> Per arcgnòsrel l'è dòunca necesâri druvê al nóme dal só indirés IP. J indirés IP a pōlen èser spartî cun êter utèint. Se t'é un utèint sèinsa nòm e 't pèins che i cumèint in cla pàgina ché an riguêrden mìa té, [[Special:CreateAccount|fa 'n' utèinsa nōva]] o [[Special:UserLogin|vîn dèinter cun còla ch' ét gh'ê bèle]] per schivşêr, in futûr,  'd èser cunfûş cun 'd j êter utèint sèinsa nòm.",
+       "noarticletext": "In cól mumèint ché la pàgina serchêda l'é vōda.Ét pō\n[[Special:Search/{{PAGENAME}}|serchêr cól tétoi ché]] int al j êtri pàgini dal sît, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} serchêr int i regéster coleghê] opór  [{{fullurl:{{FULLPAGENAME}}|action=edit}} e fêr cla pàgina ché]</span>.",
        "noarticletext-nopermission": "In cól mumèint ché la pàgina serchêda l'é vōda. L'é pusébil [[Special:Search/{{PAGENAME}}|serchêr sté tétol]] int al j êtri pàgini dal sît o<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} serchêr int i regéster coleghê] <span>, mó an 't gh'ê mìa al permès ed fêr cla pàgina ché.",
        "missing-revision": "La revişiòun #$1 'd la pagina \"{{FULLPAGENAME}}\" l' an gh'è mìa. Còst, ed sôlit, a sucēd mèint'r as va drē a 'n colegamèint a 'na pàgina scanşlêda, in 'na stòria, di lavōr fât, mìa arnuvêda. I particulêr a 's pōlen catêr int al [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} regéster dal scanşladûri].",
        "userpage-userdoesnotexist": "L'inscrisiòun \"<nowiki>$1</nowiki>\" la cumbîna mìa cun 'n utèint registrê. Ét sicûr ed vrèir fêr o mudifichêr cla pàgina ché.",
        "userpage-userdoesnotexist-view": "L'utèin \"$1\" an n'à mìa fât l'inscrisiòun.",
        "blocked-notice-logextract": "Cl'utèint ché adèsa l'é bluchê. \nPer infurmasiòun l'ûltem elemèint dal regéster di blôch l'é scrét ché sòta:",
-       "clearyourcache": "'''Nôta:''' dôpa vèir salvê a pré èser necesâri pulîr la memôria pruvişôria dal navigadôr per vèder i cambiamèint.\n*'''Firefox / Safari''': tgnîr cucê al tâst dal lètri grândi e clichêr insém a \"Ricarica\" opór cucêr i tâst ''Ctrl-F5'' o ''Ctrl-R'' (''⌘-R'' insém a un Mac)\n*'''Google Chrome''': cucêr i tâst ''Ctrl-Shift-R'' (''⌘-Shift-R'' insém a un Mac) \n*'''Internet Explorer''': tgnîr cucê al tâst ''Ctrl'' mènter es fà cléch insém a ''Refresh'', opór cucêr ''Ctrl-F5'' \n*'''Opera''': svudêr dal tót la memôria pruvişôria 'd la lésta ''Strumenti → Preferenze''",
+       "clearyourcache": "<strong>Nôta:</strong> dôpa vèir salvê a pré èser necesâri pulîr la memôria pruvişôria dal navigadôr per vèder i cambiamèint.\n*< strong >Firefox / Safari /<strong>: tgnîr cucê al tâst dal lètri grândi <em>Shift</em> e clichêr in sém a <em>Ricarica</em>, opór cucêr i tâst <em>Ctrl-F5</em> o <em>Ctrl-R</em> (<em>⌘-R</em> in sém a ‘n Mac)\n*<strong>Google Chrome:</strong>: cucêr i tâst <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> in sém a ‘n Mac) \n*<strong>Internet Explorer:</strong>: tgnîr cucê al tâst<em>Ctrl</em> e fêr cléch in sém a em>Aggiorna</em>, opór clichêr <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Và int al <em>Menu → Impostazioni</em> (<em>Opera → Preferenze</em> in séma 'n Mac)  e pó in <em>Privacy & sicurezza → Pulisci dati del browser → Immagini e file nella cache</em>.",
        "usercssyoucanpreview": "'''Cunséli:''' drōva al tâst 'Guêrda préma' per pruvêr al tó nōv CSS préma 'd salvêrel'''",
        "userjsyoucanpreview": "'''Cunséli:''' drōva al tâst 'Guêrda préma' per pruvêr al tó nōv  JavaScript préma 'd salvêrel'''",
        "usercsspreview": "'''Còsta l'é sōl 'na guardêda al tó CSS préma 'd salvêr al mudéfichi ch'în stêdi fâti.Ricôrdet che al mudéfichi în mìa incòra stêdi salvêdi!'''",
        "previewnote": "'''Ricôrdet che còsta l'é sōl 'na guardêda préma 'd salvêr.'''\nAl tō mudéfichi în MIA incòra stêdi salvêdi.",
        "continue-editing": "Và int la zôna 'd mudéfica",
        "previewconflict": "La vésta la cumbîna cun al tèst int la zôna 'd mudéfica tèst ché d'ed sōver e l'é cme la srà la pàgina s'ed decéd ed clichêr insém a \"Sêlva la pàgina\" in cól mumèint ché.",
-       "session_fail_preview": "'''An n'é mìa stê pusébil registrêr la mudéfica perchè a s' în pêrsi al j infurmasiòun relatîvi a la sesiòun. Tōrna a pruvêr. Se al prublēma al cunténva, a 's pōl pruvêr [[Special:UserLogout|ed coleghêres]] e fêr un ingrès nōv.'''",
+       "session_fail_preview": "A's în dispiêş. An n'é mìa stê pusébil registrêr la mudéfica perchè a 's în pêrsi al j infurmasiòun relatîvi a la sesiòun. Ét prés èser stê destachê. <strong>Contròla s' t'é incòra coleghê</strong>. Se al problēma 'l cunténva, a 's pōl pruvêr [[Special:UserLogout|ed coleghêres]] e fêr un ingrès nōv, contròla ânch se al tó navigadōr l' acèta i cookie da cól sît ché.",
        "session_fail_preview_html": "'''An n'é mìa stê pusébil registrêr la mudéfica perchè în andêdi persi al j infurmasiòun relatîvi a la sesiòun.'''\n\n''Pôst che in {{SITENAME}} a gh'é al permès ed druvêr l' HTML sèinsa lémit, an 's pōl mìa guardêr préma la pàgina mudifichêda; a 's trâta ed 'n'amzûra 'd sicurèsa cûntra j atâch JavaScript.''\n\n''' Se còst l'é un tentatîv legétim ed mudéfica, pruvêr incòra. Se al prublēma l'armâgn, a 's pōl pruvêr a [[Special:UserLogout|sarêr al colegamèint]] e fêr un nōv ingrès.'''",
        "token_suffix_mismatch": "'''La mudéfica an n'é mìa stêda salvêda perchè al ''client'' l'à fât vèder ed gestîr in môd e-sbaliê i carâter di pûn e dal virgûli int al ''token'' lighê a la mudéfica. Per schivşêr di pusébil erōr int al tèst ed la pàgina, è stê rifiutê tóta la mudéfica. Dla vôlti cla situasiòun ché la pōl sucēder quând a vînen druvê soquânt servési ''proxy'' sèinsa nòm via internèt che preşèinten di ''bug''.'''",
        "edit_form_incomplete": "'''Soquânti pêrt dal môdul ed mudéfica în mìa rivêdi al ''server''; controlêr che al mudéfichi sién intâti e turnêr a pruvêr'''",
        "undo-nochange": "A sèmbra che la mudéfica la sìa bèle stêda scanşlêda.",
        "undo-summary": "Scanşlê la mudéfica $1 ed [[Special:Contributions/$2|$2]]\n([[User talk:$2|discusiòun]])",
        "undo-summary-username-hidden": "Scanşlê la modéfica $1 ed 'n utèin lughê",
-       "cantcreateaccounttitle": "Impusébil registrêr un utèint",
        "cantcreateaccount-text": "La registrasiòun ed cl'indirés IP ché ('''$1''') l'é stêda bluchêda da [[User:$3|$3]]. \n\nAl mutîv dal blôch dê da $3 l'é còst: ''$2''.",
        "cantcreateaccount-range-text": "La registrasiòun da indirés IP int l'intervâl <strong>$1</strong>, in dó gh'é dèinter al tó  (<strong>$4</strong>), l'é stêda bluchêda da [[User:$3|$3]]. \n\nAl mutîv dê da $3 l'é <em>$2</em>",
        "viewpagelogs": "Guêrda la stòria 'd cla pàgina ché",
index 154b630..a684a3f 100644 (file)
@@ -48,7 +48,8 @@
                        "Gts-tg",
                        "Nemo bis",
                        "Αντιγόνη",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Ανώνυμος Βικιπαιδιστής"
                ]
        },
        "tog-underline": "Υπογράμμιση συνδέσμων:",
@@ -66,6 +67,7 @@
        "tog-watchdefault": "Προσθήκη σελίδων που επεξεργάζομαι στη λίστα παρακολούθησης.",
        "tog-watchmoves": "Προσθήκη σελίδων που μετακινώ στη λίστα παρακολούθησής μου",
        "tog-watchdeletion": "Προσθήκη σελίδων και αρχείων που διαγράφω στη λίστα παρακολούθησής μου",
+       "tog-watchuploads": "Να προσθέσετε νέα αρχεία που ανεβάσετε στη λίστα παρακολούθησής μου",
        "tog-watchrollback": "Προσθήκη σελίδων όπου έχω κάνει μια επαναφορά στη λίστα παρακολούθησής μου",
        "tog-minordefault": "Σήμανση εκ προεπιλογής όλων των αλλαγών ως μικρής κλίμακας",
        "tog-previewontop": "Εμφάνιση προεπισκόπησης πριν από το πλαίσιο επεξεργασίας",
        "tagline": "Από {{SITENAME}}",
        "help": "Βοήθεια",
        "search": "Αναζήτηση",
+       "search-ignored-headings": "#<!-- αφήστε αυτή τη γραμμή όπως είναι --> <pre>\n# Επικεφαλίδες που θα αγνοηθούν από την αναζήτηση.\n# Αλλαγές σε αυτό ισχύουν μόλις η σελίδα με τον τίτλο ευρετηριαστεί.\n# Μπορείτε να επιβάλετε επανευρετηρίαση της σελίδας κάνοντας μια κενή επεξεργασία.\n# Η σύνταξη είναι ως εξής:\n# * Όλα από ένα χαρακτήρα \"#\" μέχρι το τέλος της γραμμής είναι ένα σχόλιο.\n# * Κάθε μη κενή γραμμή είναι ο ακριβής τίτλος που θα αγνοήσει, κεφαλαία/πεζά και τα πάντα.\nΠαραπομπές\nΕξωτερικοί σύνδεσμοι\nΔείτε επίσης\n #</pre> <!-- leave this line exactly as it is -->",
        "searchbutton": "Αναζήτηση",
        "go": "Μετάβαση",
        "searcharticle": "Μετάβαση",
        "yourpasswordagain": "Επαναπληκτρολόγηση κωδικού:",
        "createacct-yourpasswordagain": "Επιβεβαίωση κωδικού",
        "createacct-yourpasswordagain-ph": "Εισαγωγή κωδικού ξανά",
-       "remembermypassword": "Απομνημόνευση της σύνδεσής μου σε αυτόν τον περιηγητή (για μέγιστο $1 {{PLURAL:$1|ημέρα|ημέρες}})",
        "userlogin-remembermypassword": "Να διατηρούμαι μόνιμα σε σύνδεση",
        "userlogin-signwithsecure": "Χρησιμοποιείστε ασφαλή σύνδεση",
+       "cannotlogin-title": "Δεν μπορώ να συνδεθώ",
+       "cannotlogin-text": "Η σύνδεση δεν είναι δυνατή.",
        "cannotloginnow-title": "Δεν μπορείτε να συνδεθείτε τώρα",
        "cannotloginnow-text": "Η σύνδεση δεν είναι δυνατή όταν χρησιμοποιείτε την $1.",
        "yourdomainname": "Το domain σας:",
        "botpasswords-label-delete": "Διαγραφή",
        "botpasswords-label-resetpassword": "Επαναφορά κωδικού",
        "botpasswords-label-grants": "Ισχύουσες άδειες:",
+       "botpasswords-help-grants": "Κάθε παραχώρηση δίνει πρόσβαση στα ορισμένα δικαιώματα χρήστη που που ήδη έχει ένας λογαριασμός χρήστη. Δείτε τη [[Special:ListGrants|πίνακας παραχωρήσεων]] για περισσότερες πληροφορίες.",
        "botpasswords-label-restrictions": "Περιορισμοί χρήσης:",
        "botpasswords-label-grants-column": "Χορηγήθηκε",
        "botpasswords-bad-appid": "Η ονομασία του ρομπότ «$1» δεν είναι έγκυρη.",
+       "botpasswords-insert-failed": "Αποτυχία να προστεθεί το όνομα bot \"$1\". Έχει ήδη προστεθεί;",
        "botpasswords-update-failed": "Αποτυχία ενημέρωσης της ονομασίας του ρομπότ «$1». Μήπως διαγράφτηκε ο κωδικός;",
        "botpasswords-created-title": "Ο κωδικός πρόσβασης του ρομπότ δημιουργήθηκε",
-       "botpasswords-created-body": "Ο κωδικός πρόσβασης του ρομπότ «$1» δημιουργήθηκε επιτυχώς.",
+       "botpasswords-created-body": "Ο κωδικός πρόσβασης για το όνομα ρομπότ \"$1\" του χρήστη \"$2\" δημιουργήθηκε.",
        "botpasswords-updated-title": "Ο κωδικός πρόσβασης του ρομπότ ενημερώθηκε",
-       "botpasswords-updated-body": "Ο κωδικός πρόσβασης του ρομπότ «$1» ενημερώθηκε με επιτυχία.",
+       "botpasswords-updated-body": "Ο κωδικός πρόσβασης του ρομπότ «$1» του χρήστη «$2» ενημερώθηκε.",
        "botpasswords-deleted-title": "Ο κωδικός πρόσβασης του ρομπότ διαγράφτηκε",
+       "botpasswords-deleted-body": "Ο κωδικός πρόσβασης για το όνομα ρομπότ \"$1\" του χρήστη \"$2\" διαγράφηκε.",
+       "botpasswords-newpassword": "Ο νέος κωδικός πρόσβασης για να συνδεθείτε με το <strong>$1</strong> είναι <strong>$2</strong>. <em>Παρακαλούμε σημειώστε το για μελλοντική αναφορά.</em>",
        "botpasswords-no-provider": "BotPasswordsSessionProvider δεν είναι διαθέσιμο.",
+       "botpasswords-restriction-failed": "Περιορισμοί κωδικών πρόσβασης bot εμποδίζουν τη συγκεκριμένη σύνδεση.",
        "resetpass_forbidden": "Οι κωδικοί πρόσβασης δεν μπορούν να αλλαχθούν",
+       "resetpass_forbidden-reason": "Οι κωδικοί πρόσβασης δεν μπορούν να αλλαχθούν: $1",
        "resetpass-no-info": "Πρέπει να είστε συνδεδεμένος για να δείτε αυτήν την σελίδα απευθείας",
        "resetpass-submit-loggedin": "Αλλαγή κωδικού",
        "resetpass-submit-cancel": "Ακύρωση",
-       "resetpass-wrong-oldpass": "Λάθος προσωρινός ή κανονικός κωδικός.\nΜπορεί να έχετε ήδη αλλάξει επιτυχώς τον κωδικό σας ή να έχετε ζητήσει έναν νέο προσωρινό κωδικό.",
+       "resetpass-wrong-oldpass": "Λάθος προσωρινός ή κανονικός κωδικός.\nΜπορεί να έχετε ήδη αλλάξει τον κωδικό σας ή να έχετε ζητήσει έναν νέο προσωρινό κωδικό.",
        "resetpass-recycled": "Παρακαλούμε επαναφέρετε τον κωδικό πρόσβασής σας σε κάτι διαφορετικό από τον τρέχοντα κωδικό πρόσβασης.",
        "resetpass-temp-emailed": "Έχετε συνδεθεί με έναν προσωρινό κωδικό μέσω ηλεκτρονικού ταχυδρομείου.\nΓια να ολοκληρώσετε τη σύνδεση, πρέπει να ορίσετε έναν νέο κωδικό εδώ:",
        "resetpass-temp-password": "Προσωρινός κωδικός:",
        "passwordreset-emailelement": "Όνομα χρήστη: \n$1\n\nΠροσωρινός κωδικός πρόσβασης:\n$2",
        "passwordreset-emailsentemail": "Αν αυτή η διεύθυνση ηλεκτρονικού ταχυδρομείου συνδέεται με το  λογαριασμό σας, τότε  θα σας αποσταλεί μήνυμα ηλεκτρονικού ταχυδρομείου για την επαναφορά του κωδικού πρόσβασης.",
        "passwordreset-emailsentusername": "Αν υπάρχει μια διεύθυνση ηλεκτρονικού ταχυδρομείου που συνδέεται με αυτό το όνομα χρήστη, τότε θα σας αποσταλεί ένα μήνυμα ηλεκτρονικού ταχυδρομείου για την επαναφορά του κωδικού πρόσβασης.",
-       "passwordreset-emailsent-capture": "Έχει αποσταλεί email επαναφοράς κωδικού, το οποίο φαίνεται πιο κάτω.",
-       "passwordreset-emailerror-capture": "Ένα email επαναφοράς κωδικού έχει δημιουργηθεί, το οποίο φαίνεται πιο κάτω, αλλά απέτυχε η αποστολή του στο  {{GENDER:$2|χρήστη}}: $1",
+       "passwordreset-invalideamil": "Μη έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου",
        "changeemail": "Αλλαγή ή αφαίρεση της διεύθυνσης ηλεκτρονικού ταχυδρομείου",
        "changeemail-header": "Συμπληρώστε αυτήν τη φόρμα για να αλλάξετε τη διεύθυνσή σας ηλεκτρονικού ταχυδρομείου. Αν θέλετε να καταργήσετε τη σύνδεση οποιασδήποτε διεύθυνσης ηλεκτρονικού ταχυδρομείου με το λογαριασμό σας, αφήστε τη νέα διεύθυνση ηλεκτρονικού ταχυδρομείου κενή κατά την υποβολή της φόρμας.",
-       "changeemail-passwordrequired": "Θα χρειαστεί να εισαγάγετε τον κωδικό σας για να επιβεβαιώσετε την αλλαγή αυτή.",
        "changeemail-no-info": "Πρέπει να έχετε συνδεθεί για άμεση πρόσβαση σε αυτήν τη σελίδα.",
        "changeemail-oldemail": "Τρέχουσα διεύθυνση ηλεκτρονικού ταχυδρομείου:",
        "changeemail-newemail": "Νέα διεύθυνση ηλεκτρονικού ταχυδρομείου:",
        "minoredit": "Αυτή είναι μια μικροαλλαγή",
        "watchthis": "Παρακολούθηση αυτής της σελίδας",
        "savearticle": "Αποθήκευση σελίδας",
+       "savechanges": "Αποθήκευση αλλαγών",
+       "publishpage": "Δημοσίευση σελίδας",
+       "publishchanges": "Δημοσίευση αλλαγών",
        "preview": "Προεπισκόπηση",
        "showpreview": "Εμφάνιση προεπισκόπησης",
        "showdiff": "Εμφάνιση αλλαγών",
        "previewnote": "'''Να θυμάστε ότι αυτή είναι μόνο μια προεπισκόπηση.'''\nΟι αλλαγές σας δεν έχουν ακόμη αποθηκευτεί!",
        "continue-editing": "Μεταβείτε στην περιοχή επεξεργασίας",
        "previewconflict": "Αυτή η προεπισκόπηση απεικονίζει το κείμενο στην επάνω περιοχή επεξεργασίας κειμένου, όπως θα εμφανιστεί εάν επιλέξετε να το αποθηκεύσετε.",
-       "session_fail_preview": "'''Συγγνώμη! Δεν μπορούσαμε να διεκπεραιώσουμε την επεξεργασία σας λόγω απώλειας των δεδομένων της συνεδρίας.\nΠαρακαλώ προσπαθήστε ξανά. Αν δεν δουλεύει ξανά, δοκιμάστε να αποσυνδεθείτε και να συνδεθείτε πάλι.'''",
+       "session_fail_preview": "'''Συγγνώμη! Δεν μπορούσαμε να διεκπεραιώσουμε την επεξεργασία σας λόγω απώλειας των δεδομένων της συνεδρίας.\nΠαρακαλώ προσπαθήστε ξανά. Αν δεν δουλεύει ξανά, δοκιμάστε να [[Special:UserLogout|αποσυνδεθείτε]] και να συνδεθείτε πάλι.'''",
        "session_fail_preview_html": "'''Λυπούμαστε! Δεν μπορέσαμε να διεκπεραιώσουμε την επεξεργασία σας λόγω απώλειας των δεδομένων της συνεδρίας.'''\n\n''Επειδή το {{SITENAME}} επιτρέπει την εισαγωγή ακατέργαστου HTML, η προεπισκόπηση είναι κρυμμένη ως προφύλαξη ενάντια σε επιθέσεις με Javascript.''\n\n'''Αν αυτή είναι μια έγκυρη προσπάθεια επεξεργασίας, παρακαλώ προσπαθήστε ξανά. Αν πάλι δε δουλεύει, δοκιμάστε να αποσυνδεθείτε και να συνδεθείτε πάλι.'''",
        "token_suffix_mismatch": "'''Η επεξεργασία σας απορρίφθηκε γιατί το πρόγραμμα-πελάτη σας κατακρεούργησε τους χαρακτήρες στίξης στο κουπόνι επεξεργασίας. Η επεξεργασία απορρίφθηκε για να αποφευχθεί η παραφθορά του κειμένου της σελίδας.\nΑυτό μερικές φορές συμβαίνει όταν χρησιμοποιείται ένας ανώνυμος διακομιστής μεσολάβησης διαθέσιμος μέσω του παγκόσμιου ιστού με σφάλματα.'''",
        "edit_form_incomplete": "'''Ορισμένα τμήματα της φόρμας επεξεργασίας δεν έφθασαν στο διακομιστή. Ελέγξτε ότι οι αλλαγές σας είναι άθικτες και προσπαθήστε ξανά.'''",
        "undo-nochange": "Η επεξεργασία φαίνεται να έχει ήδη αναιρεθεί.",
        "undo-summary": "Αναίρεση αναθεώρησης $1 υπό τον/την [[Special:Contribs/$2|$2]] ([[User talk:$2|Συζήτηση]])",
        "undo-summary-username-hidden": "Αναίρεση αναθεώρησης $1 από ένα κρυμμένο χρήστη",
-       "cantcreateaccounttitle": "Ο λογαριασμός δεν μπορεί να δημιουργηθεί",
        "cantcreateaccount-text": "Η δημιουργία λογαριασμού από αυτή τη διεύθυνση IP ('''$1''') έχει αποτραπεί από τον [[User:$3|$3]].\n\nΟ λόγος που δόθηκε από τον $3 είναι ''$2''",
        "cantcreateaccount-range-text": "Η δημιουργία λογαριασμού από διευθύνσεις IP στην περιοχή  <strong>$1</strong>, που περιλαμβάνει τη δική σας διεύθυνση IP (<strong>$4</strong>), έχει αποκλειστεί από τον [[User:$3|$3]].\n\nΗ αιτιολογία που δόθηκε από τον $3 είναι \"$2\"",
        "viewpagelogs": "Προβολή αρχείων καταγραφών για αυτήν τη σελίδα",
        "revdelete-submit": "Εφαρμογή {{PLURAL:$1|στην επιλεγμένη αναθεώρηση|στις επιλεγμένες αναθεωρήσεις}}",
        "revdelete-success": "'''Η ορατότητα έκδοσης ενημερώθηκε επιτυχώς.'''",
        "revdelete-failure": "'''Η ορατότητα της επεξεργασίας δεν ήταν δυνατόν να ενημερωθεί:''' $1",
-       "logdelete-success": "'''Η ορατότητα γεγονότος τέθηκε επιτυχώς.'''",
+       "logdelete-success": "Η ορατότητα γεγονότος τέθηκε επιτυχώς.",
        "logdelete-failure": "'''Η ορατότητα του καταλόγου δεν μπορούσε να ρυθμιστεί:'''\n$1",
        "revdel-restore": "αλλαγή ορατότητας",
        "pagehist": "Ιστορικό σελίδας",
        "mergehistory-done": "$3 {{PLURAL:$3|έκδοση|εκδόσεις}} του $1 συγχωνεύθηκαν επιτυχώς στο [[:$2]].",
        "mergehistory-fail": "Αδύνατη η εκτέλεση της συγχώνευσης ιστορικού, παρακαλούμε κάντε επανέλεγχο των παραμέτρων σελίδας και χρόνου.",
        "mergehistory-fail-bad-timestamp": "Η χρονική σήμανση δεν είναι έγκυρη.",
+       "mergehistory-fail-invalid-source": "Η πηγή σελίδας δεν είναι έγκυρη.",
+       "mergehistory-fail-invalid-dest": "Η σελίδα προορισμού δεν είναι έγκυρη.",
+       "mergehistory-fail-permission": "Μη επαρκή δικαιώματα για τη συγχώνευση του ιστορικού.",
+       "mergehistory-fail-self-merge": "Η πηγή και ο προορισμός των σελίδων είναι ο ίδιος.",
        "mergehistory-fail-toobig": "Δεν είναι δυνατό να πραγματοποιηθεί η συγχώνευση ιστορικών, καθώς πάνω από $1 {{PLURAL:$1|αναθεώρηση|αναθεωρήσεις}} θα μετακινούνταν.",
        "mergehistory-no-source": "Η σελίδα πηγής $1 δεν υπάρχει.",
        "mergehistory-no-destination": "Η σελίδα προορισμού $1 δεν υπάρχει.",
        "grant-group-high-volume": "Εκτέλεση υψηλής έντασης δραστηριότητας",
        "grant-group-customization": "Ρυθμίσεις και προτιμήσεις",
        "grant-group-administration": "Εκτέλεση διαχειριστικών ενεργειών",
+       "grant-group-private-information": "Πρόσβαση σε ιδιωτικά δεδομένα σχετικά με εσάς",
+       "grant-group-other": "Διάφορες δραστηριότητες",
        "grant-blockusers": "Φραγή και αναίρεση φραγής χρηστών",
        "grant-createaccount": "Δημιουργία λογαριασμών",
        "grant-createeditmovepage": "Δημιουργία, επεξεργασία και μετακίνηση σελίδων",
        "grant-delete": "Διαγραφή σελίδων, αναθεωρήσεων, και αρχείων καταγραφής",
        "grant-editinterface": "Επεξεργασία του ονοματοχώρου Mediawiki και της CSS/JavaScript χρήστη",
+       "grant-editmycssjs": "Επεξεργαστείτε το δικό σας CSS/JavaScript",
        "grant-editmyoptions": "Επεξεργασία των προτιμήσεών χρήστη σας",
        "grant-editmywatchlist": "Επεξεργασία της λίστας παρακολούθησής σας",
        "grant-editpage": "Επεξεργασία υπαρχουσών σελίδων",
        "grant-highvolume": "Υψηλής έντασης επεξεργασία",
        "grant-oversight": "Απόκρυψη χρηστών και καταστολή αναθεωρήσεων",
        "grant-patrol": "Περιπολία αλλαγών σε σελίδες",
+       "grant-privateinfo": "Πρόσβαση σε προσωπικές πληροφορίες",
        "grant-protect": "Προστασία και κατάργηση προστασίας σελίδων",
        "grant-rollback": "Η επαναφορά αλλαγών σε σελίδες",
        "grant-sendemail": "Αποστολή μηνύματος ηλεκτρονικού ταχυδρομείου σε άλλους χρήστες",
        "rightslogtext": "Καταγραφές των αλλαγών στα δικαιώματα χρηστών.",
        "action-read": "να διαβάσετε αυτή τη σελίδα",
        "action-edit": "τροποποιήσετε αυτή τη σελίδα",
-       "action-createpage": "να δημιουργήσετε σελίδες",
-       "action-createtalk": "να δημιουργήσετε σελίδες συζήτησης",
+       "action-createpage": "να δημιουργήσετε αυτή τη σελίδα",
+       "action-createtalk": "να δημιουργήσετε αυτή τη σελίδα συζήτησης",
        "action-createaccount": "να δημιουργήσετε αυτό το λογαριασμό χρήστη",
        "action-autocreateaccount": "Δημιουργείστε αυτόματα αυτόν τον εξωτερικό λογαριασμό χρήστη",
        "action-history": "προβολή ιστορικού αυτή της σελίδας",
        "action-viewmyprivateinfo": "προβάλετε τις προσωπικές σας πληροφορίες",
        "action-editmyprivateinfo": "επεξεργαστείτε τις προσωπικές σας πληροφορίες",
        "action-editcontentmodel": "επεξεργαστείτε το μοντέλο περιεχομένου σελίδας",
-       "action-managechangetags": "δημιοÏ\85Ï\81γία ÎºÎ±Î¹ Î´Î¹Î±Î³Ï\81αÏ\86ή ÎµÏ\84ικεÏ\84Ï\8eν από τη βάση δεδομένων",
+       "action-managechangetags": "δημιοÏ\85Ï\81γήÏ\83εÏ\84ε ÎºÎ±Î¹ Î´Î¹Î±Î³Ï\81άÏ\88εÏ\84ε ÎµÏ\84ικέÏ\84εÏ\82 από τη βάση δεδομένων",
        "action-applychangetags": "εφαρμογή ετικετών μαζί με τις αλλαγές σας",
        "action-changetags": "πρόσθεση και αφαίρεση αυθαίρετων ετικετών σε μεμονωμένες εκδόσεις και καταχωρήσεις καταγραφών",
+       "action-deletechangetags": "διαγράψετε ετικέτες από τη βάση δεδομένων",
+       "action-purge": "εκκαθάριση αυτής της σελίδας",
        "nchanges": "$1 {{PLURAL:$1|αλλαγή|αλλαγές}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|από την τελευταία επίσκεψη}}",
        "enhancedrc-history": "ιστορικό",
        "uploadstash-summary": "Η σελίδα παρέχει πρόσβαση σε αρχεία που είναι  επιφορτωμένα  (ή στη διαδικασία της επιφόρτωσης) αλλά δεν έχει ακόμη δημοσιευθεί για το wiki. Αυτά τα αρχεία δεν είναι ορατά σε  οποιονδήποτε, αλλά στο χρήστη που τα επιφόρτωσε.",
        "uploadstash-clear": "Καθαρά διατηρημένα αρχεία",
        "uploadstash-nofiles": "Δεν έχετε κρυμμένα αρχεία.",
-       "uploadstash-badtoken": "Î\95κÏ\84έλεÏ\83η Ï\84ηÏ\82 ÎµÎ½ Î»Ï\8cγÏ\89 ÎµÎ½Î­Ï\81γειαÏ\82  Î®Ï\84αν Î±Î½ÎµÏ\80ιÏ\84Ï\85Ï\87ήÏ\82, Î¯Ï\83Ï\89Ï\82 ÎµÏ\80ειδή Ï\84α Î´Î¹Î±Ï\80ιÏ\83Ï\84εÏ\85Ï\84ήÏ\81ιά ÎµÏ\80εξεÏ\81γαÏ\83ίαÏ\82  Ï\83αÏ\82 Î­Ï\87οÏ\85ν Î»Î®Î¾ÎµÎ¹. Î\94οκίμαστε ξανά.",
-       "uploadstash-errclear": "Î\97 ÎµÎºÎºÎ±Î¸Î¬Ï\81ιÏ\83η Ï\84Ï\89ν Î±Ï\81Ï\87είÏ\89ν Î®Ï\84αν Î±Î½ÎµÏ\80ιÏ\84Ï\85Ï\87ήÏ\82.",
+       "uploadstash-badtoken": "Î\95κÏ\84έλεÏ\83η Ï\84ηÏ\82 ÎµÎ½ Î»Ï\8cγÏ\89 ÎµÎ½Î­Ï\81γειαÏ\82  Î±Ï\80έÏ\84Ï\85Ï\87ε, Î¯Ï\83Ï\89Ï\82 ÎµÏ\80ειδή Ï\84α Î´Î¹Î±Ï\80ιÏ\83Ï\84εÏ\85Ï\84ήÏ\81ιά ÎµÏ\80εξεÏ\81γαÏ\83ίαÏ\82  Ï\83αÏ\82 Î­Ï\87οÏ\85ν Î»Î®Î¾ÎµÎ¹. Î Î±Ï\81ακαλοÏ\8dμε Î´Î¿ÎºÎ¹Î¼Î¬στε ξανά.",
+       "uploadstash-errclear": "Î\97 ÎµÎºÎºÎ±Î¸Î¬Ï\81ιÏ\83η Ï\84Ï\89ν Î±Ï\81Ï\87είÏ\89ν Î±Ï\80έÏ\84Ï\85Ï\87ε.",
        "uploadstash-refresh": "Ανανεώσετε τη λίστα των αρχείων",
+       "uploadstash-thumbnail": "προβολή μικρογραφίας",
        "invalid-chunk-offset": "Άκυρο κομμάτι όφσετ",
        "img-auth-accessdenied": "Δεν επετράπη η πρόσβαση",
        "img-auth-nopathinfo": "Λείπει το PATH_INFO.\nΟ διακομιστής σας δεν είναι ρυθμισμένος για να περάσει αυτές τις πληροφορίες.\nΜπορεί να είναι βασισμένος σε CGI και να μην υποστηρίζει img_atuh.\nΔείτε https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization",
        "apisandbox-helpurls": "Σύνδεσμοι βοήθειας",
        "apisandbox-examples": "Παραδείγματα",
        "apisandbox-dynamic-parameters": "Πρόσθετες παράμετροι",
+       "apisandbox-dynamic-parameters-add-label": "Προσθήκη παραμέτρου:",
        "apisandbox-dynamic-parameters-add-placeholder": "Ονομασία παραμέτρου",
        "apisandbox-dynamic-error-exists": "Η παράμετρος με την ονομασία \"$1\" υπάρχει ήδη",
        "apisandbox-submit-invalid-fields-title": "Κάποια από τα πεδία δεν είναι έγκυρα",
        "log-edit-tags": "Επεξεργασία ετικετών των επιλεγμένων καταχωρήσεων του αρχείου καταγραφής",
        "checkbox-all": "Όλα",
        "checkbox-none": "Κανένα",
+       "checkbox-invert": "Αντιστροφή",
        "allpages": "Όλες οι σελίδες",
        "nextpage": "Επόμενη σελίδα ($1)",
        "prevpage": "Προηγούμενη σελίδα ($1)",
        "listgrouprights-namespaceprotection-header": "Περιορισμοί ονοματοχώρων",
        "listgrouprights-namespaceprotection-namespace": "Ονοματοχώρος",
        "listgrouprights-namespaceprotection-restrictedto": "Δικαίωμα(τα) που επιτρέπει(ουν) σε χρήστη να επεξεργαστεί",
+       "listgrants": "Επιχορηγήσεις",
+       "listgrants-grant": "Επιχορήγηση",
        "listgrants-rights": "Δικαιώματα",
        "trackingcategories": "Κατηγορίες παρακολούθησης",
        "trackingcategories-summary": "Αυτή η σελίδα εμφανίζει τις κατηγορίες παρακολούθησης το περιεχόμενο των οποίων συμπληρώνεται αυτόματα από το λογισμικό MediaWiki. Τα ονόματά τους μπορεί να αλλαχθούν με την αλλαγή των σχετικών μηνυμάτων συστήματος στον ονοματοχώρο {{ns:8}}.",
        "delete-toobig": "Αυτή η σελίδα έχει μεγάλο ιστορικό τροποποιήσεων, πάνω από $1 {{PLURAL:$1|τροποποίηση|τροποποιήσεις}}.\nΗ διαγραφή τέτοιων σελίδων έχει περιοριστεί για την αποφυγή τυχαίας αναστάτωσης του {{SITENAME}}.",
        "delete-warning-toobig": "Αυτή η σελίδα έχει μεγάλο ιστορικό τροποποιήσεων, πάνω από $1 {{PLURAL:$1|τροποποίηση|τροποποιήσεις}}.\nΗ διαγραφή της μπορεί να αναστατώσει τη λειτουργία της βάσης δεδομένων του {{SITENAME}}. Συνιστούμε μεγάλη προσοχή.",
        "deleteprotected": "Δεν μπορείτε να διαγράψετε αυτή τη σελίδα επειδή είναι προστατευόμενη.",
-       "deleting-backlinks-warning": "\"'Προσοχή:\"' [[Special:WhatLinksHere/{{FULLPAGENAME}}|Άλλες σελίδες]] συνδέουν ή ενσωματώνουν τη σελίδα που πρόκειται να διαγράψετε.",
+       "deleting-backlinks-warning": "<strong>Προσοχή:</strong>  [[Special:WhatLinksHere/{{FULLPAGENAME}}|Άλλες σελίδες]] συνδέουν ή ενσωματώνουν τη σελίδα που πρόκειται να διαγράψετε.",
        "rollback": "Επαναφορά επεξεργασιών",
        "rollbacklink": "αναστροφή",
        "rollbacklinkcount": "Επαναφορά $1 {{PLURAL:$1|επεξεργασίας|επεξεργασιών}}",
        "revertpage": "Ανάκληση των αλλαγών [[Special:Contributions/$2|$2]] ([[User talk:$2|συζήτηση]]) επιστροφή στην προηγούμενη αναθεώρηση [[User:$1|$1]]",
        "revertpage-nouser": "Αναστράφηκαν οι επεξεργασίες από τον (όνομα χρήστη αφαιρέθηκε) στη τελευταία έκδοση από τον/την {{GENDER:$1|[[User:$1|$1]]}}φ",
        "rollback-success": "Ανεστραμμένες εκδόσεις από $1, αλλάχθηκαν στην προηγούμενη έκδοση από $2.",
+       "rollback-success-notify": "Αναίρεση επεξεργασιών από $1; επιστροφή στην τελευταία αναθεώρηση από $2.[$3 Εμφάνιση αλλαγών]",
        "sessionfailure-title": "Η συνεδρία απέτυχε",
        "sessionfailure": "Υπάρχει πρόβλημα με τη σύνδεσή σας -η ενέργεια αυτή ακυρώθηκε προληπτικά για την αντιμετώπιση τυχόν πειρατείας συνόδου (session hijacking). Παρακαλoύμε πατήστε \"Επιστροφή\", ξαναφορτώστε τη σελίδα από την οποία φθάσατε εδώ και προσπαθήστε ξανά.",
        "changecontentmodel": "Αλλαγή μοντέλου περιεχομένου της σελίδας",
        "changecontentmodel-title-label": "Τίτλος σελίδας",
        "changecontentmodel-model-label": "Νέο μοντέλο περιεχομένου",
        "changecontentmodel-reason-label": "Αιτία:",
+       "changecontentmodel-submit": "Αλλαγή",
        "changecontentmodel-success-title": "Το περιεχόμενο πρότυπο άλλαξε",
        "changecontentmodel-success-text": "Ο τύπος περιεχομένου του [[:$1]] έχει αλλάξει.",
        "changecontentmodel-cannot-convert": "Το περιεχόμενο του [[:$1]] δεν μπορεί να μετατραπεί σε τύπο $2.",
        "undeletedrevisions": "{{PLURAL:$1|τροποποίηση|τροποποιήσεις}} αποκαταστάθηκαν",
        "undeletedrevisions-files": "$1 {{PLURAL:$1|αναθεώρηση|αναθεωρήσεις}} και $2 {{PLURAL:$2|αρχείο|αρχεία}} επαναφέρθηκαν",
        "undeletedfiles": "$1 {{PLURAL:$1|αρχείο|αρχεία}} επαναφέρθηκαν",
-       "cannotundelete": "Î\97 Î±Î½Î±Î¯Ï\81εÏ\83η Î´Î¹Î±Î³Ï\81αÏ\86ήÏ\82 Î±Ï\80έÏ\84Ï\85Ï\87ε: $1",
+       "cannotundelete": "Î\9cεÏ\81ικέÏ\82 Î® Ï\8cλεÏ\82 Î¿Î¹  Î±Î½Î±Î¹Ï\81έÏ\83ειÏ\82 Î´Î¹Î±Î³Ï\81αÏ\86ήÏ\82 Î±Ï\80έÏ\84Ï\85Ï\87αν: $1",
        "undeletedpage": "'''Η $1 έχει επαναφερθεί'''\n\nΣυμβουλευτείτε το [[Special:Log/delete|αρχείο καταγραφής διαγραφών]] για ένα μητρώο των πρόσφατων διαγραφών και επαναφορών.",
        "undelete-header": "Δείτε [[Special:Log/delete|το αρχείο καταγραφής διαγραφών]] για πρόσφατα διαγεγραμμένες σελίδες.",
        "undelete-search-title": "Αναζήτηση διαγεγραμμένων σελίδων",
        "sp-contributions-newbies-sub": "Για νέους λογαριασμούς",
        "sp-contributions-newbies-title": "Συνεισφορές χρηστών για νέους λογαριασμούς",
        "sp-contributions-blocklog": "αρχείο καταγραφών φραγών",
-       "sp-contributions-suppresslog": "διαγεγραμμένες συνεισφορές χρήστη",
+       "sp-contributions-suppresslog": "διαγεγραμμένες συνεισφορές {{GENDER:$1|χρήστη|χρήστριας}}",
        "sp-contributions-deleted": "διαγεγραμμένες συνεισφορές χρήστη",
        "sp-contributions-uploads": "ανεβάσματα αρχείων",
        "sp-contributions-logs": "καταγραφές",
        "ipb-unblock": "Τερμάτισε τη φραγή για ένα όνομα χρήστη ή μια διεύθυνση IP",
        "ipb-blocklist": "Δες τις υπάρχουσες φραγές",
        "ipb-blocklist-contribs": "Συνεισφορές {{GENDER:$1|του $1|της $1}}",
+       "ipb-blocklist-duration-left": "Απομένουν $1",
        "unblockip": "Άρση φραγής χρήστη",
        "unblockiptext": "Χρησιμοποιήστε την παρακάτω φόρμα για να αποκαταστήσετε την πρόσβαση σε επεξεργασία, σε μια διεύθυνση IP ή σε ένα χρήστη που είχε αποκλειστεί με φραγή.",
        "ipusubmit": "Άρση φραγής",
        "lockdbsuccesstext": "Η βάση δεδομένων έχει κλειδωθεί.<br />\nΘυμηθείτε να [[Special:UnlockDB|αφαιρέσετε το κλείδωμα]] αφότου η συντήρησή σας ολοκληρωθεί.",
        "unlockdbsuccesstext": "Η βάση δεδομένων έχει ξεκλειδωθεί.",
        "lockfilenotwritable": "Το αρχείο κλειδώματος της βάσης δεδομένων δεν είναι εγγράψιμο. Για να κλειδώσετε ή να ξεκλειδώσετε τη βάση δεδομένων, αυτό το αρχείο πρέπει να είναι εγγράψιμο από τον εξυπηρετητή web.",
+       "databaselocked": "Η βάση δεδομένων είναι είδη κλειδωμένη.",
        "databasenotlocked": "Η βάση δεδομένων δεν είναι κλειδωμένη.",
        "lockedbyandtime": "(Από {{GENDER:$1| $1 }} στις $2 στις $3 )",
        "move-page": "Μετακίνηση $1",
        "tooltip-ca-nstab-category": "Προβολή της σελίδας κατηγορίας",
        "tooltip-minoredit": "Χαρακτηρισμός αυτής της επεξεργασίας ως μικροεπεξεργασία",
        "tooltip-save": "Αποθήκευση των αλλαγών σας",
+       "tooltip-publish": "Δημοσίευση των αλλαγών σας",
        "tooltip-preview": "Προεπισκόπηση των αλλαγών σας. Παρακαλούμε χρησιμοποιήστε την πριν αποθηκεύσετε!",
        "tooltip-diff": "Εμφάνιση των αλλαγών που κάνατε στο κείμενο",
        "tooltip-compareselectedversions": "Προβολή των διαφορών ανάμεσα στις δύο επιλεγμένες αναθεωρήσεις αυτής της σελίδας",
        "lastmodifiedatby": "Η σελίδα αυτή τροποποιήθηκε τελευταία φορά στις  $2, $1 από το χρήστη $3.",
        "othercontribs": "Βασισμένο στη δουλειά του/των $1",
        "others": "άλλοι",
-       "siteusers": "{{SITENAME}} {{PLURAL:$2|χρηστή|χρηστών}} του ιστοχώρου $1",
+       "siteusers": "{{SITENAME}} {{PLURAL:$2|χρηστής|χρήστες}} του ιστοχώρου $1",
        "anonusers": "{{PLURAL:$2|ανώνυμος χρήστης|ανώνυμοι χρήστες}} $1 του {{SITENAME}}",
        "creditspage": "Αναγνώριση συνεισφοράς στη σελίδα",
        "nocredits": "Δεν υπάρχουν πληροφορίες σχετικά με την αναγνώριση συνεισφοράς σε αυτή τη σελίδα.",
        "newimages-legend": "Φίλτρο",
        "newimages-label": "Όνομα αρχείου (ή μέρος αυτού):",
        "newimages-showbots": "Εμφάνιση αρχείων ανεβασμένων από ρομπότ",
+       "newimages-hidepatrolled": "Απόκρυψη ελεγμένων αρχείων.",
        "noimages": "Δεν υπάρχουν εικόνες.",
        "ilsubmit": "Αναζήτηση",
        "bydate": "ημερομηνίας",
        "confirm-watch-top": "Προσθήκη αυτής της σελίδας στη λίστα παρακολούθησης σας;",
        "confirm-unwatch-button": "Εντάξει",
        "confirm-unwatch-top": "Κατάργηση αυτής της σελίδας από τη λίστα παρακολούθησης σας;",
+       "confirm-rollback-button": "Εντάξει",
+       "confirm-rollback-top": "Επαναφέρετε τις επεξεργασίες σε αυτή τη σελίδα;",
        "quotation-marks": "«$1»",
        "imgmultipageprev": "← προηγούμενη σελίδα",
        "imgmultipagenext": "επόμενη σελίδα →",
        "watchlistedit-raw-done": "Η λίστα παρακολούθησής σας ενημερώθηκε.",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 σελίδα|$1 σελίδες}} προστέθηκαν:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 σελίδα|$1 σελίδες}} αφαιρέθηκαν:",
-       "watchlistedit-clear-title": "Î\95κκαθαÏ\81ιÏ\83μένη Î»Î¯Ï\83Ï\84α παρακολούθησης",
+       "watchlistedit-clear-title": "Î\95κκαθάÏ\81ιÏ\83η Î»Î¯Ï\83Ï\84αÏ\82 παρακολούθησης",
        "watchlistedit-clear-legend": "Εκκαθάριση λίστας παρακολούθησης",
        "watchlistedit-clear-explain": "Όλοι οι τίτλοι θα αφαιρεθούν από τη λίστα παρακολούθησής σας",
        "watchlistedit-clear-titles": "Τίτλοι:",
        "version-libraries-license": "Άδεια χρήσης",
        "version-libraries-description": "Περιγραφή",
        "version-libraries-authors": "Δημιουργοί",
-       "redirect": "Ανακατεύθυνση κατά αρχείο, χρήστη, σελίδα ή αναγνωριστικό αναθεώρησης",
+       "redirect": "Ανακατεύθυνση κατά αρχείο, χρήστη, σελίδα, αναγνωριστικό αναθεώρησης ή αρχείο καταγραφής ID",
        "redirect-submit": "Μετάβαση",
        "redirect-lookup": "Αναζήτηση:",
        "redirect-value": "Τιμή:",
        "tags-delete-submit": "Μη αναστρέψιμη διαγραφή αυτής της ετικέτας",
        "tags-delete-not-found": "Η ετικέτα «$1» δεν υπάρχει.",
        "tags-delete-too-many-uses": "Η ετικέτα «$1» εφαρμόζεται σε πάνω από {{PLURAL:$2|μία αναθεώρηση|$2 αναθεωρήσεις}}, που σημαίνει ότι δεν μπορεί να διαγραφεί.",
-       "tags-delete-warnings-after-delete": "Η ετικέτα «$1» διαγράφηκε με επιτυχία, αλλά {{PLURAL:$2|προέκυψε η ακόλουθη προειδοποίηση|προέκυψαν οι ακόλουθες προειδοποιήσεις}}:",
+       "tags-delete-warnings-after-delete": "Η ετικέτα «$1» διαγράφηκε, αλλά {{PLURAL:$2|προέκυψε η ακόλουθη προειδοποίηση|προέκυψαν οι ακόλουθες προειδοποιήσεις}}:",
+       "tags-delete-no-permission": "Δεν έχετε άδεια να διαχειριστείτε ετικέτες αλλαγής.",
        "tags-activate-title": "Ενεργοποίηση ετικέτας",
        "tags-activate-question": "Πρόκειται να ενεργοποιήσετε την ετικέτα «$1».",
        "tags-activate-reason": "Αιτία:",
        "tags-edit-reason": "Αιτία:",
        "tags-edit-revision-submit": "Εφαρμογή αλλαγών σε {{PLURAL:$1|αυτή την αναθεώρηση|$1 αναθεωρήσεις}}",
        "tags-edit-logentry-submit": "Εφαρμογή αλλαγών σε {{PLURAL:$1|αυτήν την καταχώρηση|$1 καταχωρήσεις}} του αρχείου καταγραφής",
-       "tags-edit-success": "Οι αλλαγές εφαρμόστηκαν με επιτυχία.",
+       "tags-edit-success": "Οι αλλαγές εφαρμόστηκαν.",
        "tags-edit-failure": "Οι αλλαγές δεν ήταν δυνατόν να εφαρμοστούν:\n$1",
        "tags-edit-nooldid-title": "Μη έγκυρη αναθεώρηση προορισμού",
        "tags-edit-none-selected": "Παρακαλώ επιλέξτε τουλάχιστον μία ετικέτα για να προσθέσετε ή να αφαιρέσετε.",
        "special-characters-group-ipa": "ΔΦΑ",
        "special-characters-group-symbols": "Σύμβολα",
        "special-characters-group-greek": "Ελληνικό",
+       "special-characters-group-greekextended": "Προέκταση του ελληνικού",
        "special-characters-group-cyrillic": "Κυριλλικό",
        "special-characters-group-arabic": "Αραβικό",
        "special-characters-group-arabicextended": "Arabic extended",
        "mw-widgets-dateinput-placeholder-month": "ΕΕΕΕ-ΜΜ",
        "mw-widgets-titleinput-description-new-page": "η σελίδα που δεν υπάρχει ακόμα",
        "mw-widgets-titleinput-description-redirect": "ανακατεύθυνση στο $1",
-       "api-error-blacklisted": "Παρακαλώ επιλέξτε ένα διαφορετικό, περιγραφικό τίτλο.",
        "sessionprovider-generic": "$1 συνεδρίες",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "συνεδρίες με βάση τα cookies",
        "sessionprovider-nocookies": "Τα Cookies μπορούν να απενεργοποιηθούν. Βεβαιωθείτε ότι έχετε ενεργοποιημένα τα cookies και ξεκινήστε πάλι.",
-       "randomrootpage": "Τυχαία κύρια σελίδα"
+       "randomrootpage": "Τυχαία κύρια σελίδα",
+       "log-action-filter-block": "Τύπος φραγής:",
+       "log-action-filter-rights": "Πληκτρολογήστε για αλλαγή δικαιωμάτων:",
+       "log-action-filter-all": "Όλα",
+       "log-action-filter-block-block": "Φραγή",
+       "log-action-filter-block-reblock": "Τροποποίηση φραγής",
+       "log-action-filter-block-unblock": "Άρση φραγής",
+       "log-action-filter-protect-protect": "Προστασία",
+       "log-action-filter-protect-modify": "Τροποποίηση προστασίας",
+       "log-action-filter-protect-unprotect": "Άρση προστασίας",
+       "log-action-filter-protect-move_prot": "Μετακίνηση προστασίας",
+       "log-action-filter-rights-rights": "Χειροκίνητη αλλαγή",
+       "log-action-filter-rights-autopromote": "Αυτόματη αλλαγή",
+       "authmanager-create-disabled": "Η δημιουργία λογαριασμού έχει απενεργοποιηθεί.",
+       "authmanager-realname-label": "Πραγματικό όνομα",
+       "authmanager-realname-help": "Πραγματικό όνομα του χρήστη",
+       "authmanager-provider-temporarypassword": "Προσωρινός κωδικός",
+       "authprovider-resetpass-skip-label": "Παράβλεψη",
+       "authprovider-resetpass-skip-help": "Παράβλεψη της επαναφοράς του κωδικού πρόσβασης.",
+       "specialpage-securitylevel-not-allowed-title": "Δεν επιτρέπεται",
+       "cannotauth-not-allowed-title": "Δεν έχετε δικαίωμα πρόσβασης.",
+       "cannotauth-not-allowed": "Δεν επιτρέπεται να χρησιμοποιήσετε αυτή τη σελίδα",
+       "credentialsform-account": "Όνομα λογαριασμού:",
+       "linkaccounts": "Σύνδεση λογαριασμών",
+       "linkaccounts-success-text": "Ο λογαριασμός συνδέθηκε",
+       "linkaccounts-submit": "Σύνδεση λογαριασμών"
 }
index 321a044..1a206aa 100644 (file)
@@ -95,5 +95,6 @@
        "protect_expiry_invalid": "Expiry time is invalid.",
        "protect_expiry_old": "Expiry time is in the past.",
        "protect-existing-expiry": "Existing expiry time: $3, $2",
-       "protect-existing-expiry-infinity": "Existing expiry time: infinite"
+       "protect-existing-expiry-infinity": "Existing expiry time: infinite",
+       "search-ignored-headings": " #<!-- leave this line exactly as it is --> <pre>\n# Headings that will be ignored by search.\n# Changes to this take effect as soon as the page with the heading is indexed.\n# You can force page reindexing by doing a null edit.\n# Syntax is as follows:\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.\nReferences\nExternal links\nSee also\n #</pre> <!-- leave this line exactly as it is -->"
 }
index 9ef95f3..cc7466b 100644 (file)
        "noindex-category": "Noindexed pages",
        "broken-file-category": "Pages with broken file links",
        "categoryviewer-pagedlinks": "($1) ($2)",
+       "category-header-numerals": "$1–$2",
        "about": "About",
        "article": "Content page",
        "newwindow": "(opens in new window)",
        "tagline": "From {{SITENAME}}",
        "help": "Help",
        "search": "Search",
+       "search-ignored-headings": " #<!-- leave this line exactly as it is --> <pre>\n# Headings that will be ignored by search.\n# Changes to this take effect as soon as the page with the heading is indexed.\n# You can force page reindexing by doing a null edit.\n# The syntax is as follows:\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.\nReferences\nExternal links\nSee also\n #</pre> <!-- leave this line exactly as it is -->",
        "searchbutton": "Search",
        "go": "Go",
        "searcharticle": "Go",
        "databaseerror-query": "Query: $1",
        "databaseerror-function": "Function: $1",
        "databaseerror-error": "Error: $1",
-       "transaction-duration-limit-exceeded": "To avoid creating high replication lag, this transaction was aborted because the write duration ($1) exceeded the $2 {{PLURAL:$2|second|seconds}} limit.\nIf you are changing many items at once, try doing multiple smaller operations instead.",
+       "transaction-duration-limit-exceeded": "To avoid creating high replication lag, this transaction was aborted because the write duration ($1) exceeded the $2 second limit.\nIf you are changing many items at once, try doing multiple smaller operations instead.",
        "laggedslavemode": "<strong>Warning:</strong> Page may not contain recent updates.",
        "readonly": "Database locked",
        "enterlockreason": "Enter a reason for the lock, including an estimate of when the lock will be released",
        "yourpasswordagain": "Retype password:",
        "createacct-yourpasswordagain": "Confirm password",
        "createacct-yourpasswordagain-ph": "Enter password again",
-       "remembermypassword": "Remember my login on this browser (for a maximum of $1 {{PLURAL:$1|day|days}})",
        "userlogin-remembermypassword": "Keep me logged in",
        "userlogin-signwithsecure": "Use secure connection",
+       "cannotlogin-title": "Cannot log in",
+       "cannotlogin-text": "Logging in is not possible.",
        "cannotloginnow-title": "Cannot log in now",
        "cannotloginnow-text": "Logging in is not possible when using $1.",
+       "cannotcreateaccount-title": "Cannot create accounts",
+       "cannotcreateaccount-text": "Direct account creation is not enabled on this wiki.",
        "yourdomainname": "Your domain:",
        "password-change-forbidden": "You cannot change passwords on this wiki.",
        "externaldberror": "There was either an authentication database error or you are not allowed to update your external account.",
        "passwordreset-emailelement": "Username:\n$1\n\nTemporary password:\n$2",
        "passwordreset-emailsentemail": "If this email address is associated with your account, then a password reset email will be sent.",
        "passwordreset-emailsentusername": "If there is an email address associated with this username, then a password reset email will be sent.",
-       "passwordreset-emailsent-capture": "A password reset email has been sent, which is shown below.",
-       "passwordreset-emailerror-capture": "A password reset email was generated, which is shown below, but sending it to the {{GENDER:$2|user}} failed: $1",
        "passwordreset-emailsent-capture2": "The password reset {{PLURAL:$1|email has|emails have}} been sent. The {{PLURAL:$1|username and password|list of usernames and passwords}} is shown below.",
        "passwordreset-emailerror-capture2": "Emailing the {{GENDER:$2|user}} failed: $1 The {{PLURAL:$3|username and password|list of usernames and passwords}} is shown below.",
        "passwordreset-nocaller": "A caller must be provided",
        "changeemail": "Change or remove email address",
        "changeemail-summary": "",
        "changeemail-header": "Complete this form to change your email address. If you would like to remove the association of any email address from your account, leave the new email address blank when submitting the form.",
-       "changeemail-passwordrequired": "You will need to enter your password to confirm this change.",
        "changeemail-no-info": "You must be logged in to access this page directly.",
        "changeemail-oldemail": "Current email address:",
        "changeemail-newemail": "New email address:",
        "content-model-json": "JSON",
        "content-json-empty-object": "Empty object",
        "content-json-empty-array": "Empty array",
+       "deprecated-self-close-category": "Pages using invalid self-closed HTML tags",
+       "deprecated-self-close-category-desc": "The page contains invalid self-closed HTML tags, such as <code>&lt;b/></code> or <code>&lt;span/></code>.  The behavior of these will change soon to be consistent with the HTML5 specification, so their use in wikitext is deprecated.",
        "duplicate-args-warning": "<strong>Warning:</strong> [[:$1]] is calling [[:$2]] with more than one value for the \"$3\" parameter. Only the last value provided will be used.",
        "duplicate-args-category": "Pages using duplicate arguments in template calls",
        "duplicate-args-category-desc": "The page contains template calls that use duplicates of arguments, such as <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> or <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "The edit appears to have already been undone.",
        "undo-summary": "Undo revision $1 by [[Special:Contributions/$2|$2]] ([[User talk:$2|talk]])",
        "undo-summary-username-hidden": "Undo revision $1 by a hidden user",
-       "cantcreateaccounttitle": "Cannot create account",
        "cantcreateaccount-text": "Account creation from this IP address (<strong>$1</strong>) has been blocked by [[User:$3|$3]].\n\nThe reason given by $3 is <em>$2</em>",
        "cantcreateaccount-range-text": "Account creation from IP addresses in the range <strong>$1</strong>, which includes your IP address (<strong>$4</strong>), has been blocked by [[User:$3|$3]].\n\nThe reason given by $3 is <em>$2</em>",
        "createaccount-hook-aborted": "$1",
        "grant-group-high-volume": "Perform high volume activity",
        "grant-group-customization": "Customization and preferences",
        "grant-group-administration": "Perform administrative actions",
+       "grant-group-private-information": "Access private data about you",
        "grant-group-other": "Miscellaneous activity",
        "grant-blockusers": "Block and unblock users",
        "grant-createaccount": "Create accounts",
        "grant-highvolume": "High-volume editing",
        "grant-oversight": "Hide users and suppress revisions",
        "grant-patrol": "Patrol changes to pages",
+       "grant-privateinfo": "Access private information",
        "grant-protect": "Protect and unprotect pages",
        "grant-rollback": "Rollback changes to pages",
        "grant-sendemail": "Send email to other users",
        "action-applychangetags": "apply tags along with your changes",
        "action-changetags": "add and remove arbitrary tags on individual revisions and log entries",
        "action-deletechangetags": "delete tags from the database",
+       "action-purge": "purge this page",
        "nchanges": "$1 {{PLURAL:$1|change|changes}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|since last visit}}",
        "enhancedrc-history": "history",
        "file-thumbnail-no": "The filename begins with <strong>$1</strong>.\nIt seems to be an image of reduced size <em>(thumbnail)</em>.\nIf you have this image in full resolution upload this one, otherwise change the filename please.",
        "fileexists-forbidden": "A file with this name already exists, and cannot be overwritten.\nIf you still want to upload your file, please go back and use a new name.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "A file with this name exists already in the shared file repository.\nIf you still want to upload your file, please go back and use a new name.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "The upload is an exact duplicate of the current version of <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "The upload is an exact duplicate of {{PLURAL:$2|an older version|older versions}} of <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "This file is a duplicate of the following {{PLURAL:$1|file|files}}:",
        "file-deleted-duplicate": "A file identical to this file ([[:$1]]) has previously been deleted.\nYou should check that file's deletion history before proceeding to re-upload it.",
        "file-deleted-duplicate-notitle": "A file identical to this file has previously been deleted, and the title has been suppressed.\nYou should ask someone with the ability to view suppressed file data to review the situation before proceeding to re-upload it.",
        "uploadstash-errclear": "Clearing the files failed.",
        "uploadstash-refresh": "Refresh the list of files",
        "uploadstash-thumbnail": "view thumbnail",
+       "uploadstash-exception": "Could not store upload in the stash ($1): \"$2\".",
        "invalid-chunk-offset": "Invalid chunk offset",
        "img-auth-accessdenied": "Access denied",
        "img-auth-nopathinfo": "Missing PATH_INFO.\nYour server is not set up to pass this information.\nIt may be CGI-based and cannot support img_auth.\nSee https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "filerevert-submit": "Revert",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> has been reverted to the [$4 version as of $3, $2].",
        "filerevert-badversion": "There is no previous local version of this file with the provided timestamp.",
+       "filerevert-identical": "The current version of the file is already identical to the selected one.",
        "filedelete": "Delete $1",
        "filedelete-legend": "Delete file",
        "filedelete-intro": "You are about to delete the file <strong>[[Media:$1|$1]]</strong> along with all of its history.",
        "watchnologin": "Not logged in",
        "addwatch": "Add to watchlist",
        "addedwatchtext": "\"[[:$1]]\" and its discussion page have been added to your [[Special:Watchlist|watchlist]].",
+       "addedwatchtext-talk": "\"[[:$1]]\" and its associated page have been added to your [[Special:Watchlist|watchlist]].",
        "addedwatchtext-short": "The page \"$1\" has been added to your watchlist.",
        "removewatch": "Remove from watchlist",
        "removedwatchtext": "\"[[:$1]]\" and its discussion page have been removed from your [[Special:Watchlist|watchlist]].",
+       "removedwatchtext-talk": "\"[[:$1]]\" and its associated page have been removed from your [[Special:Watchlist|watchlist]].",
        "removedwatchtext-short": "The page \"$1\" has been removed from your watchlist.",
        "watch": "Watch",
        "watchthispage": "Watch this page",
        "rollbacklinkcount-morethan": "rollback more than $1 {{PLURAL:$1|edit|edits}}",
        "rollbackfailed": "Rollback failed",
        "rollback-missingparam": "Missing required parameters on request.",
+       "rollback-missingrevision": "Unable to load revision data.",
        "cantrollback": "Cannot revert edit;\nlast contributor is only author of this page.",
        "alreadyrolled": "Cannot rollback last edit of [[:$1]] by [[User:$2|$2]] ([[User talk:$2|talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nsomeone else has edited or rolled back the page already.\n\nThe last edit to the page was by [[User:$3|$3]] ([[User talk:$3|talk]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "The edit summary was: <em>$1</em>.",
        "undeletehistorynoadmin": "This page has been deleted.\nThe reason for deletion is shown in the summary below, along with details of the users who had edited this page before deletion.\nThe actual text of these deleted revisions is only available to administrators.",
        "undelete-revision": "Deleted revision of $1 (as of $4, at $5) by $3:",
        "undeleterevision-missing": "Invalid or missing revision.\nYou may have a bad link, or the revision may have been restored or removed from the archive.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|One revision|$1 revisions}} could not be restored, because {{PLURAL:$1|its|their}} <code>rev_id</code> was already in use.",
        "undelete-nodiff": "No previous revision found.",
        "undeletebtn": "Restore",
        "undeletelink": "view/restore",
        "undeletedrevisions": "{{PLURAL:$1|1 revision|$1 revisions}} restored",
        "undeletedrevisions-files": "{{PLURAL:$1|1 revision|$1 revisions}} and {{PLURAL:$2|1 file|$2 files}} restored",
        "undeletedfiles": "{{PLURAL:$1|1 file|$1 files}} restored",
-       "cannotundelete": "Undelete failed:\n$1",
+       "cannotundelete": "Some or all of the undeletion failed:\n$1",
        "undeletedpage": "<strong>$1 has been restored</strong>\n\nConsult the [[Special:Log/delete|deletion log]] for a record of recent deletions and restorations.",
        "undelete-header": "See [[Special:Log/delete|the deletion log]] for recently deleted pages.",
        "undelete-search-title": "Search deleted pages",
        "sp-contributions-newbies-sub": "For new accounts",
        "sp-contributions-newbies-title": "User contributions for new accounts",
        "sp-contributions-blocklog": "block log",
-       "sp-contributions-suppresslog": "suppressed user contributions",
-       "sp-contributions-deleted": "deleted user contributions",
+       "sp-contributions-suppresslog": "suppressed {{GENDER:$1|user}} contributions",
+       "sp-contributions-deleted": "deleted {{GENDER:$1|user}} contributions",
        "sp-contributions-uploads": "uploads",
        "sp-contributions-logs": "logs",
        "sp-contributions-talk": "talk",
        "pageinfo-article-id": "Page ID",
        "pageinfo-language": "Page content language",
        "pageinfo-content-model": "Page content model",
+       "pageinfo-content-model-change": "change",
        "pageinfo-robot-policy": "Indexing by robots",
        "pageinfo-robot-index": "Allowed",
        "pageinfo-robot-noindex": "Disallowed",
        "mw-widgets-dateinput-placeholder-month": "YYYY-MM",
        "mw-widgets-titleinput-description-new-page": "page does not exist yet",
        "mw-widgets-titleinput-description-redirect": "redirect to $1",
-       "api-error-blacklisted": "Please choose a different, descriptive title.",
        "sessionmanager-tie": "Cannot combine multiple request authentication types: $1.",
        "sessionprovider-generic": "$1 sessions",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "cookie-based sessions",
        "log-action-filter-newusers": "Type of account creation:",
        "log-action-filter-patrol": "Type of patrol:",
        "log-action-filter-protect": "Type of protection:",
-       "log-action-filter-rights": "Type of right change",
-       "log-action-filter-suppress": "Type of suppression",
+       "log-action-filter-rights": "Type of right change:",
+       "log-action-filter-suppress": "Type of suppression:",
        "log-action-filter-upload": "Type of upload:",
        "log-action-filter-all": "All",
        "log-action-filter-block-block": "Block",
        "linkaccounts-submit": "Link accounts",
        "unlinkaccounts": "Unlink accounts",
        "unlinkaccounts-success": "The account was unlinked.",
-       "authenticationdatachange-ignored": "The authentication data change was not handled. Maybe no provider was configured?"
+       "authenticationdatachange-ignored": "The authentication data change was not handled. Maybe no provider was configured?",
+       "userjsispublic": "Please note: JavaScript subpages should not contain confidential data as they are viewable by other users.",
+       "usercssispublic": "Please note: CSS subpages should not contain confidential data as they are viewable by other users."
 }
index 0828d6c..2da1d16 100644 (file)
                        "Xð",
                        "Robin van der Vliet",
                        "Zciric",
-                       "Psychoslave"
+                       "Psychoslave",
+                       "Orikrin1998",
+                       "Gamliel Fishkin"
                ]
        },
-       "tog-underline": "Substreki ligilojn",
-       "tog-hideminor": "Kaŝi malgrandajn redaktetojn ĉe <i>Lastaj ŝanĝoj</i>",
+       "tog-underline": "Substrekado de ligiloj:",
+       "tog-hideminor": "Kaŝi etajn redaktojn de la lastaj ŝanĝoj",
        "tog-hidepatrolled": "Kaŝi patrolitajn redaktojn en lastaj ŝanĝoj",
        "tog-newpageshidepatrolled": "Kaŝi patrolitajn paĝojn de listo de novaj paĝoj",
-       "tog-hidecategorization": "Kaŝu enkategoriigon de paĝoj",
+       "tog-hidecategorization": "Kaŝi enkategoriigon de paĝoj",
        "tog-extendwatchlist": "Etendi la atentaron por montri ĉiujn ŝanĝojn, ne nur la plej lastajn",
        "tog-usenewrc": "Grupigi ŝanĝojn laŭ paĝo en \"Lastaj ŝanĝoj\" kaj \"Atentaro\" (bezonas Ĝavaskripton)",
-       "tog-numberheadings": "Aŭtomate numerigi sekciojn",
-       "tog-showtoolbar": "Montri redakto-breton (per Ĝavaskripto)",
+       "tog-numberheadings": "Aŭtomate numeri sekciojn",
+       "tog-showtoolbar": "Montri redakto-breton",
        "tog-editondblclick": "Redakti paĝojn per duobla alklako",
        "tog-editsectiononrightclick": "Ŝalti sekcian redaktadon per dekstra musklako de sekciaj titoloj (per Ĝavaskripto)",
        "tog-watchcreations": "Aldoni miajn kreatajn paĝojn kaj miajn alŝutaĵojn al mia atentaro",
        "tog-watchdefault": "Aldoni al mia atentaro paĝojn kaj dosierojn redaktitajn de mi",
        "tog-watchmoves": "Aldoni paĝojn kaj dosierojn, kiujn mi movas, al mia atentaro",
        "tog-watchdeletion": "Aldoni paĝojn kaj dosierojn, kiujn mi forigas, al mia atentaro",
-       "tog-watchuploads": "Aldonu novajn dosierojn ke mi alŝutas al mia atentaro",
+       "tog-watchuploads": "Aldoni novajn dosierojn, kiujn mi alŝutos, al mia atentaro",
        "tog-watchrollback": "Aldoni paĝojn, kie mi amasmalfaris, al mia atentaro.",
        "tog-minordefault": "Marki defaŭlte ĉiujn redaktojn kiel etajn",
        "tog-previewontop": "Montri antaŭrigardon antaŭ redaktilo",
        "tog-watchlisthidebots": "Kaŝi robotajn redaktojn de la atentaro",
        "tog-watchlisthideminor": "Kaŝi malgrandajn redaktojn de la atentaro",
        "tog-watchlisthideliu": "Kaŝi redaktojn de ensalutitaj uzantoj de la atentaro",
-       "tog-watchlistreloadautomatically": "Reŝarĝi la atentaron aŭtomate ĉiam filtro estas ŝanĝita (Necesi Ĝavoskripton)",
+       "tog-watchlistreloadautomatically": "Reŝargi la atentaron aŭtomate ĉiam, kiam filtrilo estas ŝanĝita (bezonas Ĝavoskripton)",
        "tog-watchlisthideanons": "Kaŝi redaktojn de anonimuloj de la atentaro",
        "tog-watchlisthidepatrolled": "Kaŝi patrolitajn redaktojn de la atentaro",
-       "tog-watchlisthidecategorization": "Kaŝu enkategoriigon de paĝoj",
+       "tog-watchlisthidecategorization": "Kaŝi enkategoriigon de paĝoj",
        "tog-ccmeonemails": "Sendi al mi kopiojn de retpoŝtaĵoj, kiujn mi sendis al aliaj uzantoj.",
        "tog-diffonly": "Ne montri paĝan enhavon sub la ŝanĝmontrilo",
        "tog-showhiddencats": "Montri kaŝitajn kategoriojn",
-       "tog-norollbackdiff": "Nemontri diferencon post plenumado de ŝanĝomalfaro",
+       "tog-norollbackdiff": "Ne montri diferencon post plenumado de ŝanĝomalfaro",
        "tog-useeditwarning": "Averti min kiam mi forlasas redaktan paĝon kun nekonservitaj ŝanĝoj",
        "tog-prefershttps": "Ĉiam uzu sekuran konekton ensalutite",
        "underline-always": "Ĉiam",
        "sun": "dim",
        "mon": "lun",
        "tue": "mar",
-       "wed": "Mer",
-       "thu": "Ä´aŭ",
-       "fri": "Ven",
+       "wed": "mer",
+       "thu": "ĵaŭ",
+       "fri": "ven",
        "sat": "sab",
        "january": "januaro",
        "february": "februaro",
        "october-date": "$1‑a de oktobro",
        "november-date": "$1‑a de novembro",
        "december-date": "$1‑a de decembro",
-       "period-am": "ATM",
-       "period-pm": "PTM",
+       "period-am": "atm.",
+       "period-pm": "ptm.",
        "pagecategories": "{{PLURAL:$1|Kategorio|Kategorioj}}",
-       "category_header": "Artikoloj en kategorio \"$1\"",
+       "category_header": "Paĝoj en kategorio “$1”",
        "subcategories": "Subkategorioj",
-       "category-media-header": "Dosieroj en kategorio \"$1\"",
-       "category-empty": "<em>Tiu ĉi kategorio nuntempe enhavas neniun artikolon aŭ plurmedian dosieron.</em>",
+       "category-media-header": "Dosieroj en kategorio “$1”",
+       "category-empty": "<em>Tiu ĉi kategorio nuntempe enhavas neniun paĝon aŭ plurmedian dosieron.</em>",
        "hidden-categories": "{{PLURAL:$1|Kaŝita kategorio|Kaŝitaj kategorioj}}",
        "hidden-category-category": "Kaŝitaj kategorioj",
        "category-subcat-count": "{{PLURAL:$2|Ĉi tiu kategorio havas nur la jenan subkategorion.|Ĉi tiu kategorio havas la {{PLURAL:$1|jenan subkategorion|$1 jenajn subkategoriojn}}, el $2 entute.}}",
        "listingcontinuesabbrev": "daŭrigo",
        "index-category": "Indeksitaj paĝoj",
        "noindex-category": "Neindeksitaj paĝoj",
-       "broken-file-category": "Paĝoj kun eraraj dosierligiloj",
+       "broken-file-category": "Paĝoj kun rompita ligilo al dosiero",
        "about": "Pri",
-       "article": "Artikolo",
+       "article": "Enhava paĝo",
        "newwindow": "(en nova fenestro)",
        "cancel": "Nuligi",
        "moredotdotdot": "Pli...",
        "tagline": "El {{SITENAME}}",
        "help": "Helpo",
        "search": "Serĉi",
+       "search-ignored-headings": " #<!-- lasu ĉi tiun linion ekzakte kiel ĝi estas --> <pre>\n# Titoloj kiuj estos ignoritaj en serĉado.\n# Ŝanĝoj al ĉi tio efikas tuj kiam la paĝo kun la titolo estas indeksita.\n# Vi povas igi reindeksadon de paĝo, farinte nulan redakton.\n# La sintakso estas jena:\n#   * Io ajn ekde signo \"#\" ĝis la fino de la linio estas komento.\n#   * Ĉiu nemalplena linio estas la ekzakta titolo por ignori usklecon kaj kion ajn.\nReferencoj\nEksteraj ligiloj\nVidu Ankaŭ\n #</pre> <!-- lasu ĉi tiun linion ekzakte kiel ĝi estas -->",
        "searchbutton": "Serĉi",
        "go": "Ek",
        "searcharticle": "Ek",
-       "history": "Historio de versioj",
+       "history": "Historio de paĝo",
        "history_short": "Historio",
        "updatedmarker": "ĝisdatigita de post mia lasta vizito",
        "printableversion": "Presebla versio",
        "permalink": "Konstanta ligilo",
-       "print": "Printi",
+       "print": "Presi",
        "view": "Vidi",
        "view-foreign": "Rigardi en $1",
        "edit": "Redakti",
        "protect": "Protekti",
        "protect_change": "ŝanĝi",
        "protectthispage": "Protekti la paĝon",
-       "unprotect": "Ŝanĝi protekadon",
-       "unprotectthispage": "Ŝanĝi protektadon de ĉi tiu paĝo",
+       "unprotect": "Ŝanĝi protekton",
+       "unprotectthispage": "Ŝanĝi protekton de ĉi tiu paĝo",
        "newpage": "Nova paĝo",
        "talkpage": "Diskuti la paĝon",
        "talkpagelinktext": "diskuto",
        "specialpage": "Speciala paĝo",
        "personaltools": "Personaj iloj",
-       "articlepage": "Vidi paĝenhavon",
+       "articlepage": "Vidi enhavan paĝon",
        "talk": "Diskuto",
        "views": "Vidoj",
        "toolbox": "Iloj",
-       "userpage": "Vidi uzulan paĝon",
+       "userpage": "Vidi uzantopaĝon",
        "projectpage": "Rigardi projektopaĝon",
        "imagepage": "Vidi dosieropaĝon",
        "mediawikipage": "Vidi mesaĝopaĝon",
        "jumpto": "Iri al:",
        "jumptonavigation": "navigado",
        "jumptosearch": "serĉi",
-       "view-pool-error": "Bedaŭrinde la serviloj estas tro uzataj ĉi-momente.\nTro da uzantoj provas vidi ĉi tiun paĝon.\nBonvolu atendi iom antaŭ ol provi atingi ĝin denove.\n\n$1",
-       "generic-pool-error": "Bedaŭrinde la serviloj estas tro uzataj ĉi-momente.\nTro da uzantoj provas vidi ĉi tiun risurcon.\nBonvolu iom atendi antaŭ vi provos atingi ĝin denove.",
+       "view-pool-error": "Pardonon, la serviloj estas tro uzataj ĉi-momente.\nTro da uzantoj provas vidi ĉi tiun paĝon.\nBonvolu atendi iom antaŭ ol provi atingi ĝin denove.\n\n$1",
+       "generic-pool-error": "Pardonon, la serviloj estas tro uzataj ĉi-momente.\nTro da uzantoj provas vidi ĉi tiun risurcon.\nBonvolu iom atendi antaŭ vi provos atingi ĝin denove.",
        "pool-timeout": "Tempolimo atingita dum atendo de ŝlosado",
        "pool-queuefull": "Atendovico de servilaro estas plena.",
        "pool-errorunknown": "Nekonata eraro",
        "badaccess-groups": "La ago, kiun vi petis, estas limigita al uzantoj en {{PLURAL:$2|la grupo|unu el la grupoj}}: $1.",
        "versionrequired": "Versio $1 de MediaWiki nepras",
        "versionrequiredtext": "La versio $1 de MediaWiki estas necesa por uzi ĉi tiun paĝon. Vidu [[Special:Version|paĝon pri versio]].",
-       "ok": "Ek!",
+       "ok": "Bone",
        "retrievedfrom": "Elŝutita el  \"$1\"",
        "youhavenewmessages": "{{PLURAL:$3|Vi havas}} $1 ($2).",
-       "youhavenewmessagesfromusers": "Riceviĝis $1 de {{PLURAL:$3|alia uzanto|$3 uzantoj}} ($2).",
+       "youhavenewmessagesfromusers": "Riceviĝis $1 de {{PLURAL:$3|alia uzanto|$3 uzantoj}} ($2).\n\nVi havas $1 de {{PLURAL:$3|alia uzanto|$3 uzantoj}} ($2).",
        "youhavenewmessagesmanyusers": "Riceviĝis $1 de multaj uzantoj ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|nova mesaĝo|999=novaj mesaĝoj}}",
        "newmessagesdifflinkplural": "$1 {{PLURAL:$1|ŝanĝo|ŝanĝoj}}",
        "cannotlogoutnow-text": "Ne eblas elsaluti dum uzado de $1.",
        "welcomeuser": "Bonvenon, $1!",
        "welcomecreation-msg": "Via konto estas kreita.\nNe forgesu ŝanĝi viajn [[Special:Preferences|{{SITENAME}}-preferojn]]",
-       "yourname": "Salutnomo:",
-       "userlogin-yourname": "Uzantonomo",
+       "yourname": "Uzantnomo:",
+       "userlogin-yourname": "Uzantnomo",
        "userlogin-yourname-ph": "Enigu vian uzantonomon",
        "createacct-another-username-ph": "Enigu la salutnomon:",
        "yourpassword": "Pasvorto:",
        "yourpasswordagain": "Retajpu pasvorton",
        "createacct-yourpasswordagain": "Konfirmu pasvorton",
        "createacct-yourpasswordagain-ph": "Retajpu pasvorton",
-       "remembermypassword": "Memori mian ensalutadon ĉe ĉi tiu komputilo (daŭrante maksimume $1 {{PLURAL:$1|tagon|tagojn}})",
        "userlogin-remembermypassword": "Memori mian ensaluton",
        "userlogin-signwithsecure": "Uzu sekurigitan konekton",
        "cannotloginnow-title": "Nuntempe ne eblas ensaluti",
        "createacct-reason": "Kialo",
        "createacct-reason-ph": "Kial vi kreas plian konton",
        "createacct-reason-help": "Mesaĝo vidigita en la protokolo pri kreado de konto",
-       "createacct-submit": "Krei konton",
+       "createacct-submit": "Kreu vian konton",
        "createacct-another-submit": "Krei konton",
        "createacct-continue-submit": "Daŭri kreadon de konto",
        "createacct-another-continue-submit": "Daŭri kreadon de konto",
        "changepassword-success": "Via pasvorto estis ŝanĝita!",
        "changepassword-throttled": "Vi tro ofte provis ensaluti al ĉi tiu konto.\nBonvolu atendi $1 antaŭ ol reprovi.",
        "botpasswords": "Robotaj pasvortoj",
-       "botpasswords-summary": "<em>Robotaj pasvortoj</em> ebligas aliron al uzanto-konto per API sen uzado de ĉefaj ensalutaj datumoj de la konto. La uzanto-rajtoj disponeblaj dum ensaluto per robota pasvorto povas esti limigitaj.\n\nSe vi ne scias, kial vi devus fari tion, vi probable maldevus fari tion. Neniu devus peti vin generi pasvorton tie ĉi kaj transdoni ĝin al li.",
+       "botpasswords-summary": "<em>Robotaj pasvortoj</em> ebligas aliron al uzanto-konto per API sen uzado de ĉefaj ensalutaj datumoj de la konto. La uzantorajtoj disponeblaj dum ensaluto per robota pasvorto povas esti limigitaj.\n\nSe vi ne scias, kial vi devus fari tion, vi probable ne devus fari tion. Neniu devus peti vin generi pasvorton ĉi tie por transdoni.",
        "botpasswords-disabled": "Robotaj pasvortoj estas malŝaltitaj.",
        "botpasswords-no-central-id": "Por uzi robotajn pasvortojn vi devas esti ensalutita al centra konto.",
        "botpasswords-existing": "Ekzistantaj robotaj pasvortoj",
        "passwordreset-text-many": "{{PLURAL:$1|Plenumu unu el la kampoj por restarigi vian pasvorton.}}",
        "passwordreset-disabled": "Pasvortaj restarigoj estis malŝaltitaj en ĉi tiu vikio.",
        "passwordreset-emaildisabled": "Retpoŝtaj funkcioj estas malfunkciigitaj en tiu ĉi vikio.",
-       "passwordreset-username": "Salutnomo:",
+       "passwordreset-username": "Uzantnomo:",
        "passwordreset-domain": "Domajno:",
        "passwordreset-capture": "Vidi la rezultan retpoŝton?",
        "passwordreset-capture-help": "Se vi markis ĉi tiun skatoleton, la retpoŝto (kun provizora pasvorto) estos montrita al vi kaj estos sendita al la uzanto.",
        "passwordreset-emailelement": "Salutnomo: \n$1\n\nProvizora pasvorto: \n$2",
        "passwordreset-emailsentemail": "Se tiu ĉu retpoŝta adreso estas kunligita kun via konto, tiam al ĉi tiu adreso estos sendita retpoŝto por renovigi pasvorton.",
        "passwordreset-emailsentusername": "Se estas retpoŝta adreso, kiu estas asociita kun tiu uzantnomo, tiam ni sendos retpoŝtan mesaĝon pri reagordado de la pasvorto.",
-       "passwordreset-emailsent-capture": "Retpoŝto kun renovigita pasvorto estis sendita, kiu estas montrata malsupre.",
-       "passwordreset-emailerror-capture": "Retpoŝto kun renovigita pasvorto estis generita, montrata sube, sed sendado al la {{GENDER:$2|uzanto}} malsukcesis: $1",
        "passwordreset-emailsent-capture2": "La {{PLURAL:$1|retpoŝto|retpoŝtojn}} de pasvorta reensignado estis sendita. La {{PLURAL:$1|salutnomo kaj pasvorto|listo de salutnomoj kaj pasvortoj}} estas vidigita sube.",
-       "passwordreset-emailerror-capture2": "Retpoŝtado al la {{GENDER:$2|uzantiĉo|uzantino|uzanto}} malsukcesis: $1 La {{PLURAL:$3|salutnomo kaj pasvorta|listo de salutnomoj kaj pasvortoj}} estas vidigita sube.",
+       "passwordreset-emailerror-capture2": "Retpoŝtado al la {{GENDER:$2|uzanto}} malsukcesis: $1 La {{PLURAL:$3|uzantnomo kaj pasvorto|listo de uzantnomoj kaj pasvortoj}} estas vidigita malsupre.",
        "passwordreset-nocaller": "Vokanto devas esti provizita",
        "passwordreset-nosuchcaller": "Vokanto ne ekzistas: $1",
        "passwordreset-ignored": "La pasvorta reensignado ne estis pritraktita. Eble neniu provizanto estis formita?",
        "passwordreset-nodata": "Nek salutnomo nek retpoŝta adreso estis provizita",
        "changeemail": "Ŝanĝi aŭ forigi retpoŝtadreson",
        "changeemail-header": "Plenigu ĉi tiun formularon por ŝanĝi vian retpoŝtadreson. Se vi volas forigi la difinon de retpoŝtadreso por via uzantokonto, lasu la kampon por la nova retpoŝtadreso malplena ĉe la transigo.",
-       "changeemail-passwordrequired": "Vi devas entajpi vian pasvorton, por konfirmi ĉi tiun ŝanĝon.",
        "changeemail-no-info": "Vi devas ensaluti por atingi ĉi tiun paĝon rekte.",
        "changeemail-oldemail": "Aktuala retpoŝtadreso:",
        "changeemail-newemail": "Nova retpoŝtadreso:",
        "minoredit": "Ĉi tiu ŝanĝo estas redakteto",
        "watchthis": "Atenti ĉi tiun paĝon",
        "savearticle": "Konservi paĝon",
-       "publishpage": "Publikigi paĝon",
+       "savechanges": "Konservi ŝanĝojn",
+       "publishpage": "Eldoni paĝon",
+       "publishchanges": "Eldoni ŝanĝojn",
        "preview": "Antaŭrigardo",
        "showpreview": "Antaŭrigardo",
        "showdiff": "Montri ŝanĝojn",
        "accmailtext": "Hazarde generita pasvorto por [[User talk:$1|$1]] estis sendita al $2.\n\nLa pasvorto por ĉi tiu nova konto povas esti ŝanĝita en la paĝo ''[[Special:ChangePassword|ŝanĝi pasvorton]]'' dum ensalutado.",
        "newarticle": "(Nova)",
        "newarticletext": "Vi sekvis ligilon al paĝo ankoraŭ ne ekzistanta. Se vi volas krei ĝin, ektajpu malsupre (vidu la [$1 helpopaĝon] por klarigoj.) Se vi malintence alvenis ĉi tien, simple alklaku la retrobutonon de via retumilo.",
-       "anontalkpagetext": "<em>Jen diskutopaĝo por anonima kontribuanto kiu ne jam kreis konton aŭ ne uzas ĝin.</em>\nNi tial devas uzi la cifran IP-adreson por identigi lin/ŝin.\nĈi tia IP-adreso povas esti uzata de pluraj uzantoj.\nSe vi estas anonimulo kaj preferus eviti tiajn mistrafajn komentojn al vi, bonvolu [[Special:CreateAccount|krei konton]] aŭ [[Special:UserLogin|ensaluti]] por eviti estontan konfuzon pro aliaj anonimaj uzantoj.''",
+       "anontalkpagetext": "<em>Jen diskutopaĝo por anonima kontribuanto kiu ankoraŭ ne kreis konton aŭ ne uzas ĝin.</em>\nNi tial devas uzi la cifran IP-adreson por identigi tiun kontribuanton.\nĈi tia IP-adreso povas esti uzata de pluraj uzantoj.\nSe vi estas anonimulo kaj preferus eviti tiajn mistrafajn komentojn al vi, bonvolu [[Special:CreateAccount|krei konton]] aŭ [[Special:UserLogin|ensaluti]] por eviti estontan konfuzon pro aliaj anonimaj uzantoj.''",
        "noarticletext": "Mankas teksto en ĉi tiu paĝo.\nVi povas [[Special:Search/{{PAGENAME}}|serĉi ĉi tiun paĝtitolon]] en aliaj paĝoj,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} serĉi la rilatajn protokolojn],\naŭ [{{fullurl:{{FULLPAGENAME}}|action=edit}} krei ĉi tiun paĝon]</span>.",
        "noarticletext-nopermission": "Estas neniom da teksto en ĉi tiu paĝo.\nVi povas [[Special:Search/{{PAGENAME}}|serĉi ĉi tiun paĝan titolon]] en aliaj paĝoj,\naŭ <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} serĉi la rilatajn protokolojn]</span>, sed vi ne rajtas krei ĉi tiun paĝon.",
        "missing-revision": "La revizio n-ro $1 de la paĝo nomata \"{{FULLPAGENAME}}\" ne ekzistas.\n\nTio kutime estas kaŭzata per sekvado de malaktuala historio-ligilo al paĝo forigita.\nDetaloj troveblos en la [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de forigoj].",
        "content-model-css": "CSS",
        "content-json-empty-object": "Malplena objeto",
        "content-json-empty-array": "Malplena tabelo",
+       "deprecated-self-close-category": "Paĝoj kun nevalida memferma HTML‑etikedo",
+       "deprecated-self-close-category-desc": "La paĝo enhavas malvalidajn memfermajn HTML-markojn, tiajn kiel <code>&lt;b/></code> aŭ <code>&lt;span/></code>. Ilia konduto baldaŭ estos ŝanĝita por esti kongrua kun la specifiko de HTML5, tial ilia uzo en vikia teksto estas malrekomendata.",
        "duplicate-args-warning": "'''Averto:''' [[:$1]] vokas al [[:$2]] kun pli ol unu valoro por la parametro \"$3\". Nur la lasta liverita valoro estos uzata.",
        "duplicate-args-category": "Paĝoj kun pluroblaj argumentoj en ŝablonvokoj",
        "duplicate-args-category-desc": "La paĝo enhavas uzon de ŝablono kun pluroble uzitaj argumentoj, kiel ekzemple <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> aŭ <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-norev": "La redakto ne povis esti malfarita ĉar ĝi aŭ ne ekzistas aŭ estis forigita.",
        "undo-nochange": "Ŝajne la redakto jam estis malfarita.",
        "undo-summary": "Nuligis version $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|Diskuto]] | [[Special:Contributions/$2|{{MediaWiki:Contribslink}}]])",
-       "undo-summary-username-hidden": "Malfari ŝanĝon $1 de kaŝita uzulo",
-       "cantcreateaccounttitle": "Ne eblas krei konton",
+       "undo-summary-username-hidden": "Malfari ŝanĝon $1 de kaŝita uzanto",
        "cantcreateaccount-text": "Konto-kreado de ĉi tiu IP-adreso ('''$1''') estis forbarita de [[User:$3|$3]].\n\nLa kialo donata de $3 estas ''$2''.",
        "cantcreateaccount-range-text": "La kreado de kontoj de IP-adresoj en la intervalo <strong>$1</strong>, kiu inkludas vian IP-adreson (<strong>$4</strong>), estis blokita de [[User:$3|$3]].\n\nLa donita kialo de $3 estas <em>$2</em>",
-       "viewpagelogs": "Vidi la protokolojn por tiu ĉi paĝo",
+       "viewpagelogs": "Vidi la protokolojn por ĉi tiu paĝo",
        "nohistory": "Ne ekzistas historio de redaktoj por ĉi tiu paĝo.",
        "currentrev": "Aktuala versio",
        "currentrev-asof": "Nuna versio ekde $1",
        "rev-suppressed-unhide-diff": "Unu el la revizioj de ĉi tiu diferenco estis '''kaŝita'''.\nEble estas detaloj en la [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} protokolo pri kaŝado].\nVi povas ankoraŭ [$1 vidi ĉi tiun diferencon] se vi deziras.",
        "rev-deleted-diff-view": "Unu el la revizioj de ĉi tiu diferenco estis '''forigita'''.\nVi povas rigardi ĉi tiun diferencon, detaloj estas trovebla en la [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} protokolo pri forigado].",
        "rev-suppressed-diff-view": "Unu el la revizioj de ĉi tiu diferenco estis '''kaŝita'''.\nKiel administranto, vi povas rigardi ĉi tiun diferencon, eble estas detaloj en la [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} protokolo pri kaŝadoj].",
-       "rev-delundel": "montri/kaŝi",
+       "rev-delundel": "ŝanĝi videblecon",
        "rev-showdeleted": "montri",
        "revisiondelete": "Forigi/malforigi versiojn",
        "revdelete-nooldid-title": "Nevalida cela revizio",
        "mergelog": "Protokolo de kunigoj",
        "revertmerge": "Malkunigi",
        "mergelogpagetext": "Jen listo de la plej lastatempaj kunigoj de unu paĝhistorio en alian.",
-       "history-title": "Revizio-historio de \"$1\"",
+       "history-title": "Revizio-historio de “$1”",
        "difference-title": "$1: Malsamoj inter versioj",
        "difference-title-multipage": "Malsamoj inter la paĝoj $1 kaj $2",
        "difference-multipage": "(Diferenco inter paĝoj)",
        "searchprofile-images": "Plurmedio",
        "searchprofile-everything": "Ĉion",
        "searchprofile-advanced": "Progresa",
-       "searchprofile-articles-tooltip": "Serĉo en $1",
+       "searchprofile-articles-tooltip": "Serĉi en $1",
        "searchprofile-images-tooltip": "Serĉi dosierojn",
        "searchprofile-everything-tooltip": "Traserĉi ĉiun enhavon (inkluzivante diskuto-paĝojn)",
        "searchprofile-advanced-tooltip": "Serĉi en specialaj nomspacoj",
        "prefs-reset-intro": "Vi povas uzi ĉi tiun paĝon por restarigi viajn agordojn al la originalaj defaŭltoj.\nĈi tiel ne estus malfarebla.",
        "prefs-emailconfirm-label": "Retpoŝta konfirmado:",
        "youremail": "Retadreso:",
-       "username": "{{GENDER:$1|Salutnomo}}:",
+       "username": "{{GENDER:$1|Uzantnomo}}:",
        "prefs-memberingroups": "{{GENDER:$2|Ano}} de {{PLURAL:$1|grupo|grupoj}}:",
        "prefs-registration": "Tempo de registrado:",
        "yourrealname": "Vera nomo:",
        "prefs-help-signature": "Komentoj en diskuto-paĝoj estu subskribita kun \"<nowiki>~~~~</nowiki>\" kiu estos konvertita al via subskribo kaj tempindiko.",
        "badsig": "Via kaŝnomo (por subskriboj) malvalidas. Bv. kontroli la HTML-etikedojn!",
        "badsiglength": "La subskribo estas tro longa.\nĜi devas esti sub $1 {{PLURAL:$1|signo|signoj}}.",
-       "yourgender": "Sekso:",
-       "gender-unknown": "Menciate vin, la programaro uzos vortojn de neŭtra genro, ĉiam kiam estos ebla",
-       "gender-male": "Vira",
-       "gender-female": "Ina",
+       "yourgender": "Kiel vi preferas esti priskribata?",
+       "gender-unknown": "Menciate vin, la programaro uzos seksneŭtrajn vortojn ĉiam kiam eblas",
+       "gender-male": "Li redaktas vikipaĝojn",
+       "gender-female": "Ŝi redaktas vikipaĝojn",
        "prefs-help-gender": "Nedeviga: uzita por sekseca salutado de la programaro. Ĉi tiu informo montriĝos publike.",
        "email": "Retadreso",
        "prefs-help-realname": "* Vera nomo estas nedeviga.\nSe vi elektas sciigi ĝin, ĝi estos uzita por aŭtorigi vin por viaj kontribuoj.",
        "userrights-lookup-user": "Administri grupojn de uzantoj",
        "userrights-user-editname": "Entajpu salutnomon:",
        "editusergroup": "Redakti grupojn de {{GENDER:$1|uzanto}}",
-       "editinguser": "Ŝanĝado de uzanto-rajtoj de la {{GENDER:$1|uzanto|uzantino}} <strong>[[User:$1|$1]]</strong> $2",
+       "editinguser": "Ŝanĝado de uzantorajtoj de la {{GENDER:$1|uzanto}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Redakti grupojn de uzantoj",
-       "saveusergroups": "Konservi grupojn de {{GENDER:$1|viruzuloj|uzulinoj|uzantoj}}",
+       "saveusergroups": "Konservi grupojn de {{GENDER:$1|uzantoj}}",
        "userrights-groupsmember": "Membro de:",
        "userrights-groupsmember-auto": "Implica membro de:",
        "userrights-groups-help": "Vi povas modifi la grupojn kiun ĉi uzanto enestas.\n* Markita markbutono signifas ke la uzanto estas en tiu grupo.\n* Nemarkita markbutono signifas ke la uzanto ne estas in tiu grupo.\n* Steleto (*) signifas ke vi ne povas forigi la grupon post vi aldonis ĝin, aŭ male.",
        "group-bureaucrat": "Burokratoj",
        "group-suppress": "Foriganaro",
        "group-all": "(ĉiuj)",
-       "group-user-member": "{{GENDER:$1|uzanto|uzantino}}",
-       "group-autoconfirmed-member": "{{GENDER:$1|aŭtomate konfirmita uzanto|aŭtomate konfirmita uzantino}}",
+       "group-user-member": "{{GENDER:$1|uzanto}}",
+       "group-autoconfirmed-member": "{{GENDER:$1|aŭtomate konfirmita uzanto}}",
        "group-bot-member": "{{GENDER:$1|roboto}}",
-       "group-sysop-member": "{{GENDER:$1|Administranto|Administrantino}}",
-       "group-bureaucrat-member": "{{GENDER:$1|Burokrato|Burokratino}}",
-       "group-suppress-member": "{{GENDER:$1|Superrigardanto|Superrigardantino}}",
+       "group-sysop-member": "{{GENDER:$1|administranto}}",
+       "group-bureaucrat-member": "{{GENDER:$1|burokrato}}",
+       "group-suppress-member": "{{GENDER:$1|superrigardanto}}",
        "grouppage-user": "{{ns:project}}:Uzantoj",
        "grouppage-autoconfirmed": "{{ns:project}}:Aŭtomate konfirmitaj uzantoj",
        "grouppage-bot": "{{ns:project}}:Robotoj",
        "grant-group-high-volume": "Efektivigi ampleksegajn agojn",
        "grant-group-customization": "Personecigoj kaj preferoj",
        "grant-group-administration": "Efektivigi administrajn agojn",
+       "grant-group-private-information": "Aliru privatan datumon pri vi",
        "grant-group-other": "Diversaj aktivecoj",
        "grant-blockusers": "Bloki kaj malbloki uzantojn",
        "grant-createaccount": "Krei kontojn",
        "grant-highvolume": "Ampleksega redaktado",
        "grant-oversight": "Kaŝi uzantojn kaj forigi reviziaĵojn",
        "grant-patrol": "Patroli ŝanĝojn al pâgoj",
+       "grant-privateinfo": "Aliro al privataj informoj",
        "grant-protect": "Protekti kaj malprotekti paĝojn",
        "grant-rollback": "Malfari ŝanĝojn de paĝoj",
        "grant-sendemail": "Retpoŝti al aliaj uzantoj",
        "rightslogtext": "Ĉi tio estas protokolo pri la ŝanĝoj de uzantorajtoj.",
        "action-read": "legi ĉi tiun paĝon",
        "action-edit": "redakti ĉi tiun paĝon",
-       "action-createpage": "krei paĝojn",
-       "action-createtalk": "krei diskuto-paĝojn",
+       "action-createpage": "krei ĉi tiun paĝon",
+       "action-createtalk": "krei ĉi tiun diskutpaĝon",
        "action-createaccount": "krei ĉi tiun uzanto-konton",
        "action-autocreateaccount": "Aŭtomate krei tiun eksteruzantan konton",
        "action-history": "vidi historion de tiu ĉi paĝo",
        "rcshowhidemine": "$1 miajn redaktojn",
        "rcshowhidemine-show": "Montri",
        "rcshowhidemine-hide": "Kaŝi",
-       "rcshowhidecategorization": "$1 paĝa enkategoriigo",
+       "rcshowhidecategorization": "$1 kategoriigon de paĝoj",
        "rcshowhidecategorization-show": "Montri",
        "rcshowhidecategorization-hide": "Kaŝi",
        "rclinks": "Montri $1 lastajn ŝanĝojn dum la $2 lastaj tagoj.<br />$3",
        "badfilename": "Dosiernomo estis ŝanĝita al \"$1\".",
        "filetype-mime-mismatch": "Dosiera sufikso \".$1\" ne kongruas la MIME-tipon de la dosiero ($2).",
        "filetype-badmime": "Dosieroj de la MIME-tipo \"$1\" ne estas permesitaj por alŝutado.",
-       "filetype-bad-ie-mime": "Ne povas alŝuti ĉi tiun dosieron, ĉar Interreta Esplorilo detektus ĝin kiel \"$1\", kiu estas malpermesita kaj eble danĝera dosiertipo.",
+       "filetype-bad-ie-mime": "Ne povas alŝuti ĉi tiun dosieron, ĉar Internet Explorer detektus ĝin kiel dosieron “$1”, kiu estas malpermesita kaj eble danĝera dosiertipo.",
        "filetype-unwanted-type": "'''\".$1\"''' estas nevolata dosiero-tipo. {{PLURAL:$3|Preferata dosiero-tipo|Prefereataj dosiero-tipoj}} estas $2.",
        "filetype-banned-type": "'''\".$1\"''' ne estas {{PLURAL:$4|permesita dosiero-tipo|permesitaj dosiero-tipoj}}. {{PLURAL:$3|Permesita dosiero-tipo|Permesitaj dosiero-tipoj}} estas $2.",
        "filetype-missing": "Ĉi tiu dosiero ne inkluzivas finaĵon de dosiernomo (kiel \".jpg\").",
        "usercreated": "{{GENDER:$3|Kreita}} je $1, $2",
        "newpages": "Novaj paĝoj",
        "newpages-submit": "Montri",
-       "newpages-username": "Salutnomo:",
+       "newpages-username": "Uzantnomo:",
        "ancientpages": "Plej malnovaj artikoloj",
        "move": "Alinomi",
        "movethispage": "Alinomi ĉi tiun paĝon",
        "emailuser": "Retpoŝti ĉi tiun uzanton",
        "emailuser-title-target": "Retpoŝti ĉi tiun {{GENDER:$1|uzanton}}",
        "emailuser-title-notarget": "Retpoŝti uzanton",
-       "emailpagetext": "Vi povas uzi la jenan paĝon por sendi retpoŝtan mesaĝon al ĉi tiu {{GENDER:$1|uzanto|uzantino}}.\nLa retadreso kiun vi enigis en [[Special:Preferences|viaj preferoj]] aperos kiel la \"De\" adreso de la retpoŝto, do la ricevonto eblos respondi rekte al vi.",
+       "emailpagetext": "Vi povas uzi la jenan paĝon por sendi retpoŝtan mesaĝon al ĉi tiu {{GENDER:$1|uzanto}}.\nLa retadreso kiun vi enigis en [[Special:Preferences|viaj preferoj]] aperos kiel la \"De\" adreso de la retpoŝto, do la ricevonto eblos respondi rekte al vi.",
        "defemailsubject": "{{SITENAME}} retmesaĝo de uzanto \"$1\"",
        "usermaildisabled": "Retpoŝto de uzantoj estas malŝaltita",
        "usermaildisabledtext": "Vi ne povas sendi retpoŝton al aliaj uzantoj en ĉi tiu vikio",
        "nowikiemailtext": "Ĉi tiu uzanto elektis ne ricevi retpoŝton de aliaj uzantoj.",
        "emailnotarget": "Neekzistanta aŭ malvalida salutnomo por ricevanto.",
        "emailtarget": "Enigi salutnomon de ricevonto",
-       "emailusername": "Salutnomo:",
+       "emailusername": "Uzantnomo:",
        "emailusernamesubmit": "Enigi",
        "email-legend": "Sendi retpoŝton al alia {{SITENAME}}-uzanto",
        "emailfrom": "De:",
        "watchnologin": "Ne ensalutinta",
        "addwatch": "Aldoniĝi al atentaro",
        "addedwatchtext": "\"[[:$1]]\" kaj ĝia diskutpaĝo estis aldonitaj al via [[Special:Watchlist|atentaro]].",
+       "addedwatchtext-talk": "«[[:$1]]» kaj asociita kun ĝi paĝo estas aldonitaj al via [[Special:Watchlist|atentaro]].",
        "addedwatchtext-short": "La paĝo \"$1\" estis aldonita al via atento-listo.",
        "removewatch": "Forigi el atentaro",
        "removedwatchtext": "\"[[:$1]]\" kaj ĝia diskutpaĝo estis forigita el via [[Special:Watchlist|atentaro]].",
+       "removedwatchtext-talk": "«[[:$1]]» kaj asociita kin ĝi paĝo estas forigitaj el via [[Special:Watchlist|atentaro]].",
        "removedwatchtext-short": "La paĝo \"$1\" estis forigita el via atento-listo.",
        "watch": "Atenti",
        "watchthispage": "Priatenti paĝon",
        "actionfailed": "Ago malsukcesis",
        "deletedtext": "\"$1\" estas forigita.\nVidu la paĝon $2 por registro de lastatempaj forigoj.",
        "dellogpage": "Protokolo pri forigoj",
-       "dellogpagetext": "Jen listo de la plej lastaj forigoj el la datumaro.\nĈiuj tempoj sekvas la horzonon UTC.",
+       "dellogpagetext": "Jen listo de la plej lastaj forigoj.",
        "deletionlog": "protokolo pri forigoj",
        "reverted": "Malfaris al antaŭa revisio",
        "deletecomment": "Kialo:",
        "rollbacklinkcount-morethan": "nuligi pli ol $1 {{PLURAL:$1|redakton|redaktojn}}",
        "rollbackfailed": "Malfaro malsukcesis",
        "rollback-missingparam": "Mankas neprajn parametrojn de peto.",
+       "rollback-missingrevision": "Ne eblas ŝarĝi datumojn pri revizioj.",
        "cantrollback": "Ne povas restarigi antaŭan redakton; la redaktinto lasta estas la sola aŭtoro de la paĝo.",
        "alreadyrolled": "Ne povas restarigi la lastan redakton de [[:$1]] de la [[User:$2|$2]] ([[User talk:$2|diskuto]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\npro tio, ke oni intertempe redaktis aŭ restarigis la paĝon.\nLa lasta redaktinto estis [[User:$3|$3]] ([[User talk:$3|diskuto]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "La resumo de la redakto estis: <em>$1</em>.",
        "undeletehistorynoadmin": "Ĉi tiu artikolo estis forigita. La kaŭzo por la forigo estas montrata en la malsupra resumo, kune kun detaloj pri la uzantoj, kiuj redaktis ĉi tiun paĝon antaŭ la forigo. La aktuala teksto de ĉi tiuj forigitaj versioj estas atingebla nur de administrantoj.",
        "undelete-revision": "Forigita versio de $1 (ekde $4, $5) fare de $3:",
        "undeleterevision-missing": "Malvalida aŭ malaperita revizio.\nVi verŝajne havas malbonan ligilon, aŭ la revizio eble estis restarigita aŭ forigita de la arkivo.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|Unu versio|$1 versioj}} estas nerestarigeblaj, ĉar {{PLURAL:$1|ĝ|il}}ia <code>rev_id</code> jam estas uzata.",
        "undelete-nodiff": "Neniu antaŭa versio troviĝis.",
        "undeletebtn": "Restarigi",
        "undeletelink": "vidi/restarigi",
        "namespace_association": "Asociita nomspaco",
        "tooltip-namespace_association": "Marku ĉi tiu skatolo por inkluzivi la diskutan aŭ teman nomspacon asocie de la elekta nomspaco",
        "blanknamespace": "(Ĉefa)",
-       "contributions": "Kontribuoj de {{GENDER:$1|uzanto|uzantino}}",
+       "contributions": "Kontribuoj de {{GENDER:$1|uzanto}}",
        "contributions-title": "Kontribuoj de uzanto $1",
        "mycontris": "Kontribuoj",
        "anoncontribs": "Kontribuoj",
        "sp-contributions-newbies-sub": "Kontribuoj de novaj uzantoj. Forigitaj paĝoj ne estas montritaj.",
        "sp-contributions-newbies-title": "Kontribuoj de novaj uzantoj",
        "sp-contributions-blocklog": "protokolo de forbaroj",
-       "sp-contributions-suppresslog": "kaŝitaj kontribuoj de uzanto",
-       "sp-contributions-deleted": "forigitaj kontribuoj de uzanto",
+       "sp-contributions-suppresslog": "kaŝitaj kontribuoj de {{GENDER:$1|uzanto}}",
+       "sp-contributions-deleted": "forigitaj kontribuoj de {{GENDER:$1|uzanto}}",
        "sp-contributions-uploads": "alŝutoj",
        "sp-contributions-logs": "protokoloj",
        "sp-contributions-talk": "diskuto",
        "ipboptions": "2 horoj:2 hours,1 tago:1 day,3 tagoj:3 days,1 semajno:1 week,2 semajnoj:2 weeks,1 monato:1 month,3 monatoj:3 months,6 monatoj:6 months,1 jaro:1 year,porĉiam:infinite",
        "ipbhidename": "Kaŝi salutnomon de redaktoj kaj listoj",
        "ipbwatchuser": "Atenti la paĝojn por uzanto kaj diskuto de ĉi tiu uzanto.",
-       "ipb-disableusertalk": "Preventi ĉi tiun uzanton redakti sian diskuto-paĝon, dum li estas forbarita",
+       "ipb-disableusertalk": "Preventi ĉi tiun uzanton redakti sian diskutpaĝon, dum ĝi estas forbarata",
        "ipb-change-block": "Reforbari la uzanton kun ĉi tiuj preferoj",
        "ipb-confirm": "Konfirmi forbaron",
        "badipaddress": "Neniu uzanto, aŭ la IP-adreso estas misformita.",
        "tooltip-pt-watchlist": "Listo de paĝoj kies ŝanĝojn vi priatentas.",
        "tooltip-pt-mycontris": "Listo de viaj kontribuoj",
        "tooltip-pt-anoncontribs": "Listo de redaktoj faritaj el ĉi tiu IPa adreso",
-       "tooltip-pt-login": "Vi estas invitita ensaluti, tamen ne estas devige.",
+       "tooltip-pt-login": "Vi kuraĝigas ensaluti, tamen ne estas devige.",
        "tooltip-pt-logout": "Elsaluti",
        "tooltip-pt-createaccount": "Ni rekomendas al vi kreon de uzantokonto kaj ensaluto; tamen, tio ne estas deviga",
        "tooltip-ca-talk": "Diskuto pri la artikolo",
-       "tooltip-ca-edit": "Redakti tiun ĉi paĝon",
+       "tooltip-ca-edit": "Redakti ĉi tiun paĝon",
        "tooltip-ca-addsection": "Starti novan sekcion",
        "tooltip-ca-viewsource": "Tiu paĝo estas protektita. Vi povas nur rigardi ties fonton.",
-       "tooltip-ca-history": "Antaŭaj versioj de tiu ĉi paĝo.",
+       "tooltip-ca-history": "Antaŭaj versioj de ĉi tiu paĝo.",
        "tooltip-ca-protect": "Protekti tiun ĉi paĝon",
        "tooltip-ca-unprotect": "Ŝanĝi protektadon de ĉi tiu paĝo",
        "tooltip-ca-delete": "Forigi tiun ĉi paĝon",
        "tooltip-ca-undelete": "Restarigu la redaktojn faritajn al tiu ĉi paĝo antaŭ ties forigo",
-       "tooltip-ca-move": "Alinomigi tiun ĉi paĝon",
-       "tooltip-ca-watch": "Aldoni tiun ĉi paĝon al via atentaro",
+       "tooltip-ca-move": "Alinomigi ĉi tiun paĝon",
+       "tooltip-ca-watch": "Aldoni ĉi tiun paĝon al via atentaro",
        "tooltip-ca-unwatch": "Forigi tiun ĉi paĝon el via atentaro",
        "tooltip-search": "Serĉi tra {{SITENAME}}",
        "tooltip-search-go": "Iru al paĝo kun ĉi preciza nomo se ĝi ekzistas",
        "tooltip-n-randompage": "Iri al hazarda paĝo",
        "tooltip-n-help": "La loko por eltrovi",
        "tooltip-t-whatlinkshere": "Listo de ĉiuj vikiaj paĝoj kiuj ligas ĉi tien",
-       "tooltip-t-recentchangeslinked": "Lastaj ŝanĝoj en paĝoj kiuj ligas al tiu ĉi paĝo",
+       "tooltip-t-recentchangeslinked": "Lastaj ŝanĝoj en paĝoj kiuj ligas al ĉi tiu paĝo",
        "tooltip-feed-rss": "RSS-fonto por tiu ĉi paĝo",
        "tooltip-feed-atom": "Atom-fonto por ĉi tiu paĝo",
        "tooltip-t-contributions": "Listo de kontribuoj de {{GENDER:$1|ĉi tiu uzanto}}",
-       "tooltip-t-emailuser": "Sendi retmesaĝon al {{GENDER:$1|tiu ĉi uzantiĉo|tiu ĉi uzantino|tiu ĉi uzanto}}",
+       "tooltip-t-emailuser": "Sendi retmesaĝon al {{GENDER:$1|ĉi tiu uzanto}}",
        "tooltip-t-info": "Pli da informo pri ĉi tiu paĝo",
        "tooltip-t-upload": "Alŝuti dosierojn",
        "tooltip-t-specialpages": "Listo de ĉiuj specialaj paĝoj",
        "tooltip-ca-nstab-project": "Rigardi la paĝon de la projekto",
        "tooltip-ca-nstab-image": "Rigardi la dosierpaĝon",
        "tooltip-ca-nstab-mediawiki": "Rigardi la sisteman mesaĝon",
-       "tooltip-ca-nstab-template": "Rigardi la ŝablonon",
+       "tooltip-ca-nstab-template": "Vidi la ŝablonon",
        "tooltip-ca-nstab-help": "Rigardi la helppaĝon",
        "tooltip-ca-nstab-category": "Vidi la paĝon de la kategorio",
        "tooltip-minoredit": "Marki tiun ŝanĝon kiel etan",
        "tooltip-save": "Konservi viajn ŝanĝojn",
        "tooltip-publish": "Publikigi viajn ŝanĝojn",
-       "tooltip-preview": "Antaŭrigardi viajn ŝanĝojn. Bonvolu uzi tion antaŭ ol konservi ilin!",
+       "tooltip-preview": "Antaŭrigardi viajn ŝanĝojn. Bonvolu uzi tion antaŭ ol konservi.",
        "tooltip-diff": "Montri la ŝanĝojn kiujn vi faris de la teksto.",
        "tooltip-compareselectedversions": "Rigardi la malsamojn inter ambaŭ selektitaj versioj de ĉi tiu paĝo.",
        "tooltip-watch": "Aldoni ĉi paĝon al via atentaro",
        "lastmodifiedatby": "Ĉi paĝo estis laste ŝanĝita je $2, $1 de $3.",
        "othercontribs": "Bazita sur la laboro de $1.",
        "others": "aliaj",
-       "siteusers": "{{GENDER:$2|uzantiĉo|uzantino|uzanto}}{{PLURAL:$2||j}} $1 de {{SITENAME}}",
+       "siteusers": "{{PLURAL:$2|{{GENDER:$1|uzanto}}|uzantoj}} $1 de {{SITENAME}}",
        "anonusers": "{{SITENAME}}-{{PLURAL:$2|anonimulo|anonimuloj}} $1",
        "creditspage": "Atribuoj de paĝo",
        "nocredits": "Ne estas informo pri atribuoj por ĉi paĝo.",
        "revdelete-uname-unhid": "salutnomo malkaŝita",
        "revdelete-restricted": "aplikis limojn al administrantoj",
        "revdelete-unrestricted": "forigis limojn por administrantoj",
-       "logentry-block-block": "$1 {{GENDER:$2|forbaris}} la {{GENDER:$4|uzanton|uzantinon}} $3 por daŭro de $5 $6",
-       "logentry-block-unblock": "$1 {{GENDER:$2|malforbaris}} la {{GENDER:$4|uzanton|uzantinon}} $3",
-       "logentry-block-reblock": "$1 {{GENDER:$2|ŝanĝis}} agordojn de forbaro por la {{GENDER:$4|uzanto|uzantino}} $3 por daŭro de $5 $6",
-       "logentry-suppress-block": "$1 {{GENDER:$2|forbaris}} la {{GENDER:$4|uzanton|uzantinon}} $3 por daŭro de $5 $6",
-       "logentry-suppress-reblock": "$1 {{GENDER:$2|ŝanĝis}} agordojn de forbaro por la {{GENDER:$4|uzanto|uzantino}} $3 por daŭro de $5 $6",
+       "logentry-block-block": "$1 {{GENDER:$2|forbaris}} la {{GENDER:$4|uzanton $3}} por daŭro de $5 $6",
+       "logentry-block-unblock": "$1 {{GENDER:$2|malforbaris}} la {{GENDER:$4|uzanton $3}}",
+       "logentry-block-reblock": "$1 {{GENDER:$2|ŝanĝis}} agordojn de forbaro por la {{GENDER:$4|uzanto $3}} por daŭro de $5 $6",
+       "logentry-suppress-block": "$1 {{GENDER:$2|forbaris}} la {{GENDER:$4|uzanton $3}} por daŭro de $5 $6",
+       "logentry-suppress-reblock": "$1 {{GENDER:$2|ŝanĝis}} agordojn de forbaro por la {{GENDER:$4|uzanto $3}} por daŭro de $5 $6",
        "logentry-import-upload": "$1 {{GENDER:$2|importis}} $3 per dosiera alŝuto",
        "logentry-import-upload-details": "$1 {{GENDER:$2|importis}} $3 kiel dosiera alŝuto ($4 {{PLURAL:$4|revizio|revizioj}})",
        "logentry-import-interwiki": "$1 {{GENDER:$2|importis}} $3 de alia vikio",
        "mw-widgets-dateinput-placeholder-month": "JJJJ-MM",
        "mw-widgets-titleinput-description-new-page": "paĝo ankoraŭ ne ekzistas",
        "mw-widgets-titleinput-description-redirect": "alidirekti al $1",
-       "api-error-blacklisted": "Bonvolu elekti alian, priskriban titolon.",
        "sessionmanager-tie": "Kombini diversajn tipojn de ensaluta peto ne estas permisita: $1.",
        "sessionprovider-generic": "$1 seancoj",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "kuketaj seancoj",
        "log-action-filter-newusers": "Tipo de konta kreado:",
        "log-action-filter-patrol": "Tipo de patrolado:",
        "log-action-filter-protect": "Tipo de protektad:",
-       "log-action-filter-rights": "Tipo de rajta ŝanĝo",
-       "log-action-filter-suppress": "Tipo de forigado",
+       "log-action-filter-rights": "Tipo de rajta ŝanĝo:",
+       "log-action-filter-suppress": "Tipo de subpremado:",
        "log-action-filter-upload": "Tipo de alŝutado:",
        "log-action-filter-all": "Ĉia",
        "log-action-filter-block-block": "Forbari",
        "authmanager-provider-password-domain": "Aŭtentikigo per pasvorto kaj domajno",
        "authmanager-provider-temporarypassword": "Provizora pasvorto:",
        "authprovider-confirmlink-message": "Laŭ viaj lastatempaj provoj de ensalutado, la sekvantaj kontoj povas esti ligita al via vikia konto. Ligi ilin ebligas ensalutadon per tiuj kontoj. Bonvolu elekti tiun, kiun ligendus.",
-       "authprovider-confirmlink-request-label": "Kontoj kiuj devus esti ligita",
+       "authprovider-confirmlink-request-label": "Kontoj, kiuj devas esti ligita",
        "authprovider-confirmlink-success-line": "$1: Ligita sukcese.",
        "authprovider-confirmlink-failed": "Ligado de konto ne plene sukcesis: $1",
        "authprovider-confirmlink-ok-help": "Kontinui post montrado de mesaĝoj pri malsukceso de ligado.",
        "linkaccounts-submit": "Ligi kontojn",
        "unlinkaccounts": "Malligi kontojn",
        "unlinkaccounts-success": "La konto estis malligita.",
-       "authenticationdatachange-ignored": "La ŝanĝo de dateno pri aŭtentikigado ne estis traktita. Eble neniu provizanto estis agorda?"
+       "authenticationdatachange-ignored": "La ŝanĝo de dateno pri aŭtentikigado ne estis traktita. Eble neniu provizanto estis agorda?",
+       "userjsispublic": "Bonvolu noti: subpaĝoj en JavaScript ne enhavu konfidenciajn datumojn ĉar ili estas videblaj por aliaj uzantoj.",
+       "usercssispublic": "Bonvolu noti: subpaĝoj en CSS ne enhavu konfidenciajn datumojn ĉar ili estas videblaj por aliaj uzantoj."
 }
index d3b9527..dfab8c9 100644 (file)
                        "Codynguyen1116",
                        "2axterix2",
                        "Matma Rex",
-                       "Dgstranz"
+                       "Dgstranz",
+                       "Copper12",
+                       "Ivanhercaz",
+                       "AlvaroMolina",
+                       "Tokvo"
                ]
        },
        "tog-underline": "Subrayar los enlaces:",
        "category-file-count-limited": "{{PLURAL:$1|El siguiente archivo pertenece|Los siguientes $1 archivos pertenecen}} a esta categoría.",
        "listingcontinuesabbrev": "cont.",
        "index-category": "Páginas indizadas",
-       "noindex-category": "Páginas no indizadas",
+       "noindex-category": "Páginas no indexadas",
        "broken-file-category": "Páginas con enlaces rotos a archivos",
        "about": "Acerca de",
        "article": "Página de contenido",
        "tagline": "De {{SITENAME}}",
        "help": "Ayuda",
        "search": "Buscar",
+       "search-ignored-headings": " #<!-- dejar esta línea exactamente como está --> <pre>\n# Títulos que serán ignorados por la búsqueda.\n# Los cambios estarán en vigor tan pronto como la página con el título esté indexada.\n# Puedes forzar la reindexación de una página haciendo una edición nula.\n# La sintaxis es la siguiente:\n#   * Todo lo que sigue a un carácter \"#\" hasta el final de la línea, es un comentario.\n#   * Todas las líneas que no están en blanco son los títulos exactos que se ignorarán (diferenciando mayúsculas de minúsculas).\nReferencias\nEnlaces externos\nVéase también\n #</pre> <!-- dejar esta línea exactamente como está -->",
        "searchbutton": "Buscar",
        "go": "Ir",
        "searcharticle": "Ir",
        "unprotectthispage": "Cambiar la protección de esta página",
        "newpage": "Página nueva",
        "talkpage": "Discutir esta página",
-       "talkpagelinktext": "Discusión",
+       "talkpagelinktext": "discusión",
        "specialpage": "Página especial",
        "personaltools": "Herramientas personales",
        "articlepage": "Ver artículo",
        "views": "Vistas",
        "toolbox": "Herramientas",
        "userpage": "Ver página de usuario",
-       "projectpage": "Ver página de proyecto",
+       "projectpage": "Ver página del proyecto",
        "imagepage": "Ver página del archivo",
-       "mediawikipage": "Ver página de mensaje",
+       "mediawikipage": "Ver página del mensaje",
        "templatepage": "Ver página de plantilla",
        "viewhelppage": "Ver página de ayuda",
        "categorypage": "Ver página de categoría",
        "yourpasswordagain": "Confirma la contraseña:",
        "createacct-yourpasswordagain": "Confirma la contraseña",
        "createacct-yourpasswordagain-ph": "Repite la contraseña",
-       "remembermypassword": "Mantenerme conectado en este navegador (hasta $1 {{PLURAL:$1|día|días}})",
        "userlogin-remembermypassword": "Mantener mi sesión iniciada",
        "userlogin-signwithsecure": "Usar conexión segura",
        "cannotloginnow-title": "No se puede iniciar sesión ahora",
        "passwordreset-emailelement": "Nombre de {{GENDER:$1|usuario|usuaria}}: \n$1\n\nContraseña temporal: \n$2",
        "passwordreset-emailsentemail": "Si esta dirección de correo electrónico está asociada a tu cuenta, entonces se enviará un correo electrónico para restablecer la contraseña.",
        "passwordreset-emailsentusername": "Si existe una dirección de correo electrónico asociada a este nombre de usuario, entonces se enviará un correo para restablecer la contraseña.",
-       "passwordreset-emailsent-capture": "Se ha enviado un correo para el restablecimiento de la contraseña, el cual se muestra a continuación.",
-       "passwordreset-emailerror-capture": "Se ha generado un correo electrónico de restablecimiento de contraseña, que se muestra a continuación, pero ha fallado el envío {{GENDER:$2|al usuario|a la usuaria}}: $1",
        "passwordreset-emailsent-capture2": "{{PLURAL:$1|El e-mail de restablecimiento de contraseña ha sido enviado|Los e-mails de restablecimiento de contraseña han sido enviados}}. {{PLURAL:$1|El nombre de usuario y la contraseña se muestra a continuación|La lista de nombres de usuarios y contraseñas se muestra a continuación}}.",
        "passwordreset-emailerror-capture2": "No fue posible mandar un correo electrónico {{Gender:$2|al usuario|a la usuaria}}: $1 {{PLURAL:$3|El nombre de usuario y la contraseña|La lista de nombres de usuarios y contraseñas}} se muestra a continuación.",
+       "passwordreset-nocaller": "Debe de proporcionarse un interlocutor",
+       "passwordreset-nosuchcaller": "La persona que llama no existe: $1",
+       "passwordreset-ignored": "No se logró el reestablecimiento de la contraseña. ¿Tal vez no se configuró un proveedor?",
        "passwordreset-invalideamil": "Dirección de correo electrónico no válida.",
        "passwordreset-nodata": "No se ha proporcionado ni un nombre de usuario ni una dirección de correo electrónico",
        "changeemail": "Cambiar o eliminar la dirección de correo electrónico",
        "changeemail-header": "Completa este formulario para cambiar tu dirección de correo electrónico. Si quieres eliminar la asociación de cualquier dirección de correo electrónico con tu cuenta, deja en blanco la nueva dirección de correo electrónico cuando envíes el formulario.",
-       "changeemail-passwordrequired": "Tendrás que escribir tu contraseña para confirmar este cambio.",
        "changeemail-no-info": "Debes iniciar sesión para acceder directamente a esta página.",
        "changeemail-oldemail": "Dirección de correo electrónico actual:",
        "changeemail-newemail": "Dirección de correo electrónico nueva:",
        "permissionserrors": "Error de permisos",
        "permissionserrorstext": "No tienes permiso para hacer eso, por {{PLURAL:$1|el siguiente motivo|los siguientes motivos}}:",
        "permissionserrorstext-withaction": "No tienes permiso para $2, por {{PLURAL:$1|el siguiente motivo|los siguientes motivos}}:",
-       "contentmodelediterror": "No puedes editar esta revisión porque su modelo de contenido es <code>$1</code>, la cual difiere del modelo actual de la página <code>$2</code>.",
+       "contentmodelediterror": "No puedes editar esta revisión porque su modelo de contenido es <code>$1</code>, que difiere del modelo actual de contenido de la página <code>$2</code>.",
        "recreate-moveddeleted-warn": "<strong>Atención: estás volviendo a crear una página que ha sido borrada anteriormente.</strong>\n\nPiensa si es adecuado continuar editando la página.\nA continuación, se proporciona el registro de borrado y traslados de esta página para más información:",
        "moveddeleted-notice": "Esta página ha sido borrada.\nA continuación, se proporciona el registro de borrados y traslados de la página para más información.",
-       "moveddeleted-notice-recent": "Esta página se ha eliminado recientemente (dentro de las últimas 24 horas).\nEl registro de eliminación y traslado de la página se muestran a continuación como referencia.",
+       "moveddeleted-notice-recent": "Esta página se ha eliminado recientemente (durante las últimas 24 horas).\nEl registro de eliminación y traslado de la página se muestran a continuación como referencia.",
        "log-fulllog": "Ver el registro completo",
        "edit-hook-aborted": "Una extensión ha evitado la edición.\nNo hay explicación disponible.",
        "edit-gone-missing": "No se ha podido actualizar la página.\nParece haber sido borrada.",
        "content-model-css": "CSS",
        "content-json-empty-object": "Objeto vacío",
        "content-json-empty-array": "Matriz vacía",
+       "deprecated-self-close-category": "Páginas que utilizan etiquetas HTML autocerradas no válidas",
+       "deprecated-self-close-category-desc": "Esta página contiene etiquetas HTML de autocierre inválidas, tales como <code>&lt;b/></code> o <code>&lt;span/></code>. El comportamiento de estas cambiará pronto para ser coherente con la especificación de HTML5, por lo que su utilización en el wikitexto está en desuso.",
        "duplicate-args-warning": "<strong>Aviso:</strong> [[:$1]] llama a [[:$2]] con más de un valor para el parámetro «$3». Se usará solo el último valor proporcionado.",
        "duplicate-args-category": "Páginas que usan argumentos duplicados en invocaciones de plantillas",
        "duplicate-args-category-desc": "La página contiene invocaciones de plantillas que utilizan argumentos duplicados, como <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> o <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "<strong>Advertencia:</strong> esta página contiene demasiadas llamadas a funciones sintácticas costosas.\n\nTiene {{PLURAL:$1|una llamada|$1 llamadas}}, pero debería tener menos de {{PLURAL:$2|una|$2}}.",
-       "expensive-parserfunction-category": "Páginas con llamadas a funciones sintácticas demasiado costosas",
+       "expensive-parserfunction-category": "Páginas con demasiadas llamadas a funciones sintácticas costosas",
        "post-expand-template-inclusion-warning": "<strong>Aviso:</strong> El tamaño de las plantillas incluidas es muy grande.\nAlgunas de ellas no se incluirán.",
        "post-expand-template-inclusion-category": "Páginas con sobrecarga de plantillas",
        "post-expand-template-argument-warning": "Aviso: Esta página contiene al menos un parámetro de plantilla con un tamaño de expansión demasiado grande.\nSe han descartado esos parámetros.",
        "undo-nochange": "Parece que ya se había deshecho la edición.",
        "undo-summary": "Se ha deshecho la revisión $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|disc.]])",
        "undo-summary-username-hidden": "Se ha deshecho la revisión $1 de un usuario oculto",
-       "cantcreateaccounttitle": "No se puede crear la cuenta",
        "cantcreateaccount-text": "[[User:$3|$3]] ha bloqueado la creación de cuentas desde esta dirección IP (<strong>$1</strong>).\n\nEl motivo dado por $3 es <em>$2</em>",
        "cantcreateaccount-range-text": "[[User:$3|$3]] ha bloqueado la creación de cuentas de usuario desde direcciones IP en el rango <strong>$1</strong>, en el que se encuentra tu dirección IP (<strong>$4</strong>).\n\nEl motivo dado por $3 es <em>$2</em>",
        "viewpagelogs": "Ver los registros de esta página",
        "grant-group-high-volume": "Realizar actividad de volumen alto",
        "grant-group-customization": "Personalización y preferencias",
        "grant-group-administration": "Realizar acciones administrativas",
+       "grant-group-private-information": "Acceder a información privada sobre ti",
        "grant-group-other": "Actividades diversas",
        "grant-blockusers": "Bloquear y desbloquear usuarios",
        "grant-createaccount": "Crear cuentas",
        "grant-highvolume": "Gran cantidad de ediciones",
        "grant-oversight": "Ocultar a los usuarios y suprimir las revisiones",
        "grant-patrol": "Verificar cambios a páginas",
+       "grant-privateinfo": "Acceder a información privada",
        "grant-protect": "Proteger y desproteger páginas",
        "grant-rollback": "Revertir cambios a páginas",
        "grant-sendemail": "Enviar un correo electrónico a otros usuarios",
        "action-import": "importar páginas desde otro wiki",
        "action-importupload": "importar páginas mediante la carga de un archivo",
        "action-patrol": "marcar ediciones de otros como verificadas",
-       "action-autopatrol": "tener tus ediciones marcadas como verificadas",
+       "action-autopatrol": "marcar como verificadas tus propias ediciones",
        "action-unwatchedpages": "ver la lista de páginas no vigiladas",
        "action-mergehistory": "fusionar el historial de esta página",
        "action-userrights": "modificar todos los permisos de usuario",
        "action-applychangetags": "aplicar etiquetas junto con los cambios",
        "action-changetags": "agregar y quitar etiquetas arbitrarias a revisiones individuales y entradas del registro",
        "action-deletechangetags": "eliminar etiquetas de la base de datos",
+       "action-purge": "purgar esta página",
        "nchanges": "$1 {{PLURAL:$1|cambio|cambios}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|desde la última visita}}",
        "enhancedrc-history": "historial",
        "upload-http-error": "Ha ocurrido un error HTTP: $1",
        "upload-copy-upload-invalid-domain": "No se pueden realizar subidas remotas desde este dominio.",
        "upload-foreign-cant-upload": "Este wiki no está configurado para subir archivos al repositorio de archivos externo solicitado.",
+       "upload-foreign-cant-load-config": "Falló la carga de la configuración para subir archivos al depósito de archivos externo.",
        "upload-dialog-disabled": "En este wiki están desactivadas las subidas de archivos mediante este cuadro de diálogo.",
        "upload-dialog-title": "Subir archivo",
        "upload-dialog-button-cancel": "Cancelar",
        "filerevert-submit": "Revertir",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> ha sido revertido a la [$4 versión del $2 a las $3].",
        "filerevert-badversion": "No existe versión local previa de este archivo con esa marca de tiempo.",
+       "filerevert-identical": "La versión actual del archivo ya es idéntica a la seleccionada.",
        "filedelete": "Borrar $1",
        "filedelete-legend": "Borrar archivo",
        "filedelete-intro": "Estás por borrar el archivo <strong>[[Media:$1|$1]]</strong> así como todo su historial.",
        "linksearch-ok": "Buscar",
        "linksearch-text": "Se pueden usar caracteres comodín como \"*.wikipedia.org\".\nEs necesario, por lo menos, un dominio de alto nivel, por ejemplo \"*.org\".<br />\n{{PLURAL:$2|Protocolo soportado|Protocolos soportados}}: $1 (si no se especifica ninguno, el predeterminado es http://).",
        "linksearch-line": "$1 enlazado desde $2",
-       "linksearch-error": "Los comodines sólo pueden aparecer al principio del nombre de sitio.",
+       "linksearch-error": "Los comodines solo pueden aparecer al principio del nombre de sitio.",
        "listusersfrom": "Mostrar usuarios que empiecen por:",
        "listusers-submit": "Mostrar",
        "listusers-noresult": "No se encontró al usuario.",
        "noindex-category-desc": "La página contiene la palabra mágica <code><nowiki>__NOINDEX__</nowiki></code> (y está en un espacio de nombres donde la función está activada); y por ello los robots no la indizan.",
        "index-category-desc": "La página contiene la palabra mágica <code><nowiki>__INDEX__</nowiki></code> (y está en un espacio de nombres donde la función está activada); y por ello los robots la indizarán.",
        "post-expand-template-inclusion-category-desc": "Después de expandir todas las plantillas, el tamaño de la página es más grande que <code>$wgMaxArticleSize</code>, así que algunas plantillas no se expandieron.",
-       "post-expand-template-argument-category-desc": "Después de expandir un argumento de plantilla (algunos en tres llaves, como <code>{{{Foo}}})</code>, la página es más grande que <code>$wgMaxArticleSize</code>.",
+       "post-expand-template-argument-category-desc": "Después de expandir un argumento de plantilla (cualquier cosa encerrada en tres llaves, como <code>{{{Foo}}}</code>), la página es más grande que <code>$wgMaxArticleSize</code>.",
        "expensive-parserfunction-category-desc": "La página usa demasiadas funciones sintácticas costosas (como <code>#ifexist</code>). Consulta [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgExpensiveParserFunctionLimit Manual:$wgExpensiveParserFunctionLimit].",
        "broken-file-category-desc": "La página contiene un enlace roto a archivos (un enlace para incrustar un archivo cuando el archivo no existe).",
        "hidden-category-category-desc": "La categoría contiene <code><nowiki>__HIDDENCAT__</nowiki></code> en su página de contenido, lo que evita que aparezca en el cuadro de enlaces de categorías en las páginas, de forma predeterminada.",
        "watchnologin": "No has iniciado sesión",
        "addwatch": "Añadir a la lista de seguimiento",
        "addedwatchtext": "Se han añadido «[[:$1]]» y su página de discusión a tu [[Special:Watchlist|lista de seguimiento]].",
+       "addedwatchtext-talk": "Se han añadido «[[:$1]]» y su página asociada a tu [[Special:Watchlist|lista de seguimiento]].",
        "addedwatchtext-short": "La página \"$1\" ha sido añadida a tu lista de seguimiento.",
        "removewatch": "Quitar de la lista de seguimiento",
        "removedwatchtext": "Se han eliminado «[[:$1]]» y su página de discusión de tu [[Special:Watchlist|lista de seguimiento]].",
+       "removedwatchtext-talk": "Se han eliminado «[[:$1]]» y su página asociada de tu [[Special:Watchlist|lista de seguimiento]].",
        "removedwatchtext-short": "La página \"$1\" ha sido eliminada de tu lista de seguimiento.",
        "watch": "Vigilar",
        "watchthispage": "Vigilar esta página",
        "confirm": "Confirmar",
        "excontent": "el contenido era: «$1»",
        "excontentauthor": "el contenido era: «$1», y el único autor fue «[[Special:Contributions/$2|$2]]» ([[User talk:$2|discusión]])",
-       "exbeforeblank": "El contenido antes de blanquear era: «$1»",
+       "exbeforeblank": "el contenido antes de blanquear era: «$1»",
        "delete-confirm": "Borrar «$1»",
        "delete-legend": "Borrar",
        "historywarning": "<strong>Atención:</strong> la página que estás a punto de borrar tiene un historial con $1 {{PLURAL:$1|revisión|revisiones}}:",
        "rollbacklinkcount": "revertir $1 {{PLURAL:$1|edición|ediciones}}",
        "rollbacklinkcount-morethan": "revertir más de $1 {{PLURAL:$1|edición|ediciones}}",
        "rollbackfailed": "No se pudo revertir",
+       "rollback-missingparam": "Faltan parámetros requeridos en la solicitud.",
+       "rollback-missingrevision": "No se pueden cargar datos de la revisión.",
        "cantrollback": "No se puede revertir la edición;\nel último colaborador es el único autor de esta página.",
        "alreadyrolled": "No se puede revertir la última edición de [[:$1]] hecha por [[User:$2|$2]] ([[User talk:$2|discusión]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nalguien más ya ha editado o revertido esa página.\n\nLa última edición fue hecha por [[User:$3|$3]] ([[User talk:$3|discusión]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "El resumen de la edición fue: <em>$1</em>.",
        "sp-contributions-newbies-sub": "Para cuentas nuevas",
        "sp-contributions-newbies-title": "Contribuciones de usuarios nuevos",
        "sp-contributions-blocklog": "registro de bloqueos",
-       "sp-contributions-suppresslog": "contribuciones de usuario suprimidas",
-       "sp-contributions-deleted": "contribuciones de usuario borradas",
+       "sp-contributions-suppresslog": "contribuciones {{GENDER:$1|del usuario|de la usuaria}} suprimidas",
+       "sp-contributions-deleted": "contribuciones {{GENDER:$1|del usuario|de la usuaria}} borradas",
        "sp-contributions-uploads": "subidas",
        "sp-contributions-logs": "registros",
        "sp-contributions-talk": "discusión",
        "blocklist-timestamp": "Fecha y hora",
        "blocklist-target": "Destino",
        "blocklist-expiry": "Caduca",
-       "blocklist-by": "Administrador bloqueante",
+       "blocklist-by": "Administrador que realizó el bloqueo",
        "blocklist-params": "Parámetros de bloqueo",
        "blocklist-reason": "Motivo",
        "ipblocklist-submit": "Buscar",
        "logentry-delete-revision": "$1 {{GENDER:$2|modificó}} la visibilidad de {{PLURAL:$5|una revisión |$5 revisiones}} en la página  $3: $4",
        "logentry-delete-event-legacy": "$1 ha {{GENDER:$2|cambiado}} la visibilidad de eventos del registro en $3",
        "logentry-delete-revision-legacy": "$1 ha {{GENDER:$2|cambiado}} la visibilidad de las revisiones en la página $3",
-       "logentry-suppress-delete": "$1 {{GENDER:$2|borró}} la página $3",
+       "logentry-suppress-delete": "$1 {{GENDER:$2|suprimió}} la página $3",
        "logentry-suppress-event": "$1 {{GENDER:$2|modificó}} secretamente la visibilidad de {{PLURAL:$5|un evento|$5 eventos}} del registro en $3: $4",
        "logentry-suppress-revision": "$1 {{GENDER:$2|modificó}} secretamente la visibilidad de {{PLURAL:$5|una edición|$5 ediciones}} en la página $3: $4",
        "logentry-suppress-event-legacy": "$1 {{GENDER:$2|modificó}} secretamente la visibilidad de los eventos del registro en $3",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
        "mw-widgets-titleinput-description-new-page": "la página aún no existe",
        "mw-widgets-titleinput-description-redirect": "redirigir a $1",
-       "api-error-blacklisted": "Elige un título diferente, más descriptivo.",
        "sessionmanager-tie": "No se pueden combinar múltiples tipos de autenticación de solicitudes: $1.",
        "sessionprovider-generic": "sesiones $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sesiones basadas en cookies",
        "log-action-filter-newusers": "Tipo de creación de la cuenta:",
        "log-action-filter-patrol": "Tipo de verificación:",
        "log-action-filter-protect": "Tipo de protección:",
-       "log-action-filter-rights": "El tipo de cambio correcto",
-       "log-action-filter-suppress": "Tipo de supresión",
+       "log-action-filter-rights": "Tipo de cambio de permisos:",
+       "log-action-filter-suppress": "Tipo de supresión:",
        "log-action-filter-upload": "Tipo de subida:",
        "log-action-filter-all": "Todas",
        "log-action-filter-block-block": "Bloquear",
        "authmanager-provider-password": "Autenticación basada en contraseña",
        "authmanager-provider-password-domain": "Autenticación basada en contraseña y dominio",
        "authmanager-provider-temporarypassword": "Contraseña temporal",
+       "authprovider-confirmlink-message": "Basado en tus últimos intentos para iniciar sesión, las siguientes cuentas pueden vincularse a tu cuenta wiki. Vincularlas permite iniciar sesión a través de esas cuentas. Selecciona cuáles deben vincularse.",
        "authprovider-confirmlink-request-label": "Cuentas que deberían vincularse",
        "authprovider-confirmlink-success-line": "$1: vinculado exitosamente.",
        "authprovider-confirmlink-failed": "La vinculación de cuentas no se ha realizado con éxito: $1",
        "authprovider-confirmlink-ok-help": "Continuar luego de mostrar los mensajes de error en la vinculación.",
        "authprovider-resetpass-skip-label": "Omitir",
+       "authprovider-resetpass-skip-help": "Saltar el reestablecimiento de la contraseña.",
        "authform-nosession-login": "La autenticación fue exitosa, pero tu navegador no puede \"recordar\" haber iniciado sesión.\n\n$1",
        "authform-nosession-signup": "La cuenta ha sido creada, pero tu navegador no \"recuerda\" haber iniciado sesión.\n\n$1",
+       "authform-newtoken": "Falta token. $1",
+       "authform-notoken": "Falta token",
+       "authform-wrongtoken": "Token incorrecto",
        "specialpage-securitylevel-not-allowed-title": "No está permitido",
        "specialpage-securitylevel-not-allowed": "Lo siento, no tienes permitido usar esta página, porque tu identidad no pudo verificarse.",
        "authpage-cannot-login": "No se puede iniciar la sesión.",
        "linkaccounts-success-text": "La cuenta fue vinculada.",
        "linkaccounts-submit": "Vincular cuentas",
        "unlinkaccounts": "Desvincular cuentas",
-       "unlinkaccounts-success": "Se ha desvinculado la cuenta."
+       "unlinkaccounts-success": "Se ha desvinculado la cuenta.",
+       "authenticationdatachange-ignored": "El cambio den los datos de autentificacion no fue realizado. ¿Tal vez, no se configuró un proveedor?",
+       "userjsispublic": "Recuerda: las subpáginas JavaScript no deberían contener datos confidenciales, pues otros usuarios los pueden ver.",
+       "usercssispublic": "Recuerda: las subpáginas CSS no deberían contener datos confidenciales, pues otros usuarios los pueden ver."
 }
index 97e943e..d544a4e 100644 (file)
@@ -27,7 +27,8 @@
                        "Postituvi",
                        "Purodha",
                        "Macofe",
-                       "Adeliine"
+                       "Adeliine",
+                       "Metsavend"
                ]
        },
        "tog-underline": "Linkide allakriipsutus:",
@@ -45,6 +46,7 @@
        "tog-watchdefault": "Lisa jälgimisloendisse minu muudetud leheküljed ja failid",
        "tog-watchmoves": "Lisa jälgimisloendisse minu teisaldatud leheküljed ja failid",
        "tog-watchdeletion": "Lisa jälgimisloendisse minu kustutatud leheküljed ja failid",
+       "tog-watchuploads": "Lisa jälgimisloendisse uued failid, mille olen üles laadinud",
        "tog-watchrollback": "Lisa jälgimisloendisse leheküljed, kus olen muudatuse tühistanud",
        "tog-minordefault": "Märgi kõik parandused vaikimisi pisiparandusteks",
        "tog-previewontop": "Näita eelvaadet toimetamiskasti ees",
        "october-date": "$1. oktoober",
        "november-date": "$1. november",
        "december-date": "$1. detsember",
+       "period-am": "e. l.",
+       "period-pm": "p. l.",
        "pagecategories": "{{PLURAL:$1|Kategooria|Kategooriad}}",
        "category_header": "Leheküljed kategoorias \"$1\"",
        "subcategories": "Alamkategooriad",
        "yourpasswordagain": "Sisesta parool uuesti:",
        "createacct-yourpasswordagain": "Parooli kinnitus",
        "createacct-yourpasswordagain-ph": "Sisesta uuesti parool",
-       "remembermypassword": "Jäta parool meelde (kuni $1 {{PLURAL:$1|päevaks|päevaks}})",
        "userlogin-remembermypassword": "Jää sisseloginuks",
        "userlogin-signwithsecure": "Kasuta turvalist ühendust",
        "yourdomainname": "Sinu domeen:",
        "nocookieslogin": "{{SITENAME}} kasutab kasutajate tuvastamisel küpsiseid. Sinu brauseris on küpsised keelatud. Palun sea küpsised lubatuks ja proovi siis uuesti.",
        "nocookiesfornew": "Kasutajakonto jäi loomata, kuna me ei saanud selle allikat kindlaks teha.\nVeendu, et sul on küpsised lubatud, taaslaadi see lehekülg ja proovi uuesti.",
        "noname": "Sa ei sisestanud kasutajanime lubataval kujul.",
-       "loginsuccesstitle": "Sisselogimine õnnestus",
+       "loginsuccesstitle": "Sisse logitud",
        "loginsuccess": "Oled sisse loginud. Sinu kasutajanimi on \"$1\".",
        "nosuchuser": "Kasutajat \"$1\" pole.\nKasutajanimed on tõstutundlikud.\nKontrolli kirjapilti või [[Special:CreateAccount|loo uus konto]].",
        "nosuchusershort": "Kasutajat nimega \"$1\" pole.\nKontrolli kirjapilti.",
        "noemail": "Kasutaja $1 e-posti aadressi meil kahjuks pole.",
        "noemailcreate": "Pead sisestama korrektse e-posti aadressi",
        "passwordsent": "Uus parool on saadetud kasutaja $1 registreeritud e-postiaadressil.\nPärast parooli saamist logige palun sisse.",
-       "blocked-mailpassword": "Sinu IP-aadressi jaoks on toimetamine blokeeritud, seetõttu ei saa sa kasutada ka parooli meeldetuletamise funktsiooni.",
+       "blocked-mailpassword": "Sinu IP-aadressi jaoks on toimetamine blokeeritud. Et väärtarvitust ennetada, ei saa sellelt IP-aadressilt kasutada parooli meeldetuletamise funktsiooni.",
        "eauthentsent": "Määratud e-posti aadressile on saadetud kinnituse e-kiri.\nEnne kui su kontole ükskõik milline muu e-kiri saadetakse, pead e-kirjas olevat juhist järgides kinnitama, et konto on tõepoolest sinu.",
        "throttled-mailpassword": "Parooli lähtestamise e-kiri saadetud viimase {{PLURAL:$1|tunni|$1 tunni}} jooksul.\nVäärtarvitamise vältimiseks saadetakse {{PLURAL:$1|tunni|$1 tunni}} jooksul ainult üks lähtestamise e-kiri.",
        "mailerror": "Viga kirja saatmisel: $1",
        "newpassword": "Uus parool:",
        "retypenew": "Sisesta uus parool uuesti:",
        "resetpass_submit": "Sisesta parool ja logi sisse",
-       "changepassword-success": "Sinu parool on edukalt muudetud!\nSind logitakse nüüd sisse...",
+       "changepassword-success": "Sinu parool on muudetud!",
        "changepassword-throttled": "Oled hiljuti proovinud liiga palju kordi sisse logida.\nPalun oota $1, enne kui uuesti proovid.",
        "resetpass_forbidden": "Paroole ei saa muuta",
        "resetpass-no-info": "Pead olema sisselogitud, et sellele lehele pääseda.",
        "resetpass-submit-loggedin": "Muuda parool",
        "resetpass-submit-cancel": "Loobu",
-       "resetpass-wrong-oldpass": "Vigane ajutine või praegune salasõna.\nVõib-olla oled juba edukalt muudnud oma salasõna või taotlenud uut ajutist salasõna.",
+       "resetpass-wrong-oldpass": "Vigane ajutine või praegune salasõna.\nVõib-olla oled juba muutnud oma salasõna või taotlenud uut ajutist salasõna.",
        "resetpass-recycled": "Palun vali uus salasõna, mis erineb praegusest.",
        "resetpass-temp-emailed": "Logisid sisse e-posti teel saadud ajutise koodiga.\nEt sisselogimine lõpule viia, pead määrama siin uue parooli:",
        "resetpass-temp-password": "Ajutine parool:",
        "passwordreset-emailelement": "Kasutajanimi: \n$1\n\nAjutine parool: \n$2",
        "passwordreset-emailsentemail": "Kui oled sidunud konto selle e-posti aadressiga, siis saadetakse sulle parooli lähtestamise e-kiri.",
        "passwordreset-emailsentusername": "Parooli lähtestamise e-kiri saadetakse, kui olemas on kontoga seotud e-posti aadress.",
-       "passwordreset-emailsent-capture": "E-kirjatsi on saadetud allpool näidatav parooli lähtestuskiri.",
-       "passwordreset-emailerror-capture": "Koostati allpool näidatav parooli lähtestuskiri, aga selle e-kirjatsi {{GENDER:$2|kasutajale}} saatmine ebaõnnestus: $1",
        "changeemail": "E-posti aadressi muutmine või eemaldamine",
        "changeemail-header": "Täida see vorm, et muuta oma e-posti aadress. Kui soovid, et konto poleks enam seotud ühegi e-posti aadressiga, siis jäta vormi esitamisel e-posti aadressi väli tühjaks.",
-       "changeemail-passwordrequired": "Et muudatus kinnitada, pead sisestama oma parooli.",
        "changeemail-no-info": "Otselingi kaudu sellele lehele jõudmiseks pead olema sisse loginud.",
        "changeemail-oldemail": "Praegune e-posti aadress:",
        "changeemail-newemail": "Uus e-posti aadress:",
        "minoredit": "See on pisiparandus",
        "watchthis": "Jälgi seda lehekülge",
        "savearticle": "Salvesta",
+       "savechanges": "Salvesta",
+       "publishpage": "Avalda lehekülg",
+       "publishchanges": "Avalda muudatused",
        "preview": "Eelvaade",
        "showpreview": "Näita eelvaadet",
        "showdiff": "Näita muudatusi",
        "userpage-userdoesnotexist": "Kasutajakontot \"<nowiki>$1</nowiki>\" pole olemas.\nPalun mõtle järele, kas soovid seda lehte luua või muuta.",
        "userpage-userdoesnotexist-view": "Kasutajakonto \"$1\" pole registreeritud.",
        "blocked-notice-logextract": "See kasutaja on praegu blokeeritud.\nAllpool on toodud viimane blokeerimislogi sissekanne:",
-       "clearyourcache": "'''Märkus:''' Võimalik, et pärast salvestamist tuleb muudatuste nägemiseks veebilehitseja puhver tühjendada.\n* '''Firefox / Safari:''' Hoia all ''Shift''-klahvi ja klõpsa ''Laadi uuesti'' või vajuta kas ''Ctrl-F5'' või ''Ctrl-R'' (Macis ''⌘-R'').\n* '''Google Chrome:''' Vajuta ''Ctrl-Shift-R'' (Macis ''⌘-Shift-R'').\n* '''Internet Explorer:''' Hoia all ''Ctrl''-klahvi ja klõpsa ''Värskenda'' või vajuta ''Ctrl-F5''.\n* '''Opera:''' Tühjenda puhver asukohas ''Seaded → Eelistused''.",
+       "clearyourcache": "<strong>Märkus:</strong> Võimalik, et pärast salvestamist tuleb muudatuste nägemiseks veebilehitseja puhver tühjendada.\n* <strong>Firefox / Safari:</strong> Hoia all <em>Shift</em>-klahvi ja klõpsa <em>Laadi uuesti</em> või vajuta kas <em>Ctrl-F5</em> või <em>Ctrl-R</em> (Macis <em>⌘-R</em>).\n* <strong>Google Chrome:</strong> Vajuta <em>Ctrl-Shift-R</em> (Macis <em>⌘-Shift-R</em>).\n* <strong>Internet Explorer:</strong> Hoia all <em>Ctrl</em>-klahvi ja klõpsa <em>Värskenda</em> või vajuta <em>Ctrl-F5</em>.\n* <strong>Opera:</strong> Mine asukohta <em>Menüü → Seaded</em> (Macis <em>Opera → Eelistused</em>) ja seejärel <em>Privaatsus ja turvalisus → Tühjenda sirvimisandmed → Puhverdatud pildid ja failid</em>.",
        "usercssyoucanpreview": "'''Vihje:''' Enne salvestamist kasuta oma uue CSSi proovimiseks nuppu \"{{int:showpreview}}\".",
        "userjsyoucanpreview": "'''Vihje:''' Enne salvestamist kasuta oma uue JavaScripti proovimiseks nuppu \"{{int:showpreview}}\".",
        "usercsspreview": "'''Ära unusta, et seda versiooni sinu isiklikust stiililehest pole veel salvestatud!'''",
        "previewnote": "'''Ära unusta, et see on kõigest eelvaade!'''\nSinu muudatused pole veel salvestatud!",
        "continue-editing": "Mine redigeerimiskasti juurde",
        "previewconflict": "See eelvaade näitab, kuidas ülemises toimetuskastis olev tekst hakkab välja nägema, kui otsustate salvestada.",
-       "session_fail_preview": "'''Vabandust! Meil ei õnnestunud seansiandmete kaotuse tõttu sinu muudatust töödelda.'''\nPalun proovi uuesti.\nKui see ikka ei tööta, proovi [[Special:UserLogout|välja]] ja tagasi sisse logida.",
-       "session_fail_preview_html": "'''Vabandust! Meil ei õnnestunud seansiandmete kaotuse tõttu sinu muudatust töödelda.'''\n\n''Kuna {{GRAMMAR:inessive|{{SITENAME}}}} on toor-HTML lubatud, on eelvaade JavaScripti-rünnakute vastase ettevaatusabinõuna peidetud.''\n\n'''Kui see on õigustatud redigeerimiskatse, proovi palun uuesti.'''\nKui see ikka ei tööta, proovi [[Special:UserLogout|välja]] ja tagasi sisse logida.",
+       "session_fail_preview": "Vabandust! Meil ei õnnestunud seansiandmete kaotuse tõttu sinu muudatust töödelda.\n\nVõimalik, et oled välja loginud. <strong>Palun veendu, et oled endiselt sisse logitud ja proovi uuesti</strong>.\nKui see ei toimi, siis proovi [[Special:UserLogout|logida välja]] ja seejärel tagasi sisse ning kontrolli, kas brauser lubab sellest võrgukohast küpsiseid.",
+       "session_fail_preview_html": "Vabandust! Meil ei õnnestunud seansiandmete kaotuse tõttu sinu muudatust töödelda.\n\n<em>Kuna {{GRAMMAR:inessive|{{SITENAME}}}} on toor-HTML lubatud, on eelvaade JavaScripti-rünnakute vastase ettevaatusabinõuna peidetud.</em>\n\n<strong>Kui see on õigustatud redigeerimiskatse, proovi palun uuesti.</strong>\nKui see ikka ei tööta, proovi [[Special:UserLogout|välja]] ja tagasi sisse logida ning kontrolli, kas brauser lubab sellest võrgukohast küpsiseid.",
        "token_suffix_mismatch": "'''Muudatus lükati tagasi, kuna sinu klienttarkvara ei suuda õigesti kirjavahemärke kasutada.'''\nMuudatus lükati tagasi, et vältida lehekülje segiminekut.\nSee juhtub mõnikord siis, kui kasutatakse vigast veebipõhist anonüümsusserverit.",
        "edit_form_incomplete": "'''Redigeerimisvormi mõni osa ei jõudnud serverisse; kontrolli, kas sinu tehtud muudatused on alles, ja proovi uuesti.'''",
        "editing": "Muutmisel on \"$1\"",
        "undo-nochange": "Paistab, et see muudatus on juba eemaldatud.",
        "undo-summary": "Eemaldatud muudatus $1, mille tegi [[Special:Contributions/$2|$2]] ([[User talk:$2|arutelu]])",
        "undo-summary-username-hidden": "Eemaldatud redaktsioon $1, mille tegi peidetud kasutaja",
-       "cantcreateaccounttitle": "Ei saa kontot luua",
        "cantcreateaccount-text": "[[User:$3|$3]] on blokeerinud konto loomise sellelt IP-aadressilt (<strong>$1</strong>).\n\n$3 märkis järgmise põhjuse: <em>$2</em>",
        "cantcreateaccount-range-text": "Kontode loomine IP-aadressidelt vahemikus <strong>$1</strong>, millesse jääb sinu IP-aadress (<strong>$4</strong>), on blokeeritud. Blokeeris kasutaja [[User:$3|$3]].\n\n$3 tõi järgmise põhjuse: <em>$2</em>",
        "viewpagelogs": "Vaata selle lehekülje logisissekandeid",
        "revdelete-unsuppress": "Eemalda taastatud redaktsioonidelt piirangud",
        "revdelete-log": "Põhjus:",
        "revdelete-submit": "Rakenda valitud {{PLURAL:$1|redaktsiooni|redaktsioonide}} suhtes",
-       "revdelete-success": "Redaktsiooni nähtavus edukalt värskendatud.",
+       "revdelete-success": "Redaktsiooni nähtavus värskendatud.",
        "revdelete-failure": "'''Redaktsiooni nähtavust ei saanud värskendada:'''\n$1",
-       "logdelete-success": "'''Logi nähtavus edukalt muudetud.'''",
+       "logdelete-success": "Logi nähtavus muudetud.",
        "logdelete-failure": "'''Logi nähtavust ei saanud paika:'''\n$1",
        "revdel-restore": "muuda nähtavust",
        "pagehist": "lehekülje ajalugu",
        "userrights": "Kasutajaõiguste haldus",
        "userrights-lookup-user": "Kasutajarühma muutmine",
        "userrights-user-editname": "Sisesta kasutajanimi:",
-       "editusergroup": "Muuda kasutajarühmi",
+       "editusergroup": "Muuda {{GENDER:$1|kasutajarühmi}}",
        "editinguser": "Kasutaja '''[[User:$1|$1]]''' $2 õiguste muutmine",
        "userrights-editusergroup": "Kasutajarühmade muutmine",
-       "saveusergroups": "Salvesta kasutajarühmad",
+       "saveusergroups": "Salvesta {{GENDER:$1|kasutajarühmad}}",
        "userrights-groupsmember": "Kuulub rühmadesse:",
        "userrights-groupsmember-auto": "Kuulub vaikimisi rühmadesse:",
        "userrights-groups-help": "Sa võid muuta selle kasutaja kuuluvust eri kasutajarühmadesse.\n* Märgitud kast tähendab, et kasutaja kuulub sellesse rühma.\n* Märkimata kast tähendab, et kasutaja ei kuulu sellesse rühma.\n* Aga * kasutajarühma juures tähistab õigust, mida sa peale lisamist enam eemaldada ei saa, või siis ka vastupidi.",
        "userrights-changeable-col": "Rühmad, mida sa saad muuta",
        "userrights-unchangeable-col": "Rühmad, mida sa ei saa muuta",
        "userrights-conflict": "Kasutajaõiguste muutmise konflikt! Palun vaata oma muudatused üle ja kinnita need.",
-       "userrights-removed-self": "Võtsid edukalt endalt õigused. Seetõttu sellele leheküljele sa enam ligi ei pääse.",
+       "userrights-removed-self": "Võtsid endalt õigused. Seetõttu sellele leheküljele sa enam ligi ei pääse.",
        "group": "Rühm:",
        "group-user": "Kasutajad",
        "group-autoconfirmed": "Automaatselt kinnitatud kasutajad",
        "right-override-export-depth": "Eksportida lehekülgi, kaasates viidatud leheküljed kuni viienda tasemeni",
        "right-sendemail": "Saata teistele kasutajatele e-kirju",
        "right-passwordreset": "Vaadata parooli lähtestamise e-kirju",
-       "right-managechangetags": "Koostada [[Special:Tags|märgiseid]] ja kustutada neid andmebaasist",
+       "right-managechangetags": "Koostada ja (in)aktiveerida [[Special:Tags|märgiseid]]",
        "right-applychangetags": "Rakendada [[Special:Tags|märgiseid]] enda muudatuste suhtes",
        "right-changetags": "Lisada ja eemaldada käsitsi rakendatavaid [[Special:Tags|märgiseid]] üksikute redaktsioonide ja logisissekannete juures",
        "grant-group-page-interaction": "Interaktsioon lehekülgedega",
        "grant-sendemail": "Kasutajatele e-kirjade saatmine",
        "grant-uploadeditmovefile": "Failide üleslaadimine, asendamine ja teisaldamine",
        "grant-uploadfile": "Uute failide üleslaadimine",
+       "grant-basic": "Põhiõigused",
        "grant-viewdeleted": "Kustutatud failide ja lehekülgede vaatamine",
        "grant-viewmywatchlist": "Oma jälgimisloendi vaatamine",
        "newuserlogpage": "Konto loomise logi",
        "rightslogtext": "See on logi kasutajate õiguste muutuste kohta.",
        "action-read": "seda lehekülge lugeda",
        "action-edit": "seda lehekülge muuta",
-       "action-createpage": "lehekülgi luua",
-       "action-createtalk": "arutelulehekülgi luua",
+       "action-createpage": "seda lehekülge luua",
+       "action-createtalk": "seda arutelulehekülge luua",
        "action-createaccount": "seda kasutajakontot luua",
        "action-history": "vaadata selle lehekülje ajalugu",
        "action-minoredit": "seda muudatust pisimuudatuseks märkida",
        "action-viewmyprivateinfo": "oma eraandmeid vaadata",
        "action-editmyprivateinfo": "oma eraandmeid redigeerida",
        "action-editcontentmodel": "lehekülje sisumudelit muuta",
-       "action-managechangetags": "märgiseid koostada ege neid andmebaasist kustutada",
+       "action-managechangetags": "märgiseid koostada ega (in)aktiveerida",
        "action-applychangetags": "rakendada märgiseid oma muudatuste suhtes",
        "action-changetags": "käsitsi rakendatavaid märgiseid üksikute redaktsioonide ega logisissekannete juures lisada ega eemaldada",
        "nchanges": "$1 {{PLURAL:$1|muudatus|muudatust}}",
        "recentchangeslinked-page": "Lehekülje nimi:",
        "recentchangeslinked-to": "Näita hoopis muudatusi lehekülgedel, mis sellele lehele lingivad",
        "recentchanges-page-added-to-category": "kategooriasse lisatud \"[[:$1]]\"",
-       "recentchanges-page-added-to-category-bundled": "kategooriasse lisatud \"[[:$1]]\" ja veel [[Special:WhatLinksHere/$1|{{PLURAL:$2|üks lehekülg|$2 lehekülge}}]]",
+       "recentchanges-page-added-to-category-bundled": "kategooriasse lisatud \"[[:$1]]\", [[Special:WhatLinksHere/$1|seda lehekülge kasutatakse teisel leheküljel mallina]]",
        "recentchanges-page-removed-from-category": "kategooriast eemaldatud \"[[:$1]]\"",
-       "recentchanges-page-removed-from-category-bundled": "kategooriast eemaldatud \"[[:$1]]\" ja veel {{PLURAL:$2|üks lehekülg|$2 lehekülge}}",
+       "recentchanges-page-removed-from-category-bundled": "kategooriast eemaldatud \"[[:$1]]\", [[Special:WhatLinksHere/$1|seda lehekülge kasutatakse teisel leheküljel mallina]]",
        "autochange-username": "MediaWiki automaatne muudatus",
        "upload": "Faili üleslaadimine",
        "uploadbtn": "Laadi fail üles",
        "uploaded-script-svg": "Üleslaaditud SVG-failist leiti skriptitav element \"$1\".",
        "uploaded-hostile-svg": "Üleslaaditud SVG-faili laadielemendist leiti ebaturvaline CSS.",
        "uploaded-event-handler-on-svg": "Sündmuse halduse atribuutide <code>$1=\"$2\"</code> seadmine pole SVG-failis lubatud.",
-       "uploaded-href-unsafe-target-svg": "Üleslaaditud SVG-failist leiti href, mille sihtkoht <code>&lt;$1 $2=\"$3\"&gt;</code> on ebaturvaline.",
+       "uploaded-href-unsafe-target-svg": "Üleslaaditud SVG-failist leiti href, mis viitab ebaturvalistele andmetele: URI sihtkoht <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-animate-svg": "Üleslaaditud SVG-failist leiti silt \"animate\", mis võib href-i muuta, kasutades from-atribuuti <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-setting-event-handler-svg": "Sündmuse halduse atribuutide seadmine on keelatud, üleslaaditud SVG-failist leiti <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-setting-href-svg": "Sildi \"set\" kasutamine, selleks et lisada emaelemendile href-atribuut, on keelatud.",
        "backend-fail-read": "Faili $1 ei saa lugeda.",
        "backend-fail-create": "Faili $1 ei saa kirjutada.",
        "backend-fail-maxsize": "Faili $1 ei saa kirjutada, sest see on {{PLURAL:$2|ühest baidist|$2 baidist}} suurem.",
-       "backend-fail-readonly": "Tagamälu \"$1\" on praegu kirjutuskaitstud. Põhjus: \"<em>$2</em>\"",
+       "backend-fail-readonly": "Tagamälu \"$1\" on praegu kirjutuskaitstud. Põhjus: <em>$2</em>",
        "backend-fail-synced": "Faili \"$1\" olek sisemälus on ühtimatu.",
        "backend-fail-connect": "Ühendus tagamäluga \"$1\" ebaõnnestus.",
        "backend-fail-internal": "Tagamälus \"$1\" esines tundmatu tõrge.",
        "uploadstash-summary": "See lehekülg pakub juurdepääsu failidele, mis on üles laaditud (või mida parasjagu üles laaditakse), kuid mis pole veel vikis avaldatud. Need failid on nähtavad üksnes kasutajale, kes need üles laadis.",
        "uploadstash-clear": "Kustuta failid algsest hoidlast",
        "uploadstash-nofiles": "Sul pole algses hoidlas faile.",
-       "uploadstash-badtoken": "Toiming ebaõnnestus; võib-olla redigeerimisloa aegumise tõttu. Proovi uuesti.",
+       "uploadstash-badtoken": "Toiming ebaõnnestus, võib-olla redigeerimismandaadi aegumise tõttu. Palun proovi uuesti.",
        "uploadstash-errclear": "Failide kustutamine ebaõnnestus.",
        "uploadstash-refresh": "Värskenda faililoendit",
        "invalid-chunk-offset": "Tüki vigane nihe",
        "pageswithprop-prophidden-long": "pika tekstiatribuudi väärtus peidetud ($1)",
        "pageswithprop-prophidden-binary": "kahendatribuudi väärtus peidetud ($1)",
        "doubleredirects": "Kahekordsed ümbersuunamised",
-       "doubleredirectstext": "See lehekülg loetleb leheküljed, mis on ümber suunatud teistele ümbersuunamislehekülgedel.\nIgal real on toodud esimene ja teine ümbersuunamislehekülg ning samuti lehekülg, kuhu teine ümbersuunamislehekülg on suunatud ja kuhu tavaliselt ka esimene ümbersuunamislehekülg tegelikult peaks suunama.\n<del>Läbikriipsutatud</del> kirjed on kohendatud.",
+       "doubleredirectstext": "See lehekülg loetleb leheküljed, mis on ümber suunatud teistele ümbersuunamislehekülgedele.\nIgal real on toodud esimene ja teine ümbersuunamislehekülg ning samuti lehekülg, kuhu teine ümbersuunamislehekülg on suunatud ja kuhu tavaliselt ka esimene ümbersuunamislehekülg tegelikult peaks suunama.\n<del>Läbikriipsutatud</del> kirjed on kohendatud.",
        "double-redirect-fixed-move": "[[$1]] on teisaldatud.\nLehekülg uuendati automaatselt ja see suunab nüüd leheküljele [[$2]].",
        "double-redirect-fixed-maintenance": "Hooldustöö käigus parandati automaatselt kahekordne suunamine leheküljelt [[$1]] leheküljele [[$2]].",
        "double-redirect-fixer": "Ümbersuunamiste parandaja",
        "apihelp-no-such-module": "Moodulit \"$1\" ei leitud.",
        "apisandbox": "API liivakast",
        "apisandbox-api-disabled": "API on selles võrgukohas keelatud.",
-       "apisandbox-intro": "Kasuta seda lehekülge '''MediaWiki API''' katsetamiseks.\nÜksikasjad API kasutamise kohta leiad [https://www.mediawiki.org/wiki/API:Main_page API dokumentatsioonist]. Näide: [https://www.mediawiki.org/wiki/API#A_simple_example esilehe sisu hankimine]. Vali toiming, et näha veel näiteid.\n\nPane tähele, et kuigi siin on liivakast, võivad siin leheküljel tehtud toimingud vikit muuta.",
+       "apisandbox-intro": "Kasuta seda lehekülge <strong>MediaWiki API</strong> katsetamiseks.\nÜksikasjad API kasutamise kohta leiad [[mw:API:Main page|API dokumentatsioonist]]. Näide: [https://www.mediawiki.org/wiki/API#A_simple_example esilehe sisu hankimine]. Vali toiming, et näha veel näiteid.\n\nPane tähele, et kuigi siin on liivakast, võivad siin leheküljel tehtud toimingud vikit muuta.",
        "apisandbox-submit": "Tee päring",
        "apisandbox-reset": "Puhasta",
-       "apisandbox-examples": "Näide",
-       "apisandbox-results": "Tulemus",
+       "apisandbox-examples": "Näited",
+       "apisandbox-results": "Tulemused",
        "apisandbox-request-url-label": "Päringu URL:",
-       "apisandbox-request-time": "Päringuaeg: $1",
+       "apisandbox-request-time": "Päringuaeg: {{PLURAL:$1|$1 ms}}",
        "booksources": "Raamatuotsimine",
        "booksources-search-legend": "Raamatuotsimine",
        "booksources-search": "Otsi",
        "log-title-wildcard": "Selle tekstiga algavad pealkirjad",
        "showhideselectedlogentries": "Muuda valitud logisissekannete nähtavust",
        "log-edit-tags": "Muuda valitud logisissekannete märgiseid",
+       "checkbox-select": "Vali: $1",
+       "checkbox-all": "kõik",
+       "checkbox-none": "mitte ühtegi",
+       "checkbox-invert": "pööra",
        "allpages": "Kõik leheküljed",
        "nextpage": "Järgmine lehekülg ($1)",
        "prevpage": "Eelmine lehekülg ($1)",
        "listgrouprights-namespaceprotection-header": "Nimeruumipiirangud",
        "listgrouprights-namespaceprotection-namespace": "Nimeruum",
        "listgrouprights-namespaceprotection-restrictedto": "Redigeerimiseks vajalikud õigused",
-       "listgrants-summary": "See on OAuthi-volituste ja neile vastavate kasutajaõiguste loend. Kasutaja saab volitada rakenduse tarvituse enda nimel, aga vaid kasutaja valitud volituste piires. Rakenduse abil ei saa kasutaja nimel siiski kasutada õigusi, mida kasutajal pole.\nÜksikute õiguste kohta võib leiduda [[{{MediaWiki:Listgrouprights-helppage}}|lisateavet]].",
-       "listgrants-rights": "Volitus",
+       "listgrants": "Volitused",
+       "listgrants-summary": "See on volituste ja neile vastavate kasutajaõiguste loend. Kasutaja saab volitada rakenduse tarvituse enda nimel, aga vaid kasutaja valitud volituste piires. Rakenduse abil ei saa kasutaja nimel siiski kasutada õigusi, mida kasutajal pole.\nÜksikute õiguste kohta võib leiduda [[{{MediaWiki:Listgrouprights-helppage}}|lisateavet]].",
+       "listgrants-grant": "Volitus",
+       "listgrants-rights": "Õigused",
        "trackingcategories": "Süsteemikategooriad",
        "trackingcategories-summary": "Siin leheküljel on loetletud süsteemikategooriad, millesse MediaWiki tarkvara ise lehekülgi arvab. Nende kategooriate nimesid saab muuta, kui vahetada {{ns:8}}-nimeruumis vastavaid süsteemisõnumeid.",
        "trackingcategories-msg": "Süsteemikategooria",
        "delete-toobig": "See lehekülg on pika redigeerimisajalooga – üle {{PLURAL:$1|ühe muudatuse|$1 muudatuse}}.\nSelle kustutamine on keelatud, et ära hoida ekslikku {{GRAMMAR:genitive|{{SITENAME}}}} töö häirimist.",
        "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": "'''Hoiatus:''' [[Special:WhatLinksHere/{{FULLPAGENAME}}|Teised leheküljed]] viitavad leheküljele, mida oled kustutamas, või see lehekülg on kasutuses mallina.",
+       "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.",
        "rollback": "Tühista muudatused",
        "rollbacklink": "tühista",
        "rollbacklinkcount": "tühista {{PLURAL:$1|üks muudatus|$1 muudatust}}",
        "protect-summary-cascade": "kaskaad",
        "protect-expiring": "aegumistähtaeg $1 (UTC)",
        "protect-expiring-local": "aegumistähtaeg $1",
-       "protect-expiry-indefinite": "määramatu",
+       "protect-expiry-indefinite": "tähtajatu",
        "protect-cascade": "Kaitse lehekülgi, mis on lülitatud käesoleva lehekülje koosseisu (kaskaadkaitse)",
        "protect-cantedit": "Sa ei saa lehekülje kaitsetaset muuta, sest sul puudub lehekülje redigeerimise õigus.",
        "protect-othertime": "Muu aeg:",
        "undeletedrevisions": "$1 {{PLURAL:$1|redaktsioon|redaktsiooni}} taastatud",
        "undeletedrevisions-files": "{{PLURAL:$1|1 redaktsioon|$1 redaktsiooni}} ja {{PLURAL:$2|1 fail|$2 faili}} taastatud",
        "undeletedfiles": "{{PLURAL:$1|1 fail|$1 faili}} taastatud",
-       "cannotundelete": "Taastamine ebaõnnestus:\n$1",
+       "cannotundelete": "Taastamine ebaõnnestus osaliselt või täielikult:\n$1",
        "undeletedpage": "'''$1 on taastatud'''\n\n[[Special:Log/delete|Kustutamise logist]] võib leida loendi viimastest kustutamistest ja taastamistest.",
        "undelete-header": "Hiljuti kustutatud leheküljed leiad [[Special:Log/delete|kustutamislogist]].",
        "undelete-search-title": "Kustutatud lehekülgede otsimine",
        "sp-contributions-newbies-sub": "Uute kontode kaastöö",
        "sp-contributions-newbies-title": "Uute kasutajate kaastöö",
        "sp-contributions-blocklog": "blokeerimised",
-       "sp-contributions-suppresslog": "varjatud kaastöö",
-       "sp-contributions-deleted": "kustutatud kaastöö",
+       "sp-contributions-suppresslog": "{{GENDER:$1|varjatud}} kaastöö",
+       "sp-contributions-deleted": "{{GENDER:$1|kustutatud}} kaastöö",
        "sp-contributions-uploads": "üleslaadimised",
        "sp-contributions-logs": "logid",
        "sp-contributions-talk": "arutelu",
        "unblock": "Kasutaja blokeeringu eemaldamine",
        "blockip": "Blokeeri {{GENDER:$1|kasutaja}}",
        "blockip-legend": "Kasutaja blokeerimine",
-       "blockiptext": "See vorm on kindla IP-aadressi või kasutajanime kirjutamisõiguste blokeerimiseks.\nSeda tohib teha ainult vandalismi vältimiseks ja kooskõlas [[{{MediaWiki:Policy-url}}|{{GRAMMAR:genitive|{{SITENAME}}}} sisekorraga]].\nTäida ka põhjuse väli, näiteks viidates lehekülgedele, mis rikuti.",
+       "blockiptext": "See vorm on kindla IP-aadressi või kasutajanime kirjutamisõiguste blokeerimiseks.\nSeda tohib teha ainult vandalismi vältimiseks ja kooskõlas [[{{MediaWiki:Policy-url}}|{{GRAMMAR:genitive|{{SITENAME}}}} sisekorraga]].\nTäida ka põhjuse väli, näiteks viidates lehekülgedele, mis rikuti.\nIP-aadresside vahemikke saad blokeerida [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]-süntaksi abil; suurim lubatud vahemik on IPv4 jaoks /$1 ja IPv6 jaoks /$2.",
        "ipaddressorusername": "IP-aadress või kasutajanimi:",
        "ipbexpiry": "Kehtivus:",
        "ipbreason": "Põhjus:",
        "lockedbyandtime": "(lukustas $1; $2, kell $3)",
        "move-page": "Lehekülje \"$1\" teisaldamine",
        "move-page-legend": "Lehekülje teisaldamine",
-       "movepagetext": "Allolevat vormi kasutades saad lehekülje ümber nimetada. Lehekülje ajalugu tõstetakse uue pealkirja alla automaatselt.\nPraeguse pealkirjaga leheküljest saab ümbersuunamislehekülg uuele leheküljele.\nSaad senisele pealkirjale viitavad ümbersuunamised automaatselt parandada.\nKui sa seda ei tee, kontrolli, et teisaldamise tõttu ei jää maha [[Special:DoubleRedirects|kahekordseid]] ega [[Special:BrokenRedirects|katkiseid ümbersuunamisi]].\nSinu kohus on hoolitseda selle eest, et kõik jääks toimima, nagu ette nähtud.\n\nPane tähele, et lehekülge '''ei teisaldata''' juhul, kui uue pealkirjaga lehekülg on juba olemas. Erandiks on juhud, kui viimane on redigeerimisajaloota ümbersuunamislehekülg.\nSee tähendab, et kogemata ei saa üle kirjutada juba olemasolevat lehekülge, kuid saab ebaõnnestunud ümbernimetamise tagasi pöörata.\n\n'''Hoiatus!'''\nTegu võib olla väga loetava lehekülje jaoks tõsise ja ootamatu muudatusega;\nenne jätkamist teadvusta palun tagajärgi.",
-       "movepagetext-noredirectfixer": "Allolevat vormi kasutades saad lehekülje ümber nimetada. Lehekülje ajalugu tõstetakse uue pealkirja alla automaatselt.\nPraeguse pealkirjaga leheküljest saab ümbersuunamislehekülg uuele leheküljele.\nKontrolli, et teisaldamise tõttu ei jää maha [[Special:DoubleRedirects|kahekordseid]] ega [[Special:BrokenRedirects|katkiseid ümbersuunamisi]].\nSinu kohus on hoolitseda selle eest, et kõik jääks toimima, nagu ette nähtud.\n\nPane tähele, et lehekülge '''ei teisaldata''' juhul, kui uue pealkirjaga lehekülg on juba olemas. Erandiks on juhud, kui olemasolev lehekülg on tühi või redigeerimisajaloota ümbersuunamislehekülg.\nSee tähendab, et kogemata ei saa üle kirjutada juba olemasolevat lehekülge, kuid saab ebaõnnestunud ümbernimetamise tagasi pöörata.\n\n'''Hoiatus!'''\nTegu võib olla väga loetava lehekülje jaoks tõsise ja ootamatu muudatusega;\nenne jätkamist teadvusta palun tagajärgi.",
+       "movepagetext": "Allolevat vormi kasutades saad lehekülje ümber nimetada. Lehekülje ajalugu tõstetakse uue pealkirja alla automaatselt.\nPraeguse pealkirjaga leheküljest saab ümbersuunamislehekülg uuele leheküljele.\nSaad senisele pealkirjale viitavad ümbersuunamised automaatselt parandada.\nKui sa seda ei tee, kontrolli, et teisaldamise tõttu ei jää maha [[Special:DoubleRedirects|kahekordseid]] ega [[Special:BrokenRedirects|katkiseid ümbersuunamisi]].\nSinu kohus on hoolitseda selle eest, et kõik jääks toimima, nagu ette nähtud.\n\nPane tähele, et lehekülge <strong>ei teisaldata</strong> juhul, kui uue pealkirjaga lehekülg on juba olemas. Erandiks on juhud, kui viimane on redigeerimisajaloota ümbersuunamislehekülg.\nSee tähendab, et kogemata ei saa üle kirjutada juba olemasolevat lehekülge, kuid saab ebaõnnestunud ümbernimetamise tagasi pöörata.\n\n<strong>Märkus:</strong>\nTegu võib olla väga loetava lehekülje jaoks tõsise ja ootamatu muudatusega;\nenne jätkamist teadvusta palun tagajärgi.",
+       "movepagetext-noredirectfixer": "Allolevat vormi kasutades saad lehekülje ümber nimetada. Lehekülje ajalugu tõstetakse uue pealkirja alla automaatselt.\nPraeguse pealkirjaga leheküljest saab ümbersuunamislehekülg uuele leheküljele.\nKontrolli, et teisaldamise tõttu ei jää maha [[Special:DoubleRedirects|kahekordseid]] ega [[Special:BrokenRedirects|katkiseid ümbersuunamisi]].\nSinu kohus on hoolitseda selle eest, et kõik jääks toimima, nagu ette nähtud.\n\nPane tähele, et lehekülge <strong>ei teisaldata</strong> juhul, kui uue pealkirjaga lehekülg on juba olemas. Erandiks on juhud, kui olemasolev lehekülg on tühi või redigeerimisajaloota ümbersuunamislehekülg.\nSee tähendab, et kogemata ei saa üle kirjutada juba olemasolevat lehekülge, kuid saab ebaõnnestunud ümbernimetamise tagasi pöörata.\n\n<strong>Note:</strong>\nTegu võib olla väga loetava lehekülje jaoks tõsise ja ootamatu muudatusega;\nenne jätkamist teadvusta palun tagajärgi.",
        "movepagetalktext": "Kui märgid selle ruudu, teisaldatakse arutelulehekülg automaatselt uue pealkirja alla. Seda välja arvatud juhul, kui uue pealkirja all on juba arutelulehekülg, mis pole tühi.\n\nSel juhul saad lehekülje soovi korral käsitsi teisaldada või liita.",
        "moveuserpage-warning": "'''Hoiatus:''' Oled teisaldamas kasutajalehekülge. Pane tähele, et teisaldatakse ainult lehekülg ja kasutajat '''ei''' nimetata ümber.",
        "movecategorypage-warning": "<strong>Hoiatus:</strong> Oled teisaldamas kategoorialehekülge. Pane palun tähele, et teisaldatakse vaid see lehekülg ja ühtegi vanas kategoorias sisalduvat lehekülge <em>ei</em> kategoriseerita ümber uude kategooriasse.",
        "movenosubpage": "Sellel leheküljel pole alamlehekülgi.",
        "movereason": "Põhjus:",
        "revertmove": "taasta",
-       "delete_and_move_text": "== Vajalik kustutamine ==\nSihtlehekülg \"[[:$1]]\" on juba olemas.\nKas kustutad selle, et luua võimalus teisaldamiseks?",
+       "delete_and_move_text": "Sihtlehekülg \"[[:$1]]\" on juba olemas.\nKas kustutad selle, et luua võimalus teisaldamiseks?",
        "delete_and_move_confirm": "Jah, kustuta lehekülg",
        "delete_and_move_reason": "Kustutatud, et tõsta asemele lehekülg \"[[$1]]\"",
        "selfmove": "Algne nimi ja uus nimi on samad.",
        "move-leave-redirect": "Jäta maha ümbersuunamisleht",
        "protectedpagemovewarning": "'''Hoiatus:''' See lehekülg on nii lukustatud, et ainult administraatori õigustega kasutajad saavad seda teisaldada.\nAllpool on toodud uusim logisissekanne:",
        "semiprotectedpagemovewarning": "'''Pane tähele:''' See lehekülg on lukustatud, nii et ainult registreeritud kasutajad saavad seda teisaldada.\nAllpool on toodud uusim logisissekanne:",
-       "move-over-sharedrepo": "== Fail on olemas ==\n[[:$1]] on olemas jagatud failivaramus. Faili teisaldamisel selle nime alla varjatakse jagatud failivarmus olev samanimeline fail.",
+       "move-over-sharedrepo": "[[:$1]] on olemas jagatud failivaramus. Faili teisaldamisel selle nime alla varjatakse jagatud failivarmus olev samanimeline fail.",
        "file-exists-sharedrepo": "Valitud failinimi on juba kasutusel jagatud failivaramus.\nPalun kasuta mõnda teist nime.",
        "export": "Lehekülgede eksport",
        "exporttext": "Saad eksportida kindla leheküljel või lehekülgede kogumi teksti ja redigeerimisloo XML-kujule viiduna.\nSeda saab teise vikisse importida, kasutades selleks MediaWiki [[Special:Import|impordi lehekülge]].\n\nEt eksportida lehekülgi, sisesta nende pealkirjad allolevasse tekstikasti, iga pealkiri ise reale ja vali, kas soovid viimast redaktsiooni ja kõiki vanemaid redaktsioone ühes redigeerimislooga või viimast redaktsiooni ühes andmetega viimase redigeerimise kohta.\n\nViimasel juhul saab kasutada ka linki, näiteks lehekülje \"[[{{MediaWiki:Mainpage}}]]\" jaoks [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]].",
        "import-nonewrevisions": "Ühtegi redaktsiooni ei imporditud (kõik on juba olemas või tõrgete tõttu vahele jäetud).",
        "xml-error-string": "$1 real $2, tulbas $3 (bait $4): $5",
        "import-upload": "Laadi üles XML-andmed",
-       "import-token-mismatch": "Seansiandmed läksid kaduma.\nPalun ürita uuesti.",
+       "import-token-mismatch": "Seansiandmed läksid kaduma.\n\nVõimalik, et oled välja loginud. <strong>Palun veendu, et oled endiselt sisse logitud ja proovi uuesti</strong>.\nKui see ei toimi, siis proovi [[Special:UserLogout|logida välja]] ja tagasi sisse ning kontrolli, kas brauser lubab sellest võrgukohast küpsiseid.",
        "import-invalid-interwiki": "Määratud vikist ei saa importida.",
        "import-error-edit": "Lehekülge \"$1\" ei imporditud, sest sul pole õigust seda muuta.",
        "import-error-create": "Lehekülge \"$1\" ei imporditud, sest sul pole õigust seda luua.",
        "tooltip-t-recentchangeslinked": "Viimased muudatused lehekülgedel, millele on siit viidatud",
        "tooltip-feed-rss": "Selle lehekülje RSS-voog",
        "tooltip-feed-atom": "Selle lehekülje Atom-voog",
-       "tooltip-t-contributions": "Kuva selle kasutaja kaastöö",
-       "tooltip-t-emailuser": "Saada sellele kasutajale e-kiri",
+       "tooltip-t-contributions": "Kuva {{GENDER:$1|selle kasutaja}} kaastöö",
+       "tooltip-t-emailuser": "Saada {{GENDER:$1|sellele kasutajale}} e-kiri",
        "tooltip-t-info": "Lisateave selle lehekülje kohta",
        "tooltip-t-upload": "Laadi faile üles",
        "tooltip-t-specialpages": "Erilehekülgede loend",
        "tooltip-ca-nstab-category": "Näita kategooria lehte",
        "tooltip-minoredit": "Märgi see pisiparanduseks",
        "tooltip-save": "Salvesta muudatused",
+       "tooltip-publish": "Avalda oma muudatused",
        "tooltip-preview": "Näita tehtavaid muudatusi. Palun kasutage seda enne salvestamist!",
        "tooltip-diff": "Näita tehtavaid muudatusi.",
        "tooltip-compareselectedversions": "Näita erinevusi selle lehe kahe valitud versiooni vahel.",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|lehekülg|lehekülge}}",
        "file-info": "faili suurus: $1, MIME tüüp: $2",
        "file-info-size": "$1 × $2 pikslit, faili suurus: $3, MIME tüüp: $4",
-       "file-info-size-pages": "$1 × $2 pikslit, faili suurus: $3 , MIME tüüp: $4, $5 {{PLURAL:$5|lehekülg|lehekülge}}",
+       "file-info-size-pages": "$1 × $2 pikslit, faili suurus: $3, MIME tüüp: $4, $5 {{PLURAL:$5|lehekülg|lehekülge}}",
        "file-nohires": "Sellest suuremat pilti pole.",
        "svg-long-desc": "SVG-fail, algsuurus $1 × $2 pikslit, faili suurus: $3",
        "svg-long-desc-animated": "Animeeritud SVG-fail,  algsuurus $1 × $2 pikslit, faili suurus: $3",
        "watchlistedit-raw-done": "Sinu jälgimisloend on uuendatud.",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 lehekülg|$1 lehekülge}} lisatud:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 pealkiri|$1 pealkirja}} eemaldati:",
-       "watchlistedit-clear-title": "Tühjendatud jälgimisloend",
+       "watchlistedit-clear-title": "Jälgimisloendi tühjendamine",
        "watchlistedit-clear-legend": "Jälgimisloendi tühjendamine",
        "watchlistedit-clear-explain": "Sinu jälgimisloendist eemaldatakse kõik pealkirjad.",
        "watchlistedit-clear-titles": "Pealkirjad:",
        "version-libraries-license": "Litsents",
        "version-libraries-description": "Kirjeldus",
        "version-libraries-authors": "Autorid",
-       "redirect": "Ümbersuunamine faili, kasutaja, lehekülje või redaktsiooni identifikaatori järgi",
-       "redirect-summary": "See erilehekülg suunab ümber faili (toodud failinimi), lehekülje (toodud redaktsiooni või lehekülje identifikaator) või kasutajalehekülje (toodud numbriline kasutaja identfikaator) juurde. Kasutamine: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] või [[{{#Special:Redirect}}/user/101]].",
+       "redirect": "Ümbersuunamine faili, kasutaja, lehekülje, redaktsiooni või logiidentifikaatori järgi",
+       "redirect-summary": "See erilehekülg suunab ümber faili (toodud failinimi), lehekülje (toodud redaktsiooni või lehekülje identifikaator), kasutajalehekülje (toodud numbriline kasutaja identfikaator) või logisissekande (toodud logiidentifikaator) juurde. Kasutamine: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] või [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Mine",
        "redirect-lookup": "Leia:",
        "redirect-value": "Väärtus:",
        "tags-delete-not-allowed": "Tarkvaralisas määratletud märgiseid ei saa kustutada, kui märgis on tarkvaralisas eraldi lubatud.",
        "tags-delete-not-found": "Märgist \"$1\" pole.",
        "tags-delete-too-many-uses": "Märgist \"$1\" on rakendatud rohkem kui {{PLURAL:$2|ühe|$2}} redaktsiooni juures, mistõttu ei saa seda kustutada.",
-       "tags-delete-warnings-after-delete": "Märgis \"$1\" on edukalt kustutatud, kuid väljastati {{PLURAL:$2|järgmine hoiatus|järgmised hoiatused}}:",
+       "tags-delete-warnings-after-delete": "Märgis \"$1\" on kustutatud, kuid väljastati {{PLURAL:$2|järgmine hoiatus|järgmised hoiatused}}:",
        "tags-activate-title": "Märgise lubamine",
        "tags-activate-question": "Siinkohal lubad märgise \"$1\".",
        "tags-activate-reason": "Põhjus:",
        "tags-edit-revision-legend": "Märgiste lisamine või eemaldamine {{PLURAL:$1|selle|kõigi $1}} redaktsiooni juures",
        "tags-edit-logentry-legend": "Märgiste lisamine või eemaldamine {{PLURAL:$1|selle|kõigi $1}} logisissekande juures",
        "tags-edit-existing-tags": "Senised märgised:",
-       "tags-edit-existing-tags-none": "''puuduvad''",
+       "tags-edit-existing-tags-none": "<em>puuduvad</em>",
        "tags-edit-new-tags": "Uued märgised:",
        "tags-edit-add": "Lisa need märgised:",
        "tags-edit-remove": "Eemalda need märgised:",
        "tags-edit-reason": "Põhjus:",
        "tags-edit-revision-submit": "Rakenda {{PLURAL:$1|selle|$1}} redaktsiooni suhtes",
        "tags-edit-logentry-submit": "Rakenda {{PLURAL:$1|selle|$1}} logisissekande suhtes",
-       "tags-edit-success": "Muudatused on edukalt rakendatud.",
+       "tags-edit-success": "Muudatused on rakendatud.",
        "tags-edit-failure": "Muudatusi ei õnnestunud rakendada:\n$1",
        "tags-edit-nooldid-title": "Vigane sihtredaktsioon",
        "tags-edit-nooldid-text": "Selle toimingu jaoks pole määratud ühtegi sihtredaktsiooni või määratud redaktsiooni pole olemas.",
        "logentry-protect-protect-cascade": "$1 {{GENDER:$2|kaitses}} lehekülje $3 $4 [kaskaadkaitse]",
        "logentry-protect-modify": "$1 {{GENDER:$2|muutis}} lehekülje $3 kaitsetaset $4",
        "logentry-protect-modify-cascade": "$1 {{GENDER:$2|muutis}} lehekülje $3 kaitsetaset $4 [kaskaadkaitse]",
-       "logentry-rights-rights": "$1 {{GENDER:$2|muutis}} kasutaja $3 rühmaliikmesust; enne oli $4, nüüd on $5",
+       "logentry-rights-rights": "$1 {{GENDER:$2|muutis}} kasutaja {{GENDER:$6|$3}} rühmaliikmesust; enne oli $4, nüüd on $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|muutis}} kasutaja $3 rühmaliikmesust",
        "logentry-rights-autopromote": "$1 {{GENDER:$2|viidi}} automaatselt üle teise rühma; enne oli $4, nüüd on $5",
        "logentry-upload-upload": "$1 {{GENDER:$2|laadis üles}} faili $3",
        "expand_templates_generate_xml": "Näita XML-liigenduspuud",
        "expand_templates_generate_rawhtml": "Näita toor-HTMLi",
        "expand_templates_preview": "Eelvaade",
-       "expand_templates_preview_fail_html": "<em>Kuna {{GRAMMAR:inessive|{{SITENAME}}}} on toor-HTML lubatud ja osa seansiandmeid läks kaotsi, siis on JavaScripti põhiste rünnakute vastase abinõuna eelvaade peidetud.</em>\n\n<strong>Kui see eelvaatekatse on õigustatud, proovi palun uuesti.</strong>\nKui see ikka ei tööta, proovi [[Special:UserLogout|logida välja]] ja tagasi sisse.",
+       "expand_templates_preview_fail_html": "<em>Kuna {{GRAMMAR:inessive|{{SITENAME}}}} on toor-HTML lubatud ja osa seansiandmeid läks kaotsi, siis on JavaScripti põhiste rünnakute vastase abinõuna eelvaade peidetud.</em>\n\n<strong>Kui see eelvaatekatse on õigustatud, proovi palun uuesti.</strong>\nKui see ikka ei tööta, proovi [[Special:UserLogout|logida välja]] ja tagasi sisse ning kontrolli, kas brauser lubab sellest võrgukohast küpsiseid.",
        "expand_templates_preview_fail_html_anon": "<em>Kuna {{GRAMMAR:inessive|{{SITENAME}}}} on toor-HTML lubatud ja sa pole sisse logitud, siis on JavaScripti põhiste rünnakute vastase abinõuna eelvaade peidetud.</em>\n\n<strong>Kui see eelvaatekatse on õigustatud, [[Special:UserLogin|logi]] palun sisse ja proovi uuesti.</strong>",
        "pagelanguage": "Lehekülje keele valik",
        "pagelang-name": "Lehekülg",
        "mediastatistics": "Meediafailide arvandmestik",
        "mediastatistics-summary": "Arvandmed üles laaditud failitüüpide kohta. See käib ainult failide viimaste versioonide kohta. Vanu ja kustutatud versioone pole arvesse võetud.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 bait|$1 baiti}} ($2; $3%)",
-       "mediastatistics-bytespertype": "Failide kogusuurus selles alaosas: $1 baiti.",
-       "mediastatistics-allbytes": "Kõigi failide kogusuurus: $1 baiti.",
+       "mediastatistics-bytespertype": "Failide kogusuurus selles alaosas: $1 {{PLURAL:$1|bait|baiti}} ($2; $3%).",
+       "mediastatistics-allbytes": "Kõigi failide kogusuurus: $1 {{PLURAL:$1|bait|baiti}} ($2).",
        "mediastatistics-table-mimetype": "MIME tüüp",
        "mediastatistics-table-extensions": "Võimalikud laiendid",
        "mediastatistics-table-count": "Failide arv",
        "mw-widgets-dateinput-placeholder-month": "AAAA-KK",
        "mw-widgets-titleinput-description-new-page": "lehekülge pole veel",
        "mw-widgets-titleinput-description-redirect": "ümbersuunamine leheküljele \"$1\"",
-       "api-error-blacklisted": "Palun vali muu pealkiri, mis on kirjeldav.",
        "randomrootpage": "Juhuslik juurlehekülg"
 }
index f3ecd93..523b2ef 100644 (file)
@@ -66,7 +66,7 @@
        "tog-ccmeonemails": "Beste erabiltzaileei bidaltzen dizkiedan mezuen kopiak niri ere bidali",
        "tog-diffonly": "''Diff''-ak agertzen direnean, orrialdearen edukiera ezkutatu",
        "tog-showhiddencats": "Ikusi kategoria ezkutuak",
-       "tog-norollbackdiff": "Rollback bat egin ondoren ezberdintasunak ez hartu aintzat",
+       "tog-norollbackdiff": "Rollback bat egin ondoren ezberdintasunak ez erakutsi",
        "tog-useeditwarning": "Abisa nazazu gorde gabeko aldaketak eginez orrialde bat uzten dudanean",
        "tog-prefershttps": "Erabili beti konexio seguru bat sartzerakoan",
        "underline-always": "Beti",
        "mypreferencesprotected": "Ez daukazu eskumenik zure hobespenak aldatzeko.",
        "ns-specialprotected": "Ezin dira {{ns:special}} izen-tarteko orrialdeak editatu.",
        "titleprotected": "[[User:$1|$1]]ek izenburu hau sortzea ekidin zuen.\nEmandako arrazoia <em>$2</em> izan zen.",
-       "filereadonlyerror": "Ezin izan da \"$1\" fitxategia aldatu, \"$2\" fitxategi bilduma irakrutzeko-bakarrik moduan dagoelako.\n\nBlokeoa ezarri zuen administratzaileak honako arrazoia eman zuen: \"$3\".",
+       "filereadonlyerror": "Ezin izan da \"$1\" fitxategia aldatu, \"$2\" fitxategi bilduma irakrutzeko-bakarrik moduan dagoelako.\n\nBlokeoa ezarri zuen sistema administratzaileak honako arrazoia eman zuen: \"$3\".",
        "invalidtitle-knownnamespace": "Izenburua gaizki dago \"$2\" izen eremuan eta \"$3\" testuan",
        "invalidtitle-unknownnamespace": "Izenburua gaizki dago \"$1\" izen eremuan ezezagunean eta \"$2\" testuan",
        "exception-nologin": "Saioa hasi gabe",
        "yourpasswordagain": "Pasahitza berriz",
        "createacct-yourpasswordagain": "Pasahitza berridatzi",
        "createacct-yourpasswordagain-ph": "Sartu pasahitza berriro ere",
-       "remembermypassword": "Nire saioa ordenagailu honetan gogoratu ({{PLURAL:$1|egun baterako|$1 egunetarako }} gehienez)",
        "userlogin-remembermypassword": "Manten nazazu barruan",
        "userlogin-signwithsecure": "Erabili konexio ziurra",
        "yourdomainname": "Zure domeinua",
        "createacct-another-realname-tip": "Benetako izena hautazkoa da.\nEmatea erabakitzen baduzu hori erabiliko da lanaren atribuzioa egiterako garaian.",
        "pt-login": "Saioa hasi",
        "pt-login-button": "Saioa hasi",
+       "pt-login-continue-button": "Konexioa jarraitu",
        "pt-createaccount": "Sortu kontua",
        "pt-userlogout": "Saioa itxi",
        "php-mail-error-unknown": "PHPren mail() funtzioan arazo ezezagun bat egon da.",
        "resetpass_submit": "Pasahitza definitu eta saioa hasi",
        "changepassword-success": "Zure pasahitza aldatu da!",
        "changepassword-throttled": "Saioa hasteko saiakera gehiegi egin berri dituzu.\nBerriro saiatu aurretik $1 itxoin, mesedez.",
+       "botpasswords": "Bot pasahitzak",
+       "botpasswords-disabled": "Bot pasahitzak desgaituak daude.",
        "botpasswords-label-appid": "Bot izena:",
        "botpasswords-label-create": "Sortu",
        "botpasswords-label-update": "Eguneratu",
        "passwordreset-email": "E-mail helbidea:",
        "passwordreset-emailtitle": "{{SITENAME}}-rako kontuaren xehetasunak",
        "passwordreset-emailelement": "Erabiltzaile izena: \n$1\n\nBehin-behineko pasahitza: \n$2",
-       "passwordreset-emailsentemail": "Hau zure konturako erregistratuta dagoen helbide elektronikoa baldin bada, mezu elektronikoa bidaliko da zure pasahitza berrezartzeko.",
-       "passwordreset-emailsent-capture": "Pasahitza berrezartzeko e-posta bat bidali dizugu, behean erakusten dena.",
+       "passwordreset-emailsentemail": "Hau zure kontuarekin lotuta dagoen helbide elektronikoa baldin bada, mezu elektronikoa bidaliko da zure pasahitza berrezartzeko.",
        "changeemail": "Aldatu edo kendu e-mail helbidea",
        "changeemail-header": "Bete ezazu inprimaki hau, zure helbide elektronikoa aldatzeko. Zure kontuari helbide elektronikorik elkartuta ez izatea nahi baduzu, utz ezazu hutsik helbide elektroniko berria, inprimakia bidaltzen duzunean.",
        "changeemail-no-info": "Orrialde honetara zuzenean sartzeko izena eman behar duzu.",
        "minoredit": "Aldaketa hau txikia da",
        "watchthis": "Orrialde hau jarraitu",
        "savearticle": "Gorde orria",
+       "savechanges": "Aldaketak gorde",
+       "publishpage": "Orrialdea argitaratu",
+       "publishchanges": "Aldaketak argitaratu",
        "preview": "Aurrebista erakutsi",
        "showpreview": "Aurrebista erakutsi",
        "showdiff": "Aldaketak erakutsi",
        "accmailtext": "[[User talk:$1|$1]]-entzako ausaz sortutako pasahitza $2-(r)a bidali da.\n\n''[[Special:ChangePassword|pasahitz aldaketa]]'' orrialdean alda daiteke, behin barruan sartuta.",
        "newarticle": "(Berria)",
        "newarticletext": "Orrialde hau ez da existitzen oraindik. Orrialde sortu nahi baduzu, beheko koadroan idazten hasi zaitezke (ikus [$1 laguntza orrialdea] informazio gehiagorako). Hona nahi gabe etorri bazara, nabigatzaileko '''atzera''' botoian klik egin.",
-       "anontalkpagetext": "----''Orrialde hau konturik sortu ez edo erabiltzen ez duen erabiltzaile anonimo baten eztabaida orria da.\nBere IP helbidea erabili beharko da beraz identifikatzeko.\nErabiltzaile batek baino gehiagok IP bera erabil dezakete ordea.\nErabiltzaile anonimoa bazara eta zurekin zerikusirik ez duten mezuak jasotzen badituzu, mesedez [[Special:CreateAccount|Izena eman]] edo [[Special:UserLogin|saioa hasi]] etorkizunean horrelakoak gerta ez daitezen.''",
+       "anontalkpagetext": "<em>Orrialde hau konturik sortu ez edo erabiltzen ez duen erabiltzaile anonimo baten eztabaida orria da.</em>\nBere IP helbidea erabili beharko da beraz identifikatzeko.\nErabiltzaile batek baino gehiagok IP bera erabil dezakete ordea.\nErabiltzaile anonimoa bazara eta zurekin zerikusirik ez duten mezuak jasotzen badituzu, mesedez [[Special:CreateAccount|Izena eman]] edo [[Special:UserLogin|saioa hasi]] etorkizunean horrelakoak gerta ez daitezen.",
        "noarticletext": "Oraindik ez dago testurik orri honetan.\nEdukiz hornitzeko, aukera hauek dituzu: beste orri batzuetan [[Special:Search/{{PAGENAME}}|orri izenburu hau bilatzea]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} lotutako logak bilatzea],\nedo [{{fullurl:{{FULLPAGENAME}}|action=edit}} orri hau sortzea]</span>.",
        "noarticletext-nopermission": "Une honetan ez dago testurik orrialde honetan.\nBeste orrialdeetan [[Special:Search/{{PAGENAME}}|izenburu hau bilatu dezakezu]],\nedo <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} erlazionatutako erregistroak bilatu]</span>, baina ez duzu orrialde hau sortzeko baimenik.",
        "userpage-userdoesnotexist": "\"<nowiki>$1</nowiki>\" lankidea ez dago erregistatuta. Mesedez, konprobatu orri hau editatu/sortu nahi duzun.",
        "userpage-userdoesnotexist-view": "\"$1\" erabiltzaile-kontua ez dago erregistraturik.",
        "blocked-notice-logextract": "Erabiltzaile hau blokeatuta dago une honetan.\nAzken blokeoaren erregistroa ageri da behean, erreferentzia gisa:",
-       "clearyourcache": "'''Oharra:''' Gorde ondoren, zure nabigatzailearen katxea ekidin beharko duzu aldaketak ikusteko.\n* '''Firefox / Safari:''' ''Shift'' tekla sakatu birkargatzeko momentuan, edo ''Ctrl-Shift-R'' edo ''Crtl-F5'' sakatu (''⌘-R''' Mac batean)\n* '''Google Chrome:''' ''Ctrl-Shift-R'' sakatu (''⌘-Shift-R'' Mac batean)\n* '''Internet Explorer:''' ''Ctrl'' tekla sakatu birkargatzeko momentuan, edo ''Ctrl-F5'' sakatu\n* '''Opera''' erabiltzaileek ''Tresnak → Hobespenak'' atalera joan eta katxea garbitzeko aukera hautatu",
+       "clearyourcache": "<strong>Oharra:</strong> Gorde ondoren, zure nabigatzailearen katxea ekidin beharko duzu aldaketak ikusteko.\n* <strong>Firefox / Safari:</strong> <em>Shift</em> tekla sakatu birkargatzeko momentuan, edo <em>Ctrl-Shift-R</em> edo <em>Crtl-F5</em>  sakatu (<em>⌘-R</em> Mac batean)\n* <strong>Google Chrome:</strong> <em>Ctrl-Shift-R </em>  sakatu (<em>⌘-Shift-R</em> Mac batean)\n* <strong>Internet Explorer:</strong> <em>Ctrl</em> tekla sakatu birkargatzeko momentuan, edo <em>Ctrl-F5</em> sakatu\n* <strong>Opera</strong> erabiltzaileek <em>Tresnak → Hobespenak</em> atalera joan eta katxea garbitzeko aukera hautatu",
        "usercssyoucanpreview": "'''Laguntza:''' Zure CSS berria gorde aurretik probatzeko \"{{int:showpreview}}\" botoia erabili.",
        "userjsyoucanpreview": "'''Laguntza:''' Zure JS berria gorde aurretik probatzeko \"{{int:showpreview}}\" botoia erabili.",
        "usercsspreview": "'''Ez ahaztu zure CSS kodea aurreikusten zabiltzala.'''\n'''Oraindik gorde gabe dago!'''",
        "previewnote": "'''Gogoratu hau aurrikuspen bat dela.'''\nZure aldaketak ez dira oraindik gorde!",
        "continue-editing": "Edizio-eremura joan",
        "previewconflict": "Aurreikuspenak aldaketen koadroan idatzitako testua erakusten du, gorde ondoren agertuko den bezala.",
-       "session_fail_preview": "'''Sentitzen dugu! Ezin izan da zure aldaketa prozesatu, saioko datu batzuen galera dela-eta. Mesedez, saiatu berriz. Arazoak jarraitzen badu, saiatu saioa amaitu eta berriz hasten.'''",
+       "session_fail_preview": "'''Sentitzen dugu! Ezin izan da zure aldaketa prozesatu, saioko datu batzuen galera dela-eta. Mesedez, saiatu berriz. Arazoak jarraitzen badu, saiatu [[Special:UserLogout|saioa amaitu]] eta berriz hasten.'''",
        "session_fail_preview_html": "'''Sentitzen dugu! Ezin izan dugu zure aldaketa burutu, saio datu galera bat medio.'''\n\n''Wiki honek HTML kodea onartzen duenez, aurreikuspena ezgaituta dago JavaScript erasoak saihestu asmoz.''\n\n'''Aldaketa saiakera hau zuzena baldin bada, saiatu berriro mesedez. Arazoak jarraitzen badu, saiatu saioa itxi eta berriz hasten.'''",
        "token_suffix_mismatch": "'''Zure aldaketa ezeztatua izan da zure bezeroak puntuazio-karaktereak itxuragabetu dituelako.\nAldaketa ezeztatua izan da testuaren galtzea galarazteko.\nHau batzuetan gertatzen da buggyan oinarritutako web proxy zerbitzua erabiltzean.'''",
        "edit_form_incomplete": "'''Aldaketa formularioaren atal batzuk ez dira iritsi zerbitzarira; bi aldiz ziurtatu zure aldaketak osorik daudela eta berriro saiatu.'''",
        "undo-failure": "Ezin izan da aldaketa desegin tarteko aldaketekin gatazkak direla-eta.",
        "undo-norev": "Aldaketa ezin da desegin ez delako existitzen edo ezabatu zutelako.",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|eztabaida]]) wikilariaren $1 berrikuspena desegin da",
-       "cantcreateaccounttitle": "Ezin izan da kontua sortu",
        "cantcreateaccount-text": "IP helbide honetatik ('''$1''') kontu berria sortzeko aukera blokeatu du [[User:$3|$3]](e)k.\n\n$3(e)k emandako arrazoia: ''$2''",
        "viewpagelogs": "Orrialde honen erregistroak ikusi",
        "nohistory": "Orrialde honek ez dauka aldaketa historiarik.",
        "revdelete-unsuppress": "Berrezarritako aldaketen mugak kendu",
        "revdelete-log": "Arrazoia:",
        "revdelete-submit": "Hautatutako {{PLURAL:$1|berrikuspenari|berrikuspenei}} aplikatu",
-       "revdelete-success": "'''Berrikuspenen ikusgarritasuna eguneratu da.'''",
+       "revdelete-success": "Berrikuspenen ikusgarritasuna eguneratu da.",
        "revdelete-failure": "'''Ezin da berrikuspenaren ikuspena eguneratu:'''\n$1",
-       "logdelete-success": "'''Log ikusgarritasuna ondo ezarri da.'''",
+       "logdelete-success": "Log ikusgarritasuna ondo ezarri da.",
        "logdelete-failure": "'''Erregistroaren ikusgaitasuna ezin da honela ezarri:'''\n$1",
        "revdel-restore": "Aldatu ikusgaitasuna",
        "pagehist": "Orriaren historia",
        "mergehistory-go": "Aldaketa bateragarriak erakutsi",
        "mergehistory-submit": "Berrikuspenak bateratu",
        "mergehistory-empty": "Ezin da berrikuspenik bateratu",
-       "mergehistory-done": "$1(e)ko {{PLURAL:$3|berrikuspen|berrikuspen}} bateratu egin dira [[:$2]](e)n.",
+       "mergehistory-done": "$1(e)ko {{PLURAL:$3|berrikuspen}} bateratu egin {{PLURAL:$3|da|dira}} [[:$2]](e)n.",
        "mergehistory-fail": "Ezin izan da historia bateratu; egiaztatu orrialde eta denbora parametroak.",
        "mergehistory-no-source": "Ez da $1 jatorrizko orrialdea existitzen.",
        "mergehistory-no-destination": "Ez da $1 helburu orrialdea existitzen.",
        "preferences": "Hobespenak",
        "mypreferences": "Hobespenak",
        "prefs-edits": "Aldaketa kopurua:",
-       "prefsnologintext2": "Mesedez $1 zure hobespenak aldatzeko.",
+       "prefsnologintext2": "Mesedez saioa hasi zure hobespenak aldatzeko.",
        "prefs-skin": "Itxura",
        "skin-preview": "Aurrebista",
        "datedefault": "Hobespenik ez",
        "badsig": "Baliogabeko sinadura; egiaztatu HTML etiketak.",
        "badsiglength": "Zure sinadura luzeegia da.\n$1 {{PLURAL:$1|karakteretik|karakteretik}} behera izan behar ditu.",
        "yourgender": "Nola nahiagu duzu deskribatua izatea?",
-       "gender-unknown": "Nahiago dut ez esatea",
+       "gender-unknown": "Aipatzen zaituztenean, softwareak genero neutroa erabiliko du hori posible denean",
        "gender-male": "Wiki orrialdeak editatzen dituen gizona",
        "gender-female": "Wiki orrialdeak editatzen dituen emakumea",
        "prefs-help-gender": "Hobespen hau jartzea aukerazkoa da.\nSoftwareak bere balioak erabiltzen ditu zu aipatzeko eta beste batzuek genero gramatikala erabiltzeko aukera izan dezaten.\nInformazio hau publikoa da.",
        "editusergroup": "{{GENDER:$1|Erabiltzaile}} taldeak editatu",
        "editinguser": "'''[[User:$1|$1]]''' $2 lankidearen erabiltzaile-eskubideak aldatzen",
        "userrights-editusergroup": "Erabiltzaile taldeak editatu",
-       "saveusergroups": "Erabiltzaile taldeak gorde",
+       "saveusergroups": "Erabiltzaile {{GENDER:$1|taldeak}} gorde",
        "userrights-groupsmember": "Ondorengo talde honetako kide da:",
        "userrights-groupsmember-auto": "Honen kide inplizitua:",
        "userrights-groups-help": "Lankide hau zein taldetakoa den alda dezakezu:\n* Laukia hautatuta baldin badago, esan nahi du lankidea talde horretakoa dela.\n* Laukia hautatu gabe baldin badago, esan nahi du lankidea talde horretakoa ez dela.\n* Izartxoak (*) erakusten du ezin duzula talde horretatik kendu, taldera gehitu eta gero; edo alderantziz, ezin duzula talde horretara gehitu, taldetik kendu eta gero.",
        "userrights-changeable-col": "Alda ditzakezun taldeak",
        "userrights-unchangeable-col": "Aldatu ezin ditzakezun taldeak",
        "userrights-conflict": "Gatazka gertatu da erabiltzaile eskubideak aldatzean. Mesedez, berrikusi eta baieztatu zure aldaketak.",
-       "userrights-removed-self": "Arrakasta izan duzu zure eskumenak kentzen. Beraz jada ezin duzu orrialde hau gehiago ikusi.",
+       "userrights-removed-self": "Zure eskumenak kendu dituzu. Beraz jada ezin duzu orrialde hau gehiago ikusi.",
        "group": "Taldea:",
        "group-user": "Erabiltzaileak",
        "group-autoconfirmed": "Lankide autokonfirmatuak",
        "right-deletedtext": "Ikusi ezabatutako testua eta ezabatutako berrikuspenen arteko aldaketak",
        "right-browsearchive": "Ezabatutako orrialdeak bilatu",
        "right-undelete": "Ezabatutako orrialde bat itzularazi",
-       "right-suppressrevision": "Administratzaileentzat izkutatutako berrikuspenak berrikusi edo berrezarri",
+       "right-suppressrevision": "Edozein erabiltzaileren berrikuspenak ikusi, ezkutatu ala ikustarazi",
        "right-suppressionlog": "Log pribatuak ikusi",
        "right-block": "Blokeatu beste erabiltzaile batzuk, edita ez dezaten",
        "right-blockemail": "Erabiltzaile bati blokeatu mezu elektronikoak bidaltzeko aukera",
        "right-override-export-depth": "5eko sakonerararteko loturiko orrialdeak barne esportatu",
        "right-sendemail": "Beste erabiltzaileei e-posta bidali",
        "right-passwordreset": "Ikusi pasahitza berrezartze e-postak",
+       "grant-group-email": "E-posta bidali",
        "grant-createaccount": "Kontuak sortu",
        "grant-editmycssjs": "Zure CSS/JavaScript aldatu",
        "grant-editmyoptions": "Aldatu zure hobespenak",
        "rightslogtext": "Erabiltzaile eskubideetan izandako aldaketen erregistroa da hau.",
        "action-read": "orrialde hau irakurri",
        "action-edit": "orri hau aldatu",
-       "action-createpage": "orrialdeak sortu",
-       "action-createtalk": "eztabaida orrialdeak sortu",
+       "action-createpage": "Sortu orri hau",
+       "action-createtalk": "eztabaida orrialde hau sortu",
        "action-createaccount": "lankide hau sortu",
        "action-history": "orrialde honen historia ikusi",
        "action-minoredit": "markatu aldaketa hau txikitzat",
        "upload-form-label-infoform-title": "Xehetasunak",
        "upload-form-label-infoform-name": "Izena",
        "upload-form-label-infoform-description": "Deskribapena",
+       "upload-form-label-infoform-description-tooltip": "Azaldu laburki lanaren inguruko kontu aipagarri guztiak. Argazki batean, aipatu agertzen den gai nagusia, momentua, edo lekua.",
        "upload-form-label-usage-title": "Erabilera",
        "upload-form-label-usage-filename": "Fitxategiaren izena",
        "upload-form-label-own-work": "Hau neure lana da",
        "uploadstash": "Gordailu bat igo",
        "uploadstash-clear": "Kodetutako fitxategiak izkutatu",
        "uploadstash-nofiles": "Ez duzu kodetutako fitxategirik.",
-       "uploadstash-errclear": "Fitxategiak ezabatzeak ez du arrakastarik izan.",
+       "uploadstash-errclear": "Fitxategiak ezabatzeak akatsa eman du.",
        "uploadstash-refresh": "Fitxategien zerrenda eguneratu",
        "img-auth-accessdenied": "Sarbide ukatua",
        "img-auth-nopathinfo": "PATH_INFO falta da.\nZure zerbitzaria ez dago informazio hau pasatzeko konfiguratuta.\nCGI-oinarriduna izan daiteke, img_auth onartzen ez duena.\nIkusi https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "nolicense": "Hautatu gabe",
        "licenses-edit": "Aldatu lizentzien aukerak",
        "license-nopreview": "(Aurreikuspenik ez)",
-       "upload_source_url": " (baliozko URL publikoa)",
-       "upload_source_file": " (zure ordenagailuko fitxategi bat)",
+       "upload_source_url": "(zuk aukeratutako fitxategi baliozko URL publikorako)",
+       "upload_source_file": "(zure ordenagailuko fitxategi bat)",
        "listfiles-delete": "ezabatu",
        "listfiles-summary": "Orri berezi honek igotako fitxategi guztiak erakusten ditu.",
        "listfiles_search_for": "Irudiaren izenagatik bilatu:",
        "filerevert-legend": "Fitxategia leheneratu",
        "filerevert-intro": "'''[[Media:$1|$1]]''' berrezartzen ari zara [$4 $3(e)ko, $2(e)tako bertsiora].",
        "filerevert-comment": "Arrazoia:",
-       "filerevert-defaultcomment": "$2, $1 bertsiora leheneratu da",
+       "filerevert-defaultcomment": "$2, $1 ($3) bertsiora leheneratu da",
        "filerevert-submit": "Leheneratu",
        "filerevert-success": "'''[[Media:$1|$1]]''' [$4 $3(e)ko, $2(e)tako bertsiora] lehenratua izan da.",
        "filerevert-badversion": "Ez dago aurreragoko fitxategi honen bertsio lokalik emandako denbora tartean.",
        "listgrouprights-removegroup-self-all": "Talde guztiak norbere kontutik ezabatu",
        "listgrouprights-namespaceprotection-namespace": "Izen-tartea",
        "listgrants": "Diru-laguntzak",
+       "listgrants-grant": "Baimena eman",
        "listgrants-rights": "Eskubideak",
+       "trackingcategories": "Jarraipen kategoriak",
+       "trackingcategories-msg": "Jarraipen kategoria",
        "trackingcategories-name": "Mezuaren izena",
        "trackingcategories-nodesc": "Ez dago deskribapenik eskuragarri.",
        "trackingcategories-disabled": "Kategoria desgaitua dago",
        "emailccsubject": "Zure mezuaren kopia $1(r)i: $2",
        "emailsent": "Mezua bidali egin da",
        "emailsenttext": "Zure e-posta mezua bidali egin da.",
-       "emailuserfooter": "E-posta hau $1(e)k bidali dio $2(r)i {{SITENAME}}ko \"{{int:emailuser}}\" funtzioa erabiliz.",
+       "emailuserfooter": "E-posta hau $1(e)k {{GENDER:$1|bidali}} dio {{GENDER:$2|$2}}(r)i {{SITENAME}}ko \"{{int:emailuser}}\" funtzioa erabiliz.",
        "usermessage-summary": "Sistema mezua uzten.",
        "usermessage-editor": "Sistemako mezularia",
        "watchlist": "Jarraipen zerrenda",
        "wlshowhideanons": "erabiltzaile anonimoak",
        "wlshowhidepatr": "Patruilatutako aldaketak",
        "wlshowhidemine": "nire edizioak",
+       "wlshowhidecategorization": "orrialdearen kategorizazioa",
        "watchlist-options": "Jarraitze-zerrendaren aukerak",
        "watching": "Zerrendan gehitzen...",
        "unwatching": "Zerrendatik kentzen...",
        "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-title-label": "Orriaren izenburua",
        "changecontentmodel-reason-label": "Arrazoia:",
+       "changecontentmodel-submit": "Aldatu",
        "logentry-contentmodel-change-revertlink": "desegin",
        "logentry-contentmodel-change-revert": "desegin",
        "protectlogpage": "Babes erregistroa",
        "undeletedrevisions": "{{PLURAL:$1|Berrikuspen 1 leheneratu da|$1 berrikuspen leheneratu dira}}",
        "undeletedrevisions-files": "{{PLURAL:$1|berrikuspen|berrikuspen}} eta {{PLURAL:$2|fitxategi|fitxategi}} leheneratu dira",
        "undeletedfiles": "{{PLURAL:$1|fitxategi|fitxategi}} leheneratu dira",
-       "cannotundelete": "Ezabatutako birgaitzean akatsa: $1",
+       "cannotundelete": "Ezabatutako birgaitze betean edo hainbatetan akatsa: $1",
        "undeletedpage": "'''«$1» leheneratu da'''\n\nAzken ezabatze eta leheneratzeak ikusteko, jo ezazu [[Special:Log/delete|ezabaketa erregistrora]].",
        "undelete-header": "Berriki ezabatutako orriak ikusteko, jo ezazu [[Special:Log/delete|ezabaketa erregistrora]].",
        "undelete-search-title": "Ezabatutako orrialdeak bilatu",
        "sp-contributions-newbies-sub": "Hasiberrientzako",
        "sp-contributions-newbies-title": "Lankideen ekarpenak lankide berrietn",
        "sp-contributions-blocklog": "Blokeaketa erregistroa",
-       "sp-contributions-suppresslog": "lankide-ekarpen ezabatuak",
+       "sp-contributions-suppresslog": "{{GENDER:$1|(r)en}} lankide-ekarpen ezabatuak",
        "sp-contributions-deleted": "lankide-ekarpen ezabatuak",
        "sp-contributions-uploads": "igoerak",
        "sp-contributions-logs": "erregistroak",
        "sp-contributions-search": "Ekarpenentzako bilaketa",
        "sp-contributions-username": "IP helbidea edo erabiltzaile izena:",
        "sp-contributions-toponly": "Azken aldaketak direnak soilik erakutsi",
+       "sp-contributions-newonly": "Orrialde sorkuntza direnak soilik erakutsi",
+       "sp-contributions-hideminor": "Aldaketa txikiak ezkutatu",
        "sp-contributions-submit": "Bilatu",
        "whatlinkshere": "Honanzko lotura duten orriak",
        "whatlinkshere-title": "$1(e)ra lotura duten orriak",
        "ipb-unblock": "Erabiltzaile izen edo IP helbide bati blokeoa kendu",
        "ipb-blocklist": "Blokeaketak ikusi",
        "ipb-blocklist-contribs": "{{GENDER:$1|$1(r)en}} ekarpenak",
+       "ipb-blocklist-duration-left": "gainerako $1",
        "unblockip": "Erabiltzailea desblokeatu",
        "unblockiptext": "Erabili beheko formularioa lehenago blokeatutako IP helbide edo erabiltzaile baten idazketa baimenak leheneratzeko.",
        "ipusubmit": "Blokeoa ezabatu",
        "tooltip-feed-rss": "Orrialde honen RSS jarioa",
        "tooltip-feed-atom": "Orrialde honen atom jarioa",
        "tooltip-t-contributions": "{{GENDER:$1|Lankide honen}} ekarpen zerrenda ikusi",
-       "tooltip-t-emailuser": "Lankide honi e-posta mezua bidali",
+       "tooltip-t-emailuser": "{{GENDER:$1|Lankide honi}} e-posta mezua bidali",
        "tooltip-t-info": "Orrialde honi buruzko informazio gehiago",
        "tooltip-t-upload": "Irudiak edo media fitxategiak igo",
        "tooltip-t-specialpages": "Orri berezi guztien zerrenda",
        "pageinfo-category-files": "Fitxategi kopurua",
        "markaspatrolleddiff": "Patruilatutzat markatu",
        "markaspatrolledtext": "Artikulu hau patruilatutzat markatu",
+       "markaspatrolledtext-file": "Fitxategi honen bertsioa patruilatutzat markatu",
        "markedaspatrolled": "Patruilatutzat markatu da",
        "markedaspatrolledtext": "[[:$1]]-(r)en bertsio hautatua patruilatutzat markatu da.",
        "rcpatroldisabled": "Aldaketa berrien patruilaketa ezgaituta dago",
        "confirm-watch-top": "Orrialde hau zure jarraipen-zerrendara gehitu?",
        "confirm-unwatch-button": "Ados",
        "confirm-unwatch-top": "Orrialde hau zure jarraipen-zerrendatik kendu?",
+       "confirm-rollback-button": "Ados",
        "quotation-marks": "«$1»",
        "imgmultipageprev": "&larr; aurreko orrialdea",
        "imgmultipagenext": "hurrengo orrialdea &rarr;",
        "logentry-move-move_redir": "$1 {{GENDER:$2|wikilariak}} «$3» orria «$4» izenera aldatu du, birzuzenketaren gainetik",
        "logentry-move-move_redir-noredirect": "$1 {{GENDER:wikilariak}} «$3» orria «$4» izenera aldatu du, birbideratze bat gainidatzita, birbideratzerik utzi gabe",
        "logentry-patrol-patrol": "$1(e)k $3 orrialdearen $4 berrikuspena patruilatutzat {{GENDER:$2|markatu}} du",
-       "logentry-newusers-newusers": "$1 erabiltzaile kontua sortu da",
+       "logentry-newusers-newusers": "$1 erabiltzaile kontua {{GENDER:$2|sortu da}}",
        "logentry-newusers-create": "$1 erabiltzaile kontua {{GENDER:$2|sortu da}}",
        "logentry-newusers-create2": "$1 wikilariak $3 erabiltzaile kontua sortu du",
+       "logentry-newusers-byemail": "$1(e)k $3 erabiltzaile kontua {{GENDER:$2|sortu du}} eta pasahitza emailez bidali da",
        "logentry-upload-upload": "$1(e)k $3 {{GENDER:$2|igo du}}",
        "log-name-tag": "Etiketen erregistroa",
        "rightsnone": "(bat ere ez)",
        "expand_templates_generate_xml": "Erakutsi XML parse zuhaitza",
        "expand_templates_generate_rawhtml": "Erakutsi HTML gordina",
        "expand_templates_preview": "Aurreikusi",
-       "pagelanguage": "Orriaren hizkuntza aukeratu",
+       "pagelanguage": "Orriaren hizkuntza aldatu",
        "pagelang-name": "Orria",
        "pagelang-language": "Hizkuntza",
        "pagelang-use-default": "Hizkuntza lehenetsia erabili",
        "mw-widgets-dateinput-no-date": "Ez duzu datarik aukeratu",
        "mw-widgets-titleinput-description-new-page": "orri hori oraindik ez da existitzen",
        "mw-widgets-titleinput-description-redirect": "$1ra birzuzendu",
-       "api-error-blacklisted": "Aukera ezazu, mesedez, izenburu ezberdin eta deskriptiboago bat.",
-       "sessionprovider-generic": "$1 sesio"
+       "sessionprovider-generic": "$1 sesio",
+       "log-action-filter-all": "Denak",
+       "log-action-filter-block-block": "Blokeatu",
+       "log-action-filter-block-reblock": "Blokeoa aldatu",
+       "log-action-filter-block-unblock": "blokeoa kendu",
+       "authmanager-userdoesnotexist": "\"$1\" erabiltzaile kontua ez dago erregistratua.",
+       "authmanager-email-label": "Emaila",
+       "authmanager-email-help": "Helbide elektronikoa",
+       "authmanager-realname-label": "Benetako izena",
+       "authmanager-realname-help": "Erabiltzailearen benetako izena",
+       "authprovider-resetpass-skip-label": "Utzi",
+       "authform-wrongtoken": "Token okerra"
 }
index 56e8cbe..302bc58 100644 (file)
@@ -11,7 +11,8 @@
                        "Henares",
                        "MarcoAurelio",
                        "Macofe",
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Crucifunked"
                ]
        },
        "tog-underline": "Surrayal atihus:",
        "oct": "Otu",
        "nov": "Nov",
        "dec": "Dic",
+       "january-date": "$1 eneru",
+       "february-date": "$1 de hebreru",
+       "march-date": "$1 de marçu",
+       "april-date": "$1 e abril",
+       "may-date": "$1 e mayu",
+       "june-date": "$1 e húniu",
+       "july-date": "$1 e húliu",
+       "august-date": "$1 e agostu",
+       "september-date": "$1 e setiembri",
+       "october-date": "$1 e outubri",
+       "november-date": "$1 e noviembri",
+       "december-date": "$1 e diziembri",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categoria|Categorias}}",
        "category_header": "Artículus ena categoria \"$1\"",
        "subcategories": "Sucategorias",
        "category-file-count": "{{PLURAL:$2|Esta categoria solu contiini el siguienti archivu.|{{PLURAL:$1|El siguienti archivu está|Los siguientis $1 archivus están}} nesta categoria, dun total de $2.}}",
        "category-file-count-limited": "{{PLURAL:$1|El siguienti archivu está|Los siguientis $1 archivus están}} nesta categoria.",
        "listingcontinuesabbrev": "acont.",
+       "broken-file-category": "Páhinas con atihus esgalçaos a archivus",
        "about": "Al tentu",
        "article": "Artículu",
        "newwindow": "(s'abrirá nuna nueva ventana)",
        "cancel": "Cancelal",
        "moredotdotdot": "Mas...",
-       "mypage": "La mi páhina",
+       "morenotlisted": "Esta lista nu está completa",
+       "mypage": "Páhina",
        "mytalk": "La mi caraba",
-       "anontalk": "Caraba pa esta IP",
+       "anontalk": "La mi caraba",
        "navigation": "Güiquipeandu",
        "and": "&#32;i",
        "qbfind": "Alcuentral",
        "actions": "Acionis",
        "namespaces": "Espáciu nombris",
        "variants": "Variantis",
+       "navigation-heading": "Menú de navegación",
        "errorpagetitle": "Marru",
        "returnto": "Gorvel a $1.",
        "tagline": "Dendi {{SITENAME}}",
        "printableversion": "Velsión pa imprental",
        "permalink": "Atiju remanenti",
        "print": "Imprental",
+       "view": "Guipal",
+       "view-foreign": "Vel en $1",
        "edit": "Eital",
+       "edit-local": "Eital descrición local",
        "create": "Crial",
+       "create-local": "Azeñil descrición local",
        "editthispage": "Eital esta páhina",
        "create-this-page": "Crial esta páhina",
        "delete": "Esborral",
        "deletethispage": "Esborral esta páhina",
+       "undeletethispage": "Arrecuperal esta páhina",
        "undelete_short": "Arrecuperal {{PLURAL:$1|una eición|$1 eicionis}}",
+       "viewdeleted_short": "Guipal {{PLURAL:$1|una eición esborrá|$1 eicionis esborrás}}",
        "protect": "Protegel",
        "protect_change": "escambial",
        "protectthispage": "Protegel esta página",
-       "unprotect": "esprotegel",
-       "unprotectthispage": "Esprotegel esta página",
+       "unprotect": "Escambial proteción",
+       "unprotectthispage": "Escambial la proteción esta página",
        "newpage": "Páhina nueva",
        "talkpage": "Palral sobri esta páhina",
        "talkpagelinktext": "Caraba",
        "otherlanguages": "En otras palras",
        "redirectedfrom": "(Rederihiu dendi $1)",
        "redirectpagesub": "Rederihil páhina",
+       "redirectto": "Redirihi a:",
        "lastmodifiedat": "Los úrtimus chambus desta páhina huerun a las $2 el dia $1.",
        "viewcount": "Esta páhina á siu visoreá {{PLURAL:$1|una vezi|$1 vezis}}.",
        "protectedpage": "Página protegia",
        "jumptosearch": "landeal",
        "aboutsite": "Al tentu {{SITENAME}}",
        "aboutpage": "Project:Enjolmación",
-       "copyright": "Continiu disponibri bahu $1.",
+       "copyright": "El continiu está disponibri bahu $1 a nu sel que se diga lo contrariu.",
        "copyrightpage": "{{ns:project}}:Copyright",
        "currentevents": "La trohi las notícias",
        "currentevents-url": "Project:La trohi las notícias",
        "disclaimers": "Avissu legal",
        "disclaimerpage": "Project:Arrayu heneral de responsabiliá",
        "edithelp": "Ayua d'eición",
+       "helppage-top-gethelp": "Ayua",
        "mainpage": "Página prencipal",
        "mainpage-description": "Páhina prencipal",
        "policy-url": "Project:Pulítica",
        "ok": "Dalcuerdu",
        "retrievedfrom": "Arrecuperau dendi \"$1\"",
        "youhavenewmessages": "Tiinis $1 ($2).",
+       "youhavenewmessagesmanyusers": "Tienis $1 de muchus usuárius ($2).",
+       "newmessageslinkplural": "{{PLURAL:$1|un mensahi nuevu|999=mensahis nuevus}}",
        "youhavenewmessagesmulti": "Tiinis nuevus mensahis en $1",
        "editsection": "eital",
        "editold": "eital",
        "yourname": "Nombri d'usuáriu:",
        "yourpassword": "Consínia:",
        "yourpasswordagain": "Escrebi e nuevu la consínia:",
-       "remembermypassword": "Recordal la mi cuenta nesti ordinaol (for a maximum of $1 {{PLURAL:$1|day|days}})",
        "yourdomainname": "El tu domiñu:",
        "externaldberror": "Marru d'autentificación esterna e la basi e datus, u bien nu t'alcuentras autorizau p'atualizal la tu cuenta esterna.",
        "login": "Entral",
        "createaccount-title": "Criaeru e cuentas de {{SITENAME}}",
        "createaccount-text": "Alguien á criau una cuenta pa $2 en {{SITENAME}} ($4). La consínia pa \"$2\" es \"$3\".\nEberias entral ena tu cuenta i chambal la tu consínia.\n\nSi s'á criau la cuenta ebiu a angún marru, inora esti mensahi.",
        "loginlanguagelabel": "Palra: $1",
+       "pt-login": "Acedel",
+       "pt-createaccount": "Crial cuenta",
        "changepassword": "Chambal consínia",
        "resetpass_announce": "As entrau ena tu cuenta con una consínia temporal. Pol favol, escrebi una nueva consínia aquí:",
        "resetpass_text": "<!-- Aquí s´escrebi el testu -->",
        "undo-failure": "Nu es posibri eshazel la eición ebiu a que otru usuáriu á realizau una eición entelmeya.",
        "undo-norev": "La eición nu pué sel eshecha ebiu a que nu dessisti, u hue esborrá",
        "undo-summary": "Eshazel revisión $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|Caraba]])",
-       "cantcreateaccounttitle": "Nu es posibri crial la cuenta",
        "cantcreateaccount-text": "La criación de cuentas pol parti e la IP ('''$1''') á siu pará pol el usuáriu [[User:$3|$3]].\n\nLa razón dá pol $3 es ''$2''",
        "viewpagelogs": "Vel los rustrihus d´esta páhina",
        "nohistory": "Nu ai dengún estorial d´eicionis pa esta páhina.",
        "action-browsearchive": "landeal páginas esborrás",
        "action-undelete": "arrecuperal esta página",
        "nchanges": "$1 {{PLURAL:$1|chambu|chambus}}",
+       "enhancedrc-history": "Estorial",
        "recentchanges": "Úrtimus chambus",
        "recentchanges-legend": "Ocionis enos úrtimus chambus",
        "recentchanges-summary": "Sigui los úrtimus chambus d´esti güiqui nesta páhina.",
        "rcnotefrom": "Embahu se muestran los chambus hechus dendi el '''$2''' (hata el '''$1''').",
        "rclistfrom": "Muestral los chambus hechus dendi el $3 $2",
        "rcshowhideminor": "$1 eicionis chiqueninas",
+       "rcshowhideminor-hide": "Açonchal",
        "rcshowhidebots": "$1 bots",
+       "rcshowhidebots-show": "Muestral",
        "rcshowhideliu": "$1 usuárius rustrius",
+       "rcshowhideliu-hide": "Açonchal",
        "rcshowhideanons": "$1 usuárius anónimus",
+       "rcshowhideanons-hide": "Açonchal",
        "rcshowhidepatr": "$1 eicionis patrullás",
        "rcshowhidemine": "$1 las mis eicionis",
+       "rcshowhidemine-hide": "Açonchal",
        "rclinks": "Muestral los $1 úrtimus chambus enus $2 úrtimus dias<br />$3",
        "diff": "def",
        "hist": "estor",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|usuáriu está|usuárius están}} vehilandu]",
        "rc_categories": "Arrayal a categorias (separás pol \"|\")",
        "rc_categories_any": "Cualisquiá",
+       "rc-change-size-new": "$1{{PLURAL:$1|byte|bytes}} dempués el chambu",
        "newsectionsummary": "/* $1 */ seción nueva",
        "rc-enhanced-expand": "muestral detallis (es mestel JavaScript)",
        "rc-enhanced-hide": "Açonchal detallis",
        "tooltip-upload": "Prencipial a empuntal",
        "tooltip-rollback": "\"Reveltil\" esborra las eicionis hechas a esta página pol úrtimu usuáriu con un click",
        "tooltip-undo": "\"Esjadel\" revierti ésta eición i abri el mó eición en mó previsoreal.\nÉstu premiti añiil una radón al estorial.",
+       "tooltip-summary": "Escribi un brevi resumen",
        "anonymous": "{{PLURAL:$1|Ussuáriu anónimu|Ussuárius anónimus}} en {{SITENAME}}",
        "siteuser": "{{SITENAME}} usuáriu $1",
        "lastmodifiedatby": "Esta páhina se chambó pol úrtima vezi a las $2, el dia $1 pol $3.",
        "spambot_username": "MediaWiki limpia-spam",
        "spam_reverting": "Revirtiendu a la úrtima velsión que nu contenga atihus a $1",
        "spam_blanking": "Tolas revisionis tienin atihus a $1, branqueandu",
+       "simpleantispam-label": "Compreba anti-spam.\n<strong>nu</strong> rellene estu!",
        "markaspatrolleddiff": "Aseñalal cumu patrullau",
        "markaspatrolledtext": "Aseñalal esti artículu cumu patrullau",
        "markedaspatrolled": "Aseñalal cumu patrullau",
index c7272ed..01c2892 100644 (file)
@@ -55,7 +55,8 @@
                        "Hamisun",
                        "Matma Rex",
                        "4nn1l2",
-                       "Namo"
+                       "Namo",
+                       "Alifakoor"
                ]
        },
        "tog-underline": "خط کشیدن زیر پیوندها:",
@@ -82,7 +83,7 @@
        "tog-enotifusertalkpages": "هنگامی که در صفحهٔ بحث کاربری‌ام تغییری صورت می‌گیرد به من ایمیلی فرستاده شود",
        "tog-enotifminoredits": "برای تغییرات جزئی در صفحه‌ها و پرونده‌ها هم به من ایمیلی فرستاده شود",
        "tog-enotifrevealaddr": "نشانی پست الکترونیکی من در ایمیل‌های اطلاع‌رسانی هویدا گردد",
-       "tog-shownumberswatching": "شمار کاربران پی‌گیرندهٔ نمایش یابد",
+       "tog-shownumberswatching": "نمایشِ شمار کاربران پی‌گیری کننده",
        "tog-oldsig": "امضای کنونی:",
        "tog-fancysig": "امضا به صورت ویکی‌متن در نظر گرفته شود (بدون درج خودکار پیوند)",
        "tog-uselivepreview": "استفاده از پیش‌نمایش زنده",
        "tog-showhiddencats": "نمایش رده‌های پنهان",
        "tog-norollbackdiff": "بعد از واگردانی، تفاوت نشان داده نشود",
        "tog-useeditwarning": "زمان خروج از صفحهٔ ویرایش در صورت داشتن ویرایش‌های‌ ذخیره‌نشده به من هشدار داده شود",
-       "tog-prefershttps": "در حالت ورود به سامانه همواره از اتصال امن استفاده شود",
+       "tog-prefershttps": "هنگامی که به سامانه وارد شده‌ام، همواره از اتصال امن استفاده شود",
        "underline-always": "همیشه",
        "underline-never": "هرگز",
        "underline-default": "پیش‌فرض پوسته یا مرورگر",
        "tagline": "از {{SITENAME}}",
        "help": "راهنما",
        "search": "جستجو",
+       "search-ignored-headings": "#<!-- این صفحه را درست همانطور که هست رها کنید --> <pre>\n#سر‌فصل‌هایی که توسط تحقیق نادیده گرفته خواهندشد.‌\n#به محض اینکه صفحه با سرفصل، فهرست شده‌است،تغییرات متاثر می‌شود.\n#شما می‌توانید با انجام یک ویرایش پوچ صفحه را وادار به دوباره فهرست کردن کنید.\n#نحو به شرح زیر است:\n#  *همه چیز از یک خصیصهٔ \"#\" گرفته تا آخر خط، یک نظر است\n#  *هر خط بدون فاصله، عنوان دقیق برای نادیده گرفتن،موضوع و همه چیز منابع است\nاتصالات خارجی\nهمچنین مشاهده کنید\n#</pre> <!-- leave this line exactly as it is -->",
        "searchbutton": "جستجو",
        "go": "برو",
        "searcharticle": "برو",
        "yourpasswordagain": "تکرار گذرواژه:",
        "createacct-yourpasswordagain": "گذرواژه را دوباره وارد کنید",
        "createacct-yourpasswordagain-ph": "گذرواژه را وارد کنید برای بار دوم",
-       "remembermypassword": "گذرواژه را (تا حداکثر $1 {{PLURAL:$1|روز|روز}}) در این رایانه به خاطر بسپار",
        "userlogin-remembermypassword": "من را واردشده نگه‌دار",
        "userlogin-signwithsecure": "از ورود امن استفاده کنید",
        "cannotloginnow-title": "الان امکان وررود به سامانه نیست",
        "userlogin-resetpassword-link": "گذرواژه‌تان را فراموش کردید؟",
        "userlogin-helplink2": "کمک با ورود",
        "userlogin-loggedin": "شما در حال حاضر به عنوان {{GENDER:$1|$1}} وارد شده‌اید.\nاز فرم پایین برای ورود به عنوان یک کاربر دیگر استفاده کنید.",
+       "userlogin-reauth": "برای تأييد اینکه شما خود $1 هستيد، نیاز است دوباره وارد شويد.",
        "userlogin-createanother": "ایجاد یک حساب کاربری دیگر",
        "createacct-emailrequired": "نشانی ایمیل",
        "createacct-emailoptional": "نشانی ایمیل (اختیاری)",
        "createacct-email-ph": "نشانی ایمیل خود را وارد کنید",
        "createacct-another-email-ph": "آدرس ایمیل را وارد کنید",
        "createaccountmail": "استفاده از رمز عبور موقت تصادفی و ارسال آن به آدرس ایمیل مشخص شده",
+       "createaccountmail-help": "جهت ايجاد حساب برای شخص ديگری بدون دانستن گذرواژهٔ آن کاربرد دارد.",
        "createacct-realname": "نام واقعی (اختیاری)",
        "createaccountreason": "دلیل:",
        "createacct-reason": "دلیل",
        "createacct-reason-ph": "چرا شما حساب دیگری می‌سازید؟",
+       "createacct-reason-help": "پیامی که در سياههٔ ایجاد حساب نمایش داده می‌شود",
        "createacct-submit": "حسابتان را بسازید",
        "createacct-another-submit": "ایجاد حساب کاربری",
+       "createacct-continue-submit": "ادامهٔ ايجاد حساب",
+       "createacct-another-continue-submit": "ادامهٔ ايجاد حساب",
        "createacct-benefit-heading": "{{SITENAME}} توسط افرادی مانند شما ساخته شده است",
        "createacct-benefit-body1": "{{PLURAL:$1|ویرایش}}",
        "createacct-benefit-body2": "{{PLURAL:$1|صفحه}}",
        "nocookiesnew": "حساب کاربری ایجاد شد، اما شما وارد سامانه نشدید.\n{{SITENAME}} برای ورود کاربران به سامانه از کوکی استفاده می‌کند.\nشما کوکی‌ها را از کار انداخته‌اید.\nلطفاً کوکی‌ها را به کار بیندازید، و سپس با نام کاربری و گذرواژهٔ جدیدتان به سامانه وارد شوید.",
        "nocookieslogin": "{{SITENAME}} برای ورود کاربران به سامانه از کوکی‌ها استفاده می‌کند.\nشما کوکی‌ها را از کار انداخته‌اید.\nلطفاً کوکی‌ها را به کار بیندازید و دوباره امتحان کنید.",
        "nocookiesfornew": "حساب کاربری ساخته نشد، زیرا نتوانستیم منبع آن را تأیید کنیم.\nمطمئن شوید که کوکی‌ها فعال هستند، آن‌گاه صفحه را از نو بارگیری کنید و دوباره امتحان کنید.",
+       "createacct-loginerror": "حساب با موفقيت ايجاد شد، ليکن امکان ورود خودکار شما وجود ندارد. لطفاً با [[Special:UserLogin|manual login]] ادامه دهيد.",
        "noname": "شما نام کاربری معتبری مشخص نکرده‌اید.",
        "loginsuccesstitle": "ورود به سامانه",
        "loginsuccess": "'''شما اکنون با نام «$1» به {{SITENAME}} وارد شده‌اید.'''",
        "passwordreset-emailelement": "نام کاربری: \n$1\n\nگذرواژهٔ موقت: \n$2",
        "passwordreset-emailsentemail": "اگر نشانی پست الکترونیکی که وارد کردید برای حساب کاربریتان ثبت شده باشد، یک نامهٔ بازنشانی گذرواژه به آن فرستاده می‌شود.",
        "passwordreset-emailsentusername": "اگر نشانی پست الکترونیکی مرتبطی موجود باشد، یک نامه برای بازنشانی گذرواژه به آن ارسال خواهد شد.",
-       "passwordreset-emailsent-capture": "یک ایمیل بازنشانی که در پایین نمایش داده شده، فرستاده شده است.",
-       "passwordreset-emailerror-capture": "ایمیل بازنشانی، که در زیر نمایش داده شده، ایجاد شد، ولی ارسال آن به {{GENDER:$2|کاربر}} موفقیت‌آمیز نبود: $1",
+       "passwordreset-emailsent-capture2": "{{PLURAL:$1|نامهٔ|نامهٔ}} بازنشانی گذرواژه ارسال شد. در زير $1 نام کاربری و گذرواژه در حال نمایش است.",
+       "passwordreset-emailerror-capture2": "ارسال ناموفق نامه به $2: $1\nدر زير $3 نام کاربری و گذرواژه در حال نمایش است",
+       "passwordreset-nocaller": "فراخواننده می‌بايست مشخص شده باشد",
+       "passwordreset-nosuchcaller": "اين فراخواننده وجود ندارد:$1",
+       "passwordreset-ignored": "به بازنشانی گذرواژه پرداخته نشد. آیا ممکن است که هيچ مهياکننده‌ای برای این کار تنظيم نشده باشد؟",
+       "passwordreset-invalideamil": "آدرس ایمیل نامعتبر",
+       "passwordreset-nodata": "یک نام کاربری و یا یک آدرس ايميل، هيچکدام ارائه نشده",
        "changeemail": "تغییر یا حذف نشانی ایمیل",
        "changeemail-header": "برای تغییر ایمیلتان این فرم را کامل کنید. برای حذف ایملیتان کافی است بخش ایمیل را خالی رها کنید و فرم را ارسال کنید.",
-       "changeemail-passwordrequired": "برای تائید این تغییر باید گذرواژه‌تان را وارد کنید.",
        "changeemail-no-info": "برای دسترسی مستقیم به این صفحه شما باید به سامانه وارد شده باشید.",
        "changeemail-oldemail": "آدرس ایمیل کنونی:",
        "changeemail-newemail": "آدرس ایمیل جدید:",
        "minoredit": "این ویرایش، جزئی است",
        "watchthis": "پی‌گیری این صفحه",
        "savearticle": "صفحه ذخیره شود",
+       "savechanges": "ذخیره کردن تغییرات",
        "publishpage": "انتشار صفحه",
+       "publishchanges": "انتشار تغییرات",
        "preview": "پیش‌نمایش",
        "showpreview": "پیش‌نمایش",
        "showdiff": "نمایش تغییرات",
        "anontalkpagetext": "----<em>این صفحهٔ بحث برای کاربر گمنامی است که هنوز حسابی درست نکرده است یا از آن استفاده نمی‌کند.\nبنا بر این برای شناسایی‌اش مجبوریم از نشانی آی‌پی عددی استفاده کنیم.</em>\nچنین نشانی‌های آی‌پی ممکن است توسط چندین کاربر به شکل مشترک استفاده شود.\nاگر شما کاربر گمنامی هستید و تصور می‌کنید اظهار نظرات نامربوط به شما صورت گرفته است، لطفاً برای پیشگیری از اشتباه گرفته شدن با کاربران گمنام دیگر در آینده [[Special:CreateAccount|حسابی ایجاد کنید]] یا [[Special:UserLogin|به سامانه وارد شوید]].",
        "noarticletext": "این صفحه هم‌اکنون دارای هیچ متنی نیست.\nشما می‌توانید در صفحه‌های دیگر [[Special:Search/{{PAGENAME}}|عنوان این صفحه را جستجو کنید]]،\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} سیاهه‌های مرتبط را جستجو کنید]،\nیا [{{fullurl:{{FULLPAGENAME}}|action=edit}} این صفحه را ایجاد کنید]</span>.",
        "noarticletext-nopermission": "این صفحه هم‌اکنون متنی ندارد.\nشما می‌توانید در دیگر صفحات [[Special:Search/{{PAGENAME}}|این عنوان را جستجو کنید]]،\nیا <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} سیاهه‌های مرتبط را بگردید]</span> ولی شما اجازه ایجاد این صفحه را ندارید.",
-       "missing-revision": "ویرایش #$1 از صفحهٔ «{{FULLPAGENAME}}» موجود نیست.\n\nمعمولاً در اثر پیوند به تاریخچهٔ به‌روز نشدهٔ صفحهٔ حذف شده است.\nمی‌توانید جزئیات بیشتر را در [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سیاههٔ حذف] بیابید.",
+       "missing-revision": "ویرایش #$1 از صفحهٔ «{{FULLPAGENAME}}» موجود نیست.\n\nاین اتفاق معمولاً در اثر دنبال کردن پیوندی به تاریخچهٔ یک صفحهٔ حذف‌شده پیش می‌آید.\nمی‌توانید جزئیات بیشتر را در [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سیاههٔ حذف] بیابید.",
        "userpage-userdoesnotexist": "حساب کاربر «<nowiki>$1</nowiki>» ثبت نشده‌است.\nلطفاً مطمئن شوید که می‌خواهید این صفحه را ایجاد یا ویرایش کنید.",
        "userpage-userdoesnotexist-view": "حساب کاربری «$1» ثبت نشده‌است.",
        "blocked-notice-logextract": "دسترسی این کاربر در حال حاضر بسته است.\nآخرین مورد سیاهه قطع دسترسی در زیر آمده‌است:",
        "content-model-css": "سی‌اس‌اس",
        "content-json-empty-object": "ابجکت خالی",
        "content-json-empty-array": "آرایهٔ خالی",
+       "deprecated-self-close-category": "صفحه از برچسب اچ‌تی‌ام‌ال self-closed غیرمجاز استفاده می‌کند",
+       "deprecated-self-close-category-desc": "صفحه دارای برچسب اچ‌تی‌ام‌ال self-closed است مانند <code>&lt;b/></code> یا <code>&lt;span/></code>. عملکرد این برچسب‌ها در اچ‌تی‌ام‌ال۵ تغییر می‌کند در نتیجه استفاده از آنها در ویکی‌متن منسوخ و بی‌اثر است.",
        "duplicate-args-warning": "<strong>هشدار:</strong> [[:$1]] [[:$2]] را با بیش از یک مقدار برای پارامتر «$3» صدا می‌زند. فقط آخرین مقدار داده شده استفاده خواهد شد.",
        "duplicate-args-category": "صفحه‌های دارای آرگومان تکراری در فراخوانی الگو",
        "duplicate-args-category-desc": "صفحاتی که دارای آرگومان تکراری هستند مانند، <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> یا <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-success": "این ویرایش را می‌توان خنثی کرد.\nلطفاً تفاوت زیر را بررسی کنید تا تأیید کنید که این چیزی است که می‌خواهید انجام دهید، سپس تغییرات زیر را ذخیره کنید تا خنثی‌سازی ویرایش را به پایان ببرید.",
        "undo-failure": "به علت تعارض با ویرایش‌های میانی، این ویرایش را نمی‌توان خنثی کرد.",
        "undo-norev": "این ویرایش را نمی‌توان خنثی کرد چون وجود ندارد یا حذف شده‌است.",
-       "undo-nochange": "به نظر می‌رسد ویرایش از پیش واگردانی شده است.",
+       "undo-nochange": "به نظر می‌رسد ویرایش از پیش خنثی‌سازی شده است.",
        "undo-summary": "خنثی‌سازی ویرایش $1 توسط [[Special:Contributions/$2|$2]] ([[User talk:$2|بحث]])",
        "undo-summary-username-hidden": "خنثی‌سازی نسخهٔ $1 به دست یک کاربر پنهان‌شده",
-       "cantcreateaccounttitle": "نمی‌توان حساب باز کرد",
        "cantcreateaccount-text": "امكان ساختن حساب کاربری از این این نشانی آی‌پی ('''$1''') توسط [[User:$3|$3]] سلب شده است.\n\nدلیل ارائه شده توسط $3 چنین است: $2",
        "cantcreateaccount-range-text": "ایجاد حساب از آدرس آی‌پی در مجموعه‌ی <strong>$1</strong>، که شامل آدرس آی‌پی شما (<strong>$4</strong>) است، توسط [[User:$3|$3]] متوقف شده‌است.\nدلیل ارائه شده توسط $3، $2 است.",
        "viewpagelogs": "نمایش سیاهه‌های این صفحه",
        "diff-multi-sameuser": "({{PLURAL:$1|یک نسخهٔ میانی|$1 نسخهٔ میانی}}ِ همین کاربر نمایش داده نشده است)",
        "diff-multi-otherusers": "({{PLURAL:$1|۱ نسخهٔ میانی|$1 نسخه‌ٔ میانی}} ویرایش شده توسط {{PLURAL:$2|۱ کاربر|$2 کاربر}} نشان داده نشده)",
        "diff-multi-manyusers": "({{PLURAL:$1|یک|$1}} نسخه‌ٔ میانی ویرایش شده توسط بیش از {{PLURAL:$2|یک|$2}} کاربر نشان داده نشده است)",
-       "difference-missing-revision": "{{PLURAL:$2|یک ویرایش|$2 ویرایش}}  از تفاوت نسخه‌ها ($1) {{PLURAL:$2|یافت|یافت}}  نشد.\n\nمعمولاً در اثر پیوند به تاریخچهٔ به‌روز نشدهٔ صفحهٔ حذف شده است.\nمی‌توانید جزئیات بیشتر را در [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سیاههٔ حذف] بیابید.",
+       "difference-missing-revision": "{{PLURAL:$2|یک ویرایش|$2 ویرایش}}  از تفاوت نسخه‌ها ($1) {{PLURAL:$2|یافت|یافت}}  نشد.\n\nاین اتفاق معمولاً در اثر دنبال کردن پیوند تفاوتی به یک صفحهٔ حذف‌شده پیش می‌آید.\nمی‌توانید جزئیات بیشتر را در [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سیاههٔ حذف] بیابید.",
        "searchresults": "نتایج جستجو",
        "searchresults-title": "نتایج جستجو برای «$1»",
        "titlematches": "تطبیق عنوان مقاله",
        "right-block": "قطع دسترسی ویرایشی دیگر کاربران",
        "right-blockemail": "قطع دسترسی دیگر کاربران برای ارسال ایمیل",
        "right-hideuser": "قطع دسترسی کاربر و پنهان کردن آن از دید عموم",
-       "right-ipblock-exempt": "تاثیر نپذیرفتن از قطع دسترسی‌های آی‌پی، خودکار یا فاصله‌ای",
+       "right-ipblock-exempt": "تأثیر نپذیرفتن از قطع دسترسی‌های آی‌پی، خودکار یا فاصله‌ای",
        "right-unblockself": "بازکردن دسترسی خود",
        "right-protect": "تغییر میزان محافظت صفحات و ویرایش صفحات محافظت‌شده آبشاری",
        "right-editprotected": "ویرایش صفحه‌های محافظت‌شده به عنوان «{{int:protect-level-sysop}}»",
        "grant-group-high-volume": "انجام فعالیت‌های حجم بالا",
        "grant-group-customization": "سفارشی‌سازی و تنظیمات",
        "grant-group-administration": "انجام اقدامات مدیریتی",
+       "grant-group-private-information": "دسترسی به اطلاعات محرمانهٔ خودتان",
        "grant-group-other": "فعالیت‌های متفرقه",
        "grant-blockusers": "بستن و باز کردن کاربرها",
        "grant-createaccount": "ایجاد حساب‌های کاربری",
        "grant-highvolume": "ویرایش با حجم بالا",
        "grant-oversight": "پنهان کردن ویرایش‌ها",
        "grant-patrol": "تغییرات گشت صفحات",
+       "grant-privateinfo": "دسترسی به اطلاعات محرمانه",
        "grant-protect": "حفاظت و عدم حفاظت صفحات",
        "grant-rollback": "واگردانی  تغییرات صفحات",
        "grant-sendemail": "ارسال ایمیل به دیگر کاربران",
        "action-applychangetags": "اعمال برچسب بر روی تغییرات شما",
        "action-changetags": "افزودن یا حذف برچسب قراردادی بر روی نسخه یا سیاهه ورودی‌ها",
        "action-deletechangetags": "حذف برچسب‌ها از پایگاه داده",
+       "action-purge": "خالی‌کردن میانگیر این صفحه",
        "nchanges": "$1 تغییر",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|از آخرین بازدید}}",
        "enhancedrc-history": "تاریخچه",
        "upload-http-error": "یک خطای اچ‌تی‌تی‌پی رخ داد: $1",
        "upload-copy-upload-invalid-domain": "بارگذاری کپی پرونده‌ها از این دامنه امکان‌پذیر نیست.",
        "upload-foreign-cant-upload": "این ویکی برای بارگذاری پرونده ها در مخزن پرونده های خارجی درخواست شده پیکربندی نشده است.",
+       "upload-foreign-cant-load-config": "خواندن تنظيمات مربوط به بارگذاری پرونده در مخزن خارجی با شکست مواجه شد",
+       "upload-dialog-disabled": "در اين ويکی، بارگذاری پرونده‌هااز طريق اين رابط کاربری غير فعال است",
        "upload-dialog-title": "بارگذاری پرونده",
        "upload-dialog-button-cancel": "لغو",
        "upload-dialog-button-done": "انجام شد",
        "uploadstash-errclear": "پاک‌کردن پرونده‌ها ناموفق بود.",
        "uploadstash-refresh": "تازه کردن فهرست پرونده‌ها",
        "uploadstash-thumbnail": "نمایش بندانگشتی",
+       "uploadstash-exception": "ناتوان از ذخیره کردن بارگذاری در نهانگاه ($1): ''$2''.",
        "invalid-chunk-offset": "جابجایی نامعتبر قطعه",
        "img-auth-accessdenied": "منع دسترسی",
        "img-auth-nopathinfo": "PATH_INFO موجود نیست.\nسرور شما برای ردکردن این مقدار تنظیم نشده‌است.\nممکن است مبتنی بر سی‌جی‌آی باشد و از img_auth پشتیبانی نکند.\nhttps://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization را ببینید.",
        "allinnamespace": "همهٔ صفحات (فضای نام $1)",
        "allpagessubmit": "برو",
        "allpagesprefix": "نمایش صفحه‌های دارای پیشوند:",
-       "allpagesbadtitle": "عÙ\86Ù\88اÙ\86 ØµÙ\81Ø­Ù\87Ù\94 Ø¯Ø§Ø¯Ù\87â\80\8cشدÙ\87 Ù\86اÙ\85عتبر Ø§Ø³Øª Û\8cا Ø§Û\8cÙ\86Ú©Ù\87 Ø¯Ø§Ø±Ø§Û\8c Ù¾Û\8cØ´Ù\88Ù\86دÛ\8c Ø¨Û\8cÙ\86â\80\8cزباÙ\86Û\8c Û\8cا Ø¨Û\8cÙ\86â\80\8cÙ\88Û\8cÚ©Û\8câ\80\8cاÛ\8c Ø§Ø³Øª. Ù\85Ù\85Ú©Ù\86 Ø§Ø³Øª Ù\86Ù\88Û\8cسÙ\87â\80\8cÙ\87اÛ\8cÛ\8c Ø¨Ø¯Ø§Ø±Ø¯ Ú©Ù\87 Ù\86Ù\85Û\8câ\80\8cتÙ\88اÙ\86 Ø§Ø² Ø¢Ù\86Ù\87ا در عنوان صفحات استفاده کرد.",
+       "allpagesbadtitle": "عÙ\86Ù\88اÙ\86 ØµÙ\81Ø­Ù\87Ù\94 Ø¯Ø§Ø¯Ù\87â\80\8cشدÙ\87 Ù\86اÙ\85عتبر Ø§Ø³Øª Û\8cا Ø§Û\8cÙ\86Ú©Ù\87 Ø¯Ø§Ø±Ø§Û\8c Ù¾Û\8cØ´Ù\88Ù\86دÛ\8c Ø¨Û\8cÙ\86â\80\8cزباÙ\86Û\8c Û\8cا Ø¨Û\8cÙ\86â\80\8cÙ\88Û\8cÚ©Û\8câ\80\8cاÛ\8c Ø§Ø³Øª. Ù\85Ù\85Ú©Ù\86 Ø§Ø³Øª Ù\86Ù\88Û\8cسÙ\87â\80\8cÙ\87اÛ\8cÛ\8c Ø¯Ø§Ø´ØªÙ\87 Û\8cاشد Ú©Ù\87 Ø§Ø² Ø¢Ù\86Ù\87ا Ù\86Ù\85Û\8câ\80\8cتÙ\88اÙ\86 در عنوان صفحات استفاده کرد.",
        "allpages-bad-ns": "{{SITENAME}} دارای فضای نام «$1» نیست.",
        "allpages-hide-redirects": "پنهان‌کردن تغییرمسیرها",
        "cachedspecial-viewing-cached-ttl": "شما در حال مشاهدهٔ نسخه‌ای از این صفحه که در میانگیر قرار دارد هستید که ممکن است برای $1 قبل باشد.",
        "trackingcategories-msg": "ردهٔ ردیابی",
        "trackingcategories-name": "نام پیام",
        "trackingcategories-desc": "معیارهای گنجایش رده",
+       "restricted-displaytitle-ignored": "صفحه‌ها باعنوان نمایشی نادیده‌گرفته‌شده",
+       "restricted-displaytitle-ignored-desc": "دراين صفحه <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> بی‌اهميت تلقی گرديده‌است، چراکه با عنوان اصلی صفحه یکي نيست.",
        "noindex-category-desc": "این صفحه توسط ربات‌ها فهرست‌نشده‌است به این دلیل که واژه جادویی <code><nowiki>__NOINDEX__</nowiki></code> در آن یا در فضای که پرچم مجاز است دارد.",
        "index-category-desc": "این صفحه <code><nowiki>__INDEX__</nowiki></code> درونش دارد (و در فضای نامی است که پرچم مجاز است)، و به این دلیل توسط ربات مجاز است که به‌صورت عادی نباید می‌شد.",
        "post-expand-template-inclusion-category-desc": "پس از گسترش همهٔ الگوها، حجم صفحه بزرگتر از <code>$wgMaxArticleSize</code> است بنابراین بعضی از الگو گسترش نیافته‌اند.",
        "watchnologin": "به سامانه وارد نشده‌اید",
        "addwatch": "افزودن به فهرست پی‌گیری",
        "addedwatchtext": "«[[:$1]]» و صفحهٔ بحث آن به [[Special:Watchlist|فهرست پی‌گیری‌های]] شما اضافه شد.",
+       "addedwatchtext-talk": "''[[:$1]]'' و صفحهٔ مرتبط به آن به [[Special:Watchlist|فهرست پی‌گیری]] شما افزوده شدند.",
        "addedwatchtext-short": "صفحه \" $1 \" به فهرست پیگیریهای خود اضافه شده است.",
        "removewatch": "حذف از فهرست پی‌گیری",
        "removedwatchtext": "صفحهٔ «[[:$1]]» و صفحهٔ بحث آن از [[Special:Watchlist|فهرست پی‌گیری‌های شما]] برداشته شد.",
+       "removedwatchtext-talk": "''[[:$1]]'' و صفحهٔ مرتبط به آن از [[Special:Watchlist|فهرست پی‌گیری]] شما حذف شدند.",
        "removedwatchtext-short": "صفحهٔ \"$1\" از فهرست پیگیری‌های شما حذف شده‌است.",
        "watch": "پی‌گیری",
        "watchthispage": "پی‌گیری این صفحه",
        "notvisiblerev": "آخرین نسخه توسط کاربری دیگر حذف شده‌است",
        "watchlist-details": "بدون احتساب صفحه‌های جداگانهٔ بحث، {{PLURAL:$1|$1 صفحه|$1 صفحه}} در فهرست پی‌گیری‌های شما قرار {{PLURAL:$1|دارد|دارند}}.",
        "wlheader-enotif": "ایمیل‌های اعلان فعال است.",
-       "wlheader-showupdated": "صفحه‌هایی که پس از آخرین بازدید شما تغییر کرده‌اند '''پررنگ''' نمایش داده شده‌اند.",
+       "wlheader-showupdated": "صفحه‌هایی که پس از آخرین بازدید شما تغییر کرده‌اند <strong>پررنگ</strong> نمایش داده شده‌اند.",
        "wlnote": "در زیر {{PLURAL:$1|تغییری|<strong>$1</strong> تغییری}} که در {{PLURAL:$2|ساعت|<strong>$2</strong> ساعت}} گذشته انجام شده موجود است، تاریخ آخرین بازیابی: $3، $4",
        "wlshowlast": "نمایش آخرین $1 ساعت $2 روز",
        "watchlist-hide": "نهفتن",
        "actionfailed": "عمل ناموفق بود",
        "deletedtext": "«$1» حذف شد.\nبرای سابقهٔ حذف‌های اخیر به $2 مراجعه کنید.",
        "dellogpage": "سیاههٔ حذف",
-       "dellogpagetext": "فهرست زیر فهرستی از آخرین حذف‌هاست.\nهمهٔ زمان‌های نشان‌داده‌شده زمان خادم (وقت گرینویچ) است.",
+       "dellogpagetext": "فهرست زیر فهرستی از آخرین حذف‌هاست.\nهمهٔ زمان‌های نشان‌داده‌شده زمان کارساز (وقت گرینویچ) است.",
        "deletionlog": "سیاههٔ حذف",
        "reverted": "به نسخهٔ قدیمی‌تر واگردانده شد",
        "deletecomment": "دلیل:",
        "rollbacklinkcount": "واگردانی $1 ویرایش",
        "rollbacklinkcount-morethan": "واگردانی بیش از $1 ویرایش",
        "rollbackfailed": "واگردانی نشد",
+       "rollback-missingparam": "فقدان پارامترهای ضروری در درخواست",
+       "rollback-missingrevision": "ناتوان از بارگیری اطلاعات نسخه.",
        "cantrollback": "نمی‌توان ویرایش را واگرداند؛\nآخرین مشارکت‌کننده تنها مؤلف این مقاله است.",
        "alreadyrolled": "واگردانی آخرین ویرایش [[:$1]] توسط [[User:$2|$2]] ([[User talk:$2|بحث]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) ممکن نیست؛\nپیش از این شخص دیگری مقاله را ویرایش یا واگردانی کرده‌است.\n\nآخرین ویرایش توسط [[User:$3|$3]] ([[User talk:$3|بحث]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) انجام شده‌است.",
        "editcomment": "خلاصهٔ ویرایش این بود:  <em>«$1»</em>.",
        "changecontentmodel-success-text": "نوع محتوی [[:$1]]  تغییر یافت",
        "changecontentmodel-cannot-convert": "محتوی در [[:$1]] نمی‌تواند به گونه‌ای از $2 تبدیل شود.",
        "changecontentmodel-nodirectediting": "نمونه محتوی $1 امکان ویرایش مستقیم را پشتیبانی نمی‌کند",
+       "changecontentmodel-emptymodels-title": "هيچ مدل محتوایی در دسترس نيست",
+       "changecontentmodel-emptymodels-text": "محتوای موجود در [[:$1]] به هيچ نوعی نمی‌تواند تبديل شود.",
        "log-name-contentmodel": "سیاهه تغییر نمونه محتوی",
        "log-description-contentmodel": "رویدادهای مرتبط با نمونه محتوی‌های یک صفحه",
        "logentry-contentmodel-new": "صفحهٔ $3 با استفاده از مدل‌های محتوایی غیر پیش‌فرض «$5» توسط $1 {{GENDER:$2|ساخته شد}}",
        "protect-default": "همهٔ کاربرها",
        "protect-fallback": "فقط به کاربرهایی که دسترسی «$1» دارند، اجازه داده می‌شود",
        "protect-level-autoconfirmed": "تنها کاربران تأییدشده",
-       "protect-level-sysop": "فقط مدیران",
+       "protect-level-sysop": "تنها مدیران",
        "protect-summary-cascade": "آبشاری",
        "protect-expiring": "زمان سرآمدن $1 (UTC)",
        "protect-expiring-local": "منقضی $1",
        "undeletehistorynoadmin": "این مقاله حذف شده‌است.\nدلیل حذف این مقاله به همراه مشخصات کاربرانی که قبل از حذف این صفحه را ویرایش کرده‌اند، در خلاصهٔ زیر آمده‌است.\nمتن واقعی این ویرایش‌های حذف شده فقط در دسترس مدیران است.",
        "undelete-revision": "نسخهٔ حذف شدهٔ $1 (به تاریخ $4 ساعت $5) توسط $3:",
        "undeleterevision-missing": "نسخه نامعتبر یا مفقود است.\nممکن است پیوندتان نادرست باشد یا اینکه نسخه از بایگانی حذف یا بازیابی شده باشد .",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|یک نسخه|$1 نسخه}} احیا نشد، چون <code>rev_id</code> آن {{PLURAL:$1|نسخه|نسخه‌ها}} از پیش مورد استفاده بود.",
        "undelete-nodiff": "نسخهٔ قدیمی‌تری یافت نشد.",
        "undeletebtn": "احیا",
        "undeletelink": "نمایش/احیا",
        "undeletedrevisions": "$1 نسخه احیا {{PLURAL:$1|شد}}",
        "undeletedrevisions-files": "$1 نسخه و $2 پرونده احیا {{PLURAL:$1|شد|شدند}}.",
        "undeletedfiles": "$1 پرونده احیا {{PLURAL:$1|شد|شدند}}.",
-       "cannotundelete": "احیا ناموفق بود:\n$1",
+       "cannotundelete": "تÙ\85اÙ\85 Û\8cا Ø¨Ø®Ø´Û\8c Ø§Ø² Ø§Ø­Û\8cا Ù\86اÙ\85Ù\88Ù\81Ù\82 Ø¨Ù\88د:\n$1",
        "undeletedpage": "'''$1 احیا شد'''\n\nبرای دیدن سیاههٔ حذف‌ها و احیاهای اخیر به  [[Special:Log/delete|سیاههٔ حذف]] رجوع کنید.",
        "undelete-header": "برای دیدن صفحه‌های حذف‌شدهٔ اخیر [[Special:Log/delete|سیاههٔ حذف]] را ببینید.",
        "undelete-search-title": "جستجوی صفحه‌های حذف‌شده",
        "sp-contributions-newbies-sub": "برای تازه‌کاران",
        "sp-contributions-newbies-title": "مشارکت‌های کاربری برای حساب‌های تازه‌کار",
        "sp-contributions-blocklog": "سیاههٔ بسته‌شدن‌ها",
-       "sp-contributions-suppresslog": "مشارکت‌های فرونشانی‌شده",
-       "sp-contributions-deleted": "مشارکت‌های حذف‌شدهٔ کاربر",
+       "sp-contributions-suppresslog": "مشارکت‌های فرونشانی‌شده {{GENDER:$1|کاربر}}",
+       "sp-contributions-deleted": "مشارکت‌های حذف‌شدهٔ {{GENDER:$1|کاربر}}",
        "sp-contributions-uploads": "بارگذاری‌ها",
        "sp-contributions-logs": "سیاهه‌ها",
        "sp-contributions-talk": "بحث",
        "invalidateemail": "لغو تأیید نشانی ایمیل",
        "notificationemail_subject_changed": "نشانی ایمیل ثبت شدهٔ {{SITENAME}} تغییر یافته است",
        "notificationemail_subject_removed": "نشانی ایمیل ثبت شدهٔ {{SITENAME}} حذف شده است",
+       "notificationemail_body_changed": "کسی، شايد خود شما، از آدرس اينترنتی $1، آدرس ايميل حساب $2 در {{SITENAME}} را به $3 تغيير داده است.\n\nاگر آن شخص شما نبوده‌ايد، فوراً با یک مدير سایت تماس بگيريد.",
+       "notificationemail_body_removed": "کسی، شايد خود شما، از آدرس اينترنتی $1، آدرس ايميل حساب $2 در {{SITENAME}} را حذف کرده است.\n\nاگر آن شخص شما نبوده‌ايد، فوراً با یک مدير سایت تماس بگيريد.",
        "scarytranscludedisabled": "[تراگنجانش بین‌ویکیانه فعال نیست]",
        "scarytranscludefailed": "[فراخوانی الگو برای $1 میسر نشد]",
        "scarytranscludefailed-httpstatus": "[فراخوانی الگو برای $1 میسر نشد: خطای اچ‌تی‌تی‌پی $2]",
        "confirmrecreate-noreason": "کاربر [[User:$1|$1]] ([[User talk:$1|بحث]]) این صفحه را پس از شروع ویرایش‌تان {{GENDER:$1|پاک}} کرده‌است.  لطفاً تأیید کنید که شما واقعاً می‌خواهید آن را دوباره ایجاد کنید.",
        "recreate": "باز ایجاد",
        "confirm_purge_button": "تأیید",
-       "confirm-purge-top": "پاککردن نسخهٔ حافظهٔ نهانی (Cache) این صفحه را تأیید می‌کنید؟",
+       "confirm-purge-top": "پاک کردن نسخهٔ حافظهٔ نهانی (Cache) این صفحه را تأیید می‌کنید؟",
        "confirm-purge-bottom": "خالی کردن میانگیر یک صفحه باعث می‌شود که آخرین نسخهٔ آن نمایش یابد.",
        "confirm-watch-button": "تأیید",
        "confirm-watch-top": "این صفحه به فهرست پی‌گیری‌های شما افزوده شود؟",
        "timezone-local": "محلی",
        "duplicate-defaultsort": "هشدار: ترتیب پیش‌فرض «$2» ترتیب پیش‌فرض قبلی «$1» را باطل می‌کند.",
        "duplicate-displaytitle": "<strong>هشدار:</strong> نمایش عنوان \" $2 \"باعث ابطال پیش نمایش عنوان\" $1 \" می‌شود.",
+       "restricted-displaytitle": "<strong>هشدار:</strong> از آنجايي که عنوان نمایشی «$1» با عنوان اصلی صفحه یکی نبود، مورد اغماز قرار گرفت.",
        "invalid-indicator-name": "<strong>خطا:</strong>ویژگی های شاخص‌های وضعیت صفحهٔ <code>name</code> نباید خالی باشند.",
        "version": "نسخه",
        "version-extensions": "افزونه‌های نصب‌شده",
        "logentry-protect-modify-cascade": "$1 سطح حفاظت برای $3 $4 را {{GENDER:$2|تغییر داد}}[آبشاری]",
        "logentry-rights-rights": "$1 دسترسی $3 را از گروه $4 به $5 تغییر داد",
        "logentry-rights-rights-legacy": "$1 گروه عضویت $3 را {{GENDER:$2|تغییر داد}}",
-       "logentry-rights-autopromote": "$1 به طور خودکار از $4 به $5 {{GENDER:$2|ارتقاء داد}}",
+       "logentry-rights-autopromote": "$1 به طور خودکار از $4 به $5 {{GENDER:$2|ارتقاء یافت}}",
        "logentry-upload-upload": "$1 $3 را {{GENDER:$2|بارگذاری کرد}}",
        "logentry-upload-overwrite": "$1 نسخهٔ تازه‌ای از $3 را {{GENDER:$2|بارگذاری کرد}}",
        "logentry-upload-revert": "$1 {{GENDER:$2|بارگذاری کرد}} $3",
        "api-error-nomodule": "خطای داخلی: پودمان بارگذاری تنظیم نشده‌است.",
        "api-error-ok-but-empty": "خطای داخلی : پاسخی از سرور دریافت نشد.",
        "api-error-overwrite": "جای نوشتن یک پرونده موجود مجاز نیست.",
+       "api-error-ratelimited": "شما سعی داريد، در بازه زمانی کوتاهی، بيشتر از تعدادی که اين ويکی اجازه داده‌است پرونده بارگذاری کنيد.\nلطفاً چند دقيقه بعد مجدداً تلاش نماييد.",
        "api-error-stashfailed": "خطای داخلی: کارساز نمی‌تواند پرونده موقت را ذخیره کند.",
        "api-error-publishfailed": "خطای داخلی: کارساز نمی‌تواند پرونده موقت را ذخیره کند.",
        "api-error-stasherror": "هنگام انتقال پوشه برای ذخیره خطایی بود.",
        "api-error-unknownerror": "خطای ناشناخته: «$1».",
        "api-error-uploaddisabled": "بارگذاری در این ویکی غیرفعال است.",
        "api-error-verification-error": "ممکن است پرونده آسیب دیده باشد، یا دارای پسوند نادرست باشد.",
+       "api-error-was-deleted": "پرونده‌ای با همين نام قبلاً بارگذاری و متعاقباً حذف شده است.",
        "duration-seconds": "$1 ثانیه",
        "duration-minutes": "$1 دقیقه",
        "duration-hours": "$1 ساعت",
        "mw-widgets-dateinput-no-date": "هیچ داده‌ای انتخاب نشده",
        "mw-widgets-titleinput-description-new-page": "این صفحه هنوز وجود ندارد",
        "mw-widgets-titleinput-description-redirect": "تغییر مسیر به $1",
-       "api-error-blacklisted": "لطفاً یک عنوان توصیفی متفاوت انتخاب کنید.",
        "sessionmanager-tie": "نمی‌توان چندین نوع درخواست هویت‌سنجی را ترکیب کرد: $1.",
        "sessionprovider-generic": "$1 فصل",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "فصل‌های کوکی‌محور",
        "log-action-filter-newusers": "نوع ایجاد حساب",
        "log-action-filter-patrol": "نوع گشت:",
        "log-action-filter-protect": "نوع محافظت",
-       "log-action-filter-rights": "روش تغییر صحیح",
-       "log-action-filter-suppress": "روش سرکوب",
+       "log-action-filter-rights": "روش تغییر صحیح:",
+       "log-action-filter-suppress": "روش سرکوب:",
        "log-action-filter-upload": "نوع بارگذاری",
        "log-action-filter-all": "همه",
        "log-action-filter-block-block": "بستن",
        "log-action-filter-delete-event": "حذف سیاهه",
        "log-action-filter-delete-revision": "حذف ویرایش",
        "log-action-filter-import-interwiki": "ورودی ترانسویکی",
+       "log-action-filter-import-upload": "درون‌ریزی به کمک بارگذاری XML",
        "log-action-filter-managetags-create": "ایجاد تگ",
        "log-action-filter-managetags-delete": "حذف کردن تگ",
        "log-action-filter-managetags-activate": "فعالسازی تگ",
        "log-action-filter-suppress-reblock": "مخفی‌سازی کاربر با بستن مجدد",
        "log-action-filter-upload-upload": "بارگذاری جدید",
        "log-action-filter-upload-overwrite": "بارگذاری دوباره",
+       "authmanager-authn-not-in-progress": "ارزیابی ورود در جريان نيست یا اطلاعات جلسه کاری از بين رفته است. لطفاً دوباره از ابتدا شروع کنيد.",
+       "authmanager-authn-no-primary": "اعتبارسنجی اطلاعات ارائه شده جهت ورود ميسر نيست",
+       "authmanager-authn-no-local-user": "اطلاعات ورود ارائه شده با هيچ کاربری در اين ويکی مرتبط نيست",
+       "authmanager-authn-no-local-user-link": "اطلاعات اعتبارسنجی ارائه شده معتبرند، ليکن با هيچ کاربری در اين ويکی مرتبط نشده‌اند. از طريق ديگری وارد شويد و یا یک حساب جديد بسازيد. شما امکان اتصال اطلاعات اعتبار‌سنجی قبلی را با آن حساب خواهيد داشت.",
+       "authmanager-authn-autocreate-failed": "ايجاد خودکار یک حساب بومی با شکست مواجه شد:$1",
+       "authmanager-change-not-supported": "تغییر پشتیبانی از اعتبارنامه امکان‌پذیر نیست، در صورتی که هیچ چیز از آنها استفاده نمی‌کند.",
        "authmanager-create-disabled": "قابلیت ایجاد حساب غیرفعال است",
+       "authmanager-create-from-login": "برای ايجاد حساب، قسمت‌های زير را تکميل کنيد.",
+       "authmanager-create-not-in-progress": "ايجاد حساب در جريان نيست یا اطلاعات جلسه کاری از بين رفته است. لطفاً دوباره از ابتدا شروع کنيد.",
+       "authmanager-create-no-primary": "اطلاعات اعتبارسنجی ارائه شده نمی‌تواند جهت ايجاد حساب بکار رود.",
+       "authmanager-link-no-primary": "اطلاعات اعتبارسنجی ارائه شده نمی‌تواند جهت مرتبط کردن حساب بکار رود.",
+       "authmanager-link-not-in-progress": "مرتبط کردن حساب در جريان نيست یا اطلاعات جلسه کاری از بين رفته است. لطفاً دوباره از ابتدا شروع کنيد.",
        "authmanager-authplugin-setpass-failed-title": "تغییر گذرواژه ناموفق بود",
+       "authmanager-authplugin-setpass-failed-message": "افزونهٔ اعتبارسنجی، تغيير گذزواژه را مردود دانست",
+       "authmanager-authplugin-create-fail": "افزونهٔ اعتبارسنجی، ايجاد حساب را مردود دانست",
+       "authmanager-authplugin-setpass-denied": "افزونهٔ اعتبارسنجی اجازهٔ تغيير گذرواژه را نمی‌دهد",
        "authmanager-authplugin-setpass-bad-domain": "دامنه نامعتبر است.",
        "authmanager-autocreate-noperm": "ایجاد حساب خودکار مجاز نیست.",
        "authmanager-autocreate-exception": "ایجاد حساب کاربری به خاطر خطاهای قبلی به طور موقت غیرفعال است.",
        "authmanager-userdoesnotexist": "حساب کاربری «$1» ثبت نشده‌است.",
+       "authmanager-userlogin-remembermypassword-help": "اينکه گذرواژه می‌بایست به مدتی بيش از طول جلسه به خاطر سپرده شود",
+       "authmanager-username-help": "نام کاربری جهت ورود",
+       "authmanager-password-help": "گذرواژه جهت ورود",
+       "authmanager-domain-help": "دامنه برای احراز هویت خارجی.",
+       "authmanager-retype-help": "تکراد مجدد گذرواژه جهت تأييد",
        "authmanager-email-label": "ایمیل",
        "authmanager-email-help": "آدرس ایمیل",
        "authmanager-realname-label": "نام واقعی",
        "authmanager-realname-help": "نام واقعی کاربر",
+       "authmanager-provider-password": "احراز هويت بر پايهٔ گذرواژه",
+       "authmanager-provider-password-domain": "احراز هويت بر پايهٔ دامنه و گذرواژه",
        "authmanager-provider-temporarypassword": "گذرواژهٔ موقت",
+       "authprovider-confirmlink-message": "طبق تلاش‌های اخير شما جهت ورود، حسابهای زير می‌توتنند با حساب ويکی شما پيوند بخورند. پيوند آنها شما را قادر می‌سازد تا از طریق آنها به سامانه وارد شويد. لطفاً انتخاب کنيد که کداميک از آنها می‌بايست پيوند داده شوند.",
+       "authprovider-confirmlink-request-label": "حساب‌هایي که می‌بایست پيوند داده شوند",
+       "authprovider-confirmlink-success-line": "$1 با موفقيت پيوند خورد.",
+       "authprovider-confirmlink-failed": "پيوند حسابها به طور کامل انجام نشد:$1",
+       "authprovider-confirmlink-ok-help": "پس از نمايش پيامهای عدم موفقيت پيوند زدن همچنان ادامه بده.",
        "authprovider-resetpass-skip-label": "رها کردن",
+       "authprovider-resetpass-skip-help": "از خير تغيير گذرواژه بگذر",
+       "authform-nosession-login": "احراز هويت موفقيت‌آميز بود، ليکن مرورگرشما نمی‌تواند وارد شدن شما را «به خاطر بسپارد».\n\n$1",
+       "authform-nosession-signup": "حساب کاربری ساخته شد، ليکن مرورگرشما نمی‌تواند وارد شدن شما را «به خاطر بسپارد».\n\n$1",
+       "authform-newtoken": "فقدان توکن.$1",
+       "authform-notoken": "فقدان توکن.",
+       "authform-wrongtoken": "توکن اشتباه",
        "specialpage-securitylevel-not-allowed-title": "مجاز نیست",
+       "specialpage-securitylevel-not-allowed": "با عرض پوزش، شما مجاز به استفاده از اين صفحه نمی‌باشيد، چراکه هويت شما را نمی‌توان تأييد کرد.",
+       "authpage-cannot-login": "نمی‌توان ورود را آغاز نمود",
+       "authpage-cannot-login-continue": "نمی‌توان به ورود ادامه داد. زمان جلسه کاری شما احتمالاً به سر آمده است.",
+       "authpage-cannot-create": "نمی‌توان ايجاد حساب را آغاز نمود.",
+       "authpage-cannot-create-continue": "نمی‌توان به ايجاد حساب ادامه داد. زمان جلسه کاری شما احتمالاً به سر آمده است.",
+       "authpage-cannot-link": "نمی‌توان پيوند دادن حساب را آغاز نمود.",
+       "authpage-cannot-link-continue": "نمی‌توان به پيوند دادن حساب ادامه داد. زمان جلسه کاری شما احتمالاً به سر آمده‌است.",
        "cannotauth-not-allowed-title": "اجازه داده نشد",
        "cannotauth-not-allowed": "شما برای دسترسی به این صفحه مجاز نیستید",
        "changecredentials": "تغییر اعتبارنامه‌ها",
        "linkaccounts-success-text": "حساب کاربری پیوند شده‌است.",
        "linkaccounts-submit": "پیوند حساب کاربری",
        "unlinkaccounts": "حذف پیوند حساب کاربری",
-       "unlinkaccounts-success": "پیوند کاربری بدون پیوند شد."
+       "unlinkaccounts-success": "پیوند کاربری بدون پیوند شد.",
+       "authenticationdatachange-ignored": "به تغيير اطلاعات احراز هويت پرداخته نشد. آیا ممکن است که هيچ مهيا کننده‌ای برای اين کار تنظيم نشده باشد؟",
+       "userjsispublic": "لطفاً توجه کنید: زیرصفحه‌های جاوااسکریپت نباید حاوی اطلاعات محرمانه باشند چون توسط دیگران قابل مشاهده هستند.",
+       "usercssispublic": "لطفاً توجه کنید: زیرصفحه‌های سی‌اس‌اس نباید حاوی اطلاعات محرمانه باشند چون توسط دیگران قابل مشاهده هستند."
 }
index 0708ce4..dc60afe 100644 (file)
@@ -51,7 +51,8 @@
                        "Mikahama",
                        "01miki10",
                        "Matma Rex",
-                       "BiscuitMan"
+                       "BiscuitMan",
+                       "Alluk."
                ]
        },
        "tog-underline": "Linkkien alleviivaus:",
        "tagline": "Kohteesta {{SITENAME}}",
        "help": "Ohje",
        "search": "Haku",
+       "search-ignored-headings": "#<!-- jätä tämä rivi sellaiseksi kuin se on --> <pre>\n# Otsikot, jotka haku ohittaa.\n# Muutokset tulevat voimaan heti, kun otsikon sivu indeksoidaan.\n# Voit pakottaa sivun indeksoimisen tekemällä nollamuokkauksen.\n# Syntaksi on seuraava:\n#   * Kaikki \"#\"-merkistä rivin loppuun asti on kommenttia\n#   * Kaikki ei-tyhjät rivit ovat otsikoita, jotka ohitetaan.\nLähteet\nAiheesta muualla\nKatso myös\n#</pre> <!-- jätä tämä rivi sellaiseksi kuin se on -->",
        "searchbutton": "Hae",
        "go": "Siirry",
        "searcharticle": "Siirry",
        "yourpasswordagain": "Salasana uudelleen:",
        "createacct-yourpasswordagain": "Vahvista salasana",
        "createacct-yourpasswordagain-ph": "Kirjoita salasana uudelleen",
-       "remembermypassword": "Muista kirjautumiseni tässä selaimessa (enintään $1 {{PLURAL:$1|päivä|päivää}})",
        "userlogin-remembermypassword": "Pidä minut kirjautuneena",
        "userlogin-signwithsecure": "Käytä salattua yhteyttä",
        "cannotloginnow-title": "Nyt ei voi kirjautua sisään",
        "userlogin-createanother": "Luo toinen käyttäjätunnus",
        "createacct-emailrequired": "Sähköpostiosoite",
        "createacct-emailoptional": "Sähköpostiosoite (vapaaehtoinen)",
-       "createacct-email-ph": "Anna sähköpostiosoitteesi",
+       "createacct-email-ph": "Kirjoita sähköpostiosoitteesi",
        "createacct-another-email-ph": "Lisää sähköpostiosoite",
        "createaccountmail": "Käytä satunnaista väliaikaissalasanaa ja lähetä se alla olevaan sähköpostiosoitteeseen",
        "createaccountmail-help": "Voidaan käyttää luomaan tunnus toiselle käyttäjälle ilman salasanan tietämistä.",
        "passwordtoopopular": "Tavanomaisen kaltaisia salasanoja ei saa käyttää. Valitse parempi ja yksilöllisempi salasana.",
        "password-name-match": "Salasanasi täytyy olla eri kuin käyttäjätunnuksesi.",
        "password-login-forbidden": "Tämän käyttäjänimen ja salasanan käyttö on estetty.",
-       "mailmypassword": "Uudista salasana",
+       "mailmypassword": "Hanki uusi salasana",
        "passwordremindertitle": "Uusi väliaikainen salasana {{GRAMMAR:elative|{{SITENAME}}}}",
        "passwordremindertext": "Joku IP-osoitteesta $1 pyysi {{GRAMMAR:partitive|{{SITENAME}}}} ($4) lähettämään uuden salasanan. Väliaikainen salasana käyttäjälle $2 on nyt $3. Kirjaudu sisään ja vaihda salasana. Väliaikainen salasana vanhenee {{PLURAL:$5|yhden päivän|$5 päivän}} kuluttua.\n\nJos joku muu on tehnyt tämän pyynnön, tai jos olet muistanut salasanasi ja et halua vaihtaa sitä, voit jättää tämän viestin huomiotta ja jatkaa vanhan salasanan käyttöä.",
        "noemail": "Käyttäjälle $1 ei ole määritelty sähköpostiosoitetta.",
        "botpasswords-label-update": "Päivitä",
        "botpasswords-label-cancel": "Peru",
        "botpasswords-label-delete": "Poista",
-       "botpasswords-label-resetpassword": "Uudista salasana",
+       "botpasswords-label-resetpassword": "Hanki uusi salasana",
        "botpasswords-label-grants": "Valittavissa olevat toimintaoikeudet:",
        "botpasswords-label-restrictions": "Käyttörajoitukset:",
        "botpasswords-label-grants-column": "Myönnetään",
        "resetpass-expired": "Salasanasi on vanhentunut. Valitse uusi salasana, jotta pääset kirjautumaan sisään.",
        "resetpass-expired-soft": "Salasanasi on vanhentunut ja se pitää uudistaa. Valitse uusi salasana nyt tai paina \"{{int:authprovider-resetpass-skip-label}}\", niin voit uudistaa salasanan myöhemmin.",
        "resetpass-validity-soft": "Salasanasi ei ole kelvollinen: $1\n\nValitse nyt uusi salasana tai paina \"{{int:authprovider-resetpass-skip-label}}\", niin voit vaihtaa sen myöhemmin.",
-       "passwordreset": "Salasanan uudistus",
+       "passwordreset": "Salasanan palauttaminen",
        "passwordreset-text-one": "Täytä tämä lomake uudistaaksesi salasanasi.",
-       "passwordreset-text-many": "{{PLURAL:$1|Täytä yksi kentistä, jotta saat väliaikaisen salasanan sähköpostitse.}}",
+       "passwordreset-text-many": "{{PLURAL:$1|Täytä yksi seuraavista kentistä, jolloin saat väliaikaisen salasanan sähköpostitse.}}",
        "passwordreset-disabled": "Salasanojen uudistaminen ei ole mahdollista tässä wikissä.",
        "passwordreset-emaildisabled": "Sähköpostitoiminnot on poistettu käytöstä tässä wikissä.",
        "passwordreset-username": "Käyttäjätunnus:",
        "passwordreset-emailelement": "Käyttäjätunnus: \n$1\n\nVäliaikainen salasana: \n$2",
        "passwordreset-emailsentemail": "Jos tämä on sinun tunnuksellesi rekisteröity sähköpostiosoite, salasanan uudistamisesta kertova viesti lähetetään.",
        "passwordreset-emailsentusername": "Jos on olemassa vastaava rekisteröity sähköpostiosoite, salasanan uudistamisesta kertova viesti lähetetään.",
-       "passwordreset-emailsent-capture": "Salasanan uudistamisesta kertova sähköpostiviesti on lähetetty, ja se näkyy myös alla.",
-       "passwordreset-emailerror-capture": "Allaoleva sähköpostiviesti luotiin, mutta sen lähettäminen {{GENDER:$2|käyttäjälle}} epäonnistui: $1",
        "passwordreset-emailsent-capture2": "Salasananpalautus{{PLURAL:$1|sähköposti|sähköpostit}} on lähetetty. {{PLURAL:$1|Käyttäjä ja salasana|Luettelo käyttäjistä ja salasanoista}} näytetään alapuolella.",
        "passwordreset-emailerror-capture2": "Sähköpostin lähettäminen {{GENDER:$2|käyttäjälle}} epäonnistui: $1 {{PLURAL:$3|Käyttäjänimi ja salasana|Luettelo käyttäjänimistä ja salasanoista}} näytetään alla.",
        "passwordreset-invalideamil": "Virheellinen sähköpostiosoite",
        "passwordreset-nodata": "Käyttäjätunnusta ja salasanaa ei annettu",
        "changeemail": "Muuta tai poista sähköpostiosoite",
        "changeemail-header": "Täydennä tämä lomake, jolla voit muuttaa sähköpostiosoitettasi. Jos haluat poistaa sähköpostiosoitteesi kokonaan tunnuksesi yhteydestä, älä kirjoita uudeksi osoitteeksi mitään vaan jätä se tyhjäksi.",
-       "changeemail-passwordrequired": "Sinun on syötettävä salasanasi vahvistaaksesi tämän muutoksen.",
        "changeemail-no-info": "Tämän sivun käyttö edellyttää sisäänkirjautumista.",
        "changeemail-oldemail": "Nykyinen sähköpostiosoite:",
        "changeemail-newemail": "Uusi sähköpostiosoite:",
        "minoredit": "Tämä on pieni muutos",
        "watchthis": "Tarkkaile tätä sivua",
        "savearticle": "Tallenna sivu",
+       "savechanges": "Tallenna muutokset",
        "publishpage": "Julkaise sivu",
+       "publishchanges": "Julkaise muutokset",
        "preview": "Esikatselu",
        "showpreview": "Esikatsele",
        "showdiff": "Näytä muutokset",
        "undo-nochange": "Tämä muokkaus näyttää olevan jo kumottu.",
        "undo-summary": "Kumottu muokkaus $1, jonka teki [[Special:Contributions/$2|$2]] ([[User talk:$2|keskustelu]])",
        "undo-summary-username-hidden": "Kumottu muokkaus $1, jonka on tehnyt piilotettu käyttäjä",
-       "cantcreateaccounttitle": "Tunnusta ei voida luoda",
        "cantcreateaccount-text": "Tunnusten luonti tästä IP-osoitteesta ('''$1''') on estetty. Estäjänä on [[User:$3|$3]].\n\nKäyttäjän $3 antama syy on ''$2''",
        "cantcreateaccount-range-text": "Tunnusten luominen IP-osoitteista osoitealueella <strong>$1</strong>, johon kuuluu myös sinun käyttämäsi IP-osoite (<strong>$4</strong>), on estetty. Eston on asettanut [[User:$3|$3]].\n\nSyy estolle on \"$2\".",
        "viewpagelogs": "Näytä tämän sivun lokit",
        "rightslogtext": "Tämä on loki käyttäjien käyttöoikeuksien muutoksista.",
        "action-read": "lukea tätä sivua",
        "action-edit": "muokata tätä sivua",
-       "action-createpage": "luoda sivuja",
-       "action-createtalk": "luoda keskustelusivuja",
+       "action-createpage": "luoda tätä sivua",
+       "action-createtalk": "luoda tätä keskustelusivua",
        "action-createaccount": "luoda tätä käyttäjätunnusta",
        "action-autocreateaccount": "luoda automaattisesti tätä ulkopuolista käyttäjätunnusta",
        "action-history": "tarkastella tämän sivun historiaa",
        "mw-widgets-dateinput-placeholder-month": "VVVV-KK",
        "mw-widgets-titleinput-description-new-page": "sivua ei ole olemassa vielä",
        "mw-widgets-titleinput-description-redirect": "ohjaus kohteeseen $1",
-       "api-error-blacklisted": "Valitse toinen, kuvaava nimi.",
        "sessionmanager-tie": "!!FYZZ!!Cannot combine multiple request authentication types: $1.",
        "sessionprovider-generic": "$1 istuntoa",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "istuntoja, joissa on evästeet käytössä",
        "authmanager-autocreate-noperm": "Automaattinen tunnustenluonti ei ole sallittu.",
        "authmanager-autocreate-exception": "Automaattinen tunnuksenluonti on tilapäisesti poistettu käytöstä aikaisempien virheiden vuoksi.",
        "authmanager-userdoesnotexist": "Käyttäjätunnusta ”$1” ei ole rekisteröity.",
+       "authmanager-userlogin-remembermypassword-help": "Tulisiko salasana muistaa istunnon kestoa pidempään.",
        "authmanager-username-help": "Käyttäjänimi varmentamiseen.",
        "authmanager-password-help": "Salasana varmentamiseen.",
        "authmanager-retype-help": "Salasana uudelleen vahvistaaksesi.",
index 6629b25..7bce27b 100644 (file)
                        "Psychoslave",
                        "Trial",
                        "Matma Rex",
-                       "Dcausse"
+                       "Dcausse",
+                       "Lucas",
+                       "Mabroukb"
                ]
        },
        "tog-underline": "Soulignement des liens :",
-       "tog-hideminor": "Masquer les modifications mineures dans les changements récents",
+       "tog-hideminor": "Masquer les modifications mineures dans les modifications récentes",
        "tog-hidepatrolled": "Masquer les modifications relues dans les modifications récentes",
        "tog-newpageshidepatrolled": "Masquer les pages relues dans la liste des nouvelles pages",
        "tog-hidecategorization": "Masquer la catégorisation des pages",
        "tagline": "De {{SITENAME}}",
        "help": "Aide",
        "search": "Rechercher",
+       "search-ignored-headings": " #<!-- laisser cette ligne comme telle --> <pre>\n# Titres des sections qui seront ignorés par la recherche\n# Les changements effectués ici prennent effet dès lors que la page avec le titre est indexée.\n# Vous pouvez forcer la réindexation de la page en effectuant une modification vide\n# La syntaxe est la suivante :\n#   * Toute ligne précédée d’un « # » est un commentaire\n#   * Toute ligne non-vide est le titre exact à ignorer, casse comprise\nRéférences\nLiens externes\nVoir aussi\n #</pre> <!-- laisser cette ligne comme telle -->",
        "searchbutton": "Rechercher",
        "go": "Consulter",
        "searcharticle": "Lire",
        "viewhelppage": "Voir la page d'aide",
        "categorypage": "Voir la page de catégorie",
        "viewtalkpage": "Voir la page de discussion",
-       "otherlanguages": "Dans d'autres langues",
+       "otherlanguages": "Dans dautres langues",
        "redirectedfrom": "(Redirigé depuis $1)",
        "redirectpagesub": "Page de redirection",
        "redirectto": "Rediriger vers :",
        "jumpto": "Aller à :",
        "jumptonavigation": "navigation",
        "jumptosearch": "rechercher",
-       "view-pool-error": "Désolé, les serveurs sont surchargés en ce moment.\nTrop d’utilisateurs cherchent à consulter cette page.\nVeuillez attendre un peu avant de réessayer d’accéder à celle-ci.\n\n$1",
+       "view-pool-error": "Désolé, les serveurs sont surchargés en ce moment.\nTrop d’utilisateurs cherchent à consulter cette page.\nVeuillez attendre un peu avant d’essayer à nouveau d’accéder à celle-ci.\n\n$1",
        "generic-pool-error": "Désolé, les serveurs sont surchargés en ce moment.\nTrop d’utilisateurs cherchent à consulter cette ressource.\nVeuillez attendre un peu avant de tenter à nouveau l’accès à celle-ci.",
        "pool-timeout": "Délai d’attente du verrou dépassé",
        "pool-queuefull": "La file des processus est pleine",
        "currentevents-url": "Project:Actualités",
        "disclaimers": "Avertissements",
        "disclaimerpage": "Project:Avertissements généraux",
-       "edithelp": "Aide",
+       "edithelp": "Aide pour l'édition",
        "helppage-top-gethelp": "Aide",
        "mainpage": "Accueil",
        "mainpage-description": "Accueil",
        "viewdeleted": "Voir $1 ?",
        "restorelink": "{{PLURAL:$1|une modification effacée|$1 modifications effacées}}",
        "feedlinks": "Flux :",
-       "feed-invalid": "Type de flux invalide pour abonnement.",
+       "feed-invalid": "Type de flux d’abonnement non valide.",
        "feed-unavailable": "Les flux de syndication ne sont pas disponibles",
        "site-rss-feed": "Flux RSS de $1",
        "site-atom-feed": "Flux Atom de $1",
        "missing-article": "La base de données n’a pas trouvé le texte d’une page qu’elle aurait dû trouver, intitulée « $1 » $2.\n\nGénéralement, cela survient en suivant un lien vers un diff périmé ou vers l’historique d’une page supprimée.\n\nSi ce n’est pas le cas, il peut s’agir d’une anomalie dans le programme.\nVeuillez la signaler à un [[Special:ListUsers/sysop|administrateur]] sans oublier de lui indiquer l’URL de la page.",
        "missingarticle-rev": "(numéro de version : $1)",
        "missingarticle-diff": "(diff : $1, $2)",
-       "readonly_lag": "La base de données a été automatiquement verrouillée pendant que les serveurs secondaires rattrapent leur retard sur le serveur principal.",
-       "nonwrite-api-promise-error": "L’entête HTTP « <code>Promise-Non-Write-API-Action:<code> » a été envoyé mais la requête a été faite à un module d’écriture de l’API.",
+       "readonly_lag": "La base de données a été automatiquement verrouillée pendant que les serveurs secondaires se réalignent sur le serveur principal.",
+       "nonwrite-api-promise-error": "L’entête HTTP « <code>Promise-Non-Write-API-Action:</code> » a été envoyé mais la requête a été faite à un module d’écriture de l’API.",
        "internalerror": "Erreur interne",
        "internalerror_info": "Erreur interne : $1",
        "internalerror-fatal-exception": "Erreur fatale de type « $1 »",
        "filecopyerror": "Impossible de copier le fichier « $1 » vers « $2 ».",
        "filerenameerror": "Impossible de renommer le fichier « $1 » en « $2 ».",
        "filedeleteerror": "Impossible de supprimer le fichier « $1 ».",
-       "directorycreateerror": "Impossible de créer le dossier « $1 ».",
+       "directorycreateerror": "Impossible de créer le répertoire « $1 ».",
        "directoryreadonlyerror": "Le répertoire « $1 » est en lecture seule.",
        "directorynotreadableerror": "Le répertoire « $1 » n’est pas lisible.",
        "filenotfound": "Impossible de trouver le fichier « $1 ».",
        "viewyourtext": "Vous pouvez voir et copier le contenu de <strong>vos modifications</strong> à cette page.",
        "protectedinterface": "Cette page fournit du texte d’interface pour le logiciel sur ce wiki et est protégée pour éviter les abus.\nPour ajouter ou modifier des traductions sur tous les wikis, veuillez utiliser [https://translatewiki.net/ translatewiki.net], le projet de localisation de MediaWiki.",
        "editinginterface": "<strong>Attention :</strong> vous êtes en train de modifier une page utilisée pour créer le texte de l’interface du logiciel.\nLes changements sur cette page se répercuteront sur l’apparence de l’interface utilisateur pour les autres utilisateurs de ce wiki.",
-       "translateinterface": "Pour ajouter ou modifier des traductions pour tous les wikis, veuillez utiliser [https://translatewiki.net/ translatewiki.net], le projet de régionalisation de MediaWiki.",
+       "translateinterface": "Pour ajouter ou modifier des traductions pour tous les wikis, veuillez utiliser [https://translatewiki.net/ translatewiki.net], le projet de localisation linguistique de MediaWiki.",
        "cascadeprotected": "Cette page est protégée contre les modifications car elle est transcluse par {{PLURAL:$1|la page suivante, qui a été protégée|les pages suivantes, qui ont été protégées}} avec l’option « protection en cascade » activée :\n$2",
        "namespaceprotected": "Vous n’avez pas la permission de modifier les pages de l’espace de noms « <strong>$1</strong> ».",
        "customcssprotected": "Vous n’avez pas la permission de modifier cette page de CSS, car elle contient les paramètres personnels d’un autre utilisateur.",
        "mycustomjsprotected": "Vous n’avez pas le droit de modifier cette page JavaScript.",
        "myprivateinfoprotected": "Vous n’avez pas le droit de modifier vos informations personnelles.",
        "mypreferencesprotected": "Vous n’avez pas le droit de modifier vos préférences.",
-       "ns-specialprotected": "Les pages dans l’espace de noms « {{ns:special}} » ne peuvent pas être modifiées.",
+       "ns-specialprotected": "Les pages spéciales ne peuvent pas être modifiés.",
        "titleprotected": "Ce titre a été protégé contre toute création par [[User:$1|$1]].\nLe motif fourni est <em>$2</em>.",
        "filereadonlyerror": "Impossible de modifier le fichier « $1 » parce que le répertoire de fichiers « $2 » est en lecture seule.\n\nL’administrateur système qui l’a verrouillé a fourni ce motif : « $3 ».",
        "invalidtitle-knownnamespace": "Titre non valide avec l’espace de noms « $2 » et l’intitulé « $3 »",
        "yourpasswordagain": "Confirmez le mot de passe :",
        "createacct-yourpasswordagain": "Confirmez le mot de passe",
        "createacct-yourpasswordagain-ph": "Entrez à nouveau le mot de passe",
-       "remembermypassword": "Mémoriser mes données de connection avec ce navigateur (durant au maximum $1 jour{{PLURAL:$1||s}})",
        "userlogin-remembermypassword": "Garder ma session active",
        "userlogin-signwithsecure": "Utiliser une connexion sécurisée",
+       "cannotlogin-title": "Impossible de se connecter",
+       "cannotlogin-text": "La connexion n’est pas possible.",
        "cannotloginnow-title": "Impossible de se connecter maintenant",
        "cannotloginnow-text": "La connexion n’est pas possible en utilisant $1.",
+       "cannotcreateaccount-title": "Création de comptes impossible",
+       "cannotcreateaccount-text": "La création directe de comptes utilisateurs n’est pas activée sur ce wiki.",
        "yourdomainname": "Votre domaine :",
        "password-change-forbidden": "Vous ne pouvez pas modifier les mots de passe sur ce wiki.",
        "externaldberror": "Soit une erreur s’est produite sur la base de données d’authentification, soit vous n’êtes pas autorisé à mettre à jour votre compte externe.",
        "pt-createaccount": "Créer un compte",
        "pt-userlogout": "Se déconnecter",
        "php-mail-error-unknown": "Erreur inconnue dans la fonction <code>mail()</code> de PHP.",
-       "user-mail-no-addy": "Tenté d'envoyer un courriel sans adresse de courriel",
-       "user-mail-no-body": "Essai d'envoi d'un courriel avec un corps vide ou déraisonnablement court.",
+       "user-mail-no-addy": "Impossible d’envoyer un courriel sans adresse de courriel.",
+       "user-mail-no-body": "Essai d’envoi d’un courriel avec un corps vide ou anormalement court.",
        "changepassword": "Changer de mot de passe",
-       "resetpass_announce": "Pour terminer l'enregistrement, vous devez fournir un nouveau mot de passe.",
+       "resetpass_announce": "Pour terminer votre inscription, vous devez fournir un nouveau mot de passe.",
        "resetpass_text": "<!-- Ajoutez le texte ici -->",
        "resetpass_header": "Changer le mot de passe du compte",
        "oldpassword": "Ancien mot de passe :",
        "changepassword-success": "Votre mot de passe a été modifié !",
        "changepassword-throttled": "Vous avez fait trop de tentatives de connexion récemment.\nVeuillez attendre $1 avant de réessayer.",
        "botpasswords": "Mots de passe de robots",
-       "botpasswords-summary": "<em>Mots de passe de robots</em> permet d’accéder à un compte utilisateur via l’API sans utiliser les identifiants de connexion principaux. Les droits utilisateur disponibles en étant connecté avec un mot de passe robot peuvent être réduits.\n\nSi vous ne voyez pas pourquoi vous voudriez faire cela, c’est que vous n’en avez pas besoin. Personne ne devrait jamais vous demander d’en générer un et de le lui donner.",
+       "botpasswords-summary": "Les <em>mots de passe de robots</em> permettent d’accéder à un compte utilisateur via l’API sans utiliser les identifiants de connexion principaux. Les droits utilisateur disponibles en étant connecté avec un mot de passe robot peuvent être réduits.\n\nSi vous ne voyez pas pourquoi vous voudriez faire cela, c’est que vous n’en avez pas besoin. Personne ne devrait jamais vous demander d’en générer un et de le lui donner.",
        "botpasswords-disabled": "Les mots de passe robots sont désactivés.",
        "botpasswords-no-central-id": "Pour utiliser les mots de passe de robots, vous devez être connecté à un compte centralisé.",
        "botpasswords-existing": "Mots de passe de robots existants",
        "botpasswords-label-delete": "Supprimer",
        "botpasswords-label-resetpassword": "Réinitialiser le mot de passe",
        "botpasswords-label-grants": "Droits applicables :",
-       "botpasswords-help-grants": "Chaque droit donne accès aux droits utilisateurs listés qu’a déjà un compte. Voyez le [[Special:ListGrants|tableau des droits]] pour plus d’information.",
+       "botpasswords-help-grants": "Chaque droit accordé donne accès à la liste des droits utilisateurs dont l’utilisateur dispose déjà. Voyez le [[Special:ListGrants|tableau des droits]] pour plus d’informations.",
        "botpasswords-label-restrictions": "Restrictions d’utilisation :",
        "botpasswords-label-grants-column": "Accordé",
        "botpasswords-bad-appid": "Le nom de robot « $1 » n’est pas valide.",
        "botpasswords-no-provider": "BotPasswordsSessionProvider n’est pas disponible.",
        "botpasswords-restriction-failed": "Les restrictions de mot de passe de robots empêchent cette connexion.",
        "botpasswords-invalid-name": "Le nom d’utilisateur spécifié ne contient pas de séparateur de mot de passe de robots (« $1 »).",
-       "botpasswords-not-exist": "L’utilisateur « $1 » n’a pas de nom de mot de passe de robots appelé « $2 ».",
+       "botpasswords-not-exist": "L’utilisateur « $1 » n’a pas le mot de passe de robots intitulé « $2 ».",
        "resetpass_forbidden": "Les mots de passe ne peuvent pas être changés",
        "resetpass_forbidden-reason": "Les mots de passe ne peuvent pas être modifiés : $1",
-       "resetpass-no-info": "Vous devez être connecté pour avoir accès à cette page.",
+       "resetpass-no-info": "Vous devez être connecté pour avoir accès directement à cette page.",
        "resetpass-submit-loggedin": "Changer de mot de passe",
        "resetpass-submit-cancel": "Annuler",
        "resetpass-wrong-oldpass": "Mot de passe actuel ou temporaire invalide.\nVous avez peut-être déjà changé votre mot de passe ou demandé un nouveau mot de passe temporaire.",
        "resetpass-expired": "Votre mot de passe a expiré. Veuillez en fournir un nouveau pour vous connecter.",
        "resetpass-expired-soft": "Votre mot de passe a expiré, et doit être réinitialisé. Veuillez en choisir un nouveau maintenant ou cliquer sur « {{int:authprovider-resetpass-skip-label}} » pour le faire plus tard.",
        "resetpass-validity-soft": "Votre mot de passe n’est pas valide : $1\n\nVeuillez choisir un nouveau mot de passe maintenant, ou cliquez sur « {{int:authprovider-resetpass-skip-label}} » pour le réinitialiser plus tard.",
-       "passwordreset": "Remise à zéro du mot de passe",
+       "passwordreset": "Réinitialisation du mot de passe",
        "passwordreset-text-one": "Remplissez ce formulaire pour réinitialiser votre mot de passe.",
        "passwordreset-text-many": "{{PLURAL:$1|Remplissez un des champs pour recevoir un mot de passe temporaire par courriel.}}",
        "passwordreset-disabled": "La réinitialisation des mots de passe a été désactivée sur ce wiki.",
-       "passwordreset-emaildisabled": "Les fonctionnalités e-mail ont été désactivées sur ce wiki.",
+       "passwordreset-emaildisabled": "Les fonctionnalités de courriel ont été désactivées sur ce wiki.",
        "passwordreset-username": "Nom d'utilisateur :",
        "passwordreset-domain": "Domaine :",
        "passwordreset-capture": "Voir le courriel résultant ?",
-       "passwordreset-capture-help": "Si vous cochez cette case, le courriel (avec le mot de passe temporaire) vous sera affiché en même temps qu'il sera envoyé à l'utilisateur.",
+       "passwordreset-capture-help": "Si vous cochez cette case, le courriel (avec le mot de passe temporaire) vous sera affiché en même temps qu’il sera envoyé à l’utilisateur.",
        "passwordreset-email": "Adresse de courriel :",
        "passwordreset-emailtitle": "Détails du compte sur {{SITENAME}}",
-       "passwordreset-emailtext-ip": "Quelqu'un (probablement vous, depuis l'adresse IP $1) a demandé un réinitialisation de votre mot de passe pour {{SITENAME}} ($4). {{PLURAL:$3|Le compte utilisateur suivant est associé|Les comptes utilisateurs suivants sont associés}} à cette adresse de courriel :\n\n$2\n\n{{PLURAL:$3|Ce mot de passe temporaire expirera|Ces mots de passe temporaires expireront}} dans {{PLURAL:$5|un jour|$5 jours}}. Vous devez maintenant vous connecter et choisir un nouveau mot de passe. Si cette demande ne provient pas de vous, ou que vous avez retrouvé votre mot de passe initial, et ne souhaitez plus le modifier, vous pouvez ignorer ce message et continuer à utiliser votre ancien mot de passe.",
-       "passwordreset-emailtext-user": "L'utilisateur $1 sur {{SITENAME}} a demandé un réinitialisation de votre mot de passe pour {{SITENAME}} ($4). {{PLURAL:$3|Le compte utilisateur suivant est associé|Les comptes utilisateurs suivants sont associés}} à cette adresse de courriel :\n\n$2\n\n{{PLURAL:$3|Ce mot de passe temporaire expirera|Ces mots de passe temporaires expireront}} dans {{PLURAL:$5|un jour|$5 jours}}. Vous devez maintenant vous connecter et choisir un nouveau mot de passe. Si cette demande ne provient pas de vous, ou que vous avez retrouvé votre mot de passe initial, et ne souhaitez plus le modifier, vous pouvez ignorer ce message et continuer à utiliser votre ancien mot de passe.",
-       "passwordreset-emailelement": "Nom d'utilisateur : \n$1\n\nMot de passe temporaire : \n$2",
+       "passwordreset-emailtext-ip": "Quelqu’un (probablement vous, depuis l’adresse IP $1) a demandé un réinitialisation de votre mot de passe pour {{SITENAME}} ($4). {{PLURAL:$3|Le compte utilisateur suivant est associé|Les comptes utilisateurs suivants sont associés}} à cette adresse de courriel :\n\n$2\n\n{{PLURAL:$3|Ce mot de passe temporaire expirera|Ces mots de passe temporaires expireront}} dans {{PLURAL:$5|un jour|$5 jours}}. \nVous devez maintenant vous connecter et choisir un nouveau mot de passe. Si cette demande ne provient pas de vous, ou que vous avez retrouvé votre mot de passe initial, et ne souhaitez plus le modifier, vous pouvez ignorer ce message et continuer à utiliser votre ancien mot de passe.",
+       "passwordreset-emailtext-user": "L’utilisateur $1 sur {{SITENAME}} a demandé une réinitialisation de votre mot de passe pour {{SITENAME}} ($4). {{PLURAL:$3|Le compte utilisateur suivant est associé|Les comptes utilisateurs suivants sont associés}} à cette adresse de courriel :\n\n$2\n\n{{PLURAL:$3|Ce mot de passe temporaire expirera|Ces mots de passe temporaires expireront}} dans {{PLURAL:$5|un jour|$5 jours}}. \nVous devez maintenant vous connecter et choisir un nouveau mot de passe. Si cette demande ne provient pas de vous, ou si vous avez retrouvé votre mot de passe initial, et ne souhaitez plus le modifier, vous pouvez ignorer ce message et continuer à utiliser votre ancien mot de passe.",
+       "passwordreset-emailelement": "Nom dutilisateur : \n$1\n\nMot de passe temporaire : \n$2",
        "passwordreset-emailsentemail": "Si cette adresse de courriel est associée à votre compte, alors un courriel de réinitialisation de mot de passe sera envoyé.",
        "passwordreset-emailsentusername": "S’il y a une adresse de courriel associée à ce nom d’utilisateur, alors un courriel de réinitialisation de mot de passe sera envoyé.",
-       "passwordreset-emailsent-capture": "Un courriel de réinitialisation de mot de passe a été envoyé, qui est affiché ci-dessous.",
-       "passwordreset-emailerror-capture": "Un courriel de réinitialisation de mot de passe a été généré, qui est affiché ci-dessous, mais l'envoi à l'{{GENDER:$2|utilisateur|utilisatrice}} a échoué : $1",
        "passwordreset-emailsent-capture2": "{{PLURAL:$1|Le courriel de réinitialisation du mot de passe a été envoyé|Les courriels de réinitialisation du mot de passe ont été envoyés}}. {{PLURAL:$1|Le nom d’utilisateur et le mot de passe sont affichés|La liste des noms d’utilisateur et mots de passe est affichée}} ci-dessous.",
        "passwordreset-emailerror-capture2": "L’envoi de courriel à {{GENDER:$2|l’utilisateur|l’utilisatrice}} a échoué : $1 {{PLURAL:$3|Le nom d’utilisateur et le mot de passe sont affichés|La liste des noms d’utilisateur et des mots de passe est affichée}} ci-dessous.",
        "passwordreset-nocaller": "Un appelant doit être fourni",
        "passwordreset-nosuchcaller": "L’appelant n’existe pas : $1",
-       "passwordreset-ignored": "La réinitialisation du mot de passe n’a pas été gérée. Peut-être qu'aucun fournisseur n’a été configuré ?",
+       "passwordreset-ignored": "La réinitialisation du mot de passe n’a pas été gérée. Peut-être quaucun fournisseur n’a été configuré ?",
        "passwordreset-invalideamil": "Adresse de messagerie non valide",
-       "passwordreset-nodata": "Ni nom d’utilisateur ni adresse de messagerie n’ont été fournis",
+       "passwordreset-nodata": "Aucun nom d’utilisateur ou adresse de messagerie n’a été fourni",
        "changeemail": "Changer ou supprimer l’adresse de courriel",
        "changeemail-header": "Complétez ce formulaire pour modifier votre adresse de courriel. Si vous voulez supprimer l’association d’une adresse de courriel avec votre compte, laissez la nouvelle adresse de courriel vide lors de la soumission du formulaire.",
-       "changeemail-passwordrequired": "Vous devrez saisir votre mot de passe pour confirmer cette modification.",
        "changeemail-no-info": "Vous devez être connecté pour pouvoir accéder directement à cette page.",
        "changeemail-oldemail": "Adresse de courriel actuelle :",
        "changeemail-newemail": "Nouvelle adresse de courriel :",
        "italic_tip": "Texte italique",
        "link_sample": "Titre du lien",
        "link_tip": "Lien interne",
-       "extlink_sample": "http://www.example.com titre du lien",
+       "extlink_sample": "http://www.example.com/ titre du lien",
        "extlink_tip": "Lien externe (n'oubliez pas le préfixe http://)",
        "headline_sample": "Texte du titre",
        "headline_tip": "Sous-titre niveau 2",
        "preview": "Prévisualisation",
        "showpreview": "Prévisualiser",
        "showdiff": "Voir les modifications",
-       "blankarticle": "<strong>Attention :</strong> La page que vous créez est vide.\nSi vous cliquez de nouveau sur « {{int:savearticle}} », la page sera créée sans aucun contenu.",
-       "anoneditwarning": "<strong>Attention :</strong> Vous n’êtes pas connecté. Votre adresse IP sera visible de tout le monde si vous faites des modifications. Si vous <strong>[$1 vous connectez]</strong> ou <strong>[$2 créez un compte]</strong>, vos modifications seront attribuées à votre nom d’utilisateur, entre autres avantages.",
-       "anonpreviewwarning": "''Vous n’êtes pas identifié{{GENDER:||e}}. Sauvegarder enregistrera votre adresse IP dans l’historique des modifications de la page.''",
-       "missingsummary": "'''Rappel :''' vous n'avez pas encore fourni le résumé de votre modification.\nSi vous cliquez de nouveau sur le bouton « {{int:savearticle}} », la publication sera faite sans nouvel avertissement.",
-       "selfredirect": "<strong>Attention :</strong> Vous êtes en train de rediriger la page vers elle-même.\nVous pouvez avoir spécifié la mauvaise cible pour la redirection, ou vous modifiez peut-être la mauvaise page.\nSi vous cliquez de nouveau sur « {{int:savearticle}} », la redirection sera créée tout de même.",
+       "blankarticle": "<strong>Attention :</strong> la page que vous créez est vide.\nSi vous cliquez de nouveau sur « {{int:savearticle}} », la page sera créée sans aucun contenu.",
+       "anoneditwarning": "<strong>Attention :</strong> vous n’êtes pas connecté. Votre adresse IP sera visible de tout le monde si vous faites des modifications. Si vous <strong>[$1 vous connectez]</strong> ou <strong>[$2 créez un compte]</strong>, vos modifications seront attribuées à votre nom d’utilisateur, avec d'autres avantages.",
+       "anonpreviewwarning": "<em>Vous n’êtes pas connecté{{GENDER:||e}}. Sauvegarder enregistrera votre adresse IP dans l’historique des modifications de la page.</em>",
+       "missingsummary": "<strong>Rappel :</strong> vous n’avez pas encore fourni le résumé de votre modification.\nSi vous cliquez de nouveau sur le bouton « {{int:savearticle}} », vos modifications seront sauvegardées sans résumé.",
+       "selfredirect": "<strong>Attention :</strong> vous êtes en train de rediriger la page vers elle-même.\nVous pouvez avoir spécifié la mauvaise cible pour la redirection, ou vous modifiez peut-être la mauvaise page.\nSi vous cliquez de nouveau sur « {{int:savearticle}} », la redirection sera tout de même créée.",
        "missingcommenttext": "Veuillez entrer un commentaire ci-dessous.",
-       "missingcommentheader": "<strong>Rappel :</strong> Vous n’avez pas fourni de sujet pour ce commentaire.\nSi vous cliquez de nouveau sur « {{int:Savearticle}} », votre modification sera enregistrée sans sujet.",
+       "missingcommentheader": "<strong>Rappel :</strong> vous n’avez pas fourni de sujet pour ce commentaire.\nSi vous cliquez de nouveau sur « {{int:Savearticle}} », votre modification sera enregistrée sans sujet.",
        "summary-preview": "Aperçu du résumé :",
        "subject-preview": "Aperçu du sujet :",
        "previewerrortext": "Une erreur s’est produite lors de la tentative de prévisualisation de vos modifications.",
        "blockedtitle": "L’utilisateur est bloqué.",
        "blockedtext": "'''Votre compte utilisateur ou votre adresse IP a été bloqué.'''\n\nLe blocage a été effectué par $1.\nLa raison invoquée est la suivante : ''$2''.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7.\n\nVous pouvez contacter $1 ou un autre [[{{MediaWiki:Grouppage-sysop}}|administrateur]] pour en discuter.\nVous ne pouvez utiliser la fonction « {{int:emailuser}} » que si une adresse de courriel valide est spécifiée dans vos [[Special:Preferences|préférences]] et que si cette fonctionnalité n’a pas été bloquée.\nVotre adresse IP actuelle est $3 et votre identifiant de blocage est $5.\nVeuillez préciser ces indications dans toutes les requêtes que vous ferez.",
-       "autoblockedtext": "Votre adresse IP a été bloquée automatiquement car elle a été utilisée par un autre utilisateur, lui-même bloqué par $1.\nLa raison invoquée est :\n\n:''$2''\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7\n\nVous pouvez contacter $1 ou l’un des autres [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] pour discuter de ce blocage.\n\nNotez que vous ne pourrez utiliser la fonctionnalité d’envoi de courriel que si vous avez une adresse de courriel validée dans vos [[Special:Preferences|préférences]] et que si cette fonctionnalité n’a pas été désactivée.\n\nVotre adresse IP actuelle est $3, et le numéro de blocage est $5.\nVeuillez préciser ces indications dans toutes les requêtes que vous ferez.",
+       "autoblockedtext": "Votre adresse IP a été bloquée automatiquement car elle a été utilisée par un autre utilisateur, lui-même bloqué par $1.\nLa raison invoquée est :\n\n:<em>$2:</em>\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7\n\nVous pouvez contacter $1 ou l’un des autres [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] pour discuter de ce blocage.\n\nNotez que vous ne pourrez utiliser la fonctionnalité d’envoi de courriel que si vous avez une adresse de courriel validée dans vos [[Special:Preferences|préférences]] et que cette fonctionnalité n’a pas été désactivée.\n\nVotre adresse IP actuelle est $3, et le numéro de blocage est $5.\nVeuillez préciser ces indications dans toutes les requêtes que vous ferez.",
        "blockednoreason": "aucune raison donnée",
        "whitelistedittext": "Vous devez vous $1 pour avoir la permission de modifier le contenu.",
        "confirmedittext": "Vous devez confirmer votre adresse de courriel avant de modifier les pages.\nVeuillez entrer et valider votre adresse de courriel dans vos [[Special:Preferences|préférences]].",
        "nosuchsectiontitle": "Impossible de trouver la section",
-       "nosuchsectiontext": "Vous avez essayé de modifier une section qui n'existe pas.\nElle a peut-être été déplacée ou supprimée depuis que vous avez lu cette page.",
+       "nosuchsectiontext": "Vous avez essayé de modifier une section qui n’existe pas.\nElle a peut-être été déplacée ou supprimée pendant que vous la consultiez.",
        "loginreqtitle": "Connexion nécessaire",
        "loginreqlink": "connecter",
        "loginreqpagetext": "Vous devez vous $1 pour voir les autres pages.",
        "accmailtitle": "Mot de passe envoyé.",
        "accmailtext": "Un mot de passe généré aléatoirement pour [[User talk:$1|$1]] a été envoyé à $2.\nIl peut être modifié sur la page ''[[Special:ChangePassword|Changement de mot de passe]]'' après connexion.",
        "newarticle": "(Nouveau)",
-       "newarticletext": "Vous avez suivi un lien vers une page qui n’existe pas encore. \nAfin de créer cette page, entrez votre texte dans la boîte ci-après (vous pouvez consulter [$1 la page d’aide] pour plus d’informations). \nSi vous êtes arrivé{{GENDER:||e}} ici par erreur, cliquez sur le bouton '''retour''' de votre navigateur.",
+       "newarticletext": "Vous avez suivi un lien vers une page qui n’existe pas encore. \nAfin de créer cette page, entrez votre texte dans la boîte ci-après (vous pouvez consulter [$1 la page d’aide] pour plus d’informations). \nSi vous êtes arrivé{{GENDER:||e}} ici par erreur, cliquez sur le bouton <strong>Retour</strong> de votre navigateur.",
        "anontalkpagetext": "----\n<em>Vous êtes sur la page de discussion d’un utilisateur anonyme qui n’a pas encore créé de compte ou qui n’en utilise pas</em>.\nPour cette raison, nous devons utiliser son adresse IP pour l’identifier.\nUne adresse IP peut être partagée par plusieurs utilisateurs.\nSi vous êtes un{{GENDER:||e|}} utilisat{{GENDER:|eur|rice|eur}} anonyme et si vous constatez que des commentaires qui ne vous concernent pas vous ont été adressés, vous pouvez [[Special:CreateAccount|créer un compte]] ou [[Special:UserLogin|vous connecter]] afin d’éviter toute confusion future avec d’autres contributeurs anonymes.",
        "noarticletext": "Il n’y a pour l’instant aucun texte sur cette page.\nVous pouvez [[Special:Search/{{PAGENAME}}|lancer une recherche sur ce titre]] dans les autres pages,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} rechercher dans les opérations liées]\nou [{{fullurl:{{FULLPAGENAME}}|action=edit}} créer cette page]</span>.",
-       "noarticletext-nopermission": "Il n'y a pour l'instant aucun texte sur cette page.\nVous pouvez [[Special:Search/{{PAGENAME}}|faire une recherche sur ce titre]] dans les autres pages,\nou <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} rechercher dans les journaux associés]</span>.",
-       "missing-revision": "La révision n° $1 de la page intitulée Â« {{FULLPAGENAME}} Â» n'existe pas.\n\nCela survient en général en suivant un lien historique obsolète vers une page qui a été supprimée.\nVous pouvez trouver plus de détails dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
-       "userpage-userdoesnotexist": "Le compte utilisateur « <nowiki>$1</nowiki> » n'est pas enregistré. Veuillez vérifier que vous voulez créer cette page.",
+       "noarticletext-nopermission": "Il n'y a pour l'instant aucun texte sur cette page.\nVous pouvez [[Special:Search/{{PAGENAME}}|faire une recherche sur ce titre]] dans les autres pages,\nou <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} rechercher dans les journaux associés]</span>, mais vous n'avez pas la permission de créer cette page.",
+       "missing-revision": "La révision nº $1 de la page intitulée Â« {{FULLPAGENAME}} Â» nâ\80\99existe pas.\n\nCela survient en général en suivant un lien historique obsolète vers une page qui a été supprimée.\nVous pouvez trouver plus de détails dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
+       "userpage-userdoesnotexist": "Le compte utilisateur « <nowiki>$1</nowiki> » nest pas enregistré. Veuillez vérifier que vous voulez créer cette page.",
        "userpage-userdoesnotexist-view": "Le compte utilisateur « $1 » n'est pas enregistré.",
-       "blocked-notice-logextract": "Cet utilisateur est actuellement bloqué.\nLa dernière entrée du journal des blocages est indiquée ci-dessous à titre d’information :",
-       "clearyourcache": "<strong>Note :</strong> après avoir enregistré vos modifications, il se peut que vous deviez forcer le rechargement complet du cache de votre navigateur pour voir les changements.\n* <strong>Firefox / Safari :</strong> Maintenez la touche <em>Maj</em> (<em>Shift</em>) en cliquant sur le bouton <em>Actualiser</em> ou pressez <em>Ctrl-F5</em> ou <em>Ctrl-R</em> (<em>⌘-R</em> sur un Mac) ;\n* <strong>Google Chrome :</strong> Appuyez sur <em>Ctrl-Maj-R</em> (<em>⌘-Shift-R</em> sur un Mac) ;\n* <strong>Internet Explorer :</strong> Maintenez la touche <em>Ctrl</em> en cliquant sur le bouton <em>Actualiser</em> ou pressez <em>Ctrl-F5</em> ;\n* <strong>Opera :</strong> Allez dans <em>Menu → Settings</em> (<em>Opera → Préférences</em> sur un Mac) et ensuite à <em>Confidentialité & sécurité → Effacer les données d'exploration → Images et fichiers en cache</em>.",
-       "usercssyoucanpreview": "'''Astuce :''' utilisez le bouton « {{int:showpreview}} » pour tester votre nouvelle feuille CSS avant de l'enregistrer.",
-       "userjsyoucanpreview": "'''Astuce :''' utilisez le bouton « {{int:showpreview}} » pour tester votre nouvelle feuille JavaScript avant de l'enregistrer.",
-       "usercsspreview": "'''Rappelez-vous que vous n'êtes qu'en train de prévisualiser votre propre feuille CSS.'''\n'''Elle n'a pas encore été enregistrée !'''",
-       "userjspreview": "'''Rappelez-vous que vous êtes en train de visualiser ou de tester votre code JavaScript et qu'il n'a pas encore été enregistré !'''",
-       "sitecsspreview": "'''Souvenez-vous que vous êtes seulement en train de prévisualiser cette feuille de style.'''\n'''Elle n'a pas encore été enregistrée !'''",
-       "sitejspreview": "'''Souvenez-vous que vous êtes seulement en train de prévisualiser ce code JavaScript.'''\n'''Il n'a pas encore été enregistré !'''",
-       "userinvalidcssjstitle": "'''Attention :''' il n'existe pas d'habillage « $1 ». Rappelez-vous que les pages personnelles avec extensions .css et .js utilisent des titres en minuscules, par exemple {{ns:user}}:Foo/vector.css et non {{ns:user}}:Foo/Vector.css.",
+       "blocked-notice-logextract": "Cet utilisateur est actuellement bloqué.\nLa dernière entrée du journal des blocages est affichée ci-dessous pour référence :",
+       "clearyourcache": "<strong>Note :</strong> après avoir enregistré vos modifications, il se peut que vous deviez forcer le rechargement complet du cache de votre navigateur pour voir les changements.\n* <strong>Firefox / Safari :</strong> maintenez la touche <em>Maj</em> (<em>Shift</em>) en cliquant sur le bouton <em>Actualiser</em> ou pressez <em>Ctrl-F5</em> ou <em>Ctrl-R</em> (<em>⌘-R</em> sur un Mac) \n* <strong>Google Chrome :</strong> appuyez sur <em>Ctrl-Maj-R</em> (<em>⌘-Shift-R</em> sur un Mac) \n* <strong>Internet Explorer :</strong> maintenez la touche <em>Ctrl</em> en cliquant sur le bouton <em>Actualiser</em> ou pressez <em>Ctrl-F5</em> \n* <strong>Opera :</strong> allez dans <em>Menu → Settings</em> (<em>Opera → Préférences</em> sur un Mac) et ensuite à <em>Confidentialité & sécurité → Effacer les données d’exploration → Images et fichiers en cache</em>.",
+       "usercssyoucanpreview": "<strong>Astuce :</strong> utilisez le bouton « {{int:showpreview}} » pour tester votre nouvelle feuille CSS avant de l’enregistrer.",
+       "userjsyoucanpreview": "<strong>Astuce :</strong> utilisez le bouton « {{int:showpreview}} » pour tester votre nouvelle feuille JavaScript avant de l’enregistrer.",
+       "usercsspreview": "<strong>Rappelez-vous que vous ne faites que prévisualiser votre propre feuille CSS. \nElle n’a pas encore été enregistrée !</strong>",
+       "userjspreview": "<strong>Rappelez-vous que vous ne faites que visualiser ou tester votre code JavaScript.\nIl n’a pas encore été enregistré !</strong>",
+       "sitecsspreview": "<strong>Rappelez-vous que vous ne faites que prévisualiser cette feuille de style. \nElle n’a pas encore été enregistrée !</strong>",
+       "sitejspreview": "<strong>Rappelez-vous que vous ne faites que prévisualiser ce code JavaScript. \nIl n’a pas encore été enregistré !</strong>",
+       "userinvalidcssjstitle": "<strong>Attention :</strong> il n’existe pas d’habillage « $1 ». \nLes pages personnelles avec extensions .css et .js utilisent des titres en minuscules, par exemple {{ns:user}}:Foo/vector.css et non {{ns:user}}:Foo/Vector.css.",
        "updated": "(Mis à jour)",
-       "note": "'''Note :'''",
-       "previewnote": "'''Rappelez-vous que ce n'est qu'une prévisualisation.'''\nVos modifications n'ont pas encore été enregistrées !",
+       "note": "<strong>Note :</strong>",
+       "previewnote": "<strong>Rappelez-vous que ce n’est qu’une prévisualisation.</strong>\nVos modifications n’ont pas encore été enregistrées !",
        "continue-editing": "Aller à la zone de modification",
-       "previewconflict": "Cette prévisualisation montre le texte de la boîte supérieure de modification tel qu'il apparaîtra si vous choisissez de le publier.",
+       "previewconflict": "Cette prévisualisation montre le texte de la boîte supérieure de modification tel quil apparaîtra si vous choisissez de le publier.",
        "session_fail_preview": "Désolé, nous ne pouvons enregistrer votre modification à cause d’une perte d’informations concernant votre session.\n\nVous avez peut-être été déconnecté. <strong>Veuillez vérifier que vous êtes toujours connecté et réessayer.</strong>\nSi cela échoue de nouveau, essayez en vous [[Special:UserLogout|déconnectant]], puis en vous reconnectant, et vérifiez que votre navigateur accepte les cookies de ce site.",
        "session_fail_preview_html": "Désolé, nous ne pouvons enregistrer votre modification à cause d’une perte d’informations concernant votre session.\n\n<em>Parce que {{SITENAME}} a activé le HTML brut, la prévisualisation est masquée afin de prévenir les attaques par JavaScript.</em>\n\n<strong>Si la tentative de modification est légitime, veuillez réessayer.</strong>\nSi cela échoue de nouveau, [[Special:UserLogout|déconnectez-vous]], puis reconnectez-vous, et vérifiez que votre navigateur accepte les cookies de ce site.",
-       "token_suffix_mismatch": "'''Votre modification n'a pas été acceptée car votre navigateur a mal codé les caractères de ponctuation dans l'identifiant de modification.'''\nCe rejet est nécessaire pour empêcher la corruption du texte de la page.\nCe problème se produit parfois lorsque vous utilisez un serveur mandataire anonyme problématique basé sur le web.",
-       "edit_form_incomplete": "'''Certaines parties du formulaire de modification n'ont pas atteint le serveur, vérifiez que vos modifications sont intactes et essayez à nouveau.'''",
+       "token_suffix_mismatch": "<strong>Votre modification n’a pas été acceptée car votre navigateur a mal codé les caractères de ponctuation dans l’identifiant de modification.</strong>\nCe rejet est nécessaire pour empêcher la corruption du texte de la page.\nCe problème se produit parfois lorsque vous utilisez un serveur mandataire anonyme problématique basé sur le web.",
+       "edit_form_incomplete": "<strong>Certaines parties du formulaire de modification n’ont pas atteint le serveur, vérifiez que vos modifications sont intactes et essayez à nouveau.</strong>",
        "editing": "Modification de $1",
        "creating": "Création de $1",
        "editingsection": "Modification de $1 (section)",
        "editingcomment": "Modification de $1 (nouvelle section)",
        "editconflict": "Conflit de modification : $1",
-       "explainconflict": "Cette page a été changée après que vous ayez commencé à la modifier.\nLa zone de modification supérieure contient le texte tel qu'il est actuellement enregistré dans la base de données.\nVos modifications apparaissent dans la zone de modification inférieure.\nVous allez devoir fusionner vos modifications dans le texte existant.\n'''Seul''' le texte de la zone supérieure sera sauvegardé si vous cliquez sur « {{int:savearticle}} ».",
+       "explainconflict": "Cette page a été changée après que vous ayez commencé à la modifier.\nLa zone de modification supérieure contient le texte tel qu’il est actuellement enregistré dans la base de données.\nVos modifications apparaissent dans la zone de modification inférieure.\nVous allez devoir fusionner vos modifications dans le texte existant.\n<strong>Seul</strong> le texte de la zone supérieure sera sauvegardé si vous cliquez sur « {{int:savearticle}} ».",
        "yourtext": "Votre texte",
        "storedversion": "La version enregistrée",
-       "nonunicodebrowser": "'''Attention : Votre navigateur ne supporte pas l'Unicode.'''\nUne solution de rechange a été trouvée pour vous permettre de modifier en toute sûreté une page : les caractères non-ASCII apparaîtront dans votre boîte de modification en tant que codes hexadécimaux. Vous devriez utiliser un navigateur plus récent.",
-       "editingold": "'''Attention : vous êtes en train de modifier une ancienne version de cette page.\nSi vous la publiez, toutes les modifications effectuées depuis cette version seront perdues.'''",
+       "nonunicodebrowser": "<strong>Attention : votre navigateur ne prend pas en charge l’Unicode.</strong>\nUn palliatif est en place vous permettant de modifier les pages en toute sécurité, faisant apparaître les caractères non-ASCII sous forme hexadécimale dans la boîte de modification.",
+       "editingold": "<strong>Attention : vous êtes en train de modifier une ancienne version de cette page.</strong>\nSi vous la publiez, toutes les modifications effectuées depuis cette version seront perdues.",
        "yourdiff": "Différences",
-       "copyrightwarning": "Toutes les contributions à {{SITENAME}} sont considérées comme publiées sous les termes de la $2 (voir $1 pour plus de détails). Si vous ne désirez pas que vos écrits soient modifiés et distribués à volonté, merci de ne pas les soumettre ici.<br />\nVous nous promettez aussi que vous avez écrit ceci vous-même, ou que vous l’avez copié d’une source provenant du domaine public, ou d’une ressource libre. '''N’UTILISEZ PAS DE TRAVAUX SOUS DROIT D’AUTEUR SANS AUTORISATION EXPRESSE !'''",
-       "copyrightwarning2": "Toutes les contributions à {{SITENAME}} peuvent être modifiées ou supprimées par d’autres utilisateurs. Si vous ne désirez pas que vos écrits soient modifiés et distribués à volonté, merci de ne pas les soumettre ici.<br \n/>Vous nous promettez aussi que vous avez écrit ceci vous-même, ou que vous l’avez copié d’une source provenant du domaine public, ou d’une ressource libre. (voir $1 pour plus de détails).\n'''N’UTILISEZ PAS DE TRAVAUX SOUS DROIT D’AUTEUR SANS AUTORISATION EXPRESSE !'''",
+       "copyrightwarning": "Toutes les contributions à {{SITENAME}} sont considérées comme publiées sous les termes de la $2 (voir $1 pour plus de détails). \nSi vous ne désirez pas que vos écrits soient modifiés et distribués à volonté, merci de ne pas les soumettre ici.<br /> \nVous nous promettez aussi que vous avez écrit ceci vous-même, ou que vous l’avez copié d’une source provenant du domaine public ou d’une ressource libre similaire. \n<strong>N’UTILISEZ PAS DE TRAVAUX SOUS DROIT D’AUTEUR SANS AUTORISATION EXPRESSE !</strong>",
+       "copyrightwarning2": "Notez bien que toutes les contributions à {{SITENAME}} peuvent être modifiées, transformées ou supprimées par d’autres utilisateurs. \nSi vous ne désirez pas que vos écrits soient modifiés contre votre gré, merci de ne pas les soumettre ici.<br /> \nVous nous promettez aussi que vous avez écrit ceci vous-même, ou que vous l’avez copié d’une source provenant du domaine public, ou d’une ressource libre. (voir $1 pour plus de détails).\n<strong>N’UTILISEZ PAS DE TRAVAUX SOUS DROIT D’AUTEUR SANS AUTORISATION EXPRESSE !</strong>",
        "editpage-cannot-use-custom-model": "Le modèle de contenu de cette page ne peut pas être modifié.",
        "longpageerror": "<strong>Erreur : Le texte que vous avez soumis fait {{PLURAL:$1|un Kio|$1 Kio}}, ce qui dépasse la limite fixée à {{PLURAL:$2|un Kio|$2 Kio}}.</strong>\nIl ne peut pas être sauvegardé.",
        "readonlywarning": "<strong>AVERTISSEMENT : la base de données a été verrouillée pour des opérations de maintenance. Vous ne pouvez donc pas publier vos modifications pour l’instant.</strong>\nVous pouvez copier et coller votre texte dans un fichier texte et l’enregistrer pour plus tard.\n\nL’administrateur système ayant verrouillé la base de données a donné l’explication suivante : $1",
-       "protectedpagewarning": "'''AVERTISSEMENT : cette page est protégée. Seuls les utilisateurs ayant le statut d'administrateur peuvent la modifier.'''<br />\nLa dernière entrée du journal est affichée ci-dessous pour référence :",
-       "semiprotectedpagewarning": "'''Note :''' Cette page a été protégée de telle façon que seuls les contributeurs enregistrés puissent la modifier. La dernière entrée du journal est affichée ci-dessous pour référence :",
+       "protectedpagewarning": "<strong>AVERTISSEMENT : cette page est protégée afin que seuls les utilisateurs ayant le statut d'administrateur puissent la modifier.</strong>\nLa dernière entrée du journal est affichée ci-dessous pour référence :",
+       "semiprotectedpagewarning": "<strong>Note :</strong>Cette page a été protégée de telle façon que seuls les contributeurs enregistrés puissent la modifier. \nLa dernière entrée du journal est affichée ci-dessous pour référence :",
        "cascadeprotectedwarning": "'''ATTENTION :''' Cette page a été protégée de manière à ce que seuls les administrateurs puissent la modifier car elle est transcluse dans {{PLURAL:$1|la page protégée suivante, qui a|les pages protégées suivantes, qui ont}} la « protection en cascade » activée :",
-       "titleprotectedwarning": "'''ATTENTION : Cette page a été protégée de telle manière que des [[Special:ListGroupRights|droits spécifiques]] sont requis pour pouvoir la créer.''' La dernière entrée du journal est affichée ci-dessous pour référence :",
+       "titleprotectedwarning": "<strong>ATTENTION : Cette page a été protégée de telle manière que des [[Special:ListGroupRights|droits spécifiques]] sont requis pour pouvoir la créer.</strong> \nLa dernière entrée du journal est affichée ci-dessous pour référence :",
        "templatesused": "{{PLURAL:$1|Modèle utilisé|Modèles utilisés}} par cette page :",
        "templatesusedpreview": "{{PLURAL:$1|Modèle utilisé|Modèles utilisés}} dans cette prévisualisation :",
        "templatesusedsection": "{{PLURAL:$1|Modèle utilisé|Modèles utilisés}} dans cette section :",
        "nocreatetext": "{{SITENAME}} a restreint la possibilité de créer de nouvelles pages.\nVous pouvez revenir en arrière et modifier une page existante, ou bien [[Special:UserLogin|vous connecter ou créer un compte]].",
        "nocreate-loggedin": "Vous n'avez pas la permission de créer de nouvelles pages.",
        "sectioneditnotsupported-title": "Modification de section non prise en charge",
-       "sectioneditnotsupported-text": "La modification d'une section n'est pas prise en charge pour cette page.",
+       "sectioneditnotsupported-text": "La modification d’une section n’est pas prise en charge pour cette page.",
        "permissionserrors": "Erreur de permissions",
        "permissionserrorstext": "Vous n'avez pas la permission d'effectuer l'opération demandée pour {{PLURAL:$1|la raison suivante|les raisons suivantes}} :",
        "permissionserrorstext-withaction": "Vous ne pouvez pas $2, pour {{PLURAL:$1|la raison suivante|les raisons suivantes}} :",
        "contentmodelediterror": "Vous ne pouvez pas modifier cette révision car son modèle de contenu est <code>$1</code>, ce qui diffère du modèle de contenu actuel de la page <code>$2</code>.",
-       "recreate-moveddeleted-warn": "'''Attention : vous êtes en train de recréer une page qui a été précédemment supprimée.'''\n\nAssurez-vous qu'il est pertinent de poursuivre les modifications sur cette page. Le journal des suppressions et des déplacements est affiché ci-dessous :",
-       "moveddeleted-notice": "Cette page a été supprimée. Le journal des suppressions et des déplacements est affiché ci-dessous pour référence.",
-       "moveddeleted-notice-recent": "Désolé, cette page a été récemment supprimée (dans les dernières 24 heures).\nLes journaux des suppressions et des renommages pour la page sont fournis ci-dessous à titre d’information.",
+       "recreate-moveddeleted-warn": "<strong>Attention : vous êtes en train de recréer une page qui a été précédemment supprimée.</strong>\n\nAssurez-vous qu'il est pertinent de poursuivre les modifications sur cette page. \nLe journal des suppressions et des déplacements pour cette page est affiché ci-dessous à titre d'information :",
+       "moveddeleted-notice": "Cette page a été supprimée. \nLe journal des suppressions et des déplacements de la page est affiché ci-dessous pour référence.",
+       "moveddeleted-notice-recent": "Désolé, cette page a été récemment supprimée (dans les dernières 24 heures).\nLes journaux des suppressions et des renommages pour la page sont fournis ci-dessous pour référence.",
        "log-fulllog": "Voir le journal complet",
-       "edit-hook-aborted": "Échec de la modification par une extension.\nCause inconnue",
-       "edit-gone-missing": "N'a pas pu mettre à jour la page.\nIl semble qu'elle ait été supprimée.",
+       "edit-hook-aborted": "Échec de la modification par une extension.\nAucune explication n’a été retournée.",
+       "edit-gone-missing": "N’a pas pu mettre à jour la page.\nIl semble qu’elle ait été supprimée.",
        "edit-conflict": "Conflit de modification.",
-       "edit-no-change": "Votre modification a été ignorée car aucun changement n'a été fait au texte.",
+       "edit-no-change": "Votre modification a été ignorée car aucun changement na été fait au texte.",
        "postedit-confirmation-created": "La page a été créée.",
        "postedit-confirmation-restored": "La page a été restaurée.",
        "postedit-confirmation-saved": "Votre modification a été enregistrée.",
-       "edit-already-exists": "La nouvelle page n'a pas pu être créée.\nElle existe déjà.",
+       "edit-already-exists": "La nouvelle page na pas pu être créée.\nElle existe déjà.",
        "defaultmessagetext": "Message par défaut",
        "content-failed-to-parse": "Échec de l’analyse syntaxique du contenu de $2 pour le modèle $1 : $3",
        "invalid-content-data": "Données du contenu non valides",
        "content-model-css": "CSS",
        "content-json-empty-object": "Objet vide",
        "content-json-empty-array": "Tableau vide",
+       "deprecated-self-close-category": "Pages utilisant des balises HTML auto-fermantes non valides",
+       "deprecated-self-close-category-desc": "La page contient des balises HTML auto-fermantes non valides, comme <code>&lt;b/></code> ou <code>&lt;span/></code>.  Le comportement de celles-ci changera prochainement pour être en accord avec la spécification HTML5, donc leur utilisation dans le wikitexte est obsolète.",
        "duplicate-args-warning": "<strong>Avertissement :</strong> [[:$1]] appelle [[:$2]] avec plus d'une valeur pour le paramètre « $3 ». Seule la dernière valeur fournie sera utilisée.",
        "duplicate-args-category": "Pages utilisant des arguments dupliqués dans les appels de modèle",
        "duplicate-args-category-desc": "La page contient des appels de modèle qui utilisent des arguments dupliqués, comme <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> ou <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
-       "expensive-parserfunction-warning": "Attention : cette page contient de trop nombreux appels à des fonctions coûteuses de l'analyseur syntaxique.\n\nIl devrait y avoir moins de $2 appel{{PLURAL:$2||s}}, alors qu'il y en a maintenant $1.",
+       "expensive-parserfunction-warning": "<strong>Attention :</strong> cette page contient de trop nombreux appels à des fonctions coûteuses de l’analyseur syntaxique.\n\nIl devrait y avoir moins de $2 appel{{PLURAL:$2||s}}, alors qu’il y en a maintenant $1.",
        "expensive-parserfunction-category": "Pages avec trop d'appels dispendieux de fonctions de l'analyseur syntaxique",
-       "post-expand-template-inclusion-warning": "Attention : Cette page contient trop d'inclusions de modèles. Certaines inclusions ne seront pas effectuées.",
+       "post-expand-template-inclusion-warning": "<strong>Attention :</strong> cette page contient trop d’inclusions de modèles. Certaines inclusions ne seront pas effectuées.",
        "post-expand-template-inclusion-category": "Pages contenant trop d'inclusions de modèles",
-       "post-expand-template-argument-warning": "Attention : Cette page contient au moins un paramètre de modèle dont l'inclusion est rendue impossible. Après extension, celui-ci aurait produit un résultat trop long, il n'a donc pas été inclus.",
+       "post-expand-template-argument-warning": "<strong>Attention :</strong> cette page contient au moins un paramètre de modèle dont la taille après expansion est trop importante. \nCes arguments n’ont donc pas été inclus.",
        "post-expand-template-argument-category": "Pages contenant des paramètres de modèle non évalués",
        "parser-template-loop-warning": "Modèle en boucle détecté : [[$1]]",
-       "parser-template-recursion-depth-warning": "Limite de profondeur des appels de modèles dépassée ($1)",
+       "parser-template-recursion-depth-warning": "Limite de profondeur des appels récursifs de modèles dépassée ($1)",
        "language-converter-depth-warning": "Limite de profondeur du convertisseur de langue dépassée ($1)",
-       "node-count-exceeded-category": "Pages où nombre de nœuds est dépassé",
-       "node-count-exceeded-category-desc": "Cette page dépasse le nombre maximal de nœuds.",
-       "node-count-exceeded-warning": "Page dépassant le nombre de nœuds",
-       "expansion-depth-exceeded-category": "Pages où la profondeur d'expansion est dépassée",
+       "node-count-exceeded-category": "Pages dépassant le nombre de nœuds maximal",
+       "node-count-exceeded-category-desc": "Ces pages dépassent le nombre maximal de nœuds.",
+       "node-count-exceeded-warning": "La page dépasse le nombre de nœuds maximal.",
+       "expansion-depth-exceeded-category": "Pages dépassant la profondeur d'expansion maximale",
        "expansion-depth-exceeded-category-desc": "La page dépasse la profondeur d’expansion maximale.",
-       "expansion-depth-exceeded-warning": "Page dépassant la profondeur d'expansion",
+       "expansion-depth-exceeded-warning": "Page dépassant la profondeur d’expansion maximale",
        "parser-unstrip-loop-warning": "Boucle non démontable détectée",
        "parser-unstrip-recursion-limit": "Limite de récursion non démontable dépassée ($1)",
        "converter-manual-rule-error": "Erreur détectée dans la règle manuelle de conversion de langue",
-       "undo-success": "Cette modification va être défaite. Veuillez vérifier les modifications ci-dessous, puis publier si c’est bien ce que vous voulez faire.",
+       "undo-success": "Cette modification va être annulée.\nVeuillez vérifier les différences ci-dessous, puis publier l’annulation si c’est bien ce que vous voulez faire.",
        "undo-failure": "Cette modification ne peut pas être défaite : cela entrerait en conflit avec les modifications intermédiaires.",
        "undo-norev": "La modification n’a pas pu être défaite parce qu’elle est inexistante ou qu’elle a été supprimée.",
        "undo-nochange": "Il semblerait que la modification ait déjà été annulée.",
        "undo-summary": "Annulation des modifications $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|discussion]])",
        "undo-summary-username-hidden": "Annuler la révision $1 par un utilisateur masqué",
-       "cantcreateaccounttitle": "Vous ne pouvez pas créer de compte.",
-       "cantcreateaccount-text": "La création de compte depuis cette adresse IP (<b>$1</b>) a été bloquée par [[User:$3|$3]].\n\nLa raison donnée était ''$2''.",
-       "cantcreateaccount-range-text": "La création de compte depuis les adresses IP dans la plage <strong>$1</strong>, qui comprend votre adresse IP (<strong>$4</strong>), ont été bloquées par [[User:$3|$3]].\n\nLe motif fourni par $3 est <em>$2</em>",
+       "cantcreateaccount-text": "La création de compte depuis cette adresse IP (<strong>$1</strong>) a été bloquée par [[User:$3|$3]]. \n\nLa raison donnée par $3 était : <em>$2</em>",
+       "cantcreateaccount-range-text": "La création de compte depuis les adresses IP de la plage <strong>$1</strong>, où se trouve votre adresse IP (<strong>$4</strong>), a été bloquée par [[User:$3|$3]].\n\nLe motif fourni par $3 est <em>$2</em>",
        "viewpagelogs": "Voir les opérations sur cette page",
-       "nohistory": "Il n'existe pas d'historique pour cette page.",
+       "nohistory": "Il n’existe pas d’historique des modifications pour cette page.",
        "currentrev": "Version actuelle",
-       "currentrev-asof": "Version actuelle en date du $1",
+       "currentrev-asof": "Version actuelle datée du $1",
        "revisionasof": "Version du $1",
-       "revision-info": "Révision de $1 par {{GENDER:$6|$2}}$7",
+       "revision-info": "Révision datée du $1 par {{GENDER:$6|$2}}$7",
        "previousrevision": "← Version précédente",
        "nextrevision": "Version suivante →",
        "currentrevisionlink": "Voir la version actuelle",
        "last": "diff",
        "page_first": "première",
        "page_last": "dernière",
-       "histlegend": "Légende : ({{int:cur}}) = différence avec la version actuelle, ({{int:last}}) = différence avec la version précédente, <b>{{int:minoreditletter}}</b> = modification mineure",
+       "histlegend": "Diff de sélection : cochez les boîtes radio des versions à comparer et appuyez sur entrée ou sur le bouton en bas.<br />\nLégende: <strong>({{int:cur}})</strong> = différence avec la dernière version, <strong>({{int:last}})</strong> = différence avec la précédente version, <strong>{{int:minoreditletter}}</strong> = modification mineure.",
        "history-fieldset-title": "Naviguer dans l’historique",
-       "history-show-deleted": "Masqués seulement",
+       "history-show-deleted": "Supprimés seulement",
        "histfirst": "les plus anciennes",
        "histlast": "les plus récentes",
        "historysize": "($1 octet{{PLURAL:$1||s}})",
        "historyempty": "(vide)",
        "history-feed-title": "Historique des versions",
-       "history-feed-description": "Historique pour cette page sur le wiki",
+       "history-feed-description": "Historique des versions pour cette page sur le wiki",
        "history-feed-item-nocomment": "$1 le $2",
-       "history-feed-empty": "La page demandée n'existe pas.\nElle a peut-être été effacée ou renommée.\nEssayez de [[Special:Search|rechercher sur le wiki]] pour trouver des pages en rapport.",
-       "history-edit-tags": "Modifier les balises des révisions sélectionnées",
+       "history-feed-empty": "La page demandée n'existe pas.\nElle a peut-être été effacée ou renommée.\nEssayez de [[Special:Search|rechercher sur le wiki]] pour trouver de nouvelles pages en rapport avec le sujet.",
+       "history-edit-tags": "Modifier les balises des versions sélectionnées",
        "rev-deleted-comment": "(résumé de modification retiré)",
        "rev-deleted-user": "(nom d'utilisateur retiré)",
-       "rev-deleted-event": "(détails de l’entrée retirée)",
-       "rev-deleted-user-contribs": "[nom d'utilisateur ou adresse IP retiré - modification masquée sur les contributions]",
-       "rev-deleted-text-permission": "Cette version de la page a été '''effacée'''.\nDes détails sont disponibles dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
+       "rev-deleted-event": "(détails de l’historique retirés)",
+       "rev-deleted-user-contribs": "[nom d’utilisateur ou adresse IP retiré – modification masquée dans les contributions]",
+       "rev-deleted-text-permission": "Cette version de la page a été <strong>effacée</strong>.\nDes détails sont disponibles dans le [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} journal des suppressions].",
        "rev-suppressed-text-permission": "Cette version de la page a été <strong>masquée</strong>.\nLes détails se trouvent dans le [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} journal des masquages].",
        "rev-deleted-text-unhide": "Cette version de la page a été <strong>supprimée</strong>.\nDes détails sont disponibles dans [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} le journal des suppressions].\nVous pouvez toujours [$1 voir cette version] si vous le voulez.",
        "rev-suppressed-text-unhide": "Cette version de la page a été <strong>masquée</strong>.\nDes détails sont disponibles dans [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} le journal des masquages].\nVous pouvez toujours [$1 voir cette version] si vous le voulez.",
        "logdelete-selected": "{{PLURAL:$1|Événement d'historique sélectionné|Événements d'historique sélectionnés}} :",
        "revdelete-text-text": "Les révisions supprimées continueront à apparaître dans l’historique de la page, mais une partie de leur contenu sera inaccessible au public.",
        "revdelete-text-file": "Les versions de fichier supprimées continueront à apparaître dans l’historique des fichiers, mais une partie de leur contenu sera indisponible au public.",
-       "logdelete-text": "Les évènements du journal supprimés continueront à apparaître dans les journaux, mais une partie de leur contenu sera indisponible au public.",
+       "logdelete-text": "Les évènements supprimés du journal continueront à apparaître dans les journaux, mais une partie de leur contenu sera indisponible au public.",
        "revdelete-text-others": "Les autres administrateurs seront toujours en mesure d'accéder au contenu caché et le restaurer, à moins que des restrictions supplémentaires soient fixées.",
        "revdelete-confirm": "Confirmez que vous voulez effectuer cette action, que vous en comprenez les conséquences, et que vous le faites en accord avec [[{{MediaWiki:Policy-url}}|les règles]].",
-       "revdelete-suppress-text": "La suppression ne doit être utilisée '''que''' dans les cas suivants :\n* Informations potentiellement diffamatoires\n* Informations personnelles inappropriées\n*: ''adresse, numéro de téléphone, numéro de sécurité sociale, …''",
-       "revdelete-legend": "Mettre en place des restrictions de visibilité :",
+       "revdelete-suppress-text": "La suppression ne doit être utilisée <strong>que</strong> dans les cas suivants :\n* informations potentiellement diffamatoires\n* informations personnelles inappropriées\n*: <em>adresse, numéro de téléphone, numéro de sécurité sociale, …</em>",
+       "revdelete-legend": "Mettre en place des restrictions de visibilité",
        "revdelete-hide-text": "Texte de la révision",
        "revdelete-hide-image": "Masquer le contenu du fichier",
        "revdelete-hide-name": "Masquer la cible et les paramètres",
        "revdelete-radio-same": "(ne pas changer)",
        "revdelete-radio-set": "Masqué",
        "revdelete-radio-unset": "Visible",
-       "revdelete-suppress": "Masquer également les données pour les administrateurs",
+       "revdelete-suppress": "Supprimer également les données des administrateurs",
        "revdelete-unsuppress": "Enlever les restrictions sur les versions restaurées",
        "revdelete-log": "Motif :",
        "revdelete-submit": "Appliquer {{PLURAL:$1|à la révision sélectionnée|aux révisions sélectionnées}}",
        "revdelete-no-change": "'''Attention :''' L'élément daté du $1 à $2 a déjà les paramètres de visibilité demandés.",
        "revdelete-concurrent-change": "Erreur lors de la modification de l'élément daté du $1 à $2 : son statut a été changé par quelqu'un d'autre pendant que vous le modifiez.\nVérifiez les journaux.",
        "revdelete-only-restricted": "Erreur lors de la suppression de l'entrée datée du $1 à $2 : vous ne pouvez pas supprimer ces éléments aux administrateurs sans également sélectionner les autres options de suppression.",
-       "revdelete-reason-dropdown": "* Raisons courantes de suppression :\n** Violation des droits d'auteurs ;\n** Commentaires ou renseignements personnels inappropriés ;\n** Informations potentiellement diffamatoires.",
+       "revdelete-reason-dropdown": "* Raisons courantes de suppression \n** Violation des droits d'auteurs \n** Commentaires ou renseignements personnels inappropriés \n** Nom d'utilisateur inapproprié\n** Informations potentiellement diffamatoires",
        "revdelete-otherreason": "Autre raison / raison supplémentaire :",
        "revdelete-reasonotherlist": "Autre raison",
        "revdelete-edit-reasonlist": "Modifier les motifs fréquents de suppression",
        "mergehistory-from": "Page d'origine :",
        "mergehistory-into": "Page de destination :",
        "mergehistory-list": "Historique fusionnable des modifications",
-       "mergehistory-merge": "Les versions suivantes de [[:$1]] peuvent être fusionnées avec [[:$2]]. Utilisez la colonne de boutons radio pour fusionner uniquement les versions créées du début jusqu'à la date indiquée. Notez bien que l'utilisation des liens de navigation réinitialisera cette colonne.",
+       "mergehistory-merge": "Les versions suivantes de [[:$1]] peuvent être fusionnées avec [[:$2]]. \nUtilisez la colonne de boutons radio pour fusionner uniquement les versions créées du début jusqu'à la date indiquée. \nNotez bien que l'utilisation des liens de navigation réinitialisera cette colonne.",
        "mergehistory-go": "Voir les modifications qui peuvent être fusionnées",
        "mergehistory-submit": "Fusionner les versions",
        "mergehistory-empty": "Aucune version ne peut être fusionnée.",
        "mergehistory-no-destination": "La page de destination $1 n'existe pas.",
        "mergehistory-invalid-source": "La page d'origine doit avoir un titre valide.",
        "mergehistory-invalid-destination": "La page de destination doit avoir un titre valide.",
-       "mergehistory-autocomment": "[[:$1]] fusionnée avec [[:$2]]",
-       "mergehistory-comment": "[[:$1]] fusionnée avec [[:$2]] : $3",
-       "mergehistory-same-destination": "Les pages d'origine et de destination ne peuvent pas être la même",
+       "mergehistory-autocomment": "[[:$1]] fusionnée dans [[:$2]]",
+       "mergehistory-comment": "[[:$1]] fusionnée dans [[:$2]] : $3",
+       "mergehistory-same-destination": "Les pages d'origine et de destination ne peuvent pas être les mêmes",
        "mergehistory-reason": "Motif :",
        "mergehistory-revisionrow": "$1 ($2) $3 — $4 $5 $6",
        "mergelog": "Journal des fusions",
        "revertmerge": "Séparer",
-       "mergelogpagetext": "Voici la liste des plus récentes fusions de l’historique d’une page dans celui d’une autre.",
+       "mergelogpagetext": "Voici la liste des fusions les plus récentes de l’historique d’une page dans celui d’une autre.",
        "history-title": "$1 : Historique des versions",
        "difference-title": "$1 : Différence entre versions",
        "difference-title-multipage": "Différences entre les pages « $1 » et « $2 »",
        "nextn-title": "$1 {{PLURAL:$1|résultat suivant|résultats suivants}}",
        "shown-title": "Afficher $1 résultat{{PLURAL:$1||s}} par page",
        "viewprevnext": "Voir ($1 {{int:pipe-separator}} $2) ($3).",
-       "searchmenu-exists": "'''Il existe une page nommée « [[:$1]] » sur ce wiki.''' {{PLURAL:$2|0=|Voyez également les autres résultats de votre recherche.}}",
-       "searchmenu-new": "<strong>Créer la page « [[:$1|$1]] » sur ce wiki !</strong> {{PLURAL:$2|0=|Voyez également la page trouvée avec votre recherche.|Voyez également les résultats de votre recherche.}}",
+       "searchmenu-exists": "<strong>Il existe une page nommée « [[:$1]] » sur ce wiki.</strong> {{PLURAL:$2|0=|Voyez également les autres résultats de votre recherche.}}",
+       "searchmenu-new": "<strong>Créer la page « [[:$1|$1]] » sur ce wiki !</strong> {{PLURAL:$2|0=Voyez également la page trouvée avec votre recherche.|Voyez également les résultats de votre recherche.}}",
        "searchprofile-articles": "Pages de contenu",
        "searchprofile-images": "Multimédia",
        "searchprofile-everything": "Tout",
        "searchprofile-articles-tooltip": "Rechercher dans $1",
        "searchprofile-images-tooltip": "Rechercher des fichiers multimédias",
        "searchprofile-everything-tooltip": "Rechercher dans tout le site (y compris dans les pages de discussion)",
-       "searchprofile-advanced-tooltip": "Choisir les espaces de noms pour la recherche",
+       "searchprofile-advanced-tooltip": "Chercher dans les espaces de noms clients",
        "search-result-size": "$1 ($2 mot{{PLURAL:$2||s}})",
        "search-result-category-size": "$1 membre{{PLURAL:$1||s}} ($2 sous-catégorie{{PLURAL:$2||s}}, $3 fichier{{PLURAL:$3||s}})",
        "search-redirect": "(redirection depuis $1)",
        "prefs-editwatchlist-clear": "Effacer la liste de suivi",
        "prefs-watchlist-days": "Nombre de jours à afficher dans la liste de suivi :",
        "prefs-watchlist-days-max": "(maximum $1 jour{{PLURAL:$1||s}})",
-       "prefs-watchlist-edits": "Nombre de modifications à afficher dans la liste de suivi étendue :",
+       "prefs-watchlist-edits": "Nombre maximum de modifications à afficher dans la liste de suivi étendue :",
        "prefs-watchlist-edits-max": "Nombre maximum : 1000",
        "prefs-watchlist-token": "Jeton pour la liste de suivi :",
        "prefs-misc": "Préférences diverses",
        "prefs-emailconfirm-label": "Confirmation du courriel :",
        "youremail": "Courriel :",
        "username": "{{GENDER:$1|Nom d'utilisateur|Nom d'utilisatrice}} :",
-       "prefs-memberingroups": "{{GENDER:$2|Membre}} {{PLURAL:$1|du groupe|des groupes}}:",
+       "prefs-memberingroups": "{{GENDER:$2|Membre}} {{PLURAL:$1|du groupe|des groupes}} :",
        "prefs-registration": "Date d'inscription :",
        "yourrealname": "Nom réel :",
        "yourlanguage": "Langue :",
        "yourvariant": "Variante de la langue du contenu :",
-       "prefs-help-variant": "Votre variante ou orthographe préféré dans lequel afficher les pages de contenu de ce wiki.",
-       "yournick": "Signature pour les discussions :",
+       "prefs-help-variant": "Votre variante ou orthographe préférée dans laquelle afficher les pages de contenu de ce wiki.",
+       "yournick": "Nouvelle signature :",
        "prefs-help-signature": "Les commentaires sur les pages de discussion doivent être signés avec « <nowiki>~~~~</nowiki> », qui sera remplacé par votre signature et un horodatage.",
        "badsig": "Signature brute incorrecte.\nVérifiez les balises HTML.",
        "badsiglength": "Votre signature est trop longue.\nElle ne doit pas dépasser $1 caractère{{PLURAL:$1||s}}.",
        "gender-unknown": "Lorsqu’il fera mention de vous, le logiciel utilisera des mots de genre neutre, quand c’est possible",
        "gender-male": "Il modifie des pages du wiki",
        "gender-female": "Elle modifie des pages du wiki",
-       "prefs-help-gender": "Définir cette préférence est facultatif.\nCe logiciel utilise sa valeur pour s’adresser à vous et vous mentionner aux autres en utilisant le bon genre grammatical.\nCette information sera publique.",
+       "prefs-help-gender": "Définir cette préférence est facultatif.\nLe logiciel utilise la valeur pour s’adresser à vous ou pour vous mentionner aux autres en utilisant le bon genre grammatical.\nCette information sera publique.",
        "email": "Courriel",
        "prefs-help-realname": "Le vrai nom est facultatif.\nS’il est fourni, il sera utilisé pour vous attribuer vos contributions.",
        "prefs-help-email": "L'adresse de courriel est facultative, mais elle est nécessaire pour réinitialiser votre mot de passe, si vous veniez à l'oublier.",
-       "prefs-help-email-others": "Vous pourriez aussi choisir de laisser les autres vous contacter sur votre page de discussion utilisateur sans que soit nécessaire de révéler votre identité.",
+       "prefs-help-email-others": "Vous pouvez aussi choisir de laisser les autres vous contacter par courriel via un lien sur votre page de discussion ou page utilisateur. \nVotre adresse courriel n'est pas révélée quand les autres utilisateurs vous contactent.",
        "prefs-help-email-required": "Une adresse de courriel est requise.",
        "prefs-info": "Informations de base",
        "prefs-i18n": "Internationalisation",
        "userrights-groupsmember-auto": "Membre implicite de :",
        "userrights-groups-help": "Vous pouvez modifier les groupes auxquels appartient {{GENDER:$1|cet utilisateur|cette utilisatrice}} :\n* Une case cochée signifie que l’utilisat{{GENDER:$1|eur|rice}} se trouve dans ce groupe.\n* Une case non cochée signifie qu’{{GENDER:$1|il|elle}} ne s’y trouve pas.\n* Un astérisque (*) indique que vous ne pouvez pas retirer ce groupe une fois que vous l’avez ajouté, ou vice-versa.",
        "userrights-reason": "Motif :",
-       "userrights-no-interwiki": "Vous n'avez pas la permission de modifier des droits d'utilisateurs sur d'autres wikis.",
-       "userrights-nodatabase": "La base de donnée « $1 » n'existe pas ou n'est pas locale.",
-       "userrights-nologin": "Vous devez vous [[Special:UserLogin|connecter]] avec un compte d'administrateur pour modifier des droits d'utilisateur.",
-       "userrights-notallowed": "Vous n’avez pas la permission d’ajouter ou supprimer des droits d’utilisateur.",
+       "userrights-no-interwiki": "Vous n'avez pas la permission de modifier les droits utilisateur sur d'autres wikis.",
+       "userrights-nodatabase": "La base de données « $1 » n'existe pas ou n'est pas locale.",
+       "userrights-nologin": "Vous devez vous [[Special:UserLogin|connecter]] avec un compte d'administrateur pour modifier les droits utilisateur.",
+       "userrights-notallowed": "Vous n’avez pas la permission d’ajouter ou de supprimer des droits utilisateur.",
        "userrights-changeable-col": "Les groupes que vous pouvez modifier",
        "userrights-unchangeable-col": "Les groupes que vous ne pouvez pas modifier",
-       "userrights-conflict": "Conflit de modification de droits utilisateur ! Veuillez relire et confirmer vos modifications.",
+       "userrights-conflict": "Conflit de modification des droits utilisateur ! Veuillez relire et confirmer vos modifications.",
        "userrights-removed-self": "Vous avez supprimé vos propres droits. Par conséquent, vous ne pouvez plus accéder à cette page.",
        "group": "Groupe :",
        "group-user": "Utilisateurs",
        "right-createtalk": "Créer des pages de discussion",
        "right-createaccount": "Créer des comptes utilisateur",
        "right-autocreateaccount": "Connexion automatique avec un compte utilisateur externe",
-       "right-minoredit": "Marquer ses modifications comme mineures",
+       "right-minoredit": "Marquer les modifications comme mineures",
        "right-move": "Renommer des pages",
        "right-move-subpages": "Renommer des pages avec leurs sous-pages",
        "right-move-rootuserpages": "Renommer la page principale d'un utilisateur",
        "right-move-categorypages": "Renommer des pages de catégorie",
        "right-movefile": "Renommer des fichiers",
-       "right-suppressredirect": "Ne pas créer de redirection depuis le titre d'origine en renommant une page",
+       "right-suppressredirect": "Ne pas créer de redirection depuis le titre d'origine en renommant les pages",
        "right-upload": "Importer des fichiers",
        "right-reupload": "Écraser un fichier existant",
        "right-reupload-own": "Écraser un fichier que l'on a soi-même importé",
-       "right-reupload-shared": "Écraser localement un fichier présent sur un dépôt partagé",
+       "right-reupload-shared": "Écraser localement des fichiers présents sur un dépôt partagé",
        "right-upload_by_url": "Importer un fichier depuis une adresse URL",
-       "right-purge": "Purger le cache des pages sans demande de confirmation",
+       "right-purge": "Effacer une page du cache local sans demander de confirmation",
        "right-autoconfirmed": "Ne pas être affecté par les limitations de débit liées aux adresses IP",
        "right-bot": "Être traité comme un processus automatisé",
        "right-nominornewtalk": "Ne pas déclencher la notification de nouveau message lorsqu'on effectue une modification mineure sur la page de discussion d'un utilisateur",
        "right-writeapi": "Utiliser l'API de modification du wiki",
        "right-delete": "Supprimer des pages",
        "right-bigdelete": "Supprimer des pages ayant un gros historique",
-       "right-deletelogentry": "Supprimer et restaurer une entrée particulière du journal",
-       "right-deleterevision": "Supprimer ou restaurer une version particulière d'une page",
+       "right-deletelogentry": "Supprimer et restaurer des entrées particulières du journal",
+       "right-deleterevision": "Supprimer ou restaurer des versions particulières de pages",
        "right-deletedhistory": "Voir les entrées des historiques supprimées, mais sans leur texte",
        "right-deletedtext": "Voir le texte supprimé et les différences entre les versions supprimées",
        "right-browsearchive": "Rechercher des pages supprimées",
        "right-editmyuserjs": "Modifier vos propres fichiers JavaScript utilisateur",
        "right-viewmywatchlist": "Afficher votre propre liste de suivi",
        "right-editmywatchlist": "Modifier votre propre liste de suivi. Remarquez que certaines actions ajouteront encore des pages sans ce droit.",
-       "right-viewmyprivateinfo": "Voir vos données personnelles (exemple adresse, vrai nom)",
-       "right-editmyprivateinfo": "Modifier vos données personnelles (exemple adresse, vrai nom)",
+       "right-viewmyprivateinfo": "Voir vos données personnelles (par exemple votre adresse, votre vrai nom)",
+       "right-editmyprivateinfo": "Modifier vos données personnelles (par exemple votre adresse, votre vrai nom)",
        "right-editmyoptions": "Modifier vos préférences",
        "right-rollback": "Révoquer rapidement les modifications du dernier contributeur d'une page particulière",
        "right-markbotedits": "Marquer des modifications révoquées comme ayant été faites par un robot.",
        "right-noratelimit": "Ne pas être affecté par les limites de taux",
        "right-import": "Importer des pages depuis d'autres wikis",
        "right-importupload": "Importer des pages depuis un fichier",
-       "right-patrol": "Marquer des modifications des autres comme relues",
+       "right-patrol": "Marquer les modifications des autres comme étant relues",
        "right-autopatrol": "Avoir ses modifications automatiquement marquées comme relues",
        "right-patrolmarks": "Voir les marquages de relecture dans les modifications récentes",
        "right-unwatchedpages": "Voir la liste des pages non suivies",
        "right-mergehistory": "Fusionner les historiques des pages",
        "right-userrights": "Modifier tous les droits d'un utilisateur",
-       "right-userrights-interwiki": "Modifier les droits d'utilisateurs qui sont sur un autre wiki",
+       "right-userrights-interwiki": "Modifier les droits des utilisateurs qui sont sur d'autres wikis",
        "right-siteadmin": "Verrouiller ou déverrouiller la base de données",
        "right-override-export-depth": "Exporter les pages en incluant les pages liées jusqu'à une profondeur de 5 niveaux",
        "right-sendemail": "Envoyer un courriel aux autres utilisateurs",
        "grant-group-high-volume": "Effectuer une activité de fort volume",
        "grant-group-customization": "Personnalisation et préférences",
        "grant-group-administration": "Effectuer des actions administratives",
+       "grant-group-private-information": "Accéder à vos données privées",
        "grant-group-other": "Activités diverses",
        "grant-blockusers": "Bloquer et débloquer des utilisateurs",
        "grant-createaccount": "Créer des comptes",
        "grant-editprotected": "Modifier des pages protégées",
        "grant-highvolume": "Modification de gros volumes",
        "grant-oversight": "Masquer les utilisateurs et supprimer les révisions",
-       "grant-patrol": "Patrouiller les modifications aux pages",
+       "grant-patrol": "Vérifier les modifications de pages",
+       "grant-privateinfo": "Accéder aux informations privées",
        "grant-protect": "Protéger et déprotéger des pages",
        "grant-rollback": "Révoquer des modifications sur des pages",
        "grant-sendemail": "Envoyer des courriels aux autres utilisateurs",
        "action-applychangetags": "appliquer les balises avec vos modifications",
        "action-changetags": "ajouter et supprimer de façon arbitraire des balises sur des révisions individuelles et des entrées de journal",
        "action-deletechangetags": "supprimer des balises de la base de données",
+       "action-purge": "purger cette page",
        "nchanges": "$1 modification{{PLURAL:$1||s}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|depuis la dernière visite}}",
        "enhancedrc-history": "historique",
        "newsectionsummary": "/* $1 */ nouvelle section",
        "rc-enhanced-expand": "Voir les détails",
        "rc-enhanced-hide": "Masquer les détails",
-       "rc-old-title": "créé avec le titre « $1 »",
+       "rc-old-title": "créé initialement avec le titre « $1 »",
        "recentchangeslinked": "Suivi des pages liées",
        "recentchangeslinked-feed": "Suivi des pages liées",
        "recentchangeslinked-toolbox": "Suivi des pages liées",
        "recentchangeslinked-title": "Suivi des pages associées à « $1 »",
-       "recentchangeslinked-summary": "Cette page spéciale montre les modifications récentes sur les pages qui sont liées. Les pages de votre liste de suivi sont '''en gras'''.",
+       "recentchangeslinked-summary": "Voici les modifications faites récemment sur des pages liées depuis une page spécifique ou vers des membres d'une catégorie spécifique. \nLes pages de [[Special:Watchlist|votre liste de suivi]] sont <strong>en gras</strong>.",
        "recentchangeslinked-page": "Nom de la page :",
        "recentchangeslinked-to": "Afficher les modifications des pages qui comportent un lien vers la page donnée plutôt que l'inverse",
        "recentchanges-page-added-to-category": "[[:$1]] ajouté à la catégorie",
        "autochange-username": "Modification automatique de MediaWiki",
        "upload": "Importer un fichier",
        "uploadbtn": "Importer le fichier",
-       "reuploaddesc": "Annuler et retourner au formulaire d'import",
+       "reuploaddesc": "Annuler l'importation et retourner au formulaire d'import",
        "upload-tryagain": "Envoyer la description du fichier modifiée",
        "uploadnologin": "Non connecté",
        "uploadnologintext": "Vous devez vous $1 pour importer des fichiers.",
        "upload_directory_missing": "Le répertoire d’import de fichier ($1) est introuvable et n’a pas pu être créé par le serveur web.",
-       "upload_directory_read_only": "Le répertoire d’import de fichier ($1) n’est pas accessible en écriture depuis le serveur web.",
+       "upload_directory_read_only": "Le serveur web n’a pas accès en écriture au répertoire d’import de fichier ($1).",
        "uploaderror": "Erreur lors de l’import",
-       "upload-recreate-warning": "'''Attention : Un fichier portant ce nom a été supprimé ou déplacé.'''\n\nLe journal des suppressions et celui des déplacements de cette page sont affichés ici pour informations :",
+       "upload-recreate-warning": "<strong>Attention : Un fichier portant ce nom a été supprimé ou déplacé.</strong>\n\nLe journal des suppressions et celui des déplacements de cette page sont affichés ici pour informations :",
        "uploadtext": "Utilisez ce formulaire pour importer des fichiers sur le serveur.\nPour voir ou rechercher des images précédemment envoyées, consultez la [[Special:FileList|liste des images]]. L’import est aussi enregistré dans le [[Special:Log/upload|journal d’import des fichiers]], et les suppressions dans le [[Special:Log/delete|journal des suppressions]].\n\nPour inclure un fichier dans une page, utilisez un lien de la forme :\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:fichier.jpg]]</nowiki></code>''', pour afficher le fichier en pleine résolution (dans le cas d’une image) ;\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:fichier.png|200px|thumb|left|texte descriptif]]</nowiki></code>''' pour utiliser une miniature de 200 pixels de large dans une boîte à gauche avec « texte descriptif » comme description ;\n* '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:fichier.ogg]]</nowiki></code>''' pour lier directement vers le fichier sans l’afficher.",
        "upload-permitted": "{{PLURAL:$2|Format|Formats}} de fichiers {{PLURAL:$2|autorisé|autorisés}} : $1.",
        "upload-preferred": "{{PLURAL:$2|Format|Formats}} de fichiers {{PLURAL:$2|préféré|préférés}} : $1.",
        "upload-prohibited": "‎{{PLURAL:$2|Format|Formats}} de fichiers {{PLURAL:$2|interdit|interdits}} : $1.",
        "uploadlogpage": "Journal d’import de fichiers",
-       "uploadlogpagetext": "Voici la liste des derniers fichiers importés sur le serveur.\nVoyez la [[Special:NewFiles|galerie des nouvelles images]] pour une présentation plus visuelle.",
+       "uploadlogpagetext": "Voici les derniers fichiers téléversés.\nVoyez la [[Special:NewFiles|galerie des nouveaux fichiers]] pour une représentation plus visuelle.",
        "filename": "Nom du fichier",
        "filedesc": "Description",
        "fileuploadsummary": "Description :",
        "filestatus": "Statut du droit d'auteur :",
        "filesource": "Source :",
        "ignorewarning": "Ignorer l'avertissement et sauvegarder le fichier quand même",
-       "ignorewarnings": "Ignorer les avertissements",
-       "minlength1": "Le noms de fichiers doivent comprendre au moins une lettre.",
-       "illegalfilename": "Le nom de fichier « $1 » contient des caractères interdits dans les titres de pages. Merci de le renommer et de l'importer à nouveau.",
-       "filename-toolong": "Le nom du fichier ne peut pas dépasser 240 octets.",
+       "ignorewarnings": "Ignorer tous les avertissements",
+       "minlength1": "Les noms de fichiers doivent comprendre au moins une lettre.",
+       "illegalfilename": "Le nom de fichier « $1 » contient des caractères interdits dans les titres de pages. \nMerci de le renommer et de l'importer à nouveau.",
+       "filename-toolong": "Les noms de fichier ne peuvent pas dépasser 240 octets.",
        "badfilename": "Le fichier a été renommé en « $1 ».",
        "filetype-mime-mismatch": "L'extension du fichier « .$1 » ne correspond pas au type MIME détecté du fichier ($2).",
-       "filetype-badmime": "Les fichiers du type MIME « $1 » ne peuvent pas être importés.",
+       "filetype-badmime": "Les fichiers de type MIME « $1 » ne peuvent pas être importés.",
        "filetype-bad-ie-mime": "Le fichier ne peut pas être importé parce qu'il serait détecté comme « $1 » par Internet Explorer, ce qui correspond à un type de fichier interdit car potentiellement dangereux.",
-       "filetype-unwanted-type": "'''« .$1 »''' est un format de fichier non désiré.\n{{PLURAL:$3|Le type de fichier préconisé est|Les types de fichiers préconisés sont}} $2.",
-       "filetype-banned-type": "''' « .$1 » '''{{PLURAL:$4|n'est pas un type de fichier autorisé|ne sont pas des types de fichiers autorisés}}. \n{{PLURAL:$3|le type de fichier autorisé est |les types de fichiers autorisés sont}} $2.",
+       "filetype-unwanted-type": "<strong>« .$1 »</strong> est un format de fichier non désiré.\n{{PLURAL:$3|Le type de fichier préconisé est|Les types de fichiers préconisés sont}} $2.",
+       "filetype-banned-type": "<strong>« .$1 »</strong>{{PLURAL:$4|n'est pas un type de fichier autorisé|ne sont pas des types de fichiers autorisés}}. \n{{PLURAL:$3|Le type de fichier autorisé est |Les types de fichiers autorisés sont}} $2.",
        "filetype-missing": "Le fichier n'a aucune extension (comme « .jpg » par exemple).",
        "empty-file": "Le fichier que vous avez soumis était vide.",
        "file-too-large": "Le fichier que vous avez soumis était trop grand.",
        "unknown-error": "Une erreur inconnue s’est produite.",
        "tmp-create-error": "Impossible de créer le fichier temporaire.",
        "tmp-write-error": "Erreur d'écriture du fichier temporaire.",
-       "large-file": "Les fichiers importés ne devraient pas dépasser $1 ; ce fichier fait $2.",
-       "largefileserver": "La taille de ce fichier est supérieure au maximum autorisé.",
+       "large-file": "Les fichiers importés ne devraient pas dépasser $1 ; \nce fichier fait $2.",
+       "largefileserver": "La taille de ce fichier est supérieure au maximum autorisé par le serveur.",
        "emptyfile": "Le fichier que vous voulez importer semble vide.\nCeci peut être dû à une erreur dans le nom du fichier.\nVeuillez vérifier que vous désirez vraiment importer ce fichier.",
-       "windows-nonascii-filename": "Ce wiki ne supporte pas les noms de fichiers avec des caractères spéciaux.",
+       "windows-nonascii-filename": "Ce wiki ne prend pas en charge les noms de fichiers avec des caractères spéciaux.",
        "fileexists": "Un fichier existe déjà sous ce nom.\nMerci de vérifier <strong>[[:$1]]</strong> si vous n'êtes pas certain{{GENDER:||e|}} de vouloir le remplacer.\n[[$1|thumb]]",
-       "filepageexists": "La page de description pour ce fichier a déjà été créée ici <strong>[[:$1]]</strong>, mais aucun fichier n'existe actuellement sous ce nom.\nLe résumé que vous allez spécifier n'apparaîtra pas sur la page de description.\nPour que ce soit le cas, vous devrez modifier manuellement la page. [[$1|thumb]]",
+       "filepageexists": "La page de description pour ce fichier a déjà été créée ici <strong>[[:$1]]</strong>, mais aucun fichier n'existe actuellement sous ce nom.\nLe résumé que vous allez spécifier n'apparaîtra pas sur la page de description.\nPour que ce soit le cas, vous devrez modifier manuellement la page. \n[[$1|thumb]]",
        "fileexists-extension": "Un fichier existe avec un nom proche : [[$2|thumb]]\n* Nom du fichier à importer : <strong>[[:$1]]</strong>\n* Nom du fichier existant : <strong>[[:$2]]</strong>\nPeut-être voulez-vous utiliser un nom plus explicite ?",
-       "fileexists-thumbnail-yes": "Le fichier semble être une image en taille réduite ''(vignette)''. [[$1|thumb]]\nVeuillez vérifier le fichier <strong>[[:$1]]</strong>.\nSi le fichier vérifié est la même image avec la taille initiale, il n'y a pas besoin d'importer une version réduite.",
-       "file-thumbnail-no": "Le nom du fichier commence par <strong>$1</strong>.\nIl est possible qu'il s'agisse d'une version réduite ''(vignette)''.\nSi vous disposez du fichier en haute résolution, importez-le, sinon veuillez modifier son nom.",
-       "fileexists-forbidden": "Un fichier avec ce nom existe déjà et ne peut pas être écrasé.\nSi vous voulez toujours importer votre fichier, veuillez revenir en arrière et utiliser un autre nom. [[File:$1|thumb|center|$1]]",
-       "fileexists-shared-forbidden": "Un fichier portant ce nom existe déjà dans le dépôt de fichiers partagé.\nSi vous voulez toujours importer votre fichier, veuillez revenir en arrière et utiliser un autre nom. [[File:$1|thumb|center|$1]]",
+       "fileexists-thumbnail-yes": "Le fichier semble être une image en taille réduite <em>(vignette)</em>. \n[[$1|thumb]]\nVeuillez vérifier le fichier <strong>[[:$1]]</strong>.\nSi le fichier vérifié est la même image avec la taille initiale, il n'y a pas besoin d'importer une version réduite.",
+       "file-thumbnail-no": "Le nom du fichier commence par <strong>$1</strong>.\nIl est possible qu'il s'agisse d'une version réduite <em>(vignette)</em>.\nSi vous disposez du fichier en haute résolution, importez-le, sinon veuillez modifier son nom.",
+       "fileexists-forbidden": "Un fichier avec ce nom existe déjà et ne peut pas être écrasé.\nSi vous voulez toujours importer votre fichier, veuillez revenir en arrière et utiliser un autre nom. \n[[File:$1|thumb|center|$1]]",
+       "fileexists-shared-forbidden": "Un fichier portant ce nom existe déjà dans le dépôt de fichiers partagé.\nSi vous voulez toujours importer votre fichier, veuillez revenir en arrière et utiliser un autre nom. \n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Le fichier téléchargé est une copie exacte de la version actuelle de <strong>[[:$1]]</strong>",
+       "fileexists-duplicate-version": "Le fichier téléversé est une copie exacte {{PLURAL:$2|d'une version précédente|de versions précédentes}} de <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Ce fichier est un doublon {{PLURAL:$1|du fichier suivant|des fichiers suivants}} :",
-       "file-deleted-duplicate": "Un fichier identique à celui-ci ([[:$1]]) a déjà été supprimé. Vous devriez vérifier le journal des suppressions de ce fichier avant de l'importer à nouveau.",
+       "file-deleted-duplicate": "Un fichier identique à celui-ci ([[:$1]]) a déjà été supprimé. \nVous devriez vérifier le journal des suppressions de ce fichier avant de l'importer à nouveau.",
        "file-deleted-duplicate-notitle": "Un fichier identique à ce fichier a déjà été supprimé ainsi que le titre. \nVous devriez demander à quelqu'un la possibilité de vérifier le journal de ce fichier supprimé afin d'examiner la situation  avant de l'importer à nouveau.",
        "uploadwarning": "Attention !",
        "uploadwarning-text": "Modifiez la description du fichier et essayez de nouveau.",
        "uploaddisabled": "Désolé, l’import de fichiers est désactivé.",
        "copyuploaddisabled": "Import de fichier par URL désactivé.",
        "uploaddisabledtext": "L’import de fichiers est désactivé sur ce wiki.",
-       "php-uploaddisabledtext": "L'import de fichiers a été désactivé dans PHP. Vérifiez l'option de configuration file_uploads.",
+       "php-uploaddisabledtext": "L'import de fichiers est désactivé en PHP. Vérifiez l'option de configuration file_uploads.",
        "uploadscripted": "Ce fichier contient du code HTML ou un script qui pourrait être interprété de façon incorrecte par un navigateur web.",
        "upload-scripted-pi-callback": "Impossible de charger un fichier qui contient des instructions de traitement de feuille de style XML.",
        "uploaded-script-svg": "Élément scriptable « $1 » trouvé dans le fichier SVG téléchargé.",
-       "uploaded-hostile-svg": "CSS non sûr trouvé dans l’élément style d’un fichier SVG téléchargé.",
+       "uploaded-hostile-svg": "CSS non sûr trouvé dans l’élément style d’un fichier SVG téléversé.",
        "uploaded-event-handler-on-svg": "Fixer des attributs de gestionnaire d’événement <code>$1=\"$2\"</code> n’est pas autorisé dans les fichiers SVG.",
        "uploaded-href-attribute-svg": "les attributs href dans les fichiers SVG ne sont autorisés que pour faire référence à des cibles http:// ou https://, <code>&lt;$1 $2=\"$3\"&gt;</code> trouvé.",
-       "uploaded-href-unsafe-target-svg": "href vers des données non sûres trouvé dans le fichier SVG téléchargé : URI cible <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "Un href vers des données non sûres a été trouvé dans le fichier SVG téléchargé : URI cible <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-animate-svg": "Balise « animate » trouvée, qui pourrait modifier le href en utilisant l’attribut « from » <code>&lt;$1 $2=\"$3\"&gt;</code> dans le fichier SVG téléchargé.",
-       "uploaded-setting-event-handler-svg": "Positionner des attributs de gestionnaire d’événement est bloqué, <code>&lt;$1 $2=\"$3\"&gt;</code> trouvé dans le fichier SVG téléchargé.",
+       "uploaded-setting-event-handler-svg": "Positionner les attributs du gestionnaire d’événements n'est pas possbile, <code>&lt;$1 $2=\"$3\"&gt;</code> trouvé dans le fichier SVG téléchargé.",
        "uploaded-setting-href-svg": "L’utilisation de la balise « set » pour ajouter un attribut « href » à l’élément parent est interdite.",
        "uploaded-wrong-setting-svg": "L’utilisation de la balise « set » pour ajouter une cible distante/données/script à un attribut quelconque est interdite. <code>&lt;set to=\"$1\"&gt;</code> a été trouvé dans le fichier SVG téléchargé.",
        "uploaded-setting-handler-svg": "Les SVG qui positionnent l’attribut « handler » avec distant/données/script sont interdits. <code>$1=\"$2\"</code> a été trouvé dans le fichier SVG téléchargé.",
        "watchthisupload": "Suivre ce fichier",
        "filewasdeleted": "Un fichier avec ce nom a déjà été importé, puis supprimé.\nVous devriez vérifier le $1 avant de l'importer à nouveau.",
        "filename-thumb-name": "Ce titre ressemble à celui d’une vignette. Évitez d’importer des vignettes d’une image déjà présente sur le wiki. Si ce n’est pas le cas, veuillez corriger le nom de fichier afin qu’il soit plus descriptif et qu’il ne commence pas comme un titre de vignette.",
-       "filename-bad-prefix": "Le nom du fichier commence par '''« $1 »''' qui est typiquement un nom attribué automatiquement par les appareils photo numériques.\nVeuillez choisir un nom de fichier descriptif.",
+       "filename-bad-prefix": "Le nom du fichier commence par <strong>« $1 »</strong> qui est typiquement un nom non-descriptif attribué automatiquement par les appareils photo numériques.\nVeuillez choisir un nom de fichier plus descriptif.",
        "filename-prefix-blacklist": " #<!-- laisser cette ligne telle quelle --><pre>\n# La syntaxe est la suivante :\n#  * Tout ce qui figure entre un caractère \"#\" jusqu’à la fin de la ligne est un commentaire ;\n#  * Toute ligne non vide est un préfixe typique de nom de fichier assigné automatiquement par les appareils numériques :\nCIMG # Casio\nDSC_ # Nikon\nDSCF # Fuji\nDSCN # Nikon\nDUW # certains téléphones mobiles\nIMG # générique\nJD # Jenoptik\nMGP # Pentax\nPICT # divers\n #</pre><!-- laisser cette ligne telle quelle -->",
        "upload-proto-error": "Protocole incorrect",
-       "upload-proto-error-text": "L’import requiert des URL commençant par <code>http://</code> ou <code>ftp://</code>.",
+       "upload-proto-error-text": "L’import à distance requiert que les URL commencent par <code>http://</code> ou <code>ftp://</code>.",
        "upload-file-error": "Erreur interne",
-       "upload-file-error-text": "Une erreur interne est survenue en voulant créer un fichier temporaire sur le serveur. Veuillez contacter un [[Special:ListUsers/sysop|administrateur]].",
+       "upload-file-error-text": "Une erreur interne est survenue en voulant créer un fichier temporaire sur le serveur. \nVeuillez contacter un [[Special:ListUsers/sysop|administrateur]].",
        "upload-misc-error": "Erreur d’import inconnue",
        "upload-misc-error-text": "Une erreur inconnue est survenue pendant l’import.\nVeuillez vérifier que l’URL est valide et accessible, puis essayer à nouveau.\nSi le problème persiste, contactez un [[Special:ListUsers/sysop|administrateur]].",
        "upload-too-many-redirects": "L’URL contient trop de redirections.",
        "upload-dialog-button-upload": "Téléverser",
        "upload-form-label-infoform-title": "Détails",
        "upload-form-label-infoform-name": "Nom",
-       "upload-form-label-infoform-name-tooltip": "Un titre descriptif unique pour le fichier, qui servira comme nom de fichier. Vous pouvez utiliser du langage courant avec des espaces. Ne pas inclure l’extension du fichier.",
+       "upload-form-label-infoform-name-tooltip": "Un titre descriptif unique pour le fichier, qui servira comme nom de fichier. Vous pouvez utiliser le langage courant avec des espaces. Ne pas inclure l’extension du fichier.",
        "upload-form-label-infoform-description": "Description",
        "upload-form-label-infoform-description-tooltip": "Décrire brièvement tout ce qu’il y a de particulier concernant cette œuvre.\nPour une photo, mentionner les choses principales qui sont vues, l’occasion, ou l’endroit.",
        "upload-form-label-usage-title": "Utilisation",
        "upload-form-label-not-own-work-local-generic-local": "Vous pouvez aussi essayer [[Special:Upload|la page de téléchargement par défaut]].",
        "upload-form-label-own-work-message-generic-foreign": "Je comprends que je téléverse ce fichier vers un dépôt partagé. Je confirme agir en accord avec les conditions d’utilisation et les règles relatives aux licences de celui-ci.",
        "upload-form-label-not-own-work-message-generic-foreign": "Si vous n’êtes pas en mesure de téléverser ce fichier de façon conforme aux règles de ce dépôt partagé, veuillez fermer cette boîte de dialogue et essayer une autre méthode.",
-       "upload-form-label-not-own-work-local-generic-foreign": "Vous pouvez également essayer d’utiliser [[Special:Upload|la page de téléversement de {{SITENAME}}]], si les règles du site autorisent le téléversement du fichier.",
-       "backend-fail-stream": "Impossible de lire le fichier $1.",
-       "backend-fail-backup": "Impossible de sauvegarder le fichier $1.",
+       "upload-form-label-not-own-work-local-generic-foreign": "Vous pouvez également essayer d’utiliser [[Special:Upload|la page de téléversement de {{SITENAME}}]], si leurs règles autorisent le téléversement du fichier.",
+       "backend-fail-stream": "Impossible de lire le fichier \"$1\".",
+       "backend-fail-backup": "Impossible de sauvegarder le fichier \"$1\".",
        "backend-fail-notexists": "Le fichier $1 n’existe pas.",
        "backend-fail-hashes": "Impossible d’obtenir les hachages du fichier pour comparaison.",
-       "backend-fail-notsame": "Un fichier différent existe déjà pour $1 .",
-       "backend-fail-invalidpath": "$1 n’est pas un chemin de stockage valide.",
-       "backend-fail-delete": "Impossible de supprimer le fichier $1.",
+       "backend-fail-notsame": "Un fichier différent existe déjà pour \"$1\" .",
+       "backend-fail-invalidpath": "« $1 » n’est pas un chemin de stockage valide.",
+       "backend-fail-delete": "Impossible de supprimer le fichier \"$1\".",
        "backend-fail-describe": "Impossible de modifier les métadonnées du fichier « $1 ».",
-       "backend-fail-alreadyexists": "Le fichier $1 existe déjà.",
-       "backend-fail-store": "Impossible de stocker le fichier $1 en $2.",
-       "backend-fail-copy": "Impossible de copier le fichier $1 en $2.",
-       "backend-fail-move": "Impossible de déplacer le fichier $1 en $2.",
+       "backend-fail-alreadyexists": "Le fichier \"$1\" existe déjà.",
+       "backend-fail-store": "Impossible de stocker le fichier \"$1\" en \"$2\".",
+       "backend-fail-copy": "Impossible de copier le fichier « $1 » en « $2 ».",
+       "backend-fail-move": "Impossible de déplacer le fichier \"$1\" en \"$2\".",
        "backend-fail-opentemp": "Impossible d’ouvrir le fichier temporaire.",
        "backend-fail-writetemp": "Impossible d’écrire dans le fichier temporaire.",
        "backend-fail-closetemp": "Impossible de fermer le fichier temporaire.",
-       "backend-fail-read": "Impossible de lire le fichier $1.",
-       "backend-fail-create": "Impossible d’écrire le fichier $1.",
-       "backend-fail-maxsize": "Impossible d’écrire le fichier $1 parce qu’il est plus grand {{PLURAL:$2|qu’un octet|que $2 octets}}.",
+       "backend-fail-read": "Impossible de lire le fichier \"$1\".",
+       "backend-fail-create": "Impossible d’écrire le fichier « $1 ».",
+       "backend-fail-maxsize": "Impossible d’écrire le fichier « $1 » parce qu’il est plus grand {{PLURAL:$2|qu’un octet|que $2 octets}}.",
        "backend-fail-readonly": "Le support de stockage « $1 » est actuellement en lecture seule. La raison indiquée est : <em>$2</em>",
        "backend-fail-synced": "Le fichier « $1 » est dans un état incohérent dans les supports de stockage internes",
        "backend-fail-connect": "Impossible de se connecter au support de stockage « $1 ».",
        "backend-fail-internal": "Une erreur inconnue s’est produite dans le support de stockage « $1 ».",
        "backend-fail-contenttype": "Impossible de déterminer le type de contenu du fichier à stocker en « $1 ».",
-       "backend-fail-batchsize": "Le support de stockage a fourni un lot de $1 {{PLURAL:$1|opération|opérations}} de fichier; la limite est $2 {{PLURAL:$2|opération|opérations}}.",
-       "backend-fail-usable": "Impossible de lire ou d’écrire le fichier « $1 » en raison de droits insuffisants ou répertoires/conteneurs manquants.",
+       "backend-fail-batchsize": "On a fourni au support de stockage un lot de $1 {{PLURAL:$1|opération|opérations}} de fichier; la limite est $2 {{PLURAL:$2|opération|opérations}}.",
+       "backend-fail-usable": "Impossible de lire ou d’écrire le fichier « $1 » en raison de droits insuffisants ou de répertoires/conteneurs manquants.",
        "filejournal-fail-dbconnect": "Impossible de se connecter à la base de données du journal pour le terminal de stockage « $1 ».",
        "filejournal-fail-dbquery": "Impossible de mettre à jour la base de données du journal pour le terminal de stockage « $1 ».",
        "lockmanager-notlocked": "Impossible de déverrouiller « $1 » ; elle n'est pas verrouillée.",
        "zip-file-open-error": "Une erreur s'est produite lors de l'ouverture du fichier ZIP pour contrôle.",
        "zip-wrong-format": "Le fichier spécifié n'est pas une archive ZIP.",
        "zip-bad": "Le fichier est une archive ZIP corrompue ou illisible.\nIl ne peut pas être correctement vérifié pour la sécurité.",
-       "zip-unsupported": "Le fichier est une archive ZIP qui utilise des caractéristiques non supportées par MediaWiki. \nSa sécurité ne peut pas être correctement vérifiée.",
+       "zip-unsupported": "Le fichier est une archive ZIP qui utilise des caractéristiques non prises en charge par MediaWiki.\nSa sécurité ne peut pas être correctement vérifiée.",
        "uploadstash": "Cache d’import",
        "uploadstash-summary": "Cette page donne accès aux fichiers qui sont importés (ou en cours d’importation), mais ne sont pas encore publiés dans le wiki. Ces fichiers ne sont pas encore visibles, sauf pour l’utilisateur qui les a importés.",
-       "uploadstash-clear": "Effacer les fichiers en cache",
+       "uploadstash-clear": "Effacer les fichiers en cache d'import",
        "uploadstash-nofiles": "Vous n’avez pas de fichiers en cache d’import.",
        "uploadstash-badtoken": "L’exécution de cette action a échoué, peut-être parce que vos informations d’identification ont expiré. Veuillez réessayer.",
        "uploadstash-errclear": "La suppression des fichiers a échoué.",
        "uploadstash-refresh": "Actualiser la liste des fichiers",
        "uploadstash-thumbnail": "afficher la vignette",
+       "uploadstash-exception": "Impossible de stocker le téléchargement dans la réserve ($1) : « $2 ».",
        "invalid-chunk-offset": "Offset de segment non valide",
        "img-auth-accessdenied": "Accès refusé",
-       "img-auth-nopathinfo": "PATH_INFO manquant.\nVotre serveur n'est pas paramétré pour transmettre cette information.\nIl fonctionne peut-être en CGI et ne supporte pas img_auth.\nVoir : https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
+       "img-auth-nopathinfo": "PATH_INFO manquant.\nVotre serveur n’est pas paramétré pour transmettre cette information.\nIl fonctionne peut-être en CGI et ne prend pas en charge img_auth.\nVoir : https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "img-auth-notindir": "Le chemin demandé n'est pas le répertoire d'import configuré.",
        "img-auth-badtitle": "Impossible de construire un titre valide à partir de « $1 ».",
        "img-auth-nologinnWL": "Vous n'êtes pas connecté et « $1 » n'est pas dans la liste blanche.",
        "img-auth-noread": "L'utilisateur n'a pas le droit en lecture sur « $1 ».",
        "http-invalid-url": "URL incorrecte : $1",
        "http-invalid-scheme": "Les URL avec le schéma « $1 » ne sont pas prises en charge.",
-       "http-request-error": "Erreur inconnue lors de l'envoi de la requête.",
+       "http-request-error": "Erreur inconnue lors de l'envoi de la requête HTTP.",
        "http-read-error": "Erreur de lecture HTTP.",
        "http-timed-out": "La requête HTTP a expiré.",
        "http-curl-error": "Erreur lors de la récupération de l'URL : $1",
        "http-bad-status": "Il y a eu un problème lors de la requête HTTP : $1 $2",
        "upload-curl-error6": "URL injoignable",
-       "upload-curl-error6-text": "L’URL fournie ne peut pas être atteinte. Veuillez vérifier que l’URL est correcte et que le site est en ligne.",
+       "upload-curl-error6-text": "L’URL fournie ne peut pas être atteinte. \nVeuillez vérifier que l’URL est correcte et que le site est en ligne.",
        "upload-curl-error28": "Dépassement du délai lors de l'import",
-       "upload-curl-error28-text": "Le site a mis trop longtemps à répondre. Vérifiez que le site est en ligne, attendez un peu et réessayez. Vous pouvez aussi réessayer à une heure de moindre affluence.",
+       "upload-curl-error28-text": "Le site a mis trop longtemps à répondre. \nVérifiez que le site est en ligne, attendez un peu et réessayez. \nVous pouvez aussi réessayer à une heure de moindre affluence.",
        "license": "Licence",
        "license-header": "Conditions d'utilisation",
        "nolicense": "Aucune licence sélectionnée",
        "nolinkstoimage": "Aucune page n'utilise ce fichier.",
        "morelinkstoimage": "Voir [[Special:WhatLinksHere/$1|plus de liens]] vers ce fichier.",
        "linkstoimage-redirect": "$1 (redirection de fichier) $2",
-       "duplicatesoffile": "{{PLURAL:$1|Le fichier suivant est un duplicata|Les fichiers suivants sont des duplicatas}} de celui-ci ([[Special:FileDuplicateSearch/$2|plus de détails]]) :",
+       "duplicatesoffile": "{{PLURAL:$1|Le fichier suivant est un doublon|Les $1 fichiers suivants sont des doublons}} de celui-ci ([[Special:FileDuplicateSearch/$2|plus de détails]]) :",
        "sharedupload": "Ce fichier provient de : $1. Il peut être utilisé par d'autres projets.",
        "sharedupload-desc-there": "Ce fichier provient de : $1. Il peut être utilisé par d'autres projets.\nVeuillez consulter [$2 sa page de description] pour plus d'informations.",
        "sharedupload-desc-here": "Ce fichier provient de $1. Il peut être utilisé par d'autres projets.\nSa description sur sa [$2 page de description] est affichée ci-dessous.",
        "upload-disallowed-here": "Vous ne pouvez pas remplacer ce fichier.",
        "filerevert": "Rétablir $1",
        "filerevert-legend": "Rétablir le fichier",
-       "filerevert-intro": "Vous êtes sur le point de rétablir le fichier '''[[Media:$1|$1]]''' à la [$4 version du $2 à $3].",
+       "filerevert-intro": "Vous êtes sur le point de rétablir le fichier <strong>[[Media:$1|$1]]</strong> à la [$4 version du $2 à $3].",
        "filerevert-comment": "Motif :",
-       "filerevert-defaultcomment": "Retour sur la version du $2, $1 ($3)",
+       "filerevert-defaultcomment": "Retour sur la version du $1 à $2 ($3)",
        "filerevert-submit": "Rétablir",
-       "filerevert-success": "'''[[Media:$1|$1]]''' a été rétabli à [$4 la version du $2 à $3].",
+       "filerevert-success": "<strong>[[Media:$1|$1]]</strong> a été rétabli à [$4 la version du $2 à $3].",
        "filerevert-badversion": "Il n'y a pas localement de version antérieure du fichier qui porte la date indiquée.",
+       "filerevert-identical": "La version actuelle du fichier est déjà identique à celle sélectionnée.",
        "filedelete": "Supprimer $1",
        "filedelete-legend": "Supprimer le fichier",
-       "filedelete-intro": "Vous êtes sur le point de supprimer '''[[Media:$1|$1]]''' ainsi que tout son historique.",
-       "filedelete-intro-old": "Vous êtes en train d'effacer la version de '''[[Media:$1|$1]]''' du [$4 $2 à $3].",
+       "filedelete-intro": "Vous êtes sur le point de supprimer <strong>[[Media:$1|$1]]</strong> ainsi que tout son historique.",
+       "filedelete-intro-old": "Vous êtes en train de supprimer la version <strong>[[Media:$1|$1]]</strong> du [$4 $2 à $3].",
        "filedelete-comment": "Motif :",
        "filedelete-submit": "Supprimer",
-       "filedelete-success": "'''$1''' a été supprimé.",
-       "filedelete-success-old": "La version de '''[[Media:$1|$1]]''' du $2 à $3 a été supprimée.",
-       "filedelete-nofile": "'''$1''' n'existe pas.",
-       "filedelete-nofile-old": "Il n'existe aucune version archivée de '''$1''' avec les attributs indiqués.",
+       "filedelete-success": "<strong>$1</strong> a été supprimé.",
+       "filedelete-success-old": "La version de <strong>[[Media:$1|$1]]</strong> du $2 à $3 a été supprimée.",
+       "filedelete-nofile": "<strong>$1</strong> n'existe pas.",
+       "filedelete-nofile-old": "Il n'existe aucune version archivée de <strong>$1</strong> avec les attributs indiqués.",
        "filedelete-otherreason": "Motif autre / supplémentaire :",
        "filedelete-reason-otherlist": "Autre motif",
        "filedelete-reason-dropdown": "* Motifs fréquents de suppression de fichiers\n** Violation du droit d'auteur\n** Fichier dupliqué",
-       "filedelete-edit-reasonlist": "Modifier les motifs fréquents de suppression",
-       "filedelete-maintenance": "La suppression et restauration de fichiers est temporairement désactivée durant la maintenance.",
+       "filedelete-edit-reasonlist": "Modifier les motifs de suppression",
+       "filedelete-maintenance": "La suppression et la restauration de fichiers sont  temporairement désactivées durant la maintenance.",
        "filedelete-maintenance-title": "Impossible de supprimer le fichier",
        "mimesearch": "Recherche par type de contenu MIME",
        "mimesearch-summary": "Cette page vous permet de filtrer les fichiers par leur type de contenu MIME.\nEntrée : type_de_contenu/sous-type ou type_de_contenu/*, par ex. <code>image/jpeg</code>.",
        "randompage-nopages": "Il n'y a aucune page dans {{PLURAL:$2|l'espace de noms|les espaces de noms}} : $1.",
        "randomincategory": "Page au hasard dans la catégorie",
        "randomincategory-invalidcategory": "« $1 » n’est pas un nom de catégorie valide.",
-       "randomincategory-nopages": "Il n’y a pas de page dans [[:Category:$1]].",
+       "randomincategory-nopages": "Il n’y a pas de pages dans la catégorie [[:Category:$1|$1]].",
        "randomincategory-category": "Catégorie :",
        "randomincategory-legend": "Page aléatoire dans la catégorie",
        "randomincategory-submit": "Lancer",
        "statistics-edits-average": "Nombre moyen de modifications par page",
        "statistics-users": "[[Special:ListUsers|Utilisateurs]] enregistrés",
        "statistics-users-active": "Utilisateurs actifs",
-       "statistics-users-active-desc": "Utilisateurs ayant fait au moins une action durant {{PLURAL:$1|le dernier jours|les $1 derniers jours}}",
+       "statistics-users-active-desc": "Utilisateurs ayant fait au moins une action {{PLURAL:$1|il y a un jour|pendant les $1 derniers jours}}",
        "pageswithprop": "Pages avec une propriété de page",
        "pageswithprop-legend": "Pages avec une propriété de page",
        "pageswithprop-text": "Cette page liste les pages qui utilisent une propriété de page particulière.",
        "ntransclusions": "Utilisé sur $1 {{PLURAL:$1|page|pages}}",
        "specialpage-empty": "Il n'y a aucun résultat à afficher.",
        "lonelypages": "Pages orphelines",
-       "lonelypagestext": "Les pages suivantes ne sont ni pointées, ni incluses par d'autres pages du wiki.",
-       "uncategorizedpages": "Pages sans catégories",
+       "lonelypagestext": "Les pages suivantes ne sont ni pointées, ni incluses dans d'autres pages de {{SITENAME}}.",
+       "uncategorizedpages": "Pages sans catégorie",
        "uncategorizedcategories": "Catégories sans catégories",
-       "uncategorizedimages": "Fichiers sans catégories",
-       "uncategorizedtemplates": "Modèles sans catégories",
+       "uncategorizedimages": "Fichiers sans catégorie",
+       "uncategorizedtemplates": "Modèles sans catégorie",
        "unusedcategories": "Catégories inutilisées",
        "unusedimages": "Fichiers orphelins",
        "wantedcategories": "Catégories les plus demandées",
        "wantedpages-summary": "Liste des pages inexistantes ayant le plus de lien vers elles, en excluant les pages n’ayant que des redirections pointant vers elles. Pour avoir une liste des pages inexistantes qui ont des redirections pointant vers elles, voyez [[{{#special:BrokenRedirects}}|la liste des redirections cassées]].",
        "wantedpages-badtitle": "Titre invalide dans les résultats : $1",
        "wantedfiles": "Fichiers les plus demandés",
-       "wantedfiletext-cat": "Les fichiers suivants sont utilisés, mais n'existent pas localement. S'ils se trouvent sur un dépôt partagé, ils peuvent être listés ici, bien qu'ils soient, de fait, déjà disponibles. Tous ces faux positifs seront <del>barrés</del>. En outre, les pages qui intègrent des fichiers qui n'existent pas sont répertoriées dans [[:$1]].",
-       "wantedfiletext-cat-noforeign": "Les fichiers suivants sont utilisés mais n'existent pas. De plus, les pages qui intègrent les fichiers qui n'existent pas sont listés dans [[:$1]].",
+       "wantedfiletext-cat": "Les fichiers suivants sont utilisés, mais n'existent pas localement. Les fichiers qui se trouvent sur un dépôt externe peuvent être listés ici, bien qu'ils soient, de fait, déjà disponibles. Tous ces faux positifs seront <del>barrés</del>. En outre, les pages qui intègrent des fichiers qui n'existent pas sont répertoriées dans [[:$1]].",
+       "wantedfiletext-cat-noforeign": "Les fichiers suivants sont utilisés mais n'existent pas. De plus, les pages qui intègrent les fichiers qui n'existent pas sont listées dans [[:$1]].",
        "wantedfiletext-nocat": "Les fichiers suivants sont utilisés, mais n'existent pas localement. S'ils se trouvent sur un dépôt partagé, ils peuvent être listés ici, bien qu'ils soient, de fait, déjà disponibles. Tous ces faux positifs seront <del>barrés</del>.",
        "wantedfiletext-nocat-noforeign": "Les fichiers suivants sont utilisés mais n'existent pas.",
        "wantedtemplates": "Modèles demandés",
        "mostlinkedcategories": "Catégories les plus utilisées",
        "mostlinkedtemplates": "Pages les plus incluses",
        "mostcategories": "Pages utilisant le plus de catégories",
-       "mostimages": "Fichiers les plus utilisés",
+       "mostimages": "Fichiers les plus liés",
        "mostinterwikis": "Pages avec le plus d'interwikis",
        "mostrevisions": "Pages les plus modifiées",
        "prefixindex": "Toutes les pages commençant par…",
        "shortpages": "Pages courtes",
        "longpages": "Pages longues",
        "deadendpages": "Pages en impasse",
-       "deadendpagestext": "Les pages suivantes ne contiennent aucun lien vers d'autres pages du wiki.",
+       "deadendpagestext": "Les pages suivantes ne contiennent aucun lien vers d'autres pages dans le wiki {{SITENAME}}.",
        "protectedpages": "Pages protégées",
        "protectedpages-indef": "Uniquement les protections indéfinies",
        "protectedpages-summary": "Cette page liste les pages existantes actuellement protégées. Pour une liste des titres protégés contre la création, voir [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]].",
        "protectedpages-cascade": "Uniquement les protections en cascade",
        "protectedpages-noredirect": "Masquer les redirections",
-       "protectedpagesempty": "Aucune page n'est protégée de cette façon.",
+       "protectedpagesempty": "Aucune page n'est protégée avec ces paramètres.",
        "protectedpages-timestamp": "Horodatage",
        "protectedpages-page": "Page",
        "protectedpages-expiry": "Expire le",
        "protectedtitlesempty": "Aucun titre n'est actuellement protégé avec ces paramètres.",
        "protectedtitles-submit": "Afficher les titres",
        "listusers": "Liste des utilisateurs",
-       "listusers-editsonly": "Ne montrer que les utilisateurs ayant au moins une contribution",
+       "listusers-editsonly": "Ne montrer que les utilisateurs ayant fait des modifications.",
        "listusers-creationsort": "Trier par date de création",
-       "listusers-desc": "Trier en ordre descendant",
+       "listusers-desc": "Trier par ordre décroissant",
        "usereditcount": "$1 modification{{PLURAL:$1||s}}",
        "usercreated": "{{GENDER:$3|Créé}} le $1 à $2",
        "newpages": "Nouvelles pages",
        "ancientpages": "Pages les plus anciennement modifiées",
        "move": "Renommer",
        "movethispage": "Renommer cette page",
-       "unusedimagestext": "Les fichiers suivants existent, mais ne sont inclus dans aucune page.\nVeuillez noter que d’autres sites peuvent avoir un lien direct vers un fichier, et donc qu’un fichier peut être listé ici alors qu’il est en réalité utilisé sur ces sites.",
-       "unusedcategoriestext": "Les catégories suivantes existent mais aucune page ou catégorie ne les utilise.",
+       "unusedimagestext": "Les fichiers suivants existent, mais ne sont inclus dans aucune page.\nVeuillez noter que d’autres sites peuvent accéder à ces fichiers à l’aide de liens directs (URLs), et donc qu’un fichier peut être listé ici alors qu’il est utilisé par ces sites.",
+       "unusedcategoriestext": "Les pages de catégories suivantes existent, mais aucune page ou catégorie ne les utilise.",
        "notargettitle": "Pas de cible",
        "notargettext": "Vous n'avez pas indiqué une page ou un utilisateur sur lequel vous souhaitez effectuer cette action.",
        "nopagetitle": "Page cible inexistante",
        "nopagetext": "La page cible que vous avez indiquée n'existe pas.",
-       "pager-newer-n": "{{PLURAL:$1|plus récente|$1 plus récentes}}",
-       "pager-older-n": "{{PLURAL:$1|plus ancienne|$1 plus anciennes}}",
+       "pager-newer-n": "{{PLURAL:$1|plus récente|$1 plus récentes}}",
+       "pager-older-n": "{{PLURAL:$1|plus ancienne|$1 plus anciennes}}",
        "suppress": "Supprimer",
        "querypage-disabled": "Cette page spéciale est désactivée pour des raisons de performances.",
        "apihelp": "Aide de l’API",
        "apihelp-no-such-module": "Le module « $1 » est introuvable.",
-       "apisandbox": "Bac à sable API",
+       "apisandbox": "Bac à sable de l'API",
        "apisandbox-jsonly": "Le bac à sable de l'API nécessite JavaScript",
-       "apisandbox-api-disabled": "API est désactivé sur ce site.",
+       "apisandbox-api-disabled": "L'API est désactivé sur ce site.",
        "apisandbox-intro": "Utilisez cette page pour expérimenter l’<strong>API webservice de MediaWiki</strong>.\nReportez-vous à [[mw:API:Main page|la documentation de l’API]] pour plus de détails sur l’utilisation de l’API. Exemple: [https://www.mediawiki.org/wiki/API#A_simple_example obtenir le contenu d'une page principale]. Choisissez une option pour voir d'autres exemples.",
        "apisandbox-fullscreen": "Développer le panneau",
        "apisandbox-fullscreen-tooltip": "Étendre le panneau du bac à sable pour remplir la fenêtre du navigateur.",
        "apisandbox-unfullscreen": "Afficher la page",
        "apisandbox-unfullscreen-tooltip": "Réduire le panneau du bac à sable, pour que les liens de navigation de MediaWiki soient disponibles.",
-       "apisandbox-submit": "Faire la demande",
+       "apisandbox-submit": "Envoyer la requête",
        "apisandbox-reset": "Effacer",
        "apisandbox-retry": "Réessayer",
        "apisandbox-loading": "Chargement des informations du module \"$1\" de l'API...",
        "log": "Journaux d’opérations",
        "logeventslist-submit": "Lister",
        "all-logs-page": "Tous les journaux publics",
-       "alllogstext": "Affichage combiné de tous les journaux disponibles sur {{SITENAME}}.<br />\nVous pouvez personnaliser l'affichage en sélectionnant le type de journal, le nom d'utilisateur ou la page concernée (ces deux derniers étant sensibles à la casse).",
+       "alllogstext": "Affichage combiné de tous les journaux disponibles sur {{SITENAME}}.\nVous pouvez personnaliser l'affichage en sélectionnant le type de journal, le nom d'utilisateur ou la page concernée (ces deux derniers étant sensibles à la casse).",
        "logempty": "Aucune opération correspondante dans les journaux.",
        "log-title-wildcard": "Chercher parmi les titres commençant par ce texte",
        "showhideselectedlogentries": "Afficher/masquer les entrées de journal sélectionnées",
        "allinnamespace": "Toutes les pages (dans l'espace de noms $1)",
        "allpagessubmit": "Lister",
        "allpagesprefix": "Afficher les pages commençant par :",
-       "allpagesbadtitle": "Le titre de page indiqué est incorrect : il contient un préfixe inter-langue ou inter-wiki réservé, ou contient un ou plusieurs caractères inutilisables dans les titres.",
+       "allpagesbadtitle": "Le titre de page indiqué est incorrect : il contient un préfixe inter-langue ou inter-wiki réservé.\nIl pourrait aussi contenir un ou plusieurs caractères inutilisables dans les titres.",
        "allpages-bad-ns": "{{SITENAME}} n'a pas d'espace de noms « $1 ».",
        "allpages-hide-redirects": "Masquer les redirections",
        "cachedspecial-viewing-cached-ttl": "Vous visualisez une version de cette page mise en cache, qui peut être datée d’au plus $1.",
        "listgrouprights-key": "Légende :\n*<span class=\"listgrouprights-granted\">Droit octroyé</span>\n*<span class=\"listgrouprights-revoked\">Droit révoqué</span>",
        "listgrouprights-group": "Groupe",
        "listgrouprights-rights": "Droits associés",
-       "listgrouprights-helppage": "Help:Droits des groupes",
+       "listgrouprights-helppage": "Help:Droits de groupes",
        "listgrouprights-members": "(liste des membres)",
        "listgrouprights-addgroup": "Ajouter des membres {{PLURAL:$2|au groupe|aux groupes}} : $1",
        "listgrouprights-removegroup": "Retirer des membres {{PLURAL:$2|du groupe|des groupes}} : $1",
        "trackingcategories-desc": "Critère d’inclusion de la catégorie",
        "restricted-displaytitle-ignored": "Pages avec des titres d'affichage ignorés",
        "restricted-displaytitle-ignored-desc": "La page a un <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> ignoré parce qu'il n'est pas équivalent au titre actuel de la page.",
-       "noindex-category-desc": "La page contient <code><nowiki>__NOINDEX__</nowiki></code> et est dans un espace de noms où ce marquage est autorisé ; elle ne sera donc pas indexée par les robots.",
-       "index-category-desc": "La page contient <code><nowiki>__INDEX__</nowiki></code> et est dans un espace de noms où ce marquage est autorisé ; elle sera donc indexée par les robots alors qu’elle ne l’aurait pas été normalement.",
+       "noindex-category-desc": "La page n'est pas indexée par les robots car elle contient le mot magique <code><nowiki>__NOINDEX__</nowiki></code> et se trouve dans un espace de noms où ce marquage est autorisé.",
+       "index-category-desc": "La page contient <code><nowiki>__INDEX__</nowiki></code> (et est dans un espace de noms où ce marquage est autorisé), et  sera donc indexée par les robots alors qu’elle ne l’aurait pas été normalement.",
        "post-expand-template-inclusion-category-desc": "La taille de la page dépasse <code>$wgMaxArticleSize</code> après le développement de tous ses modèles ; certains n’ont donc pas été développés.",
        "post-expand-template-argument-category-desc": "La page dépasse <code>$wgMaxArticleSize</code> après avoir développé l’argument d’un modèle (quelque chose entre accolades triples, comme <code>{{{Foo}}}</code>).",
        "expensive-parserfunction-category-desc": "La page utilise trop de fonctions coûteuses de l’analyseur (comme <code>#ifexist</code>). Voyez [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgExpensiveParserFunctionLimit Manual:$wgExpensiveParserFunctionLimit].",
        "broken-file-category-desc": "La page contient un lien de fichier incorrect (un lien pour inclure un fichier alors que celui-ci n’existe pas).",
-       "hidden-category-category-desc": "La catégorie contient <code><nowiki>__HIDDENCAT__</nowiki></code> dans son contenu, ce qui empêche son affichage dans la zone des liens de catégorie sur les pages, par défaut.",
+       "hidden-category-category-desc": "La catégorie contient <code><nowiki>__HIDDENCAT__</nowiki></code> dans son contenu, ce qui empêche son affichage dans la zone des liens de catégorie sur les pages par défaut.",
        "trackingcategories-nodesc": "Aucune description disponible.",
        "trackingcategories-disabled": "La catégorie est désactivée",
        "mailnologin": "Pas d'adresse d'expéditeur",
-       "mailnologintext": "Vous devez être [[Special:UserLogin|identifié]] et avoir indiqué une adresse électronique valide dans vos [[Special:Preferences|préférences]] pour pouvoir envoyer des courriels à d'autres utilisateurs.",
+       "mailnologintext": "Vous devez être [[Special:UserLogin|connecté]] et avoir indiqué une adresse électronique valide dans vos [[Special:Preferences|préférences]] pour pouvoir envoyer des courriels à d'autres utilisateurs.",
        "emailuser": "Lui envoyer un courriel",
        "emailuser-title-target": "Envoyer un courriel à {{GENDER:$1|cet utilisateur|cette utilisatrice}}",
        "emailuser-title-notarget": "Envoyer un courriel à l'utilisateur",
        "emailccsubject": "Copie de votre message à $1 : $2",
        "emailsent": "Courriel envoyé",
        "emailsenttext": "Votre message a été envoyé par courriel.",
-       "emailuserfooter": "Ce courriel a été envoyé par « $1 » à « $2 » par la fonction « {{int:emailuser}} » de {{SITENAME}}.",
-       "usermessage-summary": "A laissé un message système.",
+       "emailuserfooter": "Ce courriel a été {{GENDER:$1|envoyé}} par « $1 » à « {{GENDER:$2|$2}} » par la fonction « {{int:emailuser}} » de {{SITENAME}}.",
+       "usermessage-summary": "Laisser un message système.",
        "usermessage-editor": "Messager du système",
        "watchlist": "Liste de suivi",
        "mywatchlist": "Liste de suivi",
        "watchnologin": "Non connecté",
        "addwatch": "Ajouter à la liste de suivi",
        "addedwatchtext": "La page « [[:$1]] » et sa page de discussion ont été ajoutées à votre [[Special:Watchlist|liste de suivi]].",
+       "addedwatchtext-talk": "« [[:$1]] » et sa page associée ont été ajoutés à votre [[Special:Watchlist|liste de suivi]].",
        "addedwatchtext-short": "La page « $1 » a été ajoutée à votre liste de suivi.",
        "removewatch": "Supprimer de la liste de suivi",
        "removedwatchtext": "La page « [[:$1]] » et sa page de discussion ont été retirées de votre [[Special:Watchlist|liste de suivi]].",
+       "removedwatchtext-talk": "« [[:$1]] » et sa page associée ont été supprimés de votre [[Special:Watchlist|liste de suivi]].",
        "removedwatchtext-short": "La page « $1 » a été supprimée de votre liste de suivi.",
        "watch": "Suivre",
        "watchthispage": "Suivre cette page",
        "unwatch": "Ne plus suivre",
        "unwatchthispage": "Ne plus suivre",
        "notanarticle": "Ce n'est pas une page de contenu",
-       "notvisiblerev": "La version a été supprimée",
+       "notvisiblerev": "La dernière version relue par un utilisateur différent, a été supprimée",
        "watchlist-details": "{{PLURAL:$1|$1 page|$1 pages}} dans votre liste de suivi, sans compter les pages de discussion.",
        "wlheader-enotif": "La notification par courriel est activée.",
-       "wlheader-showupdated": "Les pages qui ont été modifiées depuis votre dernière visite sont affichées en '''gras'''.",
+       "wlheader-showupdated": "Les pages qui ont été modifiées depuis votre dernière visite sont affichées en <strong>gras</strong>.",
        "wlnote": "Ci-dessous {{PLURAL:$1|figure la dernière modification effectuée|figurent les <strong>$1</strong> dernières modifications effectuées}} durant {{PLURAL:$2|la dernière heure|les <strong>$2</strong> dernières heures}}, jusqu'au $3, $4.",
        "wlshowlast": "Montrer les dernières $1 heures, les derniers $2 jours",
        "watchlist-hide": "Masquer",
        "rollbacklinkcount-morethan": "révoquer plus de $1 {{PLURAL:$1|modification|modifications}}",
        "rollbackfailed": "La révocation a échoué",
        "rollback-missingparam": "Paramètres nécessaires à la demande manquants.",
+       "rollback-missingrevision": "Impossible de charger les données de la version.",
        "cantrollback": "Impossible de révoquer la modification ;\nle dernier contributeur est le seul auteur de cette page.",
        "alreadyrolled": "Impossible de révoquer la dernière modification de la page « [[:$1]] » effectuée par [[User:$2|$2]] ([[User talk:$2|Discuter]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) ;\nquelqu'un d'autre a déjà modifié ou révoqué la page.\n\nLa dernière modification de la page a été effectuée par [[User:$3|$3]] ([[User talk:$3|Discuter]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Le résumé de la modification était : <em>$1</em>.",
        "protect_expiry_invalid": "La date d'expiration est invalide.",
        "protect_expiry_old": "La date d'expiration est déjà passée.",
        "protect-unchain-permissions": "Déverrouiller davantage d’options de protection",
-       "protect-text": "Vous pouvez consulter et modifier le niveau de protection de la page '''$1'''.",
-       "protect-locked-blocked": "Vous ne pouvez pas modifier les niveaux de protection durant votre blocage.\nVoici les réglages actuels de la page '''$1''' :",
-       "protect-locked-dblock": "Le niveau de protection ne peut pas être modifié car la base de données est verrouillée.\nVoici les réglages actuels de la page '''$1''' :",
-       "protect-locked-access": "Vous n'avez pas les droits nécessaires pour modifier les niveaux de protection de pages.\nVoici les réglages actuels de la page '''$1''' :",
+       "protect-text": "Ici vous pouvez consulter et modifier le niveau de protection de la page <strong>$1</strong>.",
+       "protect-locked-blocked": "Vous ne pouvez pas modifier les niveaux de protection durant votre blocage.\nVoici les réglages actuels de la page <strong>$1</strong> :",
+       "protect-locked-dblock": "Les niveaux de protection ne peuvent pas être modifiés car la base de données est verrouillée.\nVoici les réglages actuels de la page <strong>$1</strong> :",
+       "protect-locked-access": "Vous n'avez pas les droits nécessaires pour modifier les niveaux de protection des pages.\nVoici les réglages actuels de la page <strong>$1<strong> :",
        "protect-cascadeon": "Cette page est protégée car elle est transcluse dans {{PLURAL:$1|la page suivante, qui a été protégée|les pages suivantes, qui ont été protégées}} avec l'option « protection en cascade » activée.\nLa modification du niveau de protection de cette page n'affectera pas la protection en cascade.",
        "protect-default": "Autoriser tous les utilisateurs",
        "protect-fallback": "Autoriser uniquement les utilisateurs avec le droit « $1 »",
        "protect-existing-expiry-infinity": "Délai d’expiration existant : infini",
        "protect-otherreason": "Motif autre ou supplémentaire :",
        "protect-otherreason-op": "Autre motif",
-       "protect-dropdown": "* Motifs de protection courants\n** Vandalisme excessif\n** Pourriels\n** Conflits de modifications contre-productives\n** Page à fort trafic",
+       "protect-dropdown": "* Motifs de protection courants\n** Vandalisme excessif\n** Pourriels excessifs\n** Conflits de modifications contre-productives\n** Page à fort trafic",
        "protect-edit-reasonlist": "Modifier les motifs de protection",
        "protect-expiry-options": "1 heure:1 hour,1 jour:1 day,1 semaine:1 week,2 semaines:2 weeks,1 mois:1 month,3 mois:3 months,6 mois:6 months,1 an:1 year,indéfiniment:infinite",
        "restriction-type": "Autorisation :",
        "restriction-level-all": "tout niveau",
        "undelete": "Voir les pages supprimées",
        "undeletepage": "Voir et restaurer des pages supprimées",
-       "undeletepagetitle": "'''La liste suivante contient des versions supprimées de [[:$1|$1]]'''.",
+       "undeletepagetitle": "<strong>La liste suivante contient des versions supprimées de [[:$1|$1]]</strong>.",
        "viewdeletedpage": "Voir les pages supprimées",
        "undeletepagetext": "{{PLURAL:$1|La page suivante a été supprimée et se trouve|Les pages suivantes ont été supprimées et se trouvent}} dans la base de données archive, d’où {{PLURAL:$1|elle peut|elles peuvent}} encore être restaurée{{PLURAL:$1||s}}.\nL’archive peut être nettoyée périodiquement.",
        "undelete-fieldset-title": "Restaurer les versions",
-       "undeleteextrahelp": "Pour restaurer l’historique complet de cette page, laissez toutes les cases décochées et cliquez sur '''''Restaurer'''''.\nPour effectuer une restauration partielle, cochez les cases correspondant aux versions à rétablir, puis cliquez sur '''''Restaurer'''''.",
+       "undeleteextrahelp": "Pour restaurer l’historique complet de cette page, laissez toutes les cases décochées et cliquez sur <strong><em>{{int:undeletebtn}}</em></strong>.\nPour effectuer une restauration partielle, cochez les cases correspondant aux versions à rétablir, puis cliquez sur <strong><em>{{int:undeletebtn}}</em></strong>.",
        "undeleterevisions": "$1 {{PLURAL:$1|révision supprimée|révisions supprimées}}",
-       "undeletehistory": "Si vous restaurez la page, toutes les versions seront replacées dans l’historique.\nSi une nouvelle page avec le même nom a été créée depuis la suppression, les versions restaurées apparaîtront dans l’historique antérieur et la version courante ne sera pas automatiquement remplacée.",
+       "undeletehistory": "Si vous restaurez la page, toutes les versions seront replacées dans l’historique.\nSi une nouvelle page avec le même nom a été créée depuis la suppression, les versions restaurées s’inséreront dans l’historique antérieur.",
        "undeleterevdel": "La restauration ne sera pas effectuée si, au final, la version la plus récente de la page ou du fichier reste partiellement supprimée.\nDans de tels cas, vous devez décocher ou démasquer les versions effacées les plus récentes (en tête de liste).",
        "undeletehistorynoadmin": "Cette page a été supprimée.\nLe motif de la suppression est indiqué dans le résumé ci-dessous, avec les détails des utilisateurs qui ont modifié la page avant sa suppression.\nLe contenu effectif de ces versions supprimées n’est accessible qu’aux administrateurs.",
        "undelete-revision": "Version supprimée de $1 (version du $4 à $5) par $3 :",
        "undeleterevision-missing": "Version incorrecte ou manquante.\nVous avez peut-être un mauvais lien, ou la version a pu être restaurée ou supprimée de l’archive.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|Une révision n'a|$1 révisions n'ont}} pas pu être restaurée{{PLURAL:$1|car son|s car leur}}  <code>rev_id</code> était déjà en cours d'utilisation.",
        "undelete-nodiff": "Aucune version précédente trouvée.",
        "undeletebtn": "Restaurer",
        "undeletelink": "voir/restaurer",
        "undeletedrevisions": "$1 {{PLURAL:$1|version restaurée|versions restaurées}}",
        "undeletedrevisions-files": "$1 version{{PLURAL:$1||s}} et $2 fichier{{PLURAL:$2||s}} restauré{{PLURAL:$2||s}}",
        "undeletedfiles": "$1 {{PLURAL:$1|fichier restauré|fichiers restaurés}}",
-       "cannotundelete": "Échec de la restauration :\n$1",
-       "undeletedpage": "'''La page $1 a été restaurée.'''\n\nConsultez le [[Special:Log/delete|journal des suppressions]] pour obtenir la liste des récentes suppressions et restaurations.",
+       "cannotundelete": "Certaines ou toutes les restaurations ont échoué :\n$1",
+       "undeletedpage": "<strong>La page $1 a été restaurée.</strong>\n\nConsultez le [[Special:Log/delete|journal des suppressions]] pour obtenir la liste des récentes suppressions et restaurations.",
        "undelete-header": "Consultez le [[Special:Log/delete|journal des suppressions]] pour lister les pages récemment supprimées.",
        "undelete-search-title": "Rechercher les pages supprimées",
-       "undelete-search-box": "Rechercher des pages supprimées",
+       "undelete-search-box": "Rechercher les pages supprimées",
        "undelete-search-prefix": "Montrer les pages commençant par :",
        "undelete-search-submit": "Rechercher",
        "undelete-no-results": "Aucune page correspondante n’a été trouvée dans les archives de suppression.",
        "sp-contributions-newbies-sub": "Parmi les nouveaux comptes",
        "sp-contributions-newbies-title": "Contributions d'utilisateurs parmi les nouveaux comptes",
        "sp-contributions-blocklog": "journal des blocages",
-       "sp-contributions-suppresslog": "contributions masquées",
-       "sp-contributions-deleted": "contributions supprimées",
+       "sp-contributions-suppresslog": "contributions de l'{{GENDER:$1|utilisateur|utilisatrice}} supprimées",
+       "sp-contributions-deleted": "contributions de l’{{GENDER:$1|utilisateur|utilisatrice}} supprimées",
        "sp-contributions-uploads": "imports",
        "sp-contributions-logs": "journaux",
        "sp-contributions-talk": "discuter",
        "sp-contributions-userrights": "gérer les droits",
-       "sp-contributions-blocked-notice": "Cet utilisateur est actuellement bloqué. La dernière entrée du journal des blocages est indiquée ci-dessous à titre d'information :",
-       "sp-contributions-blocked-notice-anon": "Cette adresse IP est actuellement bloquée.\nLa dernière entrée du journal des blocages est indiquée ci-dessous à titre d'information :",
+       "sp-contributions-blocked-notice": "Cet utilisateur est actuellement bloqué. \nLa dernière entrée du journal des blocages est affichée ci-dessous pour référence :",
+       "sp-contributions-blocked-notice-anon": "Cette adresse IP est actuellement bloquée.\nLa dernière entrée du journal des blocages est affichée ci-dessous pour référence :",
        "sp-contributions-search": "Rechercher les contributions",
        "sp-contributions-username": "Adresse IP ou nom d'utilisateur :",
        "sp-contributions-toponly": "Ne montrer que les contributions qui sont les dernières des articles",
        "whatlinkshere": "Pages liées",
        "whatlinkshere-title": "Pages qui pointent vers « $1 »",
        "whatlinkshere-page": "Page :",
-       "linkshere": "Les pages ci-dessous contiennent un lien vers '''[[:$1]]''' :",
-       "nolinkshere": "Aucune page ne contient de lien vers '''[[:$1]]'''.",
-       "nolinkshere-ns": "Aucune page ne contient de lien vers '''[[:$1]]''' dans l'espace de noms choisi.",
+       "linkshere": "Les pages ci-dessous contiennent un lien vers <strong>[[:$1]]</strong> :",
+       "nolinkshere": "Aucune page ne contient de lien vers <strong>[[:$1]]</strong>.",
+       "nolinkshere-ns": "Aucune page ne contient de lien vers <strong>[[:$1]]</strong> dans l'espace de noms choisi.",
        "isredirect": "page de redirection",
        "istemplate": "inclusion",
        "isimage": "lien vers le fichier",
        "ipb-hardblock": "Empêcher les utilisateurs connectés de modifier en utilisant cette adresse IP",
        "ipbcreateaccount": "Empêcher la création de compte",
        "ipbemailban": "Empêcher l'utilisateur d'envoyer des courriels",
-       "ipbenableautoblock": "Bloquer automatiquement la dernière adresse IP utilisée par l'utilisateur et toutes ses IPs ultérieures qu'il pourrait essayer",
-       "ipbsubmit": "Bloquer",
+       "ipbenableautoblock": "Bloquer automatiquement la dernière adresse IP utilisée par cet utilisateur et toutes ses IPs ultérieures qu'il pourrait essayer",
+       "ipbsubmit": "Bloquer cet utilisateur",
        "ipbother": "Autre durée :",
        "ipboptions": "2 heures:2 hours,1 jour:1 day,3 jours:3 days,1 semaine:1 week,2 semaines:2 weeks,1 mois:1 month,3 mois:3 months,6 mois:6 months,1 an:1 year,indéfiniment:infinite",
        "ipbhidename": "Masquer le nom d'utilisateur des modifications et des listes",
        "ipbwatchuser": "Suivre les pages utilisateur et de discussion de cet utilisateur",
        "ipb-disableusertalk": "Empêcher l'utilisateur de modifier sa page de discussion pendant le blocage",
-       "ipb-change-block": "Modifier les paramètres de blocage",
+       "ipb-change-block": "Bloquer à nouveau l'utilisateur avec ces paramètres",
        "ipb-confirm": "Confirmer le blocage",
        "badipaddress": "Adresse IP incorrecte",
        "blockipsuccesssub": "Blocage réussi",
        "blockipsuccesstext": "[[Special:Contributions/$1|$1]] a été bloqué{{GENDER:$1||e}}.<br />\nConsultez la [[Special:BlockList|liste des blocages]] pour revoir les blocages.",
        "ipb-blockingself": "Vous êtes sur le point de bloquer votre propre compte ! Êtes-vous certain{{GENDER:||e}} de vouloir faire cela ?",
-       "ipb-confirmhideuser": "Vous êtes sur le point de bloquer un utilisateur avec « cacher l'utilisateur » activé. Cela supprime le nom de l'utilisateur dans toutes les listes et les entrées du journal. Êtes-vous sûr{{GENDER:||e}} de vouloir le faire ?",
+       "ipb-confirmhideuser": "Vous êtes sur le point de bloquer un utilisateur avec « cacher l'utilisateur » activé. Cela supprimera le nom de l'utilisateur dans toutes les listes et les entrées du journal. Êtes-vous sûr{{GENDER:||e}} de vouloir le faire ?",
        "ipb-confirmaction": "Si vous êtes sûr{{GENDER:||e}} de vraiment vouloir le faire, veuillez cocher le champ « {{int:ipb-confirm}} » en bas.",
        "ipb-edit-dropdown": "Modifier les motifs de blocage par défaut",
        "ipb-unblock-addr": "Débloquer $1",
        "ipb-blocklist": "Voir les blocages existants",
        "ipb-blocklist-contribs": "Contributions pour {{GENDER:$1|$1}}",
        "ipb-blocklist-duration-left": "$1 restant",
-       "unblockip": "Débloquer un utilisateur ou une adresse IP",
-       "unblockiptext": "Utilisez le formulaire ci-dessous pour redonner les droits d’écriture à une adresse IP ou un nom d’utilisateur.",
+       "unblockip": "Débloquer un utilisateur",
+       "unblockiptext": "Utilisez le formulaire ci-dessous pour redonner les droits d’écriture à une adresse IP ou un nom d’utilisateur qui a été bloqué auparavant.",
        "ipusubmit": "Supprimer ce blocage",
        "unblocked": "[[User:$1|$1]] a été débloqué{{GENDER:$1||e}}",
        "unblocked-range": "Le compte $1 a été débloqué",
        "blocklist-userblocks": "Masquer les blocages de comptes",
        "blocklist-tempblocks": "Masquer les blocages temporaires",
        "blocklist-addressblocks": "Masquer les blocages d’adresses IP uniques",
-       "blocklist-rangeblocks": "Masquer les blocs de portée",
+       "blocklist-rangeblocks": "Masquer les blocages sur intervalles",
        "blocklist-timestamp": "Date et heure",
        "blocklist-target": "Cible",
        "blocklist-expiry": "Date d’expiration",
        "noautoblockblock": "blocage automatique désactivé",
        "createaccountblock": "création de compte bloquée",
        "emailblock": "courriel bloqué",
-       "blocklist-nousertalk": "ne peut modifier sa propre page de discussion",
+       "blocklist-nousertalk": "ne peut pas modifier sa propre page de discussion",
        "ipblocklist-empty": "La liste des adresses IP bloquées est actuellement vide.",
        "ipblocklist-no-results": "L'adresse IP ou l'utilisateur demandé n'est pas bloqué.",
        "blocklink": "bloquer",
        "unblocklink": "débloquer",
        "change-blocklink": "modifier le blocage",
        "contribslink": "contributions",
-       "emaillink": "Envoyer un courriel",
+       "emaillink": "envoyer un courriel",
        "autoblocker": "Vous avez été bloqué automatiquement parce que votre adresse IP a été récemment utilisée par « [[User:$1|$1]] ».\nLe motif fourni pour le blocage de $1 est « $2 »",
        "blocklogpage": "Journal des blocages",
-       "blocklog-showlog": "Cet utilisateur a été bloqué précédemment. Le journal des blocages est disponible ci-dessous :",
-       "blocklog-showsuppresslog": "Cet utilisateur a été bloqué et masqué précédemment. Le journal des masquages est disponible ci-dessous :",
+       "blocklog-showlog": "Cet utilisateur a été bloqué précédemment. \nLe journal des blocages est affiché ci-dessous pour référence :",
+       "blocklog-showsuppresslog": "Cet utilisateur a été bloqué et masqué précédemment. \nLe journal des masquages est affiché ci-dessous pour référence :",
        "blocklogentry": "a bloqué [[$1]] ; expiration : $2 $3",
        "reblock-logentry": "a modifié les paramètres du blocage de [[$1]] avec une expiration au $2 $3",
        "blocklogtext": "Ceci est le journal des actions de blocage et déblocage d’utilisateurs.\nLes adresses IP automatiquement bloquées ne sont pas listées.\nConsultez la [[Special:BlockList|liste des blocages]] pour voir les bannissements et blocages effectivement en cours.",
        "ipb_cant_unblock": "Erreur : identifiant de blocage $1 non trouvé.\nIl est possible qu'un déblocage ait déjà été effectué.",
        "ipb_blocked_as_range": "Erreur : l'adresse IP $1 n'est pas bloquée directement et ne peut donc pas être débloquée.\nElle fait cependant partie de la plage $2 qui, elle, peut être débloquée.",
        "ip_range_invalid": "Plage d’adresses IP incorrecte.",
-       "ip_range_toolarge": "Les blocages de plages plus grandes que /$1 ne sont pas autorisées.",
+       "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é.",
        "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.",
        "xffblockreason": "Une adresse IP dans l'en-tête X-Forwarded-For, soit la vôtre ou celle d'un serveur proxy que vous utilisez, a été bloquée. La raison du blocage initial est : $1",
-       "cant-see-hidden-user": "L’utilisateur que vous tentez de bloquer a déjà été bloqué et masqué. N’ayant pas le droit ''hideuser'', vous ne pouvez pas voir ou modifier le blocage de cet utilisateur.",
+       "cant-see-hidden-user": "L’utilisateur que vous tentez de bloquer a déjà été bloqué et masqué. \nN’ayant pas le droit de masquer des utilisateurs, vous ne pouvez pas voir ou modifier le blocage de cet utilisateur.",
        "ipbblocked": "Vous ne pouvez pas bloquer ou débloquer d'autres utilisateurs, parce que vous êtes vous-même bloqué{{GENDER:||e}}.",
        "ipbnounblockself": "Vous n'êtes pas autorisé{{GENDER:||e}} à vous débloquer vous-même",
        "lockdb": "Verrouiller la base de données",
        "unlockdbsuccesssub": "Verrouillage de la base de données supprimé",
        "lockdbsuccesstext": "La base de données a été verrouillée.<br />\nN'oubliez pas de la [[Special:UnlockDB|déverrouiller]] lorsque vous aurez terminé votre opération de maintenance.",
        "unlockdbsuccesstext": "La base de données a été déverrouillée.",
-       "lockfilenotwritable": "Le fichier de verrouillage de la base de données n'est pas inscriptible.\nPour bloquer ou débloquer la base de données, il doit être accessible par le serveur web.",
+       "lockfilenotwritable": "Le fichier de verrouillage de la base de données n'est pas inscriptible.\nPour bloquer ou débloquer la base de données, il doit être accessible en écriture par le serveur web.",
        "databaselocked": "La base de données est déjà verrouillée.",
        "databasenotlocked": "La base de données n'est pas verrouillée.",
-       "lockedbyandtime": "(par $1 le $2 à $3)",
+       "lockedbyandtime": "(par {{GENDER:$1|$1}} le $2 à $3)",
        "move-page": "Renommer $1",
        "move-page-legend": "Renommer une page",
-       "movepagetext": "Utilisez le formulaire ci-dessous pour renommer une page, en déplaçant tout son historique vers le nouveau nom. L’ancien titre deviendra une page de redirection vers le nouveau titre. Vous pouvez mettre à jour automatiquement les redirections actuelles qui pointent vers le titre original. Si vous choisissez de ne pas le faire, assurez-vous de vérifier toute [[Special:DoubleRedirects|double redirection]] ou [[Special:BrokenRedirects|redirection cassée]]. Vous avez la responsabilité de vous assurer que les liens continuent de pointer vers leur destination supposée.\n\nNotez que la page ne sera <string>pas</strong> renommée s’il existe déjà une page avec le nouveau titre, sauf si cette dernière est une simple redirection avec un historique de modifications vierge. Ceci permet de renommer une page vers sa position d’origine si le déplacement s’avère erroné.\n\n<strong>Attention !</strong>\nCeci peut provoquer un changement radical et imprévu pour une page souvent consultée ; assurez-vous d’en avoir compris les conséquences avant de continuer.",
+       "movepagetext": "Utilisez le formulaire ci-dessous pour renommer une page, en déplaçant tout son historique vers le nouveau nom. L’ancien titre deviendra une page de redirection vers le nouveau titre. \nVous pouvez mettre à jour automatiquement les redirections qui pointent vers le titre original. \nSi vous choisissez de ne pas le faire, assurez-vous de vérifier toute [[Special:DoubleRedirects|double redirection]] ou [[Special:BrokenRedirects|redirection cassée]]. Vous avez la responsabilité de vous assurer que les liens continuent de pointer vers leur destination supposée.\n\nNotez que la page ne sera <strong>pas</strong> renommée s’il existe déjà une page portant le nouveau titre, sauf si cette dernière est une simple redirection avec un historique de modifications vierge. \nCela signifie que vous pouvez de nouveau renommer une page vers sa position d’origine si vous avez fait une erreur et que vous ne pouvez pas écraser une page existante.\n\n<strong>Attention !</strong>\nCeci peut provoquer un changement radical et imprévu pour une page souvent consultée ; assurez-vous d’avoir compris les conséquences de votre démarche avant de continuer.",
        "movepagetext-noredirectfixer": "Utilisez le formulaire ci-dessous pour renommer une page, en déplaçant tout son historique vers le nouveau nom.\nL’ancien titre deviendra une page de redirection vers le nouveau titre.\nVérifiez bien les [[Special:DoubleRedirects|doubles redirections]] ou les [[Special:BrokenRedirects|redirections cassées]].\nVous avez la responsabilité de vous assurer que les liens continuent de pointer vers leur destination supposée.\n\nNotez que la page ne sera <strong>pas</strong> déplacée s’il existe déjà une page avec le nouveau titre, sauf si cette dernière a un historique de modifications vierge et est soit vide, soit une simple redirection. Ceci permet de renommer une page vers sa position d’origine si le déplacement s’avère erroné, et il est impossible d’écraser une page existante.\n\n<strong>Attention !</strong>\nCeci peut provoquer un changement radical et imprévu pour une page souvent consultée ; assurez-vous d’en avoir compris les conséquences avant de continuer.",
        "movepagetalktext": "Si vous cochez cette case, la page de discussion associée sera automatiquement renommée, à moins qu’une page de discussion non vide existe déjà sous ce nouveau nom.\n\nDans ce cas, vous devrez renommer ou fusionner cette page de discussion manuellement si vous le désirez.",
-       "moveuserpage-warning": "'''Attention :''' Vous êtes sur le point de renommer une page d’utilisateur. Veuillez noter que seule la page sera renommée et que l’utilisateur '''ne''' sera '''pas''' renommé.",
+       "moveuserpage-warning": "<strong>Attention :</strong> Vous êtes sur le point de renommer une page d’utilisateur. Veuillez noter que seule la page sera renommée et que l’utilisateur <em>ne</em> sera <em>pas</em> renommé.",
        "movecategorypage-warning": "<strong>Avertissement :</strong> Vous êtes sur le point de renommer une page de catégorie. Veuillez noter que seule la catégorie sera renommée et <em>qu’aucune</em> des pages de l’ancienne catégorie ne sera transférée dans la nouvelle.",
-       "movenologintext": "Pour pouvoir renommer une page, vous devez être [[Special:UserLogin|identifié{{GENDER:||e}}]] avec un compte utilisateur enregistré et d'ancienneté suffisante.",
+       "movenologintext": "Pour pouvoir renommer une page, vous devez être [[Special:UserLogin|identifié{{GENDER:||e}}]] avec un compte utilisateur enregistré.",
        "movenotallowed": "Vous n'avez pas la permission de renommer les pages.",
        "movenotallowedfile": "Vous n'avez pas la permission de renommer les fichiers.",
-       "cant-move-user-page": "Vous n’avez pas la permission de renommer les pages principales d’utilisateurs.",
-       "cant-move-to-user-page": "Vous n’avez pas la permission de renommer une page vers une page utilisateur (à l’exception d’une sous-page).",
-       "cant-move-category-page": "Vous n'avez pas la permis de renommer les pages de catégorie.",
+       "cant-move-user-page": "Vous n’avez pas la permission de renommer les pages principales des utilisateurs (sauf les sous-pages).",
+       "cant-move-to-user-page": "Vous n’avez pas la permission de renommer une page vers une page utilisateur (mais vous pouvez le faire vers une sous-page utilisateur).",
+       "cant-move-category-page": "Vous n'avez pas la permission de renommer les pages de catégorie.",
        "cant-move-to-category-page": "Vous n'avez pas la permission de renommer une page vers une page de catégorie.",
        "newtitle": "Nouveau titre :",
        "move-watch": "Suivre les pages originale et nouvelle",
        "movepagebtn": "Renommer la page",
        "pagemovedsub": "Renommage réussi",
-       "movepage-moved": "'''« $1 » a été renommée en « $2 »'''",
+       "movepage-moved": "<strong>« $1 » a été renommée en « $2 »</strong>",
        "movepage-moved-redirect": "Une redirection depuis l'ancien nom a été créée.",
        "movepage-moved-noredirect": "La création d'une redirection depuis l'ancien nom a été annulée.",
        "articleexists": "Il existe déjà une page portant ce titre, ou le titre que vous avez choisi n'est pas correct.\nVeuillez en choisir un autre.",
        "cantmove-titleprotected": "Vous ne pouvez pas déplacer une page vers cet emplacement car la création de page avec ce nouveau titre a été protégée.",
        "movetalk": "Renommer aussi la page de discussion associée",
-       "move-subpages": "Renommer les sous-pages (jusqu'à $1 {{PLURAL:$1|page|pages}})",
-       "move-talk-subpages": "Renommer les sous-pages de la page de discussion (jusqu'à $1 pages)",
+       "move-subpages": "Renommer les sous-pages (maximum $1)",
+       "move-talk-subpages": "Renommer les sous-pages de la page de discussion (maximum $1)",
        "movepage-page-exists": "La page $1 existe déjà et ne peut pas être écrasée automatiquement.",
        "movepage-page-moved": "La page $1 a été renommée en $2.",
        "movepage-page-unmoved": "La page $1 n'a pas pu être renommée en $2.",
        "selfmove": "Les titres d'origine et de destination sont les mêmes ;\nimpossible de renommer une page sur elle-même.",
        "immobile-source-namespace": "Vous ne pouvez pas renommer les pages dans l'espace de noms « $1 »",
        "immobile-target-namespace": "Vous ne pouvez pas renommer des pages vers l’espace de noms « $1 ».",
-       "immobile-target-namespace-iw": "Les destinations interwikis ne sont pas une cible valide pour les déplacements.",
+       "immobile-target-namespace-iw": "Un lien interwiki n’est pas une cible valide pour un renommage de page.",
        "immobile-source-page": "Cette page n'est pas renommable.",
        "immobile-target-page": "Il n'est pas possible de renommer la page vers ce titre.",
        "bad-target-model": "La destination souhaitée utilise un autre modèle de contenu. Impossible de convertir de $1 vers $2.",
        "imageinvalidfilename": "Le nom du fichier cible est incorrect",
        "fix-double-redirects": "Mettre à jour les redirections pointant vers le titre original",
        "move-leave-redirect": "Laisser une redirection vers le nouveau titre",
-       "protectedpagemovewarning": "'''Attention :''' Cette page a été protégée afin que seuls les utilisateurs possédant les droits d'administrateur puissent la renommer. La dernière entrée du journal est affichée ci-dessous pour référence :",
-       "semiprotectedpagemovewarning": "'''Note :''' Cette page a été protégée afin que seuls les utilisateurs enregistrés puissent la renommer. La dernière entrée du journal est affichée ci-dessous pour référence :",
+       "protectedpagemovewarning": "<strong>Attention :</strong> Cette page a été protégée afin que seuls les utilisateurs possédant les droits d'administrateur puissent la renommer. \nLa dernière entrée du journal est affichée ci-dessous pour référence :",
+       "semiprotectedpagemovewarning": "<strong>Note :</strong> Cette page a été protégée afin que seuls les utilisateurs enregistrés puissent la renommer. \nLa dernière entrée du journal est affichée ci-dessous pour référence :",
        "move-over-sharedrepo": "[[:$1]] existe déjà sur un dépôt partagé. Renommer ce fichier rendra le fichier sur le dépôt partagé inaccessible.",
        "file-exists-sharedrepo": "Le nom choisi est déjà utilisé par un fichier sur un dépôt partagé.\nChoisissez un autre nom.",
        "export": "Exporter des pages",
-       "exporttext": "Vous pouvez exporter en XML le texte et l’historique d’une page ou d’un ensemble de pages ; le résultat peut alors être importé dans un autre wiki utilisant le logiciel MediaWiki via la [[Special:Import|page d’importation]].\n\nPour exporter des pages, entrez leurs titres dans la boîte de texte ci-dessous, à raison d’un titre par ligne. Sélectionnez si vous désirez la version actuelle avec toutes les anciennes versions, avec les lignes de l’historique de la page, ou simplement la page actuelle avec des informations sur la dernière modification.\n\nDans ce dernier cas vous pouvez aussi utiliser un lien, tel que [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] pour la page [[{{MediaWiki:Mainpage}}]].",
+       "exporttext": "Vous pouvez exporter en XML le texte et l’historique d’une page ou d’un ensemble de pages.\nLe résultat peut alors être importé dans un autre wiki utilisant le logiciel MediaWiki via la [[Special:Import|page d’importation]].\n\nPour exporter des pages, entrez leurs titres dans la boîte de texte ci-dessous, à raison d’un titre par ligne. Sélectionnez si vous désirez la version actuelle avec toutes les anciennes versions, avec les lignes de l’historique de la page, ou simplement la page actuelle avec des informations sur la dernière modification.\n\nDans ce dernier cas vous pouvez aussi utiliser un lien, tel que [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] pour la page [[{{MediaWiki:Mainpage}}]].",
        "exportall": "Exporter toutes les pages",
        "exportcuronly": "Exporter uniquement la version courante, sans l’historique complet",
        "exportnohistory": "----\n'''Note :''' l’exportation de l’historique complet des pages à l’aide de ce formulaire a été désactivée pour des raisons de performance.",
        "export-submit": "Exporter",
        "export-addcattext": "Ajouter les pages de la catégorie :",
        "export-addcat": "Ajouter",
-       "export-addnstext": "Ajouter des pages dans l'espace de noms :",
+       "export-addnstext": "Ajouter des pages de l'espace de noms :",
        "export-addns": "Ajouter",
        "export-download": "Enregistrer dans un fichier",
        "export-templates": "Inclure les modèles",
-       "export-pagelinks": "Inclure les pages liées à une profondeur de :",
+       "export-pagelinks": "Inclure les pages liées jusqu'à une profondeur de :",
        "export-manual": "Ajouter des pages manuellement :",
        "allmessages": "Messages système",
-       "allmessagesname": "Nom du message",
+       "allmessagesname": "Nom",
        "allmessagesdefault": "Message par défaut",
        "allmessagescurrent": "Message actuel",
        "allmessagestext": "Ceci est la liste des messages système disponibles dans l’espace de noms MediaWiki.\nVeuillez visiter la [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation Régionalisation de MediaWiki] et [https://translatewiki.net/ translatewiki.net] si vous désirez contribuer à la régionalisation générique de MediaWiki.",
-       "allmessagesnotsupportedDB": "Cette page '''{{ns:special}}:Allmessages''' n'est pas utilisable car '''$wgUseDatabaseMessages''' a été désactivé.",
+       "allmessagesnotsupportedDB": "Cette page n’est pas utilisable car <strong>$wgUseDatabaseMessages</strong> a été désactivé.",
        "allmessages-filter-legend": "Filtrer",
        "allmessages-filter": "Filtrer par état de modification :",
        "allmessages-filter-unmodified": "Non modifié",
        "thumbnail_invalid_params": "Paramètres de la miniature incorrects",
        "thumbnail_toobigimagearea": "Fichier avec des dimensions supérieures à $1",
        "thumbnail_dest_directory": "Impossible de créer le répertoire de destination",
-       "thumbnail_image-type": "Type d'image non supporté",
+       "thumbnail_image-type": "Type d’image non pris en charge",
        "thumbnail_gd-library": "Configuration incomplète de la bibliothèque GD : fonction $1 introuvable",
        "thumbnail_image-missing": "Le fichier suivant est introuvable : $1",
        "thumbnail_image-failure-limit": "Il y a eu récemment trop de tentatives échouées ($1 ou plus) pour restituer cette vignette. Veuillez réessayer ultérieurement.",
        "import-mapping-subpage": "Importer comme sous-pages de la page suivante :",
        "import-upload-filename": "Nom du fichier :",
        "import-comment": "Commentaire :",
-       "importtext": "Veuillez exporter le fichier depuis le wiki d'origine en utilisant son [[Special:Export|outil d'exportation]].\nSauvegardez-le sur votre disque dur puis importez-le ici.",
+       "importtext": "Veuillez exporter le fichier depuis le wiki d’origine en utilisant l’[[Special:Export|outil d'exportation]].\nSauvegardez-le sur votre disque dur puis importez-le ici.",
        "importstart": "Importation des pages…",
        "import-revision-count": "$1 version{{PLURAL:$1||s}}",
        "importnopages": "Aucune page à importer.",
        "importuploaderrortemp": "L'import du fichier a échoué.\nUn dossier temporaire est manquant.",
        "import-parse-failure": "Échec lors de l'analyse du XML à importer",
        "import-noarticle": "Aucune page à importer !",
-       "import-nonewrevisions": "Aucune révision importée (toutes étaient déjà présentes, ou ignorées du fait d’erreurs).",
+       "import-nonewrevisions": "Aucune révision importée (toutes étaient soit déjà présentes, soit ignorées du fait d’erreurs).",
        "xml-error-string": "$1 à la ligne $2, colonne $3 (octet $4) : $5",
        "import-upload": "Import de données XML",
        "import-token-mismatch": "Perte des données de session.\n\nVous avez peut-être été déconnecté. <strong>Veuillez vérifier que vous êtes toujours connecté et réessayez</strong>.\nSi cela ne fonctionne toujours pas, essayez de [[Special:UserLogout|vous déconnecter]] et reconnectez-vous, et vérifiez que votre navigateur accepte les cookies de ce site.",
        "import-error-special": "La page « $1 » n’a pas été importée parce qu’elle appartient à un espace de noms spécial qui n’autorise aucune page.",
        "import-error-invalid": "Page « $1 » n’a pas été importée parce que le nom sous lequel elle aurait été importée n’est pas valide sur ce wiki.",
        "import-error-unserialize": "La révision $2 de la page « $1 » ne peut pas être désérialisée. La révision est indiquée comme utilisant le modèle de contenu $3 sérialisé en $4.",
-       "import-error-bad-location": "La révision $2 utilisant le modèle de contenu $3 n’a pas pu être stocké sur « $1 » sur ce wiki, car ce modèle n’est pas supporté sur cette page.",
+       "import-error-bad-location": "La révision $2 utilisant le modèle de contenu $3 n’a pas pu être stockée sur « $1 » sur ce wiki, car ce modèle n’est pas pris en charge sur cette page.",
        "import-options-wrong": "{{PLURAL:$2|Mauvaise option|Mauvaises options}} : <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "La page racine fournie est un titre non valide.",
        "import-rootpage-nosubpage": "L'espace de noms « $1 » de la page racine n'autorise pas les sous-pages.",
        "javascripttest-pagetext-unknownaction": "Action « $1 » inconnue.",
        "javascripttest-qunit-intro": "Voir [$1 la documentation de test] sur mediawiki.org.",
        "tooltip-pt-userpage": "{{GENDER:|Votre}} page utilisateur",
-       "tooltip-pt-anonuserpage": "La page utilisateur de l'IP avec laquelle vous contribuez",
+       "tooltip-pt-anonuserpage": "La page utilisateur avec l'adresse IP de laquelle vous contribuez",
        "tooltip-pt-mytalk": "{{GENDER:|Votre}} page de discussion",
        "tooltip-pt-anontalk": "La page de discussion pour les contributions depuis cette adresse IP",
        "tooltip-pt-preferences": "{{GENDER:|Vos}} préférences",
-       "tooltip-pt-watchlist": "La liste des pages dont vous suivez les modifications",
+       "tooltip-pt-watchlist": "Une liste des pages dont vous suivez les modifications",
        "tooltip-pt-mycontris": "La liste de {{GENDER:|vos}} contributions",
        "tooltip-pt-anoncontribs": "Une liste des modifications effectuées depuis cette adresse IP",
        "tooltip-pt-login": "Il est recommandé de vous identifier ; ce n'est cependant pas obligatoire.",
        "tooltip-n-recentchanges": "Liste des modifications récentes sur le wiki",
        "tooltip-n-randompage": "Afficher une page au hasard",
        "tooltip-n-help": "Aide",
-       "tooltip-t-whatlinkshere": "Liste des pages liées à celle-ci",
-       "tooltip-t-recentchangeslinked": "Liste des modifications récentes des pages liées à celle-ci",
+       "tooltip-t-whatlinkshere": "Liste des pages liées qui pointent sur celle-ci",
+       "tooltip-t-recentchangeslinked": "Liste des modifications récentes des pages appelées par celle-ci",
        "tooltip-feed-rss": "Flux RSS pour cette page",
        "tooltip-feed-atom": "Flux Atom pour cette page",
        "tooltip-t-contributions": "Voir la liste des contributions de {{GENDER:$1|cet utilisateur|cette utilisatrice}}",
        "tooltip-ca-nstab-template": "Voir le modèle",
        "tooltip-ca-nstab-help": "Voir la page d'aide",
        "tooltip-ca-nstab-category": "Voir la page de la catégorie",
-       "tooltip-minoredit": "Marquer mes modifications comme mineures",
+       "tooltip-minoredit": "Marquer ceci comme modification mineure",
        "tooltip-save": "Enregistrer vos modifications",
        "tooltip-publish": "Publier vos modifications",
        "tooltip-preview": "Merci de prévisualiser vos modifications avant de les publier",
-       "tooltip-diff": "Affiche les modifications que vous avez apportées au texte",
-       "tooltip-compareselectedversions": "Afficher les différences entre deux versions de cette page",
+       "tooltip-diff": "Afficher les modifications que vous avez apportées au texte",
+       "tooltip-compareselectedversions": "Afficher les différences entre les deux versions selectionnées de cette page",
        "tooltip-watch": "Ajouter cette page à votre liste de suivi",
        "tooltip-watchlistedit-normal-submit": "Enlever les titres",
        "tooltip-watchlistedit-raw-submit": "Mise à jour de la liste de suivi",
        "tooltip-recreate": "Recréer la page même si celle-ci a été effacée",
        "tooltip-upload": "Démarrer l'import",
-       "tooltip-rollback": "« Révoquer » annule en un clic la ou les modification(s) de cette page par son dernier contributeur.",
+       "tooltip-rollback": "« Révoquer » annule en un clic la ou les modification(s) de cette page réalisées par son dernier contributeur",
        "tooltip-undo": "« Annuler » rétablit la modification précédente et ouvre la fenêtre de modification en mode prévisualisation. Il est possible d’ajouter une raison dans le résumé.",
        "tooltip-preferences-save": "Sauvegarder les préférences",
        "tooltip-summary": "Entrez un bref résumé",
        "pageinfo-article-id": "Numéro de la page",
        "pageinfo-language": "Langue du contenu de la page",
        "pageinfo-content-model": "Modèle de contenu de la page",
+       "pageinfo-content-model-change": "modifier",
        "pageinfo-robot-policy": "Indexation par robots",
        "pageinfo-robot-index": "Autorisée",
        "pageinfo-robot-noindex": "Interdite",
        "pageinfo-magic-words": "{{PLURAL:$1|Mot magique|Mots magiques}} ($1)",
        "pageinfo-hidden-categories": "{{PLURAL:$1|Catégorie cachée|Catégories cachées}} ($1)",
        "pageinfo-templates": "{{PLURAL:$1|Modèle inclu|Modèles inclus}} ($1)",
-       "pageinfo-transclusions": "{{PLURAL:$1|Page dans laquelle|Pages dans lesquelles}} elle est incluse ($1)",
+       "pageinfo-transclusions": "{{PLURAL:$1|Page dans laquelle|Pages dans lesquelles}} cette page est incluse ($1)",
        "pageinfo-toolboxlink": "Information sur la page",
        "pageinfo-redirectsto": "Rediriger vers",
        "pageinfo-redirectsto-info": "info",
-       "pageinfo-contentpage": "Compté comme page de contenu",
+       "pageinfo-contentpage": "Comptée comme page de contenu",
        "pageinfo-contentpage-yes": "Oui",
-       "pageinfo-protect-cascading": "Les protections sont déduites d'ici",
+       "pageinfo-protect-cascading": "Les protections sont déduites à partir d'ici",
        "pageinfo-protect-cascading-yes": "Oui",
        "pageinfo-protect-cascading-from": "Les protections sont déduites depuis",
        "pageinfo-category-info": "Informations sur la catégorie",
        "filedelete-archive-read-only": "Le dossier d'archivage « $1 » n'est pas modifiable par le serveur.",
        "previousdiff": "← Modification précédente",
        "nextdiff": "Modification suivante →",
-       "mediawarning": "'''Attention :''' ce type de fichier peut contenir du code malveillant.\nSi vous l'exécutez, votre système peut être compromis.",
-       "imagemaxsize": "Taille maximale des images :<br />''(pour les pages de description de fichier)''",
+       "mediawarning": "<strong>Attention :</strong> ce type de fichier peut contenir du code malveillant.\nSi vous l'exécutez, votre système peut être compromis.",
+       "imagemaxsize": "Taille maximale des images :<br /><em>(pour les pages de description de fichier)</em>",
        "thumbsize": "Taille de la miniature :",
        "widthheight": "$1&nbsp;×&nbsp;$2",
        "widthheightpage": "$1 × $2, $3 page{{PLURAL:$3||s}}",
        "file-info-size-pages": "$1 × $2 pixels, taille de fichier : $3, type MIME : $4, $5 page{{PLURAL:$5||s}}",
        "file-nohires": "Pas de plus haute résolution disponible.",
        "svg-long-desc": "Fichier SVG, résolution de $1 × $2 pixels, taille : $3",
-       "svg-long-desc-animated": "Fichier SVG animé, taille $1 × $2 pixels, taille du fichier : $3",
+       "svg-long-desc-animated": "Fichier SVG animé, résolution $1 × $2 pixels, taille du fichier : $3",
        "svg-long-error": "Fichier SVG non valide : $1",
        "show-big-image": "Fichier d'origine",
        "show-big-image-preview": "Taille de cet aperçu : $1.",
        "file-info-png-looped": "en boucle",
        "file-info-png-repeat": "joué $1 {{PLURAL:$1|fois}}",
        "file-info-png-frames": "$1 {{PLURAL:$1|image|images}}",
-       "file-no-thumb-animation": "'''Remarque : En raison de limitations techniques, les vignettes de ce fichier ne seront pas animées.'''",
-       "file-no-thumb-animation-gif": "'''Remarque : En raison de limitations techniques, les vignettes d'images GIF en haute résolution telles que celle-ci ne seront pas animées.'''",
+       "file-no-thumb-animation": "<strong>Remarque : En raison de limitations techniques, les vignettes de ce fichier ne seront pas animées.</strong>",
+       "file-no-thumb-animation-gif": "<strong>Remarque : En raison de limitations techniques, les vignettes d'images GIF en haute résolution telles que celle-ci ne seront pas animées.</strong>",
        "newimages": "Galerie des nouveaux fichiers",
-       "imagelisttext": "Voici une liste de '''$1''' fichier{{PLURAL:$1||s}} classée $2.",
+       "imagelisttext": "Voici une liste de <strong>$1</strong> {{PLURAL:$1|fichier classé|fichiers classés}} $2.",
        "newimages-summary": "Cette page spéciale affiche les derniers fichiers importés.",
-       "newimages-legend": "Nom du fichier",
+       "newimages-legend": "Filtre",
        "newimages-label": "Nom du fichier (ou une partie de celui-ci) :",
-       "newimages-showbots": "Afficher les imports par des robots",
+       "newimages-showbots": "Afficher les imports faits par des robots",
        "newimages-hidepatrolled": "Masquer les téléchargements patrouillés",
        "noimages": "Aucune image à afficher.",
        "ilsubmit": "Rechercher",
        "saturday-at": "Samedi à $1",
        "sunday-at": "Dimanche à $1",
        "yesterday-at": "Hier à $1",
-       "bad_image_list": "Le format est le suivant :\n\nSeules les listes d’énumération (commençant par *) sont prises en compte. Le premier lien d’une ligne doit être celui d’une mauvaise image.\nLes autres liens sur la même ligne sont considérés comme des exceptions, par exemple des pages sur lesquelles l’image peut apparaître.",
+       "bad_image_list": "Le format est le suivant :\n\nSeules les listes d’énumération (commençant par *) sont prises en compte. \nLe premier lien d’une ligne doit être celui d’une mauvaise image.\nLes autres liens sur la même ligne sont considérés comme des exceptions, par exemple des pages sur lesquelles l’image peut apparaître.",
        "variantname-ku-arab": "ku-arab",
        "variantname-ku-latn": "ku-latn",
        "variantname-tg-cyrl": "tg-cyrl",
        "exif-yresolution": "Résolution verticale",
        "exif-stripoffsets": "Emplacement des données de l'image",
        "exif-rowsperstrip": "Nombre de lignes par bande",
-       "exif-stripbytecounts": "Taille en octets par bande",
+       "exif-stripbytecounts": "Taille en octets par bande compressée",
        "exif-jpeginterchangeformat": "Position du SOI JPEG",
        "exif-jpeginterchangeformatlength": "Taille en octets des données JPEG",
        "exif-whitepoint": "Chromaticité du point blanc",
        "exif-primarychromaticities": "Chromaticité des primaires",
        "exif-ycbcrcoefficients": "Coefficients YCbCr",
-       "exif-referenceblackwhite": "Valeurs de référence noir et blanc",
-       "exif-datetime": "Date de modification",
-       "exif-imagedescription": "Description de l'image",
-       "exif-make": "Fabricant de l'appareil",
-       "exif-model": "Modèle de l'appareil",
+       "exif-referenceblackwhite": "Valeurs des couples noir et blanc de référence",
+       "exif-datetime": "Date de modification du fichier",
+       "exif-imagedescription": "Titre de l'image",
+       "exif-make": "Fabricant de l'appareil photo",
+       "exif-model": "Modèle de l'appareil photo",
        "exif-software": "Logiciel utilisé",
        "exif-artist": "Auteur",
        "exif-copyright": "Détenteur du droit d'auteur",
        "exif-exifversion": "Version EXIF",
-       "exif-flashpixversion": "Version FlashPix",
+       "exif-flashpixversion": "Version FlashPix supportée",
        "exif-colorspace": "Espace colorimétrique",
        "exif-componentsconfiguration": "Signification de chaque composante",
        "exif-compressedbitsperpixel": "Mode de compression de l'image",
        "invalidateemail": "Annuler la confirmation de l'adresse de courriel",
        "notificationemail_subject_changed": "L’adresse courriel enregistrée sur {{SITENAME}} a été changée",
        "notificationemail_subject_removed": "L’adresse courriel enregistrée sur {{SITENAME}} a été supprimée",
-       "notificationemail_body_changed": "Quelqu’un, probablement vous, connecté depuis l’adresse IP $1, a changé l’adresse courriel\nassociée au compte « $2 » sur {{SITENAME}} en « $3 ».\n\nSi ce n’était pas vous, contactez un administrateur du site immédiatement.",
-       "notificationemail_body_removed": "Quelqu’un, probablement vous, connecté depuis l’adresse IP $1,\na suprrimé l’adresse courriel associée au compte « $2 » sur {{SITENAME}}.\n\nSi ce n’était pas vous, contactez un administrateur du site immédiatement.",
+       "notificationemail_body_changed": "Quelqu’un, probablement vous, connecté depuis l’adresse IP $1, a changé l’adresse\ncourriel associée au compte « $2 » sur {{SITENAME}} en « $3 ».\n\nSi ce n’était pas vous, contactez un administrateur du site immédiatement.",
+       "notificationemail_body_removed": "Quelqu’un, probablement vous, connecté depuis l’adresse IP $1,\na supprimé l’adresse courriel associée au compte « $2 » sur {{SITENAME}}.\n\nSi ce n’était pas vous, contactez un administrateur du site immédiatement.",
        "scarytranscludedisabled": "[La transclusion interwiki est désactivée]",
        "scarytranscludefailed": "[La récupération de modèle a échoué pour $1]",
        "scarytranscludefailed-httpstatus": "[Échec de la récupération du modèle pour  $1 : HTTP  $2 ]",
        "tags-edit-logentry-submit": "Appliquer les modifications à {{PLURAL:$1|cette entrée de journal|$1 entrées de journal}}",
        "tags-edit-success": "Les modifications ont été appliquées.",
        "tags-edit-failure": "Les modifications n’ont pas pu être appliquées :\n$1",
-       "tags-edit-nooldid-title": "Révision cible non valide",
-       "tags-edit-nooldid-text": "Vous n’avez soit pas spécifié de révision cible sur laquelle exécuter cette fonction, soit la révision spécifiée n’existe pas.",
+       "tags-edit-nooldid-title": "Version cible non valide",
+       "tags-edit-nooldid-text": "Vous n’avez soit pas spécifié de version cible sur laquelle exécuter cette fonction, soit la version spécifiée n’existe pas.",
        "tags-edit-none-selected": "Veuillez sélectionner au moins une balise à ajouter ou enlever.",
        "comparepages": "Comparer des pages",
        "compare-page1": "Page 1",
        "htmlform-title-not-exists": "$1 n’existe pas",
        "htmlform-user-not-exists": "<strong>$1</strong> n’existe pas.",
        "htmlform-user-not-valid": "<strong>$1</strong> n’est pas un nom d’utilisateur valide.",
-       "sqlite-has-fts": "$1 avec recherche en texte intégral supportée",
-       "sqlite-no-fts": "$1 sans recherche en texte intégral supportée",
+       "sqlite-has-fts": "$1 avec recherche en texte intégral prise en charge",
+       "sqlite-no-fts": "$1 sans recherche en texte intégral prise en charge",
        "logentry-delete-delete": "$1 {{GENDER:$2|a supprimé}} la page $3",
        "logentry-delete-restore": "$1 {{GENDER:$2|a restauré}} la page $3",
        "logentry-delete-event": "$1 {{GENDER:$2|a modifié}} la visibilité {{PLURAL:$5|d'un événement du journal|de $5 événements du journal}} sur $3: $4",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
        "mw-widgets-titleinput-description-new-page": "la page n’existe pas encore",
        "mw-widgets-titleinput-description-redirect": "redirection vers $1",
-       "api-error-blacklisted": "Merci de choisir un autre titre descriptif.",
        "sessionmanager-tie": "Impossible de combiner les demandes multiples de types d’authentification : $1.",
        "sessionprovider-generic": "sessions $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sessions basées sur les cookies",
        "log-action-filter-newusers": "Type de création de compte :",
        "log-action-filter-patrol": "Type de patrouille :",
        "log-action-filter-protect": "Type de protection :",
-       "log-action-filter-rights": "Type de changement de droit",
-       "log-action-filter-suppress": "Type de suppression",
+       "log-action-filter-rights": "Type de changement de droit :",
+       "log-action-filter-suppress": "Type de suppression :",
        "log-action-filter-upload": "Type de téléversement :",
        "log-action-filter-all": "Tout",
        "log-action-filter-block-block": "Blocage",
        "linkaccounts-submit": "Lier les comptes",
        "unlinkaccounts": "Dissocier les comptes",
        "unlinkaccounts-success": "Le compte a été dissocié.",
-       "authenticationdatachange-ignored": "Les modifications de données d’authentification n’ont pas été gérées. Peut-être aucun fournisseur n’a-t-il été configuré ?"
+       "authenticationdatachange-ignored": "Les modifications de données d’authentification n’ont pas été gérées. Peut-être aucun fournisseur n’a-t-il été configuré ?",
+       "userjsispublic": "Veuillez noter: les sous-pages JavaScript ne doivent pas contenir de données confidentielles parce qu'elles sont visibles des autres utilisateurs.",
+       "usercssispublic": "Veuillez noter: les sous-pages CSS ne doivent pas contenir de données confidentielles parce qu'elles sont visibles des autres utilisateurs."
 }
index a7feed5..d3828ca 100644 (file)
        "removewatch": "Enlevar de la lista de gouârda",
        "removedwatchtext": "« [[:$1]] » et sa pâge de discussion sont étâyes enlevâyes de voutra [[Special:Watchlist|lista de gouârda]].",
        "removedwatchtext-short": "La pâge « $1 » est étâye enlevâye de voutra lista de gouârda.",
-       "watch": "Siuvre",
+       "watch": "Gouardar",
        "watchthispage": "Siuvre cela pâge",
        "unwatch": "Pas més siuvre",
        "unwatchthispage": "Pas més siuvre",
        "htmlform-selectorother-other": "Ôtro",
        "sqlite-has-fts": "$1 avouéc rechèrche en tèxto complèt recognua",
        "sqlite-no-fts": "$1 sen rechèrche en tèxto complèt recognua",
-       "logentry-delete-delete": "$1 at suprimâ la pâge $3",
+       "logentry-delete-delete": "$1 {{GENDER:$2|at suprimâ}} la pâge $3",
        "logentry-delete-restore": "$1 at refêt la pâge $3",
        "logentry-delete-event": "$1 at changiê la visibilitât {{PLURAL:$5|d’un èvènement|de $5 èvènements}} du jornal dessus $3 : $4",
        "logentry-delete-revision": "$1 at changiê la visibilitât {{PLURAL:$5|d’una vèrsion|de $5 vèrsions}} sur la pâge $3 : $4",
        "revdelete-uname-unhid": "nom d’usanciér pas més cachiê",
        "revdelete-restricted": "at aplicâ les rèstriccions ux administrators",
        "revdelete-unrestricted": "rèstriccions enlevâs por los administrators",
-       "logentry-move-move": "$1 at dèplaciê la pâge $3 vers $4",
+       "logentry-move-move": "$1 {{GENDER:$2|at dèplaciê}} la pâge $3 vers $4",
        "logentry-move-move-noredirect": "$1 at dèplaciê la pâge $3 vers $4 sen lèssiér una redirèccion",
        "logentry-move-move_redir": "$1 at dèplaciê la pâge $3 vers $4 en ècrasent sa redirèccion",
        "logentry-move-move_redir-noredirect": "$1 at dèplaciê la pâge $3 vers $4 en ècrasent sa redirèccion sen lèssiér una redirèccion",
        "logentry-rights-rights": "$1 at changiê l’apartegnence a la tropa por « $3 » de $4 a $5",
        "logentry-rights-rights-legacy": "$1 at changiê l’apartegnence a la tropa por « $3 »",
        "logentry-rights-autopromote": "$1 est étâ nomâ ôtomaticament de $4 a $5",
+       "logentry-upload-upload": "$1 {{GENDER:$2|at tèlèchargiê}} $3",
        "rightsnone": "(nion)",
        "revdelete-summary": "rèsumâ du changement",
        "feedback-adding": "Aponsa de voutros avis a la pâge...",
        "special-characters-title-minus": "segno muens",
        "mw-widgets-dateinput-placeholder-day": "AAAA-MM-JJ",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
-       "api-error-blacklisted": "Volyéd chouèsir un ôtro titro dèscriptif.",
        "randomrootpage": "Pâge racena a l’hasârd"
 }
index 7e69455..2765143 100644 (file)
        "minoredit": "Mionathrú é seo",
        "watchthis": "Déan faire ar an lch seo",
        "savearticle": "Sábháil an lch",
+       "publishpage": "Foilsigh leathanach",
        "preview": "Réamhamharc",
        "showpreview": "Taispeáin réamhamharc",
        "showdiff": "Taispeáin athruithe",
        "recreate-moveddeleted-warn": "'''Rabhadh: Tá tú ag athchruthú leathanaigh a scriosadh cheana.'''\n\nAn bhfuil tú cinnte go bhfuil sé oiriúnach an leathanach seo a cur in eagar?\nCuirtear an loga scriosta ar fáil anseo mar áis:",
        "log-fulllog": "Féach ar an logchomhad iomlán",
        "undo-summary": "Cealaíodh athrú $1 le [[Special:Contributions/$2|$2]] ([[User talk:$2|plé]])",
-       "cantcreateaccounttitle": "Ní féidir cuntas a chruthú",
        "viewpagelogs": "Féach ar logaí faoin leathanach seo",
        "nohistory": "Níl aon stáir athraithe ag an leathanach seo.",
        "currentrev": "Leagan reatha",
        "tooltip-ca-nstab-category": "Féach ar an leathanach catagóire",
        "tooltip-minoredit": "Déan mionathrú den athrú seo",
        "tooltip-save": "Sábháil do chuid athruithe",
+       "tooltip-publish": "Foilsigh do chuid athruithe",
        "tooltip-preview": "Réamhamharc ar do chuid athruithe; úsáid an gné seo roimh a shábhálaíonn tú!",
        "tooltip-diff": "Taispeáin na difríochtaí áirithe a rinne tú don téacs",
        "tooltip-compareselectedversions": "Féach na difríochtaí idir an dhá leagain roghnaithe den leathanach seo.",
        "exif-gpsspeed-n": "Muirmhílte",
        "exif-gpsdirection-t": "Fíorthreo",
        "exif-gpsdirection-m": "Treo maighnéadach",
+       "exif-dc-publisher": "Foilsitheoir",
        "namespacesall": "iad uile",
        "monthsall": "gach mí",
        "confirmemail": "Deimhnigh do ríomhsheoladh",
index ddc6770..c658811 100644 (file)
        "october-date": "$1 dhen Dàmhair",
        "november-date": "$1 dhen t-Samhain",
        "december-date": "$1 dhen Dùbhlachd",
+       "period-am": "m",
+       "period-pm": "f",
        "pagecategories": "{{PLURAL:$1|Roinn-seòrsa|Roinnean-seòrsa}}",
        "category_header": "Duilleagan san roinn-seòrsa \"$1\"",
        "subcategories": "Fo-roinnean-seòrsa",
        "mailerror": "Mearachd a' cur post: $1",
        "acct_creation_throttle_hit": "Chruthaich na h-aoighean air an uicidh seo {{PLURAL:$1|$1 chunntas|$1 chunntas|$1 cunntasan|$1 cunntas}} fon IP agad an-dè agus sin an àireamh as motha a tha ceadaichte. Chan urrainn do dh'aoighean eile on IP seo barrachd chunntasan a chruthachadh air sgàth sin.",
        "emailauthenticated": "Chaidh an seòladh puist-d agad a dhearbhadh $2 aig $3.",
-       "emailnotauthenticated": "Cha deach am post-d agad a dhearbhadh fhathast.\nCha dèid post-d a chur airson gin dhe na gleusan a leanas.",
-       "noemailprefs": "Sònraich post-d sna roghainnean agad gus na gleusan seo a chur an comas.",
+       "emailnotauthenticated": "Cha deach am post-d agad a dhearbhadh fhathast.\nCha dèid post-d a chur airson gin dhe na feartan a leanas.",
+       "noemailprefs": "Sònraich post-d sna roghainnean agad gus na feartan seo a chur an comas.",
        "emailconfirmlink": "Dearbhaich an seòladh puist-dhealain agad",
        "invalidemailaddress": "Chan urrainn dhuinn gabhail ris an t-seòladh seo a chionn 's gu bheil coltas cearr air.\nCuir a-steach seòladh san fhòrmat cheart no falamhaich an raon sin.",
        "cannotchangeemail": "Cha ghabh na puist-d a tha co-cheangailte ri cunntas atharrachadh air an uicipeid seo.",
        "pt-login-button": "Log a-steach",
        "pt-createaccount": "Cruthaich cunntas",
        "pt-userlogout": "Log a-mach",
-       "php-mail-error-unknown": "Mearachd nach aithne dhuinn sa ghleus mail() aig PHP.",
+       "php-mail-error-unknown": "Mearachd neo-aithichte san fheart mail() aig PHP.",
        "user-mail-no-addy": "Cha do ghabh am post-d a chur leis nach robh seòladh puist-d ann.",
        "user-mail-no-body": "Bha bodhaig na teachdaireachd bàn no air leth goirid.",
        "changepassword": "Atharraich facal-faire",
        "passwordreset-emailtext-user": "Dh'iarr an cleachdaiche $1 air {{SITENAME}} ath-shuidheachadh an fhacail-fhaire air {{SITENAME}} ($4). Tha {{PLURAL:$3|an cunntas-cleachdaiche|na cunntasan-cleachdaiche}} a leanas co-cheangailte ris an t-seòladh puist-d seo:\n\n$2\n\nFalbhaidh an ùine air {{PLURAL:$3|an fhacal-fhaire shealach|na faclan-faire sealach}} seo an ceann $5 {{PLURAL:$5|latha|latha|làithean|latha}}.\nBu chòir dhut logadh a-steach agus facal-faire ùr a thaghadh an-dràsta. Ma dh'iarr cuideigin eile seo no ma chuimhnich thu air an fhacal-fhaire agad 's mur eil thu airson atharrachadh tuilleadh, leig seachad an teachdaireachd seo 's lean ort leis an t-seann fhacal-fhaire.",
        "passwordreset-emailelement": "Ainm-cleachdaiche: \n$1\n\nFacal-faire sealach: \n$2",
        "passwordreset-emailsentemail": "Ma tha am post-d seo co-cheangailte ris a’ chunntas agad, thèid post-d airson ath-shuidheachadh an fhacail-fhaire a chur.",
-       "passwordreset-emailsent-capture": "Chaidh post-d a chum ath-shuidheachadh an fhacail-fhaire a chur agus chì thu sin gu h-ìosal.",
-       "passwordreset-emailerror-capture": "Chaidh post-d a chum ath-shuidheachadh an fhacail-fhaire a ghintinn agus chì thu sin gu h-ìosal ach cha b' urrainn dhuinn a chur dhan chleachdaiche: $1",
        "changeemail": "Atharraich no thoir air falbh an seòladh puist-d",
        "changeemail-header": "Lìon am foirm seo a dh’atharrachadh an t-seòlaidh phuist-d agad. Ma tha thu airson an co-cheangal eadar post-d sam bith is an cunntas agad a thoirt air falbh, fàg an seòladh ùr bàn nuair a chuireas tu am foirm.",
        "changeemail-no-info": "Feumaidh tu logadh a-steach mus dèan thu inntrigeadh dìreach dhan duilleag seo.",
        "minoredit": "Seo mùthadh beag",
        "watchthis": "Cum sùil air an duilleag seo",
        "savearticle": "Sàbhail an duilleag",
+       "publishpage": "Foillsich an duilleag",
+       "publishchanges": "Foillsich na mùthaidhean",
        "preview": "Ro-shealladh",
        "showpreview": "Seall an ro-shealladh",
        "showdiff": "Seall na mùthaidhean",
        "summary-preview": "Ro-shealladh a' ghearr-chunntais:",
        "subject-preview": "Ro-shealladh a’ chuspair:",
        "blockedtitle": "Tha an cleachdair air a bhacadh",
-       "blockedtext": "<strong>Chaidh an t-ainm-cleachdaiche no an seòladh IP agad a bhacadh.</strong>\n\n'S e $1 a chur am bacadh seo ort.\n{{GENDER:$1|Thug e|Thug i|Thugadh}} an cèill gun do {{GENDER:$1|rinn e|rinn i|rinneadh}} sin air sgàth an adhbhair seo: <em>$2</em>.\n\n* Toiseach a' bhacaidh: $8\n* Deireadh a' bhacaidh: $6\n* An neach air a bheil am bacadh: $7\n\n'S urrainn dhut fios a chur gu $1 no [[{{MediaWiki:Grouppage-sysop}}|rianair]] eile gus am bacadh seo a dheasbad.\nChan urrainn dhut an gleus \"Cuir post-d dhan chleachdaiche seo\" a chleachdadh ach ma tha seòladh puist-d dligheach ann an [[Special:Preferences|roghainnean a' chunntais agad]] agus mura deach bacadh a chur air a chleachdadh.\n'S e $3 an seòladh IP làithreach agus agus 's e #$5 ID a' bhacaidh.\nThoir iomradh air a' mhion-fhiosrachadh gu h-àrd ma chuireas tu ceist sam bith mu dhèidhinn.",
-       "autoblockedtext": "Chaidh an seòladh IP agad a bhacadh gu fèin-obrachail a chionn 's gun deach a chleachdadh le cuideigin eile a chaidh a bhacadh le $1.\n{{GENDER:$1|Thug e|Thug i|Thugadh}} an cèill gun do {{GENDER:$1|rinn e|rinn i|rinneadh}} sin air sgàth an adhbhair seo: \n\n:<em>$2</em>.\n\n* Toiseach a' bhacaidh: $8\n* Deireadh a' bhacaidh: $6\n* An neach air a bheil am bacadh: $7\n\n'S urrainn dhut fios a chur gu $1 no [[{{MediaWiki:Grouppage-sysop}}|rianair]] eile gus am bacadh seo a dheasbad.\n\nDh'fhaoidte nach urrainn dhut an gleus \"Cuir post-d dhan chleachdaiche seo\" a chleachdadh ach ma tha seòladh puist-d dligheach ann an [[Special:Preferences|roghainnean a' chunntais agad]] agus mura deach bacadh a chur air a chleachdadh.\n\n'S e $3 an seòladh IP làithreach agus agus 's e #$5 ID a' bhacaidh.\nThoir iomradh air a' mhion-fhiosrachadh gu h-àrd ma chuireas tu ceist sam bith mu dhèidhinn.",
+       "blockedtext": "<strong>Chaidh an t-ainm-cleachdaiche no an seòladh IP agad a bhacadh.</strong>\n\n'S e $1 a chur am bacadh seo ort.\n{{GENDER:$1|Thug e|Thug i|Thugadh}} an cèill gun do {{GENDER:$1|rinn e|rinn i|rinneadh}} sin air sgàth an adhbhair seo: <em>$2</em>.\n\n* Toiseach a' bhacaidh: $8\n* Deireadh a' bhacaidh: $6\n* An neach air a bheil am bacadh: $7\n\n'S urrainn dhut fios a chur gu $1 no [[{{MediaWiki:Grouppage-sysop}}|rianair]] eile gus am bacadh seo a dheasbad.\nChan urrainn dhut am feart \"Cuir post-d dhan chleachdaiche seo\" a chleachdadh ach ma tha seòladh puist-d dligheach ann an [[Special:Preferences|roghainnean a' chunntais agad]] agus mura deach bacadh a chur air a chleachdadh.\n'S e $3 an seòladh IP làithreach agus agus 's e #$5 ID a' bhacaidh.\nThoir iomradh air a' mhion-fhiosrachadh gu h-àrd ma chuireas tu ceist sam bith mu dhèidhinn.",
+       "autoblockedtext": "Chaidh an seòladh IP agad a bhacadh gu fèin-obrachail a chionn 's gun deach a chleachdadh le cuideigin eile a chaidh a bhacadh le $1.\n{{GENDER:$1|Thug e|Thug i|Thugadh}} an cèill gun do {{GENDER:$1|rinn e|rinn i|rinneadh}} sin air sgàth an adhbhair seo: \n\n:<em>$2</em>.\n\n* Toiseach a' bhacaidh: $8\n* Deireadh a' bhacaidh: $6\n* An neach air a bheil am bacadh: $7\n\n'S urrainn dhut fios a chur gu $1 no [[{{MediaWiki:Grouppage-sysop}}|rianair]] eile gus am bacadh seo a dheasbad.\n\nDh'fhaoidte nach urrainn dhut am feart \"Cuir post-d dhan chleachdaiche seo\" a chleachdadh ach ma tha seòladh puist-d dligheach ann an [[Special:Preferences|roghainnean a' chunntais agad]] agus mura deach bacadh a chur air a chleachdadh.\n\n'S e $3 an seòladh IP làithreach agus agus 's e #$5 ID a' bhacaidh.\nThoir iomradh air a' mhion-fhiosrachadh gu h-àrd ma chuireas tu ceist sam bith mu dhèidhinn.",
        "blockednoreason": "cha deach adhbhar a shònrachadh",
        "whitelistedittext": "Feumaidh tu $1 mus urrainn dhut duilleagan a dheasachadh.",
        "confirmedittext": "Feumaidh tu am post-d agad a dhearbhadh mus urrainn dhut duilleagan a dheasachadh.\nSuidhich is dearbhaich am post-d agad ann an [[Special:Preferences|roghainnean a' chleachdaiche]]",
        "undo-nochange": "Tha coltas gun deach am mùthadh seo a neo-dhèanamh mu thràth.",
        "undo-summary": "Neo-dhèan mùthadh $1 leis [[Special:Contributions/$2|$2]] ([[User talk:$2|an deasbaireachd]])",
        "undo-summary-username-hidden": "Neo-dhèan am mùthadh $1 le cleachdaiche falaichte",
-       "cantcreateaccounttitle": "Cha ghabh an cunntas a chruthachadh",
        "cantcreateaccount-text": "Chuir [[User:$3|$3]] bacadh air cruthachadh chunntasan on t-seòladh IP seo (<strong>$1</strong>).\n\nDh'innis $3 gun do rinn {{GENDER:$3|e|i}} seo air sgàth: <em>$2</em>",
        "cantcreateaccount-range-text": "Chuir [[User:$3|$3]] casg air daoine a tha airson cunntasan a chruthachadh on sheòlaidhean IP taobh a-staigh na rainse <strong>$1</strong> agus tha sin a' gabhail a-steach an t-seòlaidh IP agad-sa (<strong>$4</strong>).\n\nDh'innis $3 gun do {{GENDER:$1|rinn e|rinn i|rinneadh}} seo air sgàth an adhbhair seo: <em>$2</em>",
        "viewpagelogs": "Seall logaichean na duilleige seo",
        "prefs-skin": "Bian",
        "skin-preview": "Ro-shealladh",
        "datedefault": "Gun roghainnean",
-       "prefs-labs": "Gleusan nan deuchainn-lannan",
+       "prefs-labs": "Feartan nan deuchainn-lannan",
        "prefs-user-pages": "Duilleagan a' chleachdaiche",
        "prefs-personal": "Pròifil",
        "prefs-rc": "Mùthaidhean ùra",
        "zip-file-open-error": "Thachair mearachd le fosgladh an fhaidhle airson dearbhadh ZIP.",
        "zip-wrong-format": "Chan eil am faidhle sònraichte 'na fhaidhle ZIP.",
        "zip-bad": "Tha am faidhle ZIP coirbte no cha ghabh a leughadh air adhbhar eile air choireigin.\nChan urrainn dhuinn dearbhadh mar bu chòir a bheil e tèarainte gus nach eil.",
-       "zip-unsupported": "Tha am faidhle ZIP seo a' chleachdadh gleusan ZIP ris nach cuir MediaWiki taic.\nChan urrainn dhuinn dearbhadh mar bu chòir a bheil e tèarainte gus nach eil.",
+       "zip-unsupported": "Tha am faidhle ZIP seo a' chleachdadh feartan ZIP ris nach cuir MediaWiki taic.\nChan urrainn dhuinn dearbhadh mar bu chòir a bheil e tèarainte gus nach eil.",
        "uploadstash": "Tasgadan an luchdaidh suas",
        "uploadstash-summary": "Bheir an duilleag seo inntrigeadh dhut a dh'fhaidhlichean a chaidh a luchdadh suas no a tha 'gan luchdadh suas ach nach deach fhoillseachadh air an uicidh fhathast. Chan fhaic duine na faidhlichean seo ach an cleachdaiche a rinn an luchdadh suas.",
        "uploadstash-clear": "Glan na faidhlichean ann an tasgadan an luchdaidh suas",
        "emailccsubject": "Lethbhreac dhen teachdaireachd agad gu $1: $2",
        "emailsent": "Post-d air a chur",
        "emailsenttext": "Chaidh an teachdaireachd puist-d agad a chur.",
-       "emailuserfooter": "Chaidh am post-d seo a chur o $1 gu $2 leis a' ghleus \"{{int:emailuser}}\" air {{SITENAME}}.",
+       "emailuserfooter": "Chaidh am post-d seo a chur o $1 gu $2 leis an fheart \"{{int:emailuser}}\" air {{SITENAME}}.",
        "usermessage-summary": "A' fàgail teachdaireachd an t-siostaim.",
        "usermessage-editor": "Teachdaire an t-siostaim",
        "usermessage-template": "MediaWiki:UserMessage",
        "dellogpage": "Loga an sguabaidh às",
        "dellogpagetext": "Seo liosta dhe na chaidh a sguabadh às o chionn goirid.",
        "deletionlog": "loga an sguabaidh às",
-       "reverted": "Air tilleadh gu mùthadh roimhe",
+       "reverted": "Air aiseag gu mùthadh nas sine",
        "deletecomment": "Adhbhar:",
        "deleteotherreason": "Adhbhar eile/a bharrachd:",
        "deletereasonotherlist": "Adhbhar eile",
        "tooltip-ca-nstab-category": "Seall duilleag na roinn-seòrsa",
        "tooltip-minoredit": "Comharraich seo mar dheasachadh beag",
        "tooltip-save": "Sàbhail na mùthaidhean agad",
+       "tooltip-publish": "Foillsich na mùthaidhean agad",
        "tooltip-preview": "Ro-sheall na mùthaidhean agad; saoil an cleachd thu seo mus sàbhail thu iad?",
        "tooltip-diff": "Seall na mùthaidhean a chuir mi air an teacs",
        "tooltip-compareselectedversions": "Seall an diofar eadar an dà mhùthadh dhen duilleag seo a thagh thu",
        "markaspatrolledtext": "Cuir comharra freiceadain ris an duilleag seo",
        "markedaspatrolled": "Comharra freiceadain ris",
        "markedaspatrolledtext": "Chaidh comharra freiceadain a chur ris a' mhùthadh de [[:$1]] a thagh thu.",
-       "rcpatroldisabled": "Chaidh gleus nam freiceadan airson atharraichean o chionn goirid a chur à comas",
-       "rcpatroldisabledtext": "Tha gleus nam freiceadan airson atharraichean o chionn goirid à comas an-dràsta.",
+       "rcpatroldisabled": "Chaidh feart nam freiceadan airson atharraichean o chionn goirid a chur à comas",
+       "rcpatroldisabledtext": "Tha feart nam freiceadan airson atharraichean o chionn goirid à comas an-dràsta.",
        "markedaspatrollederror": "Cha ghabh comharra freiceadain a chur ris",
        "markedaspatrollederrortext": "Feumaidh tu mùthadh a shònrachadh gus comharra freiceadain a chur ris.",
        "markedaspatrollederror-noautopatrol": "Chan fhaod thu comharra freiceadain a chur ris na h-atharraichean agad fhèin.",
        "monthsall": "na h-uile",
        "confirmemail": "Dearbhaich an seòladh puist-dhealain",
        "confirmemail_noemail": "Cha dug thu seachad seòladh puist-d dligheach ann an [[Special:Preferences|roghainnean a' chleachdaiche]] agad.",
-       "confirmemail_text": "Iarraidh {{SITENAME}} ort gun dearbhaich thu an seòladh puist-d agad mus cleachd thu gleusan puist-d.\nCleachd am putan gu h-ìosal gus post-d dearbhaidh a chur dhan t-seòladh agad.\nBidh ceangal le còd sa phost-d ud;\nluchdaich an ceangal sa bhrabhsair agad airson dearbhadh gu bheil an seòladh puist-d agad dligheach.",
+       "confirmemail_text": "Iarraidh {{SITENAME}} ort gun dearbhaich thu an seòladh puist-d agad mus cleachd thu feartan puist-d.\nCleachd am putan gu h-ìosal gus post-d dearbhaidh a chur dhan t-seòladh agad.\nBidh ceangal le còd sa phost-d ud;\nluchdaich an ceangal sa bhrabhsair agad airson dearbhadh gu bheil an seòladh puist-d agad dligheach.",
        "confirmemail_pending": "Chaidh còd dearbhaidh a chur thugad air a' phost-d mar-thà;\nma tha thu air a' chunntas agad a chruthachadh o chionn goirid, 's math dh'fhaoidte gum b' feairrde thu feitheamh mionaid no dhà ach an ruig e thu mus iarr thu còd ùr.",
        "confirmemail_send": "Cuir còd dearbhaidh thugam",
        "confirmemail_sent": "Chaidh post-d dearbhaidh a chur.",
-       "confirmemail_oncreate": "Chaidh còd dearbhaidh a chur dhan t-seòladh puist-d agad.\nChan eil thu feumach air a' chòd seo airson logadh a-steach, ach feumaidh tu a thoirt seachad mus cleachd thu gleus sam bith san uicidh a chleachdas post-d.",
+       "confirmemail_oncreate": "Chaidh còd dearbhaidh a chur dhan t-seòladh puist-d agad.\nChan eil thu feumach air a' chòd seo airson logadh a-steach, ach feumaidh tu a thoirt seachad mus cleachd thu feart sam bith san uicidh a chleachdas post-d.",
        "confirmemail_sendfailed": "Cha deach le {{SITENAME}} post-d dearbhaidh a chur thugad.\nDearbhaich nach eil caractar mì-dhligheach san t-seòladh puist-d agad.\n\nSeo na thill an t-inneal puist-d: $1",
        "confirmemail_invalid": "Tha an còd dearbhaidh mì-dhligheach.\n'S dòcha gun do dh'fhalbh an ùine air.",
        "confirmemail_needlogin": "$1 gus an seòladh puist-d agad a dhearbhadh.",
        "confirmemail_success": "Chaidh an seòladh puist-d agad a dhearbhadh.\nFaodaidh tu [[Special:UserLogin|logadh a-steach]] a-nis 's tlachd a ghabhail às an uicidh.",
        "confirmemail_loggedin": "Tha an seòladh puist-d agad air a dhearbhadh a-nis.",
        "confirmemail_subject": "Dearbhadh an t-seòlaidh puist-d air {{SITENAME}}",
-       "confirmemail_body": "Chlàraich chuideigin - 's sinne an dùil gur e tusa a bh' ann - cunntas \"$2\"\nair {{SITENAME}} leis an t-seòladh puist-d seo on t-seòladh IP $1.\n\nGus dearbhadh gur an agad fhèin a tha an cunntas seo agus gus na gleusan puist-d\na ghnìomhachadh air {{SITENAME}}, fosgail an ceangal seo sa bhrabhsair agad:\n\n$3\n\nMur e *tusa* a bh' ann a chlàraich an cunntas seo, lean air a' cheangal seo\ngus sgur dhen dearbhadh leis a' phost-d:\n\n$5\n\nFalbhaidh an ùine air a' chòd dearbhaidh seo $4.",
-       "confirmemail_body_changed": "Dh'atharraich chuideigin - 's sinne an dùil gur e tusa a bh' ann - an seòladh puist-d\naig a' chunntas \"$2\" air {{SITENAME}} dhan t-seòladh puist-d seo on t-seòladh IP $1.\n\nGus dearbhadh gur an agad fhèin a tha an cunntas seo agus gus na gleusan puist-d\na ghnìomhachadh às ùr air {{SITENAME}}, fosgail an ceangal seo sa bhrabhsair agad:\n\n$3\n\nMur e *tusa* a bh' ann a chlàraich an cunntas seo, lean air a' cheangal seo\ngus sgur dhen dearbhadh leis a' phost-d:\n\n$5\n\nFalbhaidh an ùine air a' chòd dearbhaidh seo $4.",
-       "confirmemail_body_set": "Shuidhich chuideigin - 's sinne an dùil gur e tusa a bh' ann - an seòladh puist-d\naig a' chunntas \"$2\" air {{SITENAME}} dhan t-seòladh puist-d seo on t-seòladh IP $1.\n\nGus dearbhadh gur an agad fhèin a tha an cunntas seo agus gus na gleusan puist-d\na ghnìomhachadh air {{SITENAME}}, fosgail an ceangal seo sa bhrabhsair agad:\n\n$3\n\nMur e *tusa* a bh' ann a chlàraich an cunntas seo, lean air a' cheangal seo\ngus sgur dhen dearbhadh leis a' phost-d:\n\n$5\n\nFalbhaidh an ùine air a' chòd dearbhaidh seo $4.",
+       "confirmemail_body": "Chlàraich chuideigin - 's sinne an dùil gur e tusa a bh' ann - cunntas \"$2\"\nair {{SITENAME}} leis an t-seòladh puist-d seo on t-seòladh IP $1.\n\nGus dearbhadh gur an agad fhèin a tha an cunntas seo agus gus na feartan puist-d\na ghnìomhachadh air {{SITENAME}}, fosgail an ceangal seo sa bhrabhsair agad:\n\n$3\n\nMur e *tusa* a bh' ann a chlàraich an cunntas seo, lean air a' cheangal seo\ngus sgur dhen dearbhadh leis a' phost-d:\n\n$5\n\nFalbhaidh an ùine air a' chòd dearbhaidh seo $4.",
+       "confirmemail_body_changed": "Dh'atharraich chuideigin - 's sinne an dùil gur e tusa a bh' ann - an seòladh puist-d\naig a' chunntas \"$2\" air {{SITENAME}} dhan t-seòladh puist-d seo on t-seòladh IP $1.\n\nGus dearbhadh gur an agad fhèin a tha an cunntas seo agus gus na feartan puist-d\na ghnìomhachadh às ùr air {{SITENAME}}, fosgail an ceangal seo sa bhrabhsair agad:\n\n$3\n\nMur e *tusa* a bh' ann a chlàraich an cunntas seo, lean air a' cheangal seo\ngus sgur dhen dearbhadh leis a' phost-d:\n\n$5\n\nFalbhaidh an ùine air a' chòd dearbhaidh seo $4.",
+       "confirmemail_body_set": "Shuidhich chuideigin - 's sinne an dùil gur e tusa a bh' ann - an seòladh puist-d\naig a' chunntas \"$2\" air {{SITENAME}} dhan t-seòladh puist-d seo on t-seòladh IP $1.\n\nGus dearbhadh gur an agad fhèin a tha an cunntas seo agus gus na feartan puist-d\na ghnìomhachadh air {{SITENAME}}, fosgail an ceangal seo sa bhrabhsair agad:\n\n$3\n\nMur e *tusa* a bh' ann a chlàraich an cunntas seo, lean air a' cheangal seo\ngus sgur dhen dearbhadh leis a' phost-d:\n\n$5\n\nFalbhaidh an ùine air a' chòd dearbhaidh seo $4.",
        "confirmemail_invalidated": "Chaidh sgur de dhearbhadh an t-seòlaidh puist-d",
        "invalidateemail": "Sguir de dhearbhadh an t-seòlaidh puist-d",
        "scarytranscludedisabled": "[Tha gabhail a-steach 'na iomradh eadar-uicidh à comas]",
index 5fbcb34..8494c41 100644 (file)
@@ -22,7 +22,8 @@
                        "VaiPolaSombra",
                        "Macofe",
                        "Banjo",
-                       "Josep Maria Roca Peña"
+                       "Josep Maria Roca Peña",
+                       "Luan"
                ]
        },
        "tog-underline": "Subliñar as ligazóns:",
        "tagline": "De {{SITENAME}}",
        "help": "Axuda",
        "search": "Procura",
+       "search-ignored-headings": " #<!-- Deixe esta liña tal e como está --> <pre>\n# Cabeceiras que serán ignoradas nas buscas.\n# Os cambios feitos aquí realízanse en canto se indexa a páxina coa cabeceira.\n# Pode forzar o reindexado da páxina facendo unha edición baleira.\n# A sintaxe é a seguinte:\n#   * Todo o que vaia despois dun carácter \"#\" ata o final da liña é un comentario\n#   * Toda liña que non estea en branco é o título exacto que ignorar, coas maiúsculas e minúsculas\nReferencias\nLigazóns externas\nVéxase tamén\n #</pre> <!-- Deixe esta liña tal e como está -->",
        "searchbutton": "Procurar",
        "go": "Mostrar",
        "searcharticle": "Artigo",
        "yourpasswordagain": "Insira o contrasinal outra vez:",
        "createacct-yourpasswordagain": "Confirme o contrasinal",
        "createacct-yourpasswordagain-ph": "Insira o contrasinal outra vez",
-       "remembermypassword": "Lembrar o meu contrasinal neste ordenador (ata $1 {{PLURAL:$1|día|días}})",
        "userlogin-remembermypassword": "Manter a miña conexión",
        "userlogin-signwithsecure": "Utilizar a conexión segura",
        "cannotloginnow-title": "Non se pode iniciar a sesión agora mesmo",
        "passwordreset-emailelement": "Nome de usuario: \n$1\n\nContrasinal temporal: \n$2",
        "passwordreset-emailsentemail": "Se esta é unha dirección de correo electrónico asociada á súa conta, entón enviarase un correo electrónico para o restablecemento do seu contrasinal.",
        "passwordreset-emailsentusername": "Se hai unha dirección de correo electrónico asociada con este nome de usuario, entón enviarase un correo electrónico para o restablecemento do contrasinal.",
-       "passwordreset-emailsent-capture": "Enviouse un correo electrónico de restablecemento do contrasinal, mostrado a continuación.",
-       "passwordreset-emailerror-capture": "Xerouse un correo electrónico de restablecemento do contrasinal, mostrado a continuación, pero o envío {{GENDER:$2|ao usuario|á usuaria}} fallou: $1",
        "passwordreset-emailsent-capture2": "{{PLURAL:$1|O correo de reinicialización do contrasinal foi enviado|Os correos de reinicialización do contrasinal foron enviados}}. {{PLURAL:$1|O nome de usuario e contrasinal móstrase abaixo|A lista de nomes de usuarios e contrasinais móstranse abaixo}}.",
        "passwordreset-emailerror-capture2": "O envío do correo {{GENDER:$2|ó usuario|á usuaria}} fallou: $1 {{PLURAL:$3|O nome de usuario e contrasinal móstrase abaixo|A lista de usuarios e contrasinais móstranse abaixo}}.",
        "passwordreset-nocaller": "Cómpre proporcionar un chamador",
        "passwordreset-nodata": "Non se indicou o nome de usuario ou a dirección de correo electrónico",
        "changeemail": "Cambiar ou eliminar o enderezo de correo electrónico",
        "changeemail-header": "Encha este formulario para cambiar o seu enderezo de correo electrónico. Se vostede quere eliminar a asociación da dirección de correo electrónico da súa conta, deixe en branco a nova dirección de correo electrónico cando envíe o formulario.",
-       "changeemail-passwordrequired": "Terá que escribir o seu contrasinal para confirmar este cambio.",
        "changeemail-no-info": "Debe rexistrarse para acceder directamente a esta páxina.",
        "changeemail-oldemail": "Enderezo de correo electrónico actual:",
        "changeemail-newemail": "Novo enderezo de correo electrónico:",
        "minoredit": "Esta é unha edición pequena",
        "watchthis": "Vixiar esta páxina",
        "savearticle": "Gardar a páxina",
+       "savechanges": "Gardar os cambios",
        "publishpage": "Publicar a páxina",
+       "publishchanges": "Publicar cambios",
        "preview": "Vista previa",
        "showpreview": "Mostrar a vista previa",
        "showdiff": "Mostrar os cambios",
        "content-model-css": "CSS",
        "content-json-empty-object": "Obxecto baleiro",
        "content-json-empty-array": "Matriz baleira",
+       "deprecated-self-close-category": "Páxinas que usan etiquetas HTML de auto-pechado non válidas",
+       "deprecated-self-close-category-desc": "Páxinas que conteñen unha etiqueta HTML de auto-pechado non válida, como <code>&lt;b/></code> ou <code>&lt;span/></code>. O comportamento destas etiquetas vai cambiar para gardar a consistencia coa especificación HTML5, polo que o seu uso no texto wiki está desbotado.",
        "duplicate-args-warning": "<strong>Advertencia:</strong> [[:$1]] está chamando a [[:$2]] con máis dun valor para o parámetro \"$3\". Só se usará o último valor proporcionado.",
        "duplicate-args-category": "Páxinas con argumentos duplicados nas chamadas aos modelos",
        "duplicate-args-category-desc": "Esta páxina contén as chamadas aos modelos que utilizan argumentos duplicados, como <code><nowiki>{{exemplo|bar=1|bar=2}}</nowiki></code> ou <code><nowiki>{{exemplo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Semella que alguén xa desfixo a edición.",
        "undo-summary": "Desfíxose a edición $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|conversa]])",
        "undo-summary-username-hidden": "Desfíxose a edición $1 dun usuario agochado",
-       "cantcreateaccounttitle": "Non pode crear unha conta de usuario",
        "cantcreateaccount-text": "A creación de contas desde este enderezo IP ('''$1''') foi bloqueada por [[User:$3|$3]].\n\nA razón dada por $3 foi ''$2''",
        "cantcreateaccount-range-text": "O usuario [[User:$3|$3]] bloqueou a creación de contas desde enderezos IP no rango <strong>$1</strong>, no que se inclúe o seu enderezo IP (<strong>$4</strong>).\n\nA razón que deu $3 foi <em>$2</em>.",
        "viewpagelogs": "Ver os rexistros desta páxina",
        "rows": "Filas:",
        "columns": "Columnas:",
        "searchresultshead": "Procurar",
-       "stub-threshold": "Límite superior para o formato de ligazóns de bosquexo($1):",
+       "stub-threshold": "Límite superior de tamaño para o formato das ligazóns cara bosquexos ($1):",
        "stub-threshold-sample-link": "exemplo",
        "stub-threshold-disabled": "Desactivado",
        "recentchangesdays": "Número de días a mostrar nos cambios recentes:",
        "grant-group-high-volume": "Realizar actividades de alto volume",
        "grant-group-customization": "Personalización e preferencias",
        "grant-group-administration": "Realizar accións administrativas",
+       "grant-group-private-information": "Acceder a datos privados sobre ti",
        "grant-group-other": "Outras actividades",
        "grant-blockusers": "Bloquear e desbloquear usuarios",
        "grant-createaccount": "Crear contas",
        "grant-highvolume": "Edicións de gran volume",
        "grant-oversight": "Agochar usuarios e eliminar revisións",
        "grant-patrol": "Patrullar os cambios feitos nas páxinas",
+       "grant-privateinfo": "Acceder a información privada",
        "grant-protect": "Protexer e desprotexer páxinas",
        "grant-rollback": "Reverter os cambios feitos nas páxinas",
        "grant-sendemail": "Enviar correos electrónicos a outros usuarios",
        "action-applychangetags": "aplicar etiquetas xunto cos cambios",
        "action-changetags": "engadir e quitar etiquetas arbitrarias a revisións individuais e entradas do rexistro",
        "action-deletechangetags": "borrar etiquetas da base de datos",
+       "action-purge": "purgar esta páxina",
        "nchanges": "$1 {{PLURAL:$1|modificación|modificacións}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|desde a última visita}}",
        "enhancedrc-history": "historial",
        "recentchanges": "Cambios recentes",
        "recentchanges-legend": "Opcións dos cambios",
-       "recentchanges-summary": "Nesta páxina pode seguir as modificacións máis recentes feitas no wiki.",
+       "recentchanges-summary": "Nesta páxina podes seguir as modificacións máis recentes feitas no wiki.",
        "recentchanges-noresult": "Non se produciron cambios que coincidisen con eses criterios durante o período especificado.",
        "recentchanges-feed-description": "Nesta fonte de novas pode seguir as modificacións máis recentes feitas no wiki.",
        "recentchanges-label-newpage": "Esta edición creou unha nova páxina",
        "uploadstash-errclear": "Fallou o borrado de ficheiros.",
        "uploadstash-refresh": "Actualizar a lista de ficheiros",
        "uploadstash-thumbnail": "ver miniatura",
+       "uploadstash-exception": "Imposible gardar a subida na reserva ($1): \"$2\".",
        "invalid-chunk-offset": "Desprazamento inválido do fragmento",
        "img-auth-accessdenied": "Acceso rexeitado",
        "img-auth-nopathinfo": "Falta a PATH_INFO.\nO seu servidor non está configurado para pasar esta información.\nPode ser que estea baseado en CGI e non soporte img_auth.\nVéxase https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "filerevert-submit": "Reverter",
        "filerevert-success": "Reverteuse \"'''[[Media:$1|$1]]'''\" á [$4 versión do $2 ás $3].",
        "filerevert-badversion": "Non existe unha versión local anterior deste ficheiro coa data e hora indicadas.",
+       "filerevert-identical": "A versión actual do ficheiro é igual á seleccionada.",
        "filedelete": "Borrar \"$1\"",
        "filedelete-legend": "Eliminar un ficheiro",
        "filedelete-intro": "Está a piques de eliminar o ficheiro \"'''[[Media:$1|$1]]'''\" xunto con todo o seu historial.",
        "watchnologin": "Non accedeu ao sistema",
        "addwatch": "Engadir á lista vixilancia",
        "addedwatchtext": "A páxina \"[[:$1]]\" e mais a súa conversa foron engadidas á súa [[Special:Watchlist|lista de vixilancia]].",
+       "addedwatchtext-talk": "\"[[:$1]]\" xunto coa súa páxina asociada foron engadidas á túa [[Special:Watchlist|lista de vixilancia]].",
        "addedwatchtext-short": "A páxina \"$1\" foi engadida á súa lista de vixilancia.",
        "removewatch": "Eliminar da lista de vixilancia",
        "removedwatchtext": "A páxina \"[[:$1]]\" e mais a súa conversa foron eliminadas da súa [[Special:Watchlist|lista de vixilancia]].",
+       "removedwatchtext-talk": "\"[[:$1]]\" xunto coa súa páxina asociada foron eliminadas da túa [[Special:Watchlist|lista de vixilancia]].",
        "removedwatchtext-short": "A páxina \"$1\" foi eliminada da súa lista de vixilancia.",
        "watch": "Vixiar",
        "watchthispage": "Vixiar esta páxina",
        "notvisiblerev": "A revisión foi borrada",
        "watchlist-details": "Hai {{PLURAL:$1|unha páxina|$1 páxinas}} na súa lista de vixilancia, sen contar as de conversa.",
        "wlheader-enotif": "A notificación por correo electrónico está activada.",
-       "wlheader-showupdated": "As páxinas que cambiaron desde a súa última visita móstranse en '''negra'''.",
+       "wlheader-showupdated": "As páxinas que cambiaron desde a súa última visita móstranse en <strong>letra grosa</strong>.",
        "wlnote": "A continuación {{PLURAL:$1|está a última modificación|están as últimas <strong>$1</strong> modificacións}} {{PLURAL:$2|na última hora|nas últimas <strong>$2</strong> horas}} ata o $3 ás $4.",
        "wlshowlast": "Mostrar as últimas $1 horas e os últimos $2 días",
        "watchlist-hide": "Agochar",
        "rollbacklinkcount-morethan": "reverter máis de $1 {{PLURAL:$1|edición|edicións}}",
        "rollbackfailed": "Houbo un erro ao reverter as edicións",
        "rollback-missingparam": "Faltan parámetros obrigatorios na solicitude.",
+       "rollback-missingrevision": "Non se poden cargar datos da revisión.",
        "cantrollback": "Non se pode desfacer a edición; o último colaborador é o único autor desta páxina.",
        "alreadyrolled": "Non se pode desfacer a edición en \"[[:$1]]\" feita por [[User:$2|$2]] ([[User talk:$2|conversa]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); alguén máis editou ou desfixo os cambios desta páxina.\n\nA última edición fíxoa [[User:$3|$3]] ([[User talk:$3|conversa]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "O resumo de edición foi: <em>$1</em>.",
        "undeletehistorynoadmin": "Esta páxina foi borrada.\nO motivo do borrado consta no resumo que aparece a continuación, xunto cos detalles dos usuarios que editaron esta páxina antes da súa eliminación.\nO texto destas revisións eliminadas só está á disposición dos administradores.",
        "undelete-revision": "Revisión eliminada de \"$1\" (o $4 ás $5) feita por $3:",
        "undeleterevision-missing": "Revisión non válida ou inexistente. Pode que a ligazón conteña un erro ou que a revisión se restaurase ou eliminase do arquivo.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|Unha revisión non pode ser restaurada|$1 revisións non poden ser restauradas}} porque {{PLURAL:$1|o seu|os seus}}  <code>rev_id</code> xa {{PLURAL:$1|está|están}} en uso.",
        "undelete-nodiff": "Non se atopou ningunha revisión anterior.",
        "undeletebtn": "Restaurar",
        "undeletelink": "ver/restaurar",
        "undeletedrevisions": "{{PLURAL:$1|Restaurouse $1 revisión|Restauráronse $1 revisións}}",
        "undeletedrevisions-files": "Restauráronse $1 {{PLURAL:$1|revisión|revisións}} e $2 {{PLURAL:$2|ficheiro|ficheiros}}",
        "undeletedfiles": "{{PLURAL:$1|Restaurouse $1 ficheiro|Restauráronse $1 ficheiros}}",
-       "cannotundelete": "Houbo un erro durante a restauración:\n$1",
+       "cannotundelete": "Algunhas ou todas as restauracións fallaronː\n$1",
        "undeletedpage": "'''A páxina \"$1\" foi restaurada'''\n\nComprobe o [[Special:Log/delete|rexistro de borrados]] para ver as entradas recentes no rexistro de páxinas eliminadas e restauradas.",
        "undelete-header": "Consulte [[Special:Log/delete|no rexistro de borrados]] as páxinas borradas recentemente.",
        "undelete-search-title": "Procurar páxinas borradas",
        "sp-contributions-newbies-sub": "Contribucións dos usuarios novos",
        "sp-contributions-newbies-title": "Contribucións dos usuarios novos",
        "sp-contributions-blocklog": "rexistro de bloqueos",
-       "sp-contributions-suppresslog": "contribucións borradas do usuario",
-       "sp-contributions-deleted": "contribucións borradas do usuario",
+       "sp-contributions-suppresslog": "contribucións {{GENDER:$1|do usuario|da usuaria}} suprimidas",
+       "sp-contributions-deleted": "contribucións {{GENDER:$1|do usuario|da usuaria}} borradas",
        "sp-contributions-uploads": "cargas",
        "sp-contributions-logs": "rexistros",
        "sp-contributions-talk": "conversa",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
        "mw-widgets-titleinput-description-new-page": "a páxina aínda non existe",
        "mw-widgets-titleinput-description-redirect": "redirección cara a $1",
-       "api-error-blacklisted": "Escolla un título diferente e descritivo.",
        "sessionmanager-tie": "Non pode combinar peticións múltiples de tipos de autenticación: $1.",
        "sessionprovider-generic": "sesións $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sesións baseadas nas cookies",
        "log-action-filter-newusers": "Tipo de creación de conta:",
        "log-action-filter-patrol": "Tipo de vixilancia:",
        "log-action-filter-protect": "Tipo de protección:",
-       "log-action-filter-rights": "Tipo de cambio de dereito",
-       "log-action-filter-suppress": "Tipo de borrado",
+       "log-action-filter-rights": "Tipo de cambio de dereito:",
+       "log-action-filter-suppress": "Tipo de borrado:",
        "log-action-filter-upload": "Tipo de subida:",
        "log-action-filter-all": "Todas",
        "log-action-filter-block-block": "Bloquear",
        "linkaccounts-submit": "Vincular contas",
        "unlinkaccounts": "Desvincular contas",
        "unlinkaccounts-success": "A conta foi desvinculada.",
-       "authenticationdatachange-ignored": "Os cambios de datos de autenticación non foron xerados. Está configurado o provedor?"
+       "authenticationdatachange-ignored": "Os cambios de datos de autenticación non foron xerados. Está configurado o provedor?",
+       "userjsispublic": "Lembre: As subpáxinas JavaScript non deberían conter datos confidenciais porque outros usuarios poden velos.",
+       "usercssispublic": "Lembre: As subpáxinas CSS non deberían conter datos confidenciais porque outros usuarios poden velos."
 }
index b14e01a..432338d 100644 (file)
@@ -6,7 +6,8 @@
                        "Marwan Mohamad",
                        "Matma Rex",
                        "NoiX180",
-                       "Zhoelyakin"
+                       "Zhoelyakin",
+                       "Amire80"
                ]
        },
        "tog-underline": "Garisiyi totibawa pranala",
        "exif-orientation-1": "Normal",
        "namespacesall": "nga'amila",
        "monthsall": "nga'amila",
-       "signature": "[[{{ns:user}}:$1|$2]]\n([[{{ns:user_talk}}:$1|bisala]])",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|bisala]])",
        "specialpages": "Halaman Spesial",
        "tag-filter": "[[Special:Tags|Tag]]filter:",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag}}]]: $2)",
index 9650318..75ce391 100644 (file)
                        "Gothicspeaker"
                ]
        },
-       "underline-always": "Sinteino",
-       "underline-never": "Niu",
+       "tog-previewonfirst": "𐌰𐍄𐌰𐌿𐌲𐌴𐌹 𐍆𐌰𐌿𐍂𐌰𐍃𐌹𐌿𐌽 𐌰𐍄 𐍆𐍂𐌿𐌼𐌹𐍃𐍄𐌰 𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽",
+       "underline-always": "𐍃𐌹𐌽𐍄𐌴𐌹𐌽𐍉",
+       "underline-never": "𐌽𐌹 𐌰𐌹𐍅",
        "sunday": "𐌰𐍆𐌰𐍂𐍃𐌰𐌱𐌱𐌰𐍄𐍉",
        "monday": "𐌼𐌴𐌽𐌹𐌽𐍃 𐌳𐌰𐌲𐍃",
        "tuesday": "𐍄𐌴𐌹𐍅𐌹𐍃 𐌳𐌰𐌲𐍃",
-       "wednesday": "Midiwiko",
-       "thursday": "ð\90\8d\80ð\90\8c°ð\90\8c¹ð\90\8c½ð\90\8d\84ð\90\8c´𐌳𐌰𐌲𐍃",
-       "friday": "ð\90\8d\80ð\90\8c°ð\90\8d\82ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8d\83𐌳𐌰𐌲𐍃",
+       "wednesday": "𐍅𐍉𐌳𐌰𐌽𐌹𐍃 𐌳𐌰𐌲𐍃",
+       "thursday": "ð\90\8c¸ð\90\8c¿ð\90\8c½ð\90\8c°ð\90\8d\82ð\90\8c¹ð\90\8d\83 𐌳𐌰𐌲𐍃",
+       "friday": "ð\90\8d\86ð\90\8d\82ð\90\8c°ð\90\8c¿ð\90\8c¾ð\90\8d\89ð\90\8c½ð\90\8d\83 𐌳𐌰𐌲𐍃",
        "saturday": "𐍃𐌰𐌱𐌱𐌰𐍄𐍉",
        "sun": "𐍃𐌿𐌽",
        "mon": "𐌼𐌴𐌽",
-       "tue": "ð\90\8c°ð\90\8d\82ð\90\8c´",
+       "tue": "ð\90\8d\84ð\90\8c´ð\90\8c¹ð\90\8d\85",
        "wed": "𐍅𐍉𐌳",
-       "thu": "ð\90\8d\80ð\90\8c°ð\90\8c¹",
-       "fri": "ð\90\8d\86ð\90\8d\82ð\90\8c¹",
+       "thu": "ð\90\8c¸ð\90\8c¿ð\90\8c½",
+       "fri": "ð\90\8d\86ð\90\8d\82ð\90\8c°ð\90\8c¿",
        "sat": "𐍃𐌰𐌼",
        "january": "𐌾𐌰𐌽𐌿𐌰𐍂𐌴𐌹𐍃",
        "february": "𐍆𐌰𐌹𐌱𐍂𐌿𐌰𐍂𐌴𐌹𐍃",
        "october": "𐌰𐌿𐌺𐍄𐍉𐌱𐌰𐌹𐍂",
        "november": "𐌽𐌰𐌿𐌱𐌰𐌹𐌼𐌱𐌰𐌹𐍂",
        "december": "𐌾𐌹𐌿𐌻𐌴𐌹𐍃",
-       "january-gen": "ð\90\8c°ð\90\8d\86ð\90\8d\84ð\90\8c¿ð\90\8c¼ð\90\8c¹ð\90\8c½ð\90\8d\83 ð\90\8c¾ð\90\8c¹ð\90\8c¿ð\90\8c»ð\90\8c´ð\90\8c¹𐍃",
-       "february-gen": "ð\90\8d\86ð\90\8c°ð\90\8c¹ð\90\8c±ð\90\8d\82ð\90\8c¿ð\90\8c°ð\90\8d\82ð\90\8c´ð\90\8c¹𐍃",
-       "march-gen": "ð\90\8cºð\90\8c°ð\90\8c»ð\90\8c³ð\90\8c¼ð\90\8c´ð\90\8c½ð\90\8d\89ð\90\8c¸ð\90\8c¹𐍃",
-       "april-gen": "ð\90\8c²ð\90\8d\82ð\90\8c°ð\90\8d\83ð\90\8c¼ð\90\8c´ð\90\8c½ð\90\8d\89ð\90\8c¸𐌹𐍃",
-       "may-gen": "ð\90\8c±ð\90\8c»ð\90\8d\89ð\90\8c¼ð\90\8c°ð\90\8c¼ð\90\8c´ð\90\8c½ð\90\8d\89ð\90\8c¸ð\90\8c¹𐍃",
-       "june-gen": "ð\90\8d\85ð\90\8c°ð\90\8d\82ð\90\8c¼ð\90\8c¼ð\90\8c´ð\90\8c½ð\90\8d\89ð\90\8c¸ð\90\8c¹𐍃",
-       "july-gen": "ð\90\8c·ð\90\8c°ð\90\8d\85ð\90\8c¹ð\90\8c¼ð\90\8c´ð\90\8c½ð\90\8d\89ð\90\8c¸ð\90\8c¹𐍃",
-       "august-gen": "ð\90\8c°ð\90\8d\83ð\90\8c°ð\90\8c½ð\90\8c¼ð\90\8c´ð\90\8c½ð\90\8d\89ð\90\8c¸ð\90\8c¹𐍃",
-       "september-gen": "ð\90\8c°ð\90\8cºð\90\8d\82ð\90\8c°ð\90\8c½ð\90\8c¼ð\90\8c´ð\90\8c½ð\90\8d\89ð\90\8c¸ð\90\8c¹ð\90\8d\83",
-       "october-gen": "ð\90\8d\85ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c´ð\90\8c½ð\90\8d\89ð\90\8c¸ð\90\8c¹ð\90\8d\83",
-       "november-gen": "ð\90\8d\86ð\90\8d\82ð\90\8c¿ð\90\8c¼ð\90\8c¹ð\90\8c½ð\90\8d\83 ð\90\8c¾ð\90\8c¹ð\90\8c¿ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8d\83",
-       "december-gen": "ð\90\8c¾ð\90\8c¹ð\90\8c¿ð\90\8c»ð\90\8c´ð\90\8c¹𐍃",
+       "january-gen": "ð\90\8c¾ð\90\8c°ð\90\8c½ð\90\8c¿ð\90\8c°ð\90\8d\82ð\90\8c¾ð\90\8d\89𐍃",
+       "february-gen": "ð\90\8d\86ð\90\8c°ð\90\8c¹ð\90\8c±ð\90\8d\82ð\90\8c¿ð\90\8c°ð\90\8d\82ð\90\8c¾ð\90\8d\89𐍃",
+       "march-gen": "ð\90\8c¼ð\90\8c°ð\90\8d\82ð\90\8d\84ð\90\8c¹ð\90\8c°ð\90\8c¿𐍃",
+       "april-gen": "ð\90\8c°ð\90\8d\80ð\90\8d\82ð\90\8c´ð\90\8c¹ð\90\8c»𐌹𐍃",
+       "may-gen": "ð\90\8c¼ð\90\8c°ð\90\8c¾ð\90\8c°ð\90\8c¿𐍃",
+       "june-gen": "ð\90\8c¾ð\90\8c¿ð\90\8c½ð\90\8c¹ð\90\8c°ð\90\8c¿𐍃",
+       "july-gen": "ð\90\8c¾ð\90\8c¿ð\90\8c»ð\90\8c¹ð\90\8c°ð\90\8c¿𐍃",
+       "august-gen": "ð\90\8c°ð\90\8c²ð\90\8c¿ð\90\8d\83ð\90\8d\84ð\90\8c°ð\90\8c¿𐍃",
+       "september-gen": "ð\90\8d\83ð\90\8c°ð\90\8c¹ð\90\8d\80ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8c¼ð\90\8c±ð\90\8c°ð\90\8c¹ð\90\8d\82",
+       "october-gen": "ð\90\8c°ð\90\8c¿ð\90\8cºð\90\8d\84ð\90\8d\89ð\90\8c±ð\90\8c°ð\90\8c¹ð\90\8d\82",
+       "november-gen": "ð\90\8c½ð\90\8c°ð\90\8c¿ð\90\8c±ð\90\8c°ð\90\8c¹ð\90\8c¼ð\90\8c±ð\90\8c°ð\90\8c¹ð\90\8d\82",
+       "december-gen": "ð\90\8c¾ð\90\8c¹ð\90\8c¿ð\90\8c»ð\90\8c¾ð\90\8d\89𐍃",
        "jan": "𐌾𐌰𐌽",
        "feb": "𐍆𐌰𐌽",
        "mar": "𐌼𐌰𐍂",
        "oct": "𐌰𐌿𐌺",
        "nov": "𐌽𐌰𐌿𐌱",
        "dec": "𐌳𐌰𐌹𐌺",
+       "january-date": "𐌾𐌰𐌽𐌿𐌰𐍂𐌴𐌹𐍃 $1",
+       "february-date": "𐍆𐌰𐌹𐌱𐍂𐌿𐌰𐍂𐌴𐌹𐍃 $1",
+       "march-date": "𐌼𐌰𐍂𐍄𐌹𐌿𐍃 $1",
+       "april-date": "𐌰𐍀𐍂𐌹𐌻𐌹𐍃 $1",
        "september-date": "𐍃𐌰𐌹𐍀𐍄𐌰𐌹𐌼𐌱𐌰𐌹𐍂 $1",
        "november-date": "𐌽𐌰𐌿𐌱𐌰𐌹𐌼𐌱𐌰𐌹𐍂 $1",
        "pagecategories": "{{PLURAL:$1|𐌺𐌿𐌽𐌹|𐌺𐌿𐌽𐌾𐌰}}",
        "category_header": "𐌻𐌰𐌿𐌱𐍉𐍃 𐌹𐌽 𐌺𐌿𐌽𐌾𐌰 \"$1\"",
-       "subcategories": "Dalaþkunjos",
-       "category-media-header": "𐌼𐌴𐌳𐌾𐌰 𐌹𐌽𐌽 𐌺𐌿𐌽𐌾𐌰 \"$1\"",
+       "subcategories": "𐌼𐌹𐌽𐌽𐌹𐌶𐍉𐌽𐌰 𐌺𐌿𐌽𐌾𐌰",
+       "category-media-header": "𐌼𐌴𐌳𐌾𐌰 𐌹𐌽 𐌺𐌿𐌽𐌾𐌰 \"$1\"",
        "hidden-categories": "{{PLURAL:$1|𐌰𐍆𐍆𐌹𐌻𐌷𐌰𐌽 𐌺𐌿𐌽𐌹|𐌰𐍆𐍆𐌹𐌻𐌷𐌰𐌽𐌰 𐌺𐌿𐌽𐌾𐌰}}",
+       "hidden-category-category": "𐌰𐍆𐍆𐌹𐌻𐌷𐌰𐌽𐌰 𐌺𐌿𐌽𐌾𐌰",
        "category-subcat-count": "{{PLURAL:$2|𐌸𐌰𐍄𐌰 𐌺𐌿𐌽𐌹 𐌷𐌰𐌱𐌰𐌹𐌸 𐌸𐌰𐍄𐌴𐌹𐌽𐌴𐌹 𐌹𐍆𐍄𐌿𐌼 𐌼𐌹𐌽𐌽𐌹𐌶𐍉𐌽 𐌺𐌿𐌽𐌹|𐌸𐌰𐍄𐌰 𐌺𐌿𐌽𐌹 𐌷𐌰𐌱𐌰𐌹𐌸 {{PLURAL:$1|𐌼𐌹𐌽𐌽𐌹𐌶𐍉𐌽 𐌺𐌿𐌽𐌹|𐌹𐍆𐍄𐌿𐌼𐌰 $1 𐌼𐌹𐌽𐌽𐌹𐌶𐍉𐌽𐌰 𐌺𐌿𐌽𐌾𐌰}}, 𐌰𐌻𐌻𐌰𐌹𐌶𐌴 $2 𐌺𐌿𐌽𐌾𐌴.}}",
+       "category-subcat-count-limited": "𐌸𐌰𐍄𐌰 𐌺𐌿𐌽𐌹 𐌷𐌰𐌱𐌰𐌹𐌸 𐌹𐍆𐍄𐌿𐌼𐍉𐌽/𐌹𐍆𐍄𐌿𐌼𐍉𐌽𐌰\n{{PLURAL:$1|𐌼𐌹𐌽𐌽𐌹𐌶𐍉𐌽 𐌺𐌿𐌽𐌹|$1 𐌼𐌹𐌽𐌽𐌹𐌶𐍉𐌽𐌰 𐌺𐌿𐌽𐌾𐌰}}.",
        "category-article-count": "{{PLURAL:$2|𐌸𐌰𐍄𐌰 𐌺𐌿𐌽𐌹 𐌷𐌰𐌱𐌰𐌹𐌸 𐌸𐌰𐍄𐌰𐌹𐌽𐌴𐌹 𐌹𐍆𐍄𐌿𐌼𐌰𐌽 𐌻𐌰𐌿𐍆.|𐌹𐍆𐍄𐌿𐌼𐌰(𐌽𐍃) {{PLURAL:$1|𐌻𐌰𐌿𐍆𐍃 𐌹𐍃𐍄|$1 𐌻𐌰𐌿𐌱𐍉𐍃 𐍃𐌹𐌽𐌳}} 𐌹𐌽 𐌸𐌰𐌼𐌼𐌰 𐌺𐌿𐌽𐌾𐌰, 𐌰𐌻𐌻𐌰𐌹𐌶𐌴 $2 𐌻𐌰𐌿𐌱𐌴.}}",
-       "about": "ð\90\8c¿ð\90\8d\86ð\90\8c°ð\90\8d\82",
-       "article": "ð\90\8d\83ð\90\8c°ð\90\8c¸ð\90\8d\83ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89",
-       "newwindow": "(ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c·ð\90\8c¿ð\90\8c»ð\90\8c¾ð\90\8c¹ð\90\8c¸ ð\90\8c¹ð\90\8c½ð\90\8c½ ð\90\8c½ð\90\8c¹ð\90\8c¿ð\90\8c¾ð\90\8c° ð\90\8c°ð\90\8c¿ð\90\8c²ð\90\8c°ð\90\8c³ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8d\89)",
-       "cancel": "ð\90\8c·ð\90\8c°ð\90\8c»ð\90\8d\84ð\90\8d\83",
+       "about": "ð\90\8c±ð\90\8c¹",
+       "article": "ð\90\8c·ð\90\8c°ð\90\8c±ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8d\83 ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8d\86ð\90\8d\83",
+       "newwindow": "(ð\90\8c¿ð\90\8d\83ð\90\8c»ð\90\8c¿ð\90\8cºð\90\8c¹ð\90\8c¸ ð\90\8c¹ð\90\8c½ ð\90\8c½ð\90\8c¹ð\90\8c¿ð\90\8c¾ð\90\8c°ð\90\8c¼ð\90\8c¼ð\90\8c° ð\90\8c°ð\90\8c¿ð\90\8c²ð\90\8c°ð\90\8c³ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c¹ð\90\8c½)",
+       "cancel": "ð\90\8d\83ð\90\8d\85ð\90\8c´ð\90\8c¹ð\90\8c±",
        "moredotdotdot": "𐌼𐌰𐌹𐍃...",
-       "mypage": "ð\90\8c¼ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c° ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8c°",
-       "mytalk": "𐌲𐌰𐍅𐌰𐌿𐍂𐌳𐌰",
-       "navigation": "ð\90\8d\85ð\90\8c¹ð\90\8c²ð\90\8c°ð\90\8d\84ð\90\8c°ð\90\8c¿ð\90\8c·ð\90\8d\84𐍃",
+       "mypage": "ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8d\86ð\90\8d\83",
+       "mytalk": "ð\90\8c²ð\90\8c°ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c³ð\90\8c¾ð\90\8c°",
+       "navigation": "ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8c±ð\90\8c°ð\90\8c²ð\90\8c°ð\90\8d\85ð\90\8c¹ð\90\8d\83ð\90\8d\83ð\90\8c´ð\90\8c¹𐍃",
        "and": "𐌾𐌰𐌷",
-       "qbfind": "ð\90\8d\83ð\90\8d\89ð\90\8cºð\90\8c´ð\90\8c¹ð\90\8c¸",
-       "qbedit": "ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c¾ð\90\8c°ð\90\8c½",
-       "qbpageoptions": "ð\90\8d\83ð\90\8d\89 ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8c°",
-       "qbmyoptions": "𐌼𐌴𐌹𐌽𐌰 𐍃𐌴𐌹𐌳𐍉𐍃",
-       "actions": "ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8d\83ð\90\8d\84ð\90\8d\85ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8d\83",
-       "namespaces": "ð\90\8c½ð\90\8c°ð\90\8c¼ð\90\8d\89𐍂𐌿𐌼𐌰",
+       "qbfind": "ð\90\8c±ð\90\8c¹ð\90\8c²ð\90\8c¹ð\90\8d\84",
+       "qbedit": "ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c´ð\90\8c¹",
+       "qbpageoptions": "ð\90\8d\83ð\90\8c° ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8d\86ð\90\8d\83",
+       "qbmyoptions": "𐌼𐌴𐌹𐌽𐌰𐌹 𐌻𐌰𐌿𐌱𐍉𐍃",
+       "actions": "ð\90\8d\84ð\90\8d\89ð\90\8c¾ð\90\8c°",
+       "namespaces": "ð\90\8c½ð\90\8c°ð\90\8c¼ð\90\8c°𐍂𐌿𐌼𐌰",
        "variants": "𐌼𐌹𐍃𐍃𐌰𐌻𐌴𐌹𐌺",
-       "errorpagetitle": "ð\90\8d\86ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c¹ð\90\8c½ð\90\8c° ð\90\8c³ð\90\8d\85ð\90\8c°ð\90\8c»ð\90\8c¹ð\90\8d\83",
-       "returnto": "ð\90\8c²ð\90\8c°ð\90\8d\85ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c¾ð\90\8c°ð\90\8c½ ð\90\8c°ð\90\8d\84 $1.",
+       "errorpagetitle": "ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c¶ð\90\8c´ð\90\8c¹",
+       "returnto": "ð\90\8c²ð\90\8c°ð\90\8d\85ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c´ð\90\8c¹ ð\90\8c³ð\90\8c¿ $1.",
        "tagline": "𐍆𐍂𐌰𐌼 {{SITENAME}}",
        "help": "𐌷𐌹𐌻𐍀𐌰",
        "search": "𐍃𐍉𐌺𐌴𐌹",
        "searchbutton": "𐍃𐍉𐌺𐌴𐌹",
-       "go": "𐌲𐌰𐌲𐌲𐌰",
-       "searcharticle": "ð\90\8c°ð\90\8d\86ð\90\8c²ð\90\8c°ð\90\8c²ð\90\8c²ð\90\8c°ð\90\8c½",
+       "go": "𐌲𐌰𐌲𐌲",
+       "searcharticle": "ð\90\8c²ð\90\8c°ð\90\8c²ð\90\8c²",
        "history": "𐌻𐌰𐌿𐌱𐌰𐍃𐍀𐌹𐌻𐌻",
        "history_short": "𐍃𐍀𐌹𐌻𐌻",
        "printableversion": "𐌿𐍃𐌼𐌴𐍂𐌴𐌹𐌽𐍃 𐌳𐌿 𐌿𐍃𐌼𐌴𐌻𐌾𐌰𐌽",
        "permalink": "𐌰𐌹𐍅𐌴𐌹𐌽𐌰 𐌲𐌰𐍅𐌹𐍃𐍃",
+       "print": "𐌿𐍃𐌼𐌴𐌻𐌴𐌹",
        "view": "𐍃𐌰𐌹𐍈",
        "view-foreign": "𐍃𐌰𐌹𐍈 𐌰𐌽𐌰 $1",
        "edit": "𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹",
-       "create": "𐍃𐌺𐌰𐍀𐌾𐌰𐌽",
-       "editthispage": "𐌼𐌰𐌹𐌳𐌾𐌰 𐌸𐍉 𐍃𐌴𐌹𐌳𐍉",
-       "create-this-page": "Skapja þo seido",
-       "delete": "𐌿𐍃𐌽𐌹𐌼",
-       "deletethispage": "𐌿𐍃𐌽𐌹𐌼 𐌸𐍉 𐍃𐌴𐌹𐌳𐌰",
-       "protect": "𐌱𐌰𐌹𐍂𐌲𐌰𐌽",
+       "create": "𐍃𐌺𐌰𐍀𐌴𐌹",
+       "editthispage": "𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹 𐌸𐌰𐌽𐌰 𐌻𐌰𐌿𐍆",
+       "create-this-page": "𐍃𐌺𐌰𐍀𐌴𐌹 𐌸𐌰𐌽𐌰 𐌻𐌰𐌿𐍆",
+       "delete": "𐍆𐍂𐌰𐌵𐌹𐍃𐍄𐌴𐌹",
+       "deletethispage": "𐍆𐍂𐌰𐌵𐌹𐍃𐍄𐌴𐌹 𐌸𐌰𐌼𐌼𐌰 𐌻𐌰𐌿𐌱𐌰",
+       "undeletethispage": "𐌽𐌰𐍃𐌴𐌹 𐌸𐌰𐌽𐌰 𐌻𐌰𐌿𐍆",
+       "protect": "𐍆𐍂𐌹𐌸",
        "protect_change": "𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹",
-       "protectthispage": "ð\90\8c±ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c² ð\90\8c¸ð\90\8d\89 ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8c°",
-       "unprotect": "ð\90\8c½ð\90\8c¹ð\90\8c±ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c²ð\90\8c°",
-       "unprotectthispage": "Nibaírga þo siedo",
+       "protectthispage": "ð\90\8d\86ð\90\8d\82ð\90\8c¹ð\90\8c¸ ð\90\8c¸ð\90\8c°ð\90\8c½ð\90\8c° ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8d\86",
+       "unprotect": "ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c´ð\90\8c¹ ð\90\8c¼ð\90\8c¿ð\90\8c½ð\90\8c³",
+       "unprotectthispage": "𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹 𐌼𐌿𐌽𐌳 𐌸𐌹𐍃 𐌻𐌰𐌿𐌱𐌹𐍃",
        "newpage": "𐌽𐌹𐌿𐌾𐌹𐍃 𐌻𐌰𐌿𐍆𐍃",
-       "talkpage": "ð\90\8c¼ð\90\8c°ð\90\8c¸ð\90\8c»ð\90\8c°ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8c°",
+       "talkpage": "ð\90\8d\82ð\90\8d\89ð\90\8c³ð\90\8c´ð\90\8c¹ ð\90\8c±ð\90\8c¹ ð\90\8c¸ð\90\8c°ð\90\8c½ð\90\8c° ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8d\86",
        "talkpagelinktext": "𐌲𐌰𐍅𐌰𐌿𐍂𐌳𐌾𐌰",
-       "specialpage": "ð\90\8c¿ð\90\8d\83ð\90\8d\83ð\90\8c¹ð\90\8c½ð\90\8c³ð\90\8c°ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89𐍃",
+       "specialpage": "ð\90\8c¿ð\90\8d\83ð\90\8d\83ð\90\8c¹ð\90\8c½ð\90\8c³ð\90\8d\83 ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8d\86𐍃",
        "personaltools": "𐍃𐍅𐌴𐍃𐌰𐌹 𐍃𐌰𐍂𐍅𐌰𐌽𐍃",
        "talk": "𐌲𐌰𐍅𐌰𐌿𐍂𐌳𐌾𐌰",
        "views": "𐍃𐌹𐌿𐌽𐌴𐌹𐍃",
        "toolbox": "𐍃𐌰𐍂𐍅𐌰𐌽𐍃",
+       "projectpage": "𐌰𐌽𐌳𐌷𐌿𐌻𐌴𐌹 𐍆𐌰𐌿𐍂𐌰𐍅𐌰𐌿𐍂𐍀𐌰𐌻𐌰𐌿𐍆",
+       "viewhelppage": "𐌰𐌽𐌳𐌷𐌿𐌻𐌴𐌹 𐌷𐌹𐌻𐍀𐌰𐌻𐌰𐌿𐍆",
        "otherlanguages": "𐌰𐌽𐌸𐌰𐍂𐌰𐌹𐌼 𐍂𐌰𐌶𐌳𐍉𐌼",
-       "redirectedfrom": "(𐌹𐍃 𐍄𐌹𐌿𐌷𐌰𐌽𐍃/𐍄𐌹𐌿𐌷𐌰𐌽𐌰 𐌷𐌹𐌳𐍂𐌴 𐍆𐍂𐌰𐌼 $1)",
-       "redirectpagesub": "ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8cºð\90\8c¾ð\90\8c°ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89",
+       "redirectedfrom": "(𐌹𐍃 {{GENDER:𐍄𐌹𐌿𐌷𐌰𐌽𐍃|𐍄𐌹𐌿𐌷𐌰𐌽𐌰}} 𐌷𐌹𐌳𐍂𐌴 𐍆𐍂𐌰𐌼 $1)",
+       "redirectpagesub": "ð\90\8c°ð\90\8c»ð\90\8c¾ð\90\8c°ð\90\8d\82 ð\90\8c±ð\90\8d\82ð\90\8c¹ð\90\8c²ð\90\8c²ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8d\83 ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8d\86ð\90\8d\83",
        "lastmodifiedat": "𐍃𐌰 𐌻𐌰𐌿𐍆𐍃 𐌸𐌰𐍄𐌰 𐌰𐍆𐍄𐌿𐌼𐌹𐍃𐍄 𐌹𐌽𐌼𐌰𐌹𐌳𐌹𐌸𐍃 𐍅𐌰𐍃 ($1) ($2).",
+       "protectedpage": "𐍆𐍂𐌹𐌸𐍉𐌽𐍃 𐌻𐌰𐌿𐍆𐍃",
        "jumpto": "𐌲𐌰𐌲𐌲 𐌳𐌿:",
        "jumptonavigation": "𐌻𐌰𐌿𐌱𐌰𐌲𐌰𐍅𐌹𐍃𐍃𐌴𐌹𐍃",
        "jumptosearch": "𐍃𐍉𐌺𐌴𐌹",
+       "pool-errorunknown": "𐌽𐌰𐍃𐌴𐌹 𐌸𐌰𐌽𐌰 𐌻𐌰𐌿𐍆",
        "aboutsite": "𐌱𐌹 {{SITENAME}}",
        "aboutpage": "Project:𐌱𐌹",
-       "copyrightpage": "{{ns:project}}:ð\90\8c¼ð\90\8c°ð\90\8c½ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8cºð\90\8c°ð\90\8d\85ð\90\8c¹ð\90\8d\84ð\90\8d\89ð\90\8c¸ð\90\8c°",
-       "currentevents": "ð\90\8c½ð\90\8c¹ð\90\8c¿ð\90\8c¾ð\90\8c° 𐍅𐌰𐌹𐌷𐍄𐍃",
-       "currentevents-url": "Project:ð\90\8c½ð\90\8c¹ð\90\8c¿ð\90\8c¾ð\90\8c° 𐍅𐌰𐌹𐌷𐍄𐍃",
+       "copyrightpage": "{{ns:project}}:ð\90\8c±ð\90\8d\82ð\90\8c¿ð\90\8cºð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c¹ð\90\8d\82ð\90\8c°ð\90\8c¹ð\90\8c·ð\90\8d\84ð\90\8c´ð\90\8c¹ð\90\8d\83",
+       "currentevents": "ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8d\85ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c¸ð\90\8d\89ð\90\8d\83 ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c¸ð\90\8c°ð\90\8c½ð\90\8d\89ð\90\8d\83 𐍅𐌰𐌹𐌷𐍄𐍃",
+       "currentevents-url": "Project:ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8d\85ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c¸ð\90\8d\89ð\90\8d\83 ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c¸ð\90\8c°ð\90\8c½ð\90\8d\89ð\90\8d\83 𐍅𐌰𐌹𐌷𐍄𐍃",
        "disclaimers": "𐌲𐌰𐍂𐌰𐌹𐌳𐌴𐌹𐌽𐍉𐍃 𐍂𐌰𐌹𐌷𐍄𐌰𐌹𐍃",
        "disclaimerpage": "Project:𐌲𐌰𐌼𐌰𐌹𐌽𐌰 𐌲𐌰𐍂𐌰𐌹𐌳𐌴𐌹𐌽𐍃 𐍂𐌰𐌹𐌷𐍄𐌰𐌹𐍃",
-       "edithelp": "𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐌹𐌷𐌹𐌻𐍀𐌰",
+       "edithelp": "ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c¹ð\90\8c·ð\90\8c¹ð\90\8c»ð\90\8d\80ð\90\8c°",
        "mainpage": "𐌰𐌽𐌰𐍃𐍄𐍉𐌳𐌴𐌹𐌽𐌹𐌻𐌰𐌿𐍆𐍃",
        "mainpage-description": "𐌰𐌽𐌰𐍃𐍄𐍉𐌳𐌴𐌹𐌽𐌹𐌻𐌰𐌿𐍆𐍃",
        "portal": "𐌱𐌰𐌿𐍂𐌲𐍃 𐌲𐌰𐍅𐌹",
        "privacy": "𐌲𐌰𐍂𐌴𐌳𐌴𐌹𐌽𐍉𐍃 𐍃𐌿𐌽𐌳𐍂𐍉𐍅𐌹𐍃𐌰𐌽𐌰",
        "privacypage": "Project:𐌲𐌰𐍂𐌰𐌹𐌳𐌴𐌹𐌽𐍉𐍃 𐍃𐌿𐌽𐌳𐍂𐍉𐍅𐌹𐍃𐌰𐌽𐌰",
        "retrievedfrom": "𐌲𐌰𐌽𐌿𐌼𐌰𐌽 𐍆𐍂𐌰𐌼 \"$1\"",
-       "youhavenewmessages": "𐌸𐌿 𐌷𐌰𐌱𐌹𐍃 $1 ($2).",
+       "youhavenewmessages": "{{PLURAL:$3|𐌷𐌰𐌱𐌰𐌹𐍃}} $1 ($2).",
        "editsection": "𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹",
-       "editold": "ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c¾ð\90\8c°ð\90\8c½",
+       "editold": "ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c´ð\90\8c¹",
        "editlink": "𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹",
        "viewsourcelink": "𐍃𐌰𐌹𐍈 𐌱𐍂𐌿𐌽𐌽𐌰𐌽",
        "editsectionhint": "𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹 𐌳𐌰𐌹𐌻: $1",
        "toc": "𐌹𐌽𐌽𐌰𐌽𐌰",
-       "showtoc": "ð\90\8c°ð\90\8c¿ð\90\8c²ð\90\8c¾ð\90\8c°",
-       "hidetoc": "ð\90\8d\86ð\90\8c¹ð\90\8c»ð\90\8c·ð\90\8c°ð\90\8c½",
+       "showtoc": "ð\90\8c°ð\90\8d\84ð\90\8c°ð\90\8c¿ð\90\8c²ð\90\8c´ð\90\8c¹",
+       "hidetoc": "ð\90\8c°ð\90\8d\86ð\90\8d\86ð\90\8c¹ð\90\8c»ð\90\8c·",
        "confirmable-yes": "𐌾𐌰",
        "confirmable-no": "𐌽𐌴",
-       "site-rss-feed": "$1 RSS Miþnatifodjan",
-       "site-atom-feed": "$1 ð\90\8c°ð\90\8d\84ð\90\8d\89ð\90\8c¼ ð\90\8d\86ð\90\8d\89ð\90\8c³ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8d\83",
+       "site-rss-feed": "$1 RSS 𐍂𐌹𐌽𐌽𐍉",
+       "site-atom-feed": "$1 ð\90\8c°ð\90\8d\84ð\90\8d\89ð\90\8c¼ ð\90\8d\82ð\90\8c¹ð\90\8c½ð\90\8c½ð\90\8d\89",
        "page-atom-feed": "\"$1\" 𐌰𐍄𐍉𐌼 𐍂𐌹𐌽𐌽𐍉",
        "red-link-title": "$1 (𐌻𐌰𐌿𐍆𐍃 𐌽𐌹𐍃𐍄)",
        "nstab-main": "𐌻𐌰𐌿𐍆𐍃",
        "nstab-user": "𐌱𐍂𐌿𐌺𐌾𐌰𐌻𐌰𐌿𐍆𐍃",
        "nstab-special": "𐌿𐍃𐍃𐌹𐌽𐌳𐍃 𐌻𐌰𐌿𐍆𐍃",
        "nstab-project": "𐍆𐌰𐌿𐍂𐌰𐍅𐌰𐌿𐍂𐍀𐌰𐌻𐌰𐌿𐍆𐍃",
-       "nstab-image": "ð\90\8d\86ð\90\8c´ð\90\8c¹ð\90\8c»ð\90\8c°",
-       "nstab-template": "ð\90\8d\86ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c°ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8c½𐍃",
-       "nstab-help": "𐌷𐌹𐌻𐍀𐌰",
+       "nstab-image": "ð\90\8d\86ð\90\8c°ð\90\8c´ð\90\8c¹ð\90\8c»",
+       "nstab-template": "ð\90\8d\83ð\90\8cºð\90\8c´ð\90\8c¹ð\90\8d\82ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c¹ð\90\8d\86ð\90\8d\82ð\90\8c¹ð\90\8d\83ð\90\8c°ð\90\8c·ð\90\8d\84𐍃",
+       "nstab-help": "𐌷𐌹𐌻𐍀𐌰𐌻𐌰𐌿𐍆𐍃",
        "nstab-category": "𐌺𐌿𐌽𐌹",
        "mainpage-nstab": "𐌰𐌽𐌰𐍃𐍄𐍉𐌳𐌴𐌹𐌽𐌹𐌻𐌰𐌿𐍆𐍃",
        "error": "𐌰𐌹𐍂𐌶𐌴𐌹",
        "databaseerror-error": "𐌰𐌹𐍂𐌶𐌴𐌹: $1",
-       "missing-article": "ð\90\8d\83ð\90\8c° ð\90\8c³ð\90\8c°ð\90\8d\84ð\90\8c°ð\90\8c±ð\90\8c¿ð\90\8d\83 ð\90\8c½ð\90\8c¹ ð\90\8c²ð\90\8c°ð\90\8c½ð\90\8c°ð\90\8c¼ ð\90\8c¸ð\90\8c°ð\90\8c½ð\90\8c° ð\90\8c±ð\90\8d\89ð\90\8cºð\90\8c°ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c³ð\90\8c°ð\90\8c½ ð\90\8c´ð\90\8c¹ ð\90\8c¹ð\90\8d\84ð\90\8c° ð\90\8d\83ð\90\8cºð\90\8c°ð\90\8c» ð\90\8c±ð\90\8c¹ð\90\8c²ð\90\8c¹ð\90\8d\84ð\90\8c°ð\90\8c½: \"$1\" $2\n\n(The data base did not find the text of a page that it should have found, named \"$1\" $2.\n\nThis is usually caused by following an outdated diff or history link to a page that has been deleted.\n\nIf this is not the case, you may have found a bug in the software.\nPlease report this to an [[Special:ListUsers/sysop|administrator]], making note of the URL.)",
+       "missing-article": "ð\90\8c³ð\90\8c°ð\90\8d\84ð\90\8c°ð\90\8c±ð\90\8c´ð\90\8d\83 ð\90\8c½ð\90\8c¹ ð\90\8c±ð\90\8c¹ð\90\8c²ð\90\8c°ð\90\8d\84 ð\90\8c±ð\90\8d\89ð\90\8cºð\90\8d\89ð\90\8d\83 ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8c±ð\90\8c¹ð\90\8d\83 ð\90\8c¸ð\90\8c¹ð\90\8c¶ð\90\8c´ð\90\8c¹ ð\90\8d\83ð\90\8cºð\90\8c¿ð\90\8c»ð\90\8c³ð\90\8c´ð\90\8c³ð\90\8c¹ ð\90\8c±ð\90\8c¹ð\90\8c²ð\90\8c¹ð\90\8d\84ð\90\8c°ð\90\8c½, ð\90\8c·ð\90\8c°ð\90\8c¹ð\90\8d\84ð\90\8c°ð\90\8c½ð\90\8d\83 \"$1\" $2. \n\nð\90\8c¸ð\90\8c°ð\90\8d\84ð\90\8c° ð\90\8c¿ð\90\8d\86ð\90\8d\84ð\90\8c° ð\90\8d\85ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c¸ð\90\8c¹ð\90\8c¸ ð\90\8c¾ð\90\8c°ð\90\8c±ð\90\8c°ð\90\8c¹ ð\90\8c»ð\90\8c°ð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8c¾ð\90\8c°ð\90\8c³ð\90\8c° ð\90\8d\86ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c½ð\90\8c¾ð\90\8c° ð\90\8c³ð\90\8c¹ð\90\8d\86ð\90\8d\86 ð\90\8c¸ð\90\8c°ð\90\8c¿ ð\90\8d\83ð\90\8d\80ð\90\8c¹ð\90\8c»ð\90\8c»ð\90\8c°ð\90\8c²ð\90\8c°ð\90\8d\85ð\90\8c¹ð\90\8d\83ð\90\8d\83 ð\90\8d\83ð\90\8c´ð\90\8c¹ ð\90\8d\86ð\90\8d\82ð\90\8c°ð\90\8cµð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8c¹ð\90\8c³ð\90\8c° ð\90\8c¹ð\90\8d\83ð\90\8d\84. ð\90\8c½ð\90\8c¹ð\90\8c±ð\90\8c°ð\90\8c¹ ð\90\8c¹ð\90\8d\83ð\90\8d\84, ð\90\8c¼ð\90\8c°ð\90\8c·ð\90\8d\84ð\90\8d\83 ð\90\8c¹ð\90\8d\83ð\90\8d\84 ð\90\8c´ð\90\8c¹ ð\90\8c±ð\90\8c¹ð\90\8c²ð\90\8c´ð\90\8d\84ð\90\8c´ð\90\8c¹ð\90\8d\83 ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c¶ð\90\8c´ð\90\8c¹ð\90\8c½ ð\90\8c¹ð\90\8c½ ð\90\8d\83ð\90\8c°ð\90\8c¿ð\90\8d\86ð\90\8d\84ð\90\8d\85ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c°. \n\nð\90\8c±ð\90\8c¹ð\90\8c³ð\90\8c¾ð\90\8c°ð\90\8c¼ ð\90\8c¸ð\90\8c¿ð\90\8cº, ð\90\8c¼ð\90\8c´ð\90\8d\82ð\90\8c´ð\90\8c¹ ð\90\8c¸ð\90\8c°ð\90\8d\84ð\90\8c° ð\90\8c³ð\90\8c¿ [[Special:ListUsers/sysop\n|ð\90\8d\82ð\90\8c´ð\90\8c¹ð\90\8cº]] ð\90\8c²ð\90\8c¹ð\90\8d\86ð\90\8c¿ð\90\8c· ð\90\8c²ð\90\8c°ð\90\8d\85ð\90\8c¹ð\90\8d\83ð\90\8d\83.",
        "badtitle": "𐌿𐌽𐍂𐌰𐌹𐌷𐍄𐌰𐍄𐌰 𐌿𐍆𐌰𐍂𐌼𐌴𐌻𐌹",
        "badtitletext": "𐍆𐍂𐌰𐌹𐌷𐌰𐌽𐍃 𐌻𐌰𐌿𐍆𐍃 𐍅𐌰𐍃 𐌿𐌽𐌲𐌰𐌼𐌰𐌲𐌰𐌽𐌳𐍃, 𐌻𐌰𐌿𐍃, 𐌰𐌹𐌸𐌸𐌰𐌿 𐌿𐌽𐍂𐌰𐌹𐌷𐍄𐌰𐌱𐌰 𐌲𐌰𐍅𐌹𐌳𐌰𐌽𐍃 𐌼𐌹𐌸𐍂𐌰𐌶𐌳𐌰 𐌸𐌰𐌿 𐌼𐌹𐌸-𐍅𐌹𐌺𐌹 𐌿𐍆𐌰𐍂𐌼𐌴𐌻𐌹. 𐌼𐌰𐌲𐌹 𐌷𐌰𐌱𐌰𐌽 𐌰𐌹𐌽𐌰 𐌸𐌰𐌿 𐌼𐌰𐌽𐌰𐌲𐌹𐌶𐍉𐍃 𐌱𐍉𐌺𐍉𐍃 𐌱𐍂𐌿𐌺𐌹𐌳𐍉𐍃 𐌹𐌽 𐌿𐍆𐌰𐍂𐌼𐌴𐌻𐌾𐌰𐌼.",
-       "viewsource": "𐍃𐌰𐌹𐍈𐌹𐍃 𐌱𐍂𐌿𐌽𐌽𐌰𐌽",
-       "yourname": "ð\90\8c½ð\90\8c¹ð\90\8c¿ð\90\8d\84ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c¹ð\90\8d\83𐌽𐌰𐌼𐍉:",
+       "viewsource": "𐍃𐌰𐌹𐍈 𐌱𐍂𐌿𐌽𐌽𐌰𐌽",
+       "yourname": "ð\90\8c°ð\90\8d\84ð\90\8c²ð\90\8c°ð\90\8c²ð\90\8c²ð\90\8c°𐌽𐌰𐌼𐍉:",
        "userlogin-yourname": "𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽𐌰𐌼𐍉",
-       "userlogin-yourname-ph": "ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c°ð\90\8c¹ð\90\8d\83 𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽𐌰𐌼𐍉 𐌸𐌴𐌹𐌽",
-       "createacct-another-username-ph": "ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c°ð\90\8c¹ð\90\8d\83 𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽𐌰𐌼𐍉",
-       "yourpassword": "ð\90\8c°ð\90\8c½ð\90\8c°ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8c²ð\90\8c½ð\90\8d\83 ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c³ð\90\8c°:",
+       "userlogin-yourname-ph": "ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c´ð\90\8c¹ 𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽𐌰𐌼𐍉 𐌸𐌴𐌹𐌽",
+       "createacct-another-username-ph": "ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c´ð\90\8c¹ 𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽𐌰𐌼𐍉",
+       "yourpassword": "ð\90\8c²ð\90\8c°ð\90\8c¼ð\90\8d\89ð\90\8d\84ð\90\8c°ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c³:",
        "userlogin-yourpassword": "𐌲𐌰𐌼𐍉𐍄𐌰𐍅𐌰𐌿𐍂𐌳",
-       "userlogin-yourpassword-ph": "ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c°ð\90\8c¹ð\90\8d\83 𐌲𐌰𐌼𐍉𐍄𐌰𐍅𐌰𐌿𐍂𐌳 𐌸𐌴𐌹𐌽",
-       "createacct-yourpassword-ph": "ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c°ð\90\8c¹ð\90\8d\83 𐌲𐌰𐌼𐍉𐍄𐌰𐍅𐌰𐌿𐍂𐌳",
+       "userlogin-yourpassword-ph": "ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c´ð\90\8c¹ 𐌲𐌰𐌼𐍉𐍄𐌰𐍅𐌰𐌿𐍂𐌳 𐌸𐌴𐌹𐌽",
+       "createacct-yourpassword-ph": "ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c´ð\90\8c¹ 𐌲𐌰𐌼𐍉𐍄𐌰𐍅𐌰𐌿𐍂𐌳",
        "createacct-yourpasswordagain": "𐌲𐌰𐍃𐌹𐌲𐌻𐌴𐌹 𐌲𐌰𐌼𐍉𐍄𐌰𐍅𐌰𐌿𐍂𐌳",
        "createacct-yourpasswordagain-ph": "𐌼𐌴𐌻𐌴𐌹 𐌲𐌰𐌼𐍉𐍄𐌰𐍅𐌰𐌿𐍂𐌳 𐌰𐍆𐍄𐍂𐌰",
-       "userlogin-remembermypassword": "𐌲𐌰𐍆𐌰𐍃𐍄 𐌼𐌹𐌺 𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽𐌰𐌽𐌰/𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽𐌰",
-       "login": "Atgaggan",
-       "nav-login-createaccount": "𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽 / 𐌲𐌰𐌻𐌰𐌽𐌲𐌾𐌰𐌽 𐌽𐌹𐌿𐍄𐌰𐌽𐌳𐌹𐍃",
-       "userlogin": "Atgaggan / gaskapjan niutandis",
-       "userloginnocreate": "𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽",
-       "logout": "ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8c¸ð\90\8c°ð\90\8c½",
-       "userlogout": "ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8c¸ð\90\8c°ð\90\8c½",
+       "userlogin-remembermypassword": "𐌲𐌰𐍆𐌰𐍃𐍄 𐌼𐌹𐌺 {{GENDER:𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽𐌰𐌽𐌰|𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽𐌰}}",
+       "login": "𐌰𐍄𐌲𐌰𐌲𐌲",
+       "nav-login-createaccount": "𐌰𐍄𐌲𐌰𐌲𐌲 / 𐍃𐌺𐌰𐍀𐌴𐌹 𐌺𐌰𐍅𐍄𐍃𐌾𐍉𐌽",
+       "userlogin": "𐌰𐍄𐌲𐌰𐌲𐌲 / 𐍃𐌺𐌰𐍀𐌴𐌹 𐌺𐌰𐍅𐍄𐍃𐌾𐍉𐌽",
+       "userloginnocreate": "𐌰𐍄𐌲𐌰𐌲𐌲",
+       "logout": "ð\90\8c°ð\90\8d\86ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8c¸",
+       "userlogout": "ð\90\8c°ð\90\8d\86ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8c¸",
        "userlogin-noaccount": "𐌽𐌹 𐌷𐌰𐌱𐌰𐌹𐍃 𐌺𐌰𐍅𐍄𐍃𐌾𐍉𐌽?",
-       "userlogin-joinproject": "ð\90\8c²ð\90\8c°ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c½ð\90\8c´ð\90\8c¹ {{SITENAME}}",
-       "nologinlink": "Gaskapjan þein niutandis",
-       "createaccount": "ð\90\8c²ð\90\8c°ð\90\8c»ð\90\8c°ð\90\8c²ð\90\8c¾ð\90\8c°ð\90\8c½ ð\90\8c½ð\90\8c¹ð\90\8c¿ð\90\8d\84ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c¹ð\90\8d\83",
-       "gotaccount": "Habiþ þu niutandis? '''$1'''",
-       "gotaccountlink": "Atgaggan",
+       "userlogin-joinproject": "ð\90\8c²ð\90\8c°ð\90\8c³ð\90\8c°ð\90\8c¹ð\90\8c»ð\90\8c´ð\90\8c¹ ð\90\8c¹ð\90\8c½ ð\90\8c½ð\90\8c°ð\90\8d\84ð\90\8c¾ð\90\8c°ð\90\8d\83ð\90\8d\84ð\90\8c°ð\90\8c³ð\90\8c° {{SITENAME}}",
+       "nologinlink": "𐍃𐌺𐌰𐍀𐌴𐌹 𐌺𐌰𐍅𐍄𐍃𐌾𐍉𐌽",
+       "createaccount": "ð\90\8d\83ð\90\8cºð\90\8c°ð\90\8d\80ð\90\8c´ð\90\8c¹ ð\90\8cºð\90\8c°ð\90\8d\85ð\90\8d\84ð\90\8d\83ð\90\8c¾ð\90\8d\89ð\90\8c½",
+       "gotaccount": "𐌾𐌿 𐌺𐌰𐍅𐍄𐍃𐌾𐍉𐌽 𐌷𐌰𐌱𐌰𐌹𐍃? $1.",
+       "gotaccountlink": "𐌰𐍄𐌲𐌰𐌲𐌲",
        "userlogin-resetpassword-link": "𐌿𐍆𐌰𐍂𐌼𐌿𐌽𐌽𐍉𐌳𐌴𐍃 𐌸𐌴𐌹𐌽𐌰𐌼𐌼𐌰 𐌲𐌰𐌼𐍉𐍄𐌰𐍅𐌰𐌿𐍂𐌳𐌰?",
        "userlogin-helplink2": "𐌷𐌹𐌻𐍀𐌰 𐌼𐌹𐌸 𐌰𐍄𐌲𐌰𐌲𐌲𐌰",
        "createacct-emailoptional": "𐌴-𐌱𐍉𐌺𐍉𐍃 (𐌼𐌰𐌷𐍄𐌴𐌹𐌲𐍉𐍃)",
        "createacct-email-ph": "𐌼𐌴𐌻𐌴𐌹 𐌸𐌴𐌹𐌽𐍉𐍃 𐌴-𐌱𐍉𐌺𐍉𐍃",
-       "createaccountreason": "ð\90\8d\86ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c¹ð\90\8c½ð\90\8c°:",
-       "createacct-reason": "ð\90\8d\86ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c¹ð\90\8c½ð\90\8c°",
+       "createaccountreason": "ð\90\8c²ð\90\8d\82ð\90\8c¿ð\90\8c½ð\90\8c³ð\90\8c¿ð\90\8d\83:",
+       "createacct-reason": "ð\90\8c²ð\90\8d\82ð\90\8c¿ð\90\8c½ð\90\8c³ð\90\8c¿ð\90\8d\83",
        "createacct-submit": "𐍃𐌺𐌰𐍀𐌴𐌹 𐌸𐌴𐌹𐌽𐌰 𐌺𐌰𐍅𐍄𐍃𐌾𐍉𐌽",
        "createacct-benefit-heading": "{{SITENAME}} 𐍄𐌰𐍅𐌹𐌸 𐌹𐍃𐍄 𐍆𐍂𐌰𐌼 𐌼𐌰𐌽𐌽𐌰𐌼 𐍃𐍅𐌴 𐌸𐌿𐌺.",
        "createacct-benefit-body1": "{{PLURAL:$1|𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍃|𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍉𐍃}}",
        "createacct-benefit-body2": "{{PLURAL:$1|𐌻𐌰𐌿𐍆𐍃|𐌻𐌰𐌿𐌱𐍉𐍃}}",
-       "loginlanguagelabel": "Razda: $1",
+       "loginlanguagelabel": "𐍂𐌰𐌶𐌳𐌰: $1",
        "pt-login": "𐌰𐍄𐌲𐌰𐌲𐌲",
        "pt-login-button": "𐌰𐍄𐌲𐌰𐌲𐌲",
        "pt-createaccount": "𐍃𐌺𐌰𐍀𐌴𐌹 𐌺𐌰𐍅𐍄𐍃𐌾𐍉𐌽",
        "passwordreset": "𐌰𐍆𐍄𐍂𐌰 𐍃𐌰𐍄𐌴𐌹 𐌲𐌰𐌼𐍉𐍄𐌰𐍅𐌰𐌿𐍂𐌳",
-       "bold_sample": "ð\90\8c°ð\90\8c±ð\90\8d\82ð\90\8d\83 ð\90\8c±ð\90\8d\89ð\90\8cºð\90\8c°",
-       "bold_tip": "ð\90\8c°ð\90\8c±ð\90\8d\82 ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c³ð\90\8c°",
-       "italic_sample": "ð\90\8d\85ð\90\8d\82ð\90\8c°ð\90\8c¹ð\90\8cµð\90\8d\83 ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c³ð\90\8c°",
-       "italic_tip": "ð\90\8c³ð\90\8d\82ð\90\8c¹ð\90\8c¿ð\90\8d\83ð\90\8d\89 ð\90\8c±ð\90\8d\89ð\90\8cºð\90\8c°",
-       "link_sample": "ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8cºð\90\8c¾ð\90\8c°ð\90\8c½ð\90\8c±ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c¹ ð\90\8c½ð\90\8c°ð\90\8c¼ð\90\8d\89",
-       "link_tip": "ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8cºð\90\8c¾ð\90\8c°ð\90\8c½ð\90\8c±ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c¹ ð\90\8c¹ð\90\8c½ð\90\8c½ð\90\8c°ð\90\8c½ð\90\8c°",
-       "extlink_sample": "http://www.example.com ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8cºð\90\8c¾ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c¹ ð\90\8c½ð\90\8c°ð\90\8c¼ð\90\8d\89",
-       "extlink_tip": "Uta táikjabandi (maúdjan http://)",
-       "headline_sample": "ð\90\8c·ð\90\8c°ð\90\8c¿ð\90\8c±ð\90\8c¹ð\90\8c³ð\90\8c°ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c³ð\90\8c°",
-       "headline_tip": "ð\90\8c·ð\90\8c°ð\90\8c¿ð\90\8c·ð\90\8d\83 ð\90\8c·ð\90\8c°ð\90\8c¿ð\90\8c±ð\90\8c¹ð\90\8c³ð\90\8c°ð\90\8c±ð\90\8d\89ð\90\8cºð\90\8c° â\80¢ð\90\8c±â\80¢",
-       "nowiki_sample": "ð\90\8d\83ð\90\8c°ð\90\8d\84ð\90\8c¾ð\90\8c´ð\90\8c¹ ð\90\8c¿ð\90\8c½ð\90\8d\83ð\90\8c½ð\90\8c´ð\90\8c¹ð\90\8c¸ð\90\8c¾ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8d\83 ð\90\8d\84ð\90\8c´ð\90\8cºð\90\8d\83ð\90\8d\84 ð\90\8c±ð\90\8d\89ð\90\8cºð\90\8c°ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c³ð\90\8c°ð\90\8c¼ ð\90\8c·ð\90\8c¹ð\90\8d\84",
-       "nowiki_tip": "ð\90\8c¿ð\90\8c½ð\90\8d\85ð\90\8c¹ð\90\8d\84ð\90\8c¾ð\90\8c¹ð\90\8d\83 ð\90\8d\85ð\90\8c¹ð\90\8cºð\90\8c¹ð\90\8d\83ð\90\8c½ð\90\8c´ð\90\8c¹ð\90\8c¸ð\90\8c¾ð\90\8c°ð\90\8c½ð\90\8c³𐍃",
-       "image_tip": "𐌹𐌽𐌽𐌱𐍉𐌳𐌰𐌽𐍃 𐍆𐌴𐌹𐌻𐌰",
-       "media_tip": "ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8cºð\90\8c¾ð\90\8c°ð\90\8c±ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c¾ð\90\8c¹ð\90\8d\83 ð\90\8d\86ð\90\8c´ð\90\8c¹ð\90\8c»ð\90\8c°ð\90\8c½ð\90\8c¹ð\90\8d\83",
-       "sig_tip": "ð\90\8c¿ð\90\8c½ð\90\8c³ð\90\8c°ð\90\8d\82ð\90\8c¼ð\90\8c´ð\90\8c» ð\90\8c¸ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c° ð\90\8c¼ð\90\8c¹ð\90\8c¸ ð\90\8d\83ð\90\8d\84ð\90\8c¿ð\90\8c½ð\90\8c³ð\90\8c°𐌼𐌴𐌻𐌰",
-       "hr_tip": "𐍂𐌰𐌹𐌷𐍄𐍃𐌱𐌰𐌿𐍂𐌳 (𐌱𐍂𐌿𐌺𐌾𐌰𐌽 𐌼𐌹𐌸 𐌽𐌹𐌿𐍆𐌰𐍂𐌿𐍃𐍃𐌿𐍃)",
-       "summary": "ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c¾ð\90\8c°ð\90\8c½ð\90\8d\83ð\90\8d\80ð\90\8c¹ð\90\8c»ð\90\8c»ð\90\8d\89ð\90\8c½:",
-       "subject": "ð\90\8c·ð\90\8c°ð\90\8c¿ð\90\8c±ð\90\8c¹ð\90\8c³ð\90\8c°ð\90\8c±ð\90\8d\89ð\90\8cº𐌰:",
-       "minoredit": "ð\90\8d\83ð\90\8c° ð\90\8c¹ð\90\8d\83ð\90\8d\84 ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8d\84ð\90\8c¹ð\90\8c»ð\90\8c° 𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍃",
+       "bold_sample": "ð\90\8d\83ð\90\8d\85ð\90\8c¹ð\90\8c½ð\90\8c¸ð\90\8d\89ð\90\8d\83 ð\90\8c±ð\90\8d\89ð\90\8cºð\90\8d\89ð\90\8d\83",
+       "bold_tip": "ð\90\8d\83ð\90\8d\85ð\90\8c¹ð\90\8c½ð\90\8c¸ð\90\8d\89ð\90\8d\83 ð\90\8c±ð\90\8d\89ð\90\8cºð\90\8d\89ð\90\8d\83",
+       "italic_sample": "ð\90\8d\85ð\90\8d\82ð\90\8c°ð\90\8c¹ð\90\8cµð\90\8d\89ð\90\8d\83 ð\90\8c±ð\90\8d\89ð\90\8cºð\90\8d\89ð\90\8d\83",
+       "italic_tip": "ð\90\8d\85ð\90\8d\82ð\90\8c°ð\90\8c¹ð\90\8cµð\90\8d\89ð\90\8d\83 ð\90\8c±ð\90\8d\89ð\90\8cºð\90\8d\89ð\90\8d\83",
+       "link_sample": "ð\90\8c²ð\90\8c°ð\90\8d\85ð\90\8c¹ð\90\8d\83ð\90\8d\83ð\90\8c¹-ð\90\8c¿ð\90\8d\86ð\90\8c°ð\90\8d\82ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c¹",
+       "link_tip": "ð\90\8c¹ð\90\8c½ð\90\8c½ð\90\8c°ð\90\8c½ð\90\8c° ð\90\8c²ð\90\8c°ð\90\8d\85ð\90\8c¹ð\90\8d\83ð\90\8d\83",
+       "extlink_sample": "http://www.example.com ð\90\8c²ð\90\8c°ð\90\8d\85ð\90\8c¹ð\90\8d\83ð\90\8d\83ð\90\8c¹-ð\90\8c¿ð\90\8d\86ð\90\8c°ð\90\8d\82ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c¹",
+       "extlink_tip": "𐌿𐍄𐌰𐌲𐌰𐍅𐌹𐍃𐍃 (𐌲𐌰𐌼𐌹𐌽𐌸𐌴𐌹 http:// 𐍆𐌰𐌿𐍂𐌰𐌻𐌰𐌲𐌴𐌹𐌽𐍃)",
+       "headline_sample": "ð\90\8c¿ð\90\8d\86ð\90\8c°ð\90\8d\82ð\90\8d\83ð\90\8d\84ð\90\8d\82ð\90\8c¹ð\90\8cºð\90\8c°ð\90\8c±ð\90\8d\89ð\90\8cºð\90\8d\89ð\90\8d\83",
+       "headline_tip": "ð\90\8c·ð\90\8c°ð\90\8c¿ð\90\8c·ð\90\8c¹ð\90\8c¸ð\90\8c° â\80¢ð\90\8c±â\80¢ ð\90\8c¿ð\90\8d\86ð\90\8c°ð\90\8d\82ð\90\8d\83ð\90\8d\84ð\90\8d\82ð\90\8c¹ð\90\8cºð\90\8d\83",
+       "nowiki_sample": "ð\90\8d\83ð\90\8c°ð\90\8d\84ð\90\8c´ð\90\8c¹ ð\90\8c±ð\90\8d\89ð\90\8cºð\90\8d\89ð\90\8d\83 ð\90\8c¹ð\90\8c½ð\90\8c¿ð\90\8c· ð\90\8c²ð\90\8c°ð\90\8d\82ð\90\8d\85ð\90\8c¹ ð\90\8c·ð\90\8c´ð\90\8d\82",
+       "nowiki_tip": "ð\90\8c½ð\90\8c¹ ð\90\8c±ð\90\8d\82ð\90\8c¿ð\90\8cºð\90\8c´ð\90\8c¹ ð\90\8d\85ð\90\8c¹ð\90\8cºð\90\8c¹-ð\90\8d\86ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c¼ð\90\8c°ð\90\8d\84ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c°ð\90\8c¹𐍃",
+       "image_tip": "\n𐌹𐌽𐌱𐌰𐌳𐌹𐌸 𐍆𐌰𐌴𐌹𐌻",
+       "media_tip": "ð\90\8c²ð\90\8c°ð\90\8d\85ð\90\8c¹ð\90\8d\83ð\90\8d\83 ð\90\8c³ð\90\8c¿ ð\90\8d\86ð\90\8c°ð\90\8c´ð\90\8c¹ð\90\8c»ð\90\8c°",
+       "sig_tip": "ð\90\8c¸ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c° ð\90\8c¿ð\90\8d\86ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8d\83 ð\90\8c¼ð\90\8c¹ð\90\8c¸ ð\90\8c²ð\90\8c»ð\90\8c°ð\90\8c²ð\90\8c²ð\90\8d\85ð\90\8c°ð\90\8c¼ð\90\8c¼ð\90\8c° 𐌼𐌴𐌻𐌰",
+       "hr_tip": "𐍂𐌰𐌹𐌷𐍄𐍃 𐍃𐍄𐍂𐌹𐌺𐍃 (𐌽𐌹 𐌱𐍂𐌿𐌺𐌴𐌹 𐌿𐍆𐌰𐍂𐍆𐌹𐌻𐌿)",
+       "summary": "ð\90\8c¼ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c²ð\90\8c¿ð\90\8d\83 ð\90\8d\83ð\90\8cºð\90\8c´ð\90\8c¹ð\90\8d\82ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8d\83:",
+       "subject": "ð\90\8c¿ð\90\8d\86ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8d\80𐌰:",
+       "minoredit": "ð\90\8c¸ð\90\8c°ð\90\8d\84ð\90\8c° ð\90\8c¹ð\90\8d\83ð\90\8d\84 ð\90\8c¼ð\90\8c¹ð\90\8c½ð\90\8c½ð\90\8c¹ð\90\8c¶ð\90\8c´ð\90\8c¹ ð\90\8c¹ð\90\8c½𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍃",
        "watchthis": "𐌰𐍄𐍅𐌹𐍄 𐌻𐌰𐌿𐌱𐌰",
        "savearticle": "𐌲𐌰𐍆𐌰𐍃𐍄 𐌻𐌰𐌿𐍆",
-       "preview": "ð\90\8d\86ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8d\83ð\90\8c°ð\90\8c¹ð\90\8d\88ð\90\8c° ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89",
-       "showpreview": "ð\90\8d\85ð\90\8c¹ð\90\8d\84ð\90\8c°ð\90\8c½ ð\90\8d\86ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8d\83ð\90\8c°ð\90\8c¹ð\90\8d\88ð\90\8c°",
+       "preview": "ð\90\8d\86ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c°ð\90\8d\83ð\90\8c¹ð\90\8c¿ð\90\8c½ð\90\8d\83",
+       "showpreview": "ð\90\8c°ð\90\8d\84ð\90\8c°ð\90\8c¿ð\90\8c²ð\90\8c´ð\90\8c¹ ð\90\8d\86ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c°ð\90\8d\83ð\90\8c¹ð\90\8c¿ð\90\8c½",
        "showdiff": "𐌰𐍄𐌰𐌿𐌲𐌴𐌹 𐌹𐌽𐌼𐌰𐌹𐌳𐌹𐌽𐌹𐌽𐍃",
        "loginreqlink": "𐌰𐍄𐌲𐌰𐌲𐌲",
-       "newarticle": "(Niu)",
+       "newarticle": "(𐌽𐌹𐌿𐌾𐌰𐍄𐌰)",
        "newarticletext": "𐌻𐌰𐌹𐍃𐍄𐌹𐌳𐌴𐍃 𐌲𐌰𐍅𐌹𐍃 𐌳𐌿 𐌻𐌰𐌿𐌱𐌰 𐍃𐌰𐌴𐌹 𐌽𐌹𐍃𐍄. 𐌳𐌿 𐍃𐌺𐌰𐍀𐌾𐌰𐌽 𐌸𐌰𐌽𐌰 𐌻𐌰𐌿𐍆, 𐌰𐌽 𐌰𐍃𐍄𐍉𐌳𐌴𐌹 𐌼𐌴𐌻𐌾𐌰𐌽 𐌹𐌽 𐌰𐍂𐌺𐌰𐌹 𐌿𐍆 (𐍃𐌰𐌹𐍈 [$1 𐌷𐌹𐌻𐍀𐌰𐌻𐌰𐌿𐍆] 𐌼𐌰𐌽𐌰𐌲𐌹𐌶𐌹𐌽 𐌺𐌿𐌽𐌸𐌾𐌰). 𐌾𐌰𐌱𐌰𐌹 𐌹𐍃 𐌷𐌴𐍂 𐌹𐌽 𐌰𐌹𐍂𐌶𐌴𐌹𐌽𐍃, 𐌲𐌰𐌲𐌲 𐌳𐌿 <𐍃𐍄𐍂𐍉𐌽𐌲>𐌹𐌱𐌿𐌺𐌰𐌷𐌰𐌿𐌱𐌹𐌳𐌹𐌻𐍉𐌽.",
        "noarticletext": "𐌽𐌿 𐌽𐌹 𐍃𐌹𐌽𐌳 𐌱𐍉𐌺𐍉𐍃 𐌹𐌽 𐌸𐌰𐌼𐌼𐌰 𐌻𐌰𐌿𐌱𐌰.\n𐌼𐌰𐌲𐍄 [[Special:Search/{{PAGENAME}}|𐍃𐍉𐌺𐌾𐌰𐌽 𐌸𐌰𐍄𐌰 𐌻𐌰𐌿𐌱𐌰-𐌿𐍆𐌰𐍂𐌼𐌴𐌻𐌹]] 𐌹𐌽 𐌰𐌽𐌸𐌰𐍂𐌰𐌹𐌼 𐌻𐌰𐌿𐌱𐌰𐌼,  <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 𐍃𐍉𐌺𐌾𐌰𐌽 𐌲𐌰𐌷𐌰𐌷𐌾𐍉 𐌲𐌰𐍆𐌰𐍃𐍄𐍉𐍃], 𐌰𐌹𐌸𐌸𐌰𐌿 [{{fullurl:{{FULLPAGENAME}}|action=edit}} 𐍃𐌺𐌰𐍀𐌾𐌰𐌽 𐌸𐌰𐌽𐌰 𐌻𐌰𐌿𐍆.]</ span>",
        "noarticletext-nopermission": "𐌽𐌿 𐌽𐌹 𐍃𐌹𐌽𐌳 𐌱𐍉𐌺𐍉𐍃 𐌹𐌽 𐌸𐌰𐌼𐌼𐌰 𐌻𐌰𐌿𐌱𐌰.\n𐌼𐌰𐌲𐍄 [[Special:Search/{{PAGENAME}}|𐍃𐍉𐌺𐌾𐌰𐌽 𐌸𐌰𐍄𐌰 𐌻𐌰𐌿𐌱𐌰-𐌿𐍆𐌰𐍂𐌼𐌴𐌻𐌹]] 𐌹𐌽 𐌰𐌽𐌸𐌰𐍂𐌰𐌹𐌼 𐌻𐌰𐌿𐌱𐌰𐌼, 𐌸𐌰𐌿 <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 𐍃𐍉𐌺𐌾𐌰𐌽 𐌲𐌰𐌷𐌰𐌷𐌾𐍉 𐌲𐌰𐍆𐌰𐍃𐍄𐍉𐍃]</span>, 𐌹𐌸 𐌽𐌹 𐌷𐌰𐌱𐌰𐌹𐍃 𐌰𐌽𐌳𐌻𐌴𐍄 𐍃𐌺𐌰𐍀𐌾𐌰𐌽 𐌸𐌰𐌽𐌰 𐌻𐌰𐌿𐍆.",
-       "updated": "(Nuwisan)",
-       "previewnote": "'''𐍃𐌰𐌷 𐌹𐍃𐍄 𐍆𐌰𐌿𐍂𐍃𐌰𐌹𐍈𐌰. 𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍃 𐌲𐌰𐌼𐌴𐌻𐌾𐌹𐌸 𐌽𐌹 𐌰𐍆 𐌸𐌹𐌶𐍉𐍃 𐍃𐌴𐌹𐌳𐍉𐍃!'''",
-       "editing": "𐌼𐌰𐌹𐌳𐌾𐌰𐌽 𐌰𐍆 $1",
-       "creating": "𐍃𐌺𐌰𐍀𐌾𐌰𐌽𐌳𐍃/𐍃𐌺𐌰𐍀𐌾𐌰𐌽𐌳𐌴𐌹 $1",
-       "editingsection": "𐌼𐌰𐌹𐌳𐌾𐌰𐌽 𐌰𐍆 $1 (𐍆𐌴𐍂𐌰)",
-       "editingcomment": "𐌼𐌰𐌹𐌳𐌾𐌰𐌽 𐌰𐍆 $1 (𐍂𐍉𐌳𐌾𐌰𐍆𐌴𐍂𐌰)",
-       "yourdiff": "ð\90\8c¼ð\90\8c¹ð\90\8d\83ð\90\8d\83ð\90\8c°ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8cº𐍉𐍃",
+       "updated": "(𐌰𐌽𐌰𐌽𐌹𐍅𐌹𐌸)",
+       "previewnote": "<strong>𐌲𐌰𐌼𐌹𐌽𐌸𐌴𐌹 𐌸𐌰𐍄𐌴𐌹 𐌸𐌰𐍄𐌰 𐌹𐍃𐍄 𐌸𐌰𐍄𐌰𐌹𐌽𐌴𐌹 𐍆𐌰𐌿𐍂𐌰𐍃𐌹𐌿𐌽𐍃.</strong>\n𐌸𐌴𐌹𐌽𐍉𐍃 𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍉𐍃 𐌽𐌰𐌿𐌷 𐌽𐌹 𐌲𐌰𐍆𐌰𐍃𐍄𐌰𐌽𐍉𐍃 𐍃𐌹𐌽𐌳!",
+       "editing": "{{GENDER:𐌹𐌽𐌼𐌰𐌹𐌳𐌾𐌰𐌽𐌳𐍃|𐌹𐌽𐌼𐌰𐌹𐌳𐌾𐌰𐌽𐌳𐌴𐌹}} $1",
+       "creating": "{{GENDER:𐍃𐌺𐌰𐍀𐌾𐌰𐌽𐌳𐍃|𐍃𐌺𐌰𐍀𐌾𐌰𐌽𐌳𐌴𐌹}} $1",
+       "editingsection": "{{GENDER:𐌹𐌽𐌼𐌰𐌹𐌳𐌾𐌰𐌽𐌳𐍃|𐌹𐌽𐌼𐌰𐌹𐌳𐌾𐌰𐌽𐌳𐌴𐌹}} $1 (𐌳𐌰𐌹𐌻)",
+       "editingcomment": "{{GENDER:𐌹𐌽𐌼𐌰𐌹𐌳𐌾𐌰𐌽𐌳𐍃|𐌹𐌽𐌼𐌰𐌹𐌳𐌾𐌰𐌽𐌳𐌴𐌹}} $1 (𐌽𐌹𐌿𐌾𐌰 𐌳𐌰𐌹𐌻)",
+       "yourdiff": "ð\90\8c²ð\90\8c°ð\90\8d\83ð\90\8cºð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c´ð\90\8c¹ð\90\8c½𐍉𐍃",
        "template-protected": "(𐌷𐌰𐌱𐌰𐌹𐌸 𐌼𐌿𐌽𐌳)",
-       "template-semiprotected": "(halb-gabaírgjan)",
+       "template-semiprotected": "(𐌷𐌰𐌱𐌰𐌹𐌸 𐌷𐌰𐌻𐌱𐌰𐌼𐌿𐌽𐌸)",
        "hiddencategories": "𐍃𐌰 𐌻𐌰𐌿𐍆𐍃 𐌹𐍃𐍄 𐌲𐌰𐌳𐌰𐌹𐌻𐌰 {{PLURAL:$1|1 𐌰𐌽𐌰𐌻𐌰𐌿𐌲𐌽𐌹𐍃 𐌺𐌿𐌽𐌾𐌹𐍃|$1 𐌰𐌽𐌰𐌻𐌰𐌿𐌲𐌽𐌰𐌹𐌶𐌴 𐌺𐌿𐌽𐌾𐌴}}:‎",
        "permissionserrorstext-withaction": "𐌽𐌹 𐌷𐌰𐌱𐌰𐌹𐍃 𐌰𐌽𐌳𐌻𐌴𐍄 𐌳𐌿 $2, 𐌹𐌽 {{PLURAL:$1|𐌹𐍆𐍄𐌿𐌼𐌰𐌹𐌶𐍉𐍃 𐍅𐌰𐌹𐌷𐍄𐌰𐌹𐍃|𐌹𐍆𐍄𐌿𐌼𐌰𐌹𐌶𐍉 𐍅𐌰𐌹𐌷𐍄𐌴}}:",
        "moveddeleted-notice": "𐍃𐌰 𐌻𐌰𐌿𐍆𐍃 𐌿𐍃𐌽𐌿𐌼𐌰𐌽𐍃 𐌹𐍃𐍄. 𐌿𐍃𐌽𐌿𐌼𐍄𐍃 𐌾𐌰𐌷 𐌲𐌰𐍆𐌰𐍃𐍄𐌰𐌹𐌽𐍃 𐌼𐌹𐌸𐍃𐌰𐍄𐌴𐌹𐌽𐌰𐌹𐍃 𐌿𐍆 𐍃𐌹𐌽𐌳 𐌿𐍃𐍄𐌰𐌹𐌺𐌽𐌴𐌹𐌽𐌰𐌹.",
        "post-expand-template-inclusion-warning": "'''𐌷𐍅𐍉𐍄𐌾𐌰𐌽𐌳𐍃:''' 𐍆𐌰𐌿𐍂𐌰𐌼𐌴𐌻𐌴𐌹𐌽𐍃 𐍃𐌹𐌽𐌳 𐌿𐍆𐌰𐍂𐌼𐌹𐌺𐌹𐌻𐍃. 𐍃𐌿𐌼𐍃 𐍆𐌰𐌿𐍂𐌴𐌼𐌴𐌻𐌴𐌹𐌽𐍉𐍃 𐌽𐌹 𐌼𐌰𐌲 𐍅𐌹𐍃𐌰𐌽 𐌸𐌰𐍂",
        "post-expand-template-inclusion-category": "𐍃𐌴𐌹𐌳𐍉𐌽𐍃 𐌸𐌰𐍂 𐍆𐌰𐌿𐍂𐌰𐌼𐌴𐌻𐌴𐌹𐌽𐍃 𐍃𐌹𐌽𐌳 𐌿𐍆𐌰𐍂𐌼𐌹𐌺𐌹𐌻𐍃",
        "viewpagelogs": "𐌰𐍄𐌰𐌿𐌲𐌴𐌹 𐌲𐌰𐍆𐌰𐍃𐍄𐌰𐌹𐌽𐌹𐌽𐍃 𐌸𐌰𐌼𐌼𐌰 𐌻𐌰𐌿𐌱𐌰",
-       "currentrev": "ð\90\8c½ð\90\8c¿ ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³𐌴𐌹𐌽𐍃",
+       "currentrev": "ð\90\8c°ð\90\8d\86ð\90\8d\84ð\90\8c¿ð\90\8c¼ð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8c° ð\90\8c²ð\90\8c°ð\90\8c±ð\90\8d\89ð\90\8d\84𐌴𐌹𐌽𐍃",
        "currentrev-asof": "𐌰𐍆𐍄𐌿𐌼𐌹𐍃𐍄𐌰 𐌲𐌰𐌱𐍉𐍄𐌴𐌹𐌽𐍃 𐍆𐍂𐌰𐌼 $1",
        "revisionasof": "𐌲𐌰𐌱𐍉𐍄𐌴𐌹𐌽𐍃 𐍆𐍂𐌰𐌼 $1",
        "revision-info": "𐌲𐌰𐌱𐍉𐍄𐌴𐌹𐌽𐍃 𐌹𐌽 $1 𐍆𐍂𐌰𐌼 {{GENDER:$6|$2}}$7",
-       "previousrevision": "←𐌰𐌹𐍂𐌹𐍃 𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍃",
-       "nextrevision": "Iftuma máideins→",
-       "currentrevisionlink": "ð\90\8c½ð\90\8c¿ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³𐌴𐌹𐌽𐍃",
+       "previousrevision": "← 𐌰𐌹𐍂𐌹𐌶𐌴𐌹 𐌲𐌰𐌱𐍉𐍄𐌴𐌹𐌽𐍃",
+       "nextrevision": "𐌽𐌹𐌿𐌾𐌹𐌶𐌴𐌹 𐌲𐌰𐌱𐍉𐍄𐌴𐌹𐌽𐍃 →",
+       "currentrevisionlink": "ð\90\8c°ð\90\8d\86ð\90\8d\84ð\90\8c¿ð\90\8c¼ð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8c° ð\90\8c²ð\90\8c°ð\90\8c±ð\90\8d\89ð\90\8d\84𐌴𐌹𐌽𐍃",
        "cur": "𐌽𐌿",
        "next": "𐌹𐍆𐍄𐌿𐌼𐌰",
        "last": "𐌰𐍆𐍄𐌿𐌼𐌹𐍃𐍄𐍃",
-       "page_first": "frumists",
+       "page_first": "𐍆𐍂𐌿𐌼𐌹𐍃𐍄𐍃",
        "page_last": "𐍃𐍀𐌴𐌳𐌿𐌼𐌹𐍃𐍄𐍃",
-       "histfirst": "ð\90\8d\86ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c¸ð\90\8c¹ð\90\8d\83",
-       "histlast": "ð\90\8d\83ð\90\8d\80ð\90\8c´ð\90\8c³ð\90\8c¿ð\90\8c¼ð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8d\83",
-       "history-feed-item-nocomment": "$1 at $2",
+       "histfirst": "ð\90\8c°ð\90\8c»ð\90\8c¸ð\90\8c¹ð\90\8c¶ð\90\8d\89",
+       "histlast": "ð\90\8c½ð\90\8c¹ð\90\8c¿ð\90\8c¾ð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8d\89",
+       "history-feed-item-nocomment": "$1 𐌰𐍄 $2",
        "rev-delundel": "𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹 𐌰𐌽𐌰𐍃𐌹𐌿𐌽",
-       "revdel-restore": "ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c¾ð\90\8c¹ð\90\8d\83 ð\90\8c°ð\90\8c½ð\90\8c°ð\90\8d\83ð\90\8c¹ð\90\8c¿ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c·ð\90\8d\84ð\90\8c´ð\90\8c¹ð\90\8c²ð\90\8d\83",
+       "revdel-restore": "ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c´ð\90\8c¹ ð\90\8c°ð\90\8c½ð\90\8c°ð\90\8d\83ð\90\8c¹ð\90\8c¿ð\90\8c½",
        "revertmerge": "𐌿𐌽𐌲𐌰𐍄𐌹𐌻𐍉𐍃",
        "history-title": "𐌰𐍆𐍄𐍂𐌰𐍃𐌹𐌿𐌽𐌹𐍃𐍀𐌹𐌻𐌻 𐌻𐌰𐌿𐌱𐌹𐍃 \"$1\"",
        "difference-title": "𐌲𐌰𐍃𐌺𐌰𐌹𐌳𐌴𐌹𐌽𐍃 𐌼𐌹𐌸 𐌰𐍆𐍄𐍂𐌰𐍃𐌹𐌿𐌽𐍉𐌼 𐌻𐌰𐌿𐌱𐌹𐍃 \"$1\"",
        "searchresults-title": "𐍃𐍉𐌺𐌴𐌹𐌽𐌰𐌹𐍃 𐍄𐍉𐌾𐌰 𐍆𐌰𐌿𐍂 \"$1\"",
        "prevn": "𐌰𐍆𐍄𐌿𐌼𐌰 {{PLURAL:$1|$1}}",
        "nextn": "𐌹𐍆𐍄𐌿𐌼𐌰 {{PLURAL:$1|$1}}",
-       "prevn-title": "ð\90\8d\86ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c½ð\90\8c° $1 {{PLURAL:$1|ð\90\8d\84ð\90\8c°ð\90\8c¿ð\90\8c¹|ð\90\8d\84ð\90\8c°ð\90\8c¿ð\90\8c¾ð\90\8d\89ð\90\8d\83}}",
-       "nextn-title": "𐌰𐍆𐍄𐌿𐌼𐌰 $1 {{PLURAL:$1|𐍄𐌰𐌿𐌹|𐍄𐌰𐌿𐌾𐍉𐍃}}",
+       "prevn-title": "ð\90\8c°ð\90\8d\86ð\90\8d\84ð\90\8c¿ð\90\8c¼ð\90\8c¹ð\90\8d\83ð\90\8d\84\90\8c°) $1 {{PLURAL:$1|ð\90\8d\84ð\90\8c°ð\90\8c¿ð\90\8c¹|ð\90\8d\84ð\90\8d\89ð\90\8c¾ð\90\8c°}}",
+       "nextn-title": "𐌰𐍆𐍄𐌿𐌼(𐌰) $1 {{PLURAL:$1|𐍄𐌰𐌿𐌹|𐍄𐍉𐌾𐌰}}",
        "shown-title": "𐌰𐍄𐌰𐌿𐌲𐌴𐌹 $1 {{PLURAL:$1|𐍄𐌰𐌿𐌹|𐍄𐍉𐌾𐌰}} 𐍈𐌰𐍂𐌾𐌰𐌼𐌼𐌴𐌷 𐌻𐌰𐌿𐌱𐌰.",
        "viewprevnext": "𐍃𐌹𐌿𐌽𐌴𐌹𐍃 ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-new": "<strong>𐍃𐌺𐌰𐍀𐌴𐌹 𐌻𐌰𐌿𐍆 \"[[:$1]]\" 𐌰𐌽𐌰 𐌸𐌹𐌶𐌰𐌹 𐍅𐌹𐌺𐌹!</strong> {{{{PLURAL:$2|0=|𐍃𐌰𐌹 𐌾𐌰𐌷 𐌻𐌰𐌿𐍆 𐌱𐌹𐌲𐌹𐍄𐌰𐌽𐌰 𐌸𐌴𐌹𐌽𐌰𐌹 𐍃𐍉𐌺𐌴𐌹𐌽𐌰𐌹.|𐍃𐌰𐌹 𐌾𐌰𐌷 𐍄𐍉𐌾𐌰 𐍃𐍉𐌺𐌴𐌹𐌽𐌰𐌹𐍃 𐌱𐌹𐌲𐌹𐍄𐌰𐌽𐌰.}}",
        "searchprofile-articles": "𐌷𐌰𐌱𐌰𐌽𐌳𐌰𐌽𐍃 𐌻𐌰𐌿𐌱𐍉𐍃",
        "searchprofile-images": "𐌼𐌰𐌽𐌰𐌲𐌼𐌴𐌳𐌾𐌰",
        "searchprofile-everything": "𐌰𐌻𐌻",
-       "searchprofile-advanced": "ð\90\8d\86ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8d\82ð\90\8c°ð\90\8d\86ð\90\8d\82ð\90\8c°ð\90\8c¼ð\90\8c°",
+       "searchprofile-advanced": "ð\90\8c¼ð\90\8c°ð\90\8c½ð\90\8c°ð\90\8c²ð\90\8d\86ð\90\8c°ð\90\8c»ð\90\8c¸",
        "searchprofile-articles-tooltip": "𐍃𐍉𐌺𐌴𐌹 𐌹𐌽 $1",
        "searchprofile-images-tooltip": "𐍃𐍉𐌺𐌾𐌹𐍃 𐍆𐌴𐌹𐌻𐌰𐌽𐍃",
        "searchprofile-everything-tooltip": "𐍃𐍉𐌺𐌴𐌹 𐌰𐌻𐌻 𐌸𐌰𐍄𐌰 (𐌾𐌰𐌷 𐌲𐌰𐍅𐌰𐌿𐍂𐌳𐌾𐌰𐌻𐌰𐌿𐌱𐌰𐌽𐍃)",
        "search-result-size": "$1 ({{PLURAL:$2|•𐌰• 𐍅𐌰𐌿𐍂𐌳|•$2• 𐍅𐌰𐌿𐍂𐌳𐌰}})",
        "search-redirect": "(𐌰𐍆𐍄𐍂𐌰𐍅𐌴𐌹𐍄𐍃 𐍆𐍂𐌰𐌼 𐌸𐌰𐌼𐌼𐌰 $1)",
        "search-section": "(𐍆𐌴𐍂𐌰 $1)",
-       "search-suggest": "ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8cºð\90\8c½ð\90\8c¹ð\90\8c³ð\90\8c° ð\90\8c¸ð\90\8c¿: $1",
+       "search-suggest": "ð\90\8c²ð\90\8c°ð\90\8c¼ð\90\8c°ð\90\8c½ð\90\8d\84: $1",
        "searchall": "𐌰𐌻𐌻𐍃",
        "search-showingresults": "{{ZPLURAL:$4|𐍄𐌰𐌿𐌹 <strong>$1 𐍅𐌰𐌹𐌷𐍄𐌰𐌹𐍃 <strong>$3|𐍄𐍉𐌾𐌰 <strong>$1 - $2 𐍅𐌰𐌹𐌷𐍄𐌰𐌹𐍃 <strong>$3}}",
-       "search-nonefound": "ð\90\8c½ð\90\8c¹ ð\90\8c°ð\90\8c¹ð\90\8c½ð\90\8c·ð\90\8c¿ð\90\8c½ ð\90\8c²ð\90\8c°ð\90\8c²ð\90\8c¹ð\90\8c±ð\90\8c¾ð\90\8d\89 ð\90\8d\86ð\90\8c¿ð\90\8c»ð\90\8c»ð\90\8c¾ð\90\8c¹ð\90\8c¸ ð\90\8d\83ð\90\8d\89ð\90\8cºð\90\8c´ð\90\8c¹ð\90\8c½",
-       "powersearch-legend": "𐍃𐍉𐌺𐌴𐌹𐌸",
+       "search-nonefound": "ð\90\8c½ð\90\8c¹ ð\90\8d\84ð\90\8c°ð\90\8c¿ð\90\8c¹ ð\90\8d\85ð\90\8c°ð\90\8d\83 ð\90\8d\83ð\90\8c°ð\90\8c¼ð\90\8c°ð\90\8c½ð\90\8c° ð\90\8d\83ð\90\8d\85ð\90\8c° ð\90\8d\83ð\90\8d\89ð\90\8cºð\90\8c´ð\90\8c¹ð\90\8c½.",
+       "powersearch-legend": "𐍃𐍉𐌺𐌴𐌹",
        "preferences": "𐌼𐌴𐌹𐌽𐍉𐍃 𐌱𐍂𐌿𐌺𐌾𐌰𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐌴𐌹𐍃",
        "mypreferences": "𐌲𐌰𐌻𐌴𐌹𐌺𐌰𐌽𐌳𐌴𐌹𐌽𐍃 𐍅𐌰𐌹𐌷𐍄𐍃",
-       "prefs-skin": "Seidofill",
-       "skin-preview": "Faúrsaiƕa",
-       "saveprefs": "Melja",
-       "searchresultshead": "Sokeiþ",
-       "grouppage-sysop": "{{ns:project}}:ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89ð\90\8d\86ð\90\8c°ð\90\8c¸𐍃",
+       "prefs-skin": "𐍆𐌹𐌻𐌻",
+       "skin-preview": "𐍆𐌰𐌿𐍂𐌰𐍃𐌰𐌹𐍈",
+       "saveprefs": "𐌲𐌰𐍆𐌰𐍃𐍄",
+       "searchresultshead": "𐍃𐍉𐌺𐌴𐌹",
+       "grouppage-sysop": "{{ns:project}}:ð\90\8d\82ð\90\8c´ð\90\8c¹ð\90\8cº𐍃",
        "right-writeapi": "𐌱𐍂𐌿𐌺𐌴𐌹𐌽𐍃 API 𐌼𐌴𐌻𐌴𐌹𐌽𐌰𐌹𐍃",
        "rightslog": "Niutandis stutjanlog",
-       "nchanges": "$1 {{PLURAL:$1|máidein|máideins}}",
+       "nchanges": "$1 {{PLURAL:$1|𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍃|𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍉𐍃}}",
        "enhancedrc-history": "𐍃𐍀𐌹𐌻𐌻",
        "recentchanges": "𐌰𐍆𐍄𐌿𐌼𐌹𐍃𐍄𐍉𐍃 𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍉𐍃",
        "recentchanges-summary": "𐌰𐍆𐌰𐍂𐌻𐌰𐌹𐍃𐍄𐌴𐌹 𐌸𐌰𐌹𐌼 𐌰𐌽𐌳𐍅𐌰𐌹𐍂𐌸𐌹𐍃𐍄𐍉𐌼 𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍉𐌼 𐌳𐌿 𐍅𐌹𐌺𐌾𐌰 𐌰𐌽𐌰 𐌸𐌰𐌼𐌼𐌰 𐌻𐌰𐌿𐌱𐌰.",
        "rclinks": "𐌰𐍄𐌰𐌿𐌲𐌴𐌹 𐌰𐍆𐍄𐌿𐌼𐌹𐍃𐍄𐍉𐍃 $1 𐌹𐌽𐌼𐌰𐌹𐌳𐌹𐌽𐌹𐌽𐍃 𐌹𐌽 𐌰𐍆𐍄𐌿𐌼𐌹𐍃𐍄𐌰𐌹𐌼 $2 𐌳𐌰𐌲𐌰𐌼 <br />$3",
        "diff": "𐌼𐌹𐍃𐍃",
        "hist": "𐍃𐍀𐌹𐌻𐌻",
-       "hide": "ð\90\8d\86ð\90\8c¹ð\90\8c»ð\90\8c·ð\90\8c°ð\90\8c½",
-       "show": "ð\90\8c·ð\90\8c¿ð\90\8c»ð\90\8c¾ð\90\8c°ð\90\8c½",
+       "hide": "ð\90\8c°ð\90\8d\86ð\90\8d\86ð\90\8c¹ð\90\8c»ð\90\8c·",
+       "show": "ð\90\8c°ð\90\8d\84ð\90\8c°ð\90\8c¿ð\90\8c²ð\90\8c´ð\90\8c¹",
        "minoreditletter": "l",
        "newpageletter": "N",
        "boteditletter": "b",
        "recentchangeslinked": "𐌲𐌰𐍅𐌹𐌳𐌰𐌽𐍉𐍃 𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍉𐍃",
        "recentchangeslinked-feed": "Máideinlieks",
-       "recentchangeslinked-toolbox": "ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c»ð\90\8c¹ð\90\8c´ð\90\8cº𐍃",
+       "recentchangeslinked-toolbox": "ð\90\8c²ð\90\8c°ð\90\8d\85ð\90\8c¹ð\90\8c³ð\90\8c°ð\90\8c½ð\90\8d\89ð\90\8d\83 ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8d\89𐍃",
        "recentchangeslinked-title": "𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍉𐍃 𐌲𐌰𐍅𐌹𐌳𐌰𐌽𐍉𐍃 𐌼𐌹𐌸 \"$1\"",
        "recentchangeslinked-summary": "𐍃𐍉 𐌹𐍃𐍄 𐌻𐌴𐌹𐍃𐍄𐌰 𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐌴 𐌰𐍆𐍄𐌿𐌼𐌹𐍃𐍄𐍃 𐍃𐌺𐍉𐍀 𐌰𐌽𐌰 𐍃𐌴𐌹𐌳𐍉𐌽𐍃 𐌻𐌴𐌹𐌽𐌺𐍉𐌽𐌳 𐌿𐍃 𐌿𐍃𐍃𐌹𐌽𐌳𐌰𐌹 𐍃𐌴𐌹𐌳𐍉𐌽 (𐌰𐌹𐌸𐌸𐌰𐌿 𐌻𐌹𐌸𐌰𐌿𐍃 𐌿𐍃𐍃𐌹𐌽𐌳𐌰𐌹𐌶𐍉𐍃 𐌷𐌰𐌽𐍃𐍉𐍃). 𐍃𐌴𐌹𐌳𐍉𐌽𐍃 [[Special:Watchlist|𐍅𐌹𐍄𐌰𐌽𐌳𐌻𐌴𐌹𐍃𐍄𐍉𐍃 𐌸𐌴𐌹𐌽𐍉𐍃]] 𐍃𐌹𐌽𐌳 '''𐌳𐌹𐌲𐍂𐍃𐍄𐌰𐍆𐍃'''.",
        "recentchangeslinked-page": "𐌻𐌰𐌿𐌱𐌰𐌽𐌰𐌼𐍉:",
        "uploadlogpage": "Log af Ushlaþan",
        "filedesc": "𐌼𐌰𐌿𐍂𐌲𐌿𐍃 𐍃𐌺𐌴𐌹𐍂𐌴𐌹𐌽𐍃",
        "watchthisupload": "Witan so seido",
-       "imgfile": "Feilans",
+       "imgfile": "𐍆𐌰𐌴𐌹𐌻",
        "listfiles": "Feilans tala",
        "file-anchor-link": "𐍆𐌴𐌹𐌻𐌰𐌽𐍃",
        "filehist": "𐍆𐌴𐌹𐌻𐌰𐌽𐍃 𐌰𐌹𐍂𐌹𐍃",
        "filehist-dimensions": "𐍅𐌰𐌷𐍃𐍄𐌿𐍃",
        "filehist-filesize": "Feilans wahstus",
        "filehist-comment": "𐍅𐌰𐌿𐍂𐌳",
-       "imagelinks": "ð\90\8d\86ð\90\8c´ð\90\8c¹ð\90\8c»ð\90\8c¹ð\90\8c½ð\90\8d\83 ð\90\8c±ð\90\8d\82ð\90\8c¿ð\90\8cºð\90\8c¹ð\90\8d\83",
+       "imagelinks": "ð\90\8c±ð\90\8d\82ð\90\8c¿ð\90\8cºð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8d\83 ð\90\8d\86ð\90\8c°ð\90\8c´ð\90\8c¹ð\90\8c»ð\90\8c´",
        "linkstoimage": "𐌰𐍆𐍄𐌿𐌼𐌰 {{PLURAL:$1|𐍃𐍉 𐍃𐌴𐌹𐌳𐍉 𐌻𐌴𐌹𐌽𐌺𐍉𐌸|𐌸𐍉𐍃 𐍃𐌴𐌹𐌳𐍉𐌽𐍃 𐌻𐌴𐌹𐌽𐌺𐍉𐌽𐌳}} 𐌸𐌹𐌶𐍉𐌶𐌿𐌷 𐍆𐌴𐌹𐌻𐍉𐍃",
        "sharedupload-desc-here": "𐍃𐍉 𐌳𐌰𐍄𐌰 𐌹𐍃𐍄 𐍆𐍂𐌰𐌼 $1 𐌾𐌰𐌷 𐌼𐌰𐌲 𐌱𐍂𐌿𐌺𐌾𐌰𐌳𐌰 𐍆𐍂𐌰𐌼 𐌰𐌽𐌸𐌰𐍂𐌰𐌹𐌼 𐍆𐌰𐌿𐍂𐌰𐍅𐌰𐌿𐍂𐍀𐍉𐌼.\n𐌲𐌰𐍃𐌺𐌴𐌹𐍂𐌴𐌹𐌽𐍃 𐌰𐌽𐌰 𐍃𐌴𐌹𐌽𐌰𐌼𐌼𐌰 [$2 𐌳𐌰𐍄𐌰 𐍃𐌺𐌴𐌹𐍂𐌴𐌹𐌽𐌹𐌻𐌰𐌿𐌱𐌰] 𐌾𐌰𐌹𐌽𐌰𐍂 𐌰𐍄𐌰𐌿𐌲𐌹𐌸𐍃 𐌹𐍃𐍄 𐌿𐍆.",
-       "filedelete-submit": "Taíran",
-       "mimesearch": "MIME sokeiþ",
+       "filedelete-submit": "𐍆𐍂𐌰𐌵𐌹𐍃𐍄𐌴𐌹",
+       "mimesearch": "MIME 𐍃𐍉𐌺𐌴𐌹",
        "listredirects": "𐍄𐌰𐌻𐌰 𐌰𐍆 𐍄𐌰𐌹𐌺𐌾𐌰𐌽𐍃𐌴𐌹𐌳𐍉𐍃",
        "randompage": "𐌸𐌿𐍃 𐌿𐌽𐌺𐌿𐌽𐌸𐍃 𐌻𐌰𐌿𐍆𐍃",
-       "statistics": "ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89ð\90\8d\83ð\90\8d\84ð\90\8c°ð\90\8d\84ð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8c¹ð\90\8cº",
-       "brokenredirects-edit": "(𐌼𐌰𐌹𐌳𐌾𐌰𐌽)",
+       "statistics": "ð\90\8d\82ð\90\8c°ð\90\8c¸ð\90\8c¾ð\90\8d\89ð\90\8c½ð\90\8d\83",
+       "brokenredirects-edit": "𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹",
        "brokenredirects-delete": "(𐍄𐌰𐌹𐍂𐌰𐌽)",
        "nbytes": "$1 {{PLURAL:$1|𐌱𐌹𐍄|𐌱𐌰𐍄𐌰}}",
        "ncategories": "$1 {{PLURAL:$1|𐌺𐌿𐌽𐌾𐌰|𐌺𐌿𐌽𐌾𐍉𐍃}}",
-       "nlinks": "$1 {{PLURAL:$1|táikjanbandi|táikjanbandja}}",
-       "nmembers": "$1 {{PLURAL:$1|niutand|niutanda}}",
-       "wantedpages": "Gaírnedum seidam",
-       "shortpages": "ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8d\84ð\90\8c¹ð\90\8c»ð\90\8c° ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³𐍉𐍃",
-       "longpages": "𐌻𐌰𐌲𐌲𐌰 𐍃𐌴𐌹𐌳𐍉𐍃",
+       "nlinks": "$1 {{PLURAL:$1|𐌲𐌰𐍅𐌹𐍃𐍃|𐌲𐌰𐍅𐌹𐍃𐍃𐌴𐌹𐍃}}",
+       "nmembers": "$1 {{PLURAL:$1|𐌲𐌰𐌳𐌰𐌹𐌻𐌰|𐌲𐌰𐌳𐌰𐌹𐌻𐌰𐌽𐍃}}",
+       "wantedpages": "𐌲𐌰𐌹𐍂𐌽𐌹𐌳𐌰𐌹 𐌻𐌰𐌿𐌱𐍉𐍃",
+       "shortpages": "ð\90\8c¼ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c²ð\90\8c°ð\90\8c¹ ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8c±𐍉𐍃",
+       "longpages": "𐌻𐌰𐌲𐌲𐌰𐌹 𐌻𐌰𐌿𐌱𐍉𐍃",
        "listusers": "𐍂𐌴𐌲𐌹𐍃𐍄𐍂𐌴𐍂𐌰𐌳𐌴 𐌱𐍂𐌿𐌺𐌾𐌰𐌽𐌳𐍃",
        "newpages": "𐌽𐌹𐌿𐌾𐌰𐌹 𐌻𐌰𐌿𐌱𐍉𐍃",
        "move": "𐌽𐌰𐌼𐌾𐌰𐌽 𐌰𐍆𐍄𐍂𐌰",
-       "movethispage": "ð\90\8d\83ð\90\8cºð\90\8c¹ð\90\8c¿ð\90\8c±ð\90\8c°ð\90\8c½ ð\90\8d\83ð\90\8c° ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89",
+       "movethispage": "ð\90\8c¼ð\90\8c¹ð\90\8c¸ð\90\8d\83ð\90\8c°ð\90\8d\84ð\90\8c´ð\90\8c¹ ð\90\8c¸ð\90\8c°ð\90\8c½ð\90\8c° ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8d\86",
        "booksources": "𐌱𐍉𐌺𐌰𐌱𐍂𐌿𐌽𐌽𐌰𐌽𐍃",
        "booksources-search-legend": "𐍃𐍉𐌺𐌴𐌹 𐌱𐍉𐌺𐌰𐌱𐍂𐌿𐌽𐌽𐌰𐌽𐍃",
        "booksources-search": "𐍃𐍉𐌺𐌴𐌹",
        "speciallogtitlelabel": "Namo:",
        "log": "𐌻𐍉𐌲𐌱𐍉𐌺𐍉𐍃",
        "all-logs-page": "𐌰𐌻𐌻𐌰 𐌻𐍉𐌲𐍉𐍃",
-       "allpages": "ð\90\8c°ð\90\8c»ð\90\8c»ð\90\8c¹ð\90\8d\83 ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³𐍉𐍃",
+       "allpages": "ð\90\8c°ð\90\8c»ð\90\8c»ð\90\8c°ð\90\8c¹ ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8c±𐍉𐍃",
        "nextpage": "𐌹𐍆𐍄𐌿𐌼𐌰 𐍃𐌴𐌹𐌳𐍉 ($1)",
-       "prevpage": "ð\90\8c°ð\90\8d\86ð\90\8d\84ð\90\8c¿ð\90\8c¼ð\90\8c° ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89 ($1)",
+       "prevpage": "ð\90\8c°ð\90\8d\86ð\90\8d\84ð\90\8c¿ð\90\8c¼ð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8d\83 ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8d\86ð\90\8d\83 ($1)",
        "allarticles": "𐌰𐌻𐌻𐌰𐌹 𐌻𐌰𐌿𐌱𐍉𐍃",
        "allpagessubmit": "𐌲𐌰𐌲𐌲",
        "categories": "𐌺𐌿𐌽𐌾𐌰",
        "linksearch-ns": "𐍃𐌴𐌹𐌳𐍉𐍆𐌴𐍂𐌰:",
-       "emailuser": "𐍃𐌰𐌽𐌳𐌾𐌰𐌽 𐌸𐍉 𐌽𐌹𐌿𐍄𐌰𐌽𐌳 𐌱𐍉𐌺𐍉𐌼",
-       "watchlist": "ð\90\8c¼ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8d\89ð\90\8d\83 ð\90\8d\85ð\90\8c¹ð\90\8d\84ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8d\83ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8c°",
+       "emailuser": "{{GENDER: 𐍃𐌰𐌽𐌳𐌴𐌹 𐌴-𐌱𐍉𐌺𐍉𐍃 𐌳𐌿 𐌸𐌰𐌼𐌼𐌰 𐌱𐍂𐌿𐌺𐌾𐌰𐌽𐌳|𐍃𐌰𐌽𐌳𐌴𐌹 𐌴-𐌱𐍉𐌺𐍉𐍃 𐌳𐌿 𐌸𐌹𐌶𐌰𐌹 𐌱𐍂𐌿𐌺𐌾𐌰𐌽𐌳𐌾𐌰𐌹}}",
+       "watchlist": "ð\90\8d\85ð\90\8c¹ð\90\8d\84ð\90\8c°ð\90\8d\85ð\90\8c¹ð\90\8cºð\90\8d\89",
        "mywatchlist": "𐌻𐌰𐌹𐍃𐍄𐌰𐌻𐌴𐌹𐍃𐍄𐌰",
-       "watch": "ð\90\8d\85ð\90\8c°ð\90\8d\82ð\90\8c°ð\90\8c½",
-       "watchthispage": "ð\90\8d\85ð\90\8c°ð\90\8d\82ð\90\8c°ð\90\8c½ ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89",
+       "watch": "ð\90\8c°ð\90\8d\84ð\90\8d\85ð\90\8c¹ð\90\8d\84",
+       "watchthispage": "ð\90\8c°ð\90\8d\84ð\90\8d\85ð\90\8c¹ð\90\8d\84 ð\90\8c¸ð\90\8c°ð\90\8c¼ð\90\8c¼ð\90\8c° ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8c±ð\90\8c°",
        "unwatch": "𐌽𐌹𐍅𐌰𐍂𐌰𐌽",
-       "watchlist-details": "{{PLURAL:$1|$1 seido|$1 seidona}} witáiþs inu maþleiseidam.",
+       "watchlist-details": "{{PLURAL:$1|$1 𐌻𐌰𐌿𐍆𐍃|$1 𐌻𐌰𐌿𐌱𐍉𐍃}} 𐌰𐌽𐌰 𐌸𐌴𐌹𐌽𐌰𐌹 𐍅𐌹𐍄𐌰𐍅𐌹𐌺𐍉𐌽, 𐌽𐌹 𐍃𐌿𐌽𐌳𐍂𐍉 𐍂𐌰𐌷𐌽𐌾𐌰𐌽𐌳𐌰 𐌲𐌰𐍅𐌰𐌿𐍂𐌳𐌾𐌰𐌻𐌰𐌿𐌱𐍉𐍃.",
        "watching": "Wita...",
        "unwatching": "Niwita...",
        "created": "𐌲𐌰𐍃𐌺𐌰𐍀𐌾𐌰𐌽",
-       "deletepage": "ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c° ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89",
+       "deletepage": "ð\90\8d\86ð\90\8d\82ð\90\8c°ð\90\8cµð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8c´ð\90\8c¹ ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8c±ð\90\8c°",
        "delete-legend": "𐍄𐌰𐌹𐍂𐌰𐌽",
        "actioncomplete": "𐍅𐌰𐍃𐌿𐌷 𐌹𐍄𐌰 𐌲𐌰𐌿𐍃𐍄𐌹𐌿𐌷𐌰𐌽",
        "dellogpage": "𐍄𐌰𐌹𐍂𐌰 𐌰𐌹𐍂𐍅𐌱𐍉𐌺𐌰",
        "deletereasonotherlist": "𐌰𐌽𐌸𐌰𐍂 𐌼𐌹𐍄𐍉𐌽𐍃",
        "rollbacklink": "𐌰𐍆𐍅𐌰𐌻𐍅𐌴𐌹",
        "rollbacklinkcount": "𐌰𐍆𐍅𐌰𐌻𐍅𐌴𐌹 $1 {{PLURAL:$1|𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽|𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐌹𐌽𐍃}}",
-       "protectlogpage": "Log af Baírgjan",
-       "prot_1movedto2": "[[$1]] skiubiþ du [[$2]]",
-       "protect-level-sysop": "ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89ð\90\8d\86ð\90\8c°ð\90\8c¸ð\90\8d\83 ð\90\8c°ð\90\8c¹ð\90\8c½ð\90\8c°ð\90\8c·ð\90\8c°",
-       "protect-expiring": "bláuþiþ $1 (UTC)",
+       "protectlogpage": "𐍆𐍂𐌹𐌸𐌿𐌲𐌰𐍆𐌰𐍃𐍄𐌰𐌹𐌽𐍃",
+       "prot_1movedto2": "[[$1]] 𐌼𐌹𐌸𐍃𐌰𐍄𐌹𐌸 𐌳𐌿 [[$2]]",
+       "protect-level-sysop": "ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c»ð\90\8c´ð\90\8d\84ð\90\8c¹ð\90\8c¸ ð\90\8c¸ð\90\8c°ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8c½ð\90\8c´ð\90\8c¹ ð\90\8d\82ð\90\8c´ð\90\8c¹ð\90\8cºð\90\8d\83",
+       "protect-expiring": "𐌿𐍃𐍄𐌹𐌿𐌷𐌹𐌸 $1 (UTC)",
        "restriction-type": "Freihals:",
-       "restriction-edit": "ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c¾ð\90\8c°ð\90\8c½",
+       "restriction-edit": "ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c´ð\90\8c¹",
        "restriction-move": "𐍃𐌺𐌹𐌿𐌱𐌰𐌽",
        "undeletebtn": "𐌰𐍆𐍄𐍂𐌰 𐌲𐌰𐌱𐍉𐍄𐌾𐌰𐌽",
        "undeletelink": "𐍃𐌰𐌹𐍈𐌰𐌽/𐌰𐍆𐍄𐍂𐌰𐌲𐌰𐍃𐌰𐍄𐌾𐌰𐌽",
        "undeleteviewlink": "𐍃𐌰𐌹𐍈𐌹𐍃",
-       "undelete-search-submit": "Sokeiþ",
+       "undelete-search-submit": "𐍃𐍉𐌺𐌴𐌹",
        "namespace": "𐌽𐌰𐌼𐌰𐍂𐌿𐌼:",
-       "invert": "Afwandjan kustus",
+       "invert": "𐌲𐌰𐍅𐌰𐌽𐌳𐌴𐌹 𐌸𐌰𐍄𐌰 𐌲𐌰𐍅𐌰𐌻𐌹𐌳𐍉",
        "blanknamespace": "(𐍆𐍂𐌿𐌼𐌹𐍃𐍄𐍃)",
        "contributions": "𐌱𐌹𐌰𐌿𐌺𐌰𐌹𐌽𐌴𐌹𐍃 𐌱𐍂𐌿𐌺𐌾𐌰𐌽𐌳𐌹𐍃 {{{{GENDER:$1|User}}",
        "mycontris": "𐌱𐌹𐌰𐌿𐌺𐌰𐌹𐌽𐌴𐌹𐍃",
        "year": "𐍆𐍂𐌰𐌼 𐌾𐌴𐍂𐌰 (𐌾𐌰𐌷 𐌰𐍆𐍄𐌿𐌼𐌰):",
        "sp-contributions-newbies-sub": "Faúr niujis niutandis",
        "sp-contributions-blocklog": "Logboka af afdraúsjan",
-       "sp-contributions-talk": "Maþleiseido",
+       "sp-contributions-talk": "𐌲𐌰𐍅𐌰𐌿𐍂𐌳𐌾𐌰",
        "whatlinkshere": "𐌰𐌻𐌻𐌰𐌹 𐌻𐌰𐌿𐌱𐍉𐍃 𐌸𐌰𐌹𐌴𐌹 𐌱𐍂𐌹𐌲𐌲𐌰𐌽𐌳 𐌸𐌿𐌺 𐌷𐌹𐌳𐍂𐌴",
        "whatlinkshere-title": "𐌻𐌰𐌿𐌱𐍉𐍃 𐌸𐌰𐌹𐌴𐌹 𐍄𐌰𐌹𐌺𐌽𐌾𐌰𐌽𐌳 𐌳𐌿 \"$1\"",
        "whatlinkshere-page": "𐌻𐌰𐌿𐍆𐍃:",
        "linkshere": "𐌹𐍆𐍄𐌿𐌼𐌰𐌹 𐌻𐌰𐌿𐌱𐍉𐍃 𐌱𐍂𐌹𐌲𐌲𐌰𐌽𐌳 𐌸𐌿𐌺  <strong>[[:$1]]</strong>:",
-       "isredirect": "ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8cºð\90\8c¾ð\90\8c°ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89",
-       "istemplate": "ináukan",
-       "whatlinkshere-prev": "{{PLURAL:$1|aftuma|aftumans $1}}",
+       "isredirect": "ð\90\8c°ð\90\8c»ð\90\8c¾ð\90\8c°ð\90\8d\82 ð\90\8c±ð\90\8d\82ð\90\8c¹ð\90\8c²ð\90\8c²ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8d\83 ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8d\86ð\90\8d\83",
+       "istemplate": "𐍄𐍂𐌰𐌽𐍃𐌺𐌻𐌿𐍃𐌾𐍉",
+       "whatlinkshere-prev": "{{PLURAL:$1|𐌰𐍆𐍄𐌿𐌼𐌹𐍃𐍄𐌰|𐌰𐍆𐍄𐌿𐌼𐌹𐍃𐍄𐌰𐌽𐍃 $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|iftuma|iftumans $1}}",
-       "whatlinkshere-links": "← táikajanbandja",
+       "whatlinkshere-links": "← 𐌲𐌰𐍅𐌹𐍃𐍃𐌴𐌹𐍃",
        "whatlinkshere-hidelinks": "$1 𐌲𐌰𐍅𐌹𐍃𐍃𐌴𐌹𐍃",
-       "blockip": "ð\90\8c°ð\90\8d\86ð\90\8c³ð\90\8d\82ð\90\8c°ð\90\8c¿ð\90\8d\83ð\90\8c¾ð\90\8c°ð\90\8c½ ð\90\8c½ð\90\8c¹ð\90\8c¿ð\90\8d\84ð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c¹ð\90\8d\83",
+       "blockip": "ð\90\8d\86ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c³ð\90\8c°ð\90\8c¼ð\90\8c¼ð\90\8c´ð\90\8c¹ {{GENDER:$1|user}}",
        "ipbreason": "𐍆𐌰𐌹𐍂𐌹𐌽𐌰:",
        "ipboptions": "𐌱 𐌰𐍅𐍂𐌰:2 hours, 𐌰 𐌳𐌰𐌲𐍃:1 day, 𐌲 𐌳𐌰𐌲𐍉𐍃:3 days, 𐌰 𐍅𐌹𐌺𐍉:1 week, 𐌱 𐍅𐌹𐌺𐍉𐌽𐍃:2 weeks, 𐌰 𐌼𐌴𐌽𐍉𐌸𐍃:1 month, 𐌲 𐌼𐌴𐌽𐍉𐌸𐍉𐍃:3 months, 𐌵 𐌼𐌴𐌽𐍉𐌸𐍉𐍃:6 months, 𐌰 𐌾𐌴𐍂:1 year, 𐌹𐌽𐌿𐍄𐍂𐌹𐌲𐌲𐌴𐌽𐌳𐌴𐌹𐍃:infinite",
-       "ipblocklist-submit": "Sokeiþ",
-       "infiniteblock": "ajukduþs",
+       "ipblocklist-submit": "𐍃𐍉𐌺𐌴𐌹",
+       "infiniteblock": "𐌰𐌽𐌳𐌰𐌻𐌰𐌿𐍃",
        "blocklink": "𐍆𐌰𐌿𐍂𐌳𐌰𐌼𐌼𐌴𐌹",
        "unblocklink": "𐍅𐌰𐌽𐌳𐌾𐌰𐌽",
        "change-blocklink": "𐌲𐌰𐌼𐌰𐌹𐌳𐌾𐌰𐌽 𐍅𐌰𐌿𐍂𐌾𐌰𐍅𐌰𐌳𐌳𐌾𐌿𐍃",
        "contribslink": "𐌱𐌹𐌰𐌿𐌺𐌰𐌹𐌽𐌴𐌹𐍃",
-       "blocklogpage": "ð\90\8c»ð\90\8d\89ð\90\8c²ð\90\8c±ð\90\8d\89ð\90\8cºð\90\8c° ð\90\8c°ð\90\8d\86 ð\90\8c°ð\90\8d\86ð\90\8c³ð\90\8d\82ð\90\8c°ð\90\8c¿ð\90\8d\83ð\90\8c¾ð\90\8c°ð\90\8c½",
+       "blocklogpage": "ð\90\8d\86ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c³ð\90\8c°ð\90\8c¼ð\90\8c¼ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c°ð\90\8c¹ð\90\8d\83 ð\90\8c²ð\90\8c°ð\90\8d\86ð\90\8c°ð\90\8d\83ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8c½ð\90\8d\83.",
        "blocklogentry": "𐌰𐍆𐌳𐍂𐌰𐌿𐍃𐌹𐌸 [[$1]] 𐍆𐌰𐌿𐍂 $2 $3",
-       "newtitle": "ð\90\8c³ð\90\8c¿ ð\90\8c½ð\90\8c¹ð\90\8c¿ð\90\8c¾ð\90\8c¹ð\90\8d\83 ð\90\8c½ð\90\8c°ð\90\8c¼ð\90\8d\89ð\90\8d\83:",
-       "move-watch": "ð\90\8d\85ð\90\8c¹ð\90\8d\84ð\90\8c°ð\90\8c½ ð\90\8d\83ð\90\8d\89 ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89",
+       "newtitle": "ð\90\8c½ð\90\8c¹ð\90\8c¿ð\90\8c¾ð\90\8c¹ ð\90\8c¿ð\90\8d\86ð\90\8c°ð\90\8d\82ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c¹:",
+       "move-watch": "ð\90\8c°ð\90\8d\84ð\90\8d\85ð\90\8c¹ð\90\8d\84 ð\90\8c±ð\90\8d\82ð\90\8c¿ð\90\8c½ð\90\8c½ð\90\8c°ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8c±ð\90\8c° ð\90\8c¾ð\90\8c°ð\90\8c· ð\90\8c¼ð\90\8c¿ð\90\8c½ð\90\8c³ð\90\8d\82ð\90\8c´ð\90\8c¹ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8c±ð\90\8c°",
        "movepagebtn": "𐍃𐌺𐌹𐌿𐌱𐌰 𐍃𐌴𐌹𐌳𐍉",
-       "movelogpage": "Log af skiubans",
+       "movelogpage": "𐌼𐌹𐌸𐍃𐌰𐍄𐌴𐌹 𐌲𐌰𐍆𐌰𐍃𐍄𐌰𐌹𐌽",
        "movereason": "𐍆𐌰𐌹𐍂𐌹𐌽𐌰:",
        "revertmove": "𐍂𐌰𐌹𐌳𐌾𐌰𐌽",
        "export": "𐌿𐍄𐌱𐌰𐌹𐍂 𐌻𐌰𐌿𐌱𐌰𐌽𐍃",
        "thumbnail-more": "\n𐌼𐌹𐌺𐌹𐌻𐌴𐌹",
        "tooltip-pt-userpage": "{{GENDER:|Your user}} 𐌻𐌰𐌿𐍆𐍃",
-       "tooltip-pt-mytalk": "{{GENDER:|Your}} 𐌻𐌰𐌿𐍆𐍃 𐌲𐌰𐍅𐌰𐌿𐍂𐌳𐌾𐌹𐍃",
+       "tooltip-pt-mytalk": "{{GENDER:|𐌸𐌴𐌹𐌽𐍃}} 𐌻𐌰𐌿𐍆𐍃 𐌲𐌰𐍅𐌰𐌿𐍂𐌳𐌾𐌹𐍃",
        "tooltip-pt-preferences": "{{GENDER:|Your}} 𐌲𐌰𐌻𐌴𐌹𐌺𐌰𐌽𐌳𐌴𐌹𐌽𐍃 𐍅𐌰𐌹𐌷𐍄𐍃",
        "tooltip-pt-watchlist": "𐍅𐌹𐌺𐍉 𐌻𐌰𐌿𐌱𐌴 𐌸𐌹𐌶𐌴𐌴𐌹 𐌰𐍄𐍅𐌰𐌹𐍃𐍄 𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐌹𐌼",
        "tooltip-pt-mycontris": "A list of {{GENDER:|your}} 𐌱𐌹𐌰𐌿𐌺𐌰𐌹𐌽𐌴𐌹𐍃 𐌱𐍂𐌿𐌺𐌾𐌰𐌽𐌳𐌹𐍃",
        "tooltip-pt-login": "𐍄𐌹𐌼𐍂𐌾𐌰𐌶𐌰 𐌳𐌿 𐌰𐍄𐌲𐌰𐌲𐌲𐌰𐌽, 𐌹𐌸 𐌽𐌹𐍃𐍄 𐍃𐌺𐌿𐌻𐌳 𐌸𐌿𐍃",
-       "tooltip-pt-logout": "ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8c¸ð\90\8c°ð\90\8c½",
+       "tooltip-pt-logout": "ð\90\8c°ð\90\8d\86ð\90\8c»ð\90\8c´ð\90\8c¹ð\90\8c¸",
        "tooltip-pt-createaccount": "𐌱𐌰𐍄𐌹𐌶𐍉 𐌹𐍃𐍄 𐌸𐌿𐍃 𐍃𐌺𐌰𐍀𐌾𐌰𐌽 𐌺𐌰𐍅𐍄𐍃𐌾𐍉𐌽, 𐌹𐌸 𐍃𐌺𐌿𐌻𐌳 𐌽𐌹𐍃𐍄",
        "tooltip-ca-talk": "𐌲𐌰𐍅𐌰𐌿𐍂𐌳𐌹 𐌱𐌹 𐌷𐌰𐌱𐌰𐌽𐌳𐌰𐌽 𐌻𐌰𐌿𐍆",
        "tooltip-ca-edit": "𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹 𐌸𐌰𐌽𐌰 𐌻𐌰𐌿𐍆",
        "tooltip-ca-addsection": "𐌰𐌽𐌰𐍃𐍄𐍉𐌳𐌴𐌹 𐌽𐌹𐌿𐌾𐌰 𐌳𐌰𐌹𐌻",
        "tooltip-ca-viewsource": "𐍃𐌰 𐌻𐌰𐌿𐍆𐍃 𐌷𐌰𐌱𐌰𐌹𐌸 𐌼𐌿𐌽𐌳. 𐌼𐌰𐌲𐍄 𐌸𐌹𐍃 𐌻𐌰𐌿𐌱𐌹𐍃 𐌼𐌿𐌽𐌳 𐍃𐌰𐌹𐍈𐌰𐌽.",
-       "tooltip-ca-history": "𐌰𐍆𐍄𐌿𐌼𐍉𐍃 𐌲𐌰𐌱𐍉𐍄𐌴𐌹𐌽𐍉𐍃 𐌸𐌹𐍃 𐌻𐌰𐌿𐌱𐌹𐍃",
+       "tooltip-ca-history": "ð\90\8c°ð\90\8d\86ð\90\8d\84ð\90\8c¿ð\90\8c¼ð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8d\89ð\90\8d\83 ð\90\8c²ð\90\8c°ð\90\8c±ð\90\8d\89ð\90\8d\84ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8d\89ð\90\8d\83 ð\90\8c¸ð\90\8c¹ð\90\8d\83 ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8c±ð\90\8c¹ð\90\8d\83",
        "tooltip-ca-protect": "𐌱𐌰𐌹𐍂𐌲𐌰 𐌸𐍉 𐍃𐌴𐌹𐌳𐍉",
-       "tooltip-ca-delete": "ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c°ð\90\8c½ ð\90\8d\83ð\90\8d\89 ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89",
+       "tooltip-ca-delete": "ð\90\8d\86ð\90\8d\82ð\90\8c°ð\90\8cµð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8c´ð\90\8c¹ ð\90\8c¸ð\90\8c°ð\90\8c¼ð\90\8c¼ð\90\8c° ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8c±ð\90\8c°",
        "tooltip-ca-move": "𐌼𐌹𐌸𐍃𐌰𐍄𐌴𐌹 𐌸𐌰𐌽𐌰 𐌻𐌰𐌿𐍆",
        "tooltip-ca-watch": "𐌱𐌹𐌰𐌹𐌰𐌿𐌺 𐌸𐌰𐌽𐌰 𐌻𐌰𐌿𐍆 𐌳𐌿 𐌸𐌴𐌹𐌽𐌰𐌹 𐍅𐌹𐍄𐌰𐍅𐌹𐌺𐍉𐌽",
-       "tooltip-search": "ð\90\8d\83ð\90\8d\89ð\90\8cºð\90\8c¾ð\90\8c°ð\90\8c½ {{SITENAME}}",
+       "tooltip-search": "ð\90\8d\83ð\90\8d\89ð\90\8cºð\90\8c´ð\90\8c¹ {{SITENAME}}",
        "tooltip-search-go": "𐌾𐌰𐌱𐌰𐌹 𐌹𐍃𐍄, 𐌲𐌰𐌲𐌲 𐌳𐌿 𐌻𐌰𐌿𐌱𐌰 𐌼𐌹𐌸 𐍃𐌰𐌼𐌹𐌽 𐌽𐌰𐌼𐌹𐌽.",
        "tooltip-search-fulltext": "𐍃𐍉𐌺𐌴𐌹 𐌻𐌰𐌿𐌱𐌰𐌽𐍃 𐌸𐌰𐌹𐌼 𐌱𐍉𐌺𐍉𐌼",
        "tooltip-p-logo": "𐌲𐌰𐍅𐌴𐌹𐍃 𐌷𐌰𐌿𐌱𐌹𐌳𐌰𐌻𐌰𐌿𐌱𐌹𐍃",
        "tooltip-ca-nstab-user": "𐍃𐌰𐌹𐍈 𐌱𐍂𐌿𐌺𐌾𐌰𐌻𐌰𐌿𐍆",
        "tooltip-ca-nstab-special": "𐍃𐌰 𐌹𐍃𐍄 𐌿𐍃𐍃𐌹𐌽𐌳𐍃 𐌻𐌰𐌿𐍆𐍃 𐌾𐌰𐌷 𐌽𐌹 𐌼𐌰𐌲 𐌹𐌽𐌼𐌰𐌹𐌳𐌾𐌰𐌳𐌰.",
        "tooltip-ca-nstab-project": "𐌰𐍄𐌰𐌿𐌲𐌴𐌹 𐍆𐌰𐌿𐍂𐌰𐍅𐌰𐌿𐍂𐍀𐌰𐌻𐌰𐌿𐍆",
-       "tooltip-ca-nstab-image": "𐍃𐌰𐌹𐍈𐌰𐌽 𐌸𐍉 𐍆𐌴𐌹𐌻𐌰𐍃𐌴𐌹𐌳𐍉𐌽",
-       "tooltip-ca-nstab-template": "𐍃𐌰𐌹𐍈𐌹𐍃 𐍆𐌰𐌿𐍂𐌰𐌼𐌴𐌻𐌴𐌹𐌽",
+       "tooltip-ca-nstab-image": "𐍃𐌰𐌹𐍈 𐍆𐌰𐌴𐌹𐌻𐌰𐌻𐌰𐌿𐍆",
+       "tooltip-ca-nstab-template": "𐍃𐌰𐌹𐍈 𐍆𐌰𐌿𐍂𐌰𐌼𐌴𐌻𐌴𐌹𐌽",
        "tooltip-ca-nstab-category": "𐍃𐌰𐌹𐍈 𐌺𐌿𐌽𐌾𐌰𐌻𐌰𐌿𐍆",
        "tooltip-save": "𐌲𐌰𐍆𐌰𐍃𐍄 𐌸𐌴𐌹𐌽𐍉𐍃 𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐌹𐌽𐍃",
-       "tooltip-preview": "ð\90\8d\86ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8d\83ð\90\8c°ð\90\8d\88ð\90\8c¹ð\90\8d\83 ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c¾ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8d\89ð\90\8d\83 ð\90\8c¸ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c°, ð\90\8c±ð\90\8c¹ð\90\8c³ð\90\8c¾ð\90\8c° ð\90\8c¸ð\90\8c¿ð\90\8cº ð\90\8c±ð\90\8d\82ð\90\8c¿ð\90\8cºð\90\8c¾ð\90\8c°ð\90\8c½ ð\90\8c¸ð\90\8c°ð\90\8d\84ð\90\8c° ð\90\8d\86ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c° ð\90\8c¼ð\90\8c´ð\90\8c»ð\90\8c¾ð\90\8c¹ð\90\8d\83!",
+       "tooltip-preview": "ð\90\8d\86ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c°ð\90\8d\83ð\90\8c°ð\90\8c¹ð\90\8d\88 ð\90\8c¸ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8d\89ð\90\8d\83 ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8d\89ð\90\8d\83. ð\90\8c±ð\90\8c¹ð\90\8c³ð\90\8c¾ð\90\8c°ð\90\8c¼ ð\90\8c¸ð\90\8c¿ð\90\8cº, ð\90\8c±ð\90\8d\82ð\90\8c¿ð\90\8cºð\90\8c´ð\90\8c¹ ð\90\8c¸ð\90\8c¹ð\90\8d\83 ð\90\8c¸ð\90\8c°ð\90\8d\84ð\90\8c° ð\90\8d\86ð\90\8d\82ð\90\8c¿ð\90\8c¼ð\90\8c¹ð\90\8d\83ð\90\8d\84 ð\90\8d\86ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c¸ð\90\8c¹ð\90\8c¶ð\90\8c´ð\90\8c¹ ð\90\8c²ð\90\8c°ð\90\8d\86ð\90\8c°ð\90\8d\83ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8d\83.",
        "tooltip-diff": "𐌰𐍄𐌰𐌿𐌲𐌴𐌹 𐍈𐌹𐌻𐌴𐌹𐌺𐍉𐍃 𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐌹𐌽𐍃 𐌲𐌰𐍄𐌰𐍅𐌹𐌳𐌴𐍃 𐌳𐌿 𐌸𐌰𐌹𐌼 𐌱𐍉𐌺𐍉𐌼",
-       "tooltip-rollback": "\"ð\90\8c°ð\90\8d\86ð\90\8d\85ð\90\8c°ð\90\8c»ð\90\8d\85ð\90\8c¾ð\90\8c°ð\90\8c½\" ð\90\8c±ð\90\8c°ð\90\8cºð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c¾ð\90\8c¹ð\90\8c¸ ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c¾ð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8d\83 ð\90\8c±ð\90\8c¹ ð\90\8c¸ð\90\8c¹ð\90\8c¶ð\90\8c°ð\90\8c¹ ð\90\8d\83ð\90\8c´ð\90\8c¹ð\90\8c³ð\90\8d\89ð\90\8c½ ð\90\8d\85ð\90\8c¹ð\90\8d\83ð\90\8c°ð\90\8c½ ð\90\8d\83ð\90\8d\89 ð\90\8c²ð\90\8c°ð\90\8c¼ð\90\8c°ð\90\8c³ð\90\8c¾ð\90\8c´ð\90\8c¹ ð\90\8c¼ð\90\8c°ð\90\8c½ð\90\8c°ð\90\8c²ð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8d\83 ð\90\8c½ð\90\8c¹ð\90\8c¿ð\90\8c¾ð\90\8c° ð\90\8c²ð\90\8c¹ð\90\8c±ð\90\8c°ð\90\8c¾ð\90\8c¹ð\90\8c½ð\90\8d\83 ð\90\8d\86ð\90\8d\82ð\90\8c°ð\90\8c¼ ð\90\8c°ð\90\8c¹ð\90\8c½ð\90\8d\83 ð\90\8cºð\90\8c»ð\90\8c¹ð\90\8cº",
+       "tooltip-rollback": "\"ð\90\8c°ð\90\8d\86ð\90\8d\85ð\90\8c°ð\90\8c»ð\90\8d\85ð\90\8c´ð\90\8c¹\" ð\90\8c²ð\90\8c°ð\90\8c½ð\90\8c°ð\90\8d\83ð\90\8c¾ð\90\8c¹ð\90\8c¸ ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c´ð\90\8c¹ð\90\8c½(ð\90\8d\89ð\90\8d\83) ð\90\8c³ð\90\8c¿ ð\90\8c¸ð\90\8c°ð\90\8c¼ð\90\8c¼ð\90\8c° ð\90\8c»ð\90\8c°ð\90\8c¿ð\90\8c±ð\90\8c° ð\90\8c°ð\90\8d\86ð\90\8d\84ð\90\8c¿ð\90\8c¼ð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8c¹ð\90\8d\83 ð\90\8c±ð\90\8c¹ð\90\8c°ð\90\8c¿ð\90\8cºð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c¹ð\90\8d\83\90\8c°ð\90\8d\86ð\90\8d\84ð\90\8c¿ð\90\8c¼ð\90\8c¹ð\90\8d\83ð\90\8d\84ð\90\8c°ð\90\8c¹ð\90\8c¶ð\90\8d\89ð\90\8d\83 ð\90\8c±ð\90\8c¹ð\90\8c°ð\90\8c¿ð\90\8cºð\90\8c°ð\90\8c½ð\90\8c³ð\90\8c¾ð\90\8d\89ð\90\8d\83 ð\90\8c¹ð\90\8c½ ð\90\8c°ð\90\8c¹ð\90\8c½ð\90\8c°ð\90\8c¼ð\90\8c¼ð\90\8c° ð\90\8d\83ð\90\8c¹ð\90\8c½ð\90\8c¸ð\90\8c°",
        "tooltip-undo": "\"𐌽𐌹𐌿𐍃𐌺𐌰𐍀𐌾𐌰𐌽\" 𐌱𐌰𐌺𐌼𐌰𐌹𐌳𐌾𐌹𐌸 𐌹𐌽𐌼𐌰𐌹𐌳𐌲𐌴𐌹𐌽𐍃 𐌾𐌰𐌷 𐌿𐍃𐌻𐌿𐌺𐍉𐌸 𐌼𐌰𐌹𐌳𐌾𐌰𐍆𐍉𐍂𐌼𐍉𐌽 𐍃𐍅𐌴 𐍆𐌰𐌿𐍂𐍃𐌰𐌹𐍈𐌰 𐌷𐌹𐍅𐌾𐌰. 𐌸𐌰𐍄𐌰 𐌻𐌴𐍄 𐌰𐌽𐌰𐌿𐌺𐌰𐌽𐌰𐌽 𐍃𐌰𐌿𐌸𐌰 𐌹𐌽 𐌹𐌽𐌽𐌰𐌷𐌰𐌻𐌳𐌰𐌰𐌽𐌲𐌰𐌱𐌰.",
        "tooltip-summary": "𐌰𐍄𐌲𐌰𐌲𐌲𐌹𐍃 𐌹𐌽𐌽𐌰𐌷𐌰𐌻𐌳𐌰𐌰𐌽𐌲𐌰𐌱𐌰 𐌼𐌰𐌿𐍂𐌲𐌾𐌰",
        "pageinfo-toolboxlink": "𐌺𐌿𐌽𐌸𐌹 𐌻𐌰𐌿𐌱𐌹𐍃",
-       "previousdiff": "â\86\90 ð\90\8c°ð\90\8d\86ð\90\8d\84ð\90\8c¿ð\90\8c¼ð\90\8c° ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c¹𐍃",
+       "previousdiff": "â\86\90 ð\90\8d\86ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c½ð\90\8c¹ð\90\8c¶ð\90\8c´ð\90\8c¹ ð\90\8c¹ð\90\8c½ð\90\8c¼ð\90\8c°ð\90\8c¹ð\90\8c³ð\90\8c´ð\90\8c¹ð\90\8c½𐍃",
        "nextdiff": "𐌽𐌹𐌿𐌾𐌹𐌶𐌴𐌹 𐌹𐌽𐌼𐌰𐌹𐌳𐌴𐌹𐌽𐍃 →",
        "file-info-size": "$1 × $2 𐍀𐌹𐌺𐍃𐌴𐌻𐌰, 𐍆𐌴𐌹𐌻𐍅𐌰𐌷𐍃𐍄𐌿𐍃: $3, 𐌼𐌹𐌼𐌴 𐌺𐌿𐌽𐌹: $4",
-       "show-big-image": "𐍆𐍂𐌿𐌼𐌹𐍃𐍄𐌰 𐌳𐌰𐍄𐌰",
+       "show-big-image": "𐍆𐍂𐌿𐌼𐌹𐍃𐍄 𐍆𐌰𐌴𐌹𐌻",
        "show-big-image-preview": "𐌼𐌹𐌺𐌹𐌻𐌴𐌹 𐌸𐌹𐌶𐍉𐍃 𐍆𐌰𐌿𐍂𐌰𐍃𐌹𐌿𐌽𐌰𐌹𐍃: $1.",
        "show-big-image-size": "$1 × $2 𐍆𐍂𐌹𐍃𐌰𐌷𐍄𐌹𐍃𐍄𐌰𐌱𐌴𐌹𐍃",
-       "ilsubmit": "Sokeiþ",
+       "ilsubmit": "𐍃𐍉𐌺𐌴𐌹",
        "metadata": "𐌿𐍆𐌰𐍂𐌳𐌰𐍄𐌰",
        "exif-colorspace": "𐍆𐌰𐍂𐍅𐌰𐍂𐌿𐌼",
        "exif-orientation-1": "𐌱𐌹 𐌱𐌹𐌿𐌷𐍄𐌾𐌰",
        "namespacesall": "𐌰𐌻𐌻",
-       "monthsall": "𐌰𐌻𐌻𐌹𐍃",
-       "imgmultigo": "Afgaggan!",
+       "monthsall": "𐌰𐌻𐌻",
+       "imgmultigo": "𐌲𐌰𐌲𐌲!",
        "table_pager_limit_submit": "Affgaggan",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|𐌲𐌰𐍅𐌰𐌿𐍂𐌳𐌾𐌰]])",
        "version-other": "Anþar",
        "tags-create-reason": "𐌳𐌿𐌸𐌸𐌴:",
        "tags-create-submit": "𐍃𐌺𐌰𐍀𐌴𐌹",
        "tags-create-warnings-below": "𐍅𐌹𐌻𐌴𐌹𐌶𐌿 𐌸𐌰𐌹𐍂𐍈𐌹𐍃𐌰𐌽 𐍃𐌺𐌰𐍀𐌾𐌰𐌽 𐌸𐌰𐍄𐌰 𐍃𐍉𐌺𐌰𐍅𐌰𐌿𐍂𐌳?",
-       "tags-delete-title": "ð\90\8c¿ð\90\8d\83ð\90\8c½ð\90\8c¹ð\90\8c¼ ð\90\8d\83ð\90\8d\89ð\90\8cºð\90\8c°𐍅𐌰𐌿𐍂𐌳",
+       "tags-delete-title": "ð\90\8c¿ð\90\8d\83ð\90\8c½ð\90\8c¹ð\90\8c¼ ð\90\8d\83ð\90\8d\89ð\90\8cºð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c¹𐍅𐌰𐌿𐍂𐌳",
        "tags-delete-explanation-initial": "𐍅𐌰𐌹𐍂𐌸𐌰𐌹𐍃 𐍃𐍉𐌺𐌰𐍅𐌰𐌿𐍂𐌳 \"$1\" 𐌿𐍃𐌽𐌹𐌼𐌰𐌽 𐌿𐍃 𐌲𐌹𐌱𐌰𐌲𐌰𐍃𐌰𐍄𐌴𐌹𐌽𐌰𐌹.",
        "tags-delete-reason": "𐌳𐌿𐌸𐌸𐌴:",
-       "tags-delete-not-found": "ð\90\8d\83ð\90\8d\89ð\90\8cºð\90\8c°𐍅𐌰𐌿𐍂𐌳 \"$1\" 𐌽𐌹𐍃𐍄.",
-       "tags-activate-title": "ð\90\8c²ð\90\8c°ð\90\8cµð\90\8c¹ð\90\8c¿ð\90\8c´ð\90\8c¹ ð\90\8d\83ð\90\8d\89ð\90\8cºð\90\8c°𐍅𐌰𐌿𐍂𐌳",
-       "tags-activate-question": "ð\90\8d\85ð\90\8c°ð\90\8c¹ð\90\8d\82ð\90\8c¸ð\90\8c°ð\90\8c¹ð\90\8d\83 ð\90\8d\83ð\90\8d\89ð\90\8cºð\90\8c°ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c³ \"$1\" ð\90\8c²ð\90\8c°ð\90\8cµð\90\8c¹ð\90\8c¿ð\90\8c¾ð\90\8c°ð\90\8c½.",
+       "tags-delete-not-found": "ð\90\8d\83ð\90\8d\89ð\90\8cºð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c¹𐍅𐌰𐌿𐍂𐌳 \"$1\" 𐌽𐌹𐍃𐍄.",
+       "tags-activate-title": "ð\90\8c²ð\90\8c°ð\90\8cµð\90\8c¹ð\90\8c¿ð\90\8c´ð\90\8c¹ ð\90\8d\83ð\90\8d\89ð\90\8cºð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c¹𐍅𐌰𐌿𐍂𐌳",
+       "tags-activate-question": "ð\90\8d\83ð\90\8d\89ð\90\8cºð\90\8c´ð\90\8c¹ð\90\8c½ð\90\8c¹ð\90\8d\85ð\90\8c°ð\90\8c¿ð\90\8d\82ð\90\8c³ \"$1\" ð\90\8c²ð\90\8c°ð\90\8cµð\90\8c¹ð\90\8c¿ð\90\8c¾ð\90\8c¹ð\90\8d\83.",
        "tags-activate-reason": "𐌳𐌿𐌸𐌸𐌴:",
        "tags-activate-not-found": "𐍃𐍉𐌺𐌰𐍅𐌰𐌿𐍂𐌳 \"$1\" 𐌽𐌹𐍃𐍄.",
        "tags-activate-submit": "𐌲𐌰𐌵𐌹𐌿𐌴𐌹",
        "tags-deactivate-reason": "𐌳𐌿𐌸𐌸𐌴:",
        "tags-deactivate-submit": "𐌿𐌽𐌲𐌰𐌵𐌹𐌿𐌴𐌹",
        "logentry-delete-delete": "$1 {{GENDER:$2|𐌿𐍃𐌽𐌰𐌼}} 𐌻𐌰𐌿𐍆 $3",
-       "logentry-move-move": "$1 {{GENDER:$2|𐌼𐌹𐌸𐍃𐌰𐍄𐌹𐌳𐌰}} 𐌻𐌰𐌿𐍆𐍃 $3 𐌳𐌿 $4",
+       "logentry-move-move": "$1 {{GENDER:$2|𐌼𐌹𐌸𐍃𐌰𐍄𐌹𐌳𐌰}} 𐌻𐌰𐌿𐍆 $3 𐌳𐌿 $4",
        "logentry-newusers-create": "𐌱𐍂𐌿𐌺𐌾𐌰𐌺𐌰𐍅𐍄𐍃𐌾𐍉 $1 𐍅𐌰𐍃 {{{{GENDER:$2|𐌲𐌰𐍃𐌺𐌰𐍀𐌰𐌽𐌰}}",
-       "rightsnone": "(ni áinshun)",
+       "rightsnone": "(𐌽𐌹)",
        "searchsuggest-search": "𐍃𐍉𐌺𐌴𐌹"
 }
index 8e09dd7..1e8f686 100644 (file)
        "november-date": "નવેમ્બર $1",
        "december-date": "ડિસેમ્બર $1",
        "pagecategories": "{{PLURAL:$1|શ્રેણી|શ્રેણીઓ}}",
-       "category_header": "શ્રેણી \"$1\"માં પાના",
+       "category_header": "શ્રેણી \"$1\" ના પાનાં",
        "subcategories": "ઉપશ્રેણીઓ",
        "category-media-header": "શ્રેણી \"$1\"માં દ્રશ્ય કે શ્રાવ્ય સભ્યો",
        "category-empty": "''આ શ્રેણીમાં હાલમાં કોઇ લેખ કે અન્ય સભ્ય નથી.''",
        "yourpasswordagain": "ગુપ્ત સંજ્ઞા (પાસવર્ડ) ફરી લખો:",
        "createacct-yourpasswordagain": "પાસવર્ડની ખાતરી કરો",
        "createacct-yourpasswordagain-ph": "પાસવર્ડ ફરીથી દાખલ કરો",
-       "remembermypassword": "આ કોમ્યૂટર પર મારી લૉગ ઇન વિગતો ધ્યાનમાં રાખો (વધુમાં વધુ $1 {{PLURAL:$1|દિવસ|દિવસ}} માટે)",
        "userlogin-remembermypassword": "મને પ્રવેશિત રાખો",
        "userlogin-signwithsecure": "સલામત જોડાણ વાપરો",
        "yourdomainname": "તમારૂં ડોમેઇન:",
        "passwordreset-emailtext-user": "વેબસાઈટ  {{SITENAME}} ના વપરાશકર્તા $1 એ તમારા {{SITENAME}} ($4) નો પાસવર્ડ રિસેટ કરવાની રજૂઆત કરી છે. આ ઈમેઈલ એડ્રેસ સાથે {{PLURAL:$3|નું ખાતું|ના ખાતા}} જોડાયેલ છે.\n\n$2\n\n{{PLURAL:$3|આ કામચલાઉ પાસવર્ડ|આ બધા કામચલાઉ પાસવર્ડ}} {{PLURAL:$5|એક દિવસ|$5 દિવસ}} માં નષ્ટ થઇ જશે. તમારે અત્યારે જ ખાતું ખોલીને નવો પાસવર્ડ સેટ કરી લેવો જોઈએ .જો કોઈ બીજા એ આ રજૂઆત કરી હોય, અથવા જો તમને પોતાનો અસલ પાસવર્ડ યાદ હોય, અને તેને બદલવા નથી માગતા, તો આ સંદેશાને જતો કરીને પોતાના અસલ પાસવર્ડ ને વાપરી શકો છો..",
        "passwordreset-emailelement": "વપરાશકર્તા નામ: \n$1\n\nકામચલાઉ પાસવર્ડ: \n$2",
        "passwordreset-emailsentemail": "પાસવર્ડ બદલવાનો ઇમેલ મોકલવામાં આવ્યો છે.",
-       "passwordreset-emailsent-capture": "પાસવર્ડ બદલવાનો ઇમેલ મોકલવામાં આવ્યો છે, જે નીચે પ્રમાણે છે.",
-       "passwordreset-emailerror-capture": "પાસવર્ડ ફરી ગોઠવવા માટેનો ઇમેલ બનાવવામાં આવ્યો છે, જે નીચે પ્રમાણે છે, પરંતુ તે {{GENDER:$2|સભ્ય}}ને મોકલવામાં નિષ્ફળ થયો છે: $1",
        "changeemail": "ઇમેલ સરનામું બદલો",
        "changeemail-header": "તમારા ખાતાનું ઇમેલ સરનામું બદલો",
        "changeemail-no-info": "બારોબાર આ પાનું જોવા માટે પ્રવેશ કરવો આવશ્યક છે.",
        "minoredit": "આ એક નાનો સુધારો છે",
        "watchthis": "આ પાનાને ધ્યાનમાં રાખો",
        "savearticle": "પાનું સાચવો",
+       "publishpage": "પાનું પ્રકાશિત કરો",
+       "publishchanges": "ફેરફારો પ્રકાશિત કરો",
        "preview": "પૂર્વાવલોકન",
        "showpreview": "ઝલક જુવો",
        "showdiff": "ફેરફારો દર્શાવો",
        "undo-failure": "વચ્ચે થયેલા અન્ય ફેરફાર થવાને કારણે આ ફેરફારો ઉલટાવી ન શકાયા",
        "undo-norev": "ફેરફાર સાચવી ન શકાયો કેમકે તે અસ્તિત્વમાં નથી અથવા તો ભૂંસી નખાયો છે.",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|talk]])એ કરેલો ફેરફાર $1 પાછો વાળ્યો",
-       "cantcreateaccounttitle": "ખાતું ખોલી શકાય તેમ નથી",
        "cantcreateaccount-text": "આ IP સરનામા  ('''$1''') પર નવા ખાતાની રચના પર   [[User:$3|$3]] દ્વારા રોક લગાવાઇ છે.\n\n\n$3 દ્વારા અપાયેલ કારણ છે ''$2''",
        "viewpagelogs": "આ પાનાનાં લૉગ જુઓ",
        "nohistory": "આ પાનાનાં ફેરફારનો ઇતિહાસ નથી.",
        "alllogstext": "{{SITENAME}} ના લોગનો સંયુક્ત વર્ણન.\nતમે લોગનો પ્રકાર,સભ્ય નામ અથવા અસરગ્રસ્ત પાના આદિ પસંદ કરી તમારી યાદિ ટૂંકાવી શકો.",
        "logempty": "લોગમાં આને મળતી કોઇ વસ્તુ નથી",
        "log-title-wildcard": "આ શબ્દો દ્વારા શરૂ થનાર શીર્ષકો શોધો",
-       "showhideselectedlogentries": "પસàª\82દàª\97à«\80નà«\80 àª²à«\8bàª\97 àª¨à«\8bàª\82ધણà«\80àª\93 àª¬àª¤àª¾àªµà«\8b/àª\9bà«\82પાવો",
+       "showhideselectedlogentries": "પસàª\82દàª\97à«\80નà«\80 àª²à«\8bàª\97 àª¨à«\8bàª\82ધણà«\80àª\93 àª¬àª¤àª¾àªµà«\8b/àª\9bà«\81પાવો",
        "allpages": "બધા પાના",
        "nextpage": "આગળનું પાનું ($1)",
        "prevpage": "પાછળનું પાનું ($1)",
        "revertmove": "પૂર્વવત",
        "delete_and_move_text": "== પાનું દૂર કરવાની જરૂર છે  ==\nલક્ષ્ય પાનું  \"[[:$1]]\" પહેલેથી અસ્તિત્વમાં છે.\nશું તમે આને હટાવીને સ્થળાંતર કરવાનો માર્ગ મોકળો કરવા માંગો છો?",
        "delete_and_move_confirm": "હા, આ પાનું હટાવો",
-       "delete_and_move_reason": "હટાવવાનું કામ આગળ વધાવવા ભૂંસી દેવાયુ \"[[$1]]\"",
+       "delete_and_move_reason": "\"[[$1]]\"થી ખસેડીને માહિતી અહિં લાવવા માટે ભૂંસી દેવાયું.",
        "selfmove": "સ્રોત ને લક્ષ્ય શીર્ષકો સમાન છે;\nપાના ને તેવા જ નામ ધરાવતા પાના પર પુનઃ સ્થાપન નહીં કરી શકાય.",
        "immobile-source-namespace": "\"$1\" નામાસ્થળમાં પાના ન ખસેડી શાકાયા",
        "immobile-target-namespace": "\"$1\" નામાસ્થળમાં પાના ન ખસેડી શાકાયા",
        "special-characters-group-thai": "થાઈ",
        "special-characters-group-lao": "લાઓ",
        "special-characters-group-khmer": "ખ્મેર",
-       "mw-widgets-titleinput-description-new-page": "પાનું અસ્તિત્વ ધરાવતું નથી.",
-       "api-error-blacklisted": "મહેરબાની કરી વધુ વિસ્તૃત અર્થ સભર શીર્ષક આપો."
+       "mw-widgets-titleinput-description-new-page": "પાનું અસ્તિત્વ ધરાવતું નથી."
 }
index c7d7099..f1cf530 100644 (file)
@@ -28,7 +28,7 @@
        "tog-showtoolbar": "Chán-sṳ phiên-siá kûng-khí-làn",
        "tog-editondblclick": "Sûng-khim phiên-siá ya̍p-mien",
        "tog-editsectiononrightclick": "Yún-hí yu-khim phiêu-thì phiên-siá thon-lo̍k",
-       "tog-watchcreations": "Kâ ngài kien-li̍p ke ya̍p-mien lâu sông-chhòn ke vùn-khien kâ-ngi̍p ngài-ke kâm-sṳ lie̍t-péu",
+       "tog-watchcreations": "Kâ ngài kien-li̍p ke ya̍p-mien lâu sông-chhòn ke tóng-on kâ-ngi̍p ngài-ke kâm-sṳ lie̍t-péu",
        "tog-watchdefault": "Chiông ngài phiên-siá ke ya̍p-mien lâu tóng-on kâ-ngi̍p ngài-ke kâm-sṳ lie̍t-péu",
        "tog-watchmoves": "Chiông ngài yì-thûng ke ya̍p-mien lâu tóng-on kâ-ngi̍p ngài-ke kâm-sṳ lie̍t-péu",
        "tog-watchdeletion": "加亻厓刪除嘅頁面撈文件入亻厓嘅監視列表",
        "edit": "Phiên-siá",
        "create": "Kien-li̍p",
        "create-local": "Sîn-chen pún-thi sot-mìn",
-       "editthispage": "Phiên-siá pún-ya̍p",
+       "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",
        "talkpagelinktext": "kâu-liù",
        "specialpage": "Thi̍t-sû ya̍p-mien",
        "personaltools": "Sṳ̂-ngìn kûng-khí",
-       "articlepage": "Chhà-khon nui-yùng ya̍p-mien",
+       "articlepage": "Khon nui-yùng ya̍p",
        "talk": "Thó-lun",
        "views": "Chhà-khon-sú",
        "toolbox": "Kûng-khí-siông",
        "youhavenewmessagesmulti": "Ngì chhai $1-tú yû sîn sêu-sit",
        "editsection": "phiên-siá",
        "editold": "phiên-siá",
-       "viewsourceold": "chhà-khon ngièn-ma",
+       "viewsourceold": "Khon ngièn-sṳ́-mâ",
        "editlink": "phiên-siá",
-       "viewsourcelink": "chhà-khon ngièn-ma",
+       "viewsourcelink": "Khon ngièn-sṳ́-mâ",
        "editsectionhint": "Phiên-siá chông-chiet: $1",
        "toc": "Muk-liu̍k",
        "showtoc": "Chán-sṳ",
        "perfcached": "下列係緩存數據,因此可能毋係最新嘅。最多{{PLURAL:$1|單淨有1嘅結果|$1嘅結果}}可用。",
        "perfcachedts": "下列係緩存數據,其最後更新時間係$1。單淨有{{PLURAL:$4|一嘅結果|$4嘅結果}}會畀顯示。",
        "querypage-no-updates": "當前禁止對邇頁面進行更新。\n邇位嘅數據將做毋得分立即重新整理。",
-       "viewsource": "Chhà-khon ngièn-ma",
-       "viewsource-title": "查看$1嘅源代碼",
+       "viewsource": "Khon ngièn-sṳ́-mâ",
+       "viewsource-title": "Khon $1 ke ngièn-sṳ́-mâ",
        "actionthrottled": "動作已經壓制",
        "actionthrottledtext": "基於反垃圾嘅考量,短時間內毋可以多次重複某操作,今下汝已經超過邇隻上限。\n請在數分鐘後再嘗試。",
        "protectedpagetext": "邇隻頁面已經分人保護以防止編輯或其他操作。",
        "group-bureaucrat-member": "行政員",
        "grouppage-bot": "{{ns:project}}:機器人",
        "grouppage-sysop": "{{ns:project}}:管理員",
-       "right-upload": "Sông-chhòn vùn-khien",
+       "right-upload": "Sông-chhòn tóng-on",
        "right-writeapi": "Sṳ́-yung siá-ngi̍p API",
        "newuserlogpage": "Sîn-kien yung-fu miàng-chhak",
        "newuserlogpagetext": "邇係一隻最近人創建用戶嘅新日誌",
        "recentchanges-legend": "Chui-khiûn kiên-kói sién-hong",
        "recentchanges-summary": "Kiên-chiûng pún wiki sông ke chui-sîn kiên-kói.",
        "recentchanges-feed-description": "Kiên-chiûng pún thin-ye̍t chhai wiki sông ke chui-khiûn kiên-kói.",
-       "recentchanges-label-newpage": "Liá-chhṳ phiên-si̍p kien-li̍p hí yit-chak sîn ya̍p-mien",
+       "recentchanges-label-newpage": "Liá-ke phiên-siá kien-li̍p sîn ya̍p",
        "recentchanges-label-minor": "Liá-he yit-chak se-mì siû-kói",
        "recentchanges-label-bot": "Liá-chhṳ phiên-siá he yù kî-hi-ngìn chin-hàng",
        "recentchanges-label-unpatrolled": "Liá-chhṳ phiên-siá hàn-mò sùn-chhà ko",
        "recentchanges-label-plusminus": "Kâi ya̍p-mien kiên-kói ke thai-séu  (vi-ngièn-chû)",
        "recentchanges-legend-heading": "<strong>Chu-yi:</strong>",
-       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (chhiáng chhâm-siòng [[Special:NewPages|sîn ya̍p-mien]])",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (chhâm-siòng [[Special:NewPages|sîn ya̍p]])",
        "rcnotefrom": "下背係從'''$2'''起嘅更改(最多展示'''$1'''):",
        "rclistfrom": "Chán-sṳ chhiùng $3 $2 yî-lòi ke sîn kiên-kói",
        "rcshowhideminor": "$1細微編寫",
        "recentchangeslinked-summary": "邇一隻特殊頁面列示''由''所分出嘅一隻頁面之鏈接到頁面嘅最近更改(或者是對於指定分類嘅成員)。\n在[[Special:Watchlist|汝嘅監視列表]]肚嘅頁面會用'''粗體'''顯示。",
        "recentchangeslinked-page": "Ya̍p-mien miàng:",
        "recentchangeslinked-to": "Chán-sṳ lièn-to só fûn-chhut ke ya̍p-mien",
-       "upload": "Sông-chhòn vùn-khien",
-       "uploadbtn": "Sông-chhòn vùn-khien",
+       "upload": "Sông-chhòn tóng-on",
+       "uploadbtn": "Sông-chhòn tóng-on",
        "reuploaddesc": "取消上載並返回上載表單",
        "uploadnologin": "還吂登入",
        "uploadnologintext": "汝必須先[[Special:UserLogin|登入]]\n正做得上傳文件。",
        "uploaderror": "上傳差錯",
        "uploadtext": "Sṳ́-yung ha-mien ke péu-tân lòi song-chhòn yung-chhai vùn-chông nui sîn-ke thù-hìn tóng-on. Yeu kiám-sṳ fe̍t-chá sêu-chhà yî-chhièn song-chhòn ke thù-phién khó-yî chin-ngi̍p [[Special:FileList|Thù-hìn chhîn-tân]], song-chhòn lâu chhù-hi chiông-chhai [[Special:Log/upload|Song-chhòn ngit-ki]] chûng ki-liu̍k. Yeu-chhai vùn-chông chûng kâ-ngi̍p thù-hiong, sṳ́-yung yî-ha hìn-sṳt ke lièn-chiap: '''<nowiki>[[{{ns:file}}:file.jpg]]</nowiki>''', '''<nowiki>[[{{ns:file}}:file.png|Thi-von vùn-sṳ]]</nowiki>''' fe̍t-he '''<nowiki>[[{{ns:media}}:file.ogg]]</nowiki>'''.",
        "uploadlogpage": "上傳日誌",
-       "uploadlogpagetext": "Yî-ha he chui-khiûn song-chhòn vùn-khien ke chúng-péu.",
+       "uploadlogpagetext": "Yî-ha he chui-khiûn sông-chhòn tóng-on ke chúng-péu.",
        "filename": "文件名",
        "filedesc": "Vùn-khien sot-mìn",
        "fileuploadsummary": "文件摘要:",
        "pager-older-n": "khiu $1-chhṳ",
        "booksources": "Mióng-lok sû-ngièn",
        "booksources-search-legend": "Chhìm-cháu mióng-lok sû-ngièn",
-       "booksources-search": "Chhìm-cháu",
+       "booksources-search": "Chhìm",
        "booksources-text": "下背係一份銷售新書或二手書嘅列表,並可能有汝尋找緊嘅書嘅進一步信息:",
        "specialloguserlabel": "Yung-fu:",
        "speciallogtitlelabel": "Phêu-thì:",
        "sp-contributions-username": "IP地址或用戶名:",
        "sp-contributions-toponly": "單淨展示最新修訂版本嘅編寫",
        "sp-contributions-submit": "搜尋",
-       "whatlinkshere": "Lièn-chiap ngi̍p ya̍p-mien",
+       "whatlinkshere": "Nâi-têu lièn to liá-têu",
        "whatlinkshere-title": "Lièn-chiap to \"$1\" ke ya̍p-mien",
        "whatlinkshere-page": "Ya̍p-mien:",
        "linkshere": "Hâ-poi ya̍p-mien lièn-chiap to <strong>[[:$1]]</strong>:",
        "tooltip-pt-mycontris": "{{GENDER:|Ngì ke}} kung-hien lie̍t-péu",
        "tooltip-pt-login": "Kien-ngi ngì tên-ngi̍p, than-he pin fî pit-sî ke",
        "tooltip-pt-logout": "Tên-chhut",
-       "tooltip-ca-talk": "Kôan-yî ya̍p-mien chang-vùn ke thó-lun",
+       "tooltip-ca-talk": "Liá ya̍p ke thó-lun",
        "tooltip-ca-edit": "Phiên-siá pún-ya̍p",
        "tooltip-ca-addsection": "Khôi-sṳ́ yit-chak sîn thon-lo̍k",
-       "tooltip-ca-viewsource": "Pún ya̍p-mien su-to pó-fu. \nNgì cho-tet chhà-khon khì ke ngièn-ma.",
-       "tooltip-ca-history": "Pún ya̍p-mien chó-siên ke siû-thin pán-pún",
+       "tooltip-ca-viewsource": "Liá-ke ya̍p-mien su-to pó-fu. \nNgì cho-tet khon khì ke ngièn-sṳ́-mâ.",
+       "tooltip-ca-history": "Liá ya̍p chó-siên ke pán-pún",
        "tooltip-ca-protect": "保護邇頁",
        "tooltip-ca-delete": "刪除邇頁",
        "tooltip-ca-undelete": "Chiông liá-ke vùn-chông fî-fu̍k to pûn chhù-hi yî-chhièn ke chhong-khóng",
        "tooltip-feed-atom": "Thin-ye̍t liá-ya̍p ke Atom ngièn",
        "tooltip-t-contributions": "Chhà-khon {{GENDER:$1|liá-chak yung-fu}} ke kung-hien lie̍t-péu",
        "tooltip-t-emailuser": "向邇隻用戶發送電子郵件",
-       "tooltip-t-upload": "Sông-chhòn vùn-khien",
+       "tooltip-t-upload": "Sông-chhòn tóng-on",
        "tooltip-t-specialpages": "Chhiòn-phu thi̍t-sû vùn-chông ke lie̍t-péu",
        "tooltip-t-print": "Pún ya̍p-mien cho-tet tá-yin ke pán-pún",
        "tooltip-t-permalink": "Liá-chak ya̍p-mien pán-pún ke ku-thin lièn-kiet",
-       "tooltip-ca-nstab-main": "Chhà-khon nui-yùng ya̍p",
+       "tooltip-ca-nstab-main": "Khon nui-yùng ya̍p",
        "tooltip-ca-nstab-user": "Chhà-khon yung-fu ya̍p-mien",
        "tooltip-ca-nstab-media": "Chhà-khon hìn-thí-chông",
        "tooltip-ca-nstab-special": "Pún ya̍p-mien he thi̍t-sû ya̍p-mien, ngì cho-m̀-tet phiên-siá pún-ya̍p",
        "newimages": "Sîn-kien thù-chhiong ke va̍k-lòng",
        "imagelisttext": "Yî-ha he on $2 phài-lie̍t ke $1-ke tóng-on lie̍t-péu.",
        "noimages": "Mò-khó kiám-sṳ thù-chhiong.",
-       "ilsubmit": "Chhìm-cháu",
+       "ilsubmit": "Chhìm",
        "bydate": "on-cheu ngit-khì",
        "sp-newimages-showfrom": "Chhiùng $1 khôi-sṳ́ hién-sṳ sîn thù-phién",
        "bad_image_list": "請按照下列格式編寫:\n\n單淨係有(以*開頭)列出嘅項目會分考慮。\n每一行嘅第一條鏈接必須係損壞文件嘅鏈接。\n然後同一行後方嘅鏈接會分看做例外,也就係講邇文件做得在哪兜頁面肚分顯示。",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|kâu-liù]])",
        "duplicate-defaultsort": "'''警告:'''默認排序關鍵字“$2”蓋過矣先前嘅默認排序關鍵字“$1”。",
        "version": "Pán-pún",
-       "fileduplicatesearch-submit": "Chhìm-cháu",
+       "fileduplicatesearch-submit": "Chhìm",
        "specialpages": "Thi̍t-sû ya̍p",
        "external_image_whitelist": " #留下撈邇行一樣嘅文字<pre>\n#在下背(//中間部份)輸入正則表達式\n#邇兜將會撈外部(已超鏈接嘅)圖片配合\n#遐兜配合上嘅會顯示成圖片,否則就單淨會顯示成鏈接\n#有#開頭嘅行會當成意見\n#大小寫並無區分\n\n#在邇行上片輸入全部正則表達式。留下撈邇行一樣嘅文字</pre>",
        "tag-filter": "[[Special:Tags|Phiêu-chhiam]] ko-lì-hi:",
        "logentry-upload-upload": "$1 {{GENDER:$2|yí-kîn sông-chhòn}} $3",
        "rightsnone": "(無)",
        "revdelete-summary": "piên-sip tsak-yêu",
-       "searchsuggest-search": "Chhìm-cháu"
+       "searchsuggest-search": "Chhìm"
 }
index 5e42b6b..9e045c3 100644 (file)
        "tagline": "מתוך {{SITENAME}}",
        "help": "עזרה",
        "search": "חיפוש",
+       "search-ignored-headings": " #<!-- יש להשאיר את השורה הזאת ללא שינוי --> <pre>\n# בשורות ההסבר בעברית בהערה הזאת יש תווי כיווניות לשם סידור סימני פיסוק.‏\n# כותרות של פסקאות שהחיפוש יתעלם מהן.‏\n# שינויים כאן ייכנסו לתוקף כשדף עם הכותרת הזאת ייכנס לאינדקס החיפוש.‏\n# אפשר לכפות מפתוח חוזר של דפים על־ידי עשיית עריכה אפסית.‏\n# התחביר הוא:‏\n# * כל דבר מהתו \"#\" עד סוף השורה הוא הערה.‏\n# * כל שורה שאינה ריקה היא כותרת שיש להתעלם ממנה, רגישות לאותיות רישיות וקטנות וכו'.‏\nהערות שוליים\nקישורים חיצוניים\nלקריאה נוספת\n #</pre> <!-- יש להשאיר את השורה הזאת ללא שינוי -->",
        "searchbutton": "חיפוש",
        "go": "הצגה",
        "searcharticle": "לדף",
        "actionthrottledtext": "כאמצעי נגד שימוש לרעה, קיימת מגבלה על ביצוע פעולה זו פעמים רבות מדי בזמן קצר, וחרגת מהמגבלה הזאת.\nנא לנסות שוב בעוד מספר דקות.",
        "protectedpagetext": "דף זה מוגן כדי למנוע עריכה ופעולות אחרות.",
        "viewsourcetext": "ב{{GENDER:|אפשרותך|אפשרותך|אפשרותכם}} לצפות בטקסט המקור של הדף ולהעתיקו.",
-       "viewyourtext": "באפשרותך לצפות בטקסט המקור של <strong>העריכות שלך</strong> בדף הזה ולהעתיקו.",
+       "viewyourtext": "ב{{GENDER:|אפשרותך|אפשרותך|אפשרותכם}} לצפות בטקסט המקור של <strong>העריכות {{GENDER:|שלך|שלך|שלכם}}</strong> בדף הזה ולהעתיקו.",
        "protectedinterface": "דף זה הוא אחד הדפים המספקים הודעות מערכת לתוכנה שמפעילה את {{SITENAME}}, והוא מוגן כדי למנוע השחתות.\nכדי להוסיף או לשנות תרגומים של הודעות מערכת עבור כל אתרי הוויקי, יש להשתמש ב־[https://translatewiki.net/ translatewiki.net], פרויקט התרגום של מדיה־ויקי.",
        "editinginterface": "<strong>אזהרה:</strong> הדף ש{{GENDER:|אתה עורך|את עורכת|אתם עורכים}} הוא אחד הדפים המספקים הודעות מערכת לתוכנה שמפעילה את {{SITENAME}}.\nשינויים בדף הזה ישפיעו על ממשק המשתמש של משתמשים אחרים באתר.",
        "translateinterface": "כדי להוסיף או לשנות תרגומים של הודעות מערכת עבור כל אתרי הוויקי, יש להשתמש ב־[https://translatewiki.net/ translatewiki.net], פרויקט התרגום של מדיה־ויקי.",
        "yourpasswordagain": "חזרה על הסיסמה:",
        "createacct-yourpasswordagain": "אימות הסיסמה",
        "createacct-yourpasswordagain-ph": "יש להקליד את הסיסמה שנית",
-       "remembermypassword": "שמירת הכניסה שלי בדפדפן הזה ({{PLURAL:$1|ליום אחד|ליומיים|ל־$1 ימים}} לכל היותר)",
        "userlogin-remembermypassword": "לזכור שנכנסתי",
        "userlogin-signwithsecure": "שימוש בחיבור מאובטח",
+       "cannotlogin-title": "לא ניתן להיכנס לחשבון",
+       "cannotlogin-text": "הכניסה לחשבון אינה אפשרית.",
        "cannotloginnow-title": "לא ניתן להיכנס עכשיו",
        "cannotloginnow-text": "הכניסה אינה אפשרית בעת שימוש ב{{GRAMMAR:תחילית|$1}}.",
+       "cannotcreateaccount-title": "לא ניתן ליצור חשבונות",
+       "cannotcreateaccount-text": "יצירת חשבונות באופן ישיר אינה מותרת באתר זה.",
        "yourdomainname": "המתחם שלך:",
        "password-change-forbidden": "אין באפשרותך לשנות סיסמאות באתר זה.",
        "externaldberror": "אירעה שגיאת אימות בבסיס הנתונים, או שאינך מורשה לעדכן את החשבון החיצוני שלך.",
        "pt-login-continue-button": "המשך כניסה לחשבון",
        "pt-createaccount": "יצירת חשבון",
        "pt-userlogout": "יציאה מהחשבון",
-       "php-mail-error-unknown": "שגיאה לא ידועה בפונקציה mail()‎ של PHP",
+       "php-mail-error-unknown": "שגיאה לא ידועה בפונקציה mail()‎ של PHP.",
        "user-mail-no-addy": "ניסיון לשלוח דוא\"ל ללא כתובת דוא\"ל.",
        "user-mail-no-body": "ניסיון לשלוח דוא\"ל עם תוכן ריק או קצר מאוד.",
        "changepassword": "שינוי סיסמה",
        "passwordreset-emailelement": "שם משתמש:\n$1\n\nסיסמה זמנית:\n$2",
        "passwordreset-emailsentemail": "אם כתובת הדואר האלקטרוני הזאת משויכת לחשבון שלך, אז יישלח דואר אלקטרוני לאיפוס הסיסמה.",
        "passwordreset-emailsentusername": "אם יש כתובת דואר אלקטרוני שמשויכת לשם המשתמש הזה, אז יישלח דואר אלקטרוני לאיפוס הסיסמה.",
-       "passwordreset-emailsent-capture": "נשלח דואר אלקטרוני לאיפוס הסיסמה, והוא מוצג להלן.",
-       "passwordreset-emailerror-capture": "נוצר דואר אלקטרוני לאיפוס הסיסמה, והוא מוצג להלן, אך שליחתו ל{{GENDER:$2|משתמש|משתמשת}} נכשלה: $1",
        "passwordreset-emailsent-capture2": "{{PLURAL:$1|דוא\"ל איפוס הסיסמה נשלח|הודעות דוא\"ל של איפוס הסיסמה נשלחו}}. {{PLURAL:$1|שם המשתמשים והסיסמה מוצגים|רשימה של שמות המשתמשים והסיסמאות מוצגת}} להלן.",
        "passwordreset-emailerror-capture2": "לא ניתן היה לשלוח דוא\"ל ל{{GENDER:$2|משתמש|משתמשת}}: $1 {{PLURAL:$3|שם המשתמש והסיסמה מוצגים|רשימה של שמות המשתמשים והסיסמאות מוצגת}} להלן.",
        "passwordreset-nocaller": "לא סופק הקורא הנדרש",
        "passwordreset-nodata": "לא סופק שם משתמש או כתובת דוא\"ל",
        "changeemail": "שינוי או הסרת כתובת דוא\"ל",
        "changeemail-header": "יש למלא את הטופס הזה כדי לשנות את כתובת הדוא\"ל שלך. אם ברצונך להימנע משיוך כתובת דוא\"ל כלשהי לחשבון שלך, יש להשאיר את שדה כתובת הדוא\"ל החדשה ריק בעת שליחת הטופס.",
-       "changeemail-passwordrequired": "יש להקליד את הסיסמה שלך כדי לאשר את השינוי.",
        "changeemail-no-info": "נדרשת כניסה לחשבון כדי לגשת לדף זה ישירות.",
        "changeemail-oldemail": "כתובת דוא\"ל נוכחית:",
        "changeemail-newemail": "כתובת דוא\"ל חדשה:",
        "editingsection": "עריכת הדף \"$1\" (פסקה)",
        "editingcomment": "עריכת הדף \"$1\" (פסקה חדשה)",
        "editconflict": "התנגשות עריכה: $1",
-       "explainconflict": "משתמש אחר שינה את הדף מאז שהתחלתם לערוך אותו.\nחלון העריכה העליון מציג את הטקסט בדף כפי שהוא כרגע.\nהשינויים שלכם מוצגים בחלון העריכה התחתון.\nעליכם למזג את השינויים שלכם לתוך הטקסט הקיים.\n<strong>רק</strong> הטקסט בחלון העריכה העליון יישמר כשתלחצו על \"{{int:savearticle}}\".",
+       "explainconflict": "משתמש אחר שינה את הדף מאז שהתחלת לערוך אותו.\nתיבת העריכה העליונה מכילה את הטקסט בדף כפי שהוא כרגע.\nהשינויים שלך מוצגים בתיבת העריכה התחתונה.\nיש למזג את השינויים שלך מתיבת העריכה התחתונה לתיבת העריכה העליונה.\n<strong>רק</strong> הטקסט בתיבת העריכה העליונה יישמר לאחר לחיצה על \"{{int:savearticle}}\".",
        "yourtext": "הטקסט שלך",
        "storedversion": "גרסה שמורה",
        "nonunicodebrowser": "'''אזהרה: הדפדפן שלך אינו תואם לתקן יוניקוד.'''\nכדי למנוע בעיות הנוצרות כתוצאה מכך ולאפשר לך לערוך דפים בבטחה, תווים שאינם ב־ASCII יוצגו בתיבת העריכה כקודים הקסדצימליים.",
-       "editingold": "<strong>אזהרה: אתם עורכים גרסה לא עדכנית של דף זה.</strong>\nאם תשמרו את הדף, כל השינויים שנעשו מאז גרסה זו יאבדו.",
+       "editingold": "<strong>אזהרה: {{GENDER:|אתה עורך|את עורכת|אתם עורכים}} גרסה ישנה של דף זה.</strong>\nאם {{GENDER:|תשמור|תשמרי|תשמרו}} את העריכה, כל השינויים שנעשו מאז גרסה זו יאבדו.",
        "yourdiff": "הבדלים",
        "copyrightwarning": "'''שימו לב:''' תרומתכם ל{{grammar:תחילית|{{SITENAME}}}} תפורסם תחת תנאי הרישיון $2 (ראו $1 לפרטים נוספים). אם אינכם רוצים שעבודתכם תהיה זמינה לעריכה על־ידי אחרים, שתופץ לעיני כול, ושאחרים יוכלו להעתיק ממנה בציון המקור – אל תפרסמו אותה פה. כמו־כן, אתם מבטיחים לנו כי כתבתם את הטקסט הזה בעצמכם, או העתקתם אותו ממקור שאינו מוגן בזכויות יוצרים. '''אל תעשו שימוש בחומר המוגן בזכויות יוצרים ללא רשות!'''",
        "copyrightwarning2": "'''שימו לב:''' תורמים אחרים עשויים לערוך או אף להסיר את תרומתכם ל{{grammar:תחילית|{{SITENAME}}}}. אם אינכם רוצים שעבודתכם תהיה זמינה לעריכה על־ידי אחרים, אל תפרסמו אותה פה. כמו־כן, אתם מבטיחים לנו כי כתבתם את הטקסט הזה בעצמכם, או העתקתם אותו ממקור שאינו מוגן בזכויות יוצרים (ראו $1 לפרטים נוספים). '''אל תעשו שימוש בחומר המוגן בזכויות יוצרים ללא רשות!'''",
        "content-model-css": "CSS",
        "content-json-empty-object": "אוביקט ריק",
        "content-json-empty-array": "מערך ריק",
+       "deprecated-self-close-category": "דפים שמשתמשים בתגיות HTML עם סגירה עצמית בלתי חוקית",
+       "deprecated-self-close-category-desc": "דף זה כולל תגיות HTML עם סגירה עצמית בלתי חוקית, כגון <code dir=\"ltr\">&lt;b/></code> או <code dir=\"ltr\">&lt;span/></code>. ההתנהגות של תגיות אלה תשתנה בקרוב לצורך תאימות עם מפרט HTML5, ולכן יש להימנע משימוש בהן בקוד ויקי.",
        "duplicate-args-warning": "<strong>אזהרה:</strong> [[:$1]] קורא לדף [[:$2]] עם יותר מערך אחד עבור הפרמטר \"$3\". ייעשה שימוש רק בערך האחרון.",
        "duplicate-args-category": "דפים שמשתמשים בפרמטרים כפולים בקריאות לתבניות",
        "duplicate-args-category-desc": "הדף מכיל קריאות לתבניות שמשתמשות בפרמטרים כפולים, כגון <code><nowiki>{{תאריך|יום=1|יום=2}}</nowiki></code> או <code><nowiki>{{שעה|חמש|1=שש}}</nowiki></code>.",
        "undo-nochange": "נראה שהעריכה כבר בוטלה.",
        "undo-summary": "ביטול גרסה $1 של [[Special:Contributions/$2|$2]] ([[User talk:$2|שיחה]])",
        "undo-summary-username-hidden": "ביטול גרסה $1 של משתמש מוסתר",
-       "cantcreateaccounttitle": "לא ניתן ליצור את החשבון",
        "cantcreateaccount-text": "אפשרות יצירת החשבונות מכתובת ה־IP הזאת (<strong>$1</strong>) נחסמה על־ידי [[User:$3|$3]].\n\nהסיבה שניתנה על־ידי $3 היא \"$2\".",
        "cantcreateaccount-range-text": "אפשרות יצירת החשבונות מכתובות IP בתוך הטווח <strong>$1</strong>, כולל כתובת ה־IP שלך (<strong>$4</strong>), נחסמה על־ידי [[User:$3|$3]].\n\nהסיבה שניתנה על־ידי $3 היא \"$2\".",
        "viewpagelogs": "הצגת יומנים עבור דף זה",
        "last": "קודמת",
        "page_first": "ראשון",
        "page_last": "אחרון",
-       "histlegend": "בחירת גרסאות להשוואה: סמנו את תיבות האפשרויות של הגרסאות המיועדות להשוואה, והקישו על Enter או על הכפתור למטה.<br />\nמקרא: '''({{int:cur}})''' = השוואה עם הגרסה הנוכחית, '''({{int:last}})''' = השוואה עם הגרסה הקודמת, '''{{int:minoreditletter}}''' = שינוי משני.",
+       "histlegend": "בחירת גרסאות להשוואה: {{GENDER:|בחר|בחרי|בחרו}} את הגרסאות ש{{GENDER:|ברצונך|ברצונך|ברצונכם}} להשוות ולאחר מכן {{GENDER:|הקש|הקישי|הקישו}} על Enter או {{GENDER:|לחץ|לחצי|לחצו}} על הכפתור שלמטה.<br />\nמקרא: <strong>({{int:cur}})</strong> = השוואה עם הגרסה הנוכחית, <strong>({{int:last}})</strong> = השוואה עם הגרסה הקודמת, <strong>{{int:minoreditletter}}</strong> = עריכה משנית.",
        "history-fieldset-title": "חיפוש בהיסטוריית הדף",
        "history-show-deleted": "עריכות מוסתרות בלבד",
        "histfirst": "הישנות ביותר",
        "search-suggest": "האם התכוונת ל: $1",
        "search-rewritten": "מוצגות התוצאות עבור $1. ניתן לחפש $2 במקום זאת.",
        "search-interwiki-caption": "מיזמי אחות",
-       "search-interwiki-default": "תוצאות מ{{GRAMMAR:תחילית|$1}}:",
+       "search-interwiki-default": "תוצאות מתוך $1:",
        "search-interwiki-more": "(עוד)",
        "search-relatedarticle": "קשור",
        "searchrelated": "קשור",
        "datedefault": "ברירת המחדל",
        "prefs-labs": "אפשרויות מעבדה",
        "prefs-user-pages": "דפי משתמש",
-       "prefs-personal": "פרטי המשתמש",
+       "prefs-personal": "פרטי ה{{GENDER:|משתמש|משתמשת}}",
        "prefs-rc": "שינויים אחרונים",
        "prefs-watchlist": "רשימת המעקב",
        "prefs-editwatchlist": "עריכת רשימת המעקב",
        "email": "דוא\"ל",
        "prefs-help-realname": "לא חובה למלא את השם האמיתי.\nאם סופק, הוא עשוי לשמש כדי לייחס לך את עבודתך.",
        "prefs-help-email": "כתובת דואר אלקטרוני היא אופציונלית, אבל היא חיונית לאיפוס הסיסמה במקרה ש{{GENDER:|תשכח|תשכחי}} אותה.",
-       "prefs-help-email-others": "×\91×\90פשר×\95ת×\9a ×\92×\9d ×\9c×\91×\97×\95ר ×\9c×\90פשר ×\9c×\9eשת×\9eש×\99×\9d ×\9c×\99צ×\95ר ×\90×\99ת×\9a ×§×©×¨ ×\91×\90×\9eצע×\95ת ×\93×\95×\90\"×\9c ×\93ר×\9a ×§×\99ש×\95ר ×\91×\93×£ ×\94×\9eשת×\9eש ×\90×\95 ×\91×\93×£ ×\94ש×\99×\97×\94 ×©×\9c×\9a.\n×\9bת×\95×\91ת ×\94×\93×\95×\90\"×\9c ×©×\9c×\9a ×\9c×\90 ×ª×\99×\97שף ×\9bש×\9eשת×\9eש×\99×\9d ×\99×\99צר×\95 ×\90×\99ת×\9a ×§×©×¨.",
+       "prefs-help-email-others": "באפשרותך גם לאפשר למשתמשים ליצור איתך קשר באמצעות דוא\"ל דרך קישור בדף המשתמש או בדף השיחה שלך.\nכתובת הדוא\"ל שלך לא תיחשף כשמשתמשים ייצרו איתך קשר.",
        "prefs-help-email-required": "כתובת דואר אלקטרוני נדרשת לכתיבה באתר.",
        "prefs-info": "מידע בסיסי",
        "prefs-i18n": "בינאום",
        "prefs-help-prefershttps": "העדפה זו תיכנס לתוקף בכניסה הבאה לחשבון.",
        "prefswarning-warning": "ביצעת שינויים בהעדפות שלך, והם עדיין לא נשמרו.\nעזיבת דף זה ללא לחיצה על \"$1\" תגרום לכך שההעדפות שלך לא יעודכנו.",
        "prefs-tabs-navigation-hint": "טיפ: ניתן להשתמש במקשי החצים הימני והשמאלי כדי לנווט בין הלשוניות ברשימת הלשוניות.",
-       "userrights": "ניהול הרשאות {{GENDER:{{BASEPAGENAME}}|משתמש|משתמשת}}",
+       "userrights": "ניהול הרשאות משתמש",
        "userrights-lookup-user": "ניהול קבוצות משתמש",
        "userrights-user-editname": "שם משתמש:",
        "editusergroup": "עריכת הקבוצות של ה{{GENDER:$1|משתמש}}",
        "right-reupload-own": "העלאת גרסאות חדשות של קבצים קיימים שהועלו על־ידי המשתמש עצמו",
        "right-reupload-shared": "דריסה מקומית של קבצים מאתר קובצי המדיה המשותף",
        "right-upload_by_url": "העלאת קבצים מכתובת אינטרנט (URL)",
-       "right-purge": "רענ×\95×\9f ×\96×\99×\9bר×\95×\9f ×\94×\9e×\98×\9e×\95×\9f ×©×\9c ×\94×\90תר ללא מעבר בדף אישור",
+       "right-purge": "× ×\99ק×\95×\99 ×\96×\99×\9bר×\95×\9f ×\94×\9e×\98×\9e×\95×\9f ×©×\9c ×\93×£ ללא מעבר בדף אישור",
        "right-autoconfirmed": "עקיפת ההגבלה על קצב הפעולות שניתן לבצע מכתובת IP מסוימת",
        "right-bot": "התייחסות לעריכות כאוטומטיות",
        "right-nominornewtalk": "ביטול שליחת התראה על הודעה חדשה למשתמש בעת עריכה משנית בדף השיחה שלו",
        "grant-group-file-interaction": "אינטראקציה עם קבצים",
        "grant-group-watchlist-interaction": "אינטראקציה עם רשימת המעקב שלך",
        "grant-group-email": "שליחת דוא\"ל",
-       "grant-group-high-volume": "×\91×\99צ×\95×\99 פעולות מרובות",
+       "grant-group-high-volume": "×\91×\99צ×\95×¢ פעולות מרובות",
        "grant-group-customization": "התאמה אישית והעדפות",
        "grant-group-administration": "ביצוע פעולות ניהול",
-       "grant-group-other": "פעילות שונה",
+       "grant-group-private-information": "גישה למידע פרטי על עצמך",
+       "grant-group-other": "פעולות שונות",
        "grant-blockusers": "חסימת משתמשים ושחרורם",
        "grant-createaccount": "יצירת חשבונות",
        "grant-createeditmovepage": "יצירה, עריכה והעברה של דפים",
        "grant-highvolume": "ביצוע עריכות מרובות",
        "grant-oversight": "החבאת משתמשים והעלמת גרסאות",
        "grant-patrol": "ניטור שינויים לדפים",
+       "grant-privateinfo": "גישה למידע פרטי",
        "grant-protect": "הפעלת הגנה על דפים והסרתה",
        "grant-rollback": "שחזור שינויים בדפים",
        "grant-sendemail": "שליחת דואר אלקטרוני למשתמשים אחרים",
        "action-applychangetags": "להחיל תגיות יחד עם שינויים",
        "action-changetags": "להוסיף או להסיר תגיות מגרסאות ומרשומות יומן",
        "action-deletechangetags": "למחוק תגיות מבסיס הנתונים",
+       "action-purge": "לנקות את זיכרון המטמון של דף זה",
        "nchanges": "{{PLURAL:$1|שינוי אחד|$1 שינויים}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|מאז ביקורך האחרון}}",
        "enhancedrc-history": "היסטוריה",
        "file-thumbnail-no": "שם הקובץ מתחיל ב־<strong>$1</strong>.\nנראה שזוהי תמונה מוקטנת (ממוזערת).\nאם התמונה בגודל מלא מצויה ברשותך, יש להעלות אותה ולא את התמונה הממוזערת; אחרת, יש לשנות את שם הקובץ.",
        "fileexists-forbidden": "קובץ בשם זה כבר קיים, ואינכם יכולים להחליף אותו.\nאם אתם עדיין מעוניינים להעלות קובץ זה, אנא חזרו לדף הקודם והעלו את הקובץ תחת שם חדש.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "קובץ בשם זה כבר קיים כקובץ משותף.\nאם אתם עדיין מעוניינים להעלות קובץ זה, אנא חזרו לדף הקודם והעלו את הקובץ תחת שם חדש.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "הקובץ שהועלה הוא העתק מדויק של הגרסה הנוכחית של <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "הקובץ שהועלה הוא העתק מדויק של {{PLURAL:$2|גרסה קודמת|גרסאות קודמות}} של <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "קובץ זה זהה {{PLURAL:$1|לקובץ הבא|לקבצים הבאים}}:",
        "file-deleted-duplicate": "קובץ זהה לקובץ זה ([[:$1]]) נמחק בעבר.\nיש לבדוק את היסטוריית המחיקה של הקובץ לפני העלאתו מחדש.",
        "file-deleted-duplicate-notitle": "קובץ זהה לקובץ זה נמחק בעבר, והכותרת שלו הועלמה.\nיש לבקש ממשתמש שיכול לראות נתונים על קבצים שהועלמו לבדוק את המצב לפני העלאת הקובץ מחדש.",
        "uploadstash-errclear": "מחיקת הקבצים נכשלה.",
        "uploadstash-refresh": "רענון רשימת הקבצים",
        "uploadstash-thumbnail": "הצגת תמונה ממוזערת",
+       "uploadstash-exception": "לא ניתן לאחסן את ההעלאה בסליק ($1): \"$2\".",
        "invalid-chunk-offset": "היסט גוש לא תקין",
        "img-auth-accessdenied": "הגישה נדחתה",
        "img-auth-nopathinfo": "PATH_INFO חסר.\nהשרת אינו מוגדר להעברת מידע זה.\nייתכן שהוא מבוסס על CGI ולכן אינו יכול לתמוך ב־img_auth.\nראו https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "sharedupload-desc-edit": "זהו קובץ מתוך $1 וניתן להשתמש בו גם במיזמים אחרים.\nניתן לערוך את התקציר שלו ב[$2 דף תיאור הקובץ] שם.",
        "sharedupload-desc-create": "זהו קובץ מתוך $1 וניתן להשתמש בו גם במיזמים אחרים.\nניתן לערוך את התקציר שלו ב[$2 דף תיאור הקובץ] שם.",
        "filepage-nofile": "לא קיים קובץ בשם זה.",
-       "filepage-nofile-link": "לא קיים קובץ בשם זה, אך באפשרותך [$1 להעלות אחד].",
+       "filepage-nofile-link": "לא קיים קובץ בשם זה, אך באפשרותך [$1 להעלותו].",
        "uploadnewversion-linktext": "העלאת גרסה חדשה של קובץ זה",
        "shared-repo-from": "מתוך $1",
        "shared-repo": "מקום איחסון משותף",
        "filerevert-submit": "שחזור",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> שוחזר ל[$4 גרסה מ־$3, $2].",
        "filerevert-badversion": "אין גרסה מקומית קודמת של הקובץ שהועלתה בתאריך המבוקש.",
+       "filerevert-identical": "הגרסה הנוכחית של הקובץ כבר זהה לגרסה שנבחרה.",
        "filedelete": "מחיקת $1",
        "filedelete-legend": "מחיקת קובץ",
        "filedelete-intro": "אתם עומדים למחוק את הקובץ <strong>[[Media:$1|$1]]</strong> יחד עם כל היסטוריית הגרסאות שלו.",
        "mostimages": "הקבצים המקושרים ביותר",
        "mostinterwikis": "הדפים עם המספר הרב ביותר של קישורי בינוויקי",
        "mostrevisions": "הדפים עם מספר העריכות הגבוה ביותר",
-       "prefixindex": "כל דפים עם התחילית",
-       "prefixindex-namespace": "כל דפים עם התחילית (במרחב השם $1)",
+       "prefixindex": "×\9b×\9c ×\94×\93פ×\99×\9d ×¢×\9d ×\94ת×\97×\99×\9c×\99ת",
+       "prefixindex-namespace": "×\9b×\9c ×\94×\93פ×\99×\9d ×¢×\9d ×\94ת×\97×\99×\9c×\99ת (×\91×\9eר×\97×\91 ×\94ש×\9d $1)",
        "prefixindex-submit": "הצגה",
        "prefixindex-strip": "הסתרת התחילית ברשימה",
        "shortpages": "דפים קצרים",
        "emailccsubject": "העתק של הודעתך למשתמש $1: $2",
        "emailsent": "הדואר נשלח",
        "emailsenttext": "הודעת הדואר האלקטרוני שלך נשלחה.",
-       "emailuserfooter": "$1 {{GENDER:$1|שלח|שלחה}} את הדוא\"ל הזה ל{{GRAMMAR:תחילית|$2}} באמצעות התכונה \"{{int:emailuser}}\" ב{{GRAMMAR:תחילית|{{SITENAME}}}}.",
+       "emailuserfooter": "$1 {{GENDER:$1|שלח|שלחה}} את הדוא\"ל הזה ל{{GENDER:$2|משתמש|משתמשת}} $2 באמצעות התכונה \"{{int:emailuser}}\" באתר {{SITENAME}}.",
        "usermessage-summary": "השארת הודעת מערכת.",
        "usermessage-editor": "שולח הודעות המערכת",
        "watchlist": "רשימת המעקב",
        "watchnologin": "לא נכנסת לחשבון",
        "addwatch": "הוספה לרשימת המעקב",
        "addedwatchtext": "הדף \"[[:$1]]\" ודף השיחה שלו נוספו ל[[Special:Watchlist|רשימת המעקב]] שלך.",
+       "addedwatchtext-talk": "הדף \"[[:$1]]\" ודף התוכן המשויך אליו נוספו ל[[Special:Watchlist|רשימת המעקב]] שלך.",
        "addedwatchtext-short": "הדף \"$1\" נוסף לרשימת המעקב.",
        "removewatch": "הסרה מרשימת המעקב",
        "removedwatchtext": "הדף \"[[:$1]]\" ודף השיחה שלו הוסרו מ[[Special:Watchlist|רשימת המעקב]] שלך.",
+       "removedwatchtext-talk": "הדף \"[[:$1]]\" ודף התוכן המשויך אליו הוסרו מ[[Special:Watchlist|רשימת המעקב]] שלך.",
        "removedwatchtext-short": "הדף \"$1\" הוסר מרשימת המעקב.",
        "watch": "מעקב",
        "watchthispage": "מעקב אחרי דף זה",
        "rollbacklinkcount-morethan": "שחזור יותר מ{{PLURAL:$1|עריכה אחת|־$1 עריכות}}",
        "rollbackfailed": "השחזור נכשל",
        "rollback-missingparam": "חסרים פרמטרים נדרשים להגשת הבקשה.",
+       "rollback-missingrevision": "לא ניתן לטעון את המידע על הגרסה.",
        "cantrollback": "לא ניתן לשחזר את העריכה;\nהתורם האחרון הוא היחיד שכתב בדף זה.",
        "alreadyrolled": "לא ניתן לשחזר את העריכה של [[User:$2|$2]] ([[User talk:$2|שיחה]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) בדף [[:$1]]; הדף כבר נערך או שוחזר.\n\nהעריכה האחרונה הייתה של [[User:$3|$3]] ([[User talk:$3|שיחה]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "תקציר העריכה היה: <em>$1</em>.",
        "rollback-success": "שוחזר מעריכות של $1 לעריכה האחרונה של $2",
        "rollback-success-notify": "שוחזר מעריכות של $1 לעריכה האחרונה של $2. [$3 הצגת שינויים]",
        "sessionfailure-title": "בעיה בחיבור",
-       "sessionfailure": "נראה שיש בעיה בחיבורכם לאתר;\nפעולתכם בוטלה כאמצעי זהירות נגד התחזות לתקשורת ממחשבכם.\nאנא חזרו לדף הקודם, העלו אותו מחדש ונסו שוב.",
+       "sessionfailure": "נראה שיש בעיה בחיבור שלך לאתר;\nפעולה זו בוטלה כאמצעי זהירות נגד התחזות לתקשורת ממחשבך.\nנא לחזור לדף הקודם, לטעון אותו מחדש ולנסות שוב.",
        "changecontentmodel": "שינוי מודל התוכן של דף",
        "changecontentmodel-legend": "שינוי מודל התוכן",
        "changecontentmodel-title-label": "שם הדף",
        "protect-expiring-local": "פוקעת ב{{GRAMMAR:תחילית|$1}}",
        "protect-expiry-indefinite": "בלתי מוגבלת בזמן",
        "protect-cascade": "הגנה על כל הדפים המוכללים בדף זה (הגנה מדורגת)",
-       "protect-cantedit": "אין באפשרותך לשנות את רמת ההגנה על דף זה כיוון שאין לך הרשאה לערוך אותו.",
+       "protect-cantedit": "אין {{GENDER:|באפשרותך|באפשרותך|באפשרותכם}} לשנות את רמת ההגנה של דף זה כיוון שאין {{GENDER:|לך|לך|לכם}} הרשאה לערוך אותו.",
        "protect-othertime": "זמן אחר:",
        "protect-othertime-op": "זמן אחר",
        "protect-existing-expiry": "זמן פקיעה נוכחי: $3, $2",
        "undeletehistorynoadmin": "דף זה נמחק.\nהסיבה למחיקה מוצגת בתקציר שלמטה, וגם פרטים על המשתמשים שערכו את הדף לפני שהוא נמחק.\nהטקסט של הגרסאות הללו זמין למפעילי מערכת בלבד.",
        "undelete-revision": "גרסה שנמחקה מהדף $1 (מ־$5, $4) מאת $3:",
        "undeleterevision-missing": "הגרסה שגויה או חסרה. ייתכן שמדובר בקישור שבור, או שהגרסה שוחזרה או הוסרה מהארכיון.",
+       "undeleterevision-duplicate-revid": "לא ניתן היה לשחזר {{PLURAL:$1|גרסה אחת|$1 גרסאות}}, כיוון ששדה <code>rev_id</code> {{PLURAL:$1|שלה|שלהן}} כבר נמצא בשימוש.",
        "undelete-nodiff": "לא נמצאה גרסה קודמת.",
        "undeletebtn": "שחזור",
        "undeletelink": "הצגה/שחזור",
        "undeletedrevisions": "{{PLURAL:$1|שוחזרה גרסה אחת|שוחזרו $1 גרסאות}}",
        "undeletedrevisions-files": "{{PLURAL:$1|גרסה אחת|$1 גרסאות}} ו{{PLURAL:$2|קובץ אחד|־$2 קבצים}} שוחזרו",
        "undeletedfiles": "{{PLURAL:$1|שוחזר קובץ אחד|שוחזרו $1 קבצים}}",
-       "cannotundelete": "השחזור נכשל:\n$1",
+       "cannotundelete": "השחזור (או חלק ממנו) נכשל:\n$1",
        "undeletedpage": "<strong>הדף $1 שוחזר</strong>\n\nראו את [[Special:Log/delete|יומן המחיקות]] לרשימה של המחיקות והשחזורים שבוצעו לאחרונה.",
        "undelete-header": "{{GENDER:|ראה|ראי|ראו}} את [[Special:Log/delete|יומן המחיקות]] לרשימה של דפים שנמחקו לאחרונה.",
        "undelete-search-title": "חיפוש דפים שנמחקו",
        "sp-contributions-newbies-sub": "עבור משתמשים חדשים",
        "sp-contributions-newbies-title": "תרומות של משתמשים חדשים",
        "sp-contributions-blocklog": "יומן חסימות",
-       "sp-contributions-suppresslog": "תרומות משתמש מועלמות",
-       "sp-contributions-deleted": "תרומות משתמש מחוקות",
+       "sp-contributions-suppresslog": "תרומות {{GENDER:$1|משתמש|משתמשת}} מועלמות",
+       "sp-contributions-deleted": "תרומות {{GENDER:$1|משתמש|משתמשת}} מחוקות",
        "sp-contributions-uploads": "העלאות",
        "sp-contributions-logs": "יומנים",
        "sp-contributions-talk": "שיחה",
        "autoblockid": "חסימה אוטומטית #$1",
        "block": "חסימת משתמש",
        "unblock": "שחרור חסימה של משתמש",
-       "blockip": "חסימת {{GENDER:$1|משתמש|משתמשת}}",
+       "blockip": "חסימת ה{{GENDER:$1|משתמש|משתמשת}}",
        "blockip-legend": "חסימת משתמש",
        "blockiptext": "השתמשו בטופס שלהלן כדי לחסום את הרשאות הכתיבה מכתובת IP או משתמש מסוימים.\nחסימות כאלה צריכות להתבצע רק כדי למנוע השחתה, ובהתאם ל[[{{MediaWiki:Policy-url}}|נהלים]].\nאנא מלאו את הסיבה הפרטנית לחסימה להלן (לדוגמה, באמצעות ציון דפים מסוימים שהשחית המשתמש).\nבאפשרותכם לחסום טווחי כתובות IP באמצעות תחביר [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; הטווח הגדול ביותר שניתן לחסום הוא <span dir=\"ltr\">/$1</span> עבור IPv4 ו־<span dir=\"ltr\">/$2</span> עבור IPv6.",
        "ipaddressorusername": "כתובת IP או שם משתמש:",
        "move-watch": "מעקב אחרי דף המקור ואחרי דף היעד",
        "movepagebtn": "העברת הדף",
        "pagemovedsub": "ההעברה הושלמה בהצלחה",
-       "movepage-moved": "הדף \"$1\" הועבר לשם \"$2\".",
+       "movepage-moved": "<strong>הדף \"$1\" הועבר לשם \"$2\"</strong>",
        "movepage-moved-redirect": "נוצרה הפניה.",
        "movepage-moved-noredirect": "יצירת ההפניה בוטלה.",
        "articleexists": "קיים כבר דף באותו שם, או שהשם שבחרת אינו תקין.\nנא לבחור שם אחר.",
        "fix-double-redirects": "עדכון הפניות לכותרת הדף המקורית",
        "move-leave-redirect": "השארת הפניה בדף המקורי",
        "protectedpagemovewarning": "'''אזהרה:''' דף זה מוגן כך שרק מפעילי מערכת יכולים להעביר אותו.\nפעולת היומן האחרונה מוצגת להלן:",
-       "semiprotectedpagemovewarning": "'''הערה:''' דף זה מוגן כך שרק משתמשים רשומים יכולים להעביר אותו.\nפעולת היומן האחרונה מוצגת להלן:",
+       "semiprotectedpagemovewarning": "<strong>הערה:</strong> דף זה מוגן כך שרק משתמשים רשומים יכולים להעביר אותו.\nפעולת היומן האחרונה מוצגת להלן:",
        "move-over-sharedrepo": "[[:$1]] כבר קיים במאגר משותף. העברת הקובץ לכותרת זו תדרוס את הקובץ המשותף.",
        "file-exists-sharedrepo": "קובץ בשם שנבחר כבר קיים כקובץ משותף.\nיש לבחור שם אחר.",
        "export": "ייצוא דפים",
        "tooltip-pt-anontalk": "דיון על העריכות שנעשו מכתובת ה־IP הזאת",
        "tooltip-pt-preferences": "ההעדפות שלך",
        "tooltip-pt-watchlist": "רשימת הדפים ש{{GENDER:|אתה עוקב|את עוקבת}} אחרי השינויים בהם",
-       "tooltip-pt-mycontris": "רש×\99×\9eת ×\94תר×\95×\9e×\95ת ×©×\9c×\9a",
+       "tooltip-pt-mycontris": "רש×\99×\9eת ×\94ער×\99×\9b×\95ת ×©×\91×\99צעת",
        "tooltip-pt-anoncontribs": "רשימת העריכות שנעשו מכתובת ה־IP הזאת",
        "tooltip-pt-login": "מומלץ להיכנס לחשבון, אך אין חובה לעשות זאת",
        "tooltip-pt-logout": "יציאה מהחשבון",
        "tooltip-n-recentchanges": "רשימת השינויים האחרונים באתר",
        "tooltip-n-randompage": "טעינת דף אקראי",
        "tooltip-n-help": "עזרה בשימוש באתר",
-       "tooltip-t-whatlinkshere": "רש×\99×\9eת ×\9b×\9c ×\94×\93פ×\99×\9d ×\94×\9eק×\95שר×\99×\9d ×\9c×\9b×\90×\9f",
+       "tooltip-t-whatlinkshere": "רש×\99×\9e×\94 ×©×\9c ×\9b×\9c ×\94×\93פ×\99×\9d ×©×\9e×\9b×\99×\9c×\99×\9d ×§×\99ש×\95ר×\99×\9d ×\9c×\93×£ ×\94×\96×\94",
        "tooltip-t-recentchangeslinked": "השינויים האחרונים שבוצעו בדפים המקושרים מדף זה",
        "tooltip-feed-rss": "הזנת RSS עבור דף זה",
        "tooltip-feed-atom": "הזנת Atom עבור דף זה",
        "tooltip-t-emailuser": "שליחת דואר אלקטרוני {{GENDER:$1|למשתמש זה|למשתמשת זו}}",
        "tooltip-t-info": "מידע נוסף על דף זה",
        "tooltip-t-upload": "העלאת קבצים",
-       "tooltip-t-specialpages": "רשימה של כל הדפים המיוחדים",
+       "tooltip-t-specialpages": "רשימה של כל הדפים המיוחדים באתר",
        "tooltip-t-print": "גרסה להדפסה של דף זה",
        "tooltip-t-permalink": "קישור קבוע לגרסה זו של הדף",
        "tooltip-ca-nstab-main": "צפייה בדף התוכן",
        "pageinfo-article-id": "מזהה הדף",
        "pageinfo-language": "שפת התוכן של הדף",
        "pageinfo-content-model": "מודל התוכן של הדף",
+       "pageinfo-content-model-change": "שינוי",
        "pageinfo-robot-policy": "איסוף על־ידי רובוטים של מנועי חיפוש",
        "pageinfo-robot-index": "מותר",
        "pageinfo-robot-noindex": "אסור",
        "monthsall": "הכול",
        "confirmemail": "אימות כתובת דוא\"ל",
        "confirmemail_noemail": "אין לך כתובת דוא\"ל תקפה המוגדרת ב[[Special:Preferences|העדפות המשתמש]] שלך.",
-       "confirmemail_text": "אתר זה דורש שתאמתו את כתובת הדוא\"ל שלכם לפני שתשתמשו בשירותי הדוא\"ל. לחצו על הכפתור למטה כדי לשלוח דוא\"ל עם קוד אימות לכתובת הדוא\"ל שהזנתם. טענו את הקישור בדפדפן שלכם כדי לאשר שכתובת הדוא\"ל תקפה.",
-       "confirmemail_pending": "ק×\95×\93 ×\90×\99×\9e×\95ת ×\93×\95×\90\"×\9c ×\9b×\91ר × ×©×\9c×\97 ×\90×\9c×\99×\9b×\9d; ×\90×\9d ×\99צרת×\9d ×\90ת ×\94×\97ש×\91×\95×\9f ×\9c×\90×\97ר×\95× ×\94, ×\99×\99ת×\9b×\9f ×©×ª×¨×¦×\95 ×\9c×\97×\9b×\95ת ×\9eספר ×\93ק×\95ת ×¢×\93 ×©×\99×\92×\99×¢ ×\9cפנ×\99 ×©×ª× ×¡×\95 ×\9c×\91קש קוד חדש.",
+       "confirmemail_text": "{{GENDER:|עליך|עלייך|עליכם}} לאמת את כתובת הדוא\"ל {{GENDER:|שלך|שלך|שלכם}} לפני ש{{GENDER:|תוכל|תוכלי|תוכלו}} להשתמש בשירותי הדוא\"ל של {{SITENAME}}.\n{{GENDER:|לחץ|לחצי|לחצו}} על הכפתור שלמטה כדי לשלוח קוד אימות לכתובת הדוא\"ל ש{{GENDER:|הזנת|הזנת|הזנתם}}.\n{{GENDER:|פתח|פִתחי|פִתחו}} את הקישור בדפדפן {{GENDER:|שלך|שלך|שלכם}} כדי לאשר שכתובת הדוא\"ל תקפה.",
+       "confirmemail_pending": "ק×\95×\93 ×\94×\90×\99×\9e×\95ת ×\9b×\91ר × ×©×\9c×\97 ×\9c×\93×\95×\90ר ×\94×\90×\9cק×\98ר×\95× ×\99 {{GENDER:|ש×\9c×\9a|ש×\9c×\9a|ש×\9c×\9b×\9d}};\n×\90×\9d {{GENDER:|×\99צרת|×\99צרת|×\99צרת×\9d}} ×\90ת ×\94×\97ש×\91×\95×\9f ×\9c×\90×\97ר×\95× ×\94, ×\9b×\93×\90×\99 ×\9c×\94×\9eת×\99×\9f ×\9eספר ×\93ק×\95ת ×¢×\93 ×©×\94×\93×\95×\90\"×\9c ×\99×\92×\99×¢ ×\9cפנ×\99 ×\91קשת קוד חדש.",
        "confirmemail_send": "לשלוח קוד אימות",
        "confirmemail_sent": "הדוא\"ל עם קוד האימות נשלח.",
        "confirmemail_oncreate": "קוד אימות דוא\"ל נשלח לכתובת הדוא\"ל שלכם. הקוד הזה אינו נדרש לכניסה, אך תצטרכו לספקו כדי להשתמש בכל תכונה מבוססת דוא\"ל באתר זה.",
-       "confirmemail_sendfailed": "{{SITENAME}} לא הצליח לשלוח לכם הודעת דוא\"ל עם קוד האימות.\nאנא בדקו שאין תווים שגויים בכתובת הדוא\"ל.\n\nתוכנת שליחת הדוא\"ל החזירה את ההודעה הבאה: $1",
+       "confirmemail_sendfailed": "אתר {{SITENAME}} לא הצליח לשלוח דוא\"ל האימות שלך.\nנא לבדוק שבכתובת הדוא\"ל שלך אין תווים בלתי־תקינים.\n\nמערכת שליחת הדוא\"ל החזירה את ההודעה הבאה: $1",
        "confirmemail_invalid": "קוד האימות שגוי. ייתכן שפג תוקפו.",
        "confirmemail_needlogin": "נדרשת $1 כדי לאמת את כתובת הדוא\"ל שלך.",
        "confirmemail_success": "כתובת הדוא\"ל שלך אושרה.\nכעת באפשרותך [[Special:UserLogin|להיכנס לחשבון שלך]] וליהנות מהאתר.",
        "tags-active-yes": "כן",
        "tags-active-no": "לא",
        "tags-source-extension": "הוגדר על־ידי הרחבה",
-       "tags-source-manual": "×\94וחל באופן ידני על־ידי משתמשים ובוטים",
+       "tags-source-manual": "×\9eוחל באופן ידני על־ידי משתמשים ובוטים",
        "tags-source-none": "אינו בשימוש כעת",
        "tags-edit": "עריכה",
        "tags-delete": "מחיקה",
        "mw-widgets-dateinput-no-date": "לא נבחר תאריך",
        "mw-widgets-titleinput-description-new-page": "הדף עדיין לא קיים",
        "mw-widgets-titleinput-description-redirect": "הפניה ל{{GRAMMAR:תחילית|$1}}",
-       "api-error-blacklisted": "נא לבחור כותרת אחרת, המתארת טוב יותר את התוכן.",
        "sessionmanager-tie": "לא ניתן לצרף מספר סוגי אימות זהות: $1.",
        "sessionprovider-generic": "התחברויות של $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "התחברויות המבוססות על עוגיות",
        "linkaccounts-submit": "קישור החשבונות",
        "unlinkaccounts": "ביטול הקישור של החשבונות",
        "unlinkaccounts-success": "קישור החשבון בוטל.",
-       "authenticationdatachange-ignored": "השינוי בנתוני האימות לא הצליח. ייתכן שלא הוגדר ספק."
+       "authenticationdatachange-ignored": "השינוי בנתוני האימות לא הצליח. ייתכן שלא הוגדר ספק.",
+       "userjsispublic": "שימו לב: משתמשים אחרים יכולים לצפות בדפי ה־JavaScript שלכם, ולכן אין לכלול בהם מידע סודי.",
+       "usercssispublic": "שימו לב: משתמשים אחרים יכולים לצפות בדפי ה־CSS שלכם, ולכן אין לכלול בהם מידע סודי."
 }
index 2dff172..cb933de 100644 (file)
        "yourpasswordagain": "कूटशब्द दुबारा लिखें:",
        "createacct-yourpasswordagain": "कूटशब्द की पुष्टि करें",
        "createacct-yourpasswordagain-ph": "कूटशब्द पुनः लिखें",
-       "remembermypassword": "इस ब्राउज़र पर मेरा लॉगिन याद रखें (अधिकतम $1 {{PLURAL:$1|दिन|दिनों}} के लिए)",
        "userlogin-remembermypassword": "मुझे लॉग्ड इन रखें",
        "userlogin-signwithsecure": "सुरक्षित कनेक्शन का प्रयोग करें",
        "cannotloginnow-title": "अभी प्रवेश नहीं हो रहा है",
        "passwordreset-emailelement": "सदस्यनाम: \n$1\n\nअस्थायी कूटशब्द: \n$2",
        "passwordreset-emailsentemail": "यदि आपका यह ईमेल आपके खाते के साथ जोड़ा गया है तो पासवर्ड बदलने का ईमेल इसमें भेज दिया गया है।",
        "passwordreset-emailsentusername": "यदि कोई ईमेल इस खाते से जुड़ी है तो पासवर्ड आपके ईमेल में भेज दिया जाएगा।",
-       "passwordreset-emailsent-capture": "नीचे दिखाया गया कूटशब्द रीसेट ई-मेल भेज दिया गया है।",
-       "passwordreset-emailerror-capture": "नीचे दृष्टित कूटशब्द रीसेट ई-मेल उत्पन्न किया गया था, परंतु उसे {{GENDER:$2|सदस्य}} को भेजना असफल रहा।\nत्रुटि: $1",
        "passwordreset-emailerror-capture2": "{{GENDER:$2|सदस्य}} को ईमेल भेजना विफल : $1 {{PLURAL:$3|सदस्य नाम और पासवर्ड|सदस्य नाम और पासवर्ड की सूची}} नीचे दिया गया है।",
        "passwordreset-invalideamil": "अवैध ईमेल पता",
        "changeemail": "ई-मेल पता परिवर्तित करें",
        "changeemail-header": "अपना ईमेल पता परिवर्तन हेतु इसे पूरा करें। यदि आप अपना वर्तमान ईमेल पता हटाना चाहते हैं, तो इसे खाली छोड़ दें और इसे भेजें।",
-       "changeemail-passwordrequired": "आपको इस परिवर्तन हेतु पासवर्ड (कूटशब्द) डालना होगा।",
        "changeemail-no-info": "इस पृष्ठ का सीधे प्रयोग करने के लिए आपको लॉग इन करना होगा।",
        "changeemail-oldemail": "वर्तमान ई-मेल पता:",
        "changeemail-newemail": "नया ई-मेल पता:",
        "minoredit": "यह एक छोटा बदलाव है",
        "watchthis": "इस पृष्ठ को ध्यानसूची में डालें",
        "savearticle": "पृष्ठ सहेजें",
+       "savechanges": "बदलाव सहेजें",
        "publishpage": "पृष्ठ प्रकाशित करें",
+       "publishchanges": "परिवर्तन प्रकाशित करें",
        "preview": "झलक",
        "showpreview": "झलक दिखाएँ",
        "showdiff": "बदलाव दिखाएँ",
        "undo-nochange": "ऐसा लगता है कि इस सम्पादन को पहले ही पूर्ववत कर दिया गया है।",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|वार्ता]]) द्वारा किए बदलाव $1 को पूर्ववत किया",
        "undo-summary-username-hidden": "छुपाए गए सदस्य द्वारा किये बदलाव $1 को पूर्ववत किया",
-       "cantcreateaccounttitle": "खाता खोल नहीं सकते",
        "cantcreateaccount-text": "इस आइ॰पी पते ('''$1''') को खाता निर्मित करने से [[User:$3|$3]] ने प्रतिबंधित किया है।\n\nइसके लिये $3 ने ''$2'' कारण दिया है।",
        "cantcreateaccount-range-text": "<strong>$1</strong> की श्रेणी में आने वाले आई॰पी पतों से, जिसमें आपका आई॰पी पता (<strong>$4</strong>) शामिल है, नए खातों की रचना [[User:$3|$3]] द्वारा अवरोधित की गयी है। \n\n$3 द्वारा दिया गया कारण है: \"$2\"",
        "viewpagelogs": "इस पृष्ठ का लॉग देखें",
        "mw-widgets-dateinput-placeholder-day": "DD-MM-YYYY",
        "mw-widgets-titleinput-description-new-page": "पृष्ठ अभी मौजूद नहीं है",
        "mw-widgets-titleinput-description-redirect": "$1 को अनुप्रेषित",
-       "api-error-blacklisted": "कृपया कोई दूसरा विवरणात्मक शीर्षक चुनें।",
        "sessionmanager-tie": "एक साथ कई अनुरोध को नहीं मिला सकता: $1",
        "sessionprovider-generic": "$1 सत्र",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "कुकी-आधारित सत्र",
index eb64a27..ef1b90a 100644 (file)
                        "Srdjan m",
                        "Teoo3",
                        "Matma Rex",
-                       "Vrhnje"
+                       "Vrhnje",
+                       "Ivi104"
                ]
        },
-       "tog-underline": "Podcrtane poveznice",
+       "tog-underline": "Podcrtavanje poveznica",
        "tog-hideminor": "Sakrij manje izmjene u nedavnim promjenama",
        "tog-hidepatrolled": "Sakrij pregledane izmjene u nedavnim promjenama",
        "tog-newpageshidepatrolled": "Sakrij pregledane stranice iz popisa novih stranica",
        "tog-watchlisthidebots": "Sakrij uređivanja botova s popisa praćenja",
        "tog-watchlisthideminor": "Sakrij manje promjene s popisa praćenja",
        "tog-watchlisthideliu": "Sakrij uređivanja prijavljenih s popisa praćenja",
-       "tog-watchlistreloadautomatically": "Automatski ponovno pokreni nadzornu listu kad god dođe do promjene filtra (potreban JavaScript)",
+       "tog-watchlistreloadautomatically": "Ponovno učitaj popis praćenja kad god dođe do promjene filtra (potreban JavaScript)",
        "tog-watchlisthideanons": "Sakrij uređivanja neprijavljenih s popisa praćenja",
        "tog-watchlisthidepatrolled": "Sakrij pregledane izmjene u popisu praćenja",
        "tog-watchlisthidecategorization": "Sakrij kategorizaciju stranica",
        "tog-ccmeonemails": "Pošalji mi kopiju e-maila kojeg pošaljem drugim suradnicima",
        "tog-diffonly": "Ne prikazuj sadržaj stranice prilikom usporedbe inačica",
        "tog-showhiddencats": "Prikaži skrivene kategorije",
-       "tog-norollbackdiff": "Izostavi razliku nakon upotrebe ukloni",
+       "tog-norollbackdiff": "Izostavi razliku nakon vraćanja",
        "tog-useeditwarning": "Upozori me kad napuštam stranicu za uređivanje bez spremanja izmjena",
        "tog-prefershttps": "Uvijek koristi sigurnu vezu kod prijave",
        "underline-always": "Uvijek",
        "readonly_lag": "Baza podataka je automatski zaključana dok se sekundarni bazni poslužitelji ne usklade s glavnim",
        "internalerror": "Pogreška sustava",
        "internalerror_info": "Interna pogrješka: $1",
+       "internalerror-fatal-exception": "Terminalna pogreška \"$1\"",
        "filecopyerror": "Ne mogu kopirati datoteku \"$1\" u \"$2\".",
        "filerenameerror": "Ne mogu preimenovati datoteku \"$1\" u \"$2\".",
        "filedeleteerror": "Ne mogu obrisati datoteku \"$1\".",
        "cannotdelete": "Ne može se obrisati stranica ili datoteka \"$1\".\nMoguće je da ju je netko drugi već obrisao.",
        "cannotdelete-title": "Brisanje stranice \"$1\" nije moguće",
        "delete-hook-aborted": "Brisanje prekinuto softverskim priključkom (hook).\nNema obrazloženja ili poruke o pogrješci.",
+       "no-null-revision": "Ne mogu stvoriti praznu izmjenu stranice \"$1\"",
        "badtitle": "Loš naslov",
        "badtitletext": "Navedeni naslov stranice nepravilan ili loše formirana interwiki poveznica.",
        "title-invalid-empty": "Naslov tražene stranice je prazan ili sadrži samo naziv prostora imena.",
        "title-invalid-interwiki": "Traženi naziv stranice sadrži interwiki poveznicu koja se ne može koristiti u nazivima.",
        "title-invalid-talk-namespace": "Traženi naziv stranice odnosi se na stranicu rasprave koja ne može postojati.",
        "title-invalid-characters": "Traženi naziv stranice sadrži nevažeće znakove: \"$1\"",
+       "title-invalid-relative": "Naslov ima relativan put. Relativni putovi u naslovu su nevažeći, jer ih web pretraživač često ne može doseći.",
+       "title-invalid-too-long": "Naslov stranice je predugačak. Naslov ne smije biti duži od $1 {{PLURAL:$1|bajt|bajtova}} u UTF-8 kodiranju.",
        "perfcached": "Sljedeći podaci su iz međuspremnika i možda nisu najsvježiji. Međuspremnik sadrži $1 {{PLURAL:$1|rezultat|rezultata}} pretraživanja.",
        "perfcachedts": "Sljedeći podaci su iz međuspremnika i posljednji puta su ažurirani u $1. Međuspremnik sadrži $4 {{PLURAL:$4|rezultat|rezultata}} pretraživanja.",
        "querypage-no-updates": "Osvježavanje ove stranice je trenutačno onemogućeno. Nove promjene neće biti vidljive.",
        "viewsource": "Vidi izvornik",
        "viewsource-title": "Vidi kôd stranice $1",
        "actionthrottled": "Uređivanje je usporeno",
-       "actionthrottledtext": "Kao anti-spam mjeru, ograničeni ste u broju ovih radnji u određenom vremenu, i trenutačno ste dosegli to ograničenje. Pokušajte opet za koju minutu.",
+       "actionthrottledtext": "Kao mjera protiv spama, ograničeni vam je broj ovih radnji u određenom vremenu, i trenutačno ste dostigli to ograničenje. Pokušajte opet za par minuta.",
        "protectedpagetext": "Ova stranica je zaključana da bi se onemogućile izmjene.",
        "viewsourcetext": "Možete pogledati i kopirati izvorni sadržaj ove stranice.",
        "viewyourtext": "Možete vidjeti i kopirati tekst <strong>vaših uređivanja</strong> na ovoj stranici.",
        "password-change-forbidden": "Ne možete promjeniti zaporku na ovom projektu.",
        "externaldberror": "Došlo je do pogreške s vanjskom autorizacijom ili Vam nije dopušteno osvježavanje vanjskog suradničkog računa.",
        "login": "Prijavi se",
+       "login-security": "Potvrdite svoj identitet",
        "nav-login-createaccount": "Prijavi se",
        "userlogin": "Prijavi se / stvori račun",
        "userloginnocreate": "Prijavi se",
        "botpasswords-label-create": "Stvori",
        "botpasswords-label-update": "Ažuriraj",
        "botpasswords-label-cancel": "Odustani",
-       "botpasswords-label-resetpassword": "Reset lozinke",
+       "botpasswords-label-resetpassword": "Ponovno postavljanje lozinke",
        "botpasswords-insert-failed": "Nije moguće dodavanje imena bota \"$1\". Možda je već dodano?",
        "resetpass_forbidden": "Lozinka ne može biti promijenjena",
        "resetpass-no-info": "Morate biti prijavljeni da biste izravno pristupili ovoj stranici.",
        "passwordreset-emailtext-user": "Suradnik $1 na {{SITENAME}} zatražio podsjetnik o pojedinostima vašeg računa za {{SITENAME}}\n($4). Sljedeći {{PLURAL:$3|račun suradnika je|računi suradnika su}} povezani s ovom e-mail adresom:\n\n$2\n\n{{PLURAL:$3|Ova privremena lozinka|Ove privremene lozinke}} će isteći u {{PLURAL:$5|jedan dan|$5 dana}}.\nTrebate se prijaviti i odabrati novu lozinku. Ukoliko je netko drugi napravio ovaj\nzahtjev, ili ako ste sjeti Vaše izvorne lozinke, a vi je više ne želite promijeniti, \nmožete zanemariti ovu poruku i nastavite koristiti staru lozinku.",
        "passwordreset-emailelement": "Suradničko ime: \n$1\n\nPrivremena lozinka: \n$2",
        "passwordreset-emailsentemail": "Ako je ova adresa povezana s Vašim suradničkim računom, na nju će biti poslan podsjetnik na zaporku.",
-       "passwordreset-emailsent-capture": "Poslan Vam je podsjetnik kao e-pošta (tekst je prikazan dolje).",
-       "passwordreset-emailerror-capture": "Napravljena je e-poruka za ponovno postavljanje zaporke (prikazana ispod), ali njeno slanje suradniku nije uspjelo: $1",
        "changeemail": "Promijeni ili izbriši e-mail adresu",
        "changeemail-header": "Promijeni adresu e-pošte računa",
        "changeemail-no-info": "Morate biti prijavljeni da biste izravno pristupili ovoj stranici.",
        "minoredit": "Ovo je manja promjena",
        "watchthis": "Prati ovu stranicu",
        "savearticle": "Sačuvaj stranicu",
+       "publishpage": "Objavi stranicu",
+       "publishchanges": "Objavi izmjene",
        "preview": "Pregled kako će stranica izgledati",
        "showpreview": "Prikaži kako će izgledati",
        "showdiff": "Prikaži promjene",
        "blockedtext": "'''Vaše suradničko ime ili IP adresa je blokirana'''\n\nBlokirao Vas je $1.\nRazlog blokiranja je sljedeći: ''$2''.\n\n* Početak blokade: $8\n* Istek blokade: $6\n* Ime blokiranog suradnika: $7\n\nMožete kontaktirati $1 ili jednog od [[{{MediaWiki:Grouppage-sysop}}|administratora]] kako bi Vam pojasnili razlog blokiranja.\n\nPrimijetite da ne možete koristiti opciju \"Pošalji mu e-poruku\" ako niste upisali valjanu adresu e-pošte u Vašim [[Special:Preferences|suradničkim postavkama]] i ako niste u tome onemogućeni prilikom blokiranja.\n\nVaša trenutačna IP adresa je $3, a oznaka bloka #$5. Molimo navedite ovaj broj kod svakog upita vezano za razlog blokiranja.",
        "autoblockedtext": "Vaša IP adresa automatski je blokirana zbog toga što ju je koristio drugi suradnik, kojeg je blokirao $1.\nRazlog blokiranja je sljedeći:\n\n:''$2''\n\n* Početak blokade: $8\n* Blokada istječe: $6\n* Ime blokiranog suradnika: $7\n\nMožete kontaktirati $1 ili jednog od [[{{MediaWiki:Grouppage-sysop}}|administratora]] kako bi Vam pojasnili razlog blokiranja.\n\nPrimijetite da ne možete rabiti opciju \"Pošalji mu e-poruku\" ako niste upisali valjanu adresu e-pošte u Vašim [[Special:Preferences|suradničkim postavkama]] i ako niste u tome onemogućeni prilikom blokiranja.\n\nVaša trenutačna IP adresa je $3, a oznaka bloka #$5. Molimo navedite ovaj broj kod svakog upita vezano za razlog blokiranja.",
        "blockednoreason": "bez obrazloženja",
-       "whitelistedittext": "Za uređivanje stranice morate se $1.",
+       "whitelistedittext": "Za uređivanje stranice molimo $1.",
        "confirmedittext": "Morate potvrditi Vašu adresu e-pošte prije nego što Vam bude omogućeno uređivanje. Molim unesite i ovjerite Vašu adresu e-pošte u [[Special:Preferences|suradničkim postavkama]].",
        "nosuchsectiontitle": "Ne mogu pronaći odlomak",
        "nosuchsectiontext": "Pokušali ste uređivati odlomak koji ne postoji.\nMožda je premješten ili izbrisan dok ste pregledavali stranicu.",
        "loginreqtitle": "Nužna prijava",
        "loginreqlink": "prijavite se",
-       "loginreqpagetext": "Morate se $1 da biste vidjeli ostale stranice.",
+       "loginreqpagetext": "Da biste vidjeli ostale stranice, molimo $1.",
        "accmailtitle": "Lozinka poslana.",
        "accmailtext": "Nova lozinka za [[User talk:$1|$1]] je poslana na $2.\n\nNakon prijave, lozinka za ovaj novi račun može biti promijenjena na stranici ''[[Special:ChangePassword|promijeni lozinku]]'' nakon prijave.",
        "newarticle": "(Novo)",
        "undo-norev": "Izmjena nije mogla biti uklonjena jer ne postoji ili je obrisana.",
        "undo-nochange": "Čini se da je uređivanje već otkazano.",
        "undo-summary": "uklanjanje izmjene $1 {{GENDER:$2|suradnika|suradnice}} [[Posebno:Doprinosi/$2|$2]] ([[Razgovor sa suradnikom:$2|razgovor]])",
-       "cantcreateaccounttitle": "Nije moguće stvoriti suradnički račun",
        "cantcreateaccount-text": "Otvaranje suradničkog računa ove IP adrese ('''$1''') blokirao/la je [[User:$3|$3]].\n\nRazlog koji je dao/la $3 je ''$2''",
        "viewpagelogs": "Vidi evidencije za ovu stranicu",
        "nohistory": "Ova stranica nema starijih izmjena.",
        "prefs-advancedwatchlist": "Napredne mogućnosti",
        "prefs-displayrc": "Prikaži opcije",
        "prefs-displaywatchlist": "Mogućnosti prikaza",
-       "prefs-diffs": "razl",
+       "prefs-diffs": "Razlike između inačica uređivanja",
        "prefs-help-prefershttps": "Ova mogućnost će stupiti na snagu kod sljedeće prijave.",
        "prefswarning-warning": "Napravili ste promjene u Vašim postavkama koje još nisu snimljene.\nAko napustite ovu stranicu bez pritiska na \"$1\", postavke neće biti ažurirane.",
        "prefs-tabs-navigation-hint": "Savjet: možete rabiti tipke sa strjelicama lijevo i desno za prebacivanje između kartica na popisu kartica.",
        "saveusergroups": "Snimi suradničke skupine",
        "userrights-groupsmember": "Član:",
        "userrights-groupsmember-auto": "Uključeni član:",
-       "userrights-groups-help": "Možete promijeniti skupine za ovog suradnika:\n* Označena kućica pokazuje skupinu kojoj suradnik pripada.\n* Neoznačena kućica pokazuje skupinu kojoj suradnik ne pripada.\n* Zvjezdica * označava skupinu koju ne možete ukloniti kad ju jednom dodate, ili obratno.",
+       "userrights-groups-help": "Možete promijeniti skupine za ovog suradnika:\n* označena kućica pokazuje skupinu kojoj suradnik pripada;\n* neoznačena kućica pokazuje skupinu kojoj suradnik ne pripada;\n* zvjezdica (*) označava skupinu koju ne možete ukloniti kad ju jednom dodate, ili obratno.",
        "userrights-reason": "Razlog:",
        "userrights-no-interwiki": "Nemate dopuštenje za uređivanje suradničkih prava na drugim wikijima.",
        "userrights-nodatabase": "Baza podataka $1 ne postoji ili nije lokalno dostupna.",
        "grant-blockusers": "Blokiraj i odblokiraj korisnike",
        "grant-createaccount": "Otvori račune",
        "grant-createeditmovepage": "Stvori, uredi i premjesti stranice",
-       "grant-editmyoptions": "Izmjeni korisničke postavke",
+       "grant-editmyoptions": "Uredi korisničke postavke",
        "grant-highvolume": "Uređivanja velikog opsega",
        "grant-basic": "Osnovna prava",
        "grant-viewdeleted": "Prikaz izbrisanih datoteka i stranica",
        "recentchangeslinked-summary": "Ova posebna stranica pokazuje nedavne promjene na povezanim stranicama (ili stranicama određene kategorije). Stranice koje su na [[Special:Watchlist|Vašem popisu praćenja]] su '''podebljane'''.",
        "recentchangeslinked-page": "Naslov stranice:",
        "recentchangeslinked-to": "Pokaži promjene na stranicama s poveznicom na ovu stranicu",
-       "recentchanges-page-added-to-category": "[[:$1]] dodana u kategoriju",
+       "recentchanges-page-added-to-category": "[[:$1]] dodano u kategoriju",
        "recentchanges-page-removed-from-category": "[[:$1]] uklonjeno iz kategorije",
        "upload": "Postavi datoteku",
        "uploadbtn": "Postavi datoteku",
        "reuploaddesc": "Vratite se u obrazac za postavljanje.",
        "upload-tryagain": "Pošalji izmijenjeni opis datoteke",
        "uploadnologin": "Niste prijavljeni",
-       "uploadnologintext": "Za postavljanje datoteka morate biti $1.",
+       "uploadnologintext": "Da biste postavljali datoteke, molimo $1.",
        "upload_directory_missing": "Mapa za datoteke ($1) nedostaje i webserver ju ne može napraviti.",
        "upload_directory_read_only": "Server ne može pisati u direktorij za postavljanje ($1).",
        "uploaderror": "Pogreška kod postavljanja",
        "unusedimages": "Nekorištene slike",
        "wantedcategories": "Tražene kategorije",
        "wantedpages": "Tražene stranice",
+       "wantedpages-summary": "Niže je popis nepostojećih stranica s najviše poveznica prema njima, osim stranica koje imaju preusmjeravanje na njih. Cijeli popis nepostojećih stranica na koje postoje preusmjeravanja može se vidjeti na [[{{#special:BrokenRedirects}}|popisu]].",
        "wantedpages-badtitle": "Nevaljani naslov kao rezultat: $1",
        "wantedfiles": "Tražene datoteke",
        "wantedfiletext-cat": "Sljedeće datoteke se rabe ali ne postoje. Datoteke iz drugih izvora mogu biti navedene iako ne postoje. Takve datoteke će biti <del>izbrisane</del> s popisa. Osim toga, stranice koje sadrže nepostojeće datoteke popisane su [[:$1|ovdje]].",
        "protectedpages-reason": "Razlog",
        "protectedpages-submit": "Prikaži stranice",
        "protectedpages-unknown-timestamp": "Nepoznato",
-       "protectedpages-unknown-performer": "Nepoznati korisnik",
+       "protectedpages-unknown-performer": "Nepoznati suradnik",
        "protectedtitles": "Zaštićeni naslovi",
        "protectedtitlesempty": "Nijedan naslov nije trenutačno zaštićen s tim parametrima.",
        "protectedtitles-submit": "Prikaži nazive",
        "activeusers-hidebots": "Sakrij botove",
        "activeusers-hidesysops": "Sakrij administratore",
        "activeusers-noresult": "Niti jedan suradnik nije nađen.",
-       "activeusers-submit": "Prikaz aktivnih sudionika",
+       "activeusers-submit": "Prikaz aktivnih suradnika",
        "listgrouprights": "Prava suradničkih skupina",
        "listgrouprights-summary": "Ovo je popis suradničkih skupina određenih na ovoj wiki, s njihovim pripadajućim pravima.\nDodatne informacije o pojedinim pravim se mogu pronaći [[{{MediaWiki:Listgrouprights-helppage}}|ovdje]].",
        "listgrouprights-key": "* <span class=\"listgrouprights-granted\">Dodijeljeno pravo</span>\n* <span class=\"listgrouprights-revoked\">Ukinuto pravo</span>",
        "rollback-success": "uklonjeno uređivanje {{GENDER:$1|suradnika|suradnice}} $1\nvraćeno na posljednju inačicu {{GENDER:$2|suradnika|suradnice}} $2.",
        "sessionfailure-title": "Prekid sesije",
        "sessionfailure": "Uočili smo problem s Vašom prijavom. Zadnja naredba nije izvršena kako bi se izbjegla zloupotreba. Molimo Vas da se u pregledniku vratite natrag na prethodnu stranicu, ponovno je učitate i zatim pokušate opet.",
-       "changecontentmodel-legend": "Promjeni model sadržaja",
+       "changecontentmodel-legend": "Promijeni model sadržaja",
        "changecontentmodel-title-label": "Naziv stranice",
        "changecontentmodel-model-label": "Novi model sadržaja",
        "changecontentmodel-reason-label": "Razlog:",
        "sp-contributions-username": "IP adresa ili suradnik:",
        "sp-contributions-toponly": "Prikaži samo najnovije izmjene",
        "sp-contributions-newonly": "Pokaži samo stranice koje je suradnik započeo",
+       "sp-contributions-hideminor": "Sakrij manje izmjene",
        "sp-contributions-submit": "Traži",
        "whatlinkshere": "Što vodi ovamo",
        "whatlinkshere-title": "Stranice koje vode na \"$1\"",
        "exif-gaincontrol-3": "Malo smanjenje",
        "exif-gaincontrol-4": "Veliko smanjenje",
        "exif-contrast-0": "Normalno",
-       "exif-contrast-1": "Meko",
-       "exif-contrast-2": "Tvrdo",
+       "exif-contrast-1": "Slabo",
+       "exif-contrast-2": "Jako",
        "exif-saturation-0": "Normalno",
        "exif-saturation-1": "Niska saturacija",
        "exif-saturation-2": "Visoka saturacija",
        "revdelete-uname-unhid": "suradničko ime je otkriveno",
        "revdelete-restricted": "primijenjeno ograničenje za administratore",
        "revdelete-unrestricted": "uklonjeno ograničenje za administratore",
+       "logentry-merge-merge": "$1 je {{GENDER:$2|spojio|spojila}} $3 s $4 (izmjene do $5)",
        "logentry-move-move": "$1 je {{GENDER:$2|premjestio|premjestila}} stranicu $3 na $4",
        "logentry-move-move-noredirect": "$1 je {{GENDER:$2|premjestio|premjestila}} stranicu $3 na $4 bez preusmjeravanja",
        "logentry-move-move_redir": "$1 je {{GENDER:$2|premjestio|premjestila}} stranicu $3 na $4 preko preusmjeravanja",
        "mw-widgets-dateinput-placeholder-month": "GGGG-MM",
        "mw-widgets-titleinput-description-new-page": "stranica još ne postoji",
        "mw-widgets-titleinput-description-redirect": "preusmjeravanje na $1",
+       "log-action-filter-block": "Vrsta blokiranja:",
+       "log-action-filter-newusers": "Način stvaranja računa:",
        "log-action-filter-upload": "Vrsta postavljanja:",
        "log-action-filter-all": "sve",
+       "log-action-filter-block-block": "blokiranje",
+       "log-action-filter-block-reblock": "promjena blokiranja",
+       "log-action-filter-block-unblock": "deblokiranje",
+       "log-action-filter-newusers-create": "stvorio anonimni suradnik",
+       "log-action-filter-newusers-create2": "stvorio registrirani suradnik",
+       "log-action-filter-newusers-autocreate": "automatski stvoren",
+       "log-action-filter-newusers-byemail": "stvoren lozinkom poslanom na e-poštu",
        "log-action-filter-upload-upload": "novo postavljanje",
        "log-action-filter-upload-overwrite": "ponovno postavljanje"
 }
index 11357f8..007eada 100644 (file)
@@ -53,7 +53,7 @@
        "tog-ccmeonemails": "Mi kopije e-mejlkow pósłać, kotrež druhim wužiwarjam pósćelu",
        "tog-diffonly": "Jenož rozdźěle pokazać (nic pak zbytny wobsah)",
        "tog-showhiddencats": "Schowane kategorije pokazać",
-       "tog-norollbackdiff": "Rozdźěl po wróćostajenju zanjechać",
+       "tog-norollbackdiff": "Rozdźěl po wróćostajenju njepokazać",
        "tog-useeditwarning": "Warnować, hdyž so wobdźěłowanska strona z njeskładowanymi změnami wopušća",
        "tog-prefershttps": "Po přizjewjenju přeco wěsty zwisk wužiwać",
        "underline-always": "Přeco",
        "morenotlisted": "Tuta lisćina dospołna njeje.",
        "mypage": "Strona",
        "mytalk": "Diskusija",
-       "anontalk": "Diskusijna strona tuteje IP.adresy",
+       "anontalk": "Diskusija",
        "navigation": "Nawigacija",
        "and": "&#32;a",
        "qbfind": "Namakać",
        "tagline": "z {{GRAMMAR:genitiw|{{SITENAME}}}}",
        "help": "Pomoc",
        "search": "Pytać",
+       "search-ignored-headings": " #<!-- njezměń tutu linku --> <pre>\n# Nadpisma, kotrež pytanje ignoruje.\n# Tute změny budu so wuskutkować, po tym zo strona bě so indikowała.\n# Móžeš indikowanje stronow wunuzować, přewjedujo prózdnu změnu.\n# Syntaksa:\n#   * Wšitko, štož znamješku \"#\" hač do kónca linki slěduje, je komentar\n#   * Kózda njeprózdna linka je eksaktny titul, kotryž dyrbi so ignorować\nŽórła\nEksterne wotkazy\nHlej tež\n #</pre> <!-- njezměń tutu linku -->",
        "searchbutton": "Pytać",
        "go": "Pytać",
        "searcharticle": "Pytać",
        "laggedslavemode": "'''Kedźbu:''' Je móžno, zo strona žane zaktualizowanja njewobsahuje.",
        "readonly": "Datowa banka je zawrjena",
        "enterlockreason": "Zapodaj přičinu za zawrjenje a přibližny čas, hdy budźe zawrjenje zběhnjene",
-       "readonlytext": "Datowa banka je tuchwilu za nowe zapiski a druhe změny zawrjena, najskerje wothladowanskich dźěłow dla; po jich zakónčenju budźe wšitko zaso normalne.\n\nAdministrator, kiž je datowu banku zawrěł, je jako přičinu podał: $1",
+       "readonlytext": "Datowa banka je tuchwilu za nowe zapiski a druhe změny zawrjena, najskerje wothladowanskich dźěłow dla; po jich zakónčenju budźe wšitko zaso normalne.\n\nSystemowy administrator, kiž je datowu banku zawrěł, je slědowacu přičinu podał: $1",
        "missing-article": "Datowa banka njenamaka tekst strony z mjenom \"$1\" $2, kotryž dyrbjał so namakać.\n\nTo so zwjetša zawinuje, hdyž so njepłaćiwa změna abo zapisk stawiznow na stronu wotkazuje, kotraž bu wušmórnjena.\n\nJeli to njetrjechi, sy najskerje programowy zmylk w softwarje namakał.\nZdźěl to prošu [[Special:ListUsers/sysop|admininistratorej]] podawajo wotpowědny URL.",
        "missingarticle-rev": "(Wersijowe čisło: $1)",
        "missingarticle-diff": "(Rozdźěl: $1, $2)",
        "viewsource": "Žórłowy tekst",
        "viewsource-title": "Žórłowy tekst za $1 sej wobhladać",
        "actionthrottled": "Akcije wobmjezowane",
-       "actionthrottledtext": "Jako připrawa přećiwo spamej, je častosć wuwjedźenja tuteje akcije w krótkej dobje wobmjezowana a ty sy tutón limit překročił. Prošu spytaj za něšto mjeńšiny hišće raz.",
+       "actionthrottledtext": "Jako naprawa přećiwo znjewužiwanju, je ličba wuwjedźenjow tuteje akcije w krótkej dobje wobmjezowana a ty sy tutón limit překročił. Prošu spytaj za něšto mjeńšin hišće raz.",
        "protectedpagetext": "Tuta strona je přećiwo wobdźěłowanju abo druhim akcijam škitana.",
-       "viewsourcetext": "Móžeš sej žórłowy tekst tuteje strony wobhladać a jón kopěrować:",
-       "viewyourtext": "Móžeš sej žórłowy tekst '''swojich změnow''' wobhladać a do slědowaceje strony kopěrować:",
+       "viewsourcetext": "Móžeš sej žórłowy tekst tuteje strony wobhladać a jón kopěrować.",
+       "viewyourtext": "Móžeš sej žórłowy tekst <strong>swojich změnow</strong> wobhladać a do slědowaceje strony kopěrować.",
        "protectedinterface": "Tuta strona wobsahuje tekst za wužiwarski powjerch softwary na tutym wikiju a je škitana, zo by so znjewužiwanje zadźěwało.\nZo by přełožki za wšě wikije přidał abo změnił, wužij prošu [https://translatewiki.net/ translatewiki.net], projekt MediaWiki za lokalizaciju.",
        "editinginterface": "<strong>Warnowanje:</strong> Wobdźěłuješ stronu, kotraž so wužiwa, zo by tekst za wužiwarski powjerch softwary k dispoziciji stajiła. Změny na stronje  wuskutkuja so na napohlad wužiwarskeho powjercha za druhich wužiwarjow na tutym wikiju.",
        "translateinterface": "Zo by přełožki za wšě wikije přidał abo změnił, wužiwajće prošu [https://translatewiki.net/ translatewiki.net], lokalizaciski projekt MediaWiki.",
-       "cascadeprotected": "Tuta strona je za wobdźěłowanje zawrjena, dokelž je w {{PLURAL:$1|slědowacej stronje|slědowacymaj stronomaj|slědowacych stronach|slědowacych stronach}} zapřijata, {{PLURAL:$1|kotraž je|kotrejž stej|kotrež su|kotrež su}} přez kaskadowu opciju {{PLURAL:$1|škitana|škitanej|škitane|škitane}}:\n$2",
+       "cascadeprotected": "Tuta strona je za wobdźěłowanje zawrjena, dokelž je w {{PLURAL:$1|slědowacej stronje|slědowacymaj stronomaj|slědowacych stronach}} zapřijata, {{PLURAL:$1|kotraž je|kotrejž stej|kotrež su}} přez kaskadowu opciju {{PLURAL:$1|škitana|škitanej|škitane}}:\n$2",
        "namespaceprotected": "Nimaš dowolnosć, zo by stronu w mjenowym rumje '''$1''' wobdźěłał.",
        "customcssprotected": "Nimaš prawo, zo by tutu CSS-stronu wobdźěłał, dokelž wosobinske nastajenja druheho wužiwarja wobsahuje.",
        "customjsprotected": "Nimaš prawo, zo by tutu JavaScript-stronu wobdźěłał, dokelž wosobinske nastajenja druheho wužiwarja wobsahuje.",
        "mypreferencesprotected": "Nimaš prawo swoje nastajenja wobdźěłać.",
        "ns-specialprotected": "Specialne strony njedadźa so wobdźěłać.",
        "titleprotected": "Tutón titul bu přećiwo wutworjenju přez [[User:$1|$1]] škitany.\nPodata přičina je <em>$2</em>.",
-       "filereadonlyerror": "Njeje móžno dataju \"$1\" změnić, dokelž datajowy repozitorij \"$2\" je jenož čitajomny.\n\nAdministrator, kiž je jón zawrěł, je tule přičinu podał: \"$3\".",
+       "filereadonlyerror": "Njeje móžno dataju \"$1\" změnić, dokelž datajowy repozitorij \"$2\" je jenož čitajomny.\n\nSystemowy administrator, kiž je jón zawrěł, je tule přičinu podał: \"$3\".",
        "invalidtitle-knownnamespace": "Njepłaćiwy titul z mjenowym rumom \"$2\" a tekstom \"$3\"",
        "invalidtitle-unknownnamespace": "Njepłaćiwy titul z njeznatym mjenowym rumom $1 a tekstom \"$2\"",
        "exception-nologin": "Njejsy přizjewjeny",
        "nocookieslogin": "{{SITENAME}} wužiwa placki za přizjewjenje wužiwarjow.\nSy placki znjemóžnił. \nProšu zmóžń je a spytaj hišće raz.",
        "nocookiesfornew": "Wužiwarske konto njeje so załožiło, dokelž njemóžachmy jeho žórło wobkrućić.\nPřeswědč so, zo placki su zmóžnjene, ačitaj tutu stronu znowa a spytaj hišće raz.",
        "noname": "Njejsy płaćiwe wužiwarske mjeno podał.",
-       "loginsuccesstitle": "Přizjewjenje wuspěšne",
+       "loginsuccesstitle": "Přizjewjeny",
        "loginsuccess": "'''Sy nětko jako \"$1\" w {{GRAMMAR:lokatiw|{{SITENAME}}}} {{GENDER:|přizjewjeny|přizjewjena|přizjewjene}}.'''",
-       "nosuchuser": "Njeje wužiwar z mjenom \"$1\".\nWužiwarske mjena wobkedźbuja wulkopisanje.\nPřepruwuj swój prawopis abo [[Special:CreateAccount|wutwor nowe konto]].",
+       "nosuchuser": "Njeje wužiwar z mjenom \"$1\".\nWužiwarske mjena dźiwaja na wulkopisanje.\nPřepruwuj swój prawopis abo [[Special:CreateAccount|załož nowe konto]].",
        "nosuchusershort": "Wužiwarske mjeno „$1” njeeksistuje. Prošu skontroluj prawopis.",
        "nouserspecified": "Dyrbiš wužiwarske mjeno podać",
        "login-userblocked": "Tutón wužiwar je zablokowany. Přizjewjenje njedowolene.",
        "noemail": "Za wužiwarja \"$1\" žana e-mejlowa adresa podata njeje.",
        "noemailcreate": "Dyrbiš płaćiwu e-mejlowa adresu podać",
        "passwordsent": "Nowe hesło bu na e-mejlowu adresu zregistrowanu za wužiwarja „$1” pósłane.\nProšu přizjew so znowa, po tym zo sy je přijał.",
-       "blocked-mailpassword": "Twoja IP-adresa je přećiwo wobdźěłowanju zablokowana, a tohodla njeje dowolene, funkciju za wobnowjenje hesłow wužiwać, zo by znjewužiwanju zadźěwało.",
+       "blocked-mailpassword": "Twoja IP-adresa je přećiwo wobdźěłowanju zablokowana. Zo by znjewužiwanju zadźěwało, njeje dowolene, funkciju za wobnowjenje hesłow z tuteje IP-adresy wužiwać.",
        "eauthentsent": "Wobkrućenska e-mejlka je so na podatu e-mejlowu adresu pósłała.\nPrjedy hač so druha e-mejlka na konto pósćele, dyrbiš so po instrukcijach w e-mejlce měć, zo by wobkrućił, zo konto woprawdźe tebi słuša.",
        "throttled-mailpassword": "E-mejl za anulowanje hesło je so za {{PLURAL:$1|poslednju hodźinu|poslednjej $1 hodźinje|poslednje $1 hodźiny|poslednich $1 hodźin}} pósłała. Zo by znjewužiwanju zadźěwało, so jenož jedna e-mejl za anulowanje hesła na {{PLURAL:$1|hodźinu|$1 hodźinje|$1 hodźiny|$1 hodźin}} pósćele.",
        "mailerror": "Zmylk při słanju e-mejlki: $1",
        "createaccount-title": "Wutworjenje wužiwarskeho konta za {{SITENAME}}",
        "createaccount-text": "Něchtó je wužiwarske konto za twoju e-mejlowu adresu na {{SITENAME}} ($4) z mjenom \"$2\" z hesłom \"$3\" wutworił. Ty měł so nětko přizjewić a swoje hesło změnić.\n\nMóžeš tutu zdźělenku ignorować, jeli so wužiwarske konto zmylnje wutworiło.",
        "login-throttled": "Sy přehusto spytał so přizjewić. Počakaj prošu $1, prjedy hač hišće raz spytaš.",
-       "login-abort-generic": "Twoje přizjewjenje njebě wuspěšne - přetorhnjene",
+       "login-abort-generic": "Twoje přizjewjenje je so nimokuliło - přetorhnjene",
        "login-migrated-generic": "Waše konto je so přesunyło, a waše wužiwarske mjeno na tutym wikiju hižo njeeksistuje.",
        "loginlanguagelabel": "Rěč: $1",
        "suspicious-userlogout": "Twoje naprašowanje za wotzjewjenje bu wotpokazane, dokelž zda so, jako by so přez wobškodźeny wobhladowak abo pufrowacy proksy pósłało",
        "newpassword": "Nowe hesło:",
        "retypenew": "Nowe hesło wospjetować:",
        "resetpass_submit": "Hesło posrědkować a so přizjewić",
-       "changepassword-success": "Twoje hesło je so wuspěšnje změniło!",
+       "changepassword-success": "Twoje hesło je so změniło!",
        "changepassword-throttled": "Sy přehusto spytał so přizjewić. Počakaj prošu $1, prjedy hač hišće raz spytaš.",
        "resetpass_forbidden": "Hesła njedadźa so změnić.",
        "resetpass-no-info": "Dyrbiš so přizjewić, zo by direktny přistup na tutu stronu měł.",
        "resetpass-submit-loggedin": "Hesło změnić",
        "resetpass-submit-cancel": "Přetorhnyć",
-       "resetpass-wrong-oldpass": "Njepłaćiwe nachwilne abo aktualne hesło.\nSnano sy swoje hesło hižo wuspěšnje změnił abo nowe nachwilne hesło požadał.",
+       "resetpass-wrong-oldpass": "Njepłaćiwe nachwilne abo aktualne hesło.\nSnano sy swoje hesło hižo změnił abo nowe nachwilne hesło požadał.",
        "resetpass-recycled": "Prošu staj swoje hesło na druhe hesło hač twoje aktualne hesło.",
        "resetpass-temp-emailed": "Sy so z nachwilnym e-mejlowym kodom přizjewił.\nZo by přizjewjenje skónčił, dyrbiš tu nowe hesło postajić:",
        "resetpass-temp-password": "Nachwilne hesło:",
        "passwordreset-emailtext-ip": "Něchtó (najskerje ty, z IP-adresu $1) je anulowanje hesła za {{GRAMMAR:akuzatiw|{{SITENAME}}}} požadał ($4).  {{PLURAL:$3|Slědowace wužiwarske konto je|Slědowacej wužiwarskej konće stej|Slědowace wužiwarske konta su}} z tutej e-mejlowej adresu {{PLURAL:$3|zwjazane|zwjazanej|zwjazane}}:\n\n$2\n\n{{PLURAL:$3|Tute nachwilne hesło spadnje|Tutej nachwilnej hesle spadnjetej|Tute nachwilne hesła spadnu}} za {{PLURAL:$5|jedyn dźeń|$5 dnjej|$5 dny|$5 dnjow}}.\nTy měł so nětko přizjewić a nowe hesło wubrać. Jeli něchtó druhi je tute naprašowanje pósłał, abo jeli sy so zaso na prěnjotne hesło dopomnił a wjace nochceš jo změnić, móžeš tutu zdźělenku ignorować a swoje stare hesło dale wužiwać.",
        "passwordreset-emailtext-user": "Wužiwar $1 na {{GRAMMAR:lokatiw|{{SITENAME}}}} je anulowanje twojeho hesła za {{GRAMMAR:akuzatiw|{{SITENAME}}}} požadał ($4).  {{PLURAL:$3|Slědowace wužiwarske konto je|Slědowacej wužiwarskej konće stej|Slědowace wužiwarske konta su}} z tutej e-mejlowej adresu {{PLURAL:$3|zwjazane|zwjazanej|zwjazane}}:\n\n$2\n\n{{PLURAL:$3|Tute nachwilne hesło spadnje|Tutej nachwilnej hesle spadnjetej|Tute nachwilne hesła spadnu}} za {{PLURAL:$5|jedyn dźeń|$5 dnjej|$5 dny|$5 dnjow}}.\nTy měł so nětko přizjewić a nowe hesło wubrać. Jeli něchtó druhi je tute naprašowanje pósłał, abo jeli sy so zaso na prěnjotne hesło dopomnił a wjace nochceš jo změnić, móžeš tutu zdźělenku ignorować a swoje stare hesło dale wužiwać.",
        "passwordreset-emailelement": "Wužiwarske mjeno: \n$1\n\nNachwilne hesło: \n$2",
-       "passwordreset-emailsentemail": "E-mejl za anulowanje hesło je so pósłała.",
-       "passwordreset-emailsent-capture": "E-mejl za anulowanje hesła je so pósłała, kotraž so deleka pokazuje.",
-       "passwordreset-emailerror-capture": "E-mejl za anulowanje hesła je so wutworiła, kotraž so deleka pokazuje, ale słanje {{GENDER:$2|wužiwarjej|wužiwarce}} je so njeporadźiło: $1",
+       "passwordreset-emailsentemail": "Jeli tuta e-mejlowa adresa je z wašim kontom zwjazana, so mejlka za wróćostajenje hesła pósćele.",
        "changeemail": "E-mejlowu adresu změnić abo wotstronić",
-       "changeemail-header": "Kontowu e-mejlowu adresu změnić",
+       "changeemail-header": "Wupjelń tutón formular, zo by swoju e-mejlowu adresu změnił. Jeli chceš zwisk e-mejloweje adresy ze swijim kontom wotstronić,  wostaj polo e-mejloweje adresy prózdne, hdyž formular preč sćeleš.",
        "changeemail-no-info": "Dyrbiš so přizjewić, zo by direktny přistup na tutu stronu měł.",
        "changeemail-oldemail": "Aktualna e-mejlowa adresa:",
        "changeemail-newemail": "Nowa e-mejlowa adresa:",
        "sig_tip": "Twoja signatura z časowym kołkom",
        "hr_tip": "Wodoruna linija (zrědka wužiwać!)",
        "summary": "Zjeće:",
-       "subject": "Tema/Nadpismo:",
+       "subject": "Tema:",
        "minoredit": "Snadna změna",
        "watchthis": "Stronu wobkedźbować",
        "savearticle": "Stronu składować",
+       "publishpage": "Stronu wozjewić",
+       "publishchanges": "Změny wozjewić",
        "preview": "Přehlad",
        "showpreview": "Přehlad pokazać",
        "showdiff": "Změny pokazać",
        "anonpreviewwarning": "''Njejsy přizjewjeny. Składowanje přenošuje twoju IP-adresu do wobdźěłowanskeje historije tuteje strony.''",
        "missingsummary": "'''Kedźbu:''' Njejsy žane zjeće zapodał. Jeli hišće raz na \"{{int:savearticle}}\" kliknješ, budźe so twoja změna bjez njeho składować.",
        "missingcommenttext": "Prošu zapodaj komentar.",
-       "missingcommentheader": "'''Kedźbu:''' Njejsy temu/nadpis za tutón komentar podał. Jeli na „{{int:savearticle}}” kliknješ, składuje so twoja změna bjez temy/nadpisa.",
+       "missingcommentheader": "<strong>Kedźbu:</strong> Njejsy temu za tutón komentar podał. Jeli na „{{int:savearticle}}” kliknješ, składuje so twoja změna bjez temy.",
        "summary-preview": "Přehlad zjeća:",
-       "subject-preview": "Přehlad temy/napisma:",
+       "subject-preview": "Přehlad temy:",
        "blockedtitle": "Wužiwar je zablokowany",
        "blockedtext": "'''Twoje wužiwarske mjeno bu zablokowane abo twoja IP-adresa bu zablokowana.'''\n\nBlokowar je $1.\nPodata přičina je ''$2''.\n\n* Spočatk blokowanja: $8\n* Kónc blokowanja: $6\n* Zablokowany wužiwar: $7\n\nMóžeš $1 abo druheho [[{{MediaWiki:Grouppage-sysop}}|administratora]] kontaktować, zo by wo blokowanju diskutował.\nNjemóžeš funkciju 'Tutomu wužiwarjej e-mejlować' wužiwać, chibazo sy płaćiwu e-mejlowu adresu w swojich [[Special:Preferences|kontowych nastajenjach]] podał a njebu přećiwo jeje wužiwanju zablokowany.\nTwoja tuchwilna IP-adresa je $3 a blokowanski ID je #$5. Prošu podaj wšě horjeka naspomnjene podrobnosće w swojich naprašowanjach.",
        "autoblockedtext": "Twoja IP-adresa bu awtomatisce blokowana, dokelž ju druhi wužiwar wužiwaše, kiž bu wot $1 zablokowany.\nPřičina blokowanja bě:\n\n:''$2''\n\n* Započatk blokowanja: $8\n* Kónc blokowanja: $6\n* Zablokowany wužiwar: $7\n\nMóžeš $1 abo jednoho z druhich [[{{MediaWiki:Grouppage-sysop}}|administratorow]] kontaktować, zo by blokowanje diskutował.\n\nWobkedźbuj, zo njemóžeš funkciju \"Wužiwarjej mejlku pósłać\" wužiwać, jeli nimaš płaćiwu e-mejlowu adresu, kotraž je w twojich [[Special:Preferences|wužiwarskich nastajenjach]] zregistrowana a njebi blokowany ju wužiwać.\n\nTwój aktualna adresa IP je $3 a ID blokowanja je #$5.\nProšu podaj wšě horjeka naspomnjene podrobnosće w naprašowanjach, kotrež činiš.",
        "accmailtext": "Připadnje spłodźene hesło za [[User talk:$1|$1]] bu na $2 pósłane. Daj so na stronje ''[[Special:ChangePassword|hesło změnić]]'' při přizjewjenju změnić.",
        "newarticle": "(Nowy)",
        "newarticletext": "Sy wotkaz k stronje slědował, kotraž hišće njeeksistuje. Zo by stronu wutworił, wupjelń slědowace tekstowe polo (hlej [$1 stronu pomocy] za dalše informacije). Jeli sy zmylnje tu, klikń prosće na tłóčatko <b>Wróćo</b> we swojim wobhladowaku.",
-       "anontalkpagetext": "---- ''To je diskusijna strona za anonymneho wužiwarja, kiž hišće konto wutworił njeje abo je njewužiwa. Dyrbimy tohodla numerisku IP-adresu wužiwać, zo bychmy jeho/ju identifikowali. Tajka IP-adresa hodźi so wot wjacorych wužiwarjow zhromadnje wužiwać. Jeli sy anonymny wužiwar a měniš, zo buchu irelewantne komentary k tebi pósłane, [[Special:CreateAccount|wutwor prošu konto]] abo [[Special:UserLogin|přizjew so]], zo by přichodnu šmjatańcu z anonymnymi wužiwarjemi wobešoł.''",
-       "noarticletext": "Tuchwilu tuta strona žadyn tekst njewobsahuje. Móžeš [[Special:Search/{{PAGENAME}}|tutón titul strony na druhich stronach pytać]], <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} wotpowědne protokole pytać] abo [{{fullurl:{{FULLPAGENAME}}|action=edit}} tutu stronu wobdźěłać]</span>.",
+       "anontalkpagetext": "---- \n<em>To je diskusijna strona za anonymneho wužiwarja, kiž hišće konto wutworił njeje abo je njewužiwa.</em>\nDyrbimy tohodla numerisku IP-adresu wužiwać, zo bychmy jeho/ju identifikowali.\nTajka IP-adresa hodźi so wot wjacorych wužiwarjow zhromadnje wužiwać. Jeli sy anonymny wužiwar a měniš, zo buchu irelewantne komentary k tebi pósłane, [[Special:CreateAccount|wutwor prošu konto]] abo [[Special:UserLogin|přizjew so]], zo by přichodnu šmjatańcu z anonymnymi wužiwarjemi wobešoł.",
+       "noarticletext": "Tuchwilu tuta strona žadyn tekst njewobsahuje. Móžeš [[Special:Search/{{PAGENAME}}|tutón titul strony na druhich stronach pytać]], <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} wotpowědne protokole pytać] abo [{{fullurl:{{FULLPAGENAME}}|action=edit}} tutu stronu wutworić]</span>.",
        "noarticletext-nopermission": "Tuchwilu žadyn tekst na tutej stronje njeje.\nMóžeš [[Special:Search/{{PAGENAME}}|tutón titul strony]] na druhich stronach pytać abo <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} pytaj wotpowědne protokole]</span>, ale nimaš prawo, strou wutworić.",
        "missing-revision": "Wersija #$1 strony z mjenom \"{{FULLPAGENAME}}\" njeeksistuje.\n\nPřičina je zwjetša zestarjeny wotkaz w stawiznach k stronje, kotraž je so zhašała.\nPodrobnosće móžeš w  [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} protokolu wušmórnjenjow] namakać.",
        "userpage-userdoesnotexist": "Wužiwarske konto „$1“ njeje zregistrowane. Prošu pruwuj, hač chceš tutu stronu woprawdźe wutworić/wobdźěłać.",
        "userpage-userdoesnotexist-view": "Wužiwarske konto \"$1\" njeje zregistrowane.",
        "blocked-notice-logextract": "Tutón wužiwar je tuchwilu zablokowany. Najnowši protokolowy zapisk so deleka jako referenca podawa:",
-       "clearyourcache": "'''Kedźbu: Po składowanju dyrbiš snano pufrowak swojeho wobhladowaka wuprózdnić, zo by změny widźał.''' \n* '''Firefox/Safari:''' Tłóč na ''Umsch'' kliknjo na ''Znowa'' abo tłóč ''Strg-F5'' abo ''Strg-R'' (''⌘-R'' na Macintosh);\n* '''Google Chrome:''' Tłóč na ''Strg-Umsch-R'' (''⌘-Umsch-R'' na Mac)\n* '''Internet Explorer:''' Dźěrź ''Strg'' tłóčeny kliknjo na ''Znowa,'' abo tłóč ''Strg-F5''.\n* '''Opera:''' Wuprózdń pufrowak w ''Extras → Einstellungen'';",
+       "clearyourcache": "<strong>Kedźbu:</strong> Po składowanju dyrbiš snano pufrowak swojeho wobhladowaka wuprózdnić, zo by změny widźał.\n* <strong>Firefox/Safari:</strong> Dźerž tastu <em>Umsch</em> tłóčenu a klikń na <em>Znowa</em> abo tłóč pak <em>Strg-F5</em> abo <em>Strg-R</em> (<em>⌘-R</em> na Mac);\n* <strong>Google Chrome:</strong> Tłóč na <em>Strg-Umsch-R</em> (<em>⌘-Umsch-R</em> na Mac)\n* <strong>Internet Explorer:</strong> Dźěrź tastu <em>Strg</em> tłóčen a klikń na <em>Znowa</em> abo tłóč <em>Strg-F5</em>.\n* <strong>Opera:</strong> Dźi k <em>Menü → Einstellungen</em> (<em>Opera → Einstellungen</em> na Mac) a potom k <em>Datenschutz & Sicherheit → Browserdaten löschen → Gespeicherte Bilder und Dateien</em>.",
        "usercssyoucanpreview": "'''Pokiw:''' Wužij tłóčku '{{int:showpreview}}', zo by swój nowy css do składowanja testował.",
        "userjsyoucanpreview": "'''Pokiw:''' Wužij tłóčatko \"{{int:showpreview}}\", zo by swój nowy JavaScript do składowanja testował.",
        "usercsspreview": "'''Wobkedźbujće, zo sej jenož přehlad swojeho wužiwarskeho CSS wobhladuješ. Hišće njeje składowany!'''",
        "undo-nochange": "Zda so, zo změna je so hižo cofnyła.",
        "undo-summary": "Změna $1 [[Special:Contributions/$2|$2]] ([[User talk:$2|diskusija]]) bu cofnjena.",
        "undo-summary-username-hidden": "Změna $1 schowaneho wužiwarja je so anulowała",
-       "cantcreateaccounttitle": "Wužiwarske konto njeda so wutworić.",
        "cantcreateaccount-text": "Wutworjenje wužiwarskeho konta z IP-adresy '''$1''' bu wot [[User:$3|$3]] zablokowane.\n\nPřičina za blokowanje, podata wot $3, je: ''$2''",
        "cantcreateaccount-range-text": "Załoženje kontow z IP-adresow we wobłuku '''$1'', kotryž twoju IP-adresu wobsahuje (<strong>$4</strong>), je so wot [[User:$3|$3]] zablokowało.\n\nPřičina podata wot $3 je <em>$2</em>",
        "viewpagelogs": "protokole tuteje strony pokazać",
        "revdelete-unsuppress": "Wobmjezowanja za wobnowjene wersije zběhnyć",
        "revdelete-log": "Přičina:",
        "revdelete-submit": "Na {{PLURAL:$1|wubranu wersiju|wubranej wersiji|wubrane wersije|wubrane wersije}} nałožować",
-       "revdelete-success": "'''Widźomnosć wersije bu wuspěšnje zaktualizowana.'''",
+       "revdelete-success": "Widźomnosć wersije je so zaktualizowała.",
        "revdelete-failure": "'''Wersijowa widźomnosć njeda so aktualizować:'''\n$1",
-       "logdelete-success": "Widźomnosć zapiska bu wuspěšnje změnjena.",
+       "logdelete-success": "Widźomnosć protokola je so změniła.",
        "logdelete-failure": "'''Protokolowa widźomnosć njeda so nastajić:'''\n$1",
        "revdel-restore": "Widźomnosć změnić",
        "pagehist": "Stawizny strony",
        "mergehistory-go": "Zjednoćujomne změny pokazać",
        "mergehistory-submit": "Wersije zjednoćić",
        "mergehistory-empty": "Njehodźa so žane wersije zjednoćeć.",
-       "mergehistory-done": "$3 {{PLURAL:$3|wersija|wersiji|wersije|wersijow}} wot $1 wuspěšnje z [[:$2]] {{PLURAL:$3|zjednoćena|zjednoćenej|zjednoćene|zjednoćene}}.",
+       "mergehistory-done": "$3 {{PLURAL:$3|wersija|wersiji|wersije|wersijow}} wot $1 z [[:$2]] {{PLURAL:$3|zjednoćena|zjednoćenej|zjednoćene}}.",
        "mergehistory-fail": "Njeje móžno zjednócenje stawiznow přewjesć, prošu přepruwuj stronu a časowe parametry.",
        "mergehistory-no-source": "Žórłowa strona $1 njeeksistuje.",
        "mergehistory-no-destination": "Cilowa strona $1 njeeksistuje.",
        "prefs-watchlist-token": "Marka wobkedźbowankow:",
        "prefs-misc": "Wšelake nastajenja",
        "prefs-resetpass": "Hesło změnić",
-       "prefs-changeemail": "E-mejl změnić",
+       "prefs-changeemail": "E-mejlowu adresu změnić abo wotstronić",
        "prefs-setemail": "E-mejlowu adresu postajić",
        "prefs-email": "E-mejlowe opcije",
        "prefs-rendering": "Napohlad",
        "rows": "Rjadki:",
        "columns": "Stołpiki:",
        "searchresultshead": "Pytać",
-       "stub-threshold": "Wotkazowe formatowanje <a href=\"#\" class=\"stub\">małych stronow</a> (w bajtach):",
+       "stub-threshold": "Wotkazowe formatowanje małych stronow ($1):",
        "stub-threshold-disabled": "Znjemóžnjeny",
        "recentchangesdays": "Ličba dnjow w lisćinje aktualnych změnow:",
        "recentchangesdays-max": "(Maksimalnje $1 {{PLURAL:$1|dźeń|dnjej|dny|dnjow}})",
        "userrights": "Zrjadowanje wužiwarskich prawow",
        "userrights-lookup-user": "Wužiwarske skupiny zrjadować",
        "userrights-user-editname": "Wužiwarske mjeno:",
-       "editusergroup": "Wužiwarske skupiny wobdźěłać",
-       "editinguser": "Změni wužiwarske prawa wužiwarja '''[[User:$1|$1]]''' $2",
+       "editusergroup": "{{GENDER:$1|Wužiwarske}} skupiny wobdźěłać",
+       "editinguser": "Změni wužiwarske prawa {{GENDER:$1|wužiwarja|wužiwarki}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Wužiwarske skupiny wobdźěłać",
-       "saveusergroups": "Wužiwarske skupiny składować",
+       "saveusergroups": "{{GENDER:$1|Wužiwarske}} skupiny składować",
        "userrights-groupsmember": "Čłon wot:",
        "userrights-groupsmember-auto": "Implicitny čłon wot:",
        "userrights-groups-help": "Móžeš skupiny změnić, w kotrychž wužiwar je.\n* Markěrowany kašćik woznamjenja, zo wužiwar je w tej skupinje.\n* Njemarkěrowany kašćik woznamjenja, zo wužiwar w tej skupinje njeje.\n* \"*\" podawa, zo njemóžeš skupinu wotstronić, tak ruče kaž sy ju přidał abo nawopak.",
        "userrights-changeable-col": "Skupiny, kotrež móžeš změnić",
        "userrights-unchangeable-col": "Skupiny, kotrež njemóžeš změnić",
        "userrights-conflict": "Konflikt změnow wužiwarskich prawow! Prošu přepruwuj a wobkruć swoje změny.",
-       "userrights-removed-self": "Sy swoje prawa wuspěšnje wotstronił. Tohodla nimaš hižo přistup na tutu stronu měć.",
+       "userrights-removed-self": "Sy swoje prawa wotstronił. Tohodla nimaš hižo přistup na tutu stronu měć.",
        "group": "Skupina:",
        "group-user": "wužiwarjo",
        "group-autoconfirmed": "Awtomatisce potwjerdźeni wužiwarjo",
        "group-bot": "Boty",
        "group-sysop": "Administratorojo",
        "group-bureaucrat": "Běrokraća",
-       "group-suppress": "dohladowarjo",
+       "group-suppress": "Potłóčowarjo",
        "group-all": "(wšě)",
        "group-user-member": "{{GENDER:$1|wužiwar|wužiwarka}}",
        "group-autoconfirmed-member": "{{GENDER:$1|awtomatisce potwjerdźeny wužiwar|awtomatisce potwjerdźena wužiwarka}}",
        "group-bot-member": "{{GENDER:$1|bot}}",
        "group-sysop-member": "{{GENDER:$1|administrator|administratorka}}",
        "group-bureaucrat-member": "{{GENDER:$1|běrokrat|běrokratka}}",
-       "group-suppress-member": "{{GENDER:$1|dohladowar|dohladowarka}}",
+       "group-suppress-member": "{{GENDER:$1|potłóčowar|potłóčowarka}}",
        "grouppage-user": "{{ns:project}}:Wužiwarjo",
        "grouppage-autoconfirmed": "{{ns:project}}:Awtomatisce potwjerdźeni wužiwarjo",
        "grouppage-bot": "{{ns:project}}:Boćiki",
        "grouppage-sysop": "{{ns:project}}:Administratorojo",
        "grouppage-bureaucrat": "{{ns:project}}:Běrokraća",
-       "grouppage-suppress": "{{ns:project}}:Dohladowanje",
+       "grouppage-suppress": "{{ns:project}}:Potłóčowarjo",
        "right-read": "Strony čitać",
        "right-edit": "Strony wobdźěłać",
        "right-createpage": "Strony wutworić (kotrež diskusijne strony njejsu)",
        "right-override-export-depth": "Strony inkluziwnje wotkazanych stronow hač do hłubokosće 5 eksportować",
        "right-sendemail": "Druhim wužiwarjam e-mejl pósłać",
        "right-passwordreset": "E-mejlki za wróćostajenje hesłow sej wobhladać",
-       "right-managechangetags": "[[Special:Tags|markěrowanja]] wutworić a z datoweje banki zhašeć",
+       "right-managechangetags": "[[Special:Tags|Markěrowanja]] wutworić a z(nje)móžnić",
        "right-applychangetags": "[[Special:Tags|Markěrowanja]] hromadźe ze změnami nałožować",
        "newuserlogpage": "Protokol nowych wužiwarjow",
        "newuserlogpagetext": "To je protokol wutworjenja nowych wužiwarskich kontow.",
        "rightslogtext": "To je protokol změnow wužiwarskich prawow.",
        "action-read": "tutu stronu čitać",
        "action-edit": "tutu stronu wobdźěłać",
-       "action-createpage": "strony wutworić",
-       "action-createtalk": "diskusijne strony wutworić",
+       "action-createpage": "tutu stronu wutworić",
+       "action-createtalk": "tutu diskusijnu stronu wutworić",
        "action-createaccount": "wužiwarske konto załožić",
        "action-history": "historiju tuteje strony pokazać",
        "action-minoredit": "tutu změnu jako snadnu markěrować",
        "action-viewmyprivateinfo": "twoje priwatne informacije sej wobhladać",
        "action-editmyprivateinfo": "twoje priwatne informacije wobdźěłać",
        "action-editcontentmodel": "wobsahowy model strony wobdźěłać",
-       "action-managechangetags": "markěrowanja wutworić a z datoweje banki zhašeć",
+       "action-managechangetags": "markěrowanja wutworić a z(nje)móžnić",
        "action-applychangetags": "markěrowanja hromadźe z wašimi změnami nałožować",
        "nchanges": "$1 {{PLURAL:$1|změna|změnje|změny|změnow}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|wot poslednjeho wopyta}}",
        "boteditletter": "B",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|wobkedźbowacy wužiwar|wobkedźbowacaj wužiwarjej|wobkedźbowacy wužiwarjo|wobkedźbowacych wužiwarjow}}]",
        "rc_categories": "Jenož kategorije (dźělene z \"|\")",
-       "rc_categories_any": "wšě",
+       "rc_categories_any": "Někajka z wubranych",
        "rc-change-size": "$1 {{PLURAL:$1|bajt|bajtaj|bajty|bajtow}}",
        "rc-change-size-new": "$1 {{PLURAL:$1|bajt|bajtaj|bajty|bajtow}} po změnje",
        "newsectionsummary": "Nowy wotrězk: /* $1 */",
        "backend-fail-read": "Dataja $1 njeda so čitać",
        "backend-fail-create": "Dataja $1 njeda so pisać.",
        "backend-fail-maxsize": "Dataja $1 njeda so pisać, dokelž je wjetša hač {{PLURAL:$2|jedyn bajt|$2 bajtaj|$2 bajty|$2 bajtow}}.",
-       "backend-fail-readonly": "Składowanski backend \"$1\" da so tuchwilu jenož čitać. Přičina je była: \"$2\"",
+       "backend-fail-readonly": "Składowanski backend \"$1\" da so tuchwilu jenož čitać. Přičina je: <em>$2</em>",
        "backend-fail-synced": "Dataja \"$1\" je znutřka interneho składowanskeho backenda w inkonsistentnym stawje",
        "backend-fail-connect": "Zwisk z datajowym składowanskim backendom \"$1\" móžno njeje.",
        "backend-fail-internal": "W składowanskim backendźe \"$1\" je njeznaty zmylk wustupił.",
        "uploadstash-summary": "Tuta strona zmóžnja přistup k datajam, kotrež su nahrate (abo so nahrawaja), ale hišće njejsu do wikija wozjejwene. Tute dataje  za nikoho widźomne njejsu, jenož za wužiwarja, kiž je je nahrał.",
        "uploadstash-clear": "Schowane nahrate dataje zhašeć",
        "uploadstash-nofiles": "Nimaš žane schowane nahrate dataje.",
-       "uploadstash-badtoken": "Wuwjedźenje teje akcije je so njeporadźiło, snano dokelž twoje wobdźěłowanske daty su spadnjene. Spytaj hišće raz.",
-       "uploadstash-errclear": "Wotstronjenje datajow je so njeporadźiło.",
+       "uploadstash-badtoken": "Wuwjedźenje teje akcije njeje so poradźiło, snano dokelž twoje wobdźěłowanske daty su spadnjene. Spytaj prošu hišće raz.",
+       "uploadstash-errclear": "Wotstronjenje datajow njeje so poradźiło.",
        "uploadstash-refresh": "Lisćinu datajow aktualizować",
        "invalid-chunk-offset": "Njepłaćiwy startowy dypk",
        "img-auth-accessdenied": "Přistup wotpokazany",
        "nopagetext": "Cilowa strona, kotruž sće podał, njeeksistuje.",
        "pager-newer-n": "{{PLURAL:$1|nowši 1|nowšej $1|nowše $1|nowšich $1}}",
        "pager-older-n": "{{PLURAL:$1|starši 1|staršej $1|starše $1|staršich $1}}",
-       "suppress": "Dohladowanje",
+       "suppress": "Potłóčować",
        "querypage-disabled": "Tuta specialna strona je z wukonowych přičinow znjemóžnjena.",
        "apihelp": "API-pomoc",
        "apihelp-no-such-module": "Modul \"$1\" njeje so namakał.",
        "apisandbox-intro": "Wužij tutu stronu, zo by z '''websłužbu Mediawiki API''' eksperimentował.\nHlej [https://www.mediawiki.org/wiki/API:Main_page API-dokumentaciju] za dalše podrobnosće za wužiwanje API. Přikład: [https://www.mediawiki.org/wiki/API#A_simple_example Wobsah hłowneje strony wotwołać]. Wubjer akciju, zo by dalše přikłady widźał.\n\nDźiwaj na to, zo, hačrunjež to je hrajkanišćo, akcije, kotrež na tutej stronje přewjedźeš, móhli wiki změnić.",
        "apisandbox-submit": "Naprašowanje přewjesć",
        "apisandbox-reset": "Wuprózdnić",
-       "apisandbox-examples": "Přikład",
-       "apisandbox-results": "Wuslědk",
+       "apisandbox-examples": "Přikłady",
+       "apisandbox-results": "Wuslědki",
        "apisandbox-request-url-label": "URL naprašowanja:",
-       "apisandbox-request-time": "Naprašowanski čas: $1",
+       "apisandbox-request-time": "Traće naprašowanja: {{PLURAL:$1|$1 milisekunda|$1 milisekundźe|$1 milisekundy|$1 milisekundow}}",
        "booksources": "Pytanje po ISBN",
        "booksources-search-legend": "Žórła za knihi pytać",
        "booksources-search": "Pytać",
        "booksources-text": "To je lisćina wotkazow k druhim sydłam, kotrež nowe a trjebane knihi předawaja. Tam móžeš tež dalše informacije wo knihach dóstać, kotrež pytaš:",
        "booksources-invalid-isbn": "Podate ISBN-čisło njezda so płaćiwe być; přepruwuj za zmylkami, z tym zo z orginialneho žórła kopěruješ.",
        "specialloguserlabel": "Wukonjer:",
-       "speciallogtitlelabel": "Cil (titul abo wužiwar):",
+       "speciallogtitlelabel": "Cil (titul abo {{ns:user}}:wužiwarske mjeno za wužiwarja):",
        "log": "Protokole",
        "all-logs-page": "Wšě zjawne protokole",
        "alllogstext": "Kombinowane zwobraznjenje wšěch k dispozicij stejacych protokolow w {{GRAMMAR:lokatiw|{{SITENAME}}}}. Móžeš napohlad wobmjezować, wuběrajo typ protokola, wužiwarske mjeno (dźiwajo na wulkopisanje) abo potrjechu stronu (tež dźiwajo na wulkopisanje).",
        "wlheader-showupdated": "Strony, kotrež su so po twojim poslednim wopyće změnili, so '''tučne''' pokazuja.",
        "wlnote": "Deleka {{PLURAL:$1|je poslednja změna|stej poslednjej <strong>$1</strong> změnje|su poslednje <strong>$1</strong> změny|je poslednich <strong>$1</strong> změnow}} za {{PLURAL:$2|poslednju hodźinu|poslednje <strong>$2</strong> hodźinje|poslednje <strong>$2</strong> hodźiny|poslednich <strong>$2</strong> hodźin}}, staw : $3, $4.",
        "wlshowlast": "Změny zańdźenych $1 hodźin, $2 dnjow, pokazać",
-       "wlshowtime": "Pokazaj změny zašłych",
+       "wlshowtime": "Perioda, kotraž ma so pokazać:",
        "watchlist-options": "Opcije wobkedźbowankow",
        "watching": "Wobkedźbuju…",
        "unwatching": "Njewobkedźbuju…",
        "deletepage": "Stronu zhašeć",
        "confirm": "Wobkrućić",
        "excontent": "wobsah běše: '$1'",
-       "excontentauthor": "wobsah bě: '$1' (a jenički wobdźěłowar bě '[[Special:Contributions/$2|$2]]')",
+       "excontentauthor": "wobsah bě: „$1“, a jenički wobdźěłar bě „[[Special:Contributions/$2|$2]]“ ([[User talk:$2|diskusija]])",
        "exbeforeblank": "wobsah do wuprózdnjenja běše: '$1'",
        "delete-confirm": "„$1“ wušmórnyć",
        "delete-legend": "Wušmórnyć",
        "delete-toobig": "Tuta strona ma z wjace hač $1 {{PLURAL:$1|wersiju|wersijomaj|wersijemi|wersijemi}} wulke wobdźěłanske stawizny. Wušmórnjenje tajkich stronow bu wobmjezowane, zo by připadne přetorhnjenje {{SITENAME}} wobešło.",
        "delete-warning-toobig": "Tuta strona ma z wjace hač $1 {{PLURAL:$1|wersiju|wersijomaj|wersijemi|wersijemi}} wulke wobdźěłanske stawizny. Wušmórnjenje móže operacije datoweje banki {{SITENAME}} přetorhnyć; pokročuj z kedźbliwosću.",
        "deleteprotected": "Njemóžeš tutu stronu zhašeć, dokelž je so škitała.",
-       "deleting-backlinks-warning": "'''Warnowanje:''' [[Special:WhatLinksHere/{{FULLPAGENAME}}|Druhe strony]] wotkazuja k stronje abo strona je druhdźe zapřijata, kotruž chceš zhašeć.",
+       "deleting-backlinks-warning": "<strong>Warnowanje:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Druhe strony]] wotkazuja k stronje abo strona je druhdźe zapřijata, kotruž chceš zhašeć.",
        "rollback": "Změny cofnyć",
        "rollbacklink": "Cofnyć",
        "rollbacklinkcount": "$1 {{PLURAL:$1|změnu|změnje|změny|změnow}} cofnyć",
        "protect-locked-blocked": "Njemóžeš škit strony změnič, dokelž twoje konto je zablokowane. Tu widźiš aktualne škitne nastajenja za stronu'''„$1“:'''",
        "protect-locked-dblock": "Datowa banka je zawrjena, tohodla njemóžeš škit strony změnić. Tu widźiš aktualne škitne nastajenja za stronu'''„$1“:'''",
        "protect-locked-access": "Nimaš trěbne prawa, zo by škit strony změnił. Tu widźiš aktualne škitne nastajenja za stronu'''„$1“:'''",
-       "protect-cascadeon": "Tuta strona je tuchwilu škitana, dokelž je w {{PLURAL:$1|slědowacej stronje|slědowacych stronach}} zapřijata, {{PLURAL:$1|kotraž je|kotrež su}} přez kaskadowu opciju {{PLURAL:$1|škitana|škitane}}. Změny na škitnym schodźenku tuteje strony njebudu kaskadowy škit wobwliwować.",
+       "protect-cascadeon": "Tuta strona je tuchwilu škitana, dokelž je w {{PLURAL:$1|slědowacej stronje|slědowacymaj stronomaj|slědowacych stronach}} zapřijata, {{PLURAL:$1|kotraž je|kotrejž stej|kotrež su}} přez kaskadowu opciju {{PLURAL:$1|škitana|škitanej|škitane}}. Změny na škitnym schodźenku tuteje strony njebudu kaskadowy škit wobwliwować.",
        "protect-default": "Wšěm wužiwarjam dowolić",
        "protect-fallback": "Jenož wužiwarjow z prawom \"$1\" dowolić",
        "protect-level-autoconfirmed": "Jenož awtomatisce wobkrućenym wužiwarjam dowolić",
        "undeletepagetext": "{{PLURAL:$1|Slědowaca strona bu wušmórnjena, ale je|Slědowacej $1 stronje buštej wušmórnjenej, ale stej|Slědowace $1 strony buchu wušmórnjene, ale su|Slědowacych $1 bu wušmórnjene, ale je}} hišće w archiwje a {{PLURAL:$1|da so|datej so|dadźa so|da so}} wobnowić.\nArchiw da so periodisce wuprózdnić.",
        "undelete-fieldset-title": "Wersije wobnowić",
        "undeleteextrahelp": "Zo by wšě stawizny strony wobnowił, wostaj prošu wšě kontrolowe kašćiki njewubrane a klikń na '''''{{int:undeletebtn}}'''''.\nZo by selektiwne wobnowjenje přewjedł, wubjer kašćiki, kotrež wersijam wotpowěduja, kotrež maja so wobnowić, a klikń na '''''{{int:undeletebtn}}'''''.",
-       "undeleterevisions": "$1 {{PLURAL:$1|wersija|wersiji|wersije|wersijow}} {{PLURAL:$1|archiwowana|archiwowanej|archiwowane|archiwowane}}",
+       "undeleterevisions": "$1 {{PLURAL:$1|wersija|wersiji|wersije|wersijow}} {{PLURAL:$1|zhašana|zhašanej|zhašane|zhašanych}}",
        "undeletehistory": "Jeli tutu stronu wobnowiš, so wšě (tež prjedy wušmórnjene) wersije zaso do stawiznow wobnowja. Jeli bu po wušmórnjenju nowa strona ze samsnym mjenom wutworjena, budu so wobnowjene wersije w prjedawšich stawiznach jewić.",
        "undeleterevdel": "Wobnowjenje so njepřewjedźe, jeli je najwyša strona docpěta abo datajowa wersija budźe so zdźěla wušmórnje.\nW tutym padźe dyrbiš najnowšu wušmórnjenu wersiju znjemóžnić abo pokazać.",
        "undeletehistorynoadmin": "Strona bu wušmórnjena. Přičina za wušmórnjenje so deleka w zjeću pokazuje, zhromadnje z podrobnosćemi wužiwarjow, kotřiž běchu tutu stronu do zničenja wobdźěłali. Tuchwilny wobsah strony je jenož administratoram přistupny.",
        "contributions": "Přinoški {{GENDER:$1|wužiwarja|wužiwarki}}",
        "contributions-title": "Wužiwarske přinoški wot „$1“",
        "mycontris": "Přinoški",
+       "anoncontribs": "Přinoški",
        "contribsub2": "Za {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "Wužiwarske konto \"$1\" njeje zregistrowane.",
        "nocontribs": "Žane změny, kotrež podatym kriterijam wotpowěduja.",
        "whatlinkshere-next": "{{PLURAL:$1|přichodny|přichodnej|přichodne|přichodne $1}}",
        "whatlinkshere-links": "← wotkazy",
        "whatlinkshere-hideredirs": "Daleposrědkowanja $1",
-       "whatlinkshere-hidetrans": "Zapřijeća $1",
+       "whatlinkshere-hidetrans": "Transkluzije $1",
        "whatlinkshere-hidelinks": "Wotkazy $1",
        "whatlinkshere-hideimages": "Datajowe wotkazy $1",
        "whatlinkshere-filters": "Filtry",
        "cant-move-to-user-page": "Nimaš prawo stronu do wužiwarskeje strony přesunyć (z wuwzaćom do wužiwarskeje podstrony).",
        "cant-move-category-page": "Nimaš prawo, zo by kategorijowe strony přesunył.",
        "cant-move-to-category-page": "Nimaš prawo, stronu do kategorijoweje strony přesunyć.",
-       "newtitle": "pod nowe hesło",
+       "newtitle": "Nowy titul:",
        "move-watch": "Stronu wobkedźbować",
        "movepagebtn": "Stronu přesunyć",
        "pagemovedsub": "Přesunjenje wuspěšne",
        "javascripttest": "JavaScriptowy test",
        "javascripttest-pagetext-unknownaction": "Njeznata akcija \"$1\"",
        "javascripttest-qunit-intro": "Hlej [$1 testowansku dokumentaciju] na mediawiki.org.",
-       "tooltip-pt-userpage": "Twoja wužiwarska strona",
+       "tooltip-pt-userpage": "{{GENDER:|Twoja wužiwarska}} strona",
        "tooltip-pt-anonuserpage": "Wužiwarska strona IP-adresy, z kotrejž tuchwilu dźěłaš",
-       "tooltip-pt-mytalk": "Twoja diskusijna strona",
+       "tooltip-pt-mytalk": "{{GENDER:|Twoja}} diskusijna strona",
        "tooltip-pt-anontalk": "Diskusija wo změnach z tuteje IP-adresy",
-       "tooltip-pt-preferences": "moje nastajenja",
+       "tooltip-pt-preferences": "{{GENDER:|Twoje}} nastajenja",
        "tooltip-pt-watchlist": "lisćina stronow, kotrež wobkedźbuješ",
-       "tooltip-pt-mycontris": "Lisćina twojich přinoškow",
+       "tooltip-pt-mycontris": "Lisćina {{GENDER:|twojich}} přinoškow",
        "tooltip-pt-login": "Móžeš so woměrje přizjewić, to pak zawjazowace njeje.",
        "tooltip-pt-logout": "so wotzjewić",
        "tooltip-pt-createaccount": "Pozbudźujemy će, konto załožić a so přizjewić; njeje wšak nuznje trěbne",
        "tooltip-t-recentchangeslinked": "aktualne změny w stronach, na kotrež tuta strona wotkazuje",
        "tooltip-feed-rss": "RSS-feed za tutu stronu",
        "tooltip-feed-atom": "Atom-feed za tutu stronu",
-       "tooltip-t-contributions": "přinoški tutoho wužiwarja wobhladać",
-       "tooltip-t-emailuser": "wužiwarjej mejlku pósłać",
+       "tooltip-t-contributions": "Lisćina přinoškow {{GENDER:$1|tutoho wužiwarja|tuteje wužiwarki}}",
+       "tooltip-t-emailuser": "{{GENDER:$1|Tutomu wužiwarjej|Tutej wužiwarce}} mejlku pósłać",
        "tooltip-t-info": "Dalše informacije wo tutej stronje",
        "tooltip-t-upload": "Dataje nahrać",
        "tooltip-t-specialpages": "lisćina wšěch specialnych stronow",
        "lastmodifiedatby": "Strona bu dnja $1 w $2 hodź. wot $3 změnjena.",
        "othercontribs": "Bazěruje na dźěle wužiwarja $1.",
        "others": "druhich",
-       "siteusers": " {{PLURAL:$2|wužiwarja|wužiwarjeju|wužiwarjow|wužiwarjow}} $1 na {{GRAMMAR:lokatiw|{{SITENAME}}}}",
+       "siteusers": " {{PLURAL:$2|{{GENDER:$1|wužiwarja|wužiwarki}}|wužiwarjow|wužiwarkow}} $1 na {{GRAMMAR:lokatiw|{{SITENAME}}}}",
        "anonusers": " {{PLURAL:$2|anonymny wužiwar|anonymnaj wužiwarjej|anonymni wužiwarjo|anonymnych wužiwarjow}} $1 na {{GRAMMAR:lokatiw|{{SITENAME}}}}",
        "creditspage": "Dźak awtoram",
        "nocredits": "Za tutu stronu žane informacije wo zasłužbach njejsu.",
        "scarytranscludefailed-httpstatus": "[Wotwołanje předłohi za $1 je so njeporadźiło: HTTP $2]",
        "scarytranscludetoolong": "[URL je předołhi]",
        "deletedwhileediting": "'''Kedźbu''': Tuta strona bu wušmórnjena, po tym zo sy započał ju wobdźěłać!",
-       "confirmrecreate": "Wužiwar [[User:$1|$1]] ([[User talk:$1|diskusija]]) je stronu wušmórnył, po tym zo sy započał ju wobdźěłać. Přičina:\n: ''$2''\nProšu potwjerdź, zo chceš tutu stronu woprawdźe znowa wutworić.",
-       "confirmrecreate-noreason": "Wužiwar [[User:$1|$1]] ([[User talk:$1|diskusija]]) je tutu stronu zhašał, po tym zo sće wobdźěłanje započał. Prošu wobkruć, zo woprawdźe chceš tutu stronu znowa wutworić.",
+       "confirmrecreate": "{{GENDER:$1|Wužiwar|Wužiwarka}} [[User:$1|$1]] ([[User talk:$1|diskusija]]) je stronu {{GENDER:$1|wušmórnył|wušmórnyła}}, po tym zo sy započał ju wobdźěłać. Přičina:\n: <em>$2</em>\nProšu potwjerdź, zo chceš tutu stronu woprawdźe znowa wutworić.",
+       "confirmrecreate-noreason": "{{GENDER:$1|Wužiwar|Wužiwarka}} [[User:$1|$1]] ([[User talk:$1|diskusija]]) je tutu stronu {{GENDER:$1|zhašał|zhašała}}, po tym zo sće wobdźěłowanje započał. Prošu wobkruć, zo woprawdźe chceš tutu stronu znowa wutworić.",
        "recreate": "Znowa wutworić",
        "confirm_purge_button": "W porjadku",
        "confirm-purge-top": "Pufrowak strony wuprózdnić?",
        "watchlistedit-raw-done": "Twoje wobkedźbowanki buchu składowane.",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 zapisk bu dodaty|$1 zapiskaj buštej dodatej|$1 zapiski buchu dodate|$1 zapiskow buchu dodate}}:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 zapisk bu wotstronjeny|$1 zapiskaj buštej wotstronjenej|$1 zapiski buchu wotstronjene|$1 zapiskow buchu wotstronjene}}:",
-       "watchlistedit-clear-title": "Wotstronjene wobkedźbowanki",
+       "watchlistedit-clear-title": "Wobkedźbowanki zhašeć",
        "watchlistedit-clear-legend": "Wobkedźbowanki wotstronić",
        "watchlistedit-clear-explain": "Wšě titule budu so z twojich wobkedźbowankow wotstronjeć",
        "watchlistedit-clear-titles": "Titule:",
        "version-libraries": "Instalowane biblioteki",
        "version-libraries-library": "Biblioteka",
        "version-libraries-version": "Wersija",
-       "redirect": "Po datajowym mjenje, wužiwarju, stronje abo wersijowym ID dale sposrědkować",
+       "redirect": "Na dataju, wužiwarja, stronu abo wersiju abo protokolowy ID dale sposrědkować",
        "redirect-summary": "Tuta specialna strona so do dataje (datajowe mjeno je podate), strony (wersijowy ID abo ID strony je podaty) abo wužiwarskeje strony (numeriski wužiwarski ID je podaty) dale sposrědkuje. Wužiće:\n[[{{#Special:Redirect}}/file/Přikład.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] abo [[{{#Special:Redirect}}/user/101]].",
        "redirect-submit": "Los",
        "redirect-lookup": "Pytać:",
        "tags-deactivate-submit": "Znjemóžnić",
        "tags-edit-revision-selected": "{{PLURAL:$1|Wubrana wersija|Wubranej wersiji|Wubrane wersije}} wot [[:$2]]:",
        "tags-edit-logentry-selected": "{{PLURAL:$1|Wubrany protokolowy podawk|Wubranej protokolowej podawkaj|Wubrane protokolowe podawki}}",
-       "tags-edit-existing-tags-none": "\"Žadyn\"",
+       "tags-edit-existing-tags-none": "<em>Žadyn</em>",
        "tags-edit-reason": "Přičina:",
        "tags-edit-revision-submit": "Změny na {{PLURAL:$1|tutu wersiju|$1 wersiji|$1 wersije|$1 wersijow}} nałožić",
        "tags-edit-logentry-submit": "Změny na {{PLURAL:$1|tutón protokolowy zapisk|$1 protokolowej zapiskaj|$1 protokolowe zapiski|$1 protokolowych zapiskow}}",
-       "tags-edit-success": "Změny su so wuspěšnje nałožili.",
+       "tags-edit-success": "Změny su so nałožili.",
        "tags-edit-failure": "Změny njehodźa so nałožować:\n$1",
        "tags-edit-nooldid-title": "Njepłaćiwa cilowa wersija",
        "comparepages": "Strony přirunać",
        "logentry-newusers-create2": "$1 je wužiwarske konto $3 {{GENDER:$2|załožił|załožiła}}",
        "logentry-newusers-byemail": "$1 je wužiwarske konto $3 {{GENDER:$2|załožił|załožiła}} a hesło je so přez e-mejl pósłało.",
        "logentry-newusers-autocreate": "Wužiwarske konto $1 je so awtomatisce {{GENDER:$2|załožiło}}",
-       "logentry-rights-rights": "$1 je skupinske čłonstwo za $3 z $4 do $5 {{GENDER:$2|změnił|změniła}}",
+       "logentry-rights-rights": "$1 je skupinske čłonstwo za {{GENDER:$6|$3}} z $4 do $5 {{GENDER:$2|změnił|změniła}}",
        "logentry-rights-rights-legacy": "$1 je skupinske čłonstwo za $3 {{GENDER:$2|změnił|změniła}}",
        "logentry-rights-autopromote": "$1 je so awtomatisce wot $4 do $5 {{GENDER:$2|přirjadował|přirjadowała}}",
        "logentry-upload-upload": "$1 je $3 {{GENDER:$2|nahrał|nahrała}}",
        "api-error-badaccess-groups": "Nimaš prawo dataje do tutoho wikija nahrać.",
        "api-error-badtoken": "Nutřkowny zmylk: Wopačny token.",
        "api-error-copyuploaddisabled": "Nahrawanje přez URL je na tutym serwerje znjemóžnjene.",
-       "api-error-duplicate": "{{PLURAL:$1|Je|Stej|Su|Je}} hižo {{PLURAL:$1|druha dataja|druhej dataji|druhe dataje|druhich datajow}} ze samsnym wobsahom na sydle",
+       "api-error-duplicate": "{{PLURAL:$1|Je|Stej|Su}} hižo {{PLURAL:$1|druha dataja|druhej dataji|druhe dataje}} ze samsnym wobsahom na sydle.",
        "api-error-duplicate-archive": "{{PLURAL:$1|Bě druha dataja|Běštej druhej dataji|Běchu druhe dataje|Bě druhich datajow}} hižo na websydle ze samsnym wobsahom, ale {{PLURAL:$1|je so zhašała|stej so zhašałoj|su so zhašeli|je so zhašało}}.",
        "api-error-empty-file": "Dataja, kotruž sy nahrał, je prózdna.",
        "api-error-emptypage": "Wutworjenje nowych, prózdnych stronow njeje dowolene.",
        "expand_templates_generate_xml": "Analyzowy štom XML pokazać",
        "expand_templates_generate_rawhtml": "Hruby HTML pokazać",
        "expand_templates_preview": "Přehlad",
-       "pagelanguage": "Selektor rěče strony",
+       "pagelanguage": "Rěč strony změnić",
        "pagelang-name": "Strona",
        "pagelang-language": "Rěč",
        "pagelang-use-default": "Standardnu rěč wužiwać",
        "special-characters-title-minus": "minusowe znamješko",
        "mw-widgets-dateinput-placeholder-day": "JJJJ-MM-TT",
        "mw-widgets-dateinput-placeholder-month": "JJJJ-MM",
-       "api-error-blacklisted": "Prošu wubjer druhi, wuprajiwy titul.",
        "randomrootpage": "Připadna korjenjowa strona"
 }
index 722d650..17df2d7 100644 (file)
@@ -13,7 +13,7 @@
                        "Lucas"
                ]
        },
-       "tog-underline": "Souliyen lyen yo&nbsp;:",
+       "tog-underline": "Souliyen lyen yo :",
        "tog-hideminor": "Kache tout modifikasyon resan yo ki tou piti",
        "tog-hidepatrolled": "Kache modifikasyon yo ki fèk fèt pou moun ki ap veye yo",
        "tog-newpageshidepatrolled": "Kache paj ki siveye yo nan mitan lis nouvo paj yo",
        "parser-template-recursion-depth-warning": "Limit depase pou kantite fwa yon modèl ka rele tèt li ($1)",
        "language-converter-depth-warning": "Limit sou pwofondè konvètisè lang yo depase ($1)",
        "undo-summary": "Revoke revizyon $1 ki te fèt pa [[Special:Contributions/$2|$2]] ([[User talk:$2|diskite]])",
-       "cantcreateaccounttitle": "Ou pa kapab kreye yon kont.",
        "viewpagelogs": "gade jounal paj sa a",
        "nohistory": "Istorik pou paj sa pa egziste ditou.",
        "currentrev": "Vèsyon kounye a",
        "skin-preview": "Voye kout je",
        "youremail": "Adrès imèl :",
        "username": "Non itilizatè a:",
-       "prefs-memberingroups": "Manm {{PLURAL:$1|nan gwoup sa|nan gwoup sa yo }} :",
+       "prefs-memberingroups": "{{GENDER:$2|Manm}} {{PLURAL:$1|nan gwoup sa|nan gwoup sa yo}} :",
        "yourrealname": "Vre non ou:",
        "yourlanguage": "Lang:",
        "yournick": "Siyati pou espas diskisyon :",
index 1a51e60..d8780e1 100644 (file)
        "tagline": "A {{SITENAME}} wikiből",
        "help": "Segítség",
        "search": "Keresés",
+       "search-ignored-headings": " #<!-- ezen a soron ne változtass --> <pre>\n# Az itt megadott szakaszokat figyelmen kívül hagyja a kereső.\n# Ha megváltoztatod ezt a listát, csak a változtatás után indexelt lapokra lesz hatása.\n# Ha újra akarsz indexelni egy adott oldalt, egy üres szerkesztéssel (megnyit-elment) megteheted.\n# Szintaxis:\n#   * A # jeltől a sor végéig tartó rész megjegyzés, a szoftver figyelmen kívül hagyja\n#   * Minden nem üres sor egy olyan szakasz címe, amit nem akarjuk, hogy indexeljen a kereső. (Csak a pontos egyezés számít, kisbetű/nagybetűt is beleértve.)\nForrások\nJegyzetek\nHivatkozások\nKülső hivatkozások\nLásd még\n #</pre> <!-- ezen a soron ne változtass -->",
        "searchbutton": "Keresés",
        "go": "Menj",
        "searcharticle": "Menj",
        "yourpasswordagain": "Jelszavad ismét:",
        "createacct-yourpasswordagain": "Új jelszó megerősítése",
        "createacct-yourpasswordagain-ph": "Írd be a jelszót újra",
-       "remembermypassword": "Emlékezzen rám ezen a számítógépen (legfeljebb $1 napig)",
        "userlogin-remembermypassword": "Maradjak bejelentkezve",
        "userlogin-signwithsecure": "Biztonságos kapcsolat használata",
+       "cannotlogin-title": "A bejelentkezés nem lehetséges.",
+       "cannotlogin-text": "A bejelentkezés nem lehetséges.",
        "cannotloginnow-title": "Nem lehet most bejelentkezni",
        "cannotloginnow-text": "A bejelentkezés nem lehetséges $1 használatakor.",
+       "cannotcreateaccount-title": "Felhasználói fiók létrehozása sikertelen",
+       "cannotcreateaccount-text": "Közvetlen fiók létrehozása nem engedélyezett ezen a wikin.",
        "yourdomainname": "A domainneved:",
        "password-change-forbidden": "Nem módosíthatod a jelszót ezen a wikin.",
        "externaldberror": "Hiba történt a külső adatbázis hitelesítése közben, vagy nem vagy jogosult a külső fiókod frissítésére.",
        "login": "Bejelentkezés",
+       "login-security": "Személyazonosságod igazolása",
        "nav-login-createaccount": "Bejelentkezés / fiók létrehozása",
        "userlogin": "Bejelentkezés / fiók létrehozása",
        "userloginnocreate": "Bejelentkezés",
        "userlogin-resetpassword-link": "Elfelejtetted a jelszavad?",
        "userlogin-helplink2": "Segítség a bejelentkezéshez",
        "userlogin-loggedin": "Már be vagy jelentkezve mint {{GENDER:$1|$1}}. Ha más néven akarsz belépni, alább megteheted.",
+       "userlogin-reauth": "Újra be kell jelentkezned, hogy igazold, te vagy $1.",
        "userlogin-createanother": "Másik felhasználói fiók létrehozása",
        "createacct-emailrequired": "E-mail cím",
        "createacct-emailoptional": "E-mail cím (opcionális)",
        "createacct-reason-ph": "Miért hozol létre egy másik fiókot",
        "createacct-submit": "Felhasználói fiók létrehozása",
        "createacct-another-submit": "Fiók létrehozása",
+       "createacct-continue-submit": "Fiók létrehozásának folytatása",
+       "createacct-another-continue-submit": "Fiók létrehozásának folytatása",
        "createacct-benefit-heading": "A(z) {{SITENAME}}-t hozzád hasonló emberek készítik.",
        "createacct-benefit-body1": "{{PLURAL:$1|szerkesztés|szerkesztés}}",
        "createacct-benefit-body2": "{{PLURAL:$1|lap|lap}}",
        "nocookiesnew": "A felhasználói fiókod létrejött, de nem vagy bejelentkezve. A wiki sütiket („cookie”) használ a szerkesztők azonosítására. Nálad ezek le vannak tiltva. Kérlek, engedélyezd őket a böngésződben, majd lépj be az új azonosítóddal és jelszavaddal.",
        "nocookieslogin": "A wiki sütiket („cookie”) használ a szerkesztők azonosításhoz.\nNálad ezek le vannak tiltva.\nEngedélyezd őket a böngésződben, majd próbáld újra.",
        "nocookiesfornew": "A felhasználói fiók nem lett létrehozva, mivel nem sikerült megerősítenünk a forrását.\nEllenőrizd, hogy a sütik engedélyezve vannak-e, majd frissítsd az oldalt, és próbálkozz újra.",
+       "createacct-loginerror": "A fiók sikeresen létrejött, de nem tudtál automatikusan bejelentkezni. Kérjük, [[Special:UserLogin|jelentkezz be manuálisan]]!",
        "noname": "Érvénytelen szerkesztőnevet adtál meg.",
        "loginsuccesstitle": "Sikeres bejelentkezés",
        "loginsuccess": "'''Sikeresen bejelentkeztél a(z) {{SITENAME}} wikibe „$1” néven.'''",
        "botpasswords-bad-appid": "A(z) „$1” botnév érvénytelen.",
        "botpasswords-insert-failed": "A(z) „$1” botnév hozzáadása sikertelen. Nem lehet, hogy már hozzá lett adva?",
        "botpasswords-created-title": "Botjelszó létrehozva",
-       "botpasswords-created-body": "Bot jelszó \"$1\" sikeresen létrehozva.",
+       "botpasswords-created-body": "\"$2\" felhasználó \"$1\" bot jelszava létrehozva.",
        "botpasswords-updated-title": "Botjelszó frissítve",
-       "botpasswords-updated-body": "Bot jelszó \"$1\" frissítése sikerült.",
+       "botpasswords-updated-body": "\"$2\" felhasználó \"$1\" bot jelszava módosítva.",
        "botpasswords-deleted-title": "Botjelszó törölve",
-       "botpasswords-deleted-body": "Bot jelszó \"$1\" törölve.",
+       "botpasswords-deleted-body": "\"$2\" felhasználó \"$1\" bot jelszava törölve.",
        "botpasswords-no-provider": "A BotPasswordsSessionProvider nem áll rendelkezésre.",
        "resetpass_forbidden": "A jelszavak nem változtathatók meg",
+       "resetpass_forbidden-reason": "A jelszavakat nem változtathatóak meg: $1",
        "resetpass-no-info": "Be kell jelentkezned, hogy közvetlenül elérd ezt a lapot.",
        "resetpass-submit-loggedin": "Jelszó megváltoztatása",
        "resetpass-submit-cancel": "Mégse",
        "resetpass-abort-generic": "A jelszómódosítást megszakította egy kiterjesztés.",
        "resetpass-expired": "A jelszavad lejárt. Adjál meg egy új jelszót a bejelentkezéshez!",
        "resetpass-expired-soft": "A jelszavad lejárt, ezért újat kell beállítanod. Válassz most egy új jelszót, vagy kattints a {{int:authprovider-resetpass-skip-label}} gombra, ha később akarod csak beállítani.",
-       "resetpass-validity-soft": "Adj meg egy új jelszót most, vagy kattints a \"{{int:authprovider-resetpass-skip-label}}\" gombra, ha később akarod megadni.",
+       "resetpass-validity-soft": "A jelszavad érvénytelen: $1\n\nAdj meg egy új jelszót most, vagy kattints a „{{int:authprovider-resetpass-skip-label}}” gombra, ha később akarod megadni.",
        "passwordreset": "Jelszó visszaállítása",
        "passwordreset-text-one": "A jelszó átmeneti beállításához töltsd ki az űrlapot.",
        "passwordreset-text-many": "{{PLURAL:$1|Az átmeneti jelszó elküldéséhez töltsd ki az alábbi mezők egyikét.}}",
        "passwordreset-emailelement": "Felhasználónév: \n$1\n\nIdeiglenes jelszó: \n$2",
        "passwordreset-emailsentemail": "Ha ez az e-mail-cím van a fiókodhoz társítva, egy jelszó-visszaállító e-mailt küldünk.",
        "passwordreset-emailsentusername": "Ha ehhez a felhasználónévhez tartozik e-mail cím, akkor egy jelszó-visszaállító levelet küld a rendszer.",
-       "passwordreset-emailsent-capture": "Az alább látható jelszó-visszaállító e-mail lett elküldve.",
-       "passwordreset-emailerror-capture": "A jelszó-visszaállító e-mail generálása megtörtént, mint az alább látszik, de elküldése a {{GENDER:$2|szerkesztőnek}} nem sikerült: $1",
+       "passwordreset-emailsent-capture2": "A jelszóvisszaállító {{PLURAL:$1|e-mailt|e-maileket}} elküldtük. A felhasználói {{PLURAL:$1|név és a jelszó|nevek és jelszavak listája}} lentebb látható.",
        "passwordreset-invalideamil": "Érvénytelen e-mail cím",
        "changeemail": "E-mail cím megváltoztatása vagy eltávolítása",
        "changeemail-header": "Töltsd ki ezt az űrlapot az e-mail-címed megváltoztatásához. Ha nem szeretnél semmilyen e-mail-címet kapcsolni a fiókodhoz, hagyd üresen az új e-mail-cím mezőjét az űrlap elküldésekor.",
-       "changeemail-passwordrequired": "Meg kell adnod a jelszavadat ennek a változtatásnak a végrehajtásához.",
        "changeemail-no-info": "A lap közvetlen eléréséhez be kell jelentkezned.",
        "changeemail-oldemail": "Jelenlegi e-mail-cím:",
        "changeemail-newemail": "Új e-mail-cím:",
        "minoredit": "Apró változtatás",
        "watchthis": "A lap figyelése",
        "savearticle": "Lap mentése",
-       "publishpage": "Lap mentése",
+       "savechanges": "Módosítások mentése",
+       "publishpage": "Lap közzététele",
+       "publishchanges": "Változtatások közzététele",
        "preview": "Előnézet",
        "showpreview": "Előnézet megtekintése",
        "showdiff": "Változtatások megtekintése",
        "userpage-userdoesnotexist": "Nincs „<nowiki>$1</nowiki>” nevű regisztrált felhasználónk.\nNézd meg, hogy valóban ezt a lapot szeretnéd-e létrehozni vagy szerkeszteni.",
        "userpage-userdoesnotexist-view": "Nincs regisztrálva „$1” szerkesztői azonosító.",
        "blocked-notice-logextract": "A felhasználó jelenleg blokkolva van.\nA blokkolási napló legutóbbi ide vonatkozó bejegyzése a következő:",
-       "clearyourcache": "'''Megjegyzés:''' mentés után frissítened kell a böngésződ gyorsítótárát, hogy lásd a változásokat.\n'''Firefox / Safari:''' tartsd lenyomva a Shift gombot és kattints a ''Frissítés'' gombra a címsorban, vagy használd a ''Ctrl–F5'' vagy ''Ctrl–R'' billentyűkombinációt (Mac-en ''Command–R'');\n'''Google Chrome:''' használd a ''Ctrl–Shift–R'' billentyűkombinációt (Mac-en ''Command–Shift–R'');\n'''Internet Explorer:''' tartsd nyomva a ''Ctrl''-t, és kattints a ''Frissítés'' gombra, vagy nyomj ''Ctrl–F5''-öt;\n'''Opera:''' ürítsd ki a gyorsítótárat a ''Beállítások / Haladó / Előzmények→Törlés most'' gombbal, majd frissítsd az oldalt.",
+       "clearyourcache": "<strong>Megjegyzés:</strong> mentés után frissítened kell a böngésződ gyorsítótárát, hogy lásd a változásokat.\n* <strong>Firefox / Safari:</strong> tartsd lenyomva a <em>Shift</em> gombot és kattints a <em>Frissítés</em> gombra a címsorban, vagy használd a <em>Ctrl–F5</em> vagy <em>Ctrl–R</em> (Macen <em>⌘–R</em>) billentyűkombinációt\n* <strong>Google Chrome:</strong> használd a <em>Ctrl–Shift–R</em> (Macen <em>⌘–Shift–R</em>) billentyűkombinációt\n* <strong>Internet Explorer:</strong> tartsd nyomva a <em>Ctrl</em>-t, és kattints a <em>Frissítés</em> gombra, vagy nyomj <em>Ctrl–F5</em>-öt\n* <strong>Opera:</strong> Nyisd meg a Beállításokat a <em>Menü</em>ből (Macen <em>Opera</em> menüből), majd válaszd az <em>Adatvédelem és biztonság → Böngészési adatok törlése → Gyorsítótáras képek és fájlok</em> opciót.",
        "usercssyoucanpreview": "'''Tipp:''' mentés előtt használd az „{{int:showpreview}}” gombot az új CSS-ed teszteléséhez.",
        "userjsyoucanpreview": "'''Tipp:''' mentés előtt használd az „{{int:showpreview}}” gombot az új JavaScipted teszteléséhez.",
        "usercsspreview": "'''Ne felejtsd el, hogy ez csak a felhasználói CSS-ed előnézete és még nincs elmentve!'''",
        "editingsection": "$1 szerkesztése (szakasz)",
        "editingcomment": "$1 szerkesztése (új szakasz)",
        "editconflict": "Szerkesztési ütközés: $1",
-       "explainconflict": "Valaki megváltoztatta a lapot, mióta elkezdted szerkeszteni. A felső szövegdobozban láthatod az oldal jelenlegi tartalmát. A te módosításaid az alsó dobozban találhatók. Át kell másolnod a módosításaidat a felsőbe! \n\n'''Csak''' a felső dobozban levő szöveg lesz elmentve, amikor a „{{int:savearticle}}” gombra kattintasz.",
+       "explainconflict": "Valaki megváltoztatta a lapot, mióta elkezdted szerkeszteni. A felső szövegdobozban láthatod az oldal jelenlegi tartalmát. A te módosításaid az alsó dobozban találhatók. Át kell másolnod a módosításaidat a felsőbe! \n\n<strong>Csak</strong> a felső dobozban levő szöveg lesz elmentve, amikor a „{{int:savearticle}}” gombra kattintasz.",
        "yourtext": "A te változatod",
        "storedversion": "A tárolt változat",
        "nonunicodebrowser": "'''Figyelem: A böngésződ nem Unicode kompatibilis. Egy kerülő megoldásként biztonságban szerkesztheted a cikkeket: a nem ASCII karakterek a szerkesztőablakban hexadeciális kódokként jelennek meg.'''",
        "content-model-css": "CSS",
        "content-json-empty-object": "Üres objektum",
        "content-json-empty-array": "Üres tömb",
+       "deprecated-self-close-category": "Érvénytelen önzáró HTML-címkéket használó lapok",
        "duplicate-args-warning": "<strong>Figyelmeztetés:</strong> A(z) [[:$1]] lap dupla értékkel hívja meg a(z) [[:$2]] sablont („$3” paraméter). Csak az utolsó érték lesz felhasználva.",
        "duplicate-args-category": "Dupla paramétermegadást tartalmazó lapok",
        "duplicate-args-category-desc": "Az oldal olyan sablon hívásokat tartalmaz, amely ugyanazt a paramétert használja, például <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> or <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "A szerkesztés már vissza lett állítva.",
        "undo-summary": "Visszavontam [[Special:Contributions/$2|$2]] ([[User talk:$2|vita]]) szerkesztését (oldid: $1)",
        "undo-summary-username-hidden": "A rejtett felhasználó által végzett $1 változat visszavonása",
-       "cantcreateaccounttitle": "Felhasználói fiók létrehozása sikertelen",
        "cantcreateaccount-text": "Erről az IP-címről ('''$1''') nem lehet regisztrálni, mert [[User:$3|$3]] blokkolta az alábbi indokkal:\n\n:''$2''",
        "cantcreateaccount-range-text": "A regisztrációt a(z) <strong>$1</strong> IP-címtartományban, amelybe a te IP-címed (<strong>$4</strong>) is tartozik, [[User:$3|$3]] blokkolta.",
        "viewpagelogs": "A lap a rendszernaplókban",
        "userrights-changeable-col": "Megváltoztatható csoportok",
        "userrights-unchangeable-col": "Nem megváltoztatható csoportok",
        "userrights-conflict": "Felhasználói jogok ütközése! Kérlek, végezd el újra a változtatásokat.",
-       "userrights-removed-self": "Sikeresen eltávolítottad a saját jogosultságaidat, így már nem tudod elérni ezt az oldalt.",
+       "userrights-removed-self": "Eltávolítottad a saját jogosultságaidat, így már nem tudod elérni ezt az oldalt.",
        "group": "Csoport:",
        "group-user": "szerkesztők",
        "group-autoconfirmed": "automatikusan megerősített szerkesztők",
        "right-override-export-depth": "Lapok exportálása a hivatkozott lapokkal együtt, legfeljebb 5-ös mélységig",
        "right-sendemail": "e-mail küldése más felhasználóknak",
        "right-passwordreset": "Jelszó visszaállítási emailek megtekintése",
-       "right-managechangetags": "[[Special:Tags|címkék]] létrehozása és törlése az adatbázisban",
+       "right-managechangetags": "[[Special:Tags|címkék]] létrehozása és (de)aktiválása",
        "right-applychangetags": "[[Special:Tags|címkék]] alkalmazása a változakra",
        "right-changetags": "egyedi lapváltozatokon és naplóbejegyzéseken tetszőleges [[Special:Tags|címkék]] hozzáadása és törlése",
        "right-deletechangetags": "[[Special:Tags|Címkék]] törlése az adatbázisból",
        "grant-highvolume": "nagy mennyiségű szerkesztés",
        "grant-oversight": "felhasználók és lapváltozatok elrejtése",
        "grant-patrol": "szerkesztések ellenőrzése",
+       "grant-privateinfo": "Hozzáférés a személyes információkhoz",
        "grant-protect": "lapok védelme és védelem feloldása",
        "grant-rollback": "szerkesztések gyors visszaállítása",
        "grant-sendemail": "e-mail küldése más felhasználóknak",
        "rightslogtext": "Ez a rendszernapló a felhasználó jogosultságok változásait mutatja.",
        "action-read": "lap olvasása",
        "action-edit": "lap szerkesztése",
-       "action-createpage": "új lap készítése",
-       "action-createtalk": "vitalap készítése",
+       "action-createpage": "ennek a lapnak a létrehozása",
+       "action-createtalk": "vitalap létrehozása",
        "action-createaccount": "felhasználói fiók elkészítése",
        "action-history": "laptörténet megtekintése",
        "action-minoredit": "szerkesztés aprónak jelölése",
        "action-viewmyprivateinfo": "személyes adatok megtekintése",
        "action-editmyprivateinfo": "személyes adatok szerkesztése",
        "action-editcontentmodel": "a lap tartalom modelljének szerkesztése",
-       "action-managechangetags": "adatbázis címkék létrehozása és törlése",
+       "action-managechangetags": "adatbáziscímkék létrehozása és (de)aktiválása",
        "action-applychangetags": "változtatások címkézése",
        "action-changetags": "egyedi változtatások és napló bejegyzések tetszőleges címkével való ellátása és törlése",
        "action-deletechangetags": "címkék törlése az adatbáziból",
+       "action-purge": "oldal gyorsítótárának ürítése",
        "nchanges": "$1 változtatás",
        "enhancedrc-since-last-visit": "$1 az utolsó látogatás óta",
        "enhancedrc-history": "történet",
        "recentchanges-page-added-to-category": "[[:$1]] hozzáadva a kategóriához",
        "recentchanges-page-added-to-category-bundled": "[[:$1]] hozzáadva a kategóriához, [[Special:WhatLinksHere/$1|ez a lap be van illesztve más lapokra]]",
        "recentchanges-page-removed-from-category": "[[:$1]] eltávolítva a kategóriából",
-       "recentchanges-page-removed-from-category-bundled": "[[:$1]] és {{PLURAL:$2|egy oldal|$2 oldal}} eltávolítva a kategóriából",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] eltávolítva a kategóriából, [[Special:WhatLinksHere/$1|ez a lap be van illesztve más lapokra]]",
        "upload": "Fájl feltöltése",
        "uploadbtn": "Fájl feltöltése",
        "reuploaddesc": "Visszatérés a feltöltési űrlaphoz.",
        "uploaded-script-svg": "A feltöltött SVG fájlodban szkriptelemet találtunk: \"$1\".",
        "uploaded-hostile-svg": "Nem biztonságos CSS kódot találtunk a feltöltött SVG fájlod stíluselemei között.",
        "uploaded-event-handler-on-svg": "Az alábbi eseménykezelő-attribútum beállítása nem megengedett az SVG fájlokban: <code>$1=$2</code>.",
-       "uploaded-href-unsafe-target-svg": "Nem biztonságos célra mutató href-et találtam a feltöltött SVG fájlban: <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "Nem biztonságos adatra mutató href-et találtunk a feltöltött SVG-fájlban: URI-cél <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-animate-svg": "A feltöltött SVG fájlban \"animate\" taget találtam, ami az alábbi \"from\" attribútumával megváltoztathat egy href-et: <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-setting-handler-svg": "Az SVG kódok, amelyek a \"handler\" attribútumot távolra/adatra/szkriptre állítják, le vannak tiltva. A feltöltött SVG fájlban a következőt találtam: <code>$1=\"$2\"</code>.",
        "uploaded-remote-url-svg": "Az SVG kódok, amelyek bármely stílus-attribútumot távoli URL-ra állítják, le vannak tiltva. A feltöltött SVG fájlban a következőt találtam: <code>$1=\"$2\"</code>.",
        "upload-too-many-redirects": "Az URL túl sokszor volt átirányítva",
        "upload-http-error": "HTTP-hiba történt: $1",
        "upload-copy-upload-invalid-domain": "Másolás nem engedélyezett ebből a tartományból.",
+       "upload-dialog-disabled": "Fájl feltöltés ezzel a párbeszéddel tiltott ezen a wikin.",
        "upload-dialog-title": "Fájl feltöltése",
        "upload-dialog-button-cancel": "Mégse",
        "upload-dialog-button-done": "Kész",
        "pageinfo-article-id": "Lapazonosító",
        "pageinfo-language": "Laptartalom nyelve",
        "pageinfo-content-model": "Oldal tartalom modell",
+       "pageinfo-content-model-change": "módosítás",
        "pageinfo-robot-policy": "Indexelés robottal",
        "pageinfo-robot-index": "Engedélyezett",
        "pageinfo-robot-noindex": "Nem engedélyezett",
        "tags-deactivate": "deaktiválás",
        "tags-hitcount": "{{PLURAL:$1|Egy|$1}} változtatás",
        "tags-manage-no-permission": "Nincs engedélyed a változtatások címkéinek kezeléséhez.",
+       "tags-manage-blocked": "Nem tudja kezelni a változás kategóriát, amíg blokkolt.",
        "tags-create-heading": "Új címke létrehozása",
        "tags-create-explanation": "Alapértelmezés szerint az újonnan létrehozott címkék felhasználók és botok által manuálisan hozzáadhatók lesznek.",
        "tags-create-tag-name": "Címke neve:",
        "tags-delete-not-found": "A(z) „$1” címke nem létezik.",
        "tags-delete-too-many-uses": "A(z) „$1” címke több mint $2 lapváltoztatásban szerepel, ezáltal nem törölhető.",
        "tags-delete-warnings-after-delete": "A(z) „$1” címke sikeresen törölve lett, de a következő {{PLURAL:$2|figyelmeztetést|figyelmeztetéseket}} találtam:",
+       "tags-delete-no-permission": "Nincs engedélye a változás címkéinek törléséhez.",
        "tags-activate-title": "Címke aktiválása",
        "tags-activate-question": "Éppen a(z) „$1” címke aktiválására készülsz.",
        "tags-activate-reason": "Indoklás:",
        "mw-widgets-dateinput-placeholder-month": "ÉÉÉÉ-HH",
        "mw-widgets-titleinput-description-new-page": "a lap még nem létezik",
        "mw-widgets-titleinput-description-redirect": "átirányítás ide: $1",
-       "api-error-blacklisted": "Válasszon egy másik, leíró címet.",
        "sessionprovider-generic": "$1-munkamenetek",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sütialapú munkamenetek",
        "sessionprovider-nocookies": "A sütik le lehetnek tiltva. Engedélyezd a sütiket, és próbáld meg újra!",
        "log-action-filter-move": "Átnevezés típusa:",
        "log-action-filter-patrol": "Járőrözés típusa:",
        "log-action-filter-protect": "Lapvédelem típusa:",
+       "log-action-filter-upload": "Feltöltés típusa:",
        "log-action-filter-all": "Mind",
        "log-action-filter-block-block": "Blokk",
        "log-action-filter-block-reblock": "Blokk módosítása",
        "log-action-filter-delete-delete": "Laptörlés",
        "log-action-filter-delete-restore": "Visszaállítás",
        "log-action-filter-delete-event": "Naplótörlés",
+       "log-action-filter-managetags-create": "Címke létrehozása",
+       "log-action-filter-managetags-delete": "Címke törlése",
+       "log-action-filter-managetags-activate": "Tag aktiválása",
+       "log-action-filter-newusers-create": "Létrehozás által anonim felhasználó által",
+       "log-action-filter-newusers-create2": "Létrehozás regisztrált felhasználó által",
        "log-action-filter-newusers-autocreate": "Automatikus létrehozás",
+       "log-action-filter-newusers-byemail": "Létrehozás jelszóval, e-mail által küldve",
        "log-action-filter-protect-protect": "Lapvédelem",
        "log-action-filter-protect-unprotect": "Védelem feloldása",
+       "log-action-filter-rights-rights": "Kézi módosítás",
        "log-action-filter-upload-upload": "Új feltöltés",
+       "authmanager-create-disabled": "Új fiók létrehozása tiltva.",
+       "authmanager-create-from-login": "A fiókja létrehozásához, kérjük, töltse ki az alábbi mezőket.",
+       "authmanager-create-not-in-progress": "Fiók létrehozása nincs folyamatban, vagy a folyamat adatai elvesztek. Kérjük, indítsa újra az elejétől.",
+       "authmanager-authplugin-setpass-failed-title": "Jelszó megváltoztatása nem sikerült",
+       "authmanager-authplugin-setpass-failed-message": "A hitelesítés beépülője megtagadta a jelszó módosítását.",
+       "authmanager-authplugin-create-fail": "A hitelesítés beépülője megtagadta a fiók létrehozását.",
+       "authmanager-authplugin-setpass-denied": "A hitelesítés beépülője nem teszi lehetővé a jelszó megváltoztatását.",
+       "authmanager-authplugin-setpass-bad-domain": "Érvénytelen domain.",
+       "authmanager-autocreate-noperm": "Az automatikus fióklétrehozás nem engedélyezett.",
+       "authmanager-autocreate-exception": "A fiókok automatikus létrehozását átmenetileg letiltottuk a korábbi hibák miatt.",
+       "authmanager-userdoesnotexist": "A(z) „$1” felhasználó nincs regisztrálva.",
+       "authmanager-retype-help": "Jelszó még egyszer a megerősítéshez.",
+       "authmanager-email-label": "E-mail",
+       "authmanager-email-help": "E-mail-cím",
+       "authmanager-realname-label": "Valódi név",
+       "authmanager-realname-help": "A felhasználó valódi neve",
+       "authmanager-provider-password": "Jelszó alapú hitelesítés",
+       "authmanager-provider-password-domain": "Jelszó - domain-alapú hitelesítés",
+       "authmanager-provider-temporarypassword": "Ideiglenes jelszó",
        "authprovider-resetpass-skip-label": "Kihagy",
        "cannotauth-not-allowed-title": "Engedély megtagadva",
-       "credentialsform-account": "Fiók neve:"
+       "credentialsform-account": "Fiók neve:",
+       "cannotlink-no-provider-title": "Nincsenek csatolható fiókok",
+       "cannotlink-no-provider": "Nincsenek csatolható fiókok.",
+       "linkaccounts": "A fiókok csatolása",
+       "linkaccounts-success-text": "A fiók csatolva."
 }
index a94a60a..20f83e5 100644 (file)
        "yourpasswordagain": "Կրկնեք գաղտնաբառը",
        "createacct-yourpasswordagain": "Հաստատեք գաղտնաբառը",
        "createacct-yourpasswordagain-ph": "Կրկին մուտքագրեք գաղտնաբառը",
-       "remembermypassword": "Հիշել իմ մուտքը այս դիտարկչում ($1 {{PLURAL:$1|օրից}} ոչ ավել ժամկետով)",
        "userlogin-remembermypassword": "Մուտք գործած մնալ",
        "userlogin-signwithsecure": "Օգտագործել անվտանգ միացում",
        "cannotloginnow-title": "Այժմ դուրս գալ անհնար է",
        "passwordreset-emailtitle": "{{SITENAME}} հաշվի մանրամասները",
        "passwordreset-emailelement": "Մասնակցային անունը՝ \n$1\n\nԺամանակավոր գաղտնաբառը՝ \n$2",
        "passwordreset-emailsentemail": "Ուղարկվեց հիշեցնող էլ․ նամակ։",
-       "passwordreset-emailsent-capture": "Ուղարկվեց հիշեցնող էլ․ նամակ։ Այն ներկայացված է ստորև։",
-       "passwordreset-emailerror-capture": "Ուղարկվեց հիշեցնող էլ․ նամակ։ Այն ներկայացված է ստորև։ Սակայն մասնակցին ուղարկելը չհաջողվեց․",
        "changeemail": "Փոխել էլ. հասցեն",
        "changeemail-header": "Փոխել հաշվի էլ․ հասցեն",
        "changeemail-oldemail": "Ներկա էլ․ հասցե․",
        "minoredit": "Սա չնչին խմբագրում է",
        "watchthis": "Հսկել այս էջը",
        "savearticle": "Հիշել էջը",
+       "savechanges": "Պահպանել փոփոխությունները",
+       "publishpage": "Հիշել փոփոխությունները",
+       "publishchanges": "Հիշել փոփոխությունները",
        "preview": "Նախադիտում",
        "showpreview": "Նախադիտել",
        "showdiff": "Կատարված փոփոխությունները",
        "undo-success": "Խմբագրումը կարող է հետ շրջվել։ Ստուգեք տարբերակների համեմատությունը ստորև, որպեսզի համոզվեք, որ դա է ձեզ հետաքրքրող փոփոխությունը և մատնահարեք «Հիշել էջը»՝ գործողությունն ավարտելու համար։",
        "undo-failure": "Խմբագրումը չի կարող հետ շրջվել միջանկյալ խմբագրումների ընդհարման պատճառով։",
        "undo-summary": "Հետ է շրջվում $1 խմբագրումը, որի հեղինակն է՝ [[Special:Contributions/$2|$2]] ([[User talk:$2|քննարկում]]) {{GENDER:$2|մասնակիցը|մասնակցուհին}}",
-       "cantcreateaccounttitle": "Չհաջողվեց ստեղծել մասնակցային հաշիվ",
        "cantcreateaccount-text": "Այս IP-հասցեից ('''$1''') մասնակցային հաշվի ստեղծումը արգելափակվել է [[User:$3|$3]] մասնակցի կողմից։\n\n$3 մասնակիցը տվել է հետևյալ պատճառը. ''$2''",
        "viewpagelogs": "Դիտել այս էջի տեղեկամատյանները",
        "nohistory": "Այս էջը չունի խմբագրումների պատմություն։",
index 655058d..be5860c 100644 (file)
        "tagline": "De {{SITENAME}}",
        "help": "Adjuta",
        "search": "Recerca",
+       "search-ignored-headings": " #<!-- non modificar in alcun modo iste linea --> <pre>\n# Titulos de sectiones que essera ignorate per le recerca.\n# Cambiamentos in isto habera effecto post le indexation del paginas con iste sectiones.\n# Tu pote fortiar le re-indexation de un pagina per medio de un modification nulle.\n# Le syntaxe es:\n#   * Toto a partir de un character \"#\" usque al fin del linea es un commento\n#   * Cata linea non vacue es un titulo exacte a ignorar, con distinction inter majusculas e minusculas\nReferentias\nLigamines externe\nVide etiam\n #</pre> <!-- non modificar in alcun modo iste linea -->",
        "searchbutton": "Cercar",
        "go": "Va",
        "searcharticle": "Va",
        "cannotlogoutnow-title": "Impossibile clauder session ora",
        "cannotlogoutnow-text": "Non es possibile clauder le session usante $1.",
        "welcomeuser": "Benvenite, $1!",
-       "welcomecreation-msg": "Tu conto ha essite create.\nNon oblida personalisar tu [[Special:Preferences|preferentias in {{SITENAME}}]].",
+       "welcomecreation-msg": "Tu conto ha essite create.\nTu pote personalisar tu [[Special:Preferences|preferentias]] in {{SITENAME}}, si tu vole.",
        "yourname": "Nomine de usator:",
        "userlogin-yourname": "Nomine de usator",
        "userlogin-yourname-ph": "Entra tu nomine de usator",
        "yourpasswordagain": "Repete contrasigno:",
        "createacct-yourpasswordagain": "Confirma contrasigno",
        "createacct-yourpasswordagain-ph": "Repete le contrasigno",
-       "remembermypassword": "Memorar mi contrasigno in iste navigator (pro un maximo de $1 {{PLURAL:$1|die|dies}})",
        "userlogin-remembermypassword": "Mantener mi session aperte",
        "userlogin-signwithsecure": "Usar un connexion secur",
+       "cannotlogin-title": "Impossibile aperir session",
+       "cannotlogin-text": "Non es possibile aperir un session.",
        "cannotloginnow-title": "Impossibile aperir session ora",
        "cannotloginnow-text": "Non es possibile aperir un session usante $1.",
+       "cannotcreateaccount-title": "Impossibile crear contos",
+       "cannotcreateaccount-text": "Le creation directe de contos non es activate in iste wiki.",
        "yourdomainname": "Tu dominio:",
        "password-change-forbidden": "Non es possibile cambiar le contrasigno in iste wiki.",
        "externaldberror": "O il occurreva un error in le base de datos de authentication, o tu non ha le autorisation de actualisar tu conto externe.",
        "passwordreset-emailelement": "Nomine de usator: \n$1\n\nContrasigno temporari: \n$2",
        "passwordreset-emailsentemail": "Si iste adresse es associate a tu conto, alora un e-mail pro reinitialisar le contrasigno essera inviate.",
        "passwordreset-emailsentusername": "Si il ha un adresse de e-mail associate a iste conto, alora un e-mail pro reinitialisar le contrasigno essera inviate.",
-       "passwordreset-emailsent-capture": "Un message de e-mail pro le reinitialisation del contrasigno ha essite inviate; iste message es monstrate hic infra.",
-       "passwordreset-emailerror-capture": "Un e-mail pro le reinitialisation del contrasigno ha essite generate; iste message es monstrate hic infra, ma le invio al {{GENDER:$2|usator}} ha fallite: $1",
        "passwordreset-emailsent-capture2": "Le {{PLURAL:$1|message|messages}} de e-mail pro reinitialisation de contrasigno ha essite inviate. Le {{PLURAL:$1|nomine de usator e contrasigno|lista de nomines de usator e contrasignos}} appare hic infra.",
        "passwordreset-emailerror-capture2": "Le invio de e-mail al {{GENDER:$2|usator}} ha fallite: $1 Le {{PLURAL:$3|nomine de usator e contrasigno|lista de nomines de usator e contrasignos}} appare hic infra.",
        "passwordreset-nocaller": "Un appellator debe esser fornite",
        "passwordreset-nodata": "Ni un nomine de usator ni un adresse de e-mail ha essite fornite",
        "changeemail": "Cambiar o remover adresse de e-mail",
        "changeemail-header": "Completa iste formulario pro cambiar tu adresse de e-mail. Si tu vole remover le association de omne adresse de e-mail ab tu conto, lassa le campo pro le nove adresse de e-mail vacue quando tu submitte le formulario.",
-       "changeemail-passwordrequired": "Essera necessari entrar tu contrasigno pro confirmar iste cambiamento.",
        "changeemail-no-info": "Tu debe aperir un session pro poter acceder directemente a iste pagina.",
        "changeemail-oldemail": "Adresse de e-mail actual:",
        "changeemail-newemail": "Adresse de e-mail nove:",
        "content-model-css": "CSS",
        "content-json-empty-object": "Objecto vacue",
        "content-json-empty-array": "Array vacue",
+       "deprecated-self-close-category": "Paginas que usa etiquettas HTML auto-claudite non valide",
+       "deprecated-self-close-category-desc": "Le pagina contine etiquettas HTML auto-claudite non valide, como <code>&lt;b/></code> o <code>&lt;span/></code>. Le comportamento de istes cambiara proximemente pro esser in accordo con le specification HTML5, dunque lor uso in wikitexto es obsolete.",
        "duplicate-args-warning": "<strong>Attention:</strong> [[:$1]] appella [[:$2]] con plure valores pro le parametro \"$3\". Solmente le ultime valor fornite essera usate.",
        "duplicate-args-category": "Paginas que usa parametros duplicate in appellos de patrono",
        "duplicate-args-category-desc": "Le pagina contine appellos de patrono que usa duplicatos de parametros, como per exemplo <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> or <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Pare que iste modification ha jam essite disfacite.",
        "undo-summary": "Annullava le version $1 per [[Special:Contributions/$2|$2]] ([[User talk:$2|Discussion]] | [[Special:Contributions/$2|{{MediaWiki:Contribslink}}]])",
        "undo-summary-username-hidden": "Disfacer le revision $1 facite per un usator celate",
-       "cantcreateaccounttitle": "Non pote crear conto",
        "cantcreateaccount-text": "Le creation de contos desde iste adresse IP ('''$1''') ha essite blocate per [[User:$3|$3]].\n\nLe motivo que $3 dava es ''$2''",
        "cantcreateaccount-range-text": "Le creation de contos ab le adresses IP in le intervallo <strong>$1</strong>, le qual include tu adresse IP (<strong>$4</strong>), ha essite blocate per [[User:$3|$3]].\n\nLe motivo fornite per $3 es <em>$2</em>",
        "viewpagelogs": "Vider le entratas del registro pro iste pagina",
        "grant-group-high-volume": "Exequer actiones in massa",
        "grant-group-customization": "Personalisation e perferentias",
        "grant-group-administration": "Exequer actiones administrative",
+       "grant-group-private-information": "Acceder a tu datos private",
        "grant-group-other": "Activitates diverse",
        "grant-blockusers": "Blocar e disblocar usatores",
        "grant-createaccount": "Crear contos",
        "grant-highvolume": "Modification in massa",
        "grant-oversight": "Celar usatores e supprimer versiones",
        "grant-patrol": "Patruliar cambiamentos a paginas",
+       "grant-privateinfo": "Acceder a information private",
        "grant-protect": "Proteger e disproteger paginas",
        "grant-rollback": "Revocar cambiamentos a paginas",
        "grant-sendemail": "Inviar e-mail a altere usatores",
        "action-applychangetags": "applicar etiquettas al proprie modificationes",
        "action-changetags": "adder e remover qualcunque etiquettas sur individual versiones e entratas de registro",
        "action-deletechangetags": "deler etiquettas del base de datos",
+       "action-purge": "purgar iste pagina",
        "nchanges": "$1 {{PLURAL:$1|modification|modificationes}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|desde le ultime visita}}",
        "enhancedrc-history": "historia",
        "file-thumbnail-no": "Le nomine del file comencia con <strong>$1</strong>.\nIllo pare esser un imagine a grandor reducite ''(miniatura)''.\nSi tu possede iste imagine in plen resolution, incarga lo, alteremente cambia le nomine del file per favor.",
        "fileexists-forbidden": "Un file con iste nomine existe ja, e non pote esser superscribite.\nSi tu vole ancora incargar iste file, per favor retorna e usa un nove nomine. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Un file con iste nomine existe ja in le repositorio de files commun.\nSi tu vole totevia incargar iste file, per favor retorna e usa un nove nomine. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Le file incargate es un copia exacte del version actual de <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Le file incargate es un copia exacte de {{PLURAL:$2|un version|versiones}} precedente de <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Iste file es un duplicato del sequente {{PLURAL:$1|file|files}}:",
        "file-deleted-duplicate": "Un file identic a iste file ([[:$1]]) esseva ja delite anteriormente. Tu deberea verificar le registro de deletiones concernente iste file ante de re-incargar lo.",
        "file-deleted-duplicate-notitle": "Un file identic a iste file ha essite delite anteriormente, e le titulo ha essite supprimite. Tu deberea demandar a un persona con le privilegio de vider datos de files supprimite a examinar le situation ante de incargar lo de novo.",
        "uploadstash-errclear": "Le radimento del files ha fallite.",
        "uploadstash-refresh": "Refrescar le lista de files",
        "uploadstash-thumbnail": "vider miniatura",
+       "uploadstash-exception": "Impossibile immagazinar le incargamento in le reserva ($1): \"$2\".",
        "invalid-chunk-offset": "Position de segmento invalide",
        "img-auth-accessdenied": "Accesso refusate",
        "img-auth-nopathinfo": "PATH_INFO manca.\nLe servitor non ha essite configurate pro passar iste information.\nIllo pote esser basate super CGI e non pote supportar img_auth.\nVide https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "filerevert-submit": "Reverter",
        "filerevert-success": "'''[[Media:$1|$1]]''' ha essite revertite al [$4 version del $3 a $2].",
        "filerevert-badversion": "Non existe un version local anterior de iste file con le data e hora providite.",
+       "filerevert-identical": "Le version actual del file es jam identic al file seligite.",
        "filedelete": "Deler $1",
        "filedelete-legend": "Deler file",
        "filedelete-intro": "Tu es super le puncto de deler le file '''[[Media:$1|$1]]''' con tote su historia.",
        "watchnologin": "Tu non ha aperite un session",
        "addwatch": "Adder al observatorio",
        "addedwatchtext": "Le pagina \"[[:$1]]\", e su pagina de discussion, ha essite addite a [[Special:Watchlist|tu observatorio]].",
+       "addedwatchtext-talk": "\"[[:$1]]\" e su pagina associate ha essite addite a tu [[Special:Watchlist|observatorio]].",
        "addedwatchtext-short": "Le pagina \"$1\" ha essite addite a tu observatorio.",
        "removewatch": "Remover del observatorio",
        "removedwatchtext": "Le pagina \"[[:$1]]\", e su pagina de discussion, ha essite removite de [[Special:Watchlist|tu observatorio]].",
+       "removedwatchtext-talk": "\"[[:$1]]\" e su pagina associate ha essite removite de tu [[Special:Watchlist|observatorio]].",
        "removedwatchtext-short": "Le pagina \"$1\" ha essite removite de tu observatorio.",
        "watch": "Observar",
        "watchthispage": "Observar iste pagina",
        "rollbacklinkcount-morethan": "revocar plus de $1 {{PLURAL:$1|modification|modificationes}}",
        "rollbackfailed": "Revocation fallite",
        "rollback-missingparam": "Manca parametros obligatori in le requesta.",
+       "rollback-missingrevision": "Impossibile cargar le datos del version.",
        "cantrollback": "Impossibile revocar le modification;\nle ultime contributor es le sol autor de iste pagina.",
        "alreadyrolled": "Non pote revocar le ultime modification de [[:$1]] per [[User:$2|$2]] ([[User talk:$2|discussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nun altere persona ha ja modificate o revocate le pagina.\n\nLe ultime modification esseva facite per [[User:$3|$3]] ([[User talk:$3|discussion]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Le summario del modification esseva: <em>$1</em>.",
        "protect_expiry_invalid": "Le tempore de expiration es invalide.",
        "protect_expiry_old": "Le tempore de expiration es in le passato.",
        "protect-unchain-permissions": "Disblocar ulterior optiones de protection",
-       "protect-text": "In basso tu pote vider e modificar le nivello de protection del pagina '''$1'''.",
-       "protect-locked-blocked": "Tu non pote cambiar le nivellos de protection durante que tu es blocate.\nEcce le configurationes actual del pagina '''$1''':",
-       "protect-locked-dblock": "Le nivellos de protection non pote esser cambiate proque es active un blocada del base de datos.\nEcce le configurationes actual del pagina '''$1''':",
+       "protect-text": "Hic tu pote vider e cambiar le nivello de protection del pagina <strong>$1</strong>.",
+       "protect-locked-blocked": "Tu non pote cambiar le nivellos de protection durante que tu es blocate.\nEcce le configuration actual del pagina <strong>$1</strong>:",
+       "protect-locked-dblock": "Le nivellos de protection non pote esser cambiate perque le base de datos es blocate.\nEcce le configuration actual del pagina <strong>$1</strong>:",
        "protect-locked-access": "Tu conto non ha le permission de cambiar le nivellos de protection de paginas.\nEcce le configurationes actual del pagina '''$1''':",
        "protect-cascadeon": "Iste pagina es actualmente protegite proque illo es transcludite in le sequente {{PLURAL:$1|pagina, le qual|paginas, le quales}} ha activate le protection in cascada.\nCambiamentos in le nivello de protection de iste pagina non influentia le protection in cascada.",
        "protect-default": "Permitter a tote le usatores",
        "undeletehistorynoadmin": "Iste pagina ha essite delite.\nLe motivo del deletion se monstra in le summario infra, con le detalios del usatores que habeva modificate iste pagina ante le deletion.\nLe texto complete de iste versiones delite es solmente disponibile al administratores.",
        "undelete-revision": "Version delite de $1 (facite le $4 a $5) per $3:",
        "undeleterevision-missing": "Version invalide o mancante.\nEs possibile que le adresse URL es invalide, o que le version ha essite restaurate o eliminate del archivo.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|Un version|$1 versiones}} non poteva esser restaurate, perque {{PLURAL:$1|su|lor}} <code>rev_id</code> esseva jam in uso.",
        "undelete-nodiff": "Nulle version precedente trovate.",
        "undeletebtn": "Restaurar",
        "undeletelink": "vider/restaurar",
        "undeletecomment": "Motivo:",
        "undeletedrevisions": "{{PLURAL:$1|1 version|$1 versiones}} restaurate",
        "undeletedrevisions-files": "{{PLURAL:$1|1 version|$1 versiones}} e {{PLURAL:$2|1 file|$2 files}} restaurate",
-       "undeletedfiles": "$1 {{PLURAL:$1|archivo|archivos}} restaurate",
-       "cannotundelete": "Le restauration ha fallite:\n$1",
+       "undeletedfiles": "$1 {{PLURAL:$1|file|files}} restaurate",
+       "cannotundelete": "Le restauration ha partial- o totalmente fallite:\n$1",
        "undeletedpage": "'''$1 ha essite restaurate'''\n\nConsulta le [[Special:Log/delete|registro de deletiones]] pro un lista de deletiones e restaurationes recente.",
        "undelete-header": "Vide [[Special:Log/delete|le registro de deletiones]] pro un lista de paginas recentemente delite.",
        "undelete-search-title": "Cercar in paginas delite",
        "anoncontribs": "Contributiones",
        "contribsub2": "Pro {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "Le conto de usator \"$1\" non es registrate.",
-       "nocontribs": "Necun modification ha essite trovate secundo iste criterios.",
+       "nocontribs": "Nulle modification correspondente a iste criterios ha essite trovate.",
        "uctop": "(ultime)",
        "month": "A partir del mense (e anterior):",
        "year": "A partir del anno (e anterior):",
        "sp-contributions-newbies-sub": "Pro nove contos",
        "sp-contributions-newbies-title": "Contributiones de nove contos de usator",
        "sp-contributions-blocklog": "Registro de blocadas",
-       "sp-contributions-suppresslog": "contributiones supprimite de usatores",
-       "sp-contributions-deleted": "contributiones delite de usatores",
+       "sp-contributions-suppresslog": "contributiones supprimite del {{GENDER:$1|usator}}",
+       "sp-contributions-deleted": "contributiones delite del {{GENDER:$1|usator}}",
        "sp-contributions-uploads": "incargamentos",
        "sp-contributions-logs": "registros",
        "sp-contributions-talk": "discussion",
        "ipb-blocklist": "Vider blocadas existente",
        "ipb-blocklist-contribs": "Contributiones de {{GENDER:$1|$1}}",
        "ipb-blocklist-duration-left": "$1 restante",
-       "unblockip": "Disblocar adresse IP",
-       "unblockiptext": "Usa le formulario infra pro restaurar le accesso de scriptura\na un adresse IP blocate previemente.",
+       "unblockip": "Disblocar usator",
+       "unblockiptext": "Usa le formulario infra pro restaurar le accesso de scriptura a un adresse IP o nomine de usator blocate previemente.",
        "ipusubmit": "Cancellar iste blocada",
-       "unblocked": "[[User:$1|$1]] ha essite disblocate",
+       "unblocked": "[[User:$1|$1]] ha essite disblocate.",
        "unblocked-range": "$1 ha essite disblocate",
-       "unblocked-id": "Le blocada $1 ha essite eliminate",
+       "unblocked-id": "Le blocada $1 ha essite removite.",
        "unblocked-ip": "[[Special:Contributions/$1|$1]] ha essite disblocate.",
        "blocklist": "Usatores blocate",
        "ipblocklist": "Usatores blocate",
        "locknoconfirm": "Tu non ha marcate le quadrato de confirmation.",
        "lockdbsuccesssub": "Base de datos blocate con successo",
        "unlockdbsuccesssub": "Base de datos disblocate con successo",
-       "lockdbsuccesstext": "Le base de datos de {{SITENAME}} ha essite blocate.\n<br />Rememora te de disblocar lo post completar tu mantenentia.",
-       "unlockdbsuccesstext": "Le base de datos de {{SITENAME}} ha essite disblocate.",
+       "lockdbsuccesstext": "Le base de datos ha essite blocate.<br />\nNon oblida de [[Special:UnlockDB|disblocar lo]] post haber terminate le mantenentia.",
+       "unlockdbsuccesstext": "Le base de datos ha essite disblocate.",
        "lockfilenotwritable": "Impossibile scriber al file de blocada del base de datos.\nPro blocar o disblocar le base de datos, le servitor web debe poter scriber a iste file.",
        "databaselocked": "Le base de datos es jam blocate.",
        "databasenotlocked": "Le base de datos non es blocate.",
        "movenologintext": "Tu debe esser un usator registrate e [[Special:UserLogin|aperir un session]] pro poter renominar un pagina.",
        "movenotallowed": "Tu non ha le permission de renominar paginas.",
        "movenotallowedfile": "Tu non ha le permission de renominar files.",
-       "cant-move-user-page": "Tu non ha le permission de renominar paginas principal de usatores.",
+       "cant-move-user-page": "Tu non ha le permission de renominar paginas de usator (excepte subpaginas).",
        "cant-move-to-user-page": "Tu non ha le permission de renominar un pagina verso un pagina de usator (excepte un subpagina de usator).",
        "cant-move-category-page": "Tu non ha le permission de renominar paginas de categoria.",
        "cant-move-to-category-page": "Tu non ha le permission de renominar un pagina in un pagina de categoria.",
        "pageinfo-article-id": "ID del pagina",
        "pageinfo-language": "Lingua del contento del pagina",
        "pageinfo-content-model": "Modello de contento de pagina",
+       "pageinfo-content-model-change": "cambiar",
        "pageinfo-robot-policy": "Indexation per robots",
        "pageinfo-robot-index": "Permittite",
        "pageinfo-robot-noindex": "Non permittite",
        "table_pager_empty": "Nulle resultato",
        "autosumm-blank": "Pagina vacuate",
        "autosumm-replace": "Contento reimplaciate per '$1'",
-       "autoredircomment": "Redirection verso [[$1]]",
+       "autoredircomment": "Pagina redirigite verso [[$1]]",
        "autosumm-new": "Pagina create con '$1'",
        "autosumm-newblank": "Pagina vacue create",
        "lag-warn-normal": "Le modificationes plus nove que $1 {{PLURAL:$1|secunda|secundas}} possibilemente non se revela in iste lista.",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
        "mw-widgets-titleinput-description-new-page": "pagina non existe ancora",
        "mw-widgets-titleinput-description-redirect": "redirection a $1",
-       "api-error-blacklisted": "Per favor elige un altere titulo, plus descriptive.",
        "sessionmanager-tie": "Impossibile combinar plure typos de authentication de requesta: $1.",
        "sessionprovider-generic": "sessiones $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sessiones basate sur cookies",
        "log-action-filter-newusers": "Typo de creation de conto:",
        "log-action-filter-patrol": "Typo de patrulia:",
        "log-action-filter-protect": "Typo de protection:",
-       "log-action-filter-rights": "Typo de cambio de derecto",
-       "log-action-filter-suppress": "Typo de suppression",
+       "log-action-filter-rights": "Typo de cambio de derecto:",
+       "log-action-filter-suppress": "Typo de suppression:",
        "log-action-filter-upload": "Typo de incargamento:",
        "log-action-filter-all": "Toto",
        "log-action-filter-block-block": "Blocar",
        "linkaccounts-submit": "Ligar contos",
        "unlinkaccounts": "Disligar contos",
        "unlinkaccounts-success": "Le conto ha essite disligate.",
-       "authenticationdatachange-ignored": "Le cambiamento del datos de authentication non ha succedite. Pote esser que nulle fornitor ha essite configurate?"
+       "authenticationdatachange-ignored": "Le cambiamento del datos de authentication non ha succedite. Pote esser que nulle fornitor ha essite configurate?",
+       "userjsispublic": "Nota ben: Subpaginas JavaScript non debe continer datos confidential perque altere usatores pote vider los.",
+       "usercssispublic": "Nota ben: Subpaginas CSS non debe continer datos confidential perque altere usatores pote vider los."
 }
index 1e7af01..2d8d4ef 100644 (file)
@@ -47,7 +47,8 @@
                        "Arief",
                        "Nemo bis",
                        "Mbrt",
-                       "Beeyan"
+                       "Beeyan",
+                       "Bonaditya"
                ]
        },
        "tog-underline": "Garis bawahi pranala:",
        "yourpasswordagain": "Ulangi kata sandi:",
        "createacct-yourpasswordagain": "Konfirmasi kata sandi",
        "createacct-yourpasswordagain-ph": "Masukkan lagi kata sandi",
-       "remembermypassword": "Ingat kata sandi saya di komputer ini (selama $1 {{PLURAL:$1|hari|hari}})",
        "userlogin-remembermypassword": "Biarkan saya tetap masuk",
        "userlogin-signwithsecure": "Gunakan server aman",
        "cannotloginnow-title": "Tidak dapat masuk log saat ini",
        "passwordreset-emailelement": "Nama pengguna: \n$1\n\nSandi sementara: \n$2",
        "passwordreset-emailsentemail": "Jika alamat surel ini berkaitan dengan akun Anda, maka surel untuk menyetel ulang kata sandi akan dikirim.",
        "passwordreset-emailsentusername": "Jika ada alamat surel yang berkaitan dengan nama pengguna ini, maka surel untuk menyetel ulang kata sandi akan dikirim.",
-       "passwordreset-emailsent-capture": "Surel setel ulang kata sandi telah dikirim, yang ditampilkan di bawah.",
-       "passwordreset-emailerror-capture": "Surel setel ulang kata sandi telah dibuat, yang ditampilkan di bawah, namun pengiriman pada {{GENDER:$2|pengguna}} gagal: $1",
        "passwordreset-emailsent-capture2": "Pemulihan kata sandi {{PLURAL:$1|surel|surel}} telah dikirim. {{PLURAL:$1|Nama pengguna dan kata sandi|Daftar nama pengguna dan kata sandi}} ditampilkan di bawah.",
        "passwordreset-emailerror-capture2": "Pengiriman surel kepada {{GENDER:$2|pengguna}} gagal: $1 {{PLURAL:$3|Nama pengguna dan kata sandi|Daftar nama pengguna dan kata sandi}} ditampilkan di bawah.",
        "passwordreset-nocaller": "Pemanggil harus diberikan",
        "passwordreset-nodata": "Nama pengguna ataupun alamat surel tidak diberikan",
        "changeemail": "Ubah atau hapus alamat surel",
        "changeemail-header": "Lengkapi formulir ini untuk mengubah alamat surel Anda. Jika Anda ingin menghapus seluruh alamat surel yang berkaitan dengan akun Anda, kosongkan alamat surel ketika mengirim formulir.",
-       "changeemail-passwordrequired": "Anda diharuskan memasukkan kata sandi untuk mengonfirmasikan perubahan ini.",
        "changeemail-no-info": "Anda harus masuk log untuk mengakses halaman ini secara langsung.",
        "changeemail-oldemail": "Alamat surel saat ini:",
        "changeemail-newemail": "Alamat surel baru:",
        "minoredit": "Ini adalah suntingan kecil.",
        "watchthis": "Pantau halaman ini",
        "savearticle": "Simpan halaman",
+       "savechanges": "Simpan perubahan",
        "publishpage": "Terbitkan halaman",
        "preview": "Pratayang",
        "showpreview": "Lihat pratayang",
        "undo-nochange": "Suntingan ini nampaknya telah dibatalkan.",
        "undo-summary": "Membalikkan revisi $1 oleh [[Special:Contributions/$2|$2]] ([[User talk:$2|bicara]])",
        "undo-summary-username-hidden": "Batalkan revisi $1 oleh seorang pengguna tersembunyi",
-       "cantcreateaccounttitle": "Akun tak dapat dibuat",
        "cantcreateaccount-text": "Pembuatan akun dari alamat IP ini (<strong>$1</strong>) telah diblokir oleh [[User:$3|$3]].\n\nAlasan yang diberikan oleh $3 adalah ''$2''",
        "cantcreateaccount-range-text": "Pembuatan akun dari alamat IP dalam rentang <strong>$1</strong>, yang mencakup alamat IP anda (<strong>$4</strong>), telah diblokir oleh [[User:$3|$3]].\n\nAlasan yang diberikan oleh  $3  adalah <em>$2</em>",
        "viewpagelogs": "Lihat log halaman ini",
        "mw-widgets-dateinput-placeholder-month": "TTTT-BB",
        "mw-widgets-titleinput-description-new-page": "halaman belum ada",
        "mw-widgets-titleinput-description-redirect": "mengalihkan ke $1",
-       "api-error-blacklisted": "Pilih judul lain yang deskriptif",
        "sessionmanager-tie": "Tidak dapat menggabungkan banyak jenis otentikasi permintaan: $1.",
        "sessionprovider-generic": "sesi $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sesi berdasarkan kuki",
index a9d4378..6184149 100644 (file)
@@ -27,6 +27,7 @@
        "tog-watchdefault": "Agnayon kadagiti panid ken papeles nga inurnosko iti listaan ti bambantayak",
        "tog-watchmoves": "Agnayon kadagiti panid ken papeles nga inyalisko iti listaan ti bambantayak",
        "tog-watchdeletion": "Agnayon kadagiti panid ken papeles nga inikkatko iti listaan ti bambantayak",
+       "tog-watchuploads": "Inayon dagiti baro a papeles iti bambantayak",
        "tog-watchrollback": "Agnayon kadagiti panid nga adda inramidko nga insubli iti bambantayak",
        "tog-minordefault": "Markaan amin dagiti inurnos a kas bassit babaen ti kasisigud",
        "tog-previewontop": "Ipakita ti panagipadas sakbay ti pagurnosan a kahon",
@@ -51,7 +52,7 @@
        "tog-ccmeonemails": "Patulodandak kadagiti kopia ti esurat nga ipatulodko kadagiti sabali nga agar-aramat",
        "tog-diffonly": "Saan nga iparang ti linaon ti panid dita baba dagiti pagiddiatan",
        "tog-showhiddencats": "Ipakita dagiti nailemmeng a kategoria",
-       "tog-norollbackdiff": "Laksiden ti paggiddiatan kalpasan ti panagaramid ti panagisubli",
+       "tog-norollbackdiff": "Saan nga ipakita ti paggiddiatan kalpasan ti panagaramid ti panagisubli",
        "tog-useeditwarning": "Pakaunaannak no pumanawak iti maysa pagurnosan a panid nga addaan iti saan a naidulin a sinuksukatan",
        "tog-prefershttps": "Kankanayon nga agusar ti natalged a koneksion no nakastrek",
        "underline-always": "Kanayon",
        "tagline": "Manipud iti {{SITENAME}}",
        "help": "Tulong",
        "search": "Biruken",
+       "search-ignored-headings": " #<!-- saan a kutkutien daytoy a linia --> <pre>\n# Dagiti paulo a saanto nga ikaskaso babaen ti panagbiruk.\n# Dagiti panagbalbaliw mabalinton intono ti panid nga addaan ti paulo ket maipasurotan.\n# Mabalinmo a piliten ti panangisurot manen iti panid babaen ti awan linaon a panagurnos.\n# Ti eskritu ket kasla dagiti sumaganad:\n#   * Amin manipud ti karater ti \"#\" aginggana ti gibus ti linia ket komentario.\n#   * Amin a saan a blanko a linia ket eksakto a titulo a saan nga maikaskaso, kadakkel ti letra ken amin.\nDagiti reperensia\nDagiti silpo ti ruar\nKitaen pay\n #</pre> <!-- saan a kutkutien daytoy a linia -->",
        "searchbutton": "Biruken",
        "go": "Inkan",
        "searcharticle": "Inkan",
        "yourpasswordagain": "Imakinilya manen ti kontrasenias:",
        "createacct-yourpasswordagain": "Pasingkedan ti kontrasenias",
        "createacct-yourpasswordagain-ph": "Ikabil manen ti kontrasenias",
-       "remembermypassword": "Laglagipem ti iseserrekko iti daytoy a pagbasabasa (para iti kapaut iti $1 {{PLURAL:$1|nga aldaw|nga al-aldaw}})",
        "userlogin-remembermypassword": "Taginayonennak nga iserrek",
        "userlogin-signwithsecure": "Usaren ti natalged a koneksion",
        "cannotloginnow-title": "Saan a mabalin itan iti sumrek",
        "password-change-forbidden": "Saanmo a mabaliwan dagiti kontrasenias iti daytoy a wiki.",
        "externaldberror": "Mabalin nga adda biddut iti pannakapasingked ti database wenno saanka a mapalubosan a mangpabaro ti akinruar a pakabilangam.",
        "login": "Sumrek",
+       "login-security": "Pasingkedan ti identidadmo",
        "nav-login-createaccount": "Sumrek / agpartuat iti pakabilangan",
        "userlogin": "Sumrek / agpartuat iti pakabilangan",
        "userloginnocreate": "Sumrek",
        "userlogin-resetpassword-link": "Nalipatam ti kontraseniasmo?",
        "userlogin-helplink2": "Tulong iti panagserrek",
        "userlogin-loggedin": "Nakastrekkan a kas ni {{GENDER:$1|$1}}.\nUsaren ti porma dita baba tapno sumrek a kas sabali nga agar-aramat.",
+       "userlogin-reauth": "Nasken a sumrekka manen tapno mapasingkedan a sika ni {{GENDER:$1|$1}}.",
        "userlogin-createanother": "Agpartuat iti sabali a pakabilangan",
        "createacct-emailrequired": "Esurat a pagtaengan",
        "createacct-emailoptional": "Esurat a pagtaengan (pagpilian)",
        "createacct-email-ph": "Ikabil ti esurat a pagtaengam",
        "createacct-another-email-ph": "Ikabil ti esurat a pagtaengan",
        "createaccountmail": "Agusar iti pugto a temporario a kontrasenias ken ipatulod iti naisangayan nga esurat a pagtaengan",
+       "createaccountmail-help": "Mabalin a mausar a panagpartuat ti pakabilangan para iti sabali a tao a saan a makaammo iti kontrasenias.",
        "createacct-realname": "Pudno a nagan (pagpilian)",
        "createaccountreason": "Rason:",
        "createacct-reason": "Rason",
        "createacct-reason-ph": "Apay nga agparpartuatka manen iti sabali a pakabilangan",
+       "createacct-reason-help": "Ti mensahe a naipakita iti listaan iti panagpartuat ti pakabilangan",
        "createacct-submit": "Partuatem ti pakabilangam",
        "createacct-another-submit": "Agpartuat iti pakabilangan",
+       "createacct-continue-submit": "Agtuloy iti panagpartuat ti pakabilangan",
+       "createacct-another-continue-submit": "Agtuloy iti panagpartuat ti pakabilangan",
        "createacct-benefit-heading": "Ti {{SITENAME}} ket inar-aramid babaen ti tattao a kasla kenka.",
        "createacct-benefit-body1": "{{PLURAL:$1|nga inurnos|nga inur-urnos}}",
        "createacct-benefit-body2": "{{PLURAL:$1|a panid|a pampanid}}",
        "nocookiesnew": "Napartuaten ti pakabilangan ti agar-aramat, ngem saanka a nakastrek.\nTi {{SITENAME}} ket agus-usar kadagiti galietas tapno maiserrek dagiti agar-aramat.\nNabaldado dagiti galietam.\nPangngaasi a pakabaelam ida, ken sumrekka nga agusar iti baro a naganmo ken kontrasenias.",
        "nocookieslogin": "Ti {{SITENAME}} ket agus-usar kadagiti galietas tapno maiserrek dagiti agar-aramat.\nNabaldado dagiti galietam.\nPangngaasi a pakabaelam ida ken padasem manen ti sumrek.",
        "nocookiesfornew": "Ti pakabilangan ti agar-aramat ket saan a napartuat, saanmi a mapasingkedan ti taudanna.\nSiguraduem a napakabaelan dagita galietam, ikarga manen daytoy a panid ken padasen manen.",
+       "createacct-loginerror": "Balligi ti pannakapartuat ti pakabilangan ngem saanka a mabalin nga automatiko a maiserrek. Pangngaasi a mapan iti [[Special:UserLogin|manual a panagserrek]].",
        "noname": "Saanmo a nainaganan ti umisu a nagan ti agar-aramat.",
        "loginsuccesstitle": "Nakastrek",
        "loginsuccess": "<strong>Nakastrekkan iti {{SITENAME}} a kas ni \"$1\".</strong>",
        "noemail": "Awan ti esurat a pagtaengan a nairehistro para kenni agar-aramat \"$1\".",
        "noemailcreate": "Nasken a mangitedka ti pudno nga esurat a pagtaengan.",
        "passwordsent": "Naipatuloden ti baro a kontrasenias iti esurat a pagtaengan a nairehistro kenni \"$1\".\nPangngaasi a sumrekka manen kalpasan ti pannakaawatmo.",
-       "blocked-mailpassword": "Ti IP a pagtaengam ket naserraan manipud iti panagurnos, ken isu a saan a mapalubosan nga agusar ti annong ti panagipulang ti kontrasenias tapno mapawilan ti panagabuso.",
+       "blocked-mailpassword": "Ti adresmo ti IP ket naserraan manipud iti panagurnos. Tapno mapawilan ti panagabuso, saan a maipalubos ti agusar ti panagipulang ti kontrasenias manipud iti daytoy nga adres ti IP.",
        "eauthentsent": "Naipatuloden ti pammatalged nga esurat iti naikeddeng nga esurat a pagtaengan.\nSakbay a maipatulod ti ania man nga esurat iti pakabilangan, masapul a surotem dagiti maibagbaga iti esurat, tapno mapatalgedan ti pakabilangan ket agpayso a kukuam.",
        "throttled-mailpassword": "Ti panangisaad manen ti kontrasenias ket naipatuloden, iti kaunegan ti napalabas  {{PLURAL:$1|nga oras|a $1 nga or-oras}}.\nTapno maipawilan ti panagabuso, maysa laeng a panangisaad manen ti kontrasenias ti maipatulod iti tunggal {{PLURAL:$1|maysa nga oras|$1 nga or-oras}}.",
        "mailerror": "Biddut iti panangipatulod ti surat: $1",
        "createacct-another-realname-tip": "Saan a nasken ti pudno a nagan.\nNo kayatmo nga ited, mausarto daytoy para iti panangited ti pammadayaw para kadagiti obrada.",
        "pt-login": "Sumrek",
        "pt-login-button": "Sumrek",
+       "pt-login-continue-button": "Agtuloy a sumrek",
        "pt-createaccount": "Agpartuat iti pakabilangan",
        "pt-userlogout": "Rummuar",
        "php-mail-error-unknown": "Di ammo a biddut iti surat ti annong ti PHP().",
        "botpasswords-invalid-name": "Ti naibaga a nagan ti agar-aramat ket saan nga aglaon iti panangisina ti kontrasenias ti bot (\"$1\").",
        "botpasswords-not-exist": "Ti agar-aramat \"$1\" ket awanan iti kontrasenias ti bot nga agnagan iti \"$2\".",
        "resetpass_forbidden": "Saan a masukatan dagiti kontrasenias",
+       "resetpass_forbidden-reason": "Saan a mabaliwan ti kontrasenias: $1",
        "resetpass-no-info": "Masapul a nakastrekka tapno dagus a makapanka iti daytoy a panid.",
        "resetpass-submit-loggedin": "Sukatan ti kontrasenias",
        "resetpass-submit-cancel": "Ukasen",
        "passwordreset-emailelement": "Nagan ti agar-aramat: \n$1\n\nTemporario a kontrasenias: \n$2",
        "passwordreset-emailsentemail": "No daytoy nga adres ti esurat ket mainaig iti pakabilangam, maipatulodto ti maysa nga esurat iti panangisaad manen ti kontrasenias.",
        "passwordreset-emailsentusername": "No adda adres ti esurat a mainaig iti daytoy a nagan ti agar-aramat, addanto maipatulod nga esurat iti panangisaad manen ti kontrasenia.",
-       "passwordreset-emailsent-capture": "Ti maysa nga esurat ti panangisaad manen ti kontrasenias ket naipatuloden, a naipakita dita baba.",
-       "passwordreset-emailerror-capture": "Naaramid ti maysa nga esurat a panangisaad manen ti kontrasenias, a napaikita dita baba, ngem ti panangitulod kenni {{GENDER:$2|agar-aramat}} ket napaay: $1",
+       "passwordreset-emailsent-capture2": "Naipatulodan {{PLURAL:$1|ti esurat|dagiti esurat}} ti panangisaad manen ti kontrasenias. Ti {{PLURAL:$1|nagan ti agar-aramat ken kontrasenias|listaan dagiti nagan ti agar-aramat ken dagiti kontrasenias}} ket naipakita dita baba.",
+       "passwordreset-emailerror-capture2": "Napaay ti panangitulod ti usurat iti {{GENDER:$2|agar-aramat}}: $1 Ti {{PLURAL:$3|nagan ti agar-aramat ken kontrasenias|listaan dagiti agar-aramat ken dagiti kontrasenias}} ket naipakita dita baba.",
+       "passwordreset-nocaller": "Nasken a maited ti maysa nga agtawtawag",
+       "passwordreset-nosuchcaller": "Awan ti agtawtawag: $1",
+       "passwordreset-ignored": "Saan a natengngel ti panangisaad manen ti kontrasenias. Mabalin a saan a nakompigura ti mangited?",
+       "passwordreset-invalideamil": "Imbalido nga adres ti esurat",
+       "passwordreset-nodata": "Saan a naited ti nagan ti agar-aramat wenno maysa nga adres ti esurat",
        "changeemail": "Sukatan wenno ikkaten ti adres ti esurat",
        "changeemail-header": "Kompletuen daytoy a porma tapno masukatan ti adres ti esuratmo. No kayatmo a maikkat ti pannakainaig iti ania man nga adres ti esurat manipud iti pakabilangam, ibati a blanko ti baro nga adres ti esurat no ited ti porma.",
-       "changeemail-passwordrequired": "Masapulmonto nga ikabil ti kontraseniasmo tapno mapasingkedan daytoy a panagbaliw.",
        "changeemail-no-info": "Masapul a nakastrekka tapno dagus a makapan iti ditoy a panid.",
        "changeemail-oldemail": "Agdama nga esurat a pagtaengan:",
        "changeemail-newemail": "Baro nga esurat a pagtaengan:",
        "minoredit": "Daytoy ket bassit a panagurnos",
        "watchthis": "Bantayan daytoy a panid",
        "savearticle": "Idulin ti panid",
+       "savechanges": "Idulin dagiti binaliwan",
+       "publishpage": "Ipablaak ti panid",
+       "publishchanges": "Ipablaak dagiti binaliwan",
        "preview": "Ipadas",
        "showpreview": "Ipakita ti ipadas",
-       "showdiff": "Ipakita dagiti sinukatan",
+       "showdiff": "Ipakita dagiti binaliwan",
        "blankarticle": "<strong>Ballaag:</strong> Ti panid a parpatuatem ket blanko.\nNo pindutem manen ti \"{{int:savearticle}}\", ti panid ket mapartuatto nga awan ti ania man a linaon.",
        "anoneditwarning": "<strong>Ballaag:</strong> Saanka a nakastrek. Ti IP a pagtaengan ket publikonto a makita nga agaramidka iti ania man a panagurnos. No <strong>[$1 sumrekka]</strong> wenno <strong>[$2 agpartuatka iti pakabilangan]</strong>, dagiti inurnosmo ket maitunosto iti naganmo nga agar-aramat, ken dagiti dadduma pay a pagimbagan.",
        "anonpreviewwarning": "<em>Saanka a nakastrek. Ti panagidulin ket agirehistro ti IP a pagtaengam kadagitoy a pakasaritaan ti panagurnos iti daytoy a panid.</em>",
        "userpage-userdoesnotexist": "Ti pakabilangan ti agar-aramat ni \"$1\" ket saan a nakarehistro. \nPangngaasi a kitaem no kayatmo ti agpartuat/agurnos iti daytoy a panid.",
        "userpage-userdoesnotexist-view": "Ti pakabilangan ti agar-aramat ni \"$1\" ket saan a nakarehistro.",
        "blocked-notice-logextract": "Agdama a naserraan daytoy nga agar-aramat.\nTi naudi a listaan ti pannakaserra ket naited dita baba para iti reperensia:",
-       "clearyourcache": "<strong>Nota:</strong> Kalpasan ti panangidulin, koma ket masapul nga ipalabas ti cahe ti pagbasabasam tapno makita dagiti sinukatam.\n* <strong>Firefox / Safari:</strong>  Tenglen ti <em>Shift</em> bayat a pinduten ti <em>Reload</em>, wenno talmegan ti <em>Ctrl-F5</em> wenno <em>Ctrl-R</em> (<em>⌘-R</em> iti Mac)\n* <strong>Google Chrome:</strong> Talmegan ti <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> iti Mac)\n* <strong>Internet Explorer:</strong> Tenglen ti <em>Ctrl</em> bayat a pinduten ti <em>Refresh</em>, wenno talmegan ti <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Dalusan ti cache idiay <em>Tools → Preferences</em>",
+       "clearyourcache": "<strong>Nota:</strong> Kalpasan ti panangidulin, koma ket masapul nga ipalabas ti cahe ti pagbasabasam tapno makita dagiti sinukatam.\n* <strong>Firefox / Safari:</strong>  Tenglen ti <em>Shift</em> bayat a pinduten ti <em>Reload</em>, wenno talmegan ti <em>Ctrl-F5</em> wenno <em>Ctrl-R</em> (<em>⌘-R</em> iti Mac)\n* <strong>Google Chrome:</strong> Talmegan ti <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> iti Mac)\n* <strong>Internet Explorer:</strong> Tenglen ti <em>Ctrl</em> bayat a pinduten ti <em>Refresh</em>, wenno talmegan ti <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Mapan iti <em>Menu → Settings</em> (<em>Opera → Preferences</em> iti Mac) ken kalpasanna iti <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
        "usercssyoucanpreview": "<strong>Paammo:</strong>  Usaren ti buton ti \"{{int:showpreview}}\" tapno masubokan ti baro a CSS sakbay nga agidulin.",
        "userjsyoucanpreview": "<strong>Pammo:</strong> Usaren ti buton ti \"{{int:showpreview}}\" tapno masubokan ti baro a JavaScript sakbay nga agidulin.",
        "usercsspreview": "<strong>Laglagipem nga ipadpadasmo laeng ti bukodmo a CSS ti agar-aramat.\nSaan pay a naidulin!</strong>",
        "content-model-css": "CSS",
        "content-json-empty-object": "Awan linaon a banag",
        "content-json-empty-array": "Awan linaon a rimpuok",
+       "deprecated-self-close-category": "Pampanid nga agus-usar kadagiti imbalido a bukod nga agrikrikep nga etiketa ti HTML",
+       "deprecated-self-close-category-desc": "Ti panid ket aglaon kadagiti imbalido a bukod nga agrikrikep nga etiketa ti HTML, a kas ti <code>&lt;b/></code> wenno <code>&lt;span/></code>.  Agbaliwton ti panagkukua dagitoy tapno maitunos iti espesipikasion ti HTML5, isu a nasukatanen ti usarda iti wikitext.",
        "duplicate-args-warning": "<strong>Ballaag:</strong> Tawtawagan ti [[:$1]] ti [[:$2]] iti ad-adu ngem maysa a pateg para iti parametro \"$3\". Mausarto laeng ti naudi a naited a pateg.",
        "duplicate-args-category": "Pampanid nga agus-usar kadagiti duplikado nga argumento kadagiti panagtawag ti plantilia",
        "duplicate-args-category-desc": "Ti panid ket aglaon kadagiti panagtawag ti plantilia nga agus-usar kadagiti duplikado dagiti argumento, a kas ti <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> wenno <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Ti inurnos ket kasla naibabawin.",
        "undo-summary": "Ibabawi ti $1 a binaliwan babaen ni [[Special:Contributions/$2|$2]] ([[User talk:$2|tungtungan]])",
        "undo-summary-username-hidden": "Isubli ti $1 a binaliwan babaen ti nailemmeng nga agar-aramat",
-       "cantcreateaccounttitle": "Saan a makapartuat iti pakabilangan",
        "cantcreateaccount-text": "Ti panagpartuat iti pakabilangan manipud ti daytoy nga IP a pagtaengan (<strong>$1</strong>) ket sinerraan babaen ni [[User:$3|$3]].\n\nTi inted a rason babaen ni $3 ket <em>$2</em>",
        "cantcreateaccount-range-text": "Ti panagpartuat iti pakabilangan manipud kadagiti pagtaengan ti IP iti sakop ti <strong>$1</strong>, a mairaman ti IP a pagtaengam (<strong>$4</strong>), ket sinerraan babaen ni [[User:$3|$3]].\n\nTi inted a rason babaen ni $3 ket <em>$2</em>",
        "viewpagelogs": "Kitaen dagiti listaan para iti daytoy a panid",
        "right-override-export-depth": "Agipan kadagiti panid a mairaman dagiti naisilpo a panid agingana iti kauneg ti 5",
        "right-sendemail": "Agipatulod iti esurat kadagiti sabali nga agar-aramat",
        "right-passwordreset": "Agkita kadagiti esurat ti panangisaad manen ti kontrasenias",
-       "right-managechangetags": "Agpartuat ken agikkat kadagiti [[Special:Tags|etiketa]] manipud iti database",
+       "right-managechangetags": "Agpartuat ken (de)aktibuen [[Special:Tags|etiketa]]",
        "right-applychangetags": "Ipakat dagiti [[Special:Tags|etiketa]] a mairaman dagiti nabaliwan",
        "right-changetags": "Agnayon ken agikkat kadagiti arbitario nga [[Special:Tags|etiketa]] kadagiti agmaymaysa a rebision ken dagiti naikabkabil iti listaan",
+       "right-deletechangetags": "Ikkaten dagiti [[Special:Tags|etiketa]] manipud iti database",
        "grant-generic": "Raay ti karbengan ti \"$1\"",
        "grant-group-page-interaction": "Makitignay kadagiti panid",
        "grant-group-file-interaction": "Makitignay iti midia",
        "grant-group-high-volume": "Agaramid iti adu iti tomo nga aktibidad",
        "grant-group-customization": "Kustomisasion ken dagiti kakaykayatan",
        "grant-group-administration": "Agaramid kadagiti administratibo nga aksion",
+       "grant-group-private-information": "Serrekan ti pribado a datos a maipanggep kenka",
        "grant-group-other": "Nadumaduma nga aktibidad",
        "grant-blockusers": "Serraan ken ikkaten ti serra dagiti agar-aramat",
        "grant-createaccount": "Agpartuat kadagiti pakabilangan",
        "grant-highvolume": "Adu a tomo a panagurnos",
        "grant-oversight": "Ilemmeng dagiti agar-aramat ken lappedan dagiti rebision",
        "grant-patrol": "Patruliaan dagiti panagbaliw kadagiti panid",
+       "grant-privateinfo": "Serrekan ti pribado a pakaammo",
        "grant-protect": "Salakniban ken ikkaten ti salaknib dagiti panid",
        "grant-rollback": "Isubli dagiti panagbaliw kadagiti panid",
        "grant-sendemail": "Agipatulod iti esurat kadagiti sabali nga agar-aramat",
        "rightslogtext": "Daytoy ket listaan dagiti sinukatan a karbengan ti agar-aramat.",
        "action-read": "agbasa iti datoy a panid",
        "action-edit": "agurnos iti daytoy a panid",
-       "action-createpage": "agpartuat kadagiti panid",
-       "action-createtalk": "agpartuat kadagiti pagtungtungan a panid",
+       "action-createpage": "agpartuat iti daytoy a panid",
+       "action-createtalk": "partuaten daytoy a pagtungtungan a panid",
        "action-createaccount": "agpartuat iti pakabilangan daytoy nga agar-aramat",
        "action-autocreateaccount": "automatiko a partuaten daytoy nga akinruar a pakabilangan ti agar-aramat",
        "action-history": "agkita iti pakasaritaan iti daytoy a panid",
        "action-viewmyprivateinfo": "agkita iti bukodmo a pribado a pakaammo",
        "action-editmyprivateinfo": "agurnos iti bukodmo a pribado a pakaammo",
        "action-editcontentmodel": "urnosen ti modelo ti linaon iti panid",
-       "action-managechangetags": "agpartuat ken agikkat kadagiti etiketa manipud iti database",
+       "action-managechangetags": "agpartuat ken (de)aktibuen kadagiti etiketa",
        "action-applychangetags": "ipakat dagiti etiketa a mairaman dagiti nabaliwan",
        "action-changetags": "agnayon ken agikkat kadagiti arbitario nga etiketa kadagiti agmaymaysa a rebision ken dagiti naikabkabil iti listaan",
+       "action-deletechangetags": "ikkaten dagiti etiketa manipud iti database",
+       "action-purge": "purgaen daytoy a panid",
        "nchanges": "$1 {{PLURAL:$1|sinukatan|dagiti sinukatan}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|manipud iti naudi a panagsarungkar}}",
        "enhancedrc-history": "pakasaritaan",
        "recentchangeslinked-page": "Nagan ti panid:",
        "recentchangeslinked-to": "Ipakita dagiti sinukatan kadagiti panid nga imbes a naisilpo iti naited a panid",
        "recentchanges-page-added-to-category": "nainayon ti [[:$1]] iti kategoria",
-       "recentchanges-page-added-to-category-bundled": "nainayon ti [[:$1]] ken [[Special:WhatLinksHere/$1|{{PLURAL:$2|maysa a panid|$2 a pampanid}}]] iti kategoria",
+       "recentchanges-page-added-to-category-bundled": "Nainayon ti [[:$1]] iti kategoria ken [[Special:WhatLinksHere/$1|daytoy a panid ket nairaman iti kaunegan dagiti sabali a panid]]",
        "recentchanges-page-removed-from-category": "naikkat ti [[:$1]] manipud iti kategoria",
-       "recentchanges-page-removed-from-category-bundled": "Naikkat ti [[:$1]] ken {{PLURAL:$2|maysa a panid|$2 a pampanid}} manipud iti kategoria",
+       "recentchanges-page-removed-from-category-bundled": "Naikkat ti [[:$1]] manipud iti kategoria ken, [[Special:WhatLinksHere/$1|daytoy a panid ket nairaman iti kaunegan dagiti sabali a panid]]",
        "autochange-username": "Automatiko a panagbaliw iti MediaWiki",
        "upload": "Agikarga iti papeles",
        "uploadbtn": "Agikarga iti papeles",
        "uploaded-setting-event-handler-svg": "Naserraan ti panangisaad ti kadagiti gupit ti panagtengngel ti pasamak, nakabiruk iti <code>&lt;$1 $2=\"$3\"&gt;</code> iti naikarga a papeles ti SVG.",
        "uploaded-setting-href-svg": "Ti panagusar ti etiketa ti \"set\" tapno mainayon ti gupit ti \"href\" iti elemento ti nagannak ket naserraan.",
        "uploaded-wrong-setting-svg": "Ti panagusar ti etiketa ti \"set\" tapno mainayon ti puntaan nga remote/data/script iti ania man a gupit ket naserraan. Nabirukan ti <code>&lt;set to=\"$1\"&gt;</code> iti naikarga a papeles ti SVG.",
+       "uploaded-setting-handler-svg": "Naserraan ti SVG a nangisaad ti gupit ti \"handler\" nga addaan iti remote/data/script. Nabirukan ti <code>$1=\"$2\"</code> iti naikarga a papeles ti SVG.",
+       "uploaded-remote-url-svg": "Naserraan ti SVG a nangisaad ti gupit iti ania man nga estilo nga addaan iti remote nga URL. Nabirukan ti <code>$1=\"$2\"</code> iti naikarga a papeles ti SVG.",
        "uploaded-image-filter-svg": "Nakabiruk ti sagat ti ladawan nga addaan iti URL: <code>&lt;$1 $2=\"$3\"&gt;</code> iti naikarga a papeles ti SVG.",
        "uploadscriptednamespace": "Daytoy a papeles ti SVG ket aglaon iti maysa a saan a mabalin a nagan ti espasio ti \"$1\".",
        "uploadinvalidxml": "Ti XML iti naikarga a papeles ket saan a maiwaswas.",
        "upload-options": "Dagiti pagpilian ti panagikarga",
        "watchthisupload": "Bantayan daytoy a papeles",
        "filewasdeleted": "Ti papeles iti daytoy a nagan ket dati a naikarga ken kanungpalan a naikkat.\nNasken a kitaem ti $1 sakbay nga agtuloy a mangikarga manen.",
+       "filename-thumb-name": "Daytoy ket kasla titulo ti bassit a ladawan. Pangngaasi a saan nga agikarga kadagiti bassit a ladawan iti agpada a wiki. Wenno saan, pangngaasi a simpaen ti nagan ti papeles tapno makaibuksilan, ken awan ti pasakbay ti bassit a ladawan.",
        "filename-bad-prefix": "Ti nagan ti papeles nga ikarkargam ket mangrugi iti <strong>\"$1\"</strong>,  ken saan a deskriptibo a nagan a kadawyan nga automatiko nga ited babaen dagiti digital a kamera.\nPangngaasi nga agpili ti nasaysayaat a deskriptibo a nagan ti papelesmo.",
        "upload-proto-error": "Saan a husto a protokol",
        "upload-proto-error-text": "Ti adayo a panagikarga ket makasapul kadagiti URL a mangrugi iti <code>http://</code> wenno <code>ftp://</code>.",
        "upload-too-many-redirects": "Ti URL ket naglaon kadagiti adu unay a baw-ing",
        "upload-http-error": "Adda napasamak a biddut ti HTTP: $1",
        "upload-copy-upload-invalid-domain": "Dagiti kopia a panagikarga ket saan a magun-od manipud iti daytoy a dominio.",
+       "upload-foreign-cant-upload": "Daytoy a wiki ket saan a nakompigura a mangikarga kadagiti papeles iti kiniddaw a ganganaet a repositorio ti papeles.",
+       "upload-foreign-cant-load-config": "Napaay a nangikarga ti kompigurasion para kadagiti panangikarga ti papeles iti ganganaet a repositorio ti papeles.",
+       "upload-dialog-disabled": "Nabaldado iti daytoy a wiki dagiti panangikarga ti papeles iti daytoy a dialogo.",
        "upload-dialog-title": "Agikarga iti papeles",
        "upload-dialog-button-cancel": "Ukasen",
        "upload-dialog-button-done": "Nalpasen",
        "upload-dialog-button-upload": "Agikarga",
        "upload-form-label-infoform-title": "Dagiti salaysay",
        "upload-form-label-infoform-name": "Nagan",
+       "upload-form-label-infoform-name-tooltip": "Ti naisangayan a deskriptibo a titulo para iti papeles, nga agserbinto a kas ti nagan ti papeles. Mabalinmo ti agusar ti naranas a pagsasao nga agraman kadagiti baetan. Saan nga iraman ti pagpaatiddog ti papeles.",
        "upload-form-label-infoform-description": "Deskripsion",
+       "upload-form-label-infoform-description-tooltip": "Ipalawag bassit dagiti amin a nalatak a maipanggep iti obra.\nPara iti retrato, ibaga dagiti nangruna a banag a nailadladawan, ti okasion, wenno ti lugar.",
        "upload-form-label-usage-title": "Panagusar",
        "upload-form-label-usage-filename": "Nagan ti papeles",
        "upload-form-label-own-work": "Daytoy ket bukodko nga obra",
        "upload-form-label-infoform-categories": "Katkategoria",
        "upload-form-label-infoform-date": "Petsa",
+       "upload-form-label-own-work-message-generic-local": "Pasingkedak nga ikarkargak daytoy a papeles a sumursurot kadagiti termino ti serbisio ken dagiti annuroten ti lisensia iti {{SITENAME}}.",
+       "upload-form-label-not-own-work-message-generic-local": "No saanka a makaikarga iti daytoy a papeles babaen dagiti annuroten iti {{SITENAME}}, pangngaasi nga irekep daytoy a dialogo ken padasen ti sabali a pamay-an.",
        "upload-form-label-not-own-work-local-generic-local": "Mabalinmo pay a padasen [[Special:Upload|ti kasisigud a pagikargaan a panid]].",
+       "upload-form-label-own-work-message-generic-foreign": "Maawatak nga agikarkargaak iti daytoy a papeles iti pagbibingayan a repositorio. Pasingkedak nga ar-aramidek a sumursurot kadagiti termino ti serbisio ken dagiti annuroten ti lisensia idiay.",
+       "upload-form-label-not-own-work-message-generic-foreign": "No saanka a makaikarga iti daytoy a papeles babaen dagiti annuroten iti pagbibingayan a repositorio, pangngaasi nga irekep daytoy a dialogo ken padasen ti sabali a pamay-an.",
+       "upload-form-label-not-own-work-local-generic-foreign": "Malinmo pay a padasen ti panagusar [[Special:Upload|ti panid a pagikargaan iti {{SITENAME}}]], no daytoy a panid ket mabalin a maikarga idiay babaen dagiti bukodda nga annuroten.",
        "backend-fail-stream": "Saan a maipan ti papeles $1.",
        "backend-fail-backup": "Saan a makaidulin ti kapada ti papeles ti $1.",
        "backend-fail-notexists": "Awan ti papeles ti $1.",
        "uploadstash-badtoken": "Napaay ti panagtungpal dayta nga aramid. Mabalin a ti talekmo nga agurnos ket nagpason. Pangngaasi a padasen manen.",
        "uploadstash-errclear": "Napaay ti panagdalus kadagiti papeles.",
        "uploadstash-refresh": "Pasadiwaen dagiti listaan ti papeles",
+       "uploadstash-thumbnail": "kitaen ti bassit a ladawan",
+       "uploadstash-exception": "Saan a naidulin ti panangikarga iti stash ($1): \"$2\".",
        "invalid-chunk-offset": "Imbalido a pirgis ti timbengan",
        "img-auth-accessdenied": "Nalibak ti iseserrek",
        "img-auth-nopathinfo": "Ti servermo ket saan a naisaad nga agipasa iti daytoy a pakaammo.\nMabalin a naibatay iti CGI ken saan a makasuporta ti img_auth.\nKitaen ti https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization .",
        "listgrouprights-namespaceprotection-namespace": "Nagan ti espasio",
        "listgrouprights-namespaceprotection-restrictedto": "Karbengan wenno karkarbengan a mangpalubos nga agurnos ti agar-aramat",
        "listgrants": "Dagiti sagut",
+       "listgrants-summary": "Ti sumagand ket listaan dagiti sagut nga agraman kadagiti mainaig a panagserrek kadagiti karbengan ti agar-aramat. Dagiti agar-aramat ket mabalinda ti mangipalubos kadagiti aplikasion a mausarda iti bukodda a pakabilangan, ngem addaan iti limitado a pammalubos a naibatay kadagiti sagut nga inted ti agar-aramat iti aplikasion. Nupay kasta ti maysa nga aplikasion nga agtigtignay para iti agar-aramat ket saan a pudno a makausar kadagiti karbengan nga awanan iti agar-aramat.\nMabalin nga adda iti [[{{MediaWiki:Listgrouprights-helppage}}|maipatinayon a pakaammo]] a maipanggep kadagiti indibidual a karbengan.",
        "listgrants-grant": "Sagut",
        "listgrants-rights": "Dagiti karbengan",
        "trackingcategories": "Pagsurotan a katkategoria",
        "trackingcategories-msg": "Pagsurotan a kategoria",
        "trackingcategories-name": "Nagan ti mensahe",
        "trackingcategories-desc": "Kriteria ti panangiraman ti kategoria",
+       "restricted-displaytitle-ignored": "Pampanid nga addaan kadagiti di naikaskaso a titulo ti panangiparang",
+       "restricted-displaytitle-ignored-desc": "Ti panid ket addaan iti maysa a di naikaskaso a <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> gapu ta saan a kapada iti pudno a titulo ti panid.",
        "noindex-category-desc": "Ti panid ket saan naipagsurotan babaen dagiti robot gapu ta addaan iti salamangka a balikas iti <code><nowiki>__NOINDEX__</nowiki></code> ken adda iti nagan ti espasio a maipalubos ti wagayway.",
        "index-category-desc": "Ti panid ket addaan iti <code><nowiki>__INDEX__</nowiki></code> (ken adda iti nagan ti espasio a maipalubos ti wagayway), ken isu a naipagsurotan babaen dagiti robot ngem no iti kadawyan ket saan.",
        "post-expand-template-inclusion-category-desc": "Ti kadakkel ti panid ket dakdakkel ngem <code>$wgMaxArticleSize</code> kalpasan ti panangipadakkel amin dagiti plantilia, isu nga adda met dagiti plantilia a saan a naipadakkel",
        "watchnologin": "Saan a nakastrek",
        "addwatch": "Inayon iti listaan ti bambantayan",
        "addedwatchtext": "Ti \"[[:$1]]\" ken ti tungtunganna a panid ket nainayonen iti [[Special:Watchlist|listaan ti bambantayam]].",
+       "addedwatchtext-talk": "Ti \"[[:$1]]\" ken ti mainaig a panidna ket nainayoen iti [[Special:Watchlist|bambantayam]].",
        "addedwatchtext-short": "Ti panid ti \"$1\" ket nainayonen iti listaan ti bambantayam.",
        "removewatch": "Ikkaten manipud ti listaan ti bambantayan",
        "removedwatchtext": "Ti \"[[:$1]]\" ken ti tungtunganna a panid ket naikkaten manipud iti [[Special:Watchlist|listaan ti bambantayam]].",
+       "removedwatchtext-talk": "Ti \"[[:$1]]\" ken ti mainaig a panidna ket naikkaten manipud iti [[Special:Watchlist|bambantayam]].",
        "removedwatchtext-short": "Ti panid ti \"$1\" ket naikkaten manipud ti listaan ti bambantayam.",
        "watch": "Bantayan",
        "watchthispage": "Bantayan daytoy a panid",
        "rollbacklinkcount": "agisubli ti $1 {{PLURAL:$1|nga inurnos|nga inur-urnos}}",
        "rollbacklinkcount-morethan": "agisubli ti ad-adu ngem $1 {{PLURAL:$1|nga inurnos|nga inur-urnos}}",
        "rollbackfailed": "Napaay ti panangisubli",
+       "rollback-missingparam": "Awan dagiti nasken a parametro iti kiddaw.",
        "cantrollback": "Saan a maisubli ti panagurnos;\nti naudi a nakaaramid ket iti laeng nagsurat iti daytoy a panid.",
        "alreadyrolled": "Saan a maipasubli ti kinaudi a panagurnos iti [[:$1]] babaen ni [[User:$2|$2]] ([[User talk:$2|tungtungan]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nadda sabali a naurnos wenno nagipasubli ti panid.\n\nTi kinaudi a panagurnos ti daytoy a panid ket babaen ni [[User:$3|$3]] ([[User talk:$3|tungtungan]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Ti pakabuklan idi ti panagurnos ket: <em>$1</em>.",
        "revertpage": "Insubli ti panagurnos babaen ni [[Special:Contributions/$2|$2]] ([[User talk:$2|tungtungan]]), naisubli ti kinaudi a rebision babaen ni [[User:$1|$1]]",
        "revertpage-nouser": "Naisubli dagiti inurnos babaen ti nailemmeng nga agar-aramat iti kinaudi a rebision babaen ni {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Naibabawi dagiti panagurnos babaen ni $1;\nnaisubli manen ti naudi a rebision babaen ni $2.",
+       "rollback-success-notify": "Naibabawi dagiti panagurnos babaen ni $1;\nisubli ti naudi a rebision babaen ni $2. [$3 Ipakita dagiti binaliwan]",
        "sessionfailure-title": "Napaay ti sesion",
        "sessionfailure": "Adda parikut ti sesion ti panagserrekmo;\ndaytoy nga aramid ket naibabawi a kas pagpawilan ti panaghijack ti sesion.\nAgsublika iti naggapuam a panid, ikargam manen ti panid ken padasen manen.",
        "changecontentmodel": "Baliwan ti modelo ti linaon ti panid",
        "changecontentmodel-success-text": "Nabaliwanen ti kita ti linaon ti [[:$1]].",
        "changecontentmodel-cannot-convert": "Ti linaon iti [[:$1]] ket saan a mabaliwan iti kita ti $2.",
        "changecontentmodel-nodirectediting": "Ti modelo ti linaon ti $1 ket saan a mangsuporta ti dagus a panagurnos",
+       "changecontentmodel-emptymodels-title": "Awan ti magun-od kadagiti modelo ti linaon",
+       "changecontentmodel-emptymodels-text": "Ti linaon iti [[:$1]] ket saan a mabaliwan iti ania man a kita.",
        "log-name-contentmodel": "Listaan ti panagbaliw ti modelo ti linaon",
        "log-description-contentmodel": "Dagiti pasamak a mainaig kadagiti modelo ti linaon ti panid",
+       "logentry-contentmodel-new": "{{GENDER:$2|Pinartuat}} ni $1 ti panid ti $3 a nagusar ti saan a kasisigud a modelo ti linaon ti \"$5\"",
        "logentry-contentmodel-change": "{{GENDER:$2|Binaliwan}} ni $1 ti modelo ti panid ti $3 manipud ti \"$4\" iti \"$5\"",
        "logentry-contentmodel-change-revertlink": "isubli",
        "logentry-contentmodel-change-revert": "isubli",
        "undeletehistorynoadmin": "Daytoy a panid ket naikkaten.\nTi rason ti panagikkat ket naipakita iti pakabuklan dita baba, ken dagita a salaysay ti agar-aramat a nagurnos iti daytoy a panid sakbay a naikkat.\nTi husto a testo dagitoy a naikat a rebision ket magun-od laeng dagiti administrador.",
        "undelete-revision": "Naikkat ti rebision ti $1 (manipud idi $4, $5) babaen ni $3:",
        "undeleterevision-missing": "Imbalido wenno napukaw a rebision.\nAddaanka ngata ti madi a silpo, wenno ti rebision ket mabalin a naipasubli wenno naikkat manipud ti arkibo.",
+       "undeleterevision-duplicate-revid": "Saan a maisubli {{PLURAL:$1|ti maysa a rebision|dagiti $1 a rebision}}, gapu ta {{PLURAL:$1|ti bukona|dagiti bukodda}} nga <code>rev_id</code> ket naus-usaren.",
        "undelete-nodiff": "Awan ti nasarakan kadagiti dati a rebision.",
        "undeletebtn": "Isubli",
        "undeletelink": "kitaen/isubli",
        "undeletedrevisions": "{{PLURAL:$1|1 a rebision|dagiti $1 a rebision}} ti naisubli",
        "undeletedrevisions-files": "{{PLURAL:$1|1 a rebision|dagiti $1 a rebision}} ken {{PLURAL:$2|1 a papeles|dagiti $2 a papeles}} ti naisubli",
        "undeletedfiles": "{{PLURAL:$1|1 a papeles|dagiti $1 a papeles}} ti naisubli",
-       "cannotundelete": "Napaay ti panagisubli iti panagikkat:\n$1",
+       "cannotundelete": "Napaay ti sangkabassit wenno amin iti panagisubli ti panagikkat:\n$1",
        "undeletedpage": "<strong>Naisublin ti $1</strong>\n\nBinsiren ti [[Special:Log/delete|listaan ti panagikkat]] para iti rehistro dagiti kaudian panagikkat ken naisubsubli.",
        "undelete-header": "Kitaen [[Special:Log/delete|ti listaan ti panagikkat]] kadagiti kaudian a naikkat a panid.",
        "undelete-search-title": "Biruken dagiti naikkat a panid",
        "sp-contributions-newbies-sub": "Para kadagiti kabarbaro a pakabilangan",
        "sp-contributions-newbies-title": "Dagiti kontribusion para kadagiti baro a pakabilangan",
        "sp-contributions-blocklog": "listaan ti serra",
-       "sp-contributions-suppresslog": "pasardengen dagiti kontribusion ti agar-aramat",
-       "sp-contributions-deleted": "dagiti naikkat a kontribusion ti agar-aramat",
+       "sp-contributions-suppresslog": "dagiti napasardeng a kontribusion ti {{GENDER:$1|agar-aramat}}",
+       "sp-contributions-deleted": "dagiti naikkat a kontribusion ti {{GENDER:$1|agar-aramat}}",
        "sp-contributions-uploads": "dagiti naikarga",
        "sp-contributions-logs": "dagiti listaan",
        "sp-contributions-talk": "tungtungan",
        "sp-contributions-username": "IP a pagtaengan wenno nagan ti agar-aramat:",
        "sp-contributions-toponly": "Ipakita laeng dagiti inurnos dagiti kaudian a rebision",
        "sp-contributions-newonly": "Ipakita laeng dagiti inurnos a pannakapartuat ti pampanid",
+       "sp-contributions-hideminor": "Ilemmeng dagiti bassit a panagurnos",
        "sp-contributions-submit": "Biruken",
        "whatlinkshere": "Dagiti nakasilpo ditoy",
        "whatlinkshere-title": "Pampanid a nakasilpo iti \"$1\"",
        "unblock": "Ikkaten ti serra ti agar-aramat",
        "blockip": "Serraan ti {{GENDER:$1|agar-aramat}}",
        "blockip-legend": "Serraan ti agar-aramat",
-       "blockiptext": "Usaren ti porma dita baba tapno maserraan ti panagsurat manipud iti naisangayan nga IP a pagtaengan wenno nagan ti agar-aramat.\nUsaren laeng daytoy tapno pawilan ti bandalismo, ken panagtunos iti [[{{MediaWiki:Policy-url}}|annuroten]].\nIkkan ti naisangayan a rason dita baba (kas pagarigan, dakamaten ti maysa a panid a nabandalismo) .",
+       "blockiptext": "Usaren ti porma dita baba tapno maserraan ti panagserrek ti panagsurat manipud iti naisangayan nga adres ti IP wenno nagan ti agar-aramat.\nDaytoy ket nasken laeng a maaramid tapno mapawilan ti bandalismo, ken segun iti [[{{MediaWiki:Policy-url}}|annuroten]].\nIkabil ti naisangayan a rason dita baba (kas pagarigan, ti panagdakamat kadagiti naisangayan a panid a nabandalismo).\nMabalinmo a serraan dagiti sakup ti adres ti IP babaen ti panagusar ti sintaksis ti [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; ti kadakkelan a maipalubos a sakup ket /$1 para iti IPv4 ken /$2 para iti IPv6.",
        "ipaddressorusername": "IP a pagtaengan wenno nagan ti agar-aramat:",
        "ipbexpiry": "Agpaso:",
        "ipbreason": "Rason:",
        "ipb-unblock": "Lukatan ti serra ti nagan ti agar-aramat wenno IP a pagtaengan",
        "ipb-blocklist": "Kitaen dagiti adda a serra",
        "ipb-blocklist-contribs": "Dagiti kontribusion para kenni {{GENDER:$1|$1}}",
+       "ipb-blocklist-duration-left": "$1 ti nabati",
        "unblockip": "Lukatan ti serra ti agar-aramat",
        "unblockiptext": "Usaren ti porma dita baba tapno maisubli ti panagserrek ti panagsurat ti dati a naserran nga IP a pagtaengan wenno nagan ti agar-aramat.",
        "ipusubmit": "Ikkaten daytoy a serra",
        "lockdbsuccesstext": "Nabalunetan ti database.<br />\nLaglagipem nga [[Special:UnlockDB|ikkaten ti balunetna]] kalpasan a malpaska nga agsimpa.",
        "unlockdbsuccesstext": "Nalukatanen ti database.",
        "lockfilenotwritable": "Ti papeles ti balunet ti database ket saan a masuratan.\nTapno mabalunetan ken malukatan ti database, nasken daytoy a masuratan babaen ti web server.",
+       "databaselocked": "Ti database ket agdaman a nabalutenan.",
        "databasenotlocked": "Saan a nabalunetan ti database.",
        "lockedbyandtime": "(ni {{GENDER:$1|$1}} idi $2, $3)",
        "move-page": "Iyalis ti $1",
        "move-page-legend": "Iyalis ti panid",
-       "movepagetext": "Ti panagusar ti porma dita baba, ket mangnagan manen ti panid, a mangiyalis amin ti pakasaritaanna iti baro a nagan.\nTi daan a titulo ket agbalin a baw-ing a panid iti baro a titulo.\nMapabarom a kas automatiko dagiti baw-ing a nakatudo dita kasisigud a titulo.\nNo agpilika a saanmo a kayat, siguraduem a kitaen ti [[Special:DoubleRedirects|doble]] wenno [[Special:BrokenRedirects|nadadael a baw-ing]].\nRenbbengmo ti mangpatalged nga amin a silpo ket agtultuloy a nakatudo iti nasken a papananda.\n\nLaglagipen a ti panid ket <strong>saan</strong> a maiyalis no addan sigud a panid iti baro a titulo, malaksid no ti kinaudi ket maysa a baw-ing ken awan ti napalabas a pakasaritaan ti panag-urnos. \nKayat a sawen daytoy a mabalinmo a suktan ti nagan ti maysa a panid manipud iti punto ti pannakasukat ti nagan no nagbiddutka, ken saan mo a mabalin a suratan manen ti addaan a panid.\n\n<strong>Ballaag!</strong>\nMabalin a maysa daytoy a nakaro ken saan a bigla a panagbaliw iti maysa a nasikat a panid;\npangngaasim a pasingkedam a maawatam ti ibunga daytoy sakbay nga agtuloyka a mangbaliw.",
-       "movepagetext-noredirectfixer": "Ti panagusar ti kinabuklan dita baba, ket panaganan ti panid, iyalisna amin ti pakasaritaanna iti baro a nagan.\nTi daan a titulo ket agbalin baw-ing a panid idiay baro a titulo.\nPasaruduam a kitaen ti [[Special:DoubleRedirects|doble]] wenno [[Special:BrokenRedirects|nadadael a baw-ing]].\nRebbengem ti mangpatalged nga amin a silpo ket agtultuloy a nakatudo iti nasken a papananda.\n\nLaglagipen a ti panid ket <strong>saan</strong> a maiyalis no addan sigud a panid iti baro a titulo, malaksid no awan linaonna wenno no maysa a baw-ing a panid ken awan ti panagbaliw iti pakasaritaan ti napalabas. \nKayat a sawen daytoy a mabalinmo a suktan ti nagan ti maysa a panid manipud iti punto ti pannakasukat ti nagan no nagbiddutka, ken saanmo a mabalin a suratan manen ti addaan a panid.\n\n<strong>Ballaag!</strong>\nMabalin a maysa daytoy a nakaro ken saan a bigla a panagbaliw iti maysa a nasikat a panid;\npangngaasim ta pasingkedam a maawatam ti ibunga daytoy sakbay nga agtuloyka a mangbaliw.",
+       "movepagetext": "Ti panagusar ti porma dita baba, ket mangnagan manen ti panid, a mangiyalis amin ti pakasaritaanna iti baro a nagan.\nTi daan a titulo ket agbalin a baw-ing a panid iti baro a titulo.\nMapabarom a kas automatiko dagiti baw-ing a nakatudo dita kasisigud a titulo.\nNo agpilika a saanmo a kayat, siguraduem a kitaen ti [[Special:DoubleRedirects|doble]] wenno [[Special:BrokenRedirects|nadadael a baw-ing]].\nRebbengmo ti mangpatalged nga amin a silpo ket agtultuloy a nakatudo iti nasken a papananda.\n\nLaglagipen a ti panid ket <strong>saan</strong> a maiyalis no addaan iti sigud a panid iti baro a titulo, malaksid no ti kinaudi ket maysa a baw-ing ken awan ti napalabas a pakasaritaan ti panagurnos. \nKayat a sawen daytoy a mabalinmo a sukatan ti nagan ti maysa a panid manipud iti punto ti pannakasukat ti nagan no nagbiddutka, ken saanmo a mabalin a suratan manen ti addaan a panid.\n\n<strong>Nota:</strong>\nMabalin a maysa daytoy a nakaro ken saan a bigla a panagbaliw iti maysa a nasikat a panid;\npangngaasim a pasingkedam a maawatam ti ibunga daytoy sakbay nga agtuloyka a mangbaliw.",
+       "movepagetext-noredirectfixer": "Ti panagusar ti kinabuklan dita baba, ket panaganan ti panid, iyalisna amin ti pakasaritaanna iti baro a nagan.\nTi daan a titulo ket agbalin baw-ing a panid idiay baro a titulo.\nSiguraduem a kitaen ti [[Special:DoubleRedirects|doble]] wenno [[Special:BrokenRedirects|nadadael a baw-ing]].\nRebbengem ti mangpatalged nga amin a silpo ket agtultuloy a nakatudo iti nasken a papananda.\n\nLaglagipen a ti panid ket <strong>saan</strong> a maiyalis no addaan iti sigud a panid iti baro a titulo, malaksid no awan linaonna wenno no maysa a baw-ing a panid ken awan ti panagbaliw iti pakasaritaan ti napalabas. \nKayat a sawen daytoy a mabalinmo a sukatan ti nagan ti maysa a panid manipud iti punto ti pannakasukat ti nagan no nagbiddutka, ken saanmo a mabalin a suratan manen ti addaan a panid.\n\n<strong>Nota:</strong>\nMabalin a maysa daytoy a nakaro ken saan a bigla a panagbaliw iti maysa a nasikat a panid;\npangngaasim ta pasingkedam a maawatam ti ibunga daytoy sakbay nga agtuloyka a mangbaliw.",
        "movepagetalktext": "No kur-item daytoy a kahon, automatikonto a maiyalis ti mainaig a tungtungan a panid, malaksid no addanto idiay iti adda linaon a tungtungan a panid.\n\nIti daytoy a kaso, masapul nga iyalis wenno manual nga itiponmo ti panid no kayatmo.",
        "moveuserpage-warning": "<strong>Ballaag:</strong> Mangrugrugika nga agiyalis ti panid ti agar-aramat. Pangngaasi a laglapipen a ti panid ket isu laeng ti maiyalis ken ti agar-aramat ket <em>saanto</em> a managanan.",
        "movecategorypage-warning": "<strong>Ballaag:</strong> Mangiyal-aliskan iti panid ti kategoria. Pangngaasi a laglagipen a ti maiyalisto laeng ket ti panid ken ti aniaman a pampanid iti daan a kategoria ket <em>saanto</em> a maikategoria iti baro.",
        "import-nonewrevisions": "Awan dagiti naala a rebision (mabalin nga adda amin dagitoyen, wenno nalabsan gapu kadagiti biddut).",
        "xml-error-string": "$1 iti linia $2, tukol $3 (byte $4): $5",
        "import-upload": "Ikarga ti datos ti XML",
-       "import-token-mismatch": "Napukaw ti sesion ti datos.\nPangngaasi a padasen manen.",
+       "import-token-mismatch": "Pannakapukaw ti sesion ti datos.\n\nMabalin a nakaruarka. <strong>Pangngaasi a pasingkedan a nakastrekka pay laeng ken padasem manen</strong>.\nNo saan pay a mabalin, padasem ti [[Special:UserLogout|rummuar]] ken sumrek manen, ken kitaen no ti pagpasabasam ket mangipalubos kadagiti galieta manipud iti daytoy a sitio.",
        "import-invalid-interwiki": "Saan a makaala manipud ti nainaganan a wiki.",
        "import-error-edit": "Ti panid ti \"$1\" ket saan idi a naala ngamin ket saanmo a mabalin nga urnosen.",
        "import-error-create": "Ti panid ti \"$1\" ket saan idi a naala ngamin ket saanmo a mabalin a partuaten.",
        "tooltip-ca-nstab-template": "Kitaen ti plantilia",
        "tooltip-ca-nstab-help": "Kitaen ti panid ti tulong",
        "tooltip-ca-nstab-category": "Kitaen ti panid ti kategoria",
-       "tooltip-minoredit": "Markaan daytoy a kas bassit a panag-urnos",
-       "tooltip-save": "Idulin dagiti sinukatam",
-       "tooltip-preview": "Ipadas dagiti sinukatam, pangngaasi nga usarem daytoy sakbay nga idulin ti panid!",
-       "tooltip-diff": "Ipakita no ania dagiti sinukatan nga inaramidmo iti testo",
+       "tooltip-minoredit": "Markaan daytoy a kas bassit a panagurnos",
+       "tooltip-save": "Idulin dagiti binaliwam",
+       "tooltip-publish": "Ipablaak dagiti binaliwam",
+       "tooltip-preview": "Ipadas dagiti binaliwam. Pangngaasi nga usaren daytoy sakbay nga idulin ti panid.",
+       "tooltip-diff": "Ipakita no ania dagiti binaliwan nga inaramidmo iti teksto",
        "tooltip-compareselectedversions": "Kitaen ti naggidiatan dagiti dua a napili a bersion iti daytoy a panid.",
        "tooltip-watch": "Inayon daytoy a panid iti listaan ti bambantayam",
        "tooltip-watchlistedit-normal-submit": "Ikkaten dagiti titulo",
        "svg-long-error": "Saan nga umiso a papeles ti SVG: $1",
        "show-big-image": "Kasisigud a papeles",
        "show-big-image-preview": "Kadakkel daytoy a panagipadas: $1.",
+       "show-big-image-preview-differ": "Kadakkel daytoy a panangipadas ti $3 iti daytoy a papeles ti $2: $1.",
        "show-big-image-other": "Sabali {{PLURAL:$2|a resolusion|kadagiti resolusion}}: $1.",
        "show-big-image-size": "$1 × $2 dagiti piksel",
        "file-info-gif-looped": "nasiluan",
        "newimages-legend": "Sagat",
        "newimages-label": "Nagan ti papeles (wenno pasetna) :",
        "newimages-showbots": "Ipakita dagiti naikarga babaen dagiti bot",
+       "newimages-hidepatrolled": "Ilemmeng dagiti panangikarga a napatruliaan",
        "noimages": "Awan ti makita.",
        "ilsubmit": "Biruken",
        "bydate": "babaen ti petsa",
        "confirm-watch-top": "Inayon daytoy a panid iti listaan ti bambantayam?",
        "confirm-unwatch-button": "Sige",
        "confirm-unwatch-top": "Ikkatem daytoy a panid manipud ti listaan ti bambantayam?",
+       "confirm-rollback-button": "Sige",
+       "confirm-rollback-top": "Isubli dagiti panagurnos iti daytoy a panid?",
        "quotation-marks": "\"$1\"",
        "imgmultipageprev": "← napalabas a panid",
        "imgmultipagenext": "sumaruno a panid →",
        "watchlistedit-raw-done": "Napabaron ti listaan ti bambantayam.",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 a titulo|$1 kadagiti titulo}} ti nainayon:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 a titulo|$1 kadagiti titulo}} ti naikkat:",
-       "watchlistedit-clear-title": "Nadalusanen ti listaan ti bambantayan",
+       "watchlistedit-clear-title": "Dalusan ti listaan ti bambantayan",
        "watchlistedit-clear-legend": "Dalusan ti listaan ti bambantayan",
        "watchlistedit-clear-explain": "Amin dagiti titulo ket maikkatto manipud ti listaan ti bambantayam",
        "watchlistedit-clear-titles": "Dagiti titulo:",
        "timezone-local": "Lokal",
        "duplicate-defaultsort": "<strong>Ballag:</strong> Kasisigud a panagilasin ti \"$2\" ket tuonana ti immuna a kasisigud a panagilasin ti \"$1\".",
        "duplicate-displaytitle": "<strong>Ballaag:</strong> Ti maiparang a titulo ti \"$2\" ket tuonanna ti immmuna a maiparang a titulo ti \"$1\".",
+       "restricted-displaytitle": "<strong>Ballaag:</strong> Di naikaskaso ti titulo ti panagiparang ti \"$1\" gapu ta saan a kapada ti pudno a titulo ti panid.",
        "invalid-indicator-name": "<strong>Biddut:</strong> Ti gupit ti <code>name</code> a panangipakita ti kasasaad ti panid ket nasken nga adda linaon.",
        "version": "Bersion",
        "version-extensions": "Dagiti naisaad a pagpaatiddog",
        "version-libraries-description": "Deskripsion",
        "version-libraries-authors": "Dagiti mannurat",
        "redirect": "Baw-ing babaen ti papeles, agar-aramat, panid, rebision, wenno ID ti listaan",
-       "redirect-summary": "Daytoy nga espesial a panid ket maibaw-ing iti papeles (iti nagan ti papeles), ti panid (iti ID ti rebision wenno ID ti panid), wenno ti panid ti agar-aramat (iti numeriko nga ID ti agar-aramat). Panagusar:\n[[{{#Special:Redirect}}/file/Example.jpg]], \n[[{{#Special:Redirect}}/page/64308]], \n[[{{#Special:Redirect}}/revision/328429]], wenno\n[[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Daytoy nga espesial a panid ket maibaw-ing iti papeles (naited ti nagan ti papeles), ti panid (naited a rebision ti ID wenno ID ti panid), wenno ti panid ti agar-aramat (iti numeriko nga ID ti agar-aramat), wenno ti naikabil iti listaan (naited ti listaan ti ID). Panagusar:\n[[{{#Special:Redirect}}/file/Example.jpg]], \n[[{{#Special:Redirect}}/page/64308]], \n[[{{#Special:Redirect}}/revision/328429]], \n[[{{#Special:Redirect}}/user/101]], wenno\n[[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Inkan",
        "redirect-lookup": "Kitaen:",
        "redirect-value": "Pateg:",
        "tags-delete-not-found": "Awan ti etiketa ti \"$1\".",
        "tags-delete-too-many-uses": "Ti etiketa ti \"$1\" ket naipakat iti ad-adu ngem $2 {{PLURAL:$2|a rebision|kadagiti rebision}}, a ti kaibuksillanna ket saan a mabalin a maikkat.",
        "tags-delete-warnings-after-delete": "Ti etiketa ti \"$1\" ket naikkat, ngem nakita {{PLURAL:$2|ti sumaganad a ballaag|dagiti sumaganad a ballaag}}:",
+       "tags-delete-no-permission": "Awan ti pammalubosmo nga agikkat kadagiti etiketa ti panagbaliw.",
        "tags-activate-title": "Patarayen ti etiketa",
        "tags-activate-question": "Isagsaganamon a patarayen ti etiketa ti \"$1\".",
        "tags-activate-reason": "Rason:",
        "logentry-protect-protect-cascade": "{{GENDER:$2|Sinalakniban}} ni $1 ti $3 $4 [sariap]",
        "logentry-protect-modify": "{{GENDER:$2|Binaliwan}} ni $1 ti agpang ti salaknib para iti $3 $4",
        "logentry-protect-modify-cascade": "{{GENDER:$2|Binaliwan}} ni $1 ti agpang ti salaknib para iti $3 $4 [sariap]",
-       "logentry-rights-rights": "Ni $1 ket {{GENDER:$2|binaliwanna}} ti grupo a pannakaikameng para kenni $3 manipud ti $4 iti $5",
+       "logentry-rights-rights": "Ni $1 ket {{GENDER:$2|binaliwanna}} ti grupo a pannakaikameng para kenni {{GENDER:$6|$3}} manipud iti $4 iti $5",
        "logentry-rights-rights-legacy": "Ni $1 ket {{GENDER:$2|binaliwanna}} ti grupo a pannakaikameng para kenni $3",
        "logentry-rights-autopromote": "Ni $1 ket automatiko idi a {{GENDER:$2|naipangato}} manipud ti $4 iti $5",
        "logentry-upload-upload": "Ni $1 ket {{GENDER:$2|inkargana}} ti $3",
        "searchsuggest-containing": "naglaon ti...",
        "api-error-badaccess-groups": "Saanka mapalubosan nga agikarga kadagiti papeles iti daytoy a wiki.",
        "api-error-badtoken": "Akin-uneg a biddut: Dakes a tandaan.",
+       "api-error-blocked": "Naserraankan manipud iti panagurnos.",
        "api-error-copyuploaddisabled": "Ti panagikarga babaen ti URL ket nabaldado iti daytoy server.",
        "api-error-duplicate": "Adda {{PLURAL:$1|sabali a papeles|dagiti sabali a papeles}} nga addan iti daytoy a sitio nga agraman iti agpada a linaon.",
        "api-error-duplicate-archive": "Adda {{PLURAL:$1|idi sabali a papeles|dagidi sabali a papeles}} nga addaan ditoy a sitio nga agpada ti linaonda, ngem {{PLURAL:$1|daytoy|dagitoy}} ket naikkat.",
        "json-error-unsupported-type": "Naited ti pateg iti kita a saan a maikodigo",
        "headline-anchor-title": "Isilpo iti daytoy a paset",
        "special-characters-group-latin": "Latin",
-       "special-characters-group-latinextended": "Latin napaatiddog",
+       "special-characters-group-latinextended": "Naipaatiddog a Latin",
        "special-characters-group-ipa": "IPA",
        "special-characters-group-symbols": "Dagiti simbolo",
        "special-characters-group-greek": "Griego",
+       "special-characters-group-greekextended": "Naipaatiddog a Griego",
        "special-characters-group-cyrillic": "Siriliko",
        "special-characters-group-arabic": "Arabiko",
-       "special-characters-group-arabicextended": "Arabiko a napaatiddog",
+       "special-characters-group-arabicextended": "Naipaatiddog nga Arabiko",
        "special-characters-group-persian": "Persiano",
        "special-characters-group-hebrew": "Hebreo",
        "special-characters-group-bangla": "Bangla",
        "mw-widgets-dateinput-placeholder-month": "TTTT-BB",
        "mw-widgets-titleinput-description-new-page": "awan pay ti panid",
        "mw-widgets-titleinput-description-redirect": "ibaw-ing iti $1",
-       "api-error-blacklisted": "Pangngaasi nga agpili iti sabali, a mangipalpalawag a titulo.",
        "sessionmanager-tie": "Saan a mabalin nga itipon dagiti nadumaduma kita ti kiddaw ti pammasingked: $1.",
        "sessionprovider-generic": "Dagiti sesion ti $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "dagiti sesion a naibatay iti galieta",
        "sessionprovider-nocookies": "Mabalin a nabaldado dagiti galieta. Siguraduem a pinakabaelam dagiti galieta ken mangrugi manen.",
-       "randomrootpage": "Pugto a ramut a panid"
+       "randomrootpage": "Pugto a ramut a panid",
+       "log-action-filter-block": "Kita ti serra:",
+       "log-action-filter-contentmodel": "Kita ti panagbaliw ti modelo ti linaon:",
+       "log-action-filter-delete": "Kita ti panagikkat:",
+       "log-action-filter-import": "Kita ti import:",
+       "log-action-filter-managetags": "Kita ti aksion ti panagtaripato ti etiketa:",
+       "log-action-filter-move": "Kita ti panagiyalis:",
+       "log-action-filter-newusers": "Kita ti panagpartuat ti pakabilangan:",
+       "log-action-filter-patrol": "Kita ti patrulia:",
+       "log-action-filter-protect": "Kita ti salaknib:",
+       "log-action-filter-rights": "Kita ti panagbaliw ti karbengan:",
+       "log-action-filter-suppress": "Kita ti panagpasardeng:",
+       "log-action-filter-upload": "Kita ti panangikarga:",
+       "log-action-filter-all": "Amin",
+       "log-action-filter-block-block": "Serra",
+       "log-action-filter-block-reblock": "Panagbaliw ti serra",
+       "log-action-filter-block-unblock": "Ikkaten ti serra",
+       "log-action-filter-contentmodel-change": "Panagbaliw ti Contentmodel",
+       "log-action-filter-contentmodel-new": "Panagpartuat ti panid iti saan a pagalagadan a Contentmodel",
+       "log-action-filter-delete-delete": "Panagikkat ti panid",
+       "log-action-filter-delete-restore": "Panangisubli ti panagikkat ti panid",
+       "log-action-filter-delete-event": "Panagikkat ti listaan",
+       "log-action-filter-delete-revision": "Panagikkat ti rebision",
+       "log-action-filter-import-interwiki": "Transwiki nga import",
+       "log-action-filter-import-upload": "Import babaen ti panangikarga ti XML",
+       "log-action-filter-managetags-create": "Panagpartuat ti etiketa",
+       "log-action-filter-managetags-delete": "Panagikkat ti etiketa",
+       "log-action-filter-managetags-activate": "Paaktibuen ti etiketa",
+       "log-action-filter-managetags-deactivate": "Deaktibuen ti etiketa",
+       "log-action-filter-move-move": "Iyalis a saan a mangisurat manen kadagiti baw-ing",
+       "log-action-filter-move-move_redir": "Iyalis a mangisurat manen kadagiti baw-ing",
+       "log-action-filter-newusers-create": "Panagpartuat babaen ti di ammo nga agar-aramat",
+       "log-action-filter-newusers-create2": "Panagpartuat babaen ti nairehistro nga agar-aramat",
+       "log-action-filter-newusers-autocreate": "Automatiko a panagpartuat",
+       "log-action-filter-newusers-byemail": "Panagpartuat nga addaan iti kontrasenias a naipatulod babaen ti esurat",
+       "log-action-filter-patrol-patrol": "Manual a patrulia",
+       "log-action-filter-patrol-autopatrol": "Automatiko a patrulia",
+       "log-action-filter-protect-protect": "Salaknib",
+       "log-action-filter-protect-modify": "Panagbaliw ti salaknib",
+       "log-action-filter-protect-unprotect": "Panagikkat ti salaknib",
+       "log-action-filter-protect-move_prot": "Salaknib ti panagiyalis",
+       "log-action-filter-rights-rights": "Manual a panagbaliw",
+       "log-action-filter-rights-autopromote": "Automatiko a panagbaliw",
+       "log-action-filter-suppress-event": "Panagpasardeng ti listaan",
+       "log-action-filter-suppress-revision": "Panagpasardeng ti rebision",
+       "log-action-filter-suppress-delete": "Panagpasardeng ti panid",
+       "log-action-filter-suppress-block": "Panagpasardeng ti agar-aramat babaen ti serra",
+       "log-action-filter-suppress-reblock": "Panagpasardeng ti agar-aramat babaen ti panagserra manen",
+       "log-action-filter-upload-upload": "Baro a panangikarga",
+       "log-action-filter-upload-overwrite": "Panangikarga manen",
+       "authmanager-authn-not-in-progress": "Saan nga agprogprogreso ti pammasingked wenno napukaw ti datos ti sesion. Pangngaasi a mangrugi manen iti pagrugian.",
+       "authmanager-authn-no-primary": "Dagiti naited a kredensial ket saan a mapasingkedan.",
+       "authmanager-authn-no-local-user": "Dagiti naited a kredensial ket saanda a mainaig iti sino man nga agar-aramat iti daytoy a wiki.",
+       "authmanager-authn-no-local-user-link": "Dagiti naited a kredensial ket husto ngem saanda a mainaig iti sino man nga agar-aramat iti daytoy a wiki. Sumrek iti sabali a waya, wenno agpartuat iti baro a pakabilangan, ken addaankanto iti maysa a pagpilian a mangisipo kadagiti dati a kredensialmo iti dayta a pakabilangan.",
+       "authmanager-authn-autocreate-failed": "Napaay ti automatiko a panagpartuat iti lokal a pakabilangan: $1",
+       "authmanager-change-not-supported": "Dagiti naited a kredensial ket saan a mabaliwan, gapu ta awan ti mangusar kaniada.",
+       "authmanager-create-disabled": "Nabaldado ti panagpartuat ti pakabilangan.",
+       "authmanager-create-from-login": "Tapno mapartuat ti pakabilangam, pangngaasi a punnuen dagiti pagikabilan dita baba.",
+       "authmanager-create-not-in-progress": "Saan nga agprogprogreso ti panagpartuat ti pakabilangan wenno napukaw ti datos ti sesion. Pangngaasi a mangrugi manen iti pagrugian.",
+       "authmanager-create-no-primary": "Dagiti naited a kredensial ket saan a mabalin a mausar para iti panagpartuat ti pakabilangan.",
+       "authmanager-link-no-primary": "Dagiti naited a kredensial ket saan a mabalin a mausar para iti panangisilpo ti pakabilangan.",
+       "authmanager-link-not-in-progress": "Saan nga agprogprogreso ti panangisilpo ti pakabilangan wenno napukaw ti datos ti sesion. Pangngaasi a mangrugi manen iti pagrugian.",
+       "authmanager-authplugin-setpass-failed-title": "Napaay ti panagbaliw ti kontrasenias",
+       "authmanager-authplugin-setpass-failed-message": "Ti plugin ti pammasingked ket di nangipalubos ti panagbaliw ti kontrasenias.",
+       "authmanager-authplugin-create-fail": "Ti plugin ti pammasingked ket di nangipalubos ti panagpartuat ti pakabilangan.",
+       "authmanager-authplugin-setpass-denied": "Ti plugin ti pammasingked ket saan a mangipalubos iti panagbaliw kadagiti kontrasenias.",
+       "authmanager-authplugin-setpass-bad-domain": "Imbalido a dominio.",
+       "authmanager-autocreate-noperm": "Saan a maipalubos ti automatiko a panagpartuat ti pakabilangan.",
+       "authmanager-autocreate-exception": "Temporario a nabaldado ti automatiko a panagpartuat iti pakabilangan gapu kadagiti dati a biddut.",
+       "authmanager-userdoesnotexist": "Ti pakabilangan ti agar-aramat ni \"$1\" ket saan a nakarehistro.",
+       "authmanager-userlogin-remembermypassword-help": "No ti kontrasenias ket nasken koma a malagip para iti napapaut ngem ti kaatiddog ti sesion.",
+       "authmanager-username-help": "Nagan ti agar-aramat para iti pammasingked.",
+       "authmanager-password-help": "Kontrasenias para iti pammasingked.",
+       "authmanager-domain-help": "Dominio para iti akinruar a pammasingked.",
+       "authmanager-retype-help": "Kontrasenias manen tapno mapasingkedan.",
+       "authmanager-email-label": "Esurat",
+       "authmanager-email-help": "Adres ti esurat",
+       "authmanager-realname-label": "Pudno a nagan",
+       "authmanager-realname-help": "Pudno a nagan ti agar-aramat",
+       "authmanager-provider-password": "Naibatay iti kontrasenias a pammasingked",
+       "authmanager-provider-password-domain": "Naibatay iti kontrasenias ken dominio a pammasingked",
+       "authmanager-provider-temporarypassword": "Temporario a kontrasenias",
+       "authprovider-confirmlink-message": "Naibatay kadagiti kinaudi a panagpadasmo a panagserrek, dagiti sumaganad a pakabilangan ket mabalin a maisilpo iti pakabilangam iti wiki. Ti panangisilpo kaniada ket mangipalubos iti panagserrek babaen kadagita a pakabilangan. Pangngaasi nga agpili no ania kadagita ti nasken a maisilpo.",
+       "authprovider-confirmlink-request-label": "Dagiti pakabilangan a nasken koma a naisilpo",
+       "authprovider-confirmlink-success-line": "$1: Balligi a naisilpo.",
+       "authprovider-confirmlink-failed": "Saan a napno a nagballigi ti panangisilpo ti pakabilangan: $1",
+       "authprovider-confirmlink-ok-help": "Agtuloy kalpasan ti panangipakita kadagiti mensahe ti pannakapaay ti panangisilpo.",
+       "authprovider-resetpass-skip-label": "Libtawan",
+       "authprovider-resetpass-skip-help": "Libtawan ti panangisaad manen ti kontrasenias.",
+       "authform-nosession-login": "Balligi ti pammasingked, ngem ti pagbasabasam ket saanna a \"malagip\" a nakastrek.\n\n$1",
+       "authform-nosession-signup": "Napartuat ti pakabilangan, ngem ti pagbasabasam ket saanna a \"malagip\" a nakastrek.\n\n$1",
+       "authform-newtoken": "Napukaw a tandaan. $1",
+       "authform-notoken": "Napukaw a tandaan",
+       "authform-wrongtoken": "Kamali a tandaan",
+       "specialpage-securitylevel-not-allowed-title": "Saan a maipalubos",
+       "specialpage-securitylevel-not-allowed": "Pasensia, saanka a mapalubosan nga agusar iti daytoy a panid gapu ta saan a mapasingkedan ti identidadmo.",
+       "authpage-cannot-login": "Saan a mabalin ti mangrugi a sumrek.",
+       "authpage-cannot-login-continue": "Saan a mabalin ti agtuloy a sumrek. Mabalin a nagsardeng ti sesionmo.",
+       "authpage-cannot-create": "Saan a mabalin ti mangrugi nga agpartuat iti pakabilangan.",
+       "authpage-cannot-create-continue": "Saan a mabalin ti agtuloy iti panagpartuat iti pakabilangan. Mabalin a nagsardeng ti sesionmo.",
+       "authpage-cannot-link": "Saan a mabalin ti mangrugi iti panangisilpo ti pakabilangan.",
+       "authpage-cannot-link-continue": "Saan a mabalin ti agtuloy iti panangisilpo ti pakabilangan. Mabalin a nagsardeng ti sesionmo.",
+       "cannotauth-not-allowed-title": "Nalibak ti pammalubos",
+       "cannotauth-not-allowed": "Saanka a mapalubosan nga agusar iti daytoy a panid",
+       "changecredentials": "Baliwan dagiti kredensial",
+       "changecredentials-submit": "Baliwan dagiti kredensial",
+       "changecredentials-invalidsubpage": "Ti $1 ket saan a husto a kita ti kredensial.",
+       "changecredentials-success": "Nabaliwanen dagiti kredensialmo.",
+       "removecredentials": "Ikkaten dagiti kredensial",
+       "removecredentials-submit": "Ikkaten dagiti kredensial",
+       "removecredentials-invalidsubpage": "Ti $1 ket saan a husto a kita ti kredensial.",
+       "removecredentials-success": "Naiyalisen dagiti kredesialmo.",
+       "credentialsform-provider": "Kita dagiti kredensial:",
+       "credentialsform-account": "Nagan ti pakabilangan:",
+       "cannotlink-no-provider-title": "Awan dagiti mabalin a maisilpo a pakabilangan",
+       "cannotlink-no-provider": "Awan dagiti mabalin a maisilpo a pakabilangan.",
+       "linkaccounts": "Isilpo dagiti pakabilangan",
+       "linkaccounts-success-text": "Naisilpon ti pakabilangan.",
+       "linkaccounts-submit": "Isilpo dagiti pakabilangan",
+       "unlinkaccounts": "Ikkaten ti silpo dagiti pakabilangan",
+       "unlinkaccounts-success": "Ti pakabilangan ket naikkat iti pannakaisilpo.",
+       "authenticationdatachange-ignored": "Saan a natengngel ti panagbaliw ti datos ti pammasingked. Mabalin nga awan ti nakompigura a mangited?"
 }
index d781023..41aa59c 100644 (file)
@@ -12,7 +12,8 @@
                        "Wyvernoid",
                        "לערי ריינהארט",
                        "아라",
-                       "Macofe"
+                       "Macofe",
+                       "Robin van der Vliet"
                ]
        },
        "tog-underline": "Sub-strekizez ligili:",
        "yourname": "Vua uzantonomo:",
        "yourpassword": "Pasovorto:",
        "yourpasswordagain": "Riskribez la pasovorto:",
-       "remembermypassword": "Memorez mea pasovorto en ca komputoro (maximo: $1 {{PLURAL:$1|dio|dii}})",
        "yourdomainname": "Vua domano:",
        "login": "Enirar",
        "nav-login-createaccount": "Enirar",
        "minoredit": "Ico esas mikra redaktajo",
        "watchthis": "Surveyar ica pagino",
        "savearticle": "Registragar pagino",
+       "publishpage": "Publikigar pagino",
+       "publishchanges": "Publikigar chanji",
        "preview": "Previdar",
        "showpreview": "Previdar",
        "showdiff": "Montrez chanji",
index 7a8e57a..fd1d4c4 100644 (file)
@@ -42,6 +42,7 @@
        "tog-watchdefault": "Bæta síðum og skrám sem ég breyti á vaktlistann minn",
        "tog-watchmoves": "Bæta á vaktlistann minn síðum og skrám sem ég færi",
        "tog-watchdeletion": "Bæta síðum og skrám sem ég eyði á vaktlistann minn",
+       "tog-watchuploads": "Bæta nýjum skrám sem ég hleð inn við á vaktlistann minn",
        "tog-watchrollback": "Bæta síðum þar sem ég hef tekið aftur breytingu á vaktlistann minn",
        "tog-minordefault": "Merkja sjálfgefið allar breytingar sem minniháttar",
        "tog-previewontop": "Sýna forskoðun á undan breytingareitnum",
        "yourpasswordagain": "Endurrita lykilorð:",
        "createacct-yourpasswordagain": "Staðfestu lykilorðið",
        "createacct-yourpasswordagain-ph": "Sláðu inn lykilorðið aftur",
-       "remembermypassword": "Muna innskráninguna mína í þessum vafra (í allt að $1 {{PLURAL:$1|dag|daga}})",
        "userlogin-remembermypassword": "Muna innskráningu mína",
        "userlogin-signwithsecure": "Nota örugga tengingu",
        "cannotloginnow-title": "Get ekki skráð inn núna",
        "passwordreset-emailelement": "Notandanafn: \n$1\n\nBráðabirgðalykilorð: \n$2",
        "passwordreset-emailsentemail": "Ef þetta netfang er skráð fyrir aðganginum þínum þá hefur töluvpóstur verið sendur til að endursetja lykilorðið.",
        "passwordreset-emailsentusername": "Ef eitthvað netfang er skráð fyrir aðganginum þínum, þá mun verða sendur töluvpóstur til að endursetja lykilorðið.",
-       "passwordreset-emailsent-capture": "Tölvupóstur til að endursetja lykilorðið hefur verið sendur í tölvupósti, sem er sýndur hér fyrir neðan.",
-       "passwordreset-emailerror-capture": "Tölvupóstur til að endursetja lykilorðið var búinn til, sem er sýndur hér fyrir neðan, en ekki tókst að senda hana til {{GENDER:$2|notandans}}: $1",
        "changeemail": "Breyta eða fjarlægja netfang",
        "changeemail-header": "Fylltu út þetta eyðublað til að breyta netfanginu þínu. Ef þú vilt fjarlægja tengingu allra netfanga frá aðganginum þínum skildu þá netfangs reitinn eftir tóman.",
-       "changeemail-passwordrequired": "Þú verður að setja inn lykilorðið þitt til að staðfesta þessa breytingu.",
        "changeemail-no-info": "Þú verður að vera skráð(ur) inn til að hafa aðgang að þessari síðu.",
        "changeemail-oldemail": "Núverandi netfang:",
        "changeemail-newemail": "Nýtt netfang:",
        "minoredit": "Þetta er minniháttar breyting",
        "watchthis": "Vakta þessa síðu",
        "savearticle": "Vista síðu",
+       "savechanges": "Vista breytingar",
        "publishpage": "Gefa út síðu",
+       "publishchanges": "Gefa út breytingar",
        "preview": "Forskoða",
        "showpreview": "Forskoða",
        "showdiff": "Sýna breytingar",
+       "blankarticle": "<strong>Viðvörun:</strong> Síðan sem þú ert að búa til er tóm.\nEf þú smellir aftur á \"{{int:savearticle}}\", verður síðan búin til án innihalds.",
        "anoneditwarning": "<strong>Viðvörun:</strong> Þú ert ekki innskráð(ur). Vistfang þitt verður sýnt opinberlega ef þú gerir einhverjar breytingar. Ef þú <strong>[$1 skráir þig inn]</strong> eða <strong>[$2 stofnar aðgang]</strong> munu breytingarnar þínar vera tengdar við notandanafn þitt, ásamt öðrum kostum.",
        "anonpreviewwarning": "Þú ert ekki innskráð(ur). Vistfang þitt skráist í breytingaskrá síðunnar.",
        "missingsummary": "'''Áminning:''' Þú hefur ekki skrifað breytingarágrip.\nEf þú smellir á Vista aftur, verður breyting þín vistuð án þess.",
        "permissionserrorstext-withaction": "Þú hefur ekki réttindi til að $2, af eftirfarandi {{PLURAL:$1|ástæðu|ástæðum}}:",
        "recreate-moveddeleted-warn": "'''Viðvörun: Þú ert að endurskapa síðu sem áður hefur verið eytt.'''\n\nAthuga skal hvort viðeigandi sé að gera þessa síðu.\nEyðingarskrá og flutningaskrá fyrir þessa síðu eru útvegaðar hér til þæginda:",
        "moveddeleted-notice": "Þessari síðu hefur verið eytt.\nEyðingaskrá og flutningaskrá síðunnar eru gefnar fyrir neðan til tilvísunar.",
+       "moveddeleted-notice-recent": "Því miður var þessari síðu eytt nýlega (innan síðustu 24 tímana).\nEyðingar og færsluskráin fyrir síðuna eru gefnar hér fyrir neðan til glöggvunar.",
        "log-fulllog": "Skoða alla aðgerðaskrána",
        "edit-hook-aborted": "Breyting síðu stöðvuð af viðbótarkrók (extension hook).\nEngin skýring gefin.",
        "edit-gone-missing": "Gat ekki uppfært síðu.\nSvo virðist sem henni hafi verið eytt.",
        "content-model-text": "hreinn texti",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
+       "content-json-empty-object": "Tómur hlutur",
+       "content-json-empty-array": "Tómt fylki",
        "duplicate-args-category": "Síður sem nota tvíteknar breytur við ítengingu sniðmáts",
        "expensive-parserfunction-warning": "'''Viðvörun:''' Þessi síða inniheldur of mörg vinnslufrek aðgerðar þáttunar köll.\n\nHún ætti að innihalda minna en $2 {{PLURAL:$2|kall|köll}}, en {{PLURAL:$1|er nú $1 kall|eru nú $1 köll}}.",
        "expensive-parserfunction-category": "Síður með of mörg vinnslufrek aðgerðar þáttunar köll",
        "undo-nochange": "Breytingin virðist þegar hafa verið tekin til baka.",
        "undo-summary": "Tek aftur breytingu $1 frá [[Special:Contributions/$2|$2]] ([[User talk:$2|spjall]])",
        "undo-summary-username-hidden": "Afturkalla breytingu $1 eftir faldan notanda",
-       "cantcreateaccounttitle": "Ekki hægt að búa til aðgang",
        "cantcreateaccount-text": "Aðgangsgerð fyrir þetta vistfang ('''$1''') hefur verið bannað af [[User:$3|$3]].\n\nÁstæðan sem $3 gaf fyrir því er ''$2''",
        "cantcreateaccount-range-text": "Aðgangsgerð frá visföngunum „$1”, sem innihalda vistfang þitt („$4”) hefur verið bönnuð af [[User:$3|$3]].\n\nÁstæðan sem $3 gaf fyrir því er „$2”",
        "viewpagelogs": "Sýna aðgerðir varðandi þessa síðu",
        "revdelete-no-file": "Umbeðin skrá er ekki til.",
        "revdelete-show-file-confirm": "Ertu viss um að þú viljir sjá eydda breytingu af síðunni \"<nowiki>$1</nowiki>\" frá $2 $3?",
        "revdelete-show-file-submit": "Já",
+       "revdelete-selected-text": "{{PLURAL:$1|Valin útgáfa|Valdar útgáfur}} [[:$2]]:",
+       "revdelete-selected-file": "{{PLURAL:$1|Valin útgáfa skráarinnar|Valdar útgáfur skráarinnar}} [[:$2]]:",
        "logdelete-selected": "{{PLURAL:$1|Valin aðgerð|Valdar aðgerðir}}:",
+       "revdelete-text-text": "Eyddar breytingar munu áfram birtast í breytingarsögu, en hlutar hennar verða óaðgengileg almenningi.",
+       "revdelete-text-file": "Eyddar breytingar skráar munu áfram birtast í breytingarsögu, en hlutar hennar verða óaðgengileg almenningi.",
+       "revdelete-text-others": "Aðrir stjórnendur munu enn sjá falda efnið og geta tekið aftur eyðinguna, nema frekari takmarkanir séu í notkun.",
        "revdelete-confirm": "Staðfestu að þú viljir gera þetta, að þú skiljir afleiðingarnar og að þú sért að gera þetta í samræmi við  [[{{MediaWiki:Policy-url}}|samþykktir]].",
        "revdelete-suppress-text": "Bælingu á '''eingöngu''' að nota í eftirfarandi tilfellum:\n* Mögulegar ærumleiðandi upplýsingar\n* Óviðeigandi persónulegar upplýsingar\n*: ''heimilisfang, símanúmer, kennitala, osfrv.''",
        "revdelete-legend": "Setja sjáanlegar hamlanir",
        "search-category": "(flokkur $1)",
        "search-file-match": "(passar við innihald skráa)",
        "search-suggest": "Varstu að leita að: $1",
+       "search-rewritten": "Sýni niðurstöður $1. Leita í staðinn að $2.",
        "search-interwiki-caption": "Systurverkefni",
        "search-interwiki-default": "Útkomur frá $1:",
        "search-interwiki-more": "(fleiri)",
        "showingresultsinrange": "Sýni allt að <strong>$1</strong> {{PLURAL:$1|niðurstöðu|niðurstöður}} á bilinu <strong>$2</strong> til <strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Niðurstaða|Niðurstöður}} <strong>$1-$2</strong> af <strong>$3</strong>",
        "search-nonefound": "Engar niðurstöður pössuðu við fyrirspurnina.",
+       "search-nonefound-thiswiki": "Engar niðurstöður fundust við fyrirspurninni á þessu vefsvæði.",
        "powersearch-legend": "Ítarlegri leit",
        "powersearch-ns": "Leita í nafnrýmum:",
        "powersearch-togglelabel": "Athuga:",
        "rightslogtext": "Þetta er skrá yfir breytingar á réttindum notenda.",
        "action-read": "lesa þessa síðu",
        "action-edit": "breyta þessari síðu",
-       "action-createpage": "skapa síður",
-       "action-createtalk": "skapa spjallsíður",
+       "action-createpage": "skapa þessa síðu",
+       "action-createtalk": "skapa þessa spjallsíðu",
        "action-createaccount": "skapa þennan notandaaðgang",
        "action-autocreateaccount": "búa sjálfvirkt til þennan ytri notandaaðgang",
        "action-history": "skoða breytingaskrá þessarar síðu",
        "undeletedrevisions": "$1 {{PLURAL:$1|breyting endurvakin|breytingar endurvaktar}}",
        "undeletedrevisions-files": "$1 {{PLURAL:$1|breyting|breytingar}} og $2 {{PLURAL:$2|skrá|skrár}} endurvaktar",
        "undeletedfiles": "$1 {{PLURAL:$1|skrá endurvakin|skrár endurvaktar}}",
-       "cannotundelete": "Ekki var hægt að afturkalla eyðingu.\n$1",
+       "cannotundelete": "Afturköllun eyðingar mistókst að hluta eða í heild: \n$1",
        "undeletedpage": "'''$1 var endurvakin'''\n\nSkoðaðu [[Special:Log/delete|eyðingaskrána]] til að skoða eyðingar og endurvakningar.",
        "undelete-header": "Sjá [[Special:Log/delete|eyðingarskrá]] fyrir síður sem nýlega hefur verið eytt.",
        "undelete-search-title": "Leita í eyddum síðum",
        "sp-contributions-newbies-sub": "Fyrir nýliða",
        "sp-contributions-newbies-title": "Breytingar nýrra notenda",
        "sp-contributions-blocklog": "fyrri bönn",
-       "sp-contributions-suppresslog": "bæld framlög notanda",
-       "sp-contributions-deleted": "eyddar breytingar notanda",
+       "sp-contributions-suppresslog": "bæld framlög {{GENDER:$1|notanda}}",
+       "sp-contributions-deleted": "eyddar breytingar {{GENDER:$1|notanda}}",
        "sp-contributions-uploads": "innsendingar",
        "sp-contributions-logs": "aðgerðaskrá",
        "sp-contributions-talk": "spjall",
        "mw-widgets-dateinput-placeholder-month": "ÁÁÁÁ-MM",
        "mw-widgets-titleinput-description-new-page": "síðan er ekki enn til",
        "mw-widgets-titleinput-description-redirect": "tilvísun á $1",
-       "api-error-blacklisted": "Veldu annan lýsandi titil",
        "sessionmanager-tie": "Get ekki sameinað margar gerðir auðkenningarbeiðna: $1.",
        "sessionprovider-generic": "$1 setur",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "setur með vefkökum",
index 9aae28e..76dd0f0 100644 (file)
        "tagline": "Da {{SITENAME}}.",
        "help": "Aiuto",
        "search": "Ricerca",
+       "search-ignored-headings": " #<!-- lascia questa riga esattamente come è --> <pre>\n# Elenco delle intestazioni che saranno ignorate dalla ricerca.\n# Le modifiche a questa pagina saranno effettive non appena la pagina sarà indicizzata.\n# Puoi forzare la re-indicizzazione di una pagina effettuando una modifica nulla.\n# La sintassi è la seguente:\n#   * Tutto dal carattere \"#\" alla fine della riga è un commento\n#   * Tutte le righe non vuote sono le intestazioni esatte da ignorare, maiuscolo/minuscolo e tutto\nNote\nVoci correlate\nCollegamenti esterni\n #</pre> <!-- lascia questa riga esattamente come è -->",
        "searchbutton": "Ricerca",
        "go": "Vai",
        "searcharticle": "Vai",
        "yourpasswordagain": "Ripeti la password:",
        "createacct-yourpasswordagain": "Conferma password",
        "createacct-yourpasswordagain-ph": "Inserisci nuovamente la password",
-       "remembermypassword": "Ricorda la password su questo browser (per un massimo di $1 {{PLURAL:$1|giorno|giorni}})",
        "userlogin-remembermypassword": "Mantienimi collegato",
        "userlogin-signwithsecure": "Usa una connessione sicura",
        "cannotloginnow-title": "Impossibile accedere ora",
        "passwordreset-emailelement": "Nome utente: \n$1\n\nPassword temporanea: \n$2",
        "passwordreset-emailsentemail": "Se questo indirizzo di posta elettronica è associato con la tua utenza, allora verrà inviata una email per reimpostare la password.",
        "passwordreset-emailsentusername": "Se c'è un indirizzo di posta elettronica associato con questo nome utente, allora verrà inviata una email per reimpostare la password.",
-       "passwordreset-emailsent-capture": "È stata inviata una email di reimpostazione della password, il contenuto è riportato di seguito.",
-       "passwordreset-emailerror-capture": "È stata generata una email di reimpostazione della password, riportata di seguito. L'invio {{GENDER:$2|all'utente}} non è riuscito: $1",
        "passwordreset-emailsent-capture2": "L'email di reimpostazione della password {{PLURAL:$1|è stata inviata|sono state inviate}}. {{PLURAL:$1|Il nome|L'elenco di nomi}} utente e password è mostrato di seguito.",
        "passwordreset-emailerror-capture2": "Invio di email {{GENDER:$2|all'utente}} non riuscito: $1. {{PLURAL:$3|Il nome|L'elenco di nomi}} utente e password è mostrato di seguito.",
        "passwordreset-nocaller": "Un chiamante deve essere fornito",
        "passwordreset-nodata": "Non è stato fornito né un nome utente né un indirizzo di posta elettronica",
        "changeemail": "Modifica o rimuovi indirizzo di posta elettronica",
        "changeemail-header": "Completa questo modulo per cambiare il tuo indirizzo email. Se vuoi rimuovere l'associazione di qualsiasi indirizzo email dalla tua utenza, lascia il nuovo indirizzo email vuoto quando invii il modulo.",
-       "changeemail-passwordrequired": "Sarà necessario inserire la password per confermare la modifica.",
        "changeemail-no-info": "Devi aver effettuato l'accesso per accedere a questa pagina direttamente.",
        "changeemail-oldemail": "Indirizzo email attuale:",
        "changeemail-newemail": "Nuovo indirizzo email:",
        "minoredit": "Questa è una modifica minore",
        "watchthis": "Aggiungi agli osservati speciali",
        "savearticle": "Salva la pagina",
+       "savechanges": "Salva le modifiche",
        "publishpage": "Pubblica pagina",
+       "publishchanges": "Pubblica modifiche",
        "preview": "Anteprima",
        "showpreview": "Visualizza anteprima",
        "showdiff": "Mostra modifiche",
        "blankarticle": "<strong>Attenzione:</strong> la pagina che stai creando è vuota.\nCliccando nuovamente su \"{{int:savearticle}}\", la pagina sarà creata senza alcun contenuto.",
-       "anoneditwarning": "<strong>Attenzione:</strong> Accesso non effettuato. Se effettuerai delle modifiche il tuo indirizzo IP sarà visibile pubblicamente. Se <strong>[$1 accedi]</strong> o <strong>[$2 crei un'utenza]</strong>, le tue modifiche saranno attribuite al tuo nome utente, insieme ad altri benefici.",
-       "anonpreviewwarning": "''Non è stato eseguito il login. Salvando la pagina, il proprio indirizzo IP sarà registrato nella cronologia.''",
+       "anoneditwarning": "<strong>Attenzione:</strong> non hai effettuato l'accesso. Se effettuerai delle modifiche il tuo indirizzo IP sarà visibile pubblicamente. Se <strong>[$1 accedi]</strong> o <strong>[$2 crei un'utenza]</strong>, le tue modifiche saranno attribuite al tuo nome utente, insieme ad altri benefici.",
+       "anonpreviewwarning": "<em>Non hai effettuato l'accesso. Salvando, il tuo indirizzo IP sarà registrato nella cronologia della pagina.</em>",
        "missingsummary": "<strong>Attenzione:</strong> non è stato specificato l'oggetto di questa modifica. Premendo di nuovo \"{{int:savearticle}}\" la modifica verrà salvata senza.",
        "selfredirect": "<strong>Attenzione:</strong> stai reindirizzando questa pagina a se stessa.\nPotresti aver indicato la destinazione errata per il redirect, o stai modificando la pagina sbagliata.\nSe fai clic nuovamente su \"{{int:savearticle}}\", il redirect sarà creato comunque.",
        "missingcommenttext": "Inserire un commento qui sotto.",
        "content-model-css": "CSS",
        "content-json-empty-object": "Oggetto vuoto",
        "content-json-empty-array": "Array vuoto",
+       "deprecated-self-close-category": "Pagine che utilizzano tag HTML auto-chiusi non validi",
+       "deprecated-self-close-category-desc": "La pagina contiene tag HTML auto-chiusi non validi, come <code>&lt;b/></code> o <code>&lt;span/></code>. Il comportamento di questi presto cambierà per essere coerente con le specifiche HTML5, per questo il loro uso nel wikitesto è deprecato.",
        "duplicate-args-warning": "<strong>Avvertenza:</strong> [[:$1]] chiama [[:$2]] con più di un valore per il parametro \"$3\". Verrà utilizzato solo l'ultimo valore fornito.",
        "duplicate-args-category": "Pagine contenenti chiamate a template con parametri duplicati",
        "duplicate-args-category-desc": "La pagina contiene chiamate a template che utilizzano argomenti duplicati, come ad esempio <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> o <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Sembra che la modifica sia già stata annullata.",
        "undo-summary": "Annullata la modifica $1 di [[Special:Contributions/$2|$2]] ([[User talk:$2|discussione]])",
        "undo-summary-username-hidden": "Annullata la modifica $1 di un utente nascosto",
-       "cantcreateaccounttitle": "Impossibile registrare un utente",
        "cantcreateaccount-text": "La registrazione è stata bloccata da [[User:$3|$3]] per questo indirizzo IP ('''$1''').\n\nLa motivazione del blocco fornita da $3 è la seguente: ''$2''",
        "cantcreateaccount-range-text": "La registrazione da indirizzi IP nell'intervallo <strong>$1</strong>, che include il tuo (<strong>$4</strong>), è stata bloccata da [[User:$3|$3]].\n\nLa motivazione fornita da $3 è <em>$2</em>",
        "viewpagelogs": "Visualizza i registri relativi a questa pagina",
        "grant-group-high-volume": "Esegue azioni massive",
        "grant-group-customization": "Personalizzazione e preferenze",
        "grant-group-administration": "Esegue azioni amministrative",
+       "grant-group-private-information": "Accede ai dati privati su di te",
        "grant-group-other": "Attività varie",
        "grant-blockusers": "Blocca e sblocca utenti",
        "grant-createaccount": "Crea un'utenza",
        "grant-highvolume": "Modifiche massive",
        "grant-oversight": "Nasconde utenti e sopprime le versioni",
        "grant-patrol": "Segna le modifiche alle pagine come verificate",
+       "grant-privateinfo": "Accede a informazioni private",
        "grant-protect": "Protegge e sprotegge pagine",
        "grant-rollback": "Rollback delle modifiche alle pagine",
        "grant-sendemail": "Invia email ad altri utenti",
        "action-applychangetags": "applicare delle etichette alle tue modifiche",
        "action-changetags": "aggiungere o rimuovere specifiche etichette su singole versioni o voci di registro",
        "action-deletechangetags": "cancellare le etichette dal database",
+       "action-purge": "aggiornare questa pagina",
        "nchanges": "$1 {{PLURAL:$1|modifica|modifiche}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|dall'ultima visita}}",
        "enhancedrc-history": "cronologia",
        "file-thumbnail-no": "Il nome del file inizia con <strong>$1</strong>; sembra quindi essere una miniatura ''(thumbnail)''.\nSe si dispone dell'immagine nella risoluzione originale, si prega di caricarla. In caso contrario, si prega di cambiare il nome del file.",
        "fileexists-forbidden": "Un file con questo nome esiste già e non può essere sovrascritto. Tornare indietro e modificare il nome con il quale caricare il file. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Un file con questo nome esiste già nell'archivio di risorse multimediali condivise. Se si desidera ancora caricare il file, tornare indietro e modificare il nome con il quale caricare il file. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Il file caricato è un duplicato esatto dell'attuale versione di <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Il file caricato è un duplicato esatto di {{PLURAL:$2|una versione precedente|versioni precedenti}} di <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Questo file è un duplicato {{PLURAL:$1|del seguente|dei seguenti}} file:",
        "file-deleted-duplicate": "Un file identico a questo ([[:$1]]) è stato cancellato in passato. Verificare la cronologia delle cancellazioni prima di caricarlo di nuovo.",
        "file-deleted-duplicate-notitle": "Un file identico a questo è stato cancellato in passato, ed il titolo è stato soppresso. Chiedi a qualcuno che ha la possibilità di vedere i file soppressi di esaminare la situazione prima di procedere nuovamente al caricamento.",
        "filerevert-submit": "Ripristina",
        "filerevert-success": "'''Il file [[Media:$1|$1]]''' è stato ripristinato alla [$4 versione del $2, $3].",
        "filerevert-badversion": "Non esistono versioni locali precedenti del file con il timestamp richiesto.",
+       "filerevert-identical": "La versione attuale del file è già identica a quella selezionata.",
        "filedelete": "Cancella $1",
        "filedelete-legend": "Cancella il file",
        "filedelete-intro": "Stai per cancellare il file '''[[Media:$1|$1]]''' con tutta la sua cronologia.",
        "watchnologin": "Accesso non effettuato",
        "addwatch": "Aggiungi agli osservati speciali",
        "addedwatchtext": "\"[[:$1]]\" e la sua pagina di discussione sono state aggiunte alla propria [[Special:Watchlist|lista degli osservati speciali]].",
+       "addedwatchtext-talk": "\"[[:$1]]\" e la sua pagina associata sono state aggiunte alla propria [[Special:Watchlist|lista degli osservati speciali]].",
        "addedwatchtext-short": "La pagina \"$1\" è stata aggiunta alla propria lista degli osservati speciali.",
        "removewatch": "Rimuovi dagli osservati speciali",
        "removedwatchtext": "\"[[:$1]]\" e la sua pagina di discussione sono state rimosse dalla propria [[Special:Watchlist|lista degli osservati speciali]].",
+       "removedwatchtext-talk": "\"[[:$1]]\" e la sua pagina associata sono state rimosse dalla propria [[Special:Watchlist|lista degli osservati speciali]].",
        "removedwatchtext-short": "La pagina \"$1\" è stata rimossa dalla propria lista degli osservati speciali.",
        "watch": "Segui",
        "watchthispage": "Segui questa pagina",
        "rollbacklinkcount-morethan": "rollback di più di {{PLURAL:$1|una modifica|$1 modifiche}}",
        "rollbackfailed": "Rollback fallito",
        "rollback-missingparam": "Parametri obbligatori mancanti nella richiesta.",
+       "rollback-missingrevision": "Impossibile caricare i dati della versione.",
        "cantrollback": "Impossibile annullare le modifiche; l'utente che le ha effettuate è l'unico ad aver contribuito alla pagina.",
        "alreadyrolled": "Non è possibile annullare le modifiche apportate alla pagina [[:$1]] da parte di [[User:$2|$2]] ([[User talk:$2|discussione]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); un altro utente ha già modificato la pagina oppure ha effettuato il rollback.\n\nLa modifica più recente alla pagina è stata apportata da [[User:$3|$3]] ([[User talk:$3|discussione]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "L'oggetto della modifica era: <em>$1</em>.",
        "undeletehistorynoadmin": "Questa pagina è stata cancellata.\nIl motivo della cancellazione è mostrato qui sotto, assieme ai dettagli dell'utente che ha modificato questa pagina prima della cancellazione.\nIl testo contenuto nelle versioni cancellate è disponibile solo agli amministratori.",
        "undelete-revision": "Versione cancellata della pagina $1, inserita il $4 alle $5 da $3:",
        "undeleterevision-missing": "Versione errata o mancante. Il collegamento è errato oppure la versione è stata già ripristinata o eliminata dall'archivio.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|Una versione non può essere ripristinata|$1 versioni non possono essere ripristinate}}, poiché {{PLURAL:$1|il suo|i loro}} <code>rev_id</code> {{PLURAL:$1|è già utilizzato|sono già utilizzati}}.",
        "undelete-nodiff": "Non è stata trovata nessuna versione precedente.",
        "undeletebtn": "Ripristina",
        "undeletelink": "visualizza/ripristina",
        "undeletedrevisions": "{{PLURAL:$1|Una versione recuperata|$1 versioni recuperate}}",
        "undeletedrevisions-files": "{{PLURAL:$1|Una versione|$1 versioni}} e $2 file recuperati",
        "undeletedfiles": "{{PLURAL:$1|Un file recuperato|$1 file recuperati}}",
-       "cannotundelete": "Ripristino non riuscito:\n$1",
+       "cannotundelete": "Alcuni o tutti i ripristini non riusciti:\n$1",
        "undeletedpage": "'''La pagina $1 è stata recuperata'''\n\nConsulta il [[Special:Log/delete|registro delle cancellazioni]] per vedere le cancellazioni e i recuperi più recenti.",
        "undelete-header": "Consulta il [[Special:Log/delete|registro delle cancellazioni]] per vedere le cancellazioni più recenti.",
        "undelete-search-title": "Ricerca nelle pagine cancellate",
        "sp-contributions-newbies-sub": "Per i nuovi utenti",
        "sp-contributions-newbies-title": "Contributi dei nuovi utenti",
        "sp-contributions-blocklog": "blocchi",
-       "sp-contributions-suppresslog": "contributi utente soppressi",
-       "sp-contributions-deleted": "contributi utente cancellati",
+       "sp-contributions-suppresslog": "contributi {{GENDER:$1|utente}} soppressi",
+       "sp-contributions-deleted": "contributi {{GENDER:$1|utente}} cancellati",
        "sp-contributions-uploads": "file caricati",
        "sp-contributions-logs": "registri",
        "sp-contributions-talk": "discussione",
        "pageinfo-article-id": "ID della pagina",
        "pageinfo-language": "Lingua del contenuto della pagina",
        "pageinfo-content-model": "Modello del contenuto della pagina",
+       "pageinfo-content-model-change": "cambia",
        "pageinfo-robot-policy": "Indicizzazione per i robot",
        "pageinfo-robot-index": "Consentito",
        "pageinfo-robot-noindex": "Non consentito",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
        "mw-widgets-titleinput-description-new-page": "questa pagina non esiste ancora",
        "mw-widgets-titleinput-description-redirect": "reindirizzamento a $1",
-       "api-error-blacklisted": "Per favore scegli un titolo diverso e descrittivo.",
        "sessionmanager-tie": "Non è possibile combinare più tipi di richieste di autenticazione: $1.",
        "sessionprovider-generic": "sessioni $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sessioni basate su cookie",
        "log-action-filter-newusers": "Tipo di creazione utenza:",
        "log-action-filter-patrol": "Tipo di verifica:",
        "log-action-filter-protect": "Tipo di protezione:",
-       "log-action-filter-rights": "Tipo di modifica diritti",
-       "log-action-filter-suppress": "Tipo di soppressione",
+       "log-action-filter-rights": "Tipo di modifica diritti:",
+       "log-action-filter-suppress": "Tipo di soppressione:",
        "log-action-filter-upload": "Tipo di caricamento:",
        "log-action-filter-all": "Tutto",
        "log-action-filter-block-block": "Blocco",
index f76e11d..5f451b9 100644 (file)
                        "Kana Higashikawa",
                        "Shield-9",
                        "Waiesu",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "組曲師"
                ]
        },
        "tog-underline": "リンクの下線:",
        "tog-hideminor": "最近の更新に細部の編集を表示しない",
        "tog-hidepatrolled": "最近の更新に巡回済みの編集を表示しない",
-       "tog-newpageshidepatrolled": "新しいページの一覧に巡回済みのページを表示しない",
+       "tog-newpageshidepatrolled": "新しいページの一覧に巡回済みのページを表示しない",
        "tog-hidecategorization": "ページのカテゴリ追加・除去を表示しない",
-       "tog-extendwatchlist": "ã\82¦ã\82©ã\83\83ã\83\81ã\83ªã\82¹ã\83\88ã\82\92æ\8b¡å¼µã\81\97ã\80\81æ\9c\80æ\96°ã\81®ã\82\82ã\81®ã\81 ã\81\91ã\81§ã\81¯ã\81ªã\81\8fã\81\99ã\81¹ã\81¦ã\81®å¤\89æ\9b´ã\82\92表示",
+       "tog-extendwatchlist": "ã\82¦ã\82©ã\83\83ã\83\81ã\83ªã\82¹ã\83\88ã\82\92æ\8b¡å¼µã\81\97ã\81¦æ\9c\80æ\96°ã\81®å¤\89æ\9b´ä»¥å¤\96ã\82\82ã\81\99ã\81¹ã\81¦表示",
        "tog-usenewrc": "最近の更新とウォッチリストで、複数の変更をページごとにまとめる",
        "tog-numberheadings": "見出しに番号を自動的に振る",
        "tog-showtoolbar": "編集用のツールバーを表示",
        "tog-ccmeonemails": "他の利用者に送信したメールの控えを自分にも送信",
        "tog-diffonly": "差分の下にページ内容を表示しない",
        "tog-showhiddencats": "隠しカテゴリを表示",
-       "tog-norollbackdiff": "ロールバック後の差分を表示しない",
+       "tog-norollbackdiff": "巻き戻し後の差分を表示しない",
        "tog-useeditwarning": "変更を保存せずに編集画面から離れようとしたら警告",
        "tog-prefershttps": "ログインする際、常に安全な接続を使用する",
        "underline-always": "常に付ける",
        "category_header": "カテゴリ「$1」にあるページ",
        "subcategories": "下位カテゴリ",
        "category-media-header": "カテゴリ「$1」にあるメディア",
-       "category-empty": "<em>このカテゴリには現在、ページやメディアが何もありません。</em>",
+       "category-empty": "<em>現在このカテゴリには、ページまたはメディアがありません。</em>",
        "hidden-categories": "{{PLURAL:$1|隠しカテゴリ}}",
        "hidden-category-category": "隠しカテゴリ",
        "category-subcat-count": "{{PLURAL:$2|このカテゴリには以下の下位カテゴリのみが含まれています。|このカテゴリには下位カテゴリ $2 件が含まれており、そのうち以下の{{PLURAL:$1| $1 件}}を表示しています。}}",
        "tagline": "提供: {{SITENAME}}",
        "help": "ヘルプ",
        "search": "検索",
+       "search-ignored-headings": " #<!-- leave this line exactly as it is --> <pre>\n# 検索で無視される見出しを記述します。\n# この変更は、見出し付きページがインデックスされると同時に有効になります。\n# 空の編集を実行することで、強制的にページの再インデックスが行われます。\n# 文法は以下のとおり:\n#   * \"#\" で始まる行は、その行末までがすべてコメントです。\n#   * 非空白行が、無視したい見出しになります。大文字小文字の区別を含め、完全に一致するものが対象です。\nReferences\nExternal links\nSee also\n #</pre> <!-- leave this line exactly as it is -->",
        "searchbutton": "検索",
        "go": "表示",
        "searcharticle": "表示",
        "yourpasswordagain": "パスワード再入力:",
        "createacct-yourpasswordagain": "パスワード再入力",
        "createacct-yourpasswordagain-ph": "パスワードを再入力",
-       "remembermypassword": "このブラウザーにログイン情報を保存 (最長 $1 {{PLURAL:$1|日|日間}})",
        "userlogin-remembermypassword": "ログイン状態を保持",
        "userlogin-signwithsecure": "安全な接続の使用",
        "cannotloginnow-title": "今はログインできません",
        "createacct-another-realname-tip": "本名は省略できます。\n入力すると、その利用者の著作物の帰属表示に使われます。",
        "pt-login": "ログイン",
        "pt-login-button": "ログイン",
+       "pt-login-continue-button": "ログインを続行",
        "pt-createaccount": "アカウント作成",
        "pt-userlogout": "ログアウト",
        "php-mail-error-unknown": "PHPのmail()関数での不明なエラーです。",
        "passwordreset-emailelement": "利用者名: \n$1\n\n仮パスワード: \n$2",
        "passwordreset-emailsentemail": "このメールアドレスがあなたのアカウントに関連付けられている場合は、パスワードリセットのメールが送信されます。",
        "passwordreset-emailsentusername": "この利用者名に関連付けられたメールアドレスがある場合は、パスワードリセットのメールが送信されます。",
-       "passwordreset-emailsent-capture": "下記の内容の、パスワード再設定メールをお送りしました。",
-       "passwordreset-emailerror-capture": "以下の内容のパスワード再設定メールを生成しましたが、{{GENDER:$2|利用者}}への送信に失敗しました: $1",
        "passwordreset-emailsent-capture2": "パスワードリセットの{{PLURAL:$1|メール}}が送信されました。{{PLURAL:$1|利用者名とパスワード|利用者名とパスワードの一覧}}は以下のとおりです。",
        "passwordreset-emailerror-capture2": "{{GENDER:$2|利用者}}へのメール送信に失敗しました: $1{{PLURAL:$3|利用者名とパスワード|利用者名とパスワードの一覧}}は以下のとおりです。",
        "passwordreset-ignored": "パスワードのリセットが処理されませんでした。プロバイダーが設定されていない可能性があります。",
        "passwordreset-invalideamil": "無効なメールアドレスです",
-       "changeemail": "ã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\82\92変更または除去",
+       "changeemail": "ã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\81®変更または除去",
        "changeemail-header": "あなたのメールアドレスを変更するには、このフォームを完成させます。もし、あなたのアカウントから任意のメールアドレスの関連付けを削除したい場合は、フォームの送信時に、新しいメールアドレスを空白のままにします。",
-       "changeemail-passwordrequired": "この変更を確認するためにパスワードを入力する必要があります。",
        "changeemail-no-info": "このページに直接アクセスするためにはログインしている必要があります。",
        "changeemail-oldemail": "現在のメールアドレス:",
        "changeemail-newemail": "新しいメールアドレス:",
        "savearticle": "ページを保存",
        "savechanges": "変更を保存",
        "publishpage": "ページを公開",
+       "publishchanges": "変更を公開",
        "preview": "プレビュー",
        "showpreview": "プレビューを表示",
        "showdiff": "差分を表示",
        "undo-nochange": "指定した編集は既に取り消されたようです。",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|トーク]]) による版 $1 を取り消し",
        "undo-summary-username-hidden": "秘匿された利用者による版 $1 を取り消し",
-       "cantcreateaccounttitle": "アカウントを作成できません",
        "cantcreateaccount-text": "この IP アドレス (<strong>$1</strong>) からのアカウント作成は、[[User:$3|$3]] によってブロックされています。\n\n$3 が示した理由: <em>$2</em>",
        "cantcreateaccount-range-text": "この IP アドレス (<strong>$4</strong>) を含む、IP アドレス範囲 <strong>$1</strong> からのアカウント作成は、[[User:$3|$3]] によってブロックされています。\n\n$3 が示した理由: <em>$2</em>",
        "viewpagelogs": "このページの記録を閲覧",
        "watchnologin": "ログインしていません",
        "addwatch": "ウォッチリストに追加",
        "addedwatchtext": "「[[:$1]]」および付随する議論ページを、[[Special:Watchlist|ウォッチリスト]]に追加しました。",
+       "addedwatchtext-talk": "「[[:$1]]」および付随するページを[[Special:Watchlist|ウォッチリスト]]に追加しました。",
        "addedwatchtext-short": "ページ「$1」をウォッチリストに追加しました。",
        "removewatch": "ウォッチリストから除去",
        "removedwatchtext": "「[[:$1]]」および付随する議論ページを[[Special:Watchlist|ウォッチリスト]]から除去しました。",
+       "removedwatchtext-talk": "「[[:$1]]」および付随するページを[[Special:Watchlist|ウォッチリスト]]から除去しました。",
        "removedwatchtext-short": "ページ「$1」をウォッチリストから除去しました。",
        "watch": "ウォッチ",
        "watchthispage": "このページをウォッチする",
        "changecontentmodel-submit": "変更",
        "changecontentmodel-success-title": "コンテンツ・モデルは変更されました",
        "changecontentmodel-success-text": "[[:$1]]のコンテンツ・タイプは変更されました。",
-       "changecontentmodel-cannot-convert": "[[:$1]]上のコンテントは$2の型には変換できません。",
+       "changecontentmodel-cannot-convert": "[[:$1]]のコンテンツは$2の型には変換できません。",
        "changecontentmodel-nodirectediting": "$1 コンテンツ・モデルは、ダイレクト編集をサポートしていません。",
+       "changecontentmodel-emptymodels-text": "[[:$1]]のコンテンツはどの型にも変換できません。",
        "log-name-contentmodel": "コンテンツ・モデル変更記録",
        "log-description-contentmodel": "ページのコンテンツ・モデルに関連する出来事",
+       "logentry-contentmodel-new": "$1 が ページ $3 を既定でないコンテンツ・モデル「$5」で{{GENDER:$2|作成しました}}。",
        "logentry-contentmodel-change": "$1 がページ $3 のコンテンツ・モデルを \"$4\" から \"$5\" に{{GENDER:$2|変更しました}}",
        "logentry-contentmodel-change-revertlink": "差し戻し",
        "logentry-contentmodel-change-revert": "差し戻し",
        "undeletehistorynoadmin": "このページは削除されています。\n削除の理由は、削除前にこのページを編集していた利用者の詳細情報と共に、以下に表示されています。\n管理者以外の利用者には、削除された各版の本文への制限がかけられています。",
        "undelete-revision": "削除されたページ $1 の $4 $5 時点での $3 による版:",
        "undeleterevision-missing": "無効または存在しない版です。\n間違ったリンクをたどったか、この版は既に復元されたか、もしくは保存版から除去された可能性があります。",
+       "undeleterevision-duplicate-revid": "<code>rev_id</code> は既に使用されているため、{{PLURAL:$1|1件の版|$1件の版}}を復元できませんでした。",
        "undelete-nodiff": "これより前の版はありません。",
        "undeletebtn": "復元",
        "undeletelink": "閲覧/復元",
        "undeletedrevisions": "{{PLURAL:$1|$1版}}を復元しました",
        "undeletedrevisions-files": "{{PLURAL:$1|$1版}}と{{PLURAL:$2|$2ファイル}}を復元しました",
        "undeletedfiles": "{{PLURAL:$1|$1ファイル}}を復元しました",
-       "cannotundelete": "復元に失敗しました:\n$1",
+       "cannotundelete": "復元に一部またはすべて失敗しました:\n$1",
        "undeletedpage": "<strong>$1を復元しました。</strong>\n\n最近の削除と復元の記録については[[Special:Log/delete|削除記録]]を参照してください。",
        "undelete-header": "最近削除されたページは[[Special:Log/delete|削除記録]]で確認できます。",
        "undelete-search-title": "削除されたページの検索",
        "sp-contributions-newbies-sub": "新規利用者のみ",
        "sp-contributions-newbies-title": "新規利用者の投稿記録",
        "sp-contributions-blocklog": "ブロック記録",
-       "sp-contributions-suppresslog": "利用者の秘匿された投稿",
-       "sp-contributions-deleted": "削除された投稿の一覧",
+       "sp-contributions-suppresslog": "{{GENDER:$1|利用者}}の秘匿された投稿",
+       "sp-contributions-deleted": "{{GENDER:$1|利用者}}の削除された投稿の一覧",
        "sp-contributions-uploads": "アップロード",
        "sp-contributions-logs": "記録",
        "sp-contributions-talk": "トーク",
        "tooltip-ca-nstab-category": "カテゴリページを閲覧",
        "tooltip-minoredit": "この編集に細部の変更の印を付ける",
        "tooltip-save": "変更を保存する",
+       "tooltip-publish": "あなたによる変更を公開",
        "tooltip-preview": "変更内容をプレビューで確認できます。保存前に使用してください。",
        "tooltip-diff": "文章への変更箇所を表示する",
        "tooltip-compareselectedversions": "選択した2つの版の差分を表示する",
        "sqlite-no-fts": "$1 (全文検索なし)",
        "logentry-delete-delete": "$1 がページ「$3」を{{GENDER:$2|削除しました}}",
        "logentry-delete-restore": "$1 がページ「$3」を{{GENDER:$2|復元しました}}",
-       "logentry-delete-event": "$1 が$3の{{PLURAL:$5|記録項目$5件}}の閲覧レベルを{{GENDER:$2|変更しました}}: $4",
-       "logentry-delete-revision": "$1 がページ「$3」の{{PLURAL:$5|$5版}}の閲覧レベルを{{GENDER:$2|変更しました}}: $4",
-       "logentry-delete-event-legacy": "$1 が「$3」の記録項目の閲覧レベルを{{GENDER:$2|変更しました}}",
+       "logentry-delete-event": "$1 が $3 の{{PLURAL:$5|記録項目|記録項目$5件}}の閲覧レベルを{{GENDER:$2|変更しました}}: $4",
+       "logentry-delete-revision": "$1 がページ「$3」の{{PLURAL:$5|版|$5件の版}}の閲覧レベルを{{GENDER:$2|変更しました}}: $4",
+       "logentry-delete-event-legacy": "$1 が $3 の記録項目の閲覧レベルを{{GENDER:$2|変更しました}}",
        "logentry-delete-revision-legacy": "$1 がページ「$3」の版の閲覧レベルを{{GENDER:$2|変更しました}}",
-       "logentry-suppress-delete": "$1 がページ「$3」を{{GENDER:$2|隠蔽しました}}",
-       "logentry-suppress-event": "$1 が$3の{{PLURAL:$5|記録項目$5件}}の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}: $4",
-       "logentry-suppress-revision": "$1 がページ「$3」の{{PLURAL:$5|$5版}}の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}: $4",
-       "logentry-suppress-event-legacy": "$1 が$3で記録項目の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}",
+       "logentry-suppress-delete": "$1 がページ「$3」を{{GENDER:$2|秘匿しました}}",
+       "logentry-suppress-event": "$1 が $3 の{{PLURAL:$5|記録項目|$5件の記録項目}}の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}: $4",
+       "logentry-suppress-revision": "$1 がページ「$3」の{{PLURAL:$5|版|$5件の版}}の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}: $4",
+       "logentry-suppress-event-legacy": "$1 が $3 の記録項目の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}",
        "logentry-suppress-revision-legacy": "$1 がページ「$3」の版の閲覧レベルを見えない形で{{GENDER:$2|変更しました}}",
        "revdelete-content-hid": "本文の不可視化",
        "revdelete-summary-hid": "編集要約の不可視化",
        "logentry-tag-update-add-logentry": "$1 がページ $3 の記録項目 $5 に{{PLURAL:$7|タグ}} $6 を{{GENDER:$2|追加しました}}",
        "logentry-tag-update-remove-revision": "$1 がページ $3 の版 $4 から{{PLURAL:$9|タグ}} $8 を{{GENDER:$2|除去しました}}",
        "logentry-tag-update-remove-logentry": "$1 がページ $3 の記録項目 $5 から{{PLURAL:$9|タグ}} $8 を{{GENDER:$2|除去しました}}",
-       "logentry-tag-update-revision": "$1 ã\81\8cã\83\9aã\83¼ã\82¸ã\80\8c$3ã\80\8dã\81®ç\89\88 $4 ã\81§ã\81®ã\82¿ã\82°ã\82\92{{GENDER:$2|æ\9b´æ\96°ã\81\97ã\81¾ã\81\97ã\81\9f}} ($6 ã\81\8c{{PLURAL:$7|追å\8a }}ã\80\81$8 ã\81\8c{{PLURAL:$9|削除}})",
+       "logentry-tag-update-revision": "$1 ã\81\8cã\83\9aã\83¼ã\82¸ã\80\8c$3ã\80\8dã\81®ç\89\88 $4 ã\81®ã\82¿ã\82°ã\82\92{{GENDER:$2|æ\9b´æ\96°ã\81\97ã\81¾ã\81\97ã\81\9f}} ($6 ã\82\92{{PLURAL:$7|追å\8a }}ã\80\81$8 ã\82\92{{PLURAL:$9|削除}})",
        "logentry-tag-update-logentry": "$1 がページ「$3」の記録項目 $5 のタグを{{GENDER:$2|更新しました}} ($6 を{{PLURAL:$7|追加}}、$8 を{{PLURAL:$9|削除}})",
        "rightsnone": "(なし)",
        "revdelete-summary": "編集内容の要約",
        "feedback-terms": "私のユーザーエージェント情報には、使用ブラウザやオペレーティングシステムのバージョンの情報が含まれており、その情報は私が提供するフィードバックとあわせて公開されることを理解しました。",
        "feedback-termsofuse": "利用規約に従い、フィードバックを提供することに同意します。",
        "feedback-thanks": "ありがとうございます。フィードバックを「[$2 $1]」のページに投稿しました。",
-       "feedback-thanks-title": "ã\81\82ã\82\8aã\81\8cã\81¨ã\81\86ã\81\94ã\81\96ã\81\84ã\81¾ã\81\99!",
+       "feedback-thanks-title": "ã\81\8aé¡\98ã\81\84ã\81\97ã\81¾ã\81\99ï¼\81",
        "feedback-useragent": "ユーザーエージェント:",
        "searchsuggest-search": "検索",
        "searchsuggest-containing": "この語句を全文検索",
        "mw-widgets-dateinput-no-date": "選択されたデータ無し",
        "mw-widgets-titleinput-description-new-page": "ページは存在しません",
        "mw-widgets-titleinput-description-redirect": "$1 へのリダイレクト",
-       "api-error-blacklisted": "他の、説明的なタイトルをお選びください。",
        "sessionmanager-tie": "複数の要求の認証方法を組み合わせることはできません: $1。",
        "sessionprovider-generic": "$1 セッション",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "クッキーベースのセッション",
        "log-action-filter-newusers": "アカウント作成の種類:",
        "log-action-filter-patrol": "巡回の種類:",
        "log-action-filter-protect": "保護の種類:",
+       "log-action-filter-suppress": "秘匿の種類:",
        "log-action-filter-upload": "アップロードの種類",
        "log-action-filter-all": "すべて",
        "log-action-filter-block-block": "ブロック",
        "log-action-filter-block-reblock": "ブロック変更",
        "log-action-filter-block-unblock": "ブロック解除",
+       "log-action-filter-contentmodel-change": "コンテンツモデルの変更",
        "log-action-filter-delete-delete": "ページの削除",
        "log-action-filter-delete-restore": "ページの復帰",
        "log-action-filter-delete-event": "記録の削除",
        "log-action-filter-protect-modify": "保護設定の変更",
        "log-action-filter-protect-unprotect": "保護解除",
        "log-action-filter-protect-move_prot": "保護設定の移動",
+       "log-action-filter-rights-rights": "手動変更",
+       "log-action-filter-rights-autopromote": "自動変更",
        "log-action-filter-suppress-event": "記録の秘匿",
        "log-action-filter-suppress-revision": "版の秘匿",
        "log-action-filter-suppress-delete": "ページの秘匿",
index 94f050f..130e070 100644 (file)
@@ -35,7 +35,7 @@
        "tog-watchmoves": "Wuwuh kaca lan barkas lih-lihanku nyang pawawanganku",
        "tog-watchdeletion": "Wuwuh kaca lan barkas busakanku nyang pawawanganku",
        "tog-watchuploads": "Wuwuh barkas anyar unggahanku nyang pawawanganku",
-       "tog-watchrollback": "Wuwuh kaca sing tak wurungaké nyang pawawanganku",
+       "tog-watchrollback": "Wuwuh kaca sing takpulihaké nyang pawawanganku",
        "tog-minordefault": "Tengeri kabèh besutan minangka besutan cilik sacara baku",
        "tog-previewontop": "Deleng pratuduh sadurungé mbesut kothak",
        "tog-previewonfirst": "Delelng pratuduh nalika mbesut pisanan",
        "listingcontinuesabbrev": "samb.",
        "index-category": "Kaca kaindhèksan",
        "noindex-category": "Kaca ora kaindhèksan",
-       "broken-file-category": "Kaca mawa pranala berkas rusak",
+       "broken-file-category": "Kaca mawa pranala barkas rusak",
        "about": "Bab",
        "article": "Kaca isi",
        "newwindow": "(buka mawa jendhéla anyar)",
-       "cancel": "Wurungaké",
+       "cancel": "Wurung",
        "moredotdotdot": "Liyané...",
        "morenotlisted": "Pratélan iki ora jangkep.",
        "mypage": "Kaca",
        "confirmable-confirm": "{{GENDER:$1|Sampéyan}} yakin?",
        "confirmable-yes": "Iya",
        "confirmable-no": "Ora",
-       "thisisdeleted": "Mirsani utawa mbalèkaké $1?",
+       "thisisdeleted": "Ndeleng utawa mbalèkaké $1?",
        "viewdeleted": "Deleng $1?",
-       "restorelink": "$1 {{PLURAL:$1|suntingan|suntingan}} sing wis kabusak",
+       "restorelink": "$1 {{PLURAL:$1|besutan}} sing wis dibusak",
        "feedlinks": "Asupan:",
        "feed-invalid": "Tipe permintaan asupan ora bener.",
        "feed-unavailable": "Umpan sindikasi (''syndication feeds'') ora kasedyakaké",
        "no-null-revision": "Ora isa nggawe revisi 'null' anyar kanggo kaca \"$1\"",
        "badtitle": "Sesirah ala",
        "badtitletext": "Sesirahing kaca sing dikarepaké ora sah, suwung, utawa salah nggayut nyang sesirah antarabasa utawa antarawiki.\nIku mungkin ngandhut pralambang siji utawa luwih sing ora kena dianggo tumrap sesirah iki.",
-       "perfcached": "Data iki mung dijupuk saka papan singgahan lan mungkin ora kaanyaran. Maksimum {{PLURAL:$1|sak asil|$1 asil}} sumadhiya nèng papan singgahan.",
-       "perfcachedts": "Data iki mung dijupuk saka papan singgahan lan mungkin dianyari pungkasan $1. Maksimum {{PLURAL:$4|sak asil|$4 asil}} sumadhiya nèng papan singgahan.",
+       "perfcached": "Data ing ngisor iki kasimpen ing telih lan mungkin durung dianyari. Paling akèh ana {{PLURAL:$1|sakasil|$1 kasil}} sumadhiya ing telih iku.",
+       "perfcachedts": "Data ing ngisor iki kasimpen ing telih, lan pungkasan dianyari $1. Paling akèh ana {{PLURAL:$4|sakasil|$4 kasil}} sumadhiya ing telih iku.",
        "querypage-no-updates": "Update saka kaca iki lagi dipatèni. Data sing ana ing kéné saiki ora bisa bakal dibalèni unggah manèh.",
        "viewsource": "Deleng sumber",
        "viewsource-title": "Delok sumberé $1",
        "viewyourtext": "Sampéyan bisa ndeleng lan nyalin sumbering <strong>besutaning sampéyan</strong> ing kaca iki.",
        "protectedinterface": "Kaca iki isiné tèks antarmuka sing dienggo software lan wis dikunci kanggo menghindari kasalahan.",
        "editinginterface": "'''Pènget:''' Panjenengan nyunting kaca sing dianggo nyedyakaké tèks antarmuka kanggo piranti alus.\nPangowahan kaca iki bakal awèh pangaruh marang tampilan antarmuka panganggo kanggoné panganggo liya.\nKanggo terjemahan, mangga nganggo [https://translatewiki.net/wiki/Main_Page?setlang=en translatewiki.net], proyèk lokalisasi MediaWiki.",
+       "translateinterface": "Saperlu nambah utawa ngowah pertalan tumrap kabèh wiki, mangga anggoa [https://translatewiki.net/ translatewiki.net] minangka proyèk panglokaling MediaWiki.",
        "cascadeprotected": "Kaca iki wis direksa saka panyuntingan amerga disertakaké ing {{PLURAL:$1|kaca|kaca-kaca}} ngisor iki sing wis direksa mawa opsi \"runtun\" diaktifaké:\n$2",
        "namespaceprotected": "Panjenengan ora kagungan idin kanggo nyunting kaca ing bilik nama '''$1'''.",
        "customcssprotected": "Sampéyan ora dililakaké nyunting kaca CSS iki amarga kaisi pangaturan pribadi saka panganggo liya.",
        "yourpasswordagain": "Tik manèh tembung wadiné:",
        "createacct-yourpasswordagain": "Netepaké tembung wadi",
        "createacct-yourpasswordagain-ph": "Lebokaké manèh tembung wadiné",
-       "remembermypassword": "Émut tembung sandi kula (salebeting $1 {{PLURAL:$1|dinten|dinten}})",
        "userlogin-remembermypassword": "Gawé amrih aku panggah kalebu",
        "userlogin-signwithsecure": "Nganggo koneksi aman",
        "cannotloginnow-title": "Ora bisa mlebu saiki",
        "cannotchangeemail": "Alamat layang èlèktronik akun ora bisa diganti nèng wiki iki.",
        "emaildisabled": "Situs iki ora bisa ngirim layang èlèktronik.",
        "accountcreated": "Akun wis kagawé",
-       "accountcreatedtext": "Akun panganggo kanggo  [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|wicara]]) wis digawé.",
+       "accountcreatedtext": "Akun panganggo [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|rembug]]) wis digawé.",
        "createaccount-title": "Gawé rékening kanggo {{SITENAME}}",
        "createaccount-text": "Ana wong sing nggawé sawijining akun utawa rékening kanggo alamat e-mail panjenengan ing {{SITENAME}} ($4) mawa jeneng \"$2\" lan tembung sandi \"$3\". Panjenengan disaranaké kanggo mlebu log lan ngganti tembung sandi panjenengan saiki.\n\nPanjenengan bisa nglirwakaké pesen iki yèn akun utawa rékening iki digawé déné sawijining kaluputan.",
        "login-throttled": "Panjenengan wis kakèhan njajal mlebu log.\nTulung nunggu dhisik $1 sadurungé njajal manèh.",
        "passwordreset-emailtext-user": "Panganggo $1 seka {{SITENAME}} njaluk ganti tembung sandhiné Sampéyan ana ing {{SITENAME}} ($4). {{PLURAL:$3|Rèkèning|Rèkèning-rèkèning}} ngisor iki magepokan karo padunungané layang èlèktronik iki:\n\n$2\n\n{{PLURAL:$3|Tembung sandhi sawetara iki}} bakal kedaluwarsa ing {{PLURAL:$5|sak dina|$5 dina}}.\nSampéyan kudu mlebu log lan milih siji tembung sandhi anyar saiki. Yèn wong liya sing njaluk iki, utawa yèn Sampéyan jebul wis kèlingan tembung sandhiné sing lawas saéngga ora ana niyat kanggo ngganti, Sampéyan bisa ngejaraké wara-wara iki lan bacutaké nganggo tembung sandhiné lawas Sampéyan.",
        "passwordreset-emailelement": "Jeneng panganggo: \n$1\n\nTembung wadi sauntara: \n$2",
        "passwordreset-emailsentemail": "Yèn layang èlèktronik iki nggayut akuning sampéyan, layang kanggo salin tembung wadi bakal dikirim.",
-       "passwordreset-emailsent-capture": "Layang èlèktronik kanggo mbalèkaké tembung sandhi wis dikirim, bisa didelok ngisor iki.",
-       "passwordreset-emailerror-capture": "Layang èlèktronik pangèling tembung sandhi wis digawe, yaiku sing ditampilaké nèng ngisor iki, nanging ora kasil dikirim ing {{GENDER:$2|panganggo}}: $1",
        "changeemail": "Owah utawa busak alamat layang èlèktronik",
        "changeemail-header": "Ganti alamat layang èlèktronik akun",
        "changeemail-no-info": "Sampéyan kudu mlebu log kanggo ngaksès kaca iki langsung.",
        "minoredit": "Iki besutan cilik",
        "watchthis": "Awasi kaca iki",
        "savearticle": "Simpen kaca",
+       "savechanges": "Simpen owahan",
+       "publishpage": "Babar kaca",
+       "publishchanges": "Babar owahan",
        "preview": "Pratuduh",
        "showpreview": "Deleng pratuduh",
        "showdiff": "Tuduhaké owahan",
        "anoneditwarning": "<strong>Penget:</strong> Panjenengan boten mlebet log. Alamat IP Panjenengan badhe katingal dening publik manawi Panjenengan ngayahi ewah-ewahan. Manawi Panjenengan  <strong>[$1 mlebet log]</strong> utawai <strong>[$2 damel akun]</strong>, suntingan Panjenengan badhe kaatribusekaken dhumateng  nama pangangge Panjenengan, lan rupi-rupi  kauntungan sanesipun.",
        "anonpreviewwarning": "''Sampéyan durung mlebu log. Nyimpen bakal nyathet alamat IP Sampéyan nèng riwayat sunting kaca iki.''",
        "missingsummary": "'''Pènget:''' Panjenengan ora nglebokaké ringkesan panyuntingan. Menawa panjenengan mencèt tombol Simpen manèh, suntingan panjenengan bakal kasimpen tanpa ringkesan panyuntingan.",
+       "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 \"{{int:savearticle}}\" manèh, kaca alihan bakal digawé.",
        "missingcommenttext": "Mangga isi tanggapan ing ngisor iki.",
        "missingcommentheader": "'''Pangéling:''' Sampéyan durung nyadhiyakaké judhul/jejer kanggo tanggepan iki.\nYèn Sampéyan klik \"{{int:savearticle}}\" manèh, suntingan Sampéyan bakal kasimpen tanpa kuwi.",
        "summary-preview": "Pratuduh tingkesan:",
        "userjsyoucanpreview": "'''Tips:''' Gunakna tombol \"{{int:showpreview}}\" kanggo ngetès JavaScript anyar panjenengan sadurungé disimpen.",
        "usercsspreview": "'''Pèngeten yèn panjenengan namung mirsani pratilik CSS panjenengan.''''\n'''Pratilik iku durung kasimpen!'''",
        "userjspreview": "'''Pèngeten yèn sing panjenengan pirsani namung pratilik JavaScript panjenengan, lan menawa pratilik iku dèrèng kasimpen!'''",
-       "sitecsspreview": "'''Èling yèn Sampéyan mung ndelok pratayang CSS iki.'''\n'''Iki durung disimpen!'''",
-       "sitejspreview": "'''Èling yèn Sampéyan mung ndelok pratayang kodhé JavaScript iki.'''\n'''Iki durung disimpen!'''",
+       "sitecsspreview": "<strong>Élinga yèn Sampéyan mung mratuduh CSS iki.\nIki durung kasimpen!</strong>",
+       "sitejspreview": "<strong>Élinga yèn Sampéyan mung mratuduh kodhé JavaScript iki.\nIki durung kasimpen!</strong>",
        "userinvalidcssjstitle": "'''Pènget:''' Kulit \"$1\" ora ditemokaké. Muga dipèngeti yèn kaca .css lan .js nggunakaké huruf cilik, conto {{ns:user}}:Foo/vector.css lan dudu {{ns:user}}:Foo/Vector.css.",
        "updated": "(Kaanyaran)",
        "note": "<strong>Cathetan:</strong>",
-       "previewnote": "'''Èling yèn Sampéyan mung ndelok pratayang.'''\nOwahan Sampéyan durung kasimpen!",
+       "previewnote": "<strong>Élinga yèn iki mung pratuduh.</strong>\nOwahanmu durung kasimpen!",
        "continue-editing": "Menyang pambesutan",
        "previewconflict": "Pratilik iki nuduhaké tèks ing bagian dhuwur kothak suntingan tèks kayadéné bakal katon yèn panjenengan bakal simpen.",
        "session_fail_preview": "'''Nuwun sèwu, suntingan panjenengan ora bisa diolah amarga dhata sèsi kabusak.\nCoba kirim dhata manèh. Yèn tetep ora bisa, coba log metua lan mlebu log manèh.''''''Amerga wiki iki marengaké panggunan kodhe HTML mentah, mula pratilik didhelikaké minangka pancegahan marang serangan JavaScript.'''\n'''Menawa iki sawijining usaha panyuntingan sing sah, mangga dicoba manèh.\nYèn isih tetep ora kasil, cobanen metu log lan mlebu manèh.'''",
        "parser-unstrip-loop-warning": "Unstrip loop detected",
        "parser-unstrip-recursion-limit": "Unstrip recursion limit exceeded ($1)",
        "converter-manual-rule-error": "Kasalahan kadètèk nèng aturan pangubahan basa manual",
-       "undo-success": "Suntingan iki bisa dibatalaké. Tulung priksa prabandhingan ing ngisor iki kanggo mesthèkaké yèn prakara iki pancèn sing bener panjenengan pèngin lakoni, banjur simpenen pangowahan iku kanggo ngrampungaké pambatalan suntingan.",
+       "undo-success": "Besutan iki kena diwurungaké.\nTiliki bandhingan ngisor iki saperlu mesthèkaké yèn bener iki sing arep kolakoni, nuli simpen owahan ngisor iki kanggo ngiyai yèn besutané diwurungaké.",
        "undo-failure": "Suntingan iki ora bisa dibatalakén amerga ana konflik panyuntingan antara.",
-       "undo-norev": "Suntingan iki ora bisa dibatalaké amerga ora ana utawa wis dibusak.",
-       "undo-summary": "Balèkaké owahan $1 déning [[Special:Contributions/$2|$2]] ([[User talk:$2|rembugan]])",
+       "undo-norev": "Besutan iki ora bisa diwurungaké amarga wis ora ana utawa wis dibusak.",
+       "undo-summary": "Mbalèkaké owahan $1 déning [[Special:Contributions/$2|$2]] ([[User talk:$2|rembugan]])",
        "undo-summary-username-hidden": "Batalna revisi $1 saking panganggo kang didhelikake",
-       "cantcreateaccounttitle": "Akun ora bisa digawé",
        "cantcreateaccount-text": "Saka alamat IP iki ('''$1''') ora diparengaké nggawé akun utawa rékening. Sing mblokir utawa ora marengaké iku [[User:$3|$3]].\n\nAlesané miturut $3 yaiku ''$2''",
        "cantcreateaccount-range-text": "Nggawe akun saking alamat IP \"$1\", sing termasuk IP sampeyan (<strong>$4</strong>), sampun diblokir kaliyan [[User:$3|$3]].\n\nAlesan pamblokiran yaiku \"$2\"",
        "viewpagelogs": "Deleng cathetaning kaca iki",
        "last": "sadurung",
        "page_first": "kapisan",
        "page_last": "pungkasan",
-       "histlegend": "Pilihen rong tombol radhio banjur pencèten tombol ''bandhingna'' kanggo mbandhingaké versi. Klik sawijining tanggal kanggo ndeleng versi kaca ing tanggal iku.<br />(skr) = prabédan karo vèrsi saiki, (akir) = prabédan karo vèrsi sadurungé, '''s''' = suntingan sithik, '''b''' = suntingan bot, → = suntingan bagian, ← = ringkesan otomatis",
+       "histlegend": "Kanggo mbandhingaké: tandhani kothak radhio révisi-révisi sing arep dibandhingaké lan pencèt ''Enter'' utawa tombol sing ana ing ngisor.<br />\nLegéndha: <strong>({{int:cur}})</strong> = béda karo révisi pungkasan, <strong>({{int:last}})</strong> = béda karo révisi sadurungé, <strong>{{int:minoreditletter}}</strong> = besutan cilik.",
        "history-fieldset-title": "Luru sujarah",
-       "history-show-deleted": "Namung sing dibusak",
-       "histfirst": "suwé dhéwé",
+       "history-show-deleted": "Mligi sing dibusak",
+       "histfirst": "lawas dhéwé",
        "histlast": "anyar dhéwé",
        "historysize": "($1 {{PLURAL:$1|bét|bét}})",
        "historyempty": "(suwung)",
        "rev-delundel": "Owah kasatmatan",
        "rev-showdeleted": "tuduhaké",
        "revisiondelete": "Busak/batal busak revisi",
-       "revdelete-nooldid-title": "Target revisi ora ditemokaké",
+       "revdelete-nooldid-title": "Rèvisi tujuan ora sah",
        "revdelete-nooldid-text": "Panjenengan durung mènèhi target revisi kanggo nglakoni fungsi iki.",
        "revdelete-no-file": "Berkas sing dituju ora ana.",
        "revdelete-show-file-confirm": "Apa panjenengan yakin arep mirsani révisi sing wis kabusak saka berkas \"<nowiki>$1</nowiki>\" ing $2, jam $3?",
        "mergehistory-fail": "Ora bisa nggabung sajarah, coba dipriksa manèh kacané lan paramèter wektuné.",
        "mergehistory-no-source": "Kaca sumber $1 ora ana.",
        "mergehistory-no-destination": "Kaca paran $1 ora ana.",
-       "mergehistory-invalid-source": "Irah-irahan kaca sumber kudu irah-irahan utawa judhul sing bener.",
-       "mergehistory-invalid-destination": "Irah-irahan kaca tujuan kudu irah-irahan utawa judhul sing bener.",
+       "mergehistory-invalid-source": "Kaca sumber kudu asesirah sing sah.",
+       "mergehistory-invalid-destination": "Kaca tujuan kudu asesirah sing sah.",
        "mergehistory-autocomment": "Nggabung [[:$1]] menyang [[:$2]]",
        "mergehistory-comment": "Nggabung [[:$1]] menyang [[:$2]]: $3",
        "mergehistory-same-destination": "Jeneng kaca sumber lan tujuan ora kena padha",
        "lineno": "Larik $1:",
        "compareselectedversions": "Bandhingna vèrsi kapilih",
        "showhideselectedversions": "Tampilaké/dhelikaké révisi kapilih",
-       "editundo": "wurungaké",
+       "editundo": "wurung",
        "diff-empty": "(Ora ana bedane)",
        "diff-multi-sameuser": "({{PLURAL:$1|Saowahan madya|$1 owahan madya}} déning panganggo sing padha ora dituduhaké)",
        "diff-multi-manyusers": "({{PLURAL:$1Siji rèvisi sedhengan|$1 rèvisi sedhengan}} déning luwih saka $2 {{PLURAL:$2|panganggo|panganggo}} ora dituduhaké)",
        "difference-missing-revision": "{{PLURAL:$2|Sak pambenahan|$2 pambenahan}} saka prabédan iki ($1) {{PLURAL:$2|ora ditemokaké|ora ditemokaké}}.\n\nIki biasané kasebab pranala prabedan sing wis ora kanggo saka kaca isi wis dibusak.\nRinciané bisa ditemokaké nèng [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} log busak].",
        "searchresults": "Kasiling golèk",
        "searchresults-title": "Kasiling golèk \"$1\"",
-       "titlematches": "Irah-irahan artikel sing cocog",
+       "titlematches": "Sesirah kaca cocog",
        "textmatches": "Tèks artikel sing cocog",
        "notextmatches": "Ora ana tèks kaca sing cocog",
        "prevn": "{{PLURAL:$1|$1}} sadurungé",
        "searchmenu-exists": "'''Ana kaca kanthi jeneng \"[[$1]]\" ing wiki iki'''",
        "searchmenu-new": "<strong>Gawéa kaca \"[[:$1]]\" nyang wiki iki!</strong> {{PLURAL:$2|0=|Uga delenga kaca sing katemu sarana panggolèking sampéyan.|Uga delenga kasiling panggolèk.}}",
        "searchprofile-articles": "Kaca isi",
-       "searchprofile-images": "Sarwamadya",
+       "searchprofile-images": "Multimédhia",
        "searchprofile-everything": "Samubarang",
        "searchprofile-advanced": "Lungidan",
        "searchprofile-articles-tooltip": "Golèkan ing $1",
        "searchrelated": "magepokan",
        "searchall": "kabèh",
        "showingresults": "Ing ngisor iki dituduhaké {{PLURAL:$1|'''1''' kasil|'''$1''' kasil}}, wiwitané saking #<strong>$2</strong>.",
+       "showingresultsinrange": "Nuduhaké nganti {{PLURAL:$1|<strong>1</strong> kasil|<strong>$1</strong> kasil}} sajeroning penthangan #<strong>$2</strong> tekan #<strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Asil <strong>$1</strong> dari <strong>$3</strong>|Asil <strong>$1 - $2</strong> saking <strong>$3</strong>}}",
        "search-nonefound": "Ora ana kasil sing cocog karo pitakonan (''query'').",
        "powersearch-legend": "Panggolèkan sabanjuré (''advance search'')",
        "prefs-searchoptions": "Golèk",
        "prefs-namespaces": "Ruang jeneng / Bilik jeneng",
        "default": "baku",
-       "prefs-files": "Berkas",
+       "prefs-files": "Barkas",
        "prefs-custom-css": "CSS priangga",
        "prefs-custom-js": "JavaScript priangga",
        "prefs-common-css-js": "CSS/JS didumaké kanggo kabèh kulit:",
        "yourvariant": "Werna basa isi:",
        "prefs-help-variant": "Varian utawa ortograpi sing Sampéyan pilih kanggo nampilaké kaca kontèn saka wiki iki.",
        "yournick": "Asma sesinglon/samaran (kagem tapak asta):",
-       "prefs-help-signature": "Komentar ing kaca wicara kudu ditapak astani nganggo \"<nowiki>~~~~</nowiki>\" sing bakal dikonvèrsi dadi tapak asta panjenengan lan tanggal wektu.",
+       "prefs-help-signature": "Tanggapan ing kaca parembugan kudu ditandhatangani mawa \"<nowiki>~~~~</nowiki>\", sing bakal salin dadi tandha tangan lan cap wektumu.",
        "badsig": "Tapak astanipun klèntu; cèk rambu HTML.",
        "badsiglength": "Tapak asta panjenengan kedawan.\nAja luwih saka {{PLURAL:$1|karakter|karakter}}.",
        "yourgender": "Kepiyé sampéyan medhar priangganing sampéyan?",
        "prefs-timeoffset": "Format wektu",
        "prefs-advancedediting": "Pilihan sabanjuré",
        "prefs-editor": "Wong besut",
-       "prefs-preview": "Pratayang",
+       "prefs-preview": "Pratuduh",
        "prefs-advancedrc": "Opsi lanjutan",
        "prefs-advancedrendering": "Opsi lanjutan",
        "prefs-advancedsearchoptions": "Opsi lanjutan",
        "prefs-tokenwatchlist": "Token",
        "prefs-diffs": "Prabédan",
        "prefs-help-prefershttps": "Pamiji iki bakal lumaku mentas sampeyan mbalèni mlebu.",
-       "prefs-tabs-navigation-hint": "Tip: Sampeyan isa nganggo dapat menggunakan tombol panah kiwa lan tengen kanggo navigasi tab-tab ing daftar tab.",
+       "prefs-tabs-navigation-hint": "Saran: Sampeyan bisa nganggo tombol jemparing kiwa lan tengen saperlu navigasi tab-tab ing pratélan tab.",
        "userrights": "Manajemen hak panganggo",
        "userrights-lookup-user": "Ngatur kelompok panganggo",
        "userrights-user-editname": "Isi jeneng panganggo:",
        "right-move-rootuserpages": "Ngalih kaca panganggo oyod",
        "right-movefile": "Mindhah berkas",
        "right-suppressredirect": "Aja nggawé pangalihan saka kaca sing lawas yèn mindhah sawijining kaca",
-       "right-upload": "Ngunggahaké berkas-berkas",
+       "right-upload": "Unggah barkas",
        "right-reupload": "Tindhihana sawijining berkas sing wis ana",
        "right-reupload-own": "Nimpa sawijining berkas sing wis ana lan diunggahaké déning panganggo sing padha",
        "right-reupload-shared": "Timpanana berkas-berkas ing khazanah binagi sacara lokal",
        "right-markbotedits": "Tandhani besutan kawurungan minangka besutan bot",
        "right-noratelimit": "Ora dipengaruhi déning wates cacahing suntingan.",
        "right-import": "Impor kaca-kaca saka wiki liya",
-       "right-importupload": "Impor kaca-kaca saka sawijining pangunggahan berkas",
+       "right-importupload": "Impor kaca saka unggahan barkas",
        "right-patrol": "Tandhanana suntingan minangka wis dipatroli",
        "right-autopatrol": "Gawé supaya suntingan-suntingan ditandhani minangka wis dipatroli",
        "right-patrolmarks": "Ndeleng tandha-tandha patroli owah-owahan anyar",
        "grant-createaccount": "Gawé akun",
        "grant-createeditmovepage": "Gawé, besut, lan lih kaca",
        "grant-delete": "Busak kaca, owahan, lan isian cathetan",
-       "newuserlogpage": "Cathetan panganggo anyar",
+       "newuserlogpage": "Log naraguna anyar",
        "newuserlogpagetext": "Ing ngisor iki kapacak log pandaftaran panganggo anyar.",
-       "rightslog": "Log pangowahan hak aksès",
+       "rightslog": "Log hak panganggo",
        "rightslogtext": "Ing ngisor iki kapacak log pangowahan marang hak-hak panganggo.",
        "action-read": "maca kaca iki",
        "action-edit": "besut kaca iki",
        "action-createpage": "nggawé kaca-kaca",
-       "action-createtalk": "gawé kaca wicara anyar",
+       "action-createtalk": "gawé kaca parembugan iki",
        "action-createaccount": "gawé akun panganggo iki",
        "action-minoredit": "tandhani iki minangka besutan cilik",
        "action-move": "alihna kaca iki",
        "rcshowhidemine": "$1 besutanku",
        "rcshowhidemine-show": "Tuduhaké",
        "rcshowhidemine-hide": "Dhelikaké",
+       "rcshowhidecategorization": "$1 kategorisasi kaca",
        "rcshowhidecategorization-show": "Tuduhaké",
        "rcshowhidecategorization-hide": "Dhelikaké",
        "rclinks": "Tuduhaké $1 owahan kawit $2 dina kapungkur.<br />$3",
        "rc_categories": "Watesana nganti kategori (dipisah karo \"|\")",
        "rc_categories_any": "Apa waé",
        "rc-change-size-new": "$1 {{PLURAL:$1|bét|bét}} sabubaré diowah",
-       "newsectionsummary": "/* $1 */ bagéyan anyar",
+       "newsectionsummary": "/* $1 */ pérangan anyar",
        "rc-enhanced-expand": "Tuduhaké princèn",
        "rc-enhanced-hide": "Dhelikaké princèn",
        "rc-old-title": "kawitané digawé minangka \"$1\"",
        "uploadnologin": "Durung mlebu log",
        "uploadnologintext": "Sampéyan kudu $1 supaya bisa ngunggah berkas.",
        "upload_directory_missing": "Direktori pamunggahan ($1) ora ditemokaké lan ora bisa digawé déning server wèb.",
-       "upload_directory_read_only": "Dirèktori pangunggahan ($1) ora bisa ditulis déning server wèb.",
+       "upload_directory_read_only": "Dhirèktori pangunggahan ($1) ora bisa ditulis déning paladèn jaringan.",
        "uploaderror": "Kaluputan pangunggahan berkas",
        "upload-recreate-warning": "'''Pèngetan: Berkas mawa jeneng kuwi wis dibusak utawa disingkiraké.'''\n\nLog pambusakan lan panyingkiran saka kaca iki sumadhiya nèng kéné:",
        "uploadtext": "Anggé formulir ing ngandhap punika kanggé nginggahaké gambar.\nKanggé mirsani utawi madosi gambar ingkang sampun dipununggah sakdèrèngipun pigunakaken [[Special:FileList|dhaftar berkas sing wis diunggah]], gambar ingkang dipununggah ulang ugi kadhaftar ing [[Special:Log/upload|log pangunggahan]], pambusakan ing [[Special:Log/delete|Log pambusakan]].\n\nKanggé nyertakaken gambar ing satunggiling kaca, pigunakaken pranala salah setunggal saking format ing ngandhap punika:\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Berkas.jpg]]</nowiki></code>''' kanggé migunakaken versi pepak gambar\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Berkas.png|200px|thumb|left|tèks alt]]</nowiki></code>''' kanggé migunakaken gambar wiyaripun 200 piksel ing kothak ing sisih kiwa kanthi 'tèks alt' minangka panjelasan\n* '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:Berkas.ogg]]</nowiki></code>''' kanggé nggandhèng langsung dhumateng gambar tanpi nampilaké gambar",
        "upload-permitted": "Jenis berkas sing diidinaké: $1.",
        "upload-preferred": "Jenis berkas sing disaranaké: $1.",
        "upload-prohibited": "Jenis berkas sing dilarang: $1.",
-       "uploadlogpage": "Log pangunggahan",
+       "uploadlogpage": "Log unggah",
        "uploadlogpagetext": "Ing ngisor iki kapacak log pangunggahan berkas sing anyar dhéwé.\nMangga mirsani [[Special:NewFiles|galeri berkas-berkas anyar]] kanggo pratélan visual.",
-       "filename": "Jeneng berkas",
+       "filename": "Jeneng barkas",
        "filedesc": "Tingkesan",
        "fileuploadsummary": "Ringkesan:",
        "filereuploadsummary": "Owah-owahan berkas:",
        "file-deleted-duplicate": "Sawijining berkas persis berkas iki ([[:$1]]) wis tau dibusak. Mangga panjenengan priksani sajarah pambusakan berkas kasebut sadurungé nerusaké ngunggahaké berkas kuwi manèh.",
        "uploadwarning": "Pèngetan pangunggahan berkas",
        "uploadwarning-text": "Mangga owah katrangan berkas nèng ngisor lan coba manèh.",
-       "savefile": "Simpen berkas",
+       "savefile": "Simpen barkas",
        "uploaddisabled": "Nuwun sèwu, fasilitas pangunggahan dipatèni.",
        "copyuploaddisabled": "Ngunggah mawa URL dipatèni.",
        "uploaddisabledtext": "Pangunggahan berkas ora diidinaké.",
        "uploadscripted": "Berkas iki ngandhut HTML utawa kode sing bisa diinterpretasi salah déning panjlajah wèb.",
        "uploadvirus": "Berkas iki ngamot virus! Détil: $1",
        "uploadjava": "Berkas kuwi berkas ZIP sing kaisi berkas .class Java.\nNgungga berkas Java ora dililakaké amarga bisa nyebabaké ngluwèhaké wates kamanan.",
-       "upload-source": "Berkas sumber",
+       "upload-source": "Barkas sumber",
        "sourcefilename": "Jeneng berkas sumber:",
        "sourceurl": "URL sumber:",
        "destfilename": "Jeneng berkas sing dituju",
        "img-auth-accessdenied": "Aksès ditulak",
        "img-auth-nopathinfo": "Kélangan PATH_INFO.\nSasana Sampéyan durung disetèl kanggo ngliwati inpormasi iki.\nMungkin amarga abasis-CGI lan ora bisa nyengkuyung img_auth.\nDelok https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "img-auth-notindir": "Alur sing dijaluk dudu dirèktori unggah kakonpigurasi.",
-       "img-auth-badtitle": "Ora bisa mbangun judhul sah saka \"$1\".",
+       "img-auth-badtitle": "Ora bisa ngyasa sesirah sing sah saka \"$1\".",
        "img-auth-nologinnWL": "Sampéyan durung mlebu log lan \"$1\" ora nèng daptar putih.",
        "img-auth-nofile": "Berkas \"$1\" ora ana.",
        "img-auth-isdir": "Sampéyan lagi njajal ngaksès dirèktori \"$1\".\nNamung aksès berkas sing dililakaké.",
        "license": "Jenis lisènsi:",
        "license-header": "Pamalilah",
        "nolicense": "Durung ana sing dipilih",
-       "license-nopreview": "(Pratayang ora sumedya)",
+       "license-nopreview": "(Pratuduh ora sumadhiya)",
        "upload_source_url": " (sawijining URL absah sing bisa diaksès publik)",
        "upload_source_file": " (sawijining berkas ing komputeré panjenengan)",
        "listfiles-summary": "Kaca mirunggan iki nuduhaké kabèh barkas sing kaunggah.",
        "listfiles_thumb": "Gambar mini",
        "listfiles_date": "Tanggal",
        "listfiles_name": "Jeneng",
-       "listfiles_user": "Panganggo",
+       "listfiles_user": "Naraguna",
        "listfiles_size": "Ukuran (bita)",
        "listfiles_description": "Dèskripsi",
        "listfiles_count": "Vèrsi",
        "filehist-thumb": "Gambar cilik",
        "filehist-thumbtext": "Gambar cilik kanggo owahan $1",
        "filehist-nothumb": "Ora ana miniatur",
-       "filehist-user": "Panganggo",
+       "filehist-user": "Naraguna",
        "filehist-dimensions": "Alang ujur",
        "filehist-filesize": "Gedhené berkas",
        "filehist-comment": "Tanggapan",
        "statistics-header-hooks": "Statistik liya",
        "statistics-articles": "Kaca-kaca isi",
        "statistics-pages": "Gunggung kaca",
-       "statistics-pages-desc": "Kabèh kaca ing wiki iki, klebu kaca wicara, pangalihan, lan liya-liyané.",
+       "statistics-pages-desc": "Kabèh kaca ing wiki iki, kalebu kaca parembugan, alihan, lsp.",
        "statistics-files": "Berkas sing diunggahaké",
        "statistics-edits": "Gunggung suntingan wiwit {{SITENAME}} diwiwiti",
        "statistics-edits-average": "Rata-rata suntingan saben kaca",
        "brokenredirectstext": "Pengalihan ing ngisor iki tumuju menyang kaca sing ora ana:",
        "brokenredirects-edit": "besut",
        "brokenredirects-delete": "busak",
-       "withoutinterwiki": "Kaca tanpa pranala antarbasa",
-       "withoutinterwiki-summary": "Kaca-kaca iki ora nduwé pranala menyang vèrsi ing  basa liyané:",
+       "withoutinterwiki": "Kaca tanpa pranala basa",
+       "withoutinterwiki-summary": "Kaca-kaca ing ngisor iki ora nggayut nyang vèrsi basa liyané.",
        "withoutinterwiki-legend": "Préfiks",
        "withoutinterwiki-submit": "Tuduhna",
        "fewestrevisions": "Artikel mawa owah-owahan sithik dhéwé",
        "unusedimages": "Berkas sing ora dienggo",
        "wantedcategories": "Kategori sing diperlokaké",
        "wantedpages": "Kaca sing dipèngini",
-       "wantedpages-badtitle": "Judhul ora valid ing sèt asil: $1",
+       "wantedpages-badtitle": "Sesirah ora sah ing omboyakan kasil: $1",
        "wantedfiles": "Berkas sing diperlokaké",
        "wantedfiletext-cat": "Berkas iki dianggo nanging ora ana. Berkas saka panyimpenan asing mungkin kadaptar tinimbang ana kasunyatan. Saben ''positip salah'' bakal <del>diorèk</del>. Lan, kaca sing nyartakaké berkas sing ora ana bakal kadaptar nèng [[:$1]].",
        "wantedfiletext-nocat": "Berkas iki dianggo nanging ora ana. Berkas saka panyimpenan asing mungkin kadaptar tinimbang ana kasunyatan. Saben ''positip salah'' bakal <del>diorèk</del>.",
        "mostlinkedtemplates": "Kaca paling akèh transklusi",
        "mostcategories": "Kaca sing kategoriné akèh dhéwé",
        "mostimages": "Berkas sing kerep dhéwé dienggo",
-       "mostinterwikis": "Halaman dengan interwiki terbanyak",
+       "mostinterwikis": "Kaca mawa interwiki paling akèh",
        "mostrevisions": "Kaca mawa pangowahan sing akèh dhéwé",
        "prefixindex": "Kabèh kaca mawa ater-ater",
        "prefixindex-namespace": "Kabèh kaca mawa ater-ater (bilik jeneng $1)",
        "shortpages": "Kaca cendhak",
        "longpages": "Kaca dawa",
        "deadendpages": "Kaca-kaca buntu (tanpa pranala)",
-       "deadendpagestext": "kaca-kaca iki ora nduwé pranala tekan ngendi waé ing wiki iki..",
+       "deadendpagestext": "Kaca-kaca ing ngisor iki ora nggayut nyang kaca liya ing {{SITENAME}}.",
        "protectedpages": "Kaca sing direksa",
        "protectedpages-indef": "Namung pangreksan ora langgeng waé",
        "protectedpages-cascade": "Amung kaca rineksan kang runtut",
        "protectedpages-page": "Kaca",
        "protectedpages-expiry": "Kadaluwarsa",
        "protectedpages-reason": "Alesan",
-       "protectedtitles": "Irah-irahan sing direksa",
-       "protectedtitlesempty": "Ora ana irah-irahan utawa judhul sing direksa karo paramèter-paramèter iki.",
+       "protectedtitles": "Sesirah direksa",
+       "protectedtitlesempty": "Ora ana sesirah sing saiki kareksa mawa paramèter iki.",
        "listusers": "Daftar panganggo",
        "listusers-editsonly": "Tampilaké mung panganggo sing nduwèni kontribusi",
        "listusers-creationsort": "Urut miturut tanggal digawé",
        "usercreated": "{{GENDER:$3|Digawé}} $1 wanci $2",
        "newpages": "Kaca anyar",
        "newpages-username": "Asma panganggo:",
-       "ancientpages": "Kaca-kaca langkung sepuh",
+       "ancientpages": "Kaca paling lawas",
        "move": "Pindhahen",
        "movethispage": "Lih kaca iki",
        "unusedimagestext": "Berkas-berkas sing kapacak iki ana nanging ora dienggo ing kaca apa waé.\nTulung digatèkaké yèn situs wèb liyané mbok-menawa bisa nyambung ing sawijining berkas sacara langsung mawa URL langsung, lan berkas-berkas kaya mengkéné iku mbok-menawa ana ing daftar iki senadyan ora dienggo aktif manèh.",
        "nopagetitle": "Kaca tujuan ora ditemokaké",
        "nopagetext": "Kaca sing panjenengan tuju ora ditemokaké.",
        "pager-newer-n": "{{PLURAL:$1|1 luwih anyar|$1 luwih anyar}}",
-       "pager-older-n": "{{PLURAL:$1|1 sing luwih lawas|$1 sing luwih lawas}}",
+       "pager-older-n": "{{PLURAL:$1|1 luwih lawas|$1 luwih lawas}}",
        "suppress": "Dhelikaké",
        "querypage-disabled": "Kaca kusus iki dipatèni kanggo alesan kinerja.",
        "apisandbox": "Kothak wedhi API",
        "booksources-invalid-isbn": "ISBN sing diwènèhaké katonané ora valid; priksa kasalahan penyalinan saka sumber asli.",
        "specialloguserlabel": "Panampil:",
        "speciallogtitlelabel": "Patujon (judhul utawa panganggo) :",
-       "log": "Cathetan",
+       "log": "Log",
        "all-logs-page": "Kabèh log publik",
        "alllogstext": "Gabungan tampilam kabèh log sing ana ing {{SITENAME}}.\nPanjenengan bisa mbatesi tampilan kanthi milih jinis log, jeneng panganggo (sènsitif aksara gedhé/cilik), utawa kaca sing magepokan (uga sènsitif aksara gedhé/cilik).",
        "logempty": "Ora ditemokaké èntri log sing pas.",
-       "log-title-wildcard": "Golèk irah-irahan utawa judhul sing diawali mawa tèks kasebut",
+       "log-title-wildcard": "Golèk sesirah sing diwiwiti tulisan iki",
        "showhideselectedlogentries": "Tuduhalé/dhelikaké èntri log kapilih",
        "allpages": "Kabèh kaca",
        "nextpage": "Kaca sabanjuré ($1)",
        "categories": "Kategori",
        "categoriespagetext": "{{PLURAL:$1|kategori ing ngisor iki ngandhut|kategori ing ngisor iki ngandhut}} kaca utawa media.\n[[Special:UnusedCategories|Kategori sing ora dianggo]] ora ditampilaké ing kéné.\nDeleng uga [[Special:WantedCategories|kategori sing diperlokaké]].",
        "categoriesfrom": "Tampilaké kategori-kategori diwiwiti saka:",
-       "deletedcontributions": "Sumbanganing panganggo sing dibusak",
+       "deletedcontributions": "Sumbangan panganggo sing dibusak",
        "deletedcontributions-title": "Sumbanganing panganggo sing dibusak",
        "sp-deletedcontributions-contribs": "sumbangan",
        "linksearch": "Golèkan pranala njaba",
        "linksearch-error": "''Wildcards'' namung bisa dienggo ing bagéyan awal saka jeneng host.",
        "listusersfrom": "Tuduhna panganggo sing diawali karo:",
        "listusers-submit": "Tuduhna",
-       "listusers-noresult": "Panganggo ora ditemokaké.",
+       "listusers-noresult": "Naraguna ora ana.",
        "listusers-blocked": "(diblokir)",
        "activeusers": "Dhaptar panganggo aktif",
        "activeusers-intro": "Iki daptar panganggo sing katon lakuné ing $1 {{PLURAL:$1|dina|dina}} kapungkur.",
        "activeusers-from": "Tampilna panganggo wiwit saka:",
        "activeusers-hidebots": "Delikna bot",
        "activeusers-hidesysops": "Delikna pangurus",
-       "activeusers-noresult": "Panganggo ora ditemokaké.",
+       "activeusers-noresult": "Naraguna ora ana.",
        "listgrouprights": "Hak-hak grup panganggo",
        "listgrouprights-summary": "Ing ngisor iki kapacak dhaftar grup panganggo sing didéfinisi ing wiki iki, kanthi hak-hak aksès gandhèngané.\nInformasi tambahan perkara hak-hak individual bisa ditemokaké ing [[{{MediaWiki:Listgrouprights-helppage}}|kéné]].",
        "listgrouprights-key": "* <span class=\"listgrouprights-granted\">Hak sing diidinaké</span>\n* <span class=\"listgrouprights-revoked\">Hak sing dijabel</span>",
        "wlheader-showupdated": "Kaca-kaca sing wis owah wiwit ditiliki panjenengan kaping pungkasan, dituduhaké mawa '''aksara kandel'''",
        "wlnote": "Ngisor iki {{PLURAL:$1|owahan pungkasan|'''$1''' owahan pungkasan}} {{PLURAL:$2|jam|'''$2''' jam}} kapungkur, per $3, $4.",
        "wlshowlast": "Tuduhna $1 jam $2 dina  pungkasan",
+       "wlshowhidecategorization": "kategorisasi kaca",
        "watchlist-options": "Pilihaning pawawangan",
        "watching": "Ngawasi...",
        "unwatching": "Ngilangi pangawasan...",
        "delete-legend": "Busak",
        "historywarning": "'''Pènget''': Kaca sing bakal panjenengan busak ana sajarahé kanthi $1 {{PLURAL:$1|révisi|révisi}}:",
        "confirmdeletetext": "Panjenengan bakal mbusak kaca utawa berkas iki minangka permanèn karo kabèh sajarahé saka basis data. Pastèkna dhisik menawa panjenengan pancèn nggayuh iki, ngerti kabèh akibat lan konsekwènsiné, lan apa sing bakal panjenengan tumindak iku cocog karo [[{{MediaWiki:Policy-url}}|kawicaksanan {{SITENAME}}]].",
-       "actioncomplete": "Proses tuntas",
+       "actioncomplete": "Kasil diayahi",
        "actionfailed": "Tindakan gagal",
-       "deletedtext": "\"$1\" sampun kabusak. Coba pirsani $2 kanggé log paling énggal kaca ingkang kabusak.",
-       "dellogpage": "Cathetan busakan",
+       "deletedtext": "\"$1\" wis dibusak. \nDelenga $2 minangka rekamaning busak-busakan pungkasan.",
+       "dellogpage": "Log busak",
        "dellogpagetext": "Ing ngisor iki kapacak log pambusakan kaca sing anyar dhéwé.",
-       "deletionlog": "Cathetan sing dibusak",
+       "deletionlog": "log busak",
        "reverted": "Dibalèkaké ing revisi sadurungé",
        "deletecomment": "Alesan:",
        "deleteotherreason": "Alesan liya utawa tambahan:",
        "delete-toobig": "Kaca iki ndarbèni sajarah panyuntingan sing dawa, yaiku ngluwihi $1 {{PLURAL:$1|revision|révisi}}.\nPambusakan kaca sing kaya mangkono mau wis ora diparengaké kanggo menggak anané karusakan ing {{SITENAME}}.",
        "delete-warning-toobig": "Kaca iki duwé sajarah panyuntingan sing dawa, luwih saka $1 {{PLURAL:$1|révisi|révisi}}.\nMbusak kaca iki bisa ngrusak operasi basis data ing {{SITENAME}};\nkudu ngati-ati.",
        "deleting-backlinks-warning": "'''Awas:''' Kaca liyane mungkin ana sing nautake ing kaca sing arep sampeyan busak.",
-       "rollback": "Wurungaké besutan",
+       "rollback": "Pulihaké besutan",
        "rollbacklink": "balèkaké",
        "rollbacklinkcount": "balèkaké $1 {{PLURAL:$1|besutan|besutan}}",
        "rollbacklinkcount-morethan": "balèkaké luwih saka $1 {{PLURAL:$1|suntingan|suntingan}}",
        "rollbackfailed": "Pambalèkan gagal dilakoni",
        "cantrollback": "Ora bisa mbalèkaké suntingan; panganggo pungkasan iku siji-sijiné penulis artikel iki.",
-       "alreadyrolled": "Ora bisa mbalèkaké suntingan pungkasan [[:$1]] déning [[User:$2|$2]] ([[User talk:$2|Wicara]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); wong liya wis nyunting utawa mbalèkaké kaca artikel iku.\n\nSuntingan pungkasan dilakoni déning [[User:$3|$3]] ([[User talk:$3|Wicara]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
+       "alreadyrolled": "Ora bisa mulihaké besutan pungkasan [[:$1]] déning [[User:$2|$2]] ([[User talk:$2|rembug]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); ana wong liya sing wis mbesut utawa mulihaké kaca iki.\n\nBesutan pungkasan kaca iku garapané [[User:$3|$3]] ([[User talk:$3|rembug]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Ringkesan suntingan yaiku: <em>$1</em>.",
        "revertpage": "Besutan sing dibalèkaké [[Special:Contributions/$2|$2]] ([[User talk:$2|rembugan]]) bab owahan pungkasan déning [[User:$1|$1]]",
        "revertpage-nouser": "Suntingan déning panganggo sing didhelikake, dibalèkaké nèng benahan pungkasan déning [[User:$1|$1]]",
        "rollback-success": "Suntingan dibalèkaké déning $1;\ndiowahi bali menyang vèrsi pungkasan déning $2.",
        "sessionfailure-title": "Sèsi gagal",
        "sessionfailure": "Katoné ana masalah karo sèsi log panjenengan; log panjenengan wis dibatalaké kanggo nyegah pambajakan. Mangga mencèt tombol \"back\" lan unggahaké manèh kaca sadurungé mlebu log, lan coba manèh.",
-       "protectlogpage": "Cathetan pangreksan",
+       "protectlogpage": "Log reksa",
        "protectlogtext": "Ngisor iki daptar owahan saka panjagan kaca.\nDelok [[Special:ProtectedPages|daptar kaca sing dijaga]] kanggo daptar panjagan kaca paling anyar.",
        "protectedarticle": "ngreksa \"[[$1]]\"",
        "modifiedarticleprotection": "ngowahi tingkat pangreksan \"[[$1]]\"",
        "protect-otherreason-op": "Alesan liya",
        "protect-dropdown": "*Alesan umum pangreksan\n** Vandalisme makaping-kaping\n** Spam makaping-kaping\n** Perang suntingan\n** Kaca kerep disunting",
        "protect-edit-reasonlist": "Mbesut jalaraning pangreksa",
-       "protect-expiry-options": "1 jam:1 hour,1 dina:1 day,1 minggu:1 week,2 minggu:2 weeks,1 sasi:1 month,3 sasi:3 months,6 sasi:6 months,1 taun:1 year,tanpa wates:infinite",
+       "protect-expiry-options": "1 jam:1 hour,1 dina:1 day,1 minggu:1 week,2 minggu:2 weeks,1 wulan:1 month,3 wulan:3 months,6 wulan:6 months,1 taun:1 year,tanpa wates:infinite",
        "restriction-type": "Pangreksan:",
        "restriction-level": "Tingkatan pambatesan:",
        "minimum-size": "Ukuran minimum",
        "tooltip-namespace_association": "Centhang kothak iki kanggo nglebokaké uga bilik jeneng gumenan utawa subyèk sing kakait karo bilik jeneng kapilih",
        "blanknamespace": "(Pokok)",
        "contributions": "Sumbangan {{GENDER:$1|panganggo}}",
-       "contributions-title": "Sumbanganing panganggo $1",
+       "contributions-title": "Sumbangan panganggo $1",
        "mycontris": "Sumbangan",
        "anoncontribs": "Sumbangan",
        "contribsub2": "Kanggo {{GENDER:$3|$1}} ($2)",
        "nocontribs": "Ora ditemokaké owah-owahan sing cocog karo kritéria kasebut iku.",
        "uctop": "(saiki)",
-       "month": "Wiwit wulan (lan sadurungé):",
+       "month": "Saka wulan (lan sadurungé):",
        "year": "Wiwit taun (lan sadurungé):",
        "sp-contributions-newbies": "Namung panganggo-panganggo anyar",
        "sp-contributions-newbies-sub": "Kanggo panganggo anyar",
        "sp-contributions-deleted": "sumbanganing panganggo sing dibusak",
        "sp-contributions-uploads": "unggahan",
        "sp-contributions-logs": "log",
-       "sp-contributions-talk": "wicara",
+       "sp-contributions-talk": "rembug",
        "sp-contributions-userrights": "pengaturan hak panganggo",
        "sp-contributions-blocked-notice": "Panganggo iki lagi diblokir.\nÈntri log blokiran pungkasan sumadhiya nèng ngisor kanggo rujukan:",
        "sp-contributions-blocked-notice-anon": "Alamat IP iki lagi diblokir.\nÈntri log blokiran pungkasan sumadhiya nèng ngisor kanggo rujukan:",
        "sp-contributions-search": "Golèk sumbangan",
        "sp-contributions-username": "Alamat IP utawa jeneng panganggo:",
-       "sp-contributions-toponly": "Tuduhaké was suntingan saka benahan pungkasan",
+       "sp-contributions-toponly": "Tuduhaké besutan mligi rèvisi anyar",
+       "sp-contributions-newonly": "Tuduhaké besutan mligi kaca gawéan",
        "sp-contributions-submit": "Golèk",
        "whatlinkshere": "Sing nggayut mréné",
        "whatlinkshere-title": "Kaca mawa pranala nggayut \"$1\"",
        "whatlinkshere-page": "Kaca:",
-       "linkshere": "Kaca-kaca iki nduwé pranala menyang '''[[:$1]]''':",
+       "linkshere": "Kaca-kaca ing ngisor iki nggayut nyang '''[[:$1]]''':",
        "nolinkshere": "Ora ana kaca sing nduwé pranala menyang '''[[:$1]]'''.",
        "nolinkshere-ns": " Ora ana kaca sing nduwé pranala menyang '''[[:$1]]''' ing bilik jeneng sing kapilih.",
        "isredirect": "kaca lih-lihan",
        "ipbenableautoblock": "Blokir alamat IP pungkasan sing dienggo déning pengguna iki sacara otomatis, lan kabèh alamat sabanjuré sing dicoba arep dienggo nyunting.",
        "ipbsubmit": "Kirimna",
        "ipbother": "Wektu liya",
-       "ipboptions": "2 jam:2 hours,1 dina:1 day,3 dina:3 days,1 minggu:1 week,2 minggu:2 weeks,1 sasi:1 month,3 sasi:3 months,6 sasi:6 months,1 taun:1 year,tanpa wates:infinite",
+       "ipboptions": "2 jam:2 hours,1 dina:1 day,3 dina:3 days,1 minggu:1 week,2 minggu:2 weeks,1 wulan:1 month,3 wulan:3 months,6 wulan:6 months,1 taun:1 year,tanpa wates:infinite",
        "ipbhidename": "Delikna jeneng panganggo saka suntingan lan pratélan",
        "ipbwatchuser": "Wasi kaca panganggoning lan kaca gegunemaning panganggo iki",
        "ipb-disableusertalk": "Alangi panganggo iki nyunting kaca gunemané nalika diblokir",
        "blocklogpage": "Log pamblokiran",
        "blocklog-showlog": "Panganggo iki wis tau diblokir sakdurungé.\nLog blokiran sumadhiya nèng ngisor kanggo rujukan:",
        "blocklog-showsuppresslog": "Panganggo iki wis tau diblokir lan didhelikaké sakdurungé.\nLog brèdèlan sumadhiya nèng ngisor kanggo rujukan:",
-       "blocklogentry": "mblokir \"[[$1]]\" dipun watesi wekdalipun $2 $3",
-       "reblock-logentry": "Ngowahi sèting pamblokiran [[$1]] kanthi wektu daluwarsa $2 $3",
+       "blocklogentry": "mblokir [[$1]] kanthi wektu kadaluwarsa $2 $3",
+       "reblock-logentry": "ngowah setèlan blokir tumrap [[$1]] kanthi wektu kadaluwarsa $2 $3",
        "blocklogtext": "Ing ngisor iki kapacak log pamblokiran lan panjabelan blokir panganggo.\nAlamat IP sing diblokir sacara otomatis ora ana ing daftar iki.\nMangga mirsani [[Special:BlockList|daftar panganggo sing diblokir]] kanggo daftar blokir pungkasan.",
        "unblocklogentry": "njabel blokir \"$1\"",
        "block-log-flags-anononly": "namung panganggo anonim waé",
        "block-log-flags-hiddenname": "jeneng panganggo didhelikaké",
        "range_block_disabled": "Fungsi pamblokir blok IP kanggo para opsis dipatèni.",
        "ipb_expiry_invalid": "Wektu kadaluwarsa ora absah.",
+       "ipb_expiry_old": "Wektu kadaluwarsa ana ing nguni.",
        "ipb_expiry_temp": "Pamblokiran tumrap jeneng panganggo sing didhelikaké kudu permanèn.",
        "ipb_hide_invalid": "Ora bisa ndhelikaké akun iki; manawa wis kakèhan suntingan.",
        "ipb_already_blocked": "\"$1\" wis diblokir",
        "movepage-page-moved": "Kaca $1 wis dipindhah menyang $2.",
        "movepage-page-unmoved": "Kaca $1 ora bisa dialihaké menyang $2.",
        "movepage-max-pages": "Paling akèh $1 {{PLURAL:$1|kaca|kaca}} wis dialihaké lan ora ana manèh sing bakal dialihaké sacara otomatis.",
-       "movelogpage": "Cathetan lih-lihan",
+       "movelogpage": "Log alih",
        "movelogpagetext": "Ing ngisor iki kapacak log pangalihan kaca.",
        "movesubpage": "{{PLURAL:$1|Anak-kaca|Anak-kaca}}",
        "movesubpagetext": "Kaca iki nduwèni $1 {{PLURAL:$1|anak-kaca|anak-kaca}} kaya kapacak ing ngisor.",
        "delete_and_move_text": "Kaca jujugan \"[[:$1]]\" wis ana.\nApa sampéyan kersa mbusak iku supaya kacané bisa dilih?",
        "delete_and_move_confirm": "Ya, busak kaca iku.",
        "delete_and_move_reason": "Dibusak kanggo jaga-jaga ananing pamindhahan saka \"[[$1]]\"",
-       "selfmove": "Pangalihan kaca ora bisa dilakoni amerga irah-irahan utawa judhul sumber lan tujuané padha.",
+       "selfmove": "Sesirah sumber lan tujuan padha;\nora bisa ngalih nyang tujuan sing padha.",
        "immobile-source-namespace": "Ora bisa mindhahaké kaca jroning bilik jeneng \"$1\"",
        "immobile-target-namespace": "Ora bisa mindhahaké kaca menyang bilik jeneng \"$1\"",
        "immobile-target-namespace-iw": "Pranala interwiki dudu target sing sah kanggo pamindhahan kaca.",
        "immobile-source-page": "Kaca iki ora bisa dilih-lih.",
        "immobile-target-page": "Ora bisa mindhahaké menyang irah-irahan tujuan kasebut.",
-       "bad-target-model": "Halaman yang dituju menggunakan model isi yang berbeda. Tidak dapat mengonversi $1 ke $2.",
+       "bad-target-model": "Tujuan sing diarepaké nganggo gagrag isi sing béda. Ora bisa ngganti $1 dadi $2.",
        "imagenocrossnamespace": "Ora bisa mindhahaké gambar menyang bilik nama non-gambar",
        "nonfile-cannot-move-to-file": "Ora bisa mindhahaké non-berkas nèng bilik jeneng berkas",
        "imagetypemismatch": "Èkstènsi anyar berkas ora cocog karo jenisé",
        "imageinvalidfilename": "Jeneng berkas tujuan ora sah",
        "fix-double-redirects": "Dandani kabèh pangalihan gandha sing tumuju marang irah-irahan asli",
-       "move-leave-redirect": "Gawé pangalihan menyang irah-irahan anyar",
+       "move-leave-redirect": "Ungkur kaca alihan",
        "protectedpagemovewarning": "'''Pènget:''' Kaca iki wis dikunci dadi mung panganggo sing nduwé hak aksès pangurus baé sing bisa mindhahaké.\nCathetan entri pungkasan disadiakaké ing ngisor kanggo referensi:",
        "semiprotectedpagemovewarning": "'''Cathetan:''' Kaca iki wis direksa saéngga mung panganggo kadhaptar sing bisa mindhahaké.\nEntri cathetan pungkasan disadiakake ing ngisor kanggo referensi:",
        "move-over-sharedrepo": "[[:$1]] ana ing panyimpenan barengan. Ngalih barkas mawa sesirah iki bakal ngamblegi barkas barengan iku.",
        "allmessages-language": "Basa:",
        "allmessages-filter-submit": "Tumuju menyang",
        "thumbnail-more": "Gedhèkaké",
-       "filemissing": "Berkas ora ditemokaké",
+       "filemissing": "Barkas ilang",
        "thumbnail_error": "Kaluputan nalika nggawé gambar cilik (''thumbnail''): $1",
        "thumbnail_error_remote": "Peringatan kasalahan saka $1:\n$2",
        "djvu_page_error": "Kaca DjVu ana ing sajabaning ranggèhan (''range'')",
        "import-interwiki-history": "Tuladen kabèh vèrsi lawas saka kaca iki",
        "import-interwiki-templates": "Katutna kabèh cithakan",
        "import-interwiki-submit": "Impor",
-       "import-upload-filename": "Jeneng berkas:",
+       "import-upload-filename": "Jeneng barkas:",
        "import-comment": "Komentar:",
        "importtext": "Mangga èkspor berkas saka wiki sumber nganggo [[Special:Export|prangkat èkspor]].\nSimpen nèng komputer Sampéyan lan unggaha nèng kéné.",
        "importstart": "Ngimpor kaca...",
        "import-error-interwiki": "Kaca \"$1\" ora diimpor amarga jenengé dicadhangaké kango pranala njaba (interwiki).",
        "import-error-special": "Kaca \"$1\" ora diimpor amarga kuwi kalebu nèng bilik jeneng kusus sing ora nglilakaké anané kaca.",
        "import-error-invalid": "Kaca \"$1\" ora diimpor amarga jenengé ora sah.",
-       "import-error-unserialize": "Revisi $2 dari halaman \"$1\" tidak dapat di-''unserialized''. Revisi tersebut dilaporkan menggunakan model konten $3 diserialisasi sebagai $4.",
+       "import-error-unserialize": "Revisi $2 saka kaca \"$1\" ora bisa diurutaké. Revisi iku dilapuraké murih nganggo gagrag isi $3 sing diurutaké minangka $4.",
        "import-options-wrong": "{{PLURAL:$2|Opsi|Opsi}} salah: <nowiki>$1</nowiki>",
-       "import-rootpage-invalid": "Halaman turunan yang diberikan adalah judul yang salah.",
+       "import-rootpage-invalid": "Kaca wod iki sesirahé ora sah.",
        "import-rootpage-nosubpage": "Ruang nama \"$1\" di halaman turunan tidak mengizinkan subhalaman.",
        "importlogpage": "Log impor",
        "importlogpagetext": "Impor administratif kaca-kaca mawa sajarah panyuntingan saka wiki liya.",
        "tooltip-diff": "Tuduhaké owah-owahan endi sing sampéyan gawé tumrap tulisan iki",
        "tooltip-compareselectedversions": "Delengen prabédan antara rong vèrsi kaca iki sing dipilih.",
        "tooltip-watch": "Wuwuh kaca iki nyang pawawanganing sampéyan",
-       "tooltip-watchlistedit-normal-submit": "Singkiraké judhul",
+       "tooltip-watchlistedit-normal-submit": "Busak sesirah",
        "tooltip-watchlistedit-raw-submit": "Anyari daptar pangawasan",
        "tooltip-recreate": "Gawéa kaca iki manèh senadyan tau dibusak",
-       "tooltip-upload": "Miwiti pangunggahan",
+       "tooltip-upload": "Wiwit ngunggah",
        "tooltip-rollback": "Balèkaké besutan-besutan kaca iki déning sing pungkasan nyumbang sarana saklikan.",
-       "tooltip-undo": "Mbalèkaké révisi iki lan mbukak kothak panyuntingan jroning mode pratayang. Wènèhi kasempatan kanggo ngisi alesan ing kothak ringkesan.",
+       "tooltip-undo": "\"Wurung\" mbalèkaké besutan iki lan mbukak blangko besutan sarana modhe pratuduh. Alesan kena diwuwuhaké ing babagan ringkesan.",
        "tooltip-preferences-save": "Simpen préperensi",
        "tooltip-summary": "Isi tingkesan cendhak",
        "anonymous": "{{PLURAL:$1|Panganggo|panganggo}} anon ing {{SITENAME}}.",
        "pageinfo-header-edits": "Sujarah besutan",
        "pageinfo-header-restrictions": "Perlindungan halaman",
        "pageinfo-header-properties": "Properti kaca",
-       "pageinfo-display-title": "Judul tampilan",
+       "pageinfo-display-title": "Sesirah pajangan",
        "pageinfo-default-sort": "Kunci urut baku",
        "pageinfo-length": "Panjang halaman (dalam bita)",
        "pageinfo-article-id": "ID kaca",
        "hours": "{{PLURAL:$1|$1 jam|$1 jam}}",
        "days": "{{PLURAL:$1|$1 dina|$1 dina}}",
        "weeks": "{{PLURAL:$1|minggu|minggu}}",
-       "months": "{{PLURAL:$1|$1 sasi|$1 sasi}}",
+       "months": "{{PLURAL:$1|$1 wulan}}",
        "years": "{{PLURAL:$1|$1 taun|$1 taun}}",
        "ago": "$1 kapungkur",
        "just-now": "baru saja",
        "exif-ycbcrcoefficients": "Koèfisièn matriks transformasi papan werna",
        "exif-referenceblackwhite": "Wiji réferènsi pasangan ireng putih",
        "exif-datetime": "Tanggal lan tabuh owahing barkas",
-       "exif-imagedescription": "Judhul gambar",
+       "exif-imagedescription": "Sesirah gambar",
        "exif-make": "Produsèn kamera",
        "exif-model": "Modhèl kaméra",
        "exif-software": "Piranti alus sing dianggo",
        "exif-subjectlocation": "Lokasi subjèk",
        "exif-exposureindex": "Indhèks pajanan",
        "exif-sensingmethod": "Métodhe pangindran",
-       "exif-filesource": "Sumber berkas",
+       "exif-filesource": "Sumber barkas",
        "exif-scenetype": "Tipe panyawangan",
        "exif-customrendered": "Prosès nggawé gambar",
        "exif-exposuremode": "Modhe pajanan",
        "exif-provinceorstatedest": "Propinsi utawa nagara bagéyan katampilaké",
        "exif-citydest": "Kutha katampilaké",
        "exif-sublocationdest": "Dhaèrahé kutha katampilaké",
-       "exif-objectname": "Judhul cendhèk",
+       "exif-objectname": "Sesirah cekak",
        "exif-specialinstructions": "Prèntah kusus",
        "exif-headline": "Tajuk",
        "exif-credit": "Krédit/Panyadhiya",
        "table_pager_limit": "Tuduhna $1 entri per kaca",
        "table_pager_limit_label": "Barang per kaca:",
        "table_pager_limit_submit": "Golèk",
-       "table_pager_empty": "Ora ditemokaké",
+       "table_pager_empty": "Ora ana",
        "autosumm-blank": "Ngothongaké kaca",
        "autosumm-replace": "←Ngganti kaca karo '$1'",
        "autoredircomment": "←Ngalihaké menyang [[$1]]",
        "watchlistedit-normal-title": "Besut pawawangan",
        "watchlistedit-normal-legend": "Busak sesirah saka pawawangan",
        "watchlistedit-normal-explain": "Irah-irahan utawa judhul ing daftar pangawasan panjenengan kapacak ing ngisor iki.\nKanggo mbusak sawijining irah-irahan, kliken kothak ing pinggiré, lan banjur kliken \"Busak judhul\".\nPanjenengan uga bisa [[Special:EditWatchlist/raw|nyunting daftar mentah]].",
-       "watchlistedit-normal-submit": "Busak irah-irahan",
+       "watchlistedit-normal-submit": "Busak sesirah",
        "watchlistedit-normal-done": "Irah-irahan {{PLURAL:$1|siji|$1}} wis dibusak saka daftar pangawasan panjenengan:",
        "watchlistedit-raw-title": "Besut pawawangan wantahan",
        "watchlistedit-raw-legend": "Besut pawawangan wantahan",
        "watchlistedit-raw-explain": "Irah-irahan ing daftar pangawasan panjenengan kapacak ing ngisor iki, lan bisa diowahi mawa nambahaké utawa mbusak daftar; sairah-irahan saban barisé.\nYèn wis rampung, anyarana kaca daftar pangawasan iki.\nPanjenengan uga bisa [[Special:EditWatchlist|nganggo éditor standar panjenengan]].",
-       "watchlistedit-raw-titles": "Irah-irahan:",
+       "watchlistedit-raw-titles": "Sesirah:",
        "watchlistedit-raw-submit": "Anyari pawawangan",
        "watchlistedit-raw-done": "Pawawanganing sampéyan wis dianyari.",
-       "watchlistedit-raw-added": "{{PLURAL:$1|1 irah-irahan wis|$1 irah-irahan wis}} ditambahaké:",
-       "watchlistedit-raw-removed": "{{PLURAL:$1|1 irah-irahan wis|$1 irah-irahan wis}} diwetokaké:",
+       "watchlistedit-raw-added": "{{PLURAL:$1|1 sesirah|$1 sesirah}} ditambahaké:",
+       "watchlistedit-raw-removed": "{{PLURAL:$1|1 sesirah|$1 sesirah}} dibusak:",
        "watchlisttools-view": "Tuduhna owah-owahan sing ana gandhèngané",
        "watchlisttools-edit": "Deleng lan besut pawawangan",
        "watchlisttools-raw": "Besut pawawangan wantahan",
        "redirect-user": "ID panganggo",
        "redirect-page": "ID kaca",
        "redirect-revision": "Revisi kaca",
-       "redirect-file": "Jeneng berkas",
+       "redirect-file": "Jeneng barkas",
        "redirect-not-exists": "Nilai ora ditemokaké",
        "fileduplicatesearch": "Golèk berkas duplikat",
        "fileduplicatesearch-summary": "Golèk duplikat berkas adhedhasar biji hash-é.",
        "compare-rev1": "Révisi 1",
        "compare-rev2": "Révisi 2",
        "compare-submit": "Bandingaké",
-       "compare-invalid-title": "Judhul sing Sampéyan awèhaké ora sah.",
-       "compare-title-not-exists": "Judhul sing Sampéyan jaluk ora ana.",
+       "compare-invalid-title": "Sesirah sing kokawèhaké ora sah.",
+       "compare-title-not-exists": "Sesirah sing kokawèhaké ora ana.",
        "compare-revision-not-exists": "Benahan sing Sampéyan jaluk ora ana.",
        "dberr-problems": "Nyuwun ngapura! Situs iki ngalami masalah tèknis.",
        "dberr-again": "Coba nunggu sawetara menit lan unggahna manèh.",
        "revdelete-uname-unhid": "jeneng panganggo dituduhaké",
        "revdelete-restricted": "rèstriksi ditrapaké marang para opsis",
        "revdelete-unrestricted": "rèstriksi marang para opsis dijabel",
+       "logentry-block-block": "$1 {{GENDER:$2|mblokir}} {{GENDER:$4|$3}} kanthi wektu kadaluwarsa $5 $6",
+       "logentry-block-reblock": "$1 {{GENDER:$2|ngowah}} setèlan blokir tumrap {{GENDER:$4|$3}} kanthi wektu kadaluwarsa $5 $6",
+       "logentry-suppress-block": "$1 {{GENDER:$2|mblokir}} {{GENDER:$4|$3}} kanthi wektu kadaluwarsa $5 $6",
+       "logentry-suppress-reblock": "$1 {{GENDER:$2|ngowah}} setèlan blokir tumrap {{GENDER:$4|$3}} kanthi wektu kadaluwarsa $5 $6",
        "logentry-move-move": "$1 {{GENDER:$2|ngalih}} kaca $3 nyang $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|mindhahaké}} kaca $3 nèng $4 tanpa ninggalaké pangalihan",
        "logentry-move-move_redir": "$1 {{GENDER:$2|mindhahaké}} kaca $3 nèng $4 nindesi pangalihan liyane",
        "logentry-newusers-create2": "Akun panganggo $3 {{GENDER:$2|digawé}} déning $1",
        "logentry-newusers-byemail": "Akun panganggo $3 {{GENDER:$2|digawé}} déning $1 lan tembung sandhine dikirim lewat layang elektronik",
        "logentry-newusers-autocreate": "Akun $1 {{GENDER:$2|digawé}} otomatis",
+       "logentry-protect-unprotect": "$1 {{GENDER:$2|ngilangi}} rereksan saka $3",
        "logentry-rights-rights": "$1 {{GENDER:$2|ngganti}} golongané {{GENDER:$6|$3}} saka $4 dadi $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|ngganti}} golongané $3",
        "logentry-rights-autopromote": "$1 otomatis {{GENDER:$2|dipromosikne}} saka $4 nèng $5",
        "feedback-bugornote": "Yèn Sampéyan siap njelasaké masalah tèhnis kanthi rinci mangga [$1 laporaké bug].\nUtawa, Sampéyan bisa nganggo pormulir gampang ngisor. Tanggepan Sampéyan bakal ditambahaké nèng kaca \"[$3 $2]\", bebarengan karo jeneng panganggo Sampéyan lan pramban sing Sampéyan anggo.",
        "feedback-cancel": "Batal",
        "feedback-close": "Rampung",
+       "feedback-error-title": "Cacad",
        "feedback-error1": "Kasalahan: Asil ora dikenal saka API",
        "feedback-error2": "Cacad: Gagal mbesut",
        "feedback-error3": "Kasalahan: Ora ana tanggepan saka API",
        "limitreport-expensivefunctioncount": "Expensive parser function count",
        "expandtemplates": "Cithakan dikembangaké",
        "expand_templates_intro": "Kaca astaméwa iki njupuk sawetara tèks lan ngembangaké kabèh cithakan sajroning iku sacara rékursif.\nKaca iki uga ngembangaké fungsi parser kaya ta\n<nowiki>{{</nowiki>#language:…}}, lan variabel kaya ta\n<nowiki>{{</nowiki>CURRENTDAY}}&mdash;sajatiné mèh kabèh sing ana ing antara rong tandha kurung akolade.",
-       "expand_templates_title": "Irah-irahan kontèks, kanggo {{FULLPAGENAME}} lan sabanjuré:",
+       "expand_templates_title": "Sesirah kontèks, kanggo {{FULLPAGENAME}}, lsp.:",
        "expand_templates_input": "Tèks sumber:",
        "expand_templates_output": "Pituwas (kasil)",
        "expand_templates_xml_output": "Pituwas XML",
        "expand_templates_remove_nowiki": "Brèdèl tag <nowiki> nèng asilé",
        "expand_templates_generate_xml": "Tuduhna uwit parser XML",
        "expand_templates_generate_rawhtml": "Show raw HTML",
-       "expand_templates_preview": "Pratayang",
+       "expand_templates_preview": "Pratuduh",
        "special-characters-group-latin": "Latin",
        "special-characters-group-latinextended": "Latin pepak",
        "special-characters-group-ipa": "IPA",
        "special-characters-group-thai": "Thailand",
        "special-characters-group-lao": "Lao",
        "special-characters-group-khmer": "Khmer",
-       "api-error-blacklisted": "Mangga pilih judhul liya sing njelasaké",
        "randomrootpage": "Kaca dhasaran waton"
 }
index 0b6fe50..e9e2f92 100644 (file)
        "noindex-category": "არ არსებობს ინდექსირებული გვერდები",
        "broken-file-category": "გვერდები ფაილების არასწორი ბმულებით",
        "categoryviewer-pagedlinks": "($1) ($2)",
+       "category-header-numerals": "$1–$2",
        "about": "შესახებ",
        "article": "სტატია",
        "newwindow": "(ახალ ფანჯარაში)",
        "tagline": "{{SITENAME}} გვერდიდან",
        "help": "დახმარება",
        "search": "ძიება",
+       "search-ignored-headings": "#<!-- დატოვეთ ეს ხაზი უცვლელად --> <pre>\n# სათაურები, რომლებთაც ძიება დააგნორებს.\n# ცვლილებები აისახება როგორც კი სათაურის მქონე გვერდს ინდექსირება გაუკეთდება.\n# შეგიძლიათ გააკეთოთ გვერდის ძალით ხელახალი ინდექსირება null edit-ის გაკეთებით.\n# სინტაქსი შემდეგია:\n#   * ყველაფერი \"#\" სიმბოლოდან ხაზის ბოლმდე კომენტარია.\n#   * ყველა არა-ცარიელი ხაზი is the exact title to ignore, case and everything.\nწყაროები\nრესურსები ინტერნეტში\nიხილეთ აგრეთვე\n #</pre> <!-- დატოვეთ ეს ხაზი უცვლელად -->",
        "searchbutton": "ძიება",
        "go": "სტატია",
        "searcharticle": "გვერდი",
        "yourpasswordagain": "ხელმეორედ შეიყვანეთ პაროლი",
        "createacct-yourpasswordagain": "დაადასტურეთ პაროლი",
        "createacct-yourpasswordagain-ph": "ხელმეორედ შეიყვანეთ პაროლი",
-       "remembermypassword": "დამიმახსოვრე ამ კომპიუტერზე (მაქსიმუმ $1 {{PLURAL:$1|დღე}})",
        "userlogin-remembermypassword": "დამიმახსოვრე",
        "userlogin-signwithsecure": "უსაფრთხო კავშირის გამოყენება",
        "cannotloginnow-title": "ამჟამად შესვლა შუეძლებელია",
        "passwordreset-emailelement": "მომხმარებლის სახელი: \n$1\n\nდროებითი პაროლი: \n$2",
        "passwordreset-emailsentemail": "თუ ეს მეილი თქვენს ანგარიშთანაა დაკავშირებული, გაიგზავნება პაროლის თავიდან დასაყენებელი ელექტრონული ფოსტა.",
        "passwordreset-emailsentusername": "თუ არსებობს მეილი, რომელიც ამ ანგარიშთანაა დაკავშირებული, გაიგზავნება პაროლის თავიდან დასაყენებელი ელექტრონული ფოსტა.",
-       "passwordreset-emailsent-capture": "ქვემოთ ნაჩვენები პაროლის თავიდან დასაყენებელი წერილი გაიგზავნა.",
-       "passwordreset-emailerror-capture": "ქვემოთ მოცემულია შექმნილი პაროლის დასაყენებელი წერილი, რომლის გაგზავნაც {{GENDER:$2|მომხმარებელთან}} ვერ მოხერხდა: $1 გამო",
        "passwordreset-emailsent-capture2": "პაროლის გაუქმების შესახებ {{PLURAL:$1|მეილი|მეილები}} გაიგზავნა. {{PLURAL:$1|სახელი და პაროლი|სახელებისა და პაროლების სია}} არის ნაჩვენები ქვემოთ.",
        "passwordreset-emailerror-capture2": "{{GENDER:$2|მომხმარებელთან}} მეილის გაგზავნა ვერ მოხერხდა: $1 {{PLURAL:$3|სახელი და პაროლი|სახელებისა და პაროლების სია}} არის ნაჩვენები ქვემოთ.",
        "passwordreset-nocaller": "გამომძახებელი უნდა იყოს მიწოდებული",
        "passwordreset-nodata": "არც მომხმარებლის სახელი და არც ელ-ფოსტის მისამართი არ იყო მოწოდებული",
        "changeemail": "ელ-ფოსტის მისამართის შეცვლა ან წაშლა",
        "changeemail-header": "შეავსეთ ეს ფორმა მეილის შესაცვლელად. თუ გსურთ თქვენი ანგარიში არ იყოს დაკავშირებული არცერთ მეილთან, ახალი მეილის მისამართის ველი დატოვეთ ცარიელი.",
-       "changeemail-passwordrequired": "ამ ცვლილების დასადასტურებლად დაგჭირდებათ პაროლის შეყვანა.",
        "changeemail-no-info": "თქვენ ავტირიზებული უნდა იყოთ ამ გვერდთან უშუალო წვდომისთვის.",
        "changeemail-oldemail": "ელ-ფოსტის ამჟამინდელი მისამართი:",
        "changeemail-newemail": "ახალი ელ-ფოსტის მისამართი:",
        "minoredit": "მცირე რედაქტირება",
        "watchthis": "უთვალთვალე ამ გვერდს",
        "savearticle": "გვერდის შენახვა",
+       "savechanges": "ცვლილებების შენახვა",
        "publishpage": "გვერდის გამოქვეყნება",
+       "publishchanges": "ცვლილებების გამოქვეყნება",
        "preview": "წინასწარი გადახედვა",
        "showpreview": "წინასწარი გადახედვის ჩვენება",
        "showdiff": "ცვლილებების ჩვენება",
        "content-model-css": "CSS",
        "content-json-empty-object": "ცარიელი ობიექტი",
        "content-json-empty-array": "ცარიელი ტაბლო",
+       "deprecated-self-close-category": "გვერდები, რომლებიც იყენებენ არავალიდურ თვითდახურვად HTML ტეგებს",
        "duplicate-args-warning": "<strong>გაფრთხილება:</strong> [[:$1]] იძახებს [[:$2]]-ის \"$3\" პარამეტრის ერთზე მეტ მნიშვნელობას. აისახება მხოლოდ ბოლოს გამოყენებული მნიშვნელობა.",
        "duplicate-args-category": "გვერდები, რომლებიც იყენებენ დუბლიკატ არგუმენტებს თარგების გამოძახებისას",
        "duplicate-args-category-desc": "გვერდები, რომლებიც იყენებენ დუბლიკატ არგუმენტებს თარგების გამოძახებისას, როგორებიც არის <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> ან <code><nowiki>{{foo|bar|1=bar}}</nowiki></code>.",
        "undo-nochange": "როგორც ჩანს, რედაქტირება უკვე გაუქმდა.",
        "undo-summary": "[[Special:Contributions/$2|$2-ის]]([[User talk:$2|განხილვა]]) ცვლილებების გაუქმება (№$1)",
        "undo-summary-username-hidden": "ცვლილების გაუქმება $1, მომხმარებლის მიერ, რომლის სახელი დამალულია",
-       "cantcreateaccounttitle": "ანგარიშის შექმნა ვერ ხერხდება",
        "cantcreateaccount-text": "ამ IP-მისამართიდან აიკრძალა (<b>$1</b>) მომხმარებელ [[User:$3|$3]]-ის მიერ.\n\n$3 -ემ ამგვარი ახსნა : ''$2''",
        "cantcreateaccount-range-text": "{{GENDER:$3|მომხმარებელმა}} [[User:$3|$3]] ანგარიშის ან IP-მისამართის $1 შექმნისთვის {{GENDER:$3|დაადო}} აკრძალვა <strong>$1</strong>, თქვენი IP-მისამართის ჩათვლით ($4).\n\nმითითებულია შემდეგი მიზეზი: $2.",
        "viewpagelogs": "ამ გვერდისთვის სარეგისტრაციო ჟურნალების ჩვენება",
        "right-managechangetags": "[[Special:Tags|ტეგების]] შექმნა და (დე)აქტივაცია",
        "right-applychangetags": "[[Special:Tags|tags]] მიღება თქვენ ცვლილებებთან ერთად",
        "right-changetags": "თვითნებური [[Special:Tags|tags]] დამატება ან წაშლა ცალკეულ ცვლილებებსა და ჟურნალის ჩანაწერებში",
+       "right-deletechangetags": "მონაცემთა ბაზიდან [[Special:Tags|ტეგების]] წაშლა",
        "grant-generic": "\"$1\" უფლებები",
        "grant-group-page-interaction": "კავშირი გვერდებთან",
        "grant-group-file-interaction": "კავშირი მედია-ფაილებთან",
        "grant-highvolume": "დიდი მოცულობით რედაქტირება",
        "grant-oversight": "მომხმარებლებისა და შესწორებების დამალვა",
        "grant-patrol": "გვერდების რედაქტირებების შემოწმება",
+       "grant-privateinfo": "პირად ინფორმაციაზე წვდომა",
        "grant-protect": "გვერდების და დაცვა და დაცვის მოხსნა",
        "grant-rollback": "გვერდების რედაქტირებების სწრაფი გაუქმება",
        "grant-sendemail": "გაგუგზავნე ელექტრონული ფოსტა სხვა მომხმარებლებს",
        "rightslogtext": "მომხმარებელთა უფლებების ცვლილებათა ჟურბალი",
        "action-read": "ამ გვერდის კითხვა",
        "action-edit": "ამ გვერდის რედაქტირება",
-       "action-createpage": "á\83\92á\83\95á\83\94á\83 á\83\93á\83\94á\83\91ის შექმნა",
-       "action-createtalk": "á\83\92á\83\90á\83\9cá\83®á\83\98á\83\9aá\83\95á\83\98á\83¡ á\83\92á\83\95á\83\94á\83 á\83\93á\83\94á\83\91ის შექმნა",
+       "action-createpage": "á\83\90á\83\9b á\83\92á\83\95á\83\94á\83 á\83\93ის შექმნა",
+       "action-createtalk": "á\83\90á\83\9b á\83\92á\83\90á\83\9cá\83®á\83\98á\83\9aá\83\95á\83\98á\83¡ á\83\92á\83\95á\83\94á\83 á\83\93ის შექმნა",
        "action-createaccount": "ამ ანგარიშის შექმნა",
        "action-autocreateaccount": "გარე მომხმარებლის ანგარიშის ავტომატურად შექმნა",
        "action-history": "ამ გვერდის ისტორიის ნახვა",
        "action-managechangetags": "ტეგების შექმნა და (დე)აქტივაცია",
        "action-applychangetags": "ტეგების მიღება თქვენ ცვლილებებთან ერთად",
        "action-changetags": "თავისუფალი ტეგების დამატება და წაშლა ცალკეულ ცვლილებებსა და ჟურნალების ჩანაწერებში",
+       "action-deletechangetags": "მონაცემთა ბაზიდან ტეგების წაშლა",
+       "action-purge": "ამ გვერდის წაშლა",
        "nchanges": "$1 ცვლილება",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ბოლო ვიზიტის შემდეგ}}",
        "enhancedrc-history": "ისტორია",
        "upload-http-error": "მოხდა HTTP შეცდომა: $1",
        "upload-copy-upload-invalid-domain": "ამ დომენში ატვირთვების კოპირება არ არის ხელმისაწვდომი.",
        "upload-foreign-cant-upload": "ეს ვიკი არ არის დაკონფიგურირებული იმისათვის, რომ ატვირთოს ფაილების უცხო ფაილთა საწყობში.",
+       "upload-dialog-disabled": "ფაილის ატვირთვა ამ დიალოგური ფანჯრით გათიშულია ამ ვიკიზე.",
        "upload-dialog-title": "ფაილის ატვირთვა",
        "upload-dialog-button-cancel": "გაუქმება",
        "upload-dialog-button-done": "შესრულდა",
        "uploadstash-badtoken": "მითითებული მოქმედება ვერ შესრულდა. შესაძლოა, რედაქტირების უფლებამოსილების მოქმედების ვადა ამოიწურა. გთხოვთ, სცადეთ თავიდან.",
        "uploadstash-errclear": "ფაილების გასუფთავება ვერ მოხერხდა.",
        "uploadstash-refresh": "ფაილების სიის განახლება",
+       "uploadstash-thumbnail": "მინიატურის ნახვა",
        "invalid-chunk-offset": "არასწორი საწყისი წერტილი",
        "img-auth-accessdenied": "მოქმედება აკრძალულია",
        "img-auth-nopathinfo": "დაკარგულია PATH_INFO.\nთქვენი სერვერი არ არის მომართული ამ ინფორმაციის გადასაცემად.\nშესაძლოა, ის მუშაობს CGI-ის ბაზაზე და არ გააჩნია img_auth მხარდაჭერა.\n[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization იხილეთ სურათის ავტორიზაცია.]",
        "filerevert-submit": "გაუქმება",
        "filerevert-success": "'''[[Media:$1|$1]]''' დაუბრუნდა ვერსიას [$4  $3, $2]-დან.",
        "filerevert-badversion": "არ არსებობს ფაილის წინა ლოკალური ვერსია მოთხოვნილი  თარიღითა და დროით",
+       "filerevert-identical": "ფაილის ამჟამინდელი ვერსია უკვე არის შერჩეულის იდენტური.",
        "filedelete": "$1 წაშლა",
        "filedelete-legend": "ფაილის წაშლა",
        "filedelete-intro": "თქვენ შლით <strong>[[Media:$1|$1]]-ს</strong> მისი ისტორიით.",
        "trackingcategories-msg": "კატეგორიის მიდევნება",
        "trackingcategories-name": "შეტყობინების სახელი",
        "trackingcategories-desc": "კატეგორიაში ჩართვის კრიტერიუმები",
+       "restricted-displaytitle-ignored": "გვერდები დაიგნორებული სათაურებით",
        "noindex-category-desc": "გვერდი არ არის ინდექსირებული საძიებო სამუშაოებით, რადგან მასზე არის „ჯადოსნური სიტყვა“ <code><nowiki>__NOINDEX__</nowiki></code> და ის იმყოფება სახელთა სივრცეში, სადაც დასაშვებია ეს დროშა.",
        "index-category-desc": "გვერდზე არის „ჯადოსნური სიტყვა“ <code><nowiki>__INDEX__</nowiki></code> (და გვერდი იმყოფება სახელთა სივრცეში, სადაც დაშვებულია ეს დროშა). ამიტომ იგი ინდექსირებულია საძიებო სამუშაოებით იმ შემთხვევებში, როცა ეს ჩვეულებრივ არ ხდება.",
        "post-expand-template-inclusion-category-desc": "გვერდის ზომა უფრო გაიზრდება <code>$wgMaxArticleSize</code> ყველა თარგის ჩვენების შემდეგ, ამიტომ ზოგიერთი მათგანი არ იყო ნაჩვენები მთლიანად.",
        "watchnologin": "რეგისტრაცია ვერ შესრულდა",
        "addwatch": "კონტროლის სიაში დამატება",
        "addedwatchtext": "„[[:$1]]“ და მისი განხილვის გვერდი დაემატა თქვენს [[Special:Watchlist|კონტროლის სიას]].",
+       "addedwatchtext-talk": "„[[:$1]]“ და მასთან დაკავშირებული გვერდი დაემატა თქვენს [[Special:Watchlist|კონტროლის სიას]].",
        "addedwatchtext-short": "გვერდი „$1“ დაემატა თქვენი კონტროლის სიას.",
        "removewatch": "კონტროლის სიიდან წაშლა",
        "removedwatchtext": "„[[:$1]]“ და მისი განხილვის გვერდი ამოღებულია თქვენი [[Special:Watchlist|კონტროლის სიიდან]].",
+       "removedwatchtext-talk": "„[[:$1]]“ და მასთან დაკავშირებული გვერდი ამოღებულია თქვენი [[Special:Watchlist|კონტროლის სიიდან]].",
        "removedwatchtext-short": "გვერდი „$1“ წაიშალა თქვენი კონტროლის სიიდან.",
        "watch": "კონტროლი",
        "watchthispage": "ამ გვერდის კონტროლი",
        "rollbacklinkcount": "$1 {{PLURAL:$1|ცვლილების|ცვლილების}} გაუქმება",
        "rollbacklinkcount-morethan": "$1-ზე მეტი {{PLURAL:$1|ცვლილების|ცვლილების}} გაუქმება",
        "rollbackfailed": "შეცდომა გაუქმებისას",
+       "rollback-missingparam": "აკლია საჭირო პარამეტრები.",
        "cantrollback": "შეუძლებელია უწინდელი რედაქციის აღდგენა; ის, ვინც უკანასკნელი ცვლილებები შეიტანა, ამ სტატიის ერთადერთი ავტორია.",
        "alreadyrolled": "შეუძლებელია ბოლო ცვლილების გაუქმება [[:$1]], გაკეებული [[User:$2|$2]] ([[User talk:$2|განხილვა]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nვიღაცა სხვამ უკვე შეასწორა ან გაააუქმა ეს გვერდი.\n\nბოლო ცვლილებები შეიტანა  [[User:$3|$3]] ([[User talk:$3|განხილვა]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "რედაქტირება განმარტებული იყო როგორც: <em>$1</em>.",
        "changecontentmodel-success-text": "[[:$1]]-ის კონტენტის ტიპი შეიცვალა.",
        "changecontentmodel-cannot-convert": "[[:$1]]-ის შინაარსის $2-ის ტიპზე კონვერტაცია შეუძლებელია.",
        "changecontentmodel-nodirectediting": "$1 შინაარსის მოდელს არ აქვს პირდაპირი რედაქტირების მხარდაჭერა",
+       "changecontentmodel-emptymodels-title": "შინაარსის მოდელები არ არის ხელმისაწვდომი",
+       "changecontentmodel-emptymodels-text": "შინაარსი [[:$1]]-ზე არ კონვერტირდება არცერთ ტიპზე.",
        "log-name-contentmodel": "შინაარსის მოდელის შეცვლის ჟურნალი",
        "log-description-contentmodel": "გვერდის შინაარსის მოდელთან დაკავშირებული მოვლენები",
        "logentry-contentmodel-new": "$1-მ {{GENDER:$2|შექმნა}} გვერდი $3, არა-სტანდარტული მოდელით \"$5\"",
        "undeletehistorynoadmin": "ეს სტატია წაშლილია. წაშლის მიზეზი ნაჩვენებია მოკლე ანოტაციაში ქვემოთ, იმ მომხმარებელთა დეტალებთან ერთად ვინც რედაქტირება გაუკეთა ამ გვერდს წაშლის წინ. იმ წაშლილი ტექსტების აქტუალური ვერსიები მიღწევადია მხოლოდ ადმინისტრატორებისათვის.",
        "undelete-revision": "$1-ის წაშლილი ვერსია ($5, $4-ის მდგომარეობით), შენახული მომხმარებლის $3 მიერ:",
        "undeleterevision-missing": "არასწორი ან არარსებული ვერსია. სავარაუდოდ ქვენ გადახვედით არასწორ ბმულზე, ან იგი წაიშალა არქივიდან.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|ერთი ცვლილება|$1 ცვლილება}} ვერ აღდგა, რადგან {{PLURAL:$1|მისი|მათი}} <code>rev_id</code> უკვე გამოიყენებოდა.",
        "undelete-nodiff": "წინა ცვლილება ვერ ვიპოვეთ.",
        "undeletebtn": "აღდგენა",
        "undeletelink": "ნახვა/აღდგენა",
        "undeletedrevisions": "$1 ვერსია აღდგენილია",
        "undeletedrevisions-files": "$1 ვერსია და $2 ფაილი აღდგენილია",
        "undeletedfiles": "$1 ფაილი აღდგენილია",
-       "cannotundelete": "á\83¬á\83\90á\83¨á\83\9aá\83\98á\83¡ á\83\92á\83\90á\83£á\83¥á\83\9bá\83\94á\83\91á\83\90 á\83\95á\83\94á\83  á\83\92á\83\90á\83\9cá\83®á\83\9dá\83 á\83ªá\83\98á\83\94á\83\9aá\83\93á\83\90\n$1",
+       "cannotundelete": "á\83\96á\83\9dá\83\92á\83\98á\83\94á\83 á\83\97á\83\98 á\83\90á\83\9c á\83§á\83\95á\83\94á\83\9aá\83\90 á\83¬á\83\90á\83¨á\83\9aá\83\98á\83¡ á\83\92á\83\90á\83£á\83¥á\83\9bá\83\94á\83\91á\83\90 á\83\95á\83\94á\83  á\83\92á\83\90á\83\9cá\83®á\83\9dá\83 á\83ªá\83\98á\83\94á\83\9aá\83\93á\83\90:\n$1",
        "undeletedpage": "'''$1 აღდგენილია'''\n\nუკანასკნელი წაშლილთა და აღდგენის სია შეგიძლიათ ნახოთ [[Special:Log/delete|წაშლილთა სიაში]].",
        "undelete-header": "ბოლოს წაშლილი გვერდების სიის ნახვა შეიძლება [[Special:Log/delete|წაშლათა ჟურნალში]].",
        "undelete-search-title": "წაშლილი გვერდების ძიება",
        "undelete-error-long": "ფაილის აღდგენისას წარმოიშვა შეცდომები\n\n$1",
        "undelete-show-file-confirm": "დარწმუნებული ხართ, რომ გსურთ ფაილ <nowiki>$1</nowiki>-ის წაშლილი ვერსიის ხილვა $2 $3-დან?",
        "undelete-show-file-submit": "ჰო",
+       "undelete-revision-row2": "$1 ($2) $3 . . $4 $5 $6 $7 $8",
        "namespace": "სახელთა სივრცე:",
        "invert": "ყველა, მონიშნულის გარდა",
        "tooltip-invert": "მონიშნეთ ეს უჯრა, რათა დამალოთ გვერდების ცვლილებები არჩეული სახელთა სივრცის ფარგლებში (და მასთან დაკავშირებულ სახელთა სივრცეში, თუ მსგავსი რამ მითითებულია)",
        "sp-contributions-newbies-sub": "ახალბედებისთვის",
        "sp-contributions-newbies-title": "ბოლოს დარეგისტრირებულ მომხმარებელთა წვლილი",
        "sp-contributions-blocklog": "ბლოკირების ისტორია",
-       "sp-contributions-suppresslog": "მომხმარებლის წაშლილი წვლილი",
-       "sp-contributions-deleted": "მომხმარებლის წაშლილი შესწოებები",
+       "sp-contributions-suppresslog": "{{GENDER:$1|მომხმარებლის}} წაშლილი წვლილი",
+       "sp-contributions-deleted": "{{GENDER:$1|მომხმარებლის}} წაშლილი შესწოებები",
        "sp-contributions-uploads": "ატვირთვები",
        "sp-contributions-logs": "ჟურნალები",
        "sp-contributions-talk": "განხილვა",
        "sp-contributions-username": "IP მისამართი ან მომხმარებლის სახელი:",
        "sp-contributions-toponly": "აჩვენე მხოლოდ ბოლო ვერსიები",
        "sp-contributions-newonly": "აჩვენე მხოლოდ ცვლილებები, რომელიც წარმოადგენს გვერდის შექმნილს",
+       "sp-contributions-hideminor": "მცირე რედაქტირებების დამალვა",
        "sp-contributions-submit": "ძიება",
        "whatlinkshere": "ბმული გვერდზე",
        "whatlinkshere-title": "გვერდები, რომლებიც შეიცავენ „$1“-ის ბმულებს",
        "ipb-unblock": "მომხმარებლის სახელზე ან IP მისამართზე ბლოკის მოხსნა",
        "ipb-blocklist": "იხილე არსებული ბლოკირებები",
        "ipb-blocklist-contribs": "მომხმარებელ {{GENDER:$1|$1}} წვლილი",
+       "ipb-blocklist-duration-left": "დარჩა $1",
        "unblockip": "მომხმარებელზე ბლოკის მოხსნა",
        "unblockiptext": "გამოიყენეთ ქვემოთ მოცემული ფორმულარი, რათა  დაბლოკილი IP მისამართი ან მომხმარებლის სახელი აღადგინოთ.",
        "ipusubmit": "ამ ბლოკის მოხსნა",
        "lockdbsuccesstext": "პროექტის მონაცემთა ბაზა დაიბლოკა.<br />\nარ დაგავიწყდეთ [[Special:UnlockDB|ბლოკის მოხსნა]] მონაცემთა ბაზასთან სამუშაოების გატარების შემდეგ.",
        "unlockdbsuccesstext": "მომაცემთა ბაზაზე ბლოკი მოიხსნა.",
        "lockfilenotwritable": "არ გაქვთ უფლება მონაცემთა ბაზის დაცვის ფაილის შესწორების. დასაბლოკად ან ბლოკის მოსახსნელად საჭიროა ფაილი ღია იყოს.",
+       "databaselocked": "მონაცემთა ბაზა უკვე ჩაკეტილია.",
        "databasenotlocked": "მონაცემთა ბაზა არაა ჩაკეტილი.",
        "lockedbyandtime": "($1 $2 $3)",
        "move-page": "$1 — გადატანა",
        "tooltip-ca-nstab-category": "გვერდის კატეგორიის ჩვენება",
        "tooltip-minoredit": "მონიშნე როგორც მცირე რედაქტირება [alt-i]",
        "tooltip-save": "თქვენი ცვლილებების შენახვა",
+       "tooltip-publish": "თქვენი ცვლილებების გამოქვეყნება",
        "tooltip-preview": "წინასწარ გადახედე ცვლილებებს, გთხოვთ გამოიყენოთ ეს შენახვამდე! [alt-p]",
        "tooltip-diff": "ტექსტში შეტანილი ცვლილებების ჩვენება. [alt-v]",
        "tooltip-compareselectedversions": "იხილეთ ამ გვერდის  ორ შერჩეულ ვერსიას შორის განსხვავებები.",
        "confirmemail_body_set": "ვიღაცამ, შესაძლოა თქვენ, IP მისამართით $1,\nპროექტში {{SITENAME}} შეცვალა ელ.ფოსტის მისამართი ანგარიშისათვის \"$2\" ამ მისამართით.\n\nიმის დასადასტურებლად, რომ ეს ანგარიში ნამდვილად თქვენ გეკუთვნით\nდა ელ.ფოსტის შესაძლებლობების  გასააქტიურებლად საიტზე {{SITENAME}}, გახსენით ეს ბმული თქვენს ბრაუზერში:\n\n$3\n\nთუ ეს თქვენ *არ* იყავით, მაშინ ელ.ფოსტის მისამართის დასტურების გასაუქმებლად, გადადით ამ ბმულზე:\n\n$5\n\nწერილის ვადის გასვლის თარიღია $4.",
        "confirmemail_invalidated": "ელ-ფოსტის დადასტურება გაუქმდა",
        "invalidateemail": "ელ-ფოსტის დადასტურების გაუქმება",
+       "notificationemail_subject_changed": "{{SITENAME}} რეგისტრირებული იმეილის მისამართები შეიცვალა",
+       "notificationemail_subject_removed": "{{SITENAME}} რეგისტრირებული იმეილის მისამართები წაიშალა",
+       "notificationemail_body_changed": "ვიღაცამ, სავარაუდოდ თქვენ, IP მისამართიდან $1,\n{{SITENAME}}ში შეცვალა „$2“ ანგარიშის იმეილის მისამართი „$3“-ზე.\n\nთუ ეს თქვენ არ იყავით, გთხოვთ დაუკავშირდით საიტის ადმინისტრატორს სასწრაფოდ.",
+       "notificationemail_body_removed": "ვიღაცამ, სავარაუდოდ თქვენ, IP მისამართიდან $1,\n{{SITENAME}}ში წაშალა ანგარიშის „$2“ იმეილის მისამართი.\n\nთუ ეს თქვენ არ იყავით, გთხოვთ დაუკავშირდით საიტის ადმინისტრატორს სასწრაფოდ.",
        "scarytranscludedisabled": "[«Interwiki transcluding» გათიშულია]",
        "scarytranscludefailed": "[$1-თან დაკავშირების შეცდომა]",
        "scarytranscludefailed-httpstatus": "[ვერ მოხერხდა თარგის ჩატვირთვა $1-თვის: HTTP $2]",
        "confirm-watch-top": "დავამატო ეს გვერდი თქვენი კონტროლის სიას?",
        "confirm-unwatch-button": "დიახ",
        "confirm-unwatch-top": "მოვხსნა ეს გვერდი თქვენი კონტროლის სიიდან?",
+       "confirm-rollback-button": "კარგი",
+       "confirm-rollback-top": "დავაბრუნოთ რედაქტირებები ამ გვერდზე?",
        "semicolon-separator": ";&#32;",
        "comma-separator": ",&#32;",
        "colon-separator": ":&#32;",
        "timezone-local": "ლოკალური",
        "duplicate-defaultsort": "'''ყურადღება.'''სორტირების გასაღებს «$2»-ს გააჭრის წინა გასაღებს «$1»-ს.",
        "duplicate-displaytitle": "<strong>ყურადღება:</strong> დისპლეის სათაური \"$2\" განსაზღვრავს ადრე გაცემულ დისპლეის სათაურს \"$1\".",
+       "restricted-displaytitle": "<strong>Warning:</strong> სათაური „$1“ იგნორირებულ იქნა, რადგან ის არ ემთხვევა გვერდის ნამდვილ სათაურს.",
        "invalid-indicator-name": "<strong>შეცდომა:</strong> გვერდის სტატუსის ინდიკატორი <code>name</code> ატრიბუტი არ უნდა იყოს ცარიელი.",
        "version": "ვერსია",
        "version-extensions": "დაყენებული გაფართოებები",
        "tags-delete-not-found": "აღნიშვნა „$1“ არ არსებობს.",
        "tags-delete-too-many-uses": "ტეგი \"$1\" მიღებულია $2 ვერსიებთან, რაც იმას ნიშნავს, რომ იგი არ შეიძლება იყოს წაშლილი",
        "tags-delete-warnings-after-delete": "ტეგი „$1“ წაიშალა, თუმცა აღმოჩენილია შემდეგი {{PLURAL:$2|შეტყობინება|შეტყობინებები}}:",
+       "tags-delete-no-permission": "თქვენ არ გაქვთ შეცვლილი ტეგების წაშლის უფლება.",
        "tags-activate-title": "ტეგის გააქტიურება",
        "tags-activate-question": "თქვენ ცდილობთ დასათაურების გააქტიურებას „$1“.",
        "tags-activate-reason": "მიზეზი:",
        "logentry-protect-protect-cascade": "$1-მ {{GENDER:$2|დაიცვა}} $3 $4 [კასკადური]",
        "logentry-protect-modify": "$1-მ {{GENDER:$2|შეცვალა}} დაცვის დონე $3 $4-სთვის",
        "logentry-protect-modify-cascade": "$1 {{GENDER:$2|შეცვალა}} დაცვის დონე $3 $4 [კასკადური]",
-       "logentry-rights-rights": "á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\94á\83\9aá\83\9bá\83\90 $1 {{GENDER:$2|á\83¨á\83\94á\83£á\83ªá\83\95á\83\90á\83\9aá\83\90}} á\83¯á\83\92á\83£á\83¤á\83\98 $3-á\83¡ $4-დან $5-ზე",
+       "logentry-rights-rights": "á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\94á\83\9aá\83\9bá\83\90 $1 {{GENDER:$2|á\83¨á\83\94á\83ªá\83\95á\83\90á\83\9aá\83\90}} á\83¯á\83\92á\83£á\83¤á\83\98á\83¡ á\83¬á\83\94á\83\95á\83 á\83\9dá\83\91á\83\90 á\83\9bá\83\9dá\83\9bá\83®á\83\9bá\83\90á\83 á\83\94á\83\91á\83\9aá\83\98á\83¡á\83\90á\83\97á\83\95á\83\98á\83¡ {{GENDER:$6|$3}} $4-დან $5-ზე",
        "logentry-rights-rights-legacy": "მომხმარებელმა $1 {{GENDER:$2|შეცვალა}} ჯგუფის წევრობა $3-თვის",
        "logentry-rights-autopromote": "მომხმარებელი $1 ავტომატურად იქნა {{GENDER:$2|გადაყვანილი}} $4–დან $5–ში",
        "logentry-upload-upload": "მომხმარებელმა $1 {{GENDER:$2|ატვირთა}} $3",
        "feedback-useragent": "მომხმარებლის აგენტი:",
        "searchsuggest-search": "ძიება",
        "searchsuggest-containing": "შეიცავს...",
+       "api-error-autoblocked": "თქვენი IP მისამართი ავტომატურად დაიბლოკა, რადგან ის გამოიყენა დაბლოკილმა მომხმარებელმა.",
        "api-error-badaccess-groups": "თქვენ არ გაქვთ ამ ვიკიში ფაილების ატვირთვის უფლება.",
        "api-error-badtoken": "შიდა შეცდომა: ცუდი ტოკენი.",
+       "api-error-blocked": "თქვენთვის რედაქტირება დაბლოკილია.",
        "api-error-copyuploaddisabled": "ამ სერვერზე URL-მისამართის საშუალებით ატვირთვა გამორთულია.",
        "api-error-duplicate": "საიტზე უკვე {{PLURAL:$1|არსებობს სხვა ფაილი|არსებობს სხვა ფაილები}} ანალოგიური შინაარსით.",
        "api-error-duplicate-archive": "საიტზე ადრე {{PLURAL:$1|უკვე იყო ფაილი}} ანალოგიური შინაარსით, მაგრამ {{PLURAL:$1|ის წაიშალა|ისინი წაიშალა}}.",
        "api-error-unknownerror": "უცნობი შეცდომა: „$1“.",
        "api-error-uploaddisabled": "ატვირთვის მექანიზმი ამ ვიკიზე გამორთულია",
        "api-error-verification-error": "ეს ფაილი ან რაიმე შეცდომას შეიცავს, ან არ აქვს სახელის გაფართოება.",
+       "api-error-was-deleted": "ფაილი ამ სახელწოდებით ადრე აიტვირთა და შემდეგ წაიშალა.",
        "duration-seconds": "$1 {{PLURAL:$1|წამი|წამი}}",
        "duration-minutes": "$1 {{PLURAL:$1|წუთი|წუთი}}",
        "duration-hours": "$1 {{PLURAL:$1|საათი|საათი}}",
        "mw-widgets-dateinput-placeholder-month": "წწწწ-თთ",
        "mw-widgets-titleinput-description-new-page": "გვერდი ჯერ არ არსებობს",
        "mw-widgets-titleinput-description-redirect": "გადამისამართება $1-ზე",
-       "api-error-blacklisted": "გთხოვთ, აირჩიეთ სხვა, აღწერილობითი სათაური.",
        "sessionmanager-tie": "შეუძლებელია მრავალი მოთხოვნის ავთენთიფიკაციის ტიპების გაერთიანება: $1.",
        "sessionprovider-generic": "$1 სესიები",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "cookie-სთან დაკავშირებული სესიები",
        "sessionprovider-nocookies": "შესაძლოა ქუქები გათიშულია. გთხოვთ ჩართეთ და სცადეთ განმეორებით.",
-       "randomrootpage": "შემთხვევითი ძირეული გვერდი"
+       "randomrootpage": "შემთხვევითი ძირეული გვერდი",
+       "log-action-filter-block": "ბლოკირების ტიპი:",
+       "log-action-filter-contentmodel": "შეინაარსის მოდელის მოდერაციის ტიპი:",
+       "log-action-filter-delete": "წაშლის ტიპი:",
+       "log-action-filter-import": "იმპორტის ტიპი:",
+       "log-action-filter-managetags": "ტეგის ცვლილების ტიპი:",
+       "log-action-filter-move": "გადატანის ტიპი:",
+       "log-action-filter-newusers": "ანგარიშის შექმნის ტიპი:",
+       "log-action-filter-patrol": "შემოწმების ტიპი:",
+       "log-action-filter-protect": "დაცვის ტიპი:",
+       "log-action-filter-rights": "უფლების შეცვლის ტიპი:",
+       "log-action-filter-suppress": "დამალვის ტიპი:",
+       "log-action-filter-upload": "ატვირთვის ტიპი:",
+       "log-action-filter-all": "ყველა",
+       "log-action-filter-block-block": "დაბლოკვა",
+       "log-action-filter-block-reblock": "ბლოკირების შეცვლა",
+       "log-action-filter-block-unblock": "განბლოკვა",
+       "log-action-filter-contentmodel-change": "შინაარსის მოდელის შეცვლა",
+       "log-action-filter-contentmodel-new": "გვერდის შექმნა არასტანდარტული შინაარსის მოდელით",
+       "log-action-filter-delete-delete": "გვერდის წაშლა",
+       "log-action-filter-delete-restore": "გვერდის აღდგენა",
+       "log-action-filter-delete-event": "ჯურნალის ჩანაწერის წაშლა",
+       "log-action-filter-delete-revision": "ვერსიის წაშლა",
+       "log-action-filter-import-interwiki": "Transwiki-ს იმპორტი",
+       "log-action-filter-import-upload": "XML ატვირთვიდან იმპორტი",
+       "log-action-filter-managetags-create": "ტეგის შექმნა",
+       "log-action-filter-managetags-delete": "ტეგის წაშლა",
+       "log-action-filter-managetags-activate": "ტეგის აქტივაცია",
+       "log-action-filter-managetags-deactivate": "ტეგის დეაქტივაცია",
+       "log-action-filter-move-move": "გადატანა გადამისამართებების გადაწერის გარეშე",
+       "log-action-filter-move-move_redir": "გადატანა გადამისამართებების გადაწერით",
+       "log-action-filter-newusers-create": "შექმნა ანონიმური მომხმარებლის მიერ",
+       "log-action-filter-newusers-create2": "დარეგისტრირებული მომხმარებლის შექმნა",
+       "log-action-filter-newusers-autocreate": "ავტომატური შექმნა",
+       "log-action-filter-newusers-byemail": "პაროლით შექმნა, რომელიც გამოიგზავნა იმეილით",
+       "log-action-filter-patrol-patrol": "ხელით შემოწმება",
+       "log-action-filter-patrol-autopatrol": "ავტომატური შემოწმება",
+       "log-action-filter-protect-protect": "დაცვა",
+       "log-action-filter-protect-modify": "დაცვის შეცვლა",
+       "log-action-filter-protect-unprotect": "დაცვის მოხსნა",
+       "log-action-filter-protect-move_prot": "დაცვა გადატანისაგან",
+       "log-action-filter-rights-rights": "ხელით შეცვლა",
+       "log-action-filter-rights-autopromote": "ავტომატური შეცვლა",
+       "log-action-filter-suppress-event": "ჟურნალის დამალვა",
+       "log-action-filter-suppress-revision": "ცვლილების დამალვა",
+       "log-action-filter-suppress-delete": "გვერდის დამალვა",
+       "log-action-filter-suppress-block": "მომხმარებლის დამალვა ბლოკით",
+       "log-action-filter-suppress-reblock": "მომხმარებლის დამალვა ხელახლა ბლოკირებით",
+       "log-action-filter-upload-upload": "ახალი ატვირთვა",
+       "log-action-filter-upload-overwrite": "ხელახლა ატვირთვა",
+       "authmanager-authn-not-in-progress": "აუტენტიფიკაცია არ მიმდინარეობს ან სესიის მონაცემი დაიკარგა. გთხოვთ დაიწყეთ თავიდან.",
+       "authmanager-authn-no-primary": "მოწოდებული მონაცემები ვერ იქნა აუტენტიფიცირებული.",
+       "authmanager-authn-no-local-user": "მოწოდებული მონაცემები არ უკავშირდება არცერთ მომხმარებელს ამ ვიკიზე.",
+       "authmanager-create-disabled": "ანგარიშის შექმნა გათიშულია.",
+       "authmanager-create-from-login": "ანგარიშის შესაქმნელად, გთხოვთ შეავსეთ ქვემოთ მოცემული ველები.",
+       "authmanager-create-not-in-progress": "ანგარიშის შექმა არ მიმდინარეობს ან სესიის მონაცემი დაიკარგა. გთხოვთ დაიწყეთ თავიდან.",
+       "authmanager-create-no-primary": "მოწოდებული მონაცემები არ გამოდგა ანგარიშის შესაქმნელად.",
+       "authmanager-link-no-primary": "მოწოდებული მონაცემები არ გამოდგა ანგარიშის დასაკავშირებლად.",
+       "authmanager-link-not-in-progress": "ანგარიშის დაკავშირება არ მიმდინარეობს ან სესიის მონაცემი დაიკარგა. გთხოვთ დაიწყეთ თავიდან.",
+       "authmanager-authplugin-setpass-failed-title": "პაროლის ცვლილება ვერ განხორციელდა",
+       "authmanager-authplugin-setpass-failed-message": "აუთენთიფიკაციის პლაგინმა უარყო პაროლის ცვლილება.",
+       "authmanager-authplugin-create-fail": "აუთენთიფიკაციის პლაგინმა უარყო ანგარიშის შექმნა.",
+       "authmanager-authplugin-setpass-denied": "აუთენთიფიკაციის პლაგინი არ იძლევა პაროლების გამოცვლის უფლებას.",
+       "authmanager-authplugin-setpass-bad-domain": "არასწორი დომეინი.",
+       "authmanager-autocreate-noperm": "ავტომატური ანგარიშის შექმნა არ არის ნებადართული.",
+       "authmanager-autocreate-exception": "ავტომატური ანგარიშის შექმნა დროებით გათიშულია ადრინდელი ხარვეზების გამო.",
+       "authmanager-userdoesnotexist": "მომხმარებლის ანგარიში „$1“ არ არის რეგისტრირებული",
+       "authmanager-username-help": "მომხმარებლის სახელი აუთენთიფიკაციისთვის.",
+       "authmanager-password-help": "პაროლი აუთენთიფიკაციისთვის.",
+       "authmanager-domain-help": "დომეინი გარე აუთენთიფიკაციისთვის.",
+       "authmanager-retype-help": "დასადასტურებლად კვლავ შეიყვანეთ პაროლი.",
+       "authmanager-email-label": "ელ. ფოსტა",
+       "authmanager-email-help": "ელ. ფოსტის მისამართი",
+       "authmanager-realname-label": "ნამდვილი სახელი",
+       "authmanager-realname-help": "მომხმარებლის ნამდვილი სახელი",
+       "authmanager-provider-password": "პაროლზე დაფუძნებული აუთენთიფიკაცია",
+       "authmanager-provider-password-domain": "პაროლზე და დომეინზე დაფუძნებული აუთენთიფიკაცია",
+       "authmanager-provider-temporarypassword": "დროებითი პაროლი",
+       "authprovider-confirmlink-request-label": "ანგარიშები, რომლებიც დაკავშირებული უნდა იყოს",
+       "authprovider-confirmlink-success-line": "$1: დაუკავშირდა წარმატებით.",
+       "authprovider-confirmlink-failed": "ანგარიშის დაკავშირება არ შესრულდა სრულად: $1",
+       "authprovider-confirmlink-ok-help": "გააგრძელე დაკავშირების ჩავარდნის შეტყობინებების ჩვენების შემდეგ.",
+       "authprovider-resetpass-skip-label": "გამოტოვება",
+       "authprovider-resetpass-skip-help": "გამოტოვეთ პაროლის შეცვლის პროცესი.",
+       "authform-nosession-login": "ავტორიზაციამ წარმატებით ჩაიარა, თუმცა თქვენი ბრაუზერი ვერ ახერხებს მის „დამახსოვრებას“.\n\n$1",
+       "authform-nosession-signup": "ანგარიში შეიქმნა, თუმცა თქვენი ბრაუზერი ვერ ახერხებს მის „დამახსოვრებას“.\n\n$1",
+       "authform-newtoken": "არ არის ტოკენი. $1",
+       "authform-notoken": "არ არის ტოკენი",
+       "authform-wrongtoken": "არასწორი ტოკენი",
+       "specialpage-securitylevel-not-allowed-title": "არ არის ნებადართული",
+       "specialpage-securitylevel-not-allowed": "უკაცრავად, თქვე არ შეგიძლიათ ამ გვერდის გამოყენება, რადგან თქვენი იდენთიფიკაცია არ არის დადასტურებული.",
+       "authpage-cannot-login": "შეუძლებელია ავტორიზაციის დაწყება.",
+       "authpage-cannot-login-continue": "შეუძლებელია ავტორიზაციის გაგრძელება. სავარაუდოდ, თქვენი სესიის დრო ამოიწურა.",
+       "authpage-cannot-create": "შეუძლებელია ანგარიშის შექმნის დაწყება.",
+       "authpage-cannot-create-continue": "შეუძლებელია ანგარიშის შექმნის გაგრძელება. სავარაუდოდ, თქვენი სესიის დრო ამოიწურა.",
+       "authpage-cannot-link": "შეუძლებელია ანგარიშის დაკავშირების დაწყება.",
+       "authpage-cannot-link-continue": "შეუძლებელია ანგარიშის დაკავშირების გაგრძელება. სავარაუდოდ, თქვენი სესიის დრო ამოიწურა.",
+       "cannotauth-not-allowed-title": "ნებართვა უარყოფილია",
+       "cannotauth-not-allowed": "თქვენ არ გაქვთ ამ გვერდის გამოყენების უფლება",
+       "changecredentials": "მონაცემების შეცვლა",
+       "changecredentials-submit": "მონაცემების შეცვლა",
+       "changecredentials-invalidsubpage": "$1 არ არის ვალიდური მონაცემის ტიპი.",
+       "changecredentials-success": "თქვენი მონაცემები შეიცვალა.",
+       "removecredentials": "მონაცემების წაშლა",
+       "removecredentials-submit": "მონაცემების წაშლა",
+       "removecredentials-invalidsubpage": "$1 არ არის ვალიდური მონაცემის ტიპი.",
+       "removecredentials-success": "თქვენი მონაცემები წაიშალა.",
+       "credentialsform-provider": "მონაცემის ტიპი:",
+       "credentialsform-account": "ანგარიშის სახელი:",
+       "cannotlink-no-provider-title": "არ არის დაკავშირებული ანგარიშები",
+       "cannotlink-no-provider": "არ არის დაკავშირებული ანგარიშები.",
+       "linkaccounts": "ანგარიშების დაკავშირება",
+       "linkaccounts-success-text": "ანგარიში დაკავშირებულია.",
+       "linkaccounts-submit": "ანგარიშების დაკავშირება",
+       "unlinkaccounts": "ანგარიშებისთვის დაკავშირების მოშორება",
+       "unlinkaccounts-success": "ანგარიშს მოეხსნა დაკავშირება.",
+       "userjsispublic": "შენიშვნა: ჯავასკრიპტის ქვეგვერდები არ უნდა შეიცავდეს პირადულ მონაცემებს, რადგან მას სხვა მომხმარებლებიც ხედავენ.",
+       "usercssispublic": "შენიშვნა: CSS ქვეგვერდები არ უნდა შეიცავდეს პირადულ მონაცემებს, რადგან მას სხვა მომხმარებლებიც ხედავენ."
 }
index e8d7ee9..b01984c 100644 (file)
        "history-show-deleted": "صرفی حذف شدہ",
        "histfirst": "قدیم ترین",
        "histlast": "تازہ ترین",
-       "historysize": "({{PLURAL:$1|1 بائٹ|$1 بائٹس}})",
+       "historysize": "({{PLURAL:$1|1 بائٹ|$1 بائٹ}})",
        "historyempty": "(خالی)",
        "history-feed-title": "تاریخچۂ نظرثانی",
        "history-feed-description": "ویکیپیڈیا ھیہ صفحو تاریخچۂ نظرثانی",
index f1d8048..5eda368 100644 (file)
@@ -6,7 +6,8 @@
                        "Marmase",
                        "Mirzali",
                        "아라",
-                       "Macofe"
+                       "Macofe",
+                       "Kumkumuk"
                ]
        },
        "tog-underline": "Bınê gırey de xete bonce:",
        "yourname": "Namê karberi:",
        "yourpassword": "Parola:",
        "yourpasswordagain": "Parola tekrar ke:",
-       "remembermypassword": "Cıkotena mı na komputeri de bia ho viri (seba tewr jêde $1 {{PLURAL:$1|roze|rozu}})",
        "yourdomainname": "Bandıra sıma:",
        "externaldberror": "Cıfeteliyaisê naskerdene de ya xeta esta ya ki tebera vırastena hesabê sıma rê destur çino.",
        "login": "Cı kuye",
        "retypenew": "Parola newiye tekrar ke:",
        "resetpass_submit": "Parola ayar ke u cı kuye",
        "changepassword-success": "Parola sıma ebe serkotene vurriye! Nıka hesabê sıma beno ra...",
+       "botpasswords-label-cancel": "Bıtexelne",
        "resetpass_forbidden": "Paroley nêşikinê bıvurniyê",
        "resetpass-submit-loggedin": "Parola bıvurne",
-       "resetpass-submit-cancel": "Bıtexelne",
+       "resetpass-submit-cancel": "Peyd kı",
        "resetpass-temp-password": "Parola vêrdiye:",
        "bold_sample": "Nusto qolınd",
        "bold_tip": "Nusto qolınd",
        "watchthis": "Na pele de şêr ke",
        "savearticle": "Pele qeyd ke",
        "preview": "Verqayt",
-       "showpreview": "Verqayti bıasne",
+       "showpreview": "Verqayti bımocne",
        "showdiff": "Vurnaisun bıasne",
        "anoneditwarning": "'''Diqet:''' Tı cınêkota.\nTarixê vurnaena na pele de, hurêndia leqeme de numra tuya IPy qeyd bena.",
        "missingcommenttext": "Cêr de jü xulasa bınuse.",
        "upload-source": "Dosya çımey",
        "sourcefilename": "Namê dosya çımey:",
        "watchthisupload": "Na dosya de şêr ke",
+       "upload-dialog-button-cancel": "Bıtexelne",
        "license": "Lisans:",
        "license-header": "Lisansdais",
        "imgfile": "dosya",
        "blanknamespace": "(Ser)",
        "contributions": "İştıraqê {{GENDER:$1|karber}}i",
        "contributions-title": "$1 de iştırakê karberi",
-       "mycontris": "İştıraqi",
+       "mycontris": "İştıraki",
        "contribsub2": "Serba $1 ($2)",
        "uctop": "(rocane)",
        "month": "Asme ra (u ravêr):",
        "blocklink": "kilıt ke",
        "unblocklink": "ra ke",
        "change-blocklink": "mani bıvurne",
-       "contribslink": "iştıraqi",
+       "contribslink": "iştıraki",
        "emaillink": "e-poste bırusne",
        "autoblocker": "Sıma otomatikmen kılit biy, çıke adresa sımawa ''IP''y terefê \"[[User:$1|$1]]\" gurenina.\nSebebê kılitbiyaena $1'i \"$2\"o",
        "blocklogpage": "Protokolê kilıti",
        "tooltip-ca-nstab-category": "Pela kategoriye bıvêne",
        "tooltip-minoredit": "Ney jê vurnaiso qıc isaret ke",
        "tooltip-save": "Vurnaisunê ho qeyd ke",
+       "tooltip-publish": "Pele xo bışevekne",
        "tooltip-preview": "Kerem ke, vurnaisunê ho qeyd-kerdene ra ravêr be verqayt bıasne!",
        "tooltip-diff": "Kamci vurnayışi ke to meqale de kerdê, ninan basne.",
        "tooltip-compareselectedversions": "Ferqunê wertê ni dı nımınunê weçinıtu bıvêne.",
        "compare-page1": "Pele 1",
        "compare-page2": "Pele 2",
        "rightsnone": "(qet jü)",
-       "revdelete-summary": "xulasa vurnaene"
+       "revdelete-summary": "xulasa vurnaene",
+       "feedback-cancel": "Bıtexelne"
 }
index d80554d..08b84e8 100644 (file)
        "yourpasswordagain": "Құпия сөзді қайталаңыз:",
        "createacct-yourpasswordagain": "Құпия сөзді құптаңыз",
        "createacct-yourpasswordagain-ph": "Құпия сөзіңізді қайтадан енгізіңіз",
-       "remembermypassword": "Тіркелгімді осы браузерде ұмытпа (ең көбі $1 {{PLURAL:$1|күн|күн}})",
        "userlogin-remembermypassword": "Мені жүйеде сақтап қою",
        "userlogin-signwithsecure": "Қауіпсіз байланысуды қолдану",
        "cannotloginnow-title": "Қазір шығу мүмкін емес",
        "createacct-reason-ph": "Неге басқа тіркегі жасамақшысыз",
        "createacct-submit": "Тіркелгіңізді жасаңыз",
        "createacct-another-submit": "Тіркелгі жасау",
+       "createacct-continue-submit": "Тіркелуді жалғастыру",
        "createacct-benefit-heading": "{{SITENAME}} сіздермен жасалады.",
        "createacct-benefit-body1": "{{PLURAL:$1|өңдеме|өңдеме}}",
        "createacct-benefit-body2": "{{PLURAL:$1|бет|бет}}",
        "passwordreset-emailtext-user": "$1 есімді қатысушы {{SITENAME}} сайтында ($4) құпия сөзді өзгертуге өтініш білдірді. Мына қатысушы {{PLURAL:$3|аккаунт|аккаунттар}} осы електронды почта қатысты:\n\n$2\n\n{{PLURAL:$3|Бұл уақытша құпия сөз|Бұл уақытша құпия сөздер}} {{PLURAL:$5|бір күнде|$5 күнде}}уақыты аяқталады.\nСіз кіруіңіз және жаңа құпия сөзді таңдауыңыз керек. Егер бұл өтінішті басқа біреу жасаса, немесе сіз  бұрынғы құпия сөзіңізді еске түсірсеңіз, және құпия сөзді ауыстыруды қаламасаңыз, сіз бұл хабарламаны ескермей және бұрыңғы құпия сөзді қолдана беруіңізге болады.",
        "passwordreset-emailelement": "Қатысушы есімі: \n$1\n\nУақытша құпия сөз: \n$2",
        "passwordreset-emailsentemail": "Бұл email мекенжайы тіркелгіңізге байланысқан, сол себепті құпия сөзді өзгерту электронды пошта арқылы жөнелтіледі.",
-       "passwordreset-emailsent-capture": "Құпия сөзді өзгерту электронды пошта арқылы жөнелтілді, ол төменде көрсетілген.",
-       "passwordreset-emailerror-capture": "Құпиясөзді өзгерту электрон хаты жасалды, ол төменде көрсетілген, бірақ ол {{GENDER:$2|қатысушыға}} жөнелтілмеді: $1",
        "changeemail": "Е-пошта мекенжайын өзгерту немесе аластау",
        "changeemail-header": "Е-пошта мекен-жайының өзгертілуі",
        "changeemail-no-info": "Бұл бетке тікелей ену үшін жүйеге кіруіңіз керек.",
        "minoredit": "Бұл шағын өңдеме",
        "watchthis": "Бұл бетті бақылау",
        "savearticle": "Бетті сақтау",
+       "savechanges": "Өзгерістерді сақтау",
+       "publishpage": "Бетті жариялау",
+       "publishchanges": "Өзгерістерді жариялау",
        "preview": "Қарап шығу",
        "showpreview": "Алдын-ала қарау",
        "showdiff": "Өзгерістерді көрсет",
        "undo-nochange": "Өңдеме әлдеқашан жоққа шығарылғаны анықталды.",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|т]]) істеген нөмір $1 нұсқасын жоққа шығарды",
        "undo-summary-username-hidden": "$1 нұсқасын жасырылған қатысушы жоққа шығарды",
-       "cantcreateaccounttitle": "Тіркелгі жасалмады",
        "cantcreateaccount-text": "Бұл IP мекенжайдан (<strong>$1</strong>) жаңа тіркелгі жасауын [[User:$3|$3]] бұғаттаған.\n\n$3 есімді қатысушының келтіріген себебі: <em>$2</em>",
        "cantcreateaccount-range-text": "<strong>$1</strong> ауқымындағы IP мекенжайдан сіздің IP мекенжайыңыз да кіреді (<strong>$4</strong>) жаңа тіркелгі жасауын [[User:$3|$3]] бұғаттаған.\n\n$3 есімді қатысушының келтіріген себебі: ''$2'",
        "viewpagelogs": "Бұл беттің журнал оқиғаларын қарау",
        "recentchanges-label-minor": "Бұл шағын өңдеме",
        "recentchanges-label-bot": "Бұл өңдемені бот жасады.",
        "recentchanges-label-unpatrolled": "Бұл өңдеме әлі тексеруден өтпеді.",
-       "recentchanges-label-plusminus": "Байт бойынша беттің өзгеріс өлшемі",
+       "recentchanges-label-plusminus": "Байт бойынша беттің өзгеріс мөлшері",
        "recentchanges-legend-heading": "<strong>Шартты белгілер:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (қ: [[Special:NewPages|бөлек бетте]])",
        "recentchanges-legend-plusminus": "(<em>±123</em>)",
        "sp-contributions-username": "IP-мекенжайы немесе қатысушы аты:",
        "sp-contributions-toponly": "Өңдемелердің тек соңғы нұсқаларын көрсету",
        "sp-contributions-newonly": "Бет бастау өңдемелерін ғана көрсету",
+       "sp-contributions-hideminor": "Шағын өңдемелерді жасыру",
        "sp-contributions-submit": "Іздеу",
        "whatlinkshere": "Мұнда сілтейтін беттер",
        "whatlinkshere-title": "$1 дегенге сілтейтін беттер",
        "tooltip-ca-nstab-category": "Санат бетін қарау",
        "tooltip-minoredit": "Бұны шағын өңдеме деп белгілеу",
        "tooltip-save": "Жасаған өзгерістеріңізді сақтау",
+       "tooltip-publish": "Өзгерістеріңізді жариялаңыз",
        "tooltip-preview": "Сақтаудың алдынан жасаған өзгерістеріңізді қарап шығыңыз!",
        "tooltip-diff": "Мәтінге қандай өзгерістерді жасағаныңызды қарау.",
        "tooltip-compareselectedversions": "Беттің екі бөлектенген нұсқасы айырмасын қарау.",
        "mw-widgets-dateinput-placeholder-month": "ЖЖЖЖ-АА",
        "mw-widgets-titleinput-description-new-page": "бет жоқ екен",
        "mw-widgets-titleinput-description-redirect": "$1 дегенге бағыттату",
-       "api-error-blacklisted": "Өтініш, сипаттама атауының басқасын таңдаңыз."
+       "log-action-filter-protect": "Қорғау түрі"
 }
index 6059e1a..282444d 100644 (file)
@@ -15,7 +15,8 @@
                        "តឹក ប៊ុនលី",
                        "វ័ណថារិទ្ធ",
                        "아라",
-                       "Macofe"
+                       "Macofe",
+                       "Dcljr"
                ]
        },
        "tog-underline": "គូសបន្ទាត់ក្រោម​តំណភ្ជាប់៖",
        "passwordreset-emailtext-user": "អ្នកប្រើប្រាស់ $1 នៅក្នុង {{SITENAME}} បានស្នើសុំស្ដារពាក្យសម្ងាត់របស់អ្នកនៅក្នុង {{SITENAME}} ($4)។\n {{PLURAL:$3|គណនី|គណនី}}អ្នកប្រើប្រាស់ដូចតទៅនេះមានជាប់ទាក់ទិននឹងអាសយដ្ឋានអ៊ីមែលនេះ៖\n\n$2\n\n{{PLURAL:$3|ពាក្យសម្ងាត់បណ្ដោះអាសន្ននេះ|ពាក្យសម្ងាត់បណ្ដោះអាសន្នទាំងនេះ}} និងហួសសុពលភាពក្នុងរយៈពេល {{PLURAL:$5|មួយថ្ងៃ|$5 ថ្ងៃ}}។\nយកល្អអ្នកគួរតែកត់ឈ្មោះចូលរួចជ្រើសរើសពាក្យសម្ងាត់ថ្មីមួយ។ ប្រសិនបើមាននរណាម្នាក់ផ្សេងធ្វើការស្នើសុំនេះ \nឬប្រសិនបើអ្នកនឹកឃើញពាក្យសម្ងាត់ដើមរបស់អ្នក ហើយអ្នកមិនប្រាថ្នាផ្លាស់ប្ដូរវាទៀតទេនោះ អ្នកគ្រាន់តែ\nបំភ្លេចអំពីសារមួយនេះ ហើយបន្តប្រើប្រាស់ពាក្យសម្ងាត់ចាស់របស់អ្នកទៅបានហើយ។",
        "passwordreset-emailelement": "អត្តនាម៖ \n$1\n\nពាក្យសម្ងាត់បណ្ដោះអាសន្ន៖ \n$2",
        "passwordreset-emailsentemail": "បើសិនជានេះអាសយដ្ឋានអ៊ីមែលដែលត្រូវបានចុះឈ្មោះសម្រាប់គណនីរបស់អ្នក នោះអ៊ីមែលសម្រាប់ស្ដារពាក្យសម្ងាត់មួយនឹងត្រូវបានផ្ញើទៅ។",
-       "passwordreset-emailsent-capture": "អ៊ីមែលស្ដារពាក្យសម្ងាត់មួយដូចបង្ហាញខាងក្រោមត្រូវបានផ្ញើទៅហើយ។",
-       "passwordreset-emailerror-capture": "អ៊ីមែលស្ដារពាក្យសម្ងាត់មួយដូចបង្ហាញខាងក្រោមត្រូវបានបង្កើតហើយ ប៉ុន្តែការផ្ញើទៅកាន់ {{GENDER:$2|អ្នកប្រើប្រាស់}}មិនបានសំរេចទេ៖ $1",
        "changeemail": "ផ្លាស់ប្ដូរឬលុបអាសយដ្ឋានអ៊ីមែល",
        "changeemail-header": "សូមបំពេញសំណុំបែបបទនេះដើម្បីផ្លាស់ប្ដូរអាសយដ្ឋានអ៊ីមែល។ បើសិនជាអ្នកចង់លុបការតភ្ជាប់អាសយដ្ឋានអ៊ីមែលពីគណនីរបស់អ្នក សូមដាក់ប្រឡោះអាសយដ្ឋានថ្មីអោយនៅទំនេរពេលសម្រេចដាក់សំណុំបែបបទ។",
        "changeemail-no-info": "អ្នក​ចាំបាច់​ត្រូវតែ​កត់ឈ្មោះចូល ដើម្បី​ចូលទៅកាន់​ទំព័រ​នេះ​ដោយផ្ទាល់​។",
        "undo-norev": "កំណែ​មិន​អាច​មិន​ធ្វើ​ឡើង​វិញ​បាន​ទេ​ ពីព្រោះ​វា​មិន​មាន​ឬ​ត្រូវ​បាន​លុប​បាត់​ទៅ​ហើយ​។",
        "undo-summary": "មិន​ធ្វើ​វិញ​នូវ​កំណែ​ប្រែ $1 ដោយ​ [[Special:Contributions/$2|$2]] ([[User talk:$2|ការពិភាក្សា​]])",
        "undo-summary-username-hidden": "មិន​ធ្វើ​វិញ​នូវ​កំណែ​ប្រែ $1 ដោយអ្នកប្រើប្រាស់លាក់ឈ្មោះ",
-       "cantcreateaccounttitle": "មិនអាចបង្កើតគណនីបានទេ",
        "cantcreateaccount-text": "ការបង្កើតគណនីពីអាសយដ្ឋាន IP ('''$1''') នេះ ត្រូវបានរារាំងដោយ [[User:$3|$3]]។\n\nហេតុផលដែលត្រូវលើកឡើងដោយ $3 គឺ ''$2''",
        "viewpagelogs": "មើលកំណត់ហេតុសម្រាប់ទំព័រនេះ",
        "nohistory": "មិនមានប្រវត្តិកំណែប្រែ​ចំពោះទំព័រនេះ។",
        "enotif_subject_moved": "ទំព័រ {{SITENAME}} មានចំណងជើងថា $1 ត្រូវបាន {{GENDER:$2|ប្ដូរទីតាំង}} ដោយ $2",
        "enotif_subject_restored": "ទំព័រ {{SITENAME}} មានចំណងជើងថា $1 ត្រូវបាន {{GENDER:$2|ស្ដារឡើងវិញ}} ដោយ $2",
        "enotif_subject_changed": "ទំព័រ {{SITENAME}} មានចំណងជើងថា $1 ត្រូវបាន {{GENDER:$2|ផ្លាស់ប្ដូរ}} ដោយ $2",
-       "enotif_body_intro_deleted": "ទំព័រ {{SITENAME}} មានចំណងជើងថា $1 ត្រូវបាន {{GENDER:$2|លុបចោល}} នៅ $PAGEEDITDATE ដោយ $2។ សូមអាន $3។",
+       "enotif_body_intro_deleted": "ទំព័រ {{SITENAME}} មានចំណងជើងថា $1 ត្រូវបាន {{GENDER:$2|លុបចោល}} នៅ $PAGEEDITDATE ដោយ $2 ។ សូមអាន $3 ។",
        "enotif_body_intro_created": "ទំព័រ {{SITENAME}} មានចំណងជើងថា $1 ត្រូវបាន {{GENDER:$2|បង្កើត}} នៅ $PAGEEDITDATE ដោយ $2។ សូមអាន $3 សម្រាប់កំណែបច្ចុប្បន្ន។",
        "enotif_body_intro_moved": "ទំព័រ {{SITENAME}} មានចំណងជើងថា $1 ត្រូវបាន {{GENDER:$2|ប្ដូរទីតាំង}} នៅ $PAGEEDITDATE ដោយ $2។ សូមអាន $3 សម្រាប់កំណែបច្ចុប្បន្ន។",
        "enotif_body_intro_restored": "ទំព័រ {{SITENAME}} មានចំណងជើងថា $1 ត្រូវបាន {{GENDER:$2|ស្ដារឡើងវិញ}} នៅ $PAGEEDITDATE ដោយ $2។ សូមអាន $3 សម្រាប់កំណែបច្ចុប្បន្ន។",
        "special-characters-title-minus": "សញ្ញាដក",
        "mw-widgets-dateinput-no-date": "គ្មានកាលបរិច្ឆេទត្រូវបានជ្រើសរើស",
        "mw-widgets-titleinput-description-new-page": "ទំព័រមិនទាន់មាននៅឡើយទេ",
-       "mw-widgets-titleinput-description-redirect": "បញ្ជូនបន្តទៅ $1",
-       "api-error-blacklisted": "សូមជ្រើសរើសឈ្មោះផ្សេងដែលក្បោះក្បាយជាង។"
+       "mw-widgets-titleinput-description-redirect": "បញ្ជូនបន្តទៅ $1"
 }
index b594b92..5d2c25c 100644 (file)
                        "Ananth subray",
                        "MarcoAurelio",
                        "Macofe",
-                       "రహ్మానుద్దీన్"
+                       "రహ్మానుద్దీన్",
+                       "ಶಿವಕುಮಾರ್ ನಾಯಕ್",
+                       "Yogesh"
                ]
        },
        "tog-underline": "ಕೊಂಡಿಗಳ ಕೆಳಗೆ ಗೆರೆ ತೋರಿಸಿ",
        "tog-hideminor": "ಇತ್ತೀಚಿನ ಬದಲಾವಣೆಗಳಲ್ಲಿ ಚಿಕ್ಕಪುಟ್ಟ ಸಂಪಾದನೆಗಳನ್ನು ಅಡಗಿಸಿ",
        "tog-hidepatrolled": "ಪಹರೆಯಲ್ಲಿ ಆದ ಸಂಪಾದನೆಗಳನ್ನು ಇತ್ತೀಚೆಗಿನ ಬದಲಾವಣೆಗಳಲ್ಲಿ ಅಡಗಿಸು",
        "tog-newpageshidepatrolled": "ಪಹರೆಯಲ್ಲಿ ಆದ ಪುಟಗಳನ್ನು ಹೊಸ ಪುಟಗಳ ಪಟ್ಟಿಯಲ್ಲಿ ಅಡಗಿಸು",
-       "tog-extendwatchlist": "à²\95à³\87ವಲ à²\87ತà³\8dತà³\80à²\9aà³\86à²\97ಿನ à²¬à²¦à²²à²¾à²µà²£à³\86à²\97ಳಲà³\8dಲದà³\86, à²¸à²\82ಬà²\82ಧಿತ à²\8eಲà³\8dಲಾ à²¬à²¦à²²à²¾à²µà²£à³\86à²\97ಳನà³\8dನà³\81 à²¤à³\8bರà³\81ವà²\82ತà³\86 à²ªà²\9fà³\8dà²\9fಿಯನà³\8dನà³\81 à²µà²¿à²¸à³\8dತರಿಸಿ",
+       "tog-extendwatchlist": "ಕೇವಲ ಇತ್ತೀಚೆಗಿನ ಬದಲಾವಣೆಗಳಲ್ಲದೆ, ಎಲ್ಲಾ ಬದಲಾವಣೆಗಳನ್ನು ತೋರುವಂತೆ ಪಟ್ಟಿಯನ್ನು ವಿಸ್ತರಿಸಿ",
        "tog-usenewrc": "ಹೆಚ್ಚು ವರ್ಧಿಸಲಾದ ಇತ್ತೀಚಿನ ಬದಲಾವಣೆಗಳು ಪುಟ ಬಳಸು",
        "tog-numberheadings": "ತಲೆಬರಹಗಳಿಗೆ ಅಂಕಿಗಳನ್ನು ತೋರಿಸು",
        "tog-showtoolbar": "ಸಂಪಾದನೆಯ ಉಪಕರಣಗಳ ಪಟ್ಟಿಯನ್ನು ತೋರು",
        "faqpage": "Project:ಸಾಮಾನ್ಯವಾಗಿ ಕೇಳಲಾಗುವ ಪ್ರಶ್ನೆಗಳು",
        "actions": "ಕ್ರಿಯೆಗಳು",
        "namespaces": "ನಾಮವರ್ಗಗಳು",
-       "variants": "ಹಲವà³\81",
-       "navigation-heading": "ಸà²\82à²\9aರಣà³\86 à²®à³\86ನà³\81",
+       "variants": "ವà³\8dಯತà³\8dಯಾಸ à²¹à³\8aà²\82ದಿರà³\81ವ",
+       "navigation-heading": "ಸà²\82à²\9aರಣà³\86 à²ªà²\9fà³\8dà²\9fಿ",
        "errorpagetitle": "ದೋಷ",
        "returnto": "$1 ಗೆ ಹಿಂತಿರುಗಿ.",
        "tagline": "{{SITENAME}} ಇಂದ",
        "history_short": "ಇತಿಹಾಸ",
        "updatedmarker": "ನನ್ನ ಕೊನೆಯ ವೀಕ್ಷಣೆಯ ನಂತರ ಬದಲಾಗಿರುವವು",
        "printableversion": "ಪ್ರಿಂಟ್ ಆವೃತ್ತಿ",
-       "permalink": "ಸà³\8dಥಿರ à²¸à²\82ಪರà³\8dà²\95",
+       "permalink": "ಸà³\8dಥಿರ à²\95à³\8aà²\82ಡಿ",
        "print": "ಮುದ್ರಿಸು",
        "view": "ನೋಟ",
        "view-foreign": "$1 ರಲ್ಲಿ ನೋಡಿ",
        "undelete_short": "{{PLURAL:$1|ಒಂದು ಸಂಪಾದನೆಯ|$1 ಸಂಪಾದನೆಗಳ}} ಅಳಿಸುವಿಕೆಯನ್ನು ತೊಡೆದುಹಾಕು",
        "viewdeleted_short": "ನೋಟ {{PLURAL:$1|೧ ಅಳಿಸಲ್ಪಟ್ಟ ಸಂಪಾದನೆ|$1 ಅಳಿಸಲ್ಪಟ್ಟ ಸಂಪಾದನೆಗಳು}}",
        "protect": "ಸಂರಕ್ಷಿಸು",
-       "protect_change": "ಬದಲಾಯಿಸಿ",
+       "protect_change": "ಬದಲಾಯಿಸà³\81",
        "protectthispage": "ಈ ಪುಟವನ್ನು ಸಂರಕ್ಷಿಸಿ",
-       "unprotect": "ರà²\95à³\8dಷಣà³\86ಯನà³\8dನà³\81 à²¬à²¦à²²à²¾à²µà²£à³\86",
-       "unprotectthispage": "à²\88 à²ªà³\81à²\9fದ à²°à²\95à³\8dಷಣà³\86ಯನà³\8dನà³\81 à²¬à²¦à²²à²¾à²¯à²¿à²¸à²²à³\81",
+       "unprotect": "ರà²\95à³\8dಷಣà³\86ಯನà³\8dನà³\81 à²¬à²¦à²²à²¾à²¯à²¿à²¸à³\81",
+       "unprotectthispage": "ಈ ಪುಟದ ರಕ್ಷಣೆಯನ್ನು ಬದಲಾಯಿಸು",
        "newpage": "ಹೊಸ ಪುಟ",
-       "talkpage": "à²\88 à²ªà³\81à²\9fದ à²¬à²\97à³\8dà²\97à³\86 à²\9aರà³\8dà²\9aà³\86 à²®à²¾à²¡ಿ",
+       "talkpage": "à²\88 à²ªà³\81à²\9fವನà³\8dನà³\81 à²\9aರà³\8dà²\9aಿಸಿ",
        "talkpagelinktext": "ಚರ್ಚೆ",
        "specialpage": "ವಿಶೇಷ ಪುಟ",
        "personaltools": "ವೈಯಕ್ತಿಕ ಉಪಕರಣಗಳು",
        "talk": "ಚರ್ಚೆ",
        "views": "ನೋಟಗಳು",
        "toolbox": "ಉಪಕರಣಗಳು",
-       "userpage": "ಸದಸ್ಯರ ಪುಟ ತೋರು",
+       "userpage": "ಸದಸ್ಯರ ಪುಟವನ್ನು ವೀಕ್ಷಿಸು",
        "projectpage": "ಯೋಜನೆಯ ಪುಟವನ್ನು ನೋಡು",
        "imagepage": "ಕಡತದ ಪುಟ ವೀಕ್ಷಿಸಿ",
        "mediawikipage": "ಸಂದೇಶ ಪುಟವನ್ನು ನೋಡು",
        "templatepage": "ಟೆಂಪ್ಲೇಟು ಪುಟವನ್ನು ವೀಕ್ಷಿಸಿ",
        "viewhelppage": "ಸಹಾಯ ಪುಟ ತೋರು",
-       "categorypage": "ವರ್ಗ ಪುಟ ತೋರು",
+       "categorypage": "ವರ್ಗದ ಪುಟವನ್ನು ವೀಕ್ಷಿಸಿ",
        "viewtalkpage": "ಚರ್ಚೆಯನ್ನು ವೀಕ್ಷಿಸಿ",
        "otherlanguages": "ಇತರ ಭಾಷೆಗಳಲ್ಲಿ",
        "redirectedfrom": "($1 ಇಂದ ಪುನರ್ನಿರ್ದೇಶಿತ)",
        "jumpto": "ಇಲ್ಲಿಗೆ ಹೋಗು:",
        "jumptonavigation": "ಸಂಚರಣೆ",
        "jumptosearch": "ಹುಡುಕು",
-       "view-pool-error": "$1",
+       "view-pool-error": "ಕ್ಷಮಿಸಿ, ಸದ್ಯಕ್ಕೆ ಸರ್ವರ್‌ಗಳ ಮೇಲೆ ಹೆಚ್ಚಿನ ಹೊರೆ ಇದೆ.\nಬಹಳಷ್ಟು ಬಳಕೆದಾರರು ಈ ಸಂಪನ್ಮೂಲವನ್ನು ನೋಡಲು ಪ್ರಯತ್ನಿಸುತ್ತಿದ್ದಾರೆ.\nನೀವು ಈ ಸಂಪನ್ಮೂಲವನ್ನು ಇನ್ನೊಮ್ಮೆ ನೋಡಲು ಪ್ರಯತ್ನಿಸುವ ಮೊದಲು ಸ್ವಲ್ಪಹೊತ್ತು ಕಾಯಿರಿ.\n\n$1",
        "generic-pool-error": "ಕ್ಷಮಿಸಿ, ಸದ್ಯಕ್ಕೆ ಸರ್ವರ್‌ಗಳ ಮೇಲೆ ಹೆಚ್ಚಿನ ಹೊರೆ ಇದೆ.\nಬಹಳಷ್ಟು ಬಳಕೆದಾರರು ಈ ಸಂಪನ್ಮೂಲವನ್ನು ನೋಡಲು ಪ್ರಯತ್ನಿಸುತ್ತಿದ್ದಾರೆ.\nನೀವು ಈ ಸಂಪನ್ಮೂಲವನ್ನು ಇನ್ನೊಮ್ಮೆ ನೋಡಲು ಪ್ರಯತ್ನಿಸುವ ಮೊದಲು ಸ್ವಲ್ಪಹೊತ್ತು ಕಾಯಿರಿ.",
        "pool-timeout": "ಕಾಲಾವಕಾಶ ಲಾಕ್ ಕಾಯುವುದು",
        "pool-queuefull": "ಪ್ರಕ್ರಿಯೆಯನ್ನು ವಿಶೇಷ ಕ್ಯು ಪೂರ್ಣ",
        "copyrightpage": "{{ns:project}}:ಕೃತಿಸ್ವಾಮ್ಯತೆಗಳು",
        "currentevents": "ಪ್ರಚಲಿತ ಸಂಗತಿಗಳು",
        "currentevents-url": "Project:ಪ್ರಚಲಿತ ಸಂಗತಿಗಳು",
-       "disclaimers": "à²\85ಬಾಧà³\8dಯತೆಗಳು",
+       "disclaimers": "ಹà²\95à³\8dà²\95à³\81 à²¨à²¿à²°à²¾à²\95ರಣೆಗಳು",
        "disclaimerpage": "Project:ಸಾಮಾನ್ಯ ಅಬಾಧ್ಯತೆಗಳು",
        "edithelp": "ಸಂಪಾದನೆಗೆ ಸಹಾಯ",
        "helppage-top-gethelp": "ಸಹಾಯ",
        "policy-url": "Project:ನಿಯಮಾವಳಿಗಳು",
        "portal": "ಸಮುದಾಯ ಪುಟ",
        "portal-url": "Project:ಸಮುದಾಯ ಪುಟ",
-       "privacy": "à²\97à³\8cಪà³\8dಯತà³\86ಯ à²\95ಾರà³\8dಯನೀತಿ",
+       "privacy": "à²\97à³\8cಪà³\8dಯತಾ ನೀತಿ",
        "privacypage": "Project:ಗೌಪ್ಯತೆಯ ಕಾರ್ಯನೀತಿ",
        "badaccess": "ಅನುಮತಿ ದೋಷ",
        "badaccess-group0": "ನೀವು ಕೋರಿರುವ ಕ್ರಿಯೆಯನ್ನು ನಿರ್ವಹಿಲು ನಿಮಗೆ ಅನುಮತಿ ಇಲ್ಲ.",
        "passwordreset-domain": "ಕ್ಷೇತ್ರ:",
        "passwordreset-email": "ಇ-ಮೇಲ್ ವಿಳಾಸ:",
        "passwordreset-emailsentemail": "ಪ್ರವೇಶಪದವನ್ನು ಪುನಃಸ್ಥಾಪಿಸಿದ ಮಿಂಚಂಚೆಯನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ.",
-       "passwordreset-emailsent-capture": "ಪ್ರವೇಶಪದವನ್ನು ಪುನಃಸ್ಥಾಪಿಸಿದ ಮಿಂಚಂಚೆಯನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ,ಇದನ್ನು ಈ ಕೆಳಗೆ ತೋರಿಸಲಾಗಿದೆ.",
        "changeemail": "ಮಿಂಚಂಚೆ ವಿಳಾಸವನ್ನು ಬದಲಾಯಿಸಿ",
        "changeemail-no-info": "ನೀವು ಈ ಪುಟವನ್ನು ನೇರತಲುಪಲು ಲಾಗಿನ್ ಆಗಿರುವುದು ಆವಶ್ಯಕ.",
        "changeemail-oldemail": "ಪ್ರಸ್ತುತ ಮಿಂಚಂಚೆ ವಿಳಾಸ:",
        "minoredit": "ಇದು ಚುಟುಕಾದ ಬದಲಾವಣೆ",
        "watchthis": "ಈ ಪುಟವನ್ನು ವೀಕ್ಷಿಸಿ",
        "savearticle": "ಪುಟವನ್ನು ಉಳಿಸಿ",
+       "publishpage": "ಪುಟವನ್ನು ಪ್ರಕಟಿಸು",
+       "publishchanges": "ಬದಲಾವಣೆಗಳನ್ನು ಪ್ರಕಟಿಸು",
        "preview": "ಮುನ್ನೋಟ",
        "showpreview": "ಮುನ್ನೋಟ ತೋರಿಸು",
        "showdiff": "ಬದಲಾವಣೆಗಳನ್ನು ತೋರಿಸು",
        "undo-success": "ಸಂಪಾದನೆಯನ್ನು ವಜಾ ಮಾಡಬಹುದು. ದಯವಿಟ್ಟು ಕೆಳಗಿರುವ ತುಲನೆಯನ್ನು ಪರೀಕ್ಷಿಸಿ ನೀವು ಮಾಡಲು ಇಚ್ಚಿಸಿರುವುದನ್ನು ಖಾತ್ರಿ ಮಾಡಿಕೊಂಡು ವಜಾಗೊಳಿಸುವ ಕ್ರಿಯೆಯನ್ನು \nಪೂರ್ಣಗೊಳಿಸಲು ಬದಲಾವಣೆಗಳನ್ನು ಉಳಿಸಿ.",
        "undo-norev": "ಸಂಪಾದನೆಯನ್ನು ವಜಾಗೊಳಿಸಲು ಸಾದ್ಯವಿಲ್ಲ ಏಕೆಂದರೆ ಒಂದೊ ಇದು ಅಸ್ತಿತ್ವದಲ್ಲಿ ಇಲ್ಲ ಅಧವಾ ಇದು ಅಳಿಸಲ್ಪಟ್ಟಿದೆ",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|talk]]) ರ $1 ಪರಿಷ್ಕರಣೆಯನ್ನು ವಜಾ ಮಾಡಿ",
-       "cantcreateaccounttitle": "ಖಾತೆಯನ್ನು ಸೃಷ್ಟಿಸಲಾಗುತ್ತಿಲ್ಲ",
        "viewpagelogs": "ಈ ಪುಟಗಳ ದಾಖಲೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ",
        "nohistory": "ಈ ಪುಟಕ್ಕೆ ಬದಲಾವಣೆಗಳ ಇತಿಹಾಸ ಇಲ್ಲ.",
        "currentrev": "ಈಗಿನ ತಿದ್ದುಪಡಿ",
        "previousrevision": "←ಹಿಂದಿನ ಪರಿಷ್ಕರಣೆ",
        "nextrevision": "ಮುಂದಿನ ಪರಿಷ್ಕರಣೆ",
        "currentrevisionlink": "ಈಗಿನ ಪರಿಷ್ಕರಣೆ",
-       "cur": "ಸಧ್ಯದ",
+       "cur": "ಸದ್ಯದ",
        "next": "ಮುಂದಿನದು",
        "last": "ಕೊನೆಯ",
        "page_first": "ಮೊದಲ",
        "recentchangeslinked-summary": "ಒಂದು ನಿರ್ದಿಷ್ಟ ಪುಟದಿಂದ (ಅಥವ ನಿರ್ದಿಷ್ಟ ವರ್ಗಕ್ಕೆ ಸೇರಿರುವ ಪುಟಗಳಿಂದ) ಸಂಪರ್ಕ ಹೊಂದಿರುವ ಪುಟಗಳಲ್ಲಿ ಇತ್ತೀಚೆಗೆ ಮಾಡಲಾಗಿರುವ ಬದಲಾವಣೆಗಳನ್ನು ಈ ಕೆಳಗೆ ಪಟ್ಟಿ ಮಾಡಲಾಗಿದೆ.\n[[Special:Watchlist|ನಿಮ್ಮ ವೀಕ್ಷಣಾಪಟ್ಟಿಯಲ್ಲಿ]] ಇರುವ ಪುಟಗಳು '''ದಪ್ಪ ಅಕ್ಷರ'''ಗಳಲ್ಲಿ ಇವೆ.",
        "recentchangeslinked-page": "ಪುಟದ ಹೆಸರು:",
        "recentchangeslinked-to": "ಇದರ ಬದಲಿಗೆ ನೇಮಿತ ಪುಟಕ್ಕೆ ಕೊಂಡಿಯನ್ನು ಹೊಂದಿರುವ ಪುಟಗಳಲ್ಲಿನ ಬದಲಾವಣೆಗಳನ್ನು ತೋರು",
-       "upload": "ಫà³\88ಲà³\8d à²\85ಪà³\8dಲà³\8bಡà³\8d",
+       "upload": "ಫà³\88ಲà³\8d à²®à³\87ಲà²\95à³\8dà²\95à³\86ರಿಸಿ",
        "uploadbtn": "ಫೈಲನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡಿ",
        "reuploaddesc": "ಅಪ್ಲೋಡ್ ಅನ್ನು ನಿಲ್ಲಿಸಿ ಮತ್ತೆ ಅಪ್ಲೋಡ್ ವಕ್ಕಣೆಗೆ ಹಿಂದಿರುಗಿ",
        "uploadnologin": "ಲಾಗಿನ್ ಆಗಿಲ್ಲ",
        "unusedtemplates": "ಉಪಯೋಗದಲ್ಲಿರದ ಟೆಂಪ್ಲೇಟುಗಳು",
        "unusedtemplatestext": "ಯಾವ ಪುಟದಲ್ಲೂ ಉಪಯೋಗದಲ್ಲಿ ಇರದ ಟೆಂಪ್ಲೇಟುಗಳನ್ನು ಇಲ್ಲಿ ಪಟ್ಟಿ ಮಾಡಲಾಗಿದೆ. ಇವನ್ನು ಅಳಿಸುವ ಮುನ್ನ ಟೆಂಪ್ಲೇಟುಗಳಿಗೆ ಇತರ ಲಿಂಕುಗಳಿದೆಯೆ ಎಂದು ಪರೀಕ್ಷಿಸಲು ಮರೆಯದಿರಿ.",
        "unusedtemplateswlh": "ಇತರ ಕೊಂಡಿಗಳು",
-       "randompage": "ಯಾದà³\83à²\9aà³\8dà²\9bಿà²\95 ಪುಟ",
+       "randompage": "ಯಾವà³\81ದಾದರà³\81 à²\92à²\82ದà³\81 ಪುಟ",
        "randompage-nopages": "ಈ ಪುಟಪ್ರಬೇಧದಲ್ಲಿ ಯಾವ ಪುಟವೂ ಇಲ್ಲ.",
        "randomincategory-category": "ವರ್ಗ:",
        "randomincategory-submit": "ಹೋಗು",
        "tooltip-search": "{{SITENAME}} ಅನ್ನು ಹುಡುಕಿ",
        "tooltip-search-go": "ಇದೇ ಹೆಸರಿನ ಪುಟವಿದ್ದಲ್ಲಿ ಅಲ್ಲಿಗೆ ಹೋಗು",
        "tooltip-search-fulltext": "ಈ ಪಠ್ಯವನ್ನು ಹೊಂದಿರುವ ಪುಟಗಳನ್ನು ಹುಡುಕು",
-       "tooltip-p-logo": "ಮುಖ್ಯ ಪುಟ",
+       "tooltip-p-logo": "ಮುಖ್ಯ ಪುಟಕ್ಕೆ ಭೇಟಿ ಕೊಡಿ",
        "tooltip-n-mainpage": "ಮುಖ್ಯ ಪುಟ ನೋಡಿ",
        "tooltip-n-mainpage-description": "ಮುಖ್ಯ ಪುಟ ನೋಡಿ",
        "tooltip-n-portal": "ಯೋಜನೆಯ ಬಗ್ಗೆ, ನೀವು ಏನು ಮಾಡಬಹುದು, ಎಲ್ಲಿ ಇದರ ಬಗ್ಗೆ ತಿಳಿದುಕೊಳ್ಳಬಹುದು",
        "tooltip-n-currentevents": "ಪ್ರಸಕ್ತ ಆಗುಹೋಗುಗಳ ಬಗ್ಗೆ ಹಿನ್ನಲೆ ಮಾಹಿತಿ ಪಡೆಯಿರಿ",
        "tooltip-n-recentchanges": "ವಿಕಿಯಲ್ಲಿನ ಇತ್ತೀಚಿನ ಬದಲಾವಣೆಗಳ ಪಟ್ಟಿ.",
-       "tooltip-n-randompage": "ಯದà³\83à²\9aà³\8dà²\9bಿà²\95 ಪುಟವೊಂದನ್ನು ತೋರಿಸು",
+       "tooltip-n-randompage": "ಯಾವà³\81ದಾದರà³\81 ಪುಟವೊಂದನ್ನು ತೋರಿಸು",
        "tooltip-n-help": "ಇದರ ಬಗ್ಗೆ ತಿಳಿದುಕೊಳ್ಳಲು ಜಾಗ.",
        "tooltip-t-whatlinkshere": "ಇಲ್ಲಿಗೆ ಸಂಪರ್ಕ ಹೊಂದಿರುವ ಎಲ್ಲಾ ವಿಕಿ ಪುಟಗಳ ಪಟ್ಟಿ",
        "tooltip-t-recentchangeslinked": "ಈ ಪುಟದಿಂದ ಸಂಪರ್ಕ ಹೊಂದಿರುವ ಪುಟಗಳಲ್ಲಿನ ಇತ್ತೀಚಿನ ಬದಲಾವಣೆಗಳು",
        "tooltip-feed-atom": "ಈ ಪುಟಕ್ಕೆ Atom ಫೀಡು",
        "tooltip-t-contributions": "ಈ ಸದಸ್ಯರ ಕಾಣಿಕೆಗಳ ಪಟ್ಟಿಯನ್ನು ತೋರಿಸು",
        "tooltip-t-emailuser": "ಈ ಸದಸ್ಯರಿಗೆ ಇ-ಅಂಚೆಯನ್ನು ಕಳುಹಿಸು",
-       "tooltip-t-upload": "ಫà³\88ಲನà³\8dನà³\81 à²\85ಪà³\8dಲà³\8bಡà³\8d à²®à²¾à²¡ಿ",
+       "tooltip-t-upload": "ಫà³\88ಲನà³\8dನà³\81 à²®à³\87ಲà²\95à³\8dà²\95à³\86ರಿಸಿ",
        "tooltip-t-specialpages": "ಎಲ್ಲಾ ವಿಶೇಷ ಪುಟಗಳ ಪಟ್ಟಿ",
        "tooltip-t-print": "ಈ ಪುಟದ ಮುದ್ರಣ ಮಾಡಬಹುದಾದಂತ ಆವೃತ್ತಿ",
        "tooltip-t-permalink": "ಪುಟದ ಈ ಆವೃತ್ತಿಗೆ ಶಾಶ್ವತ ಕೊಂಡಿ",
        "file-nohires": "ಇದಕ್ಕಿಂತ ಹೆಚ್ಚಿನ ವಿವರವಾದ ನೋಟ ಇಲ್ಲ.",
        "svg-long-desc": "SVG ಫೈಲು, ಸುಮಾರಾಗಿ $1 × $2 ಚಿತ್ರಬಿಂದುಗಳು, ಫೈಲಿನ ಗಾತ್ರ: $3",
        "show-big-image": "ಮೂಲ ಕಡತ",
+       "show-big-image-preview": "ಈ ಮುನ್ನೋಟ ಗಾತ್ರ:$1.",
+       "show-big-image-other": "ಇತರೆ{{PLURAL:$2|resolution|resolutions}}: $1.",
        "show-big-image-size": "$1 × $2 ಪಿಕ್ಸೆಲ್‌ಗಳು",
        "file-info-gif-looped": "ಲೂಪ್",
        "file-info-png-looped": "ಲೂಪ್",
        "specialpages-group-pages": "ಪುಟಗಳ ಪಟ್ಟಿ",
        "specialpages-group-redirects": "ವಿಶೇಷ ಪುಟಗಳನ್ನು ಪುನರ್ನಿರ್ದೇಶಿಸಲಾಗುತ್ತಿದೆ",
        "blankpage": "ಖಾಲಿ ಪುಟ",
-       "tag-filter": "[[ವಿಶೇಷ:ಟ್ಯಾಗ್‌ಗಳು|ಟ್ಯಾಗ್]] ಶೋಧಕ:",
+       "tag-filter": "[[Special:Tags|Tag]] ಶೋಧಕ:",
        "tag-filter-submit": "ಶೋಧಕ",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ಟ್ಯಾಗ್|ಟ್ಯಾಗ್‌ಗಳು}}]]: $2)",
        "tags-title": "ಅಂಕಿತಗಳು",
index d81fb26..2a93de8 100644 (file)
@@ -75,7 +75,7 @@
        "tog-numberheadings": "자동으로 머릿글 번호 매기기",
        "tog-showtoolbar": "편집 도구 모음 보이기",
        "tog-editondblclick": "더블 클릭으로 문서 편집하기",
-       "tog-editsectiononrightclick": "제목을 오른쪽 클릭해서 문단 편집하기 활성화",
+       "tog-editsectiononrightclick": "문단 제목을 오른쪽 클릭해서 문단 편집하기 활성화",
        "tog-watchcreations": "내가 만든 문서와 내가 올린 파일을 주시문서 목록에 추가",
        "tog-watchdefault": "내가 편집한 문서와 파일을 주시문서 목록에 추가",
        "tog-watchmoves": "내가 이동한 문서와 파일을 주시문서 목록에 추가",
@@ -93,7 +93,7 @@
        "tog-oldsig": "현재 서명:",
        "tog-fancysig": "서명을 위키텍스트로 취급 (자동으로 링크를 걸지 않음)",
        "tog-uselivepreview": "실시간 미리 보기 사용하기",
-       "tog-forceeditsummary": "편집 요약을 쓰지 않았을 때 알려주기",
+       "tog-forceeditsummary": "편집 요약을 쓰지 않았을 때 내게 물어보기",
        "tog-watchlisthideown": "주시문서 목록에서 내 편집을 숨기기",
        "tog-watchlisthidebots": "주시문서 목록에서 봇 편집을 숨기기",
        "tog-watchlisthideminor": "주시문서 목록에서 사소한 편집을 숨기기",
        "tog-diffonly": "편집 차이를 비교할 때 문서 내용을 보지 않기",
        "tog-showhiddencats": "숨은 분류 보이기",
        "tog-norollbackdiff": "되돌리기 후 차이를 보지 않기",
-       "tog-useeditwarning": "바꾼 내용을 저장하지 않고 편집 페이지를 벗어날 때 알림",
+       "tog-useeditwarning": "바꾼 내용을 저장하지 않고 편집 페이지를 벗어날 때 내게 경고하기",
        "tog-prefershttps": "로그인할 때 항상 보안 연결 사용",
        "underline-always": "항상",
        "underline-never": "항상 치지 않기",
        "underline-default": "스킨 또는 브라우저 기본값",
-       "editfont-style": "편집 영역의 글꼴:",
+       "editfont-style": "편집 영역의 글꼴 형식:",
        "editfont-default": "브라우저 기본값",
        "editfont-monospace": "고정폭 글꼴",
        "editfont-sansserif": "산세리프 글꼴",
        "october-gen": "10월",
        "november-gen": "11월",
        "december-gen": "12월",
-       "jan": "1",
-       "feb": "2",
-       "mar": "3",
-       "apr": "4",
-       "may": "5",
-       "jun": "6",
-       "jul": "7",
-       "aug": "8",
-       "sep": "9",
-       "oct": "10",
-       "nov": "11",
-       "dec": "12",
+       "jan": "1",
+       "feb": "2",
+       "mar": "3",
+       "apr": "4",
+       "may": "5",
+       "jun": "6",
+       "jul": "7",
+       "aug": "8",
+       "sep": "9",
+       "oct": "10",
+       "nov": "11",
+       "dec": "12",
        "january-date": "1월 $1일",
        "february-date": "2월 $1일",
        "march-date": "3월 $1일",
        "category_header": "\"$1\" 분류에 속하는 문서",
        "subcategories": "하위 분류",
        "category-media-header": "\"$1\" 분류에 속하는 미디어",
-       "category-empty": "<em>이 분류에는 문서나 자료가 하나도 없습니다.</em>",
+       "category-empty": "<em>현재 이 분류에는 문서나 미디어가 하나도 없습니다.</em>",
        "hidden-categories": "{{PLURAL:$1|숨은 분류|숨은 분류}}",
        "hidden-category-category": "숨은 분류",
        "category-subcat-count": "{{PLURAL:$2|이 분류에는 하위 분류 1개만이 속해 있습니다.|다음은 이 분류에 속하는 {{PLURAL:$1|하위 분류}} $2개 가운데 $1개입니다.}}",
        "tagline": "{{SITENAME}}",
        "help": "도움말",
        "search": "검색",
+       "search-ignored-headings": " #<!-- 이 줄은 그대로 두십시오 --> <pre>\n# 검색에서 무시되는 문단 제목입니다.\n# 이 문서에 대한 바뀜은 즉시 문단 제목으로 된 문서가 다시 색인됩니다.\n# null 편집을 하여 문서 다시 색인을 강제할 수 있습니다.\n# 문법은 다음과 같습니다:\n#   * \"#\" 문자에서 줄의 끝까지는 주석입니다\n#   * 빈 줄이 아닌 줄은 무시할 정확한 제목이며, 대소문자를 무시합니다\n각주\n참조\n참고\n출처\n바깥 링크\n바깥 고리\n같이 보기\n함께 보기\n #</pre> <!-- 이 줄은 그대로 두십시오 -->",
        "searchbutton": "검색",
        "go": "보기",
        "searcharticle": "보기",
        "undelete_short": "{{PLURAL:$1|편집 한 개|편집 $1개}} 되살리기",
        "viewdeleted_short": "{{PLURAL:$1|삭제된 편집 한 개|삭제된 편집 $1개}} 보기",
        "protect": "보호",
-       "protect_change": "ë³´í\98¸ ì\88\98ì¤\80 ë°\94꾸기",
+       "protect_change": "바꾸기",
        "protectthispage": "이 문서 보호하기",
        "unprotect": "보호 설정 바꾸기",
        "unprotectthispage": "이 문서의 보호 설정을 바꾸기",
        "youhavenewmessagesmanyusers": "여러 사용자로부터의 $1가 있습니다. ($2)",
        "newmessageslinkplural": "{{PLURAL:$1|새 메시지|999=새 메시지}}",
        "newmessagesdifflinkplural": "마지막으로 {{PLURAL:$1|바뀐 내용|999=바뀐 내용}}",
-       "youhavenewmessagesmulti": "다른 사용자가 $1에 남긴 새 메시지가 있습니다",
+       "youhavenewmessagesmulti": "$1에 새 메시지가 있습니다",
        "editsection": "편집",
        "editold": "편집",
        "viewsourceold": "원본 보기",
        "internalerror": "내부 오류",
        "internalerror_info": "내부 오류: $1",
        "internalerror-fatal-exception": "종류 \"$1\"에서 심각한 오류",
-       "filecopyerror": "\"$1\" 파일을 \"$2\"로 복사할 수 없습니다.",
-       "filerenameerror": "\"$1\" 파일을 \"$2\"로 옮길 수 없습니다.",
+       "filecopyerror": "\"$1\" 파일을 \"$2\"(으)로 복사할 수 없습니다.",
+       "filerenameerror": "\"$1\" 파일을 \"$2\"(으)로 이름을 바꿀 수 없습니다.",
        "filedeleteerror": "\"$1\" 파일을 삭제할 수 없습니다.",
        "directorycreateerror": "\"$1\" 디렉터리를 만들 수 없습니다.",
        "directoryreadonlyerror": "\"$1\" 디렉터리는 읽기 전용입니다.",
        "formerror": "오류: 양식을 제출할 수 없습니다.",
        "badarticleerror": "이 명령은 이 문서에서 수행할 수 없습니다.",
        "cannotdelete": "\"$1\" 문서나 파일을 삭제할 수 없습니다.\n이미 삭제되었을 수도 있습니다.",
-       "cannotdelete-title": "\"$1\" 문서를 삭제할 수 없습니다.",
+       "cannotdelete-title": "\"$1\" 문서를 삭제할 수 없습니다",
        "delete-hook-aborted": "훅이 삭제를 중단했습니다.\n아무런 설명도 주어지지 않았습니다.",
        "no-null-revision": "\"$1\" 문서에 대한 빈 판을 새로 만들 수 없습니다.",
        "badtitle": "잘못된 제목",
        "myprivateinfoprotected": "내 개인 정보를 편집할 권한이 없습니다.",
        "mypreferencesprotected": "내 환경 설정을 편집할 권한이 없습니다.",
        "ns-specialprotected": "특수 문서는 편집할 수 없습니다.",
-       "titleprotected": "[[User:$1|$1]] 사용자가 문서 만들기를 금지했습니다.\n이유는 다음과 같습니다. <em>$2</em>",
+       "titleprotected": "[[User:$1|$1]]님이 문서 만들기를 보호했습니다.\n이유는 다음과 같습니다. <em>$2</em>",
        "filereadonlyerror": "\"$2\" 파일 저장소가 읽기 전용이기 때문에 \"$1\" 파일을 바꿀 수 없습니다.\n\n파일 저장소를 잠근 시스템 관리자가 다음과 같은 설명을 남겼습니다: \"$3\".",
        "invalidtitle-knownnamespace": "제목 오류: \"$2\" 이름공간과 \"$3\" 텍스트",
        "invalidtitle-unknownnamespace": "제목 오류: 알 수 없는 $1 이름공간 번호와 \"$2\" 텍스트",
        "yourpasswordagain": "비밀번호 다시 입력:",
        "createacct-yourpasswordagain": "비밀번호 확인",
        "createacct-yourpasswordagain-ph": "비밀번호를 다시 입력하세요",
-       "remembermypassword": "이 브라우저에 로그인 상태 저장하기 (최대 $1일)",
        "userlogin-remembermypassword": "로그인 상태를 유지하기",
        "userlogin-signwithsecure": "보안 연결 사용",
+       "cannotlogin-title": "로그인할 수 없음",
+       "cannotlogin-text": "로그인할 수 없습니다.",
        "cannotloginnow-title": "지금 로그인할 수 없습니다.",
        "cannotloginnow-text": "$1 사용 중에는 로그인이 불가능합니다.",
+       "cannotcreateaccount-title": "계정을 만들 수 없습니다",
+       "cannotcreateaccount-text": "이 위키에서 직접 계정 만들기는 활성화되어 있지 않습니다.",
        "yourdomainname": "도메인 이름:",
        "password-change-forbidden": "이 위키에서 비밀번호를 바꿀 수 없습니다.",
        "externaldberror": "인증 데이터베이스에 오류가 있거나 바깥 계정을 새로 고칠 권한이 없습니다.",
        "userlogin-resetlink": "로그인 정보를 잊으셨나요?",
        "userlogin-resetpassword-link": "비밀번호를 잊으셨나요?",
        "userlogin-helplink2": "로그인에 대한 도움말",
-       "userlogin-loggedin": "이미 {{GENDER:$1|$1}} 사용자로 로그인되어 있습니다.\n다른 사용자로 로그인하려면 아래의 양식을 사용하세요.",
-       "userlogin-reauth": "사용자가 $1임을 확인하려면 다시 로그인해야 합니다.",
+       "userlogin-loggedin": "이미 {{GENDER:$1|$1}}님으로 로그인되어 있습니다.\n다른 사용자로 로그인하려면 아래의 양식을 사용하세요.",
+       "userlogin-reauth": "당신이 {{GENDER:$1|$1}}임을 검증하려면 다시 로그인해야 합니다.",
        "userlogin-createanother": "다른 계정 만들기",
        "createacct-emailrequired": "이메일 주소",
        "createacct-emailoptional": "이메일 주소 (선택 사항)",
        "createacct-loginerror": "계정 만들기는 성공했으나 자동으로 로그인하지 못했습니다. [[Special:UserLogin|수동으로 로그인]]해 주십시오.",
        "noname": "사용자 계정 이름이 올바르지 않습니다.",
        "loginsuccesstitle": "로그인함",
-       "loginsuccess": "<strong>{{SITENAME}}에 \"$1\" 계정으로 로그인했습니다.</strong>",
+       "loginsuccess": "<strong>{{SITENAME}}에 \"$1\"으로 로그인했습니다.</strong>",
        "nosuchuser": "이름이 \"$1\"인 사용자는 없습니다.\n사용자 이름은 대소문자를 구별합니다.\n철자가 맞는지 확인해주세요. [[Special:CreateAccount|새 계정을 만들 수도 있습니다]].",
        "nosuchusershort": "이름이 \"$1\"인 사용자는 없습니다.\n철자가 맞는지 확인하세요.",
        "nouserspecified": "사용자 계정 이름을 입력하지 않았습니다.",
        "password-login-forbidden": "이 사용자 계정 이름과 비밀번호는 사용할 수 없습니다.",
        "mailmypassword": "비밀번호 재설정",
        "passwordremindertitle": "{{SITENAME}}의 새 임시 비밀번호",
-       "passwordremindertext": "$1 IP 주소에서 누군가가 아마 자신이 {{SITENAME}} ($4)의 새 비밀번호를 요청했습니다.\n\"$2\" 사용자의 임시 비밀번호는 \"$3\"로 설정되었습니다. 이것이 자신이 의도한 바라면\n지금 로그인하여 새로운 비밀번호를 만드세요.\n임시 비밀번호는 {{PLURAL:$5|$5일}} 후에 만료됩니다.\n\n이 요청을 다른 사람이 했거나 이전 비밀번호를 기억해 내서 바꿀 필요가 없으면\n이 메시지를 무시하고 이전 비밀번호를 계속 사용할 수 있습니다.",
+       "passwordremindertext": "당신일 수도 있는 $1 IP 주소를 사용하는 사용자가 {{SITENAME}} ($4)의 새 비밀번호를 요청했습니다.\n\"$2\" 사용자의 임시 비밀번호는 \"$3\"로 설정되었습니다. 이것이 자신이 의도한 바라면\n지금 로그인하여 새로운 비밀번호를 만드세요.\n임시 비밀번호는 {{PLURAL:$5|1일|$5일}} 후에 만료됩니다.\n\n이 요청을 다른 사람이 했거나 이전 비밀번호를 기억해 내서 바꿀 필요가 없으면\n이 메시지를 무시하고 이전 비밀번호를 계속 사용할 수 있습니다.",
        "noemail": "\"$1\" 사용자는 이메일 주소를 등록하지 않았습니다.",
        "noemailcreate": "올바른 이메일 주소를 제공해야 합니다.",
        "passwordsent": "\"$1\" 계정의 새로운 비밀번호를 이메일로 보냈습니다.\n비밀번호를 받고 다시 로그인해 주세요.",
        "acct_creation_throttle_hit": "당신의 IP 주소를 이용한 방문자가 이전에 이미 {{PLURAL:$1|계정 $1개}}를 만들어, 계정 만들기 한도를 초과하였습니다.\n따라서 지금은 이 IP 주소로는 더 이상 계정을 만들 수 없습니다.",
        "emailauthenticated": "이메일 주소가 $2 $3에 인증되었습니다.",
        "emailnotauthenticated": "이메일 주소를 인증하지 않았습니다.\n이메일 확인 절차를 거치지 않으면 다음 이메일 기능을 사용할 수 없습니다.",
-       "noemailprefs": "이 기능을 사용하기 위해서는 사용자 환경 설정에서 이메일 주소를 설정해야 합니다.",
+       "noemailprefs": "이 기능을 사용하려면 사용자 환경 설정에서 이메일 주소를 지정하세요.",
        "emailconfirmlink": "이메일 주소 확인",
        "invalidemailaddress": "이메일 주소의 형식이 잘못되어 인식할 수 없습니다.\n정상적인 형식의 이메일을 입력하거나 칸을 비워 주세요.",
        "cannotchangeemail": "이 위키에서는 계정의 이메일 주소를 바꿀 수 없습니다.",
        "accountcreated": "계정이 만들어짐",
        "accountcreatedtext": "[[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|토론]]) 사용자 계정이 만들어졌습니다.",
        "createaccount-title": "{{SITENAME}} 계정 만들기",
-       "createaccount-text": "ë\88\84êµ°ê°\80ê°\80 {{SITENAME}} ($4)ì\97\90ì\84\9c ì\82¬ì\9a©ì\9e\90 ì\9d´ë¦\84 \"$2\", ë¹\84ë°\80ë²\88í\98¸ \"$3\"ë¡\9c ë\8b¹ì\8b ì\9d\98 ì\9d´ë©\94ì\9d¼ ì£¼ì\86\8cê°\80 ë\93±ë¡\9dë\90\9c ê³\84ì \95ì\9d\84 ë§\8cë\93¤ì\97\88ì\8aµë\8b\88ë\8b¤. \nì§\80ê¸\88 ë¡\9cê·¸ì\9d¸í\95\98ì\97¬ ë¹\84ë°\80ë²\88í\98¸ë¥¼ ë°\94꾸ì\8b­ì\8b\9cì\98¤.\n\nì\8b¤ì\88\98ë¡\9c ê³\84ì \95ì\9d\84 ì\9e\98못 ë§\8cë\93¤ì\97\88ë\8b¤ë©´ ì\9d´ ë©\94ì\8b\9cì§\80ë\8a\94 ë¬´ì\8b\9cí\95´ë\8f\84 ë\90©ë\8b\88ë\8b¤.",
+       "createaccount-text": "ë\88\84êµ°ê°\80ê°\80 {{SITENAME}} ($4)ì\97\90ì\84\9c ì\82¬ì\9a©ì\9e\90 ì\9d´ë¦\84 \"$2\", ë¹\84ë°\80ë²\88í\98¸ \"$3\"ë¡\9c ë\8b¹ì\8b ì\9d\98 ì\9d´ë©\94ì\9d¼ ì£¼ì\86\8cê°\80 ë\93±ë¡\9dë\90\9c ê³\84ì \95ì\9d\84 ë§\8cë\93¤ì\97\88ì\8aµë\8b\88ë\8b¤. \nì§\80ê¸\88 ë¡\9cê·¸ì\9d¸í\95\98ì\97¬ ë¹\84ë°\80ë²\88í\98¸ë¥¼ ë°\94꾸ì\85\94ì\95¼ í\95©ë\8b\88ë\8b¤.\n\nì\8b¤ì\88\98ë¡\9c ê³\84ì \95ì\9d\84 ì\9e\98못 ë§\8cë\93¤ì\97\88ë\8b¤ë©´ ì\9d´ ë©\94ì\8b\9cì§\80ë\8a\94 ë¬´ì\8b\9cí\95´ë\8f\84 ë\90©ë\8b\88ë\8b¤.",
        "login-throttled": "최근 너무 많이 로그인을 시도했습니다.\n$1 뒤에 다시 시도하세요.",
        "login-abort-generic": "로그인에 실패했습니다 - 중지됨",
        "login-migrated-generic": "당신의 계정이 마이그레이션되었으며, 당신의 사용자 이름이 더 이상 이 위키에 존재하지 않습니다.",
        "botpasswords-label-delete": "삭제",
        "botpasswords-label-resetpassword": "비밀번호 재설정",
        "botpasswords-label-grants": "적용할 수 있는 부여:",
-       "botpasswords-help-grants": "ê°\9cê°\9cì\9d\98 ë¶\80ì\97¬ ê¸°ë\8a¥ì\9d\80 ì\82¬ì\9a©ì\9e\90 ê³\84ì \95ì\9d´ ì\9d´ë¯¸ ì\86\8cì\9c í\95\98ê³  ì\9e\88ë\8a\94 ì\82¬ì\9a©ì\9e\90 ê¶\8cí\95\9cì\97\90 ë\8c\80í\95\9c ì \91ê·¼ì\9d\84 ì \9cê³µí\95©ë\8b\88ë\8b¤. ì\9e\90ì\84¸í\95\9c ì\82¬í\95­ì\9d\80 [[Special:ListGrants|ë¶\80ì\97¬ ëª©ë¡\9d]]ì\9d\84 í\99\95ì\9d¸í\95´ ì£¼ì\8b­ì\8b\9cì\98¤.",
+       "botpasswords-help-grants": "ê°\81ê°\81 ë¶\80ì\97¬ë\90\9c ê°\92ì\9d\80 ëª©ë¡\9dì\97\90ì\84\9c ì\82¬ì\9a©ì\9e\90 ê³\84ì \95ì\9d\84 ì\9d´ë¯¸ ê°\96ê³  ì\9e\88ë\8a\94 ì\82¬ì\9a©ì\9e\90 ê¶\8cí\95\9cì\97\90 ì \91ê·¼í\95  ì\88\98 ì\9e\88ë\8a\94 ê¶\8cí\95\9cì\9d\84 ì¤\8dë\8b\88ë\8b¤. ì\9e\90ì\84¸í\95\9c ì \95ë³´ë\8a\94 [[Special:ListGrants|ë¶\80ì\97¬ í\91\9c]]ì\9d\84 ë³´ì\84¸ì\9a\94.",
        "botpasswords-label-restrictions": "사용 제한:",
        "botpasswords-label-grants-column": "승인됨",
        "botpasswords-bad-appid": "\"$1\"이라는 봇 이름은 유효하지 않습니다.",
        "botpasswords-updated-body": "사용자 \"$2\"의 \"$1\"라는 이름의 봇 비밀번호가 업데이트되었습니다.",
        "botpasswords-deleted-title": "봇 비밀번호 제거",
        "botpasswords-deleted-body": "사용자 \"$2\"의 \"$1\"라는 이름의 봇 비밀번호가 삭제되었습니다.",
-       "botpasswords-newpassword": "<strong>$1</strong> 계정의 비밀번호가 <strong>$2</strong>로 변경되었습니다. <em>잊어버리지 않도록 기록해두시기 바랍니다.</em>",
+       "botpasswords-newpassword": "<strong>$1</strong>님으로 로그인하기 위한 새 비밀번호가 <strong>$2</strong>입니다. <em>잊어버리지 않도록 기록해두시기 바랍니다.</em>",
        "botpasswords-no-provider": "'BotPasswordsSessionProvider'는 이용할 수 없습니다.",
        "botpasswords-restriction-failed": "봇 비밀번호 제한으로 인해 로그인할 수 없습니다.",
        "botpasswords-invalid-name": "지정된 사용자 이름은 봇 비밀번호 구분자(\"$1\")를 포함하고 있지 않습니다.",
        "passwordreset-capture-help": "이 상자에 체크하면 이메일이 발송된 즉시 임시 비밀번호가 담긴 이메일을 볼 수 있습니다.",
        "passwordreset-email": "이메일 주소:",
        "passwordreset-emailtitle": "{{SITENAME}} 계정에 대한 자세한 정보",
-       "passwordreset-emailtext-ip": "$1 IP 주소를 사용하는 누군가가 아마 자신이 {{SITENAME}} ($4)의 비밀번호 재설정을 요청하였습니다.\n이 이메일 주소와 연관된 {{PLURAL:$3|계정}}의 목록입니다:\n\n$2\n\n{{PLURAL:$3|이 임시 비밀번호}}는 {{PLURAL:$5|$5일}} 후에 만료됩니다.\n이 비밀번호로 로그인한 후 비밀번호를 바꾸십시오. 만약 당신이 아닌 다른 사람이 요청하였거나,\n원래의 비밀번호를 기억해냈다면, 이 메시지를 무시하고\n이전의 비밀번호를 계속 사용할 수 있습니다.",
+       "passwordreset-emailtext-ip": "당신일 수도 있는 $1 IP 주소를 사용하는 사용자가 {{SITENAME}} ($4)의 비밀번호 재설정을 요청하였습니다.\n이 이메일 주소와 연관된 사용자 {{PLURAL:$3|계정}}의 목록입니다:\n\n$2\n\n{{PLURAL:$3|이 임시 비밀번호}}는 {{PLURAL:$5|1일|$5일}} 후에 만료됩니다.\n이 비밀번호로 로그인한 후 비밀번호를 바꾸십시오. 만약 당신이 아닌 다른 사람이 요청하였거나,\n원래의 비밀번호를 기억해냈다면, 이 메시지를 무시하고\n이전의 비밀번호를 계속 사용할 수 있습니다.",
        "passwordreset-emailtext-user": "{{SITENAME}} ($4)의 사용자 $1이 비밀번호 재설정을 요청하였습니다.\n이 이메일 주소와 연관된 {{PLURAL:$3|계정}}의 목록입니다:\n\n$2\n\n{{PLURAL:$3|이 임시 비밀번호}}는 {{PLURAL:$5|$5일}} 후에 만료됩니다.\n이 비밀번호로 로그인한 후 비밀번호를 바꾸십시오. 만약 당신이 아닌 다른 사람이 요청하였거나,\n원래의 비밀번호를 기억해냈다면, 이 메시지를 무시하고\n이전의 비밀번호를 계속 사용할 수 있습니다.",
        "passwordreset-emailelement": "사용자 이름: \n$1\n\n임시 비밀번호: \n$2",
        "passwordreset-emailsentemail": "당신의 계정과 연결된 이메일 주소가 있다면, 비밀번호 재설정 메일이 전해질 것입니다.",
        "passwordreset-emailsentusername": "이 사용자 이름과 연결된 이메일 주소가 있다면 비밀번호 초기화 이메일이 전송됩니다.",
-       "passwordreset-emailsent-capture": "비밀번호 재설정 이메일이 발송되었으며, 아래에 나타나 있습니다.",
-       "passwordreset-emailerror-capture": "비밀번호 재설정 이메일이 생성되어 아래에 나타나 있지만, {{GENDER:$2|사용자}}에게 발송하는 데에는 실패했습니다: $1",
        "passwordreset-emailsent-capture2": "비밀번호 재설정 이메일을 보냈습니다. {{PLURAL:$1|사용자 이름과 비밀번호는|사용자 이름과 비밀번호의 목록은}} 아래에 나타납니다.",
        "passwordreset-emailerror-capture2": "{{GENDER:$2|사용자}} 이메일 보내기 실패: $1 {{PLURAL:$3|사용자 이름과 비밀번호가|사용자 이름과 비밀번호의 목록이}} 아래에 나타납니다.",
        "passwordreset-nocaller": "호출자를 지정해야 합니다",
        "passwordreset-nodata": "사용자 이름이나 이메일 주소가 지정되지 않았습니다",
        "changeemail": "이메일 주소를 바꾸거나 제거하기",
        "changeemail-header": "이메일 주소를 바꾸려면 이 양식을 채우세요. 계정에서 이메일 연동을 취소하고 싶다면 양식을 제출할 때 새 이메일 주소를 공란으로 두세요.",
-       "changeemail-passwordrequired": "변경을 적용하려면 비밀번호를 입력해야 합니다.",
        "changeemail-no-info": "이 페이지에 직접 접근하려면 로그인해야 합니다.",
        "changeemail-oldemail": "현재 이메일 주소:",
        "changeemail-newemail": "새 이메일 주소:",
        "showpreview": "미리 보기",
        "showdiff": "차이 보기",
        "blankarticle": "<strong>경고:</strong> 만들려는 문서가 비어 있습니다.\n\"{{int:savearticle}}\"을 다시 클릭하면, 아무 내용 없이 문서가 만들어집니다.",
-       "anoneditwarning": "<strong>경고:</strong> 로그인을 하고 있지 않습니다. 편집을 하면 IP 주소가 공개적으로 보이게 됩니다. <strong>[$1 로그인]</strong>하거나 <strong>[$2 계정을 생성하면]</strong>, 편집 시에 사용자 이름이 표시되며 더불어 다른 혜택들도 누릴 수 있습니다.",
+       "anoneditwarning": "<strong>경고:</strong> 로그인하지 않았습니다. 편집을 하면 IP 주소가 공개되게 됩니다. <strong>[$1 로그인]</strong>하거나 <strong>[$2 계정을 생성하면]</strong> 편집자가 사용자 이름으로 기록되고, 다른 장점도 있습니다.",
        "anonpreviewwarning": "<em>로그인하고 있지 않습니다. 문서를 저장하면 당신의 IP 주소가 문서의 편집 역사에 남게 됩니다.</em>",
        "missingsummary": "'''알림:''' 편집 요약을 적지 않았습니다.\n이대로 \"{{int:savearticle}}\"을 클릭하면 편집 요약 없이 저장됩니다.",
        "selfredirect": "<strong>경고:</strong> 자기 자신으로 문서를 넘겨주고 있습니다.\n넘겨줄 대상을 잘못 입력했거나, 잘못된 문서를 편집하고 있을 수 있습니다.\n\"{{int:savearticle}}\"을 입력하면, 넘겨주기 문서가 생성될 것입니다.",
        "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이고, 차단 ID는 #$5입니다.\n문의할 때에 이 정보를 같이 알려주세요.",
-       "autoblockedtext": "당신의 IP 주소는 $1님이 차단한 사용자가 사용했던 IP이기 때문에 자동으로 차단되었습니다.\n차단된 이유는 다음과 같습니다:\n\n:$2\n\n* 차단이 시작된 시간: $8\n* 차단이 끝나는 시간: $6\n* 차단된 사용자: $7\n\n$1 또는 [[{{MediaWiki:Grouppage-sysop}}|다른 관리자]]에게 차단에 대해 문의할 수 있습니다.\n\n[[Special:Preferences|계정 환경 설정]]에 올바른 이메일 주소가 있어야만 \"이메일 보내기\" 기능을 사용할 수 있습니다. 또한 이메일 보내기 기능이 차단되어 있으면 이메일을 보낼 수 없습니다.\n\n현재 IP 주소는 $3이고, 차단 ID는 #$5입니다.\n문의할 때에 이 정보를 같이 알려주세요.",
+       "autoblockedtext": "당신의 IP 주소는 $1님이 차단한 사용자가 사용했던 IP이기 때문에 자동으로 차단되었습니다.\n차단된 이유는 다음과 같습니다:\n\n:$2\n\n* 차단이 시작된 시간: $8\n* 차단이 끝나는 시간: $6\n* 차단된 사용자: $7\n\n$1 또는 [[{{MediaWiki:Grouppage-sysop}}|다른 관리자]]에게 차단에 대해 문의할 수 있습니다.\n\n[[Special:Preferences|사용자 환경 설정]]에 올바른 이메일 주소가 있어야만 \"이메일 보내기\" 기능을 사용할 수 있습니다. 또한 이메일 보내기 기능이 차단되어 있으면 이메일을 보낼 수 없습니다.\n\n현재 IP 주소는 $3이고, 차단 ID는 #$5입니다.\n문의할 때에 이 정보를 같이 알려주세요.",
        "blockednoreason": "이유를 입력하지 않음",
        "whitelistedittext": "문서를 편집하기 전에 $1해야 합니다.",
        "confirmedittext": "문서를 고치려면 이메일 인증 절차가 필요합니다.\n[[Special:Preferences|사용자 환경 설정]]에서 이메일 주소를 입력하고 이메일 주소 인증을 해주시기 바랍니다.",
        "loginreqlink": "로그인",
        "loginreqpagetext": "다른 문서를 보기 위해서는 $1해야 합니다.",
        "accmailtitle": "비밀번호를 보냈습니다",
-       "accmailtext": "[[User talk:$1|$1]] 사용자의 비밀번호를 임의로 만들어 $2(으)로 보냈습니다. 로그인하고 나서 <em>[[Special:ChangePassword|비밀번호를 바꿀]]</em> 수 있습니다.",
+       "accmailtext": "[[User talk:$1|$1]]의 비밀번호를 임의로 만들어 $2(으)로 보냈습니다. 로그인하고 나서 <em>[[Special:ChangePassword|비밀번호를 바꿀]]</em> 수 있습니다.",
        "newarticle": "(새 문서)",
        "newarticletext": "아직 없는 문서의 링크를 따라왔습니다.\n새 문서를 만들려면 아래 상자에 내용을 입력하면 됩니다. (자세한 내용은 [$1 도움말 문서]를 참조하세요)\n만약 잘못 찾아왔다면, 브라우저의 '''뒤로''' 버튼을 눌러 주세요.",
        "anontalkpagetext": "----\n여기는 계정을 만들지 않았거나 사용하고 있지 않은 익명 사용자를 위한 토론 문서입니다.\n익명 사용자를 구별하기 위해서는 숫자로 된 IP 주소를 사용해야만 합니다.\nIP 주소는 여러 사용자가 공유할 수 있습니다.\n자신과 관계없는 의견이 자신에게 남겨져 있어 불쾌하다고 생각하는 익명 사용자는 [[Special:CreateAccount|계정을 만들고]] [[Special:UserLogin|로그인해서]] 나중에 다른 익명 사용자에게 줄 혼란을 줄일 수 있습니다.",
        "editingold": "<strong>경고: 이 문서의 오래된 판을 편집하고 있습니다.</strong>\n이것을 저장하면, 이 판 이후로 바뀐 모든 편집이 사라집니다.",
        "yourdiff": "차이",
        "copyrightwarning": "{{SITENAME}}에서의 모든 기여는 $2 라이선스로 배포된다는 점을 유의해 주세요 (자세한 내용에 대해서는 $1 문서를 읽어주세요).\n만약 여기에 동의하지 않는다면 문서를 저장하지 말아 주세요.<br />\n또한, 직접 작성했거나 퍼블릭 도메인과 같은 자유 문서에서 가져왔다는 것을 보증해야 합니다.\n<strong>저작권이 있는 내용을 허가 없이 저장하지 마세요!</strong>",
-       "copyrightwarning2": "{{SITENAME}}에서의 모든 기여는 다른 사용자가 편집, 수정, 삭제할 수 있다는 점을 유의해 주세요.\n만약 여기에 동의하지 않는다면, 문서를 저장하지 말아 주세요.<br />\n또한, 직접 작성했거나 퍼블릭 도메인과 같은 자유 문서에서 가져왔다는 것을 보증해야 합니다 (자세한 내용에 대해서는 $1 문서를 읽어 주세요).\n'''저작권이 있는 내용을 허가 없이 저장하지 마세요!'''",
+       "copyrightwarning2": "{{SITENAME}}에서의 모든 기여는 다른 기여자가 편집, 수정, 삭제할 수 있다는 점을 유의해 주세요.\n만약 여기에 동의하지 않는다면, 문서를 저장하지 말아 주세요.<br />\n또한, 직접 작성했거나 퍼블릭 도메인과 같은 자유 문서에서 가져왔다는 것을 보증해야 합니다 (자세한 사항은 $1 문서를 보세요).\n<strong>저작권이 있는 내용을 허가 없이 저장하지 마세요!</strong>",
        "editpage-cannot-use-custom-model": "이 문서의 콘텐츠 모델은 변경될 수 없습니다.",
        "longpageerror": "'''오류: 문서의 크기가 {{PLURAL:$1|$1킬로바이트}}로 최대 크기인 {{PLURAL:$2|$2킬로바이트}}보다 큽니다.'''\n저장할 수 없습니다.",
        "readonlywarning": "<strong>경고: 데이터베이스가 관리를 위해 잠겨 있습니다. 따라서 문서를 편집한 내용을 지금 저장할 수 없습니다.</strong>\n편집 내용을 복사하여 붙여넣기 등을 사용하여 일단 다른 곳에 저장한 후, 나중에 다시 시도해 주세요.\n\n데이터베이스를 잠근 시스템 관리자가 남긴 설명은 다음과 같습니다: $1",
        "permissionserrors": "권한 오류",
        "permissionserrorstext": "해당 명령을 수행할 권한이 없습니다. 다음 {{PLURAL:$1|이유}}를 확인해보세요:",
        "permissionserrorstext-withaction": "$2 권한이 없습니다. 다음 {{PLURAL:$1|이유}}를 확인해주세요:",
-       "contentmodelediterror": "ì\82¬ì\9a©ì\9e\90ë\8a\94 ì\9d´ í\8c\90ì\9d\84 í\8e¸ì§\91í\95  ì\88\98 ì\97\86ì\8aµë\8b\88ë\8b¤. ì½\98í\85\90츠 ëª¨ë\8d¸ì\9d\80 <code>$1</code>ì\9d´ë©°, ì\9d´ ë¬¸ì\84\9cì\9d\98 í\98\84 콘텐츠 모델은 <code>$2</code>이므로 차이가 있습니다.",
+       "contentmodelediterror": "ì\9d´ í\8c\90ì\9d\84 í\8e¸ì§\91í\95  ì\88\98 ì\97\86ì\8aµë\8b\88ë\8b¤. ì½\98í\85\90츠 ëª¨ë\8d¸ì\9d\80 <code>$1</code>ì\9d´ë©°, ì\9d´ ë¬¸ì\84\9cì\9d\98 í\98\84ì\9e¬ 콘텐츠 모델은 <code>$2</code>이므로 차이가 있습니다.",
        "recreate-moveddeleted-warn": "<strong>경고: 삭제된 문서를 다시 만들고 있습니다.</strong>\n\n이 문서를 계속 편집하는 것이 적합한 것인지 확인해주세요.\n편의를 위해 삭제와 이동 기록을 다음과 같이 제공합니다:",
        "moveddeleted-notice": "이 문서는 삭제되었습니다.\n이 문서의 삭제 및 이동 기록은 다음과 같습니다.",
        "moveddeleted-notice-recent": "죄송합니다, 이 문서는 최근 (24시간 내)에 삭제된 적이 있습니다.\n삭제와 이동 기록이 참고를 위해 남겨져 있습니다.",
        "content-failed-to-parse": "$1 모델에 대한 $2 내용을 구문 분석하는 데 실패했습니다: $3",
        "invalid-content-data": "잘못된 내용 데이터입니다",
        "content-not-allowed-here": "\"$1\" 내용은 [[$2]] 문서예 허용하지 않습니다",
-       "editwarning-warning": "이 페이지에서 벗어나면 저장하지 않은 바뀜이 모두 사라집니다.\n로그인을 했다면, 환경 설정의 \"{{int:편집 상자}}\"에서 이 경고를 띄우지 않도록 설정할 수 있습니다.",
+       "editwarning-warning": "이 페이지에서 벗어나면 저장하지 않은 바뀜이 모두 사라집니다.\n로그인을 했다면, 환경 설정의 \"{{int:prefs-editing}}\"에서 이 경고를 띄우지 않도록 설정할 수 있습니다.",
        "editpage-notsupportedcontentformat-title": "지원하지 않는 내용 형식",
-       "editpage-notsupportedcontentformat-text": "내용 형식 $1(은)는 $2 내용 모델에서 지원하지 않습니다.",
+       "editpage-notsupportedcontentformat-text": "내용 형식 $1은(는) $2 내용 모델에서 지원하지 않습니다.",
        "content-model-wikitext": "위키텍스트",
        "content-model-text": "일반 텍스트",
        "content-model-javascript": "자바스크립트",
        "content-model-css": "CSS",
        "content-json-empty-object": "빈 오브젝트",
        "content-json-empty-array": "빈 배열",
+       "deprecated-self-close-category": "유효하지 않은, 스스로 닫는 HTML 태그를 사용하고 있는 문서",
+       "deprecated-self-close-category-desc": "이 문서는 <code>&lt;b/></code>나 <code>&lt;span/></code>와 같은 유효하지 않은, 스스로 닫는 HTML 태그를 포함하고 있습니다. 이 태그들의 동작은 곧 HTML5 사양과 일관되도록 변경될 예정이므로 위키텍스트에서 이것들을 사용하는 것은 권장되지 않습니다.",
        "duplicate-args-warning": "<strong>경고:</strong> [[:$1]] 문서는 [[:$2]]에 \"$3\" 변수를 하나보다 더 많이 입력했습니다. 마지막으로 주어진 값만이 유효합니다.",
        "duplicate-args-category": "중복된 인수를 사용한 틀의 호출을 포함한 문서",
        "duplicate-args-category-desc": "문서에 <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code>나 <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>와 같은, 인수를 중복하여 사용한 틀 호출을 포함합니다.",
        "undo-nochange": "편집이 이미 되돌려진 것으로 나타납니다.",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|토론]])의 $1판 편집을 되돌림",
        "undo-summary-username-hidden": "숨겨진 사용자가 $1 판을 되돌림",
-       "cantcreateaccounttitle": "계정을 만들 수 없습니다",
        "cantcreateaccount-text": "현재 IP 주소('''$1''')는 [[User:$3|$3]] 사용자에 의해 계정 만들기가 차단되었습니다.\n\n차단 이유는 다음과 같습니다: $2",
-       "cantcreateaccount-range-text": "당신의 IP 주소(<strong>$4</strong>)가 속해 있는 <strong>$1</strong> 대역에서의 계정 생성을 [[User:$3|$3]] 사용자가 차단하였습니다.\n\n$3 사용자가 제시한 이유는 \"$2\"입니다.",
+       "cantcreateaccount-range-text": "당신의 IP 주소(<strong>$4</strong>)가 속해 있는 <strong>$1</strong> 대역에서의 계정 만들기를 [[User:$3|$3]]님이 차단했습니다.\n\n$3님이 제시한 이유는 \"$2\"입니다.",
        "viewpagelogs": "이 문서의 기록 보기",
        "nohistory": "이 문서는 편집 역사가 없습니다.",
        "currentrev": "최신판",
        "currentrev-asof": "$1 기준 최신판",
        "revisionasof": "$1 판",
-       "revision-info": "{{GENDER:$6|$2}} 사용자의 $1 판$7",
+       "revision-info": "{{GENDER:$6|$2}}의 $1 판$7",
        "previousrevision": "← 이전 판",
        "nextrevision": "다음 판 →",
        "currentrevisionlink": "최신판",
        "rev-showdeleted": "보이기",
        "revisiondelete": "판 삭제/되살리기",
        "revdelete-nooldid-title": "대상 판이 잘못되었습니다.",
-       "revdelete-nooldid-text": "이 기능을 수행할 특정 판을 제시하지 않았거나 해당 판이 없습니다. 또는 현재 판을 숨기려 하고 있을 수도 있습니다.",
+       "revdelete-nooldid-text": "이 기능을 수행할 대상 판을 지정하지 않았거나 해당 판이 존재하지 않습니다. 아니면 현재 판을 숨기려 하고 있을 수도 있습니다.",
        "revdelete-no-file": "해당 파일이 존재하지 않습니다.",
        "revdelete-show-file-confirm": "정말 \"<nowiki>$1</nowiki>\" 파일의 삭제된 $2 $3 버전을 보시겠습니까?",
        "revdelete-show-file-submit": "예",
        "mergehistory-comment": "[[:$1]] 문서를 [[:$2]] 문서로 합침: $3",
        "mergehistory-same-destination": "원본 문서 이름과 대상 문서 이름은 같을 수 없습니다",
        "mergehistory-reason": "이유:",
-       "mergelog": "합치기 기록",
+       "mergelog": "병합 기록",
        "revertmerge": "합치기 해제",
-       "mergelogpagetext": "다음은 한 문서의 역사를 다른 문서의 역사와 í\95©ì¹\9c ìµ\9cê·¼ ê¸°ë¡\9dì\9e\85ë\8b\88ë\8b¤.",
+       "mergelogpagetext": "다음은 한 문서의 역사를 다른 문서의 역사와 ë³\91í\95©í\95\9c ìµ\9cê·¼ ê¸°ë¡\9dì\9e\85ë\8b\88ë\8b¤.",
        "history-title": "\"$1\"의 편집 역사",
        "difference-title": "\"$1\"의 두 판 사이의 차이",
        "difference-title-multipage": "\"$1\" 문서와 \"$2\" 문서 사이의 차이",
        "search-category": "(분류 $1)",
        "search-file-match": "(내용이 일치하는 파일 있음)",
        "search-suggest": "$1 문서를 찾고 있으신가요?",
-       "search-rewritten": "$1의 결과를 보여주고 있습니다. $2 대신 검색합니다.",
+       "search-rewritten": "$1의 결과를 보여주고 있습니다. $2을(를) 대신 검색합니다.",
        "search-interwiki-caption": "자매 프로젝트",
        "search-interwiki-default": "$1로부터의 결과:",
        "search-interwiki-more": "(더 보기)",
        "search-external": "바깥 검색",
        "searchdisabled": "{{SITENAME}} 검색이 비활성화되어 있습니다.\n검색이 작동하지 않는 동안 Google을 통해 검색할 수 있습니다.\n검색 엔진의 내용은 최신이 아닐 수 있다는 점을 참고하세요.",
        "search-error": "검색하는 동안 오류가 발생했습니다: $1",
-       "preferences": "사용자 환경 설정",
+       "preferences": "환경 설정",
        "mypreferences": "환경 설정",
        "prefs-edits": "편집 수:",
        "prefsnologintext2": "사용자 환경 설정을 바꾸려면 로그인하세요.",
        "prefs-resetpass": "비밀번호 바꾸기",
        "prefs-changeemail": "이메일 주소를 바꾸거나 제거하기",
        "prefs-setemail": "이메일 주소 설정하기",
-       "prefs-email": "ì\9d´ë©\94ì\9d¼ ì\84¤ì \95",
-       "prefs-rendering": "문ì\84\9c ë³´ì\9d´ê¸° ì\84¤ì \95",
+       "prefs-email": "ì\9d´ë©\94ì\9d¼ ì\98µì\85\98",
+       "prefs-rendering": "ë³´ì\9d´ê¸°",
        "saveprefs": "저장",
        "restoreprefs": "(모든 부분에서) 모두 기본 설정으로 되돌리기",
-       "prefs-editing": "편집 상자",
+       "prefs-editing": "편집",
        "rows": "줄 수:",
        "columns": "열 수:",
        "searchresultshead": "검색",
        "recentchangesdays-max": "최대 $1{{PLURAL:$1|일}}",
        "recentchangescount": "기본으로 보여줄 편집 수:",
        "prefs-help-recentchangescount": "이 설정은 최근 바뀜, 문서 역사와 기록에 적용됩니다.",
-       "prefs-help-watchlist-token2": "내 주시문서 목록의 웹 피드의 비밀 키입니다.\n비밀 키를 알고 있는 사람은 내 주시문서 목록을 읽을 수 있으니 비밀 키를 알리지 마세요.\n필요하다면 [[Special:ResetTokens|비밀 키를 재설정할 수 있습니다]].",
+       "prefs-help-watchlist-token2": "내 주시문서 목록의 웹 피드의 비밀 키입니다.\n이 키를 알고 있는 사람은 내 주시문서 목록을 읽을 수 있으니 이 키를 공유하지 마세요.\n필요하다면 [[Special:ResetTokens|이 키를 재설정할 수 있습니다]].",
        "savedprefs": "설정을 저장했습니다.",
        "savedrights": "$1의 사용자 권한이 저장되었습니다.",
        "timezonelegend": "시간대:",
        "email": "이메일",
        "prefs-help-realname": "실명은 선택 사항입니다.\n실명을 입력하면 당신의 기여에 대한 저작자 표시에 쓰이게 될 수 있습니다.",
        "prefs-help-email": "이메일 주소 입력은 선택 사항입니다. 다만 비밀번호를 잊었을 때 비밀번호를 재설정하기 위해 필요합니다.",
-       "prefs-help-email-others": "자신의 문서나 토론 문서에 있는 이메일 보내기 링크로 다른 사용자가 연락할 수 있게 할 수도 있습니다.\n이 경우에도 이메일 주소는 다른 사용자가 연락할 때 공개되지 않습니다.",
+       "prefs-help-email-others": "자신의 사용자 문서나 토론 문서에 있는 이메일 보내기 링크로 다른 사용자가 연락할 수 있게 할 수도 있습니다.\n이 경우에도 이메일 주소는 다른 사용자가 연락할 때 공개되지 않습니다.",
        "prefs-help-email-required": "이메일 주소가 필요합니다.",
        "prefs-info": "기본 정보",
-       "prefs-i18n": "언어 설정",
+       "prefs-i18n": "국제화",
        "prefs-signature": "서명",
        "prefs-dateformat": "날짜 형식",
        "prefs-timeoffset": "시차 설정",
-       "prefs-advancedediting": "ì\9d¼ë°\98 ì\84¤ì \95",
+       "prefs-advancedediting": "ì\9d¼ë°\98 ì\98µì\85\98",
        "prefs-editor": "편집기",
        "prefs-preview": "미리 보기",
-       "prefs-advancedrc": "ê³ ê¸\89 ì\84¤ì \95",
-       "prefs-advancedrendering": "ê³ ê¸\89 ì\84¤ì \95",
-       "prefs-advancedsearchoptions": "ê³ ê¸\89 ì\84¤ì \95",
-       "prefs-advancedwatchlist": "ê³ ê¸\89 ì\84¤ì \95",
-       "prefs-displayrc": "보이기 설정",
-       "prefs-displaywatchlist": "보이기 설정",
+       "prefs-advancedrc": "ê³ ê¸\89 ì\98µì\85\98",
+       "prefs-advancedrendering": "ê³ ê¸\89 ì\98µì\85\98",
+       "prefs-advancedsearchoptions": "ê³ ê¸\89 ì\98µì\85\98",
+       "prefs-advancedwatchlist": "ê³ ê¸\89 ì\98µì\85\98",
+       "prefs-displayrc": "표시 설정",
+       "prefs-displaywatchlist": "표시 설정",
        "prefs-tokenwatchlist": "토큰",
        "prefs-diffs": "차이",
        "prefs-help-prefershttps": "이 사용자 환경 설정은 다음 로그인부터 적용됩니다.",
        "right-move-rootuserpages": "최상위 사용자 문서 이동",
        "right-move-categorypages": "분류 문서 이동",
        "right-movefile": "파일 이동",
-       "right-suppressredirect": "문ì\84\9c를 ì\98®ê¸¸ 때 원래 문서 이름으로 된 넘겨주기를 만들지 않기",
+       "right-suppressredirect": "문ì\84\9c를 ì\9d´ë\8f\99í\95  때 원래 문서 이름으로 된 넘겨주기를 만들지 않기",
        "right-upload": "파일 올리기",
        "right-reupload": "이미 존재하는 파일을 다시 올리기",
        "right-reupload-own": "자신이 이미 올린 파일 덮어쓰기",
        "right-applychangetags": "자신이 편집할 때 [[Special:Tags|태그]]를 적용하기",
        "right-changetags": "문서의 특정 판과 특정 기록 항목에 임의의 [[Special:Tags|태그]]를 추가하거나 제거하기",
        "right-deletechangetags": "데이터베이스에서 [[Special:Tags|태그]]를 지우기",
-       "grant-generic": "\"$1\" ê¶\8cí\95\9c ë²\88ë\93¤",
+       "grant-generic": "\"$1\" ê¶\8cí\95\9c ë¬¶ì\9d\8c",
        "grant-group-page-interaction": "문서로 상호 작용",
        "grant-group-file-interaction": "미디어로 상호 작용",
        "grant-group-watchlist-interaction": "당신의 주시문서로 상호작용",
        "grant-group-high-volume": "대량의 작업 수행",
        "grant-group-customization": "사용자 최적화 및 환경 설정",
        "grant-group-administration": "관리 기능 수행",
+       "grant-group-private-information": "당신에 관한 개인 데이터 접근",
        "grant-group-other": "기타 활동",
        "grant-blockusers": "사용자 차단 또는 차단 해제",
        "grant-createaccount": "계정 만들기",
        "grant-editprotected": "보호된 문서 편집하기",
        "grant-highvolume": "대용량 편집",
        "grant-oversight": "사용자 숨기기와 판 억제",
-       "grant-patrol": "페이지 검토",
+       "grant-patrol": "페이지 변경 사항 점검",
+       "grant-privateinfo": "개인 정보 접근",
        "grant-protect": "문서 보호 및 보호 해제",
        "grant-rollback": "문서의 바뀜을 되돌리기",
        "grant-sendemail": "다른 사용자에게 이메일 보내기",
        "action-applychangetags": "당신이 편집할 때 태그를 적용하기",
        "action-changetags": "문서의 특정 판과 특정 기록 항목에 임의의 태그를 추가하거나 제거하기",
        "action-deletechangetags": "데이터베이스에서 태그를 지우기",
+       "action-purge": "이 문서 새로 고침",
        "nchanges": "$1개 {{PLURAL:$1|바뀜}}",
        "enhancedrc-since-last-visit": "{{PLURAL:$1|마지막 방문 이후}} $1개",
        "enhancedrc-history": "역사",
        "minlength1": "파일 이름은 적어도 1글자 이상이어야 합니다.",
        "illegalfilename": "파일 이름 \"$1\"에는 문서 제목에 허용되지 않는 글자가 포함되어 있습니다.\n이름을 바꾸어 다시 시도해 주세요.",
        "filename-toolong": "파일 이름은 240바이트를 넘을 수 없습니다.",
-       "badfilename": "파일 이름이 \"$1\"로 바뀌었습니다.",
+       "badfilename": "파일 이름이 \"$1\"(으)로 바뀌었습니다.",
        "filetype-mime-mismatch": "\".$1\" 파일 확장자와 이 파일의 MIME 형식($2)이 일치하지 않습니다.",
        "filetype-badmime": "\"$1\" MIME 형식을 가진 파일은 올릴 수 없습니다.",
-       "filetype-bad-ie-mime": "이 파일을 올릴 수 없습니다. Internet Explorer가 잠재적으로 위험한 파일 형식으로 판단하여 사용이 금지된 \"$1\"로 인식할 수 있습니다.",
+       "filetype-bad-ie-mime": "이 파일을 올릴 수 없습니다. Internet Explorer가 잠재적으로 위험한 파일 형식으로 판단하여 사용이 금지된 \"$1\"(으)로 인식할 수 있습니다.",
        "filetype-unwanted-type": "'''\".$1\"''' 확장자는 권장하지 않습니다.\n권장하는 {{PLURAL:$3|파일 확장자}}는 $2입니다.",
        "filetype-banned-type": "'''\".$1\"''' {{PLURAL:$4|형식의 파일은 올릴 수 없습니다}}.\n$2 {{PLURAL:$3|형식만 사용할 수 있습니다}}.",
        "filetype-missing": "파일에 확장자(\".jpg\" 등)가 없습니다.",
        "backend-fail-notexists": "$1 파일이 존재하지 않습니다.",
        "backend-fail-hashes": "비교 해시값을 얻지 못했습니다.",
        "backend-fail-notsame": "\"$1\" 파일과 같은 이름을 가진 다른 파일이 존재합니다.",
-       "backend-fail-invalidpath": "\"$1\"(은)는 올바른 저장소 경로가 아닙니다.",
+       "backend-fail-invalidpath": "\"$1\"은(는) 올바른 저장소 경로가 아닙니다.",
        "backend-fail-delete": "\"$1\" 파일을 삭제할 수 없습니다.",
        "backend-fail-describe": "\"$1\" 파일에 대한 메타데이터를 바꿀 수 없습니다.",
        "backend-fail-alreadyexists": "\"$1\" 파일이 이미 존재합니다.",
        "uploadstash-errclear": "파일을 지우는 데 실패했습니다.",
        "uploadstash-refresh": "파일 목록을 새로 고침",
        "uploadstash-thumbnail": "섬네일 보기",
+       "uploadstash-exception": "비공개로 업로드할 수 없습니다 ($1): \"$2\".",
        "invalid-chunk-offset": "청크 오프셋이 잘못되었습니다.",
        "img-auth-accessdenied": "접근이 거부됨",
        "img-auth-nopathinfo": "PATH_INFO를 잃었습니다.\n서버가 이 정보를 받을 수 있도록 설정되어 있지 않습니다.\n이러한 경우는 서버가 CGI 기반이고 img_auth를 지원하지 않을 때 나타날 수 있습니다.\nhttps://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization을 참조하십시오.",
        "img-auth-public": "img_auth.php는 개인 위키 파일을 바깥 사이트로 전송하는 기능입니다.\n이 기능은 기본적으로 공개적인 위키에서 사용하도록 설계되어 있습니다.\n보안적인 문제로 기본적으로 img_auth.php 기능은 비활성화되어 있습니다.",
        "img-auth-noread": "\"$1\" 파일을 볼 권한이 없습니다.",
        "http-invalid-url": "잘못된 URL: $1",
-       "http-invalid-scheme": "\"$1\"로 시작하는 URL은 지원되지 않습니다.",
+       "http-invalid-scheme": "\"$1\"(으)로 시작하는 URL은 지원되지 않습니다.",
        "http-request-error": "알 수 없는 오류로 HTTP 요청에 실패했습니다.",
        "http-read-error": "HTTP 읽기 오류입니다.",
        "http-timed-out": "HTTP 요청 시간 초과입니다.",
        "filerevert-submit": "되돌리기",
        "filerevert-success": "'''[[Media:$1|$1]]''' 파일을 [$4 $2 $3 버전]으로 되돌렸습니다.",
        "filerevert-badversion": "입력된 시간 기록을 가진 파일의 로컬 버전이 없습니다.",
+       "filerevert-identical": "파일의 현재 버전은 선택한 것과 이미 동일합니다.",
        "filedelete": "$1 삭제하기",
        "filedelete-legend": "파일 삭제하기",
        "filedelete-intro": "'''[[Media:$1|$1]]''' 파일과 모든 역사를 삭제합니다.",
        "randompage": "임의 문서로",
        "randompage-nopages": "{{PLURAL:$2|다음}} 이름공간에는 문서가 없습니다: $1",
        "randomincategory": "분류 안의 임의 문서",
-       "randomincategory-invalidcategory": "\"$1\"(은)는 올바른 분류 이름이 아닙니다.",
+       "randomincategory-invalidcategory": "\"$1\"은(는) 올바른 분류 이름이 아닙니다.",
        "randomincategory-nopages": "[[:Category:$1]]에 문서가 없습니다.",
        "randomincategory-category": "분류:",
        "randomincategory-legend": "분류 안의 임의 문서",
        "protectedpages-performer": "보호한 사용자",
        "protectedpages-params": "보호 변수",
        "protectedpages-reason": "이유",
-       "protectedpages-submit": "문서 보이기",
+       "protectedpages-submit": "문서 표시",
        "protectedpages-unknown-timestamp": "알 수 없음",
        "protectedpages-unknown-performer": "알 수 없는 사용자",
        "protectedtitles": "만들기 보호된 표제어 목록",
        "protectedtitles-summary": "이 페이지는 현재 만들기 보호가 설정되어 있는 문서 제목을 나열합니다. 보호된 기존 문서들의 목록을 보려면 [[{{#special:ProtectedPages}}|{{int:protectedpages}}]]을 보세요.",
        "protectedtitlesempty": "해당 조건에 맞는 만들기 금지 표제어가 없습니다.",
-       "protectedtitles-submit": "제목 보이기",
+       "protectedtitles-submit": "제목 표시",
        "listusers": "사용자 목록",
        "listusers-editsonly": "기여가 있는 사용자만 보기",
        "listusers-creationsort": "계정을 만든 날짜순으로 정렬",
        "booksources-text": "아래의 목록은 새 책이나 중고 책을 판매하는 바깥 사이트로, 원하는 책의 정보를 얻을 수 있습니다.",
        "booksources-invalid-isbn": "입력한 ISBN이 올바르지 않은 것으로 보입니다. 원본과 대조해 문제가 있는지 확인해보세요.",
        "specialloguserlabel": "작업 수행자:",
-       "speciallogtitlelabel": "ë\8c\80ì\83\81 (ì \9c목 ë\98\90ë\8a\94 {{ns:user}}:ì\82¬ì\9a©ì\9e\90\9d´ë¦\84 ì\9c¼ë¡\9c ì\82¬ì\9a©ì\9e\90):",
+       "speciallogtitlelabel": "ë\8c\80ì\83\81 (ì\82¬ì\9a©ì\9e\90ê³\84ì \95ì\97\90 ë\8c\80í\95\9c ì \9c목 ë\98\90ë\8a\94 {{ns:user}}:ì\82¬ì\9a©ì\9e\90ì\9d´ë¦\84):",
        "log": "기록 목록",
        "logeventslist-submit": "보기",
        "all-logs-page": "모든 공개 기록",
        "linksearch-error": "와일드카드는 주소의 처음 부분에만 사용될 수 있습니다.",
        "listusersfrom": "다음으로 시작하는 사용자 보기:",
        "listusers-submit": "보기",
-       "listusers-noresult": "해당 사용자가 없습니다.",
+       "listusers-noresult": "사용자를 찾을 수 없습니다.",
        "listusers-blocked": "(차단됨)",
        "activeusers": "활동하는 사용자 목록",
        "activeusers-intro": "다음은 최근 $1{{PLURAL:$1|일}} 동안 활동한 사용자의 목록입니다.",
        "activeusers-from": "다음으로 시작하는 사용자를 보기:",
        "activeusers-hidebots": "봇을 숨기기",
        "activeusers-hidesysops": "관리자를 숨기기",
-       "activeusers-noresult": "사용자 없습니다.",
+       "activeusers-noresult": "사용자를 찾을 수 없습니다.",
        "activeusers-submit": "활동하고 있는 사용자 보이기",
        "listgrouprights": "사용자 권한 목록",
        "listgrouprights-summary": "다음은 이 위키에 있는 사용자 권한 그룹의 목록입니다.\n각각의 권한에 대해서는 [[{{MediaWiki:Listgrouprights-helppage}}|추가 정보]]를 참조하세요.",
        "listgrants-grant": "부여",
        "listgrants-rights": "권한",
        "trackingcategories": "추적용 분류",
-       "trackingcategories-summary": "이 페이지는 미디어위키 소프트웨어에 의해 자동으로 만들어지는 추적용 분류를 나열합니다. 그들의 이름은 {{ns:8}} 이름공간에 관련된 시스템 메시지를 바꾸어서 바꿀 수 있습니다.",
+       "trackingcategories-summary": "이 페이지는 미디어위키 소프트웨어에 의해 자동으로 만들어지는 추적용 분류를 나열합니다. 이들의 이름은 {{ns:8}} 이름공간의 관련 시스템 메시지들을 변경함으로써 바꿀 수 있습니다.",
        "trackingcategories-msg": "추적용 분류",
        "trackingcategories-name": "메시지 이름",
        "trackingcategories-desc": "분류 포함 기준",
        "emailccsubject": "$1에게 보낸 메시지의 복사본: $2",
        "emailsent": "이메일 보냄",
        "emailsenttext": "이메일을 보냈습니다.",
-       "emailuserfooter": "이 이메일은 {{SITENAME}}의 $1님이 $2 사용자에게 \"{{int:emailuser}}\" 기능을 통해 보냈습니다.",
+       "emailuserfooter": "이 이메일은 {{SITENAME}}의 $1님이 $2에게 \"{{int:emailuser}}\" 기능을 통해 보냈습니다.",
        "usermessage-summary": "시스템 메시지 남기기",
        "usermessage-editor": "시스템 메신저",
        "usermessage-template": "MediaWiki:UserMessage",
        "watchnologin": "로그인하지 않음",
        "addwatch": "주시문서 목록에 추가",
        "addedwatchtext": "\"[[:$1]]\" 문서와 그 토론 문서가 [[Special:Watchlist|주시문서 목록]]에 추가되었습니다.",
+       "addedwatchtext-talk": "\"[[:$1]]\" 문서와 관련 문서가 [[Special:Watchlist|주시문서 목록]]에 추가되었습니다.",
        "addedwatchtext-short": "\"$1\" 문서를 주시문서 목록에 추가했습니다.",
        "removewatch": "주시문서 목록에서 제거",
        "removedwatchtext": "\"[[:$1]]\" 문서와 그 토론 문서가 [[Special:Watchlist|주시문서 목록]]에서 제거되었습니다.",
+       "removedwatchtext-talk": "\"[[:$1]]\" 문서와 관련 문서가 [[Special:Watchlist|주시문서 목록]]에서 제거되었습니다.",
        "removedwatchtext-short": "\"$1\" 문서를 주시문서 목록에 제거했습니다.",
        "watch": "주시",
        "watchthispage": "주시하기",
        "watcherrortext": "\"$1\" 문서에 대한 주시 여부를 바꾸는 중 오류가 발생했습니다.",
        "enotif_reset": "모든 문서를 방문한 것으로 표시하기",
        "enotif_impersonal_salutation": "{{SITENAME}} 사용자",
-       "enotif_subject_deleted": "{{SITENAME}} $1 문서를 $2 사용자가 {{GENDER:$2|삭제했습니다}}",
-       "enotif_subject_created": "{{SITENAME}} $1 문서를 $2 사용자가 {{GENDER:$2|만들었습니다}}",
-       "enotif_subject_moved": "{{SITENAME}} $1 문서를 $2 사용자가 {{GENDER:$2|이동하였습니다}}",
-       "enotif_subject_restored": "{{SITENAME}} $1 문서를 $2 사용자가 {{GENDER:$2|되살렸습니다}}",
-       "enotif_subject_changed": "{{SITENAME}} $1 문서를 $2 사용자가 {{GENDER:$2|바꾸었습니다}}",
-       "enotif_body_intro_deleted": "{{SITENAME}} $1 문서를 $PAGEEDITDATE에 $2 사용자가 {{GENDER:$2|삭제했으며}} $3 에서 볼 수 있습니다.",
-       "enotif_body_intro_created": "{{SITENAME}} $1 문서를 $PAGEEDITDATE에 $2 사용자가 {{GENDER:$2|만들었으며}} 현재 판은 $3 에서 볼 수 있습니다.",
-       "enotif_body_intro_moved": "{{SITENAME}} $1 문서를 $PAGEEDITDATE에 $2 사용자가 {{GENDER:$2|이동하였으며}} 현재 판은 $3 에서 볼 수 있습니다.",
-       "enotif_body_intro_restored": "{{SITENAME}} $1 문서를 $PAGEEDITDATE에 $2 사용자가 {{GENDER:$2|되살렸으며}} 현재 판은 $3 에서 볼 수 있습니다.",
-       "enotif_body_intro_changed": "{{SITENAME}} $1 문서를 $PAGEEDITDATE에 $2 사용자가 {{GENDER:$2|바꾸었으며}} 현재 판은 $3 에서 볼 수 있습니다.",
+       "enotif_subject_deleted": "{{SITENAME}} $1 문서를 $2님이 {{GENDER:$2|삭제했습니다}}",
+       "enotif_subject_created": "{{SITENAME}} $1 문서를 $2님이 {{GENDER:$2|만들었습니다}}",
+       "enotif_subject_moved": "{{SITENAME}} $1 문서를 $2님이 {{GENDER:$2|이동팼습니다}}",
+       "enotif_subject_restored": "{{SITENAME}} $1 문서를 $2님이 {{GENDER:$2|되살렸습니다}}",
+       "enotif_subject_changed": "{{SITENAME}} $1 문서를 $2님이 {{GENDER:$2|바꾸었습니다}}",
+       "enotif_body_intro_deleted": "{{SITENAME}} $1 문서를 $PAGEEDITDATE에 $2님이 {{GENDER:$2|삭제했으며}} $3 에서 볼 수 있습니다.",
+       "enotif_body_intro_created": "{{SITENAME}} $1 문서를 $PAGEEDITDATE에 $2님이 {{GENDER:$2|만들었으며}} 현재 판은 $3 에서 볼 수 있습니다.",
+       "enotif_body_intro_moved": "{{SITENAME}} $1 문서를 $PAGEEDITDATE에 $2님이 {{GENDER:$2|이동하였으며}} 현재 판은 $3 에서 볼 수 있습니다.",
+       "enotif_body_intro_restored": "{{SITENAME}} $1 문서를 $PAGEEDITDATE에 $2님이 {{GENDER:$2|되살렸으며}} 현재 판은 $3 에서 볼 수 있습니다.",
+       "enotif_body_intro_changed": "{{SITENAME}} $1 문서를 $PAGEEDITDATE에 $2님이 {{GENDER:$2|바꾸었으며}} 현재 판은 $3 에서 볼 수 있습니다.",
        "enotif_lastvisited": "마지막으로 방문한 뒤 생긴 모든 바뀜을 보려면 $1을 보세요.",
        "enotif_lastdiff": "이 바뀜을 보려면 $1을 보세요.",
        "enotif_anon_editor": "익명 사용자 $1",
        "rollbacklinkcount-morethan": "{{PLURAL:$1|편집}} $1회 이상 되돌리기",
        "rollbackfailed": "되돌리기 실패",
        "rollback-missingparam": "요청에 필요한 변수가 존재하지 않습니다.",
-       "cantrollback": "편집을 되돌릴 수 없습니다.\n문서를 편집한 사용자가 한 명뿐입니다.",
+       "rollback-missingrevision": "판 데이터를 불러올 수 없습니다.",
+       "cantrollback": "편집을 되돌릴 수 없습니다.\n문서를 편집한 저자가 한 명뿐입니다.",
        "alreadyrolled": "[[:$1]]에서 [[User:$2|$2]] ([[User talk:$2|토론]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]])의 편집을 되돌릴 수 없습니다.\n누군가가 이미 문서를 고치거나 되돌렸습니다.\n\n마지막으로 이 문서를 편집한 사용자는 [[User:$3|$3]] ([[User talk:$3|토론]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])입니다.",
        "editcomment": "편집 요약: <em>$1</em>",
        "revertpage": "[[Special:Contributions/$2|$2]]([[User talk:$2|토론]])의 편집을 [[User:$1|$1]]의 마지막 판으로 되돌림",
        "log-name-contentmodel": "콘텐츠 모델 변경 기록",
        "log-description-contentmodel": "페이지의 콘텐츠 모델과 관련된 행위",
        "logentry-contentmodel-new": "$1님이 비 기본값 \"$5\" 콘텐츠 모델을 사용해  $3 문서를 {{GENDER:$2|만들었습니다}}",
-       "logentry-contentmodel-change": "$1님이 $3 문서의 콘텐츠 모델을 \"$4\"에서 \"$5\"로 {{GENDER:$2|바꾸었습니다}}",
+       "logentry-contentmodel-change": "$1님이 $3 문서의 콘텐츠 모델을 \"$4\"에서 \"$5\"(으)로 {{GENDER:$2|바꾸었습니다}}",
        "logentry-contentmodel-change-revertlink": "되돌리기",
        "logentry-contentmodel-change-revert": "되돌리기",
        "protectlogpage": "문서 보호 기록",
        "protectlogtext": "아래의 목록은 문서 보호에 관한 바뀜에 대한 기록입니다.\n현재 보호된 문서의 목록에 대해서는 [[Special:ProtectedPages|보호된 문서 목록]]을 참조하세요.",
-       "protectedarticle": "사용자가 \"[[$1]]\" 문서를 보호했습니다",
-       "modifiedarticleprotection": "사용자가 \"[[$1]]\" 문서의 보호 설정을 바꿨습니다",
-       "unprotectedarticle": "사용자가 \"[[$1]]\" 문서를 보호 해제했습니다",
-       "movedarticleprotection": "사용자가 문서의 보호 설정을 \"[[$2]]\"에서 \"[[$1]]\"으로 변경했습니다",
+       "protectedarticle": "님이 \"[[$1]]\" 문서를 보호했습니다",
+       "modifiedarticleprotection": "님이 \"[[$1]]\" 문서의 보호 설정을 바꿨습니다",
+       "unprotectedarticle": "\"[[$1]]\" 문서를 보호 해제했습니다",
+       "movedarticleprotection": "님이 문서의 보호 설정을 \"[[$2]]\"에서 \"[[$1]]\"(으)로 이동했습니다",
        "protect-title": "\"$1\" 보호하기",
        "protect-title-notallowed": "\"$1\" 문서의 보호 수준 보기",
-       "prot_1movedto2": "[[$1]] ë¬¸ì\84\9c를 [[$2]] ë¬¸ì\84\9cë¡\9c ì\98®ê¹\80",
+       "prot_1movedto2": "[[$1]] ë¬¸ì\84\9c를 [[$2]] ë¬¸ì\84\9cë¡\9c ì\9d´ë\8f\99í\95¨",
        "protect-badnamespace-title": "보호할 수 없는 이름공간",
        "protect-badnamespace-text": "이 이름공간에 있는 문서는 보호할 수 없습니다.",
        "protect-norestrictiontypes-text": "이 문서는 제한 유형을 사용할 수 없음으로 보호할 수 없습니다.",
        "undeletehistorynoadmin": "이 문서는 삭제되었습니다.\n삭제된 이유와 삭제되기 전에 이 문서를 편집한 사용자가 아래에 나와 있습니다.\n삭제된 문서의 내용을 보려면 관리자 권한이 필요합니다.",
        "undelete-revision": "삭제된 $1 문서의 $4 $5 버전 (기여자 $3):",
        "undeleterevision-missing": "해당 판이 잘못되었거나 존재하지 않습니다.\n잘못된 링크를 따라왔거나, 특정 판이 이미 되살렸거나 데이터베이스에서 제거되었을 수도 있습니다.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|해당 판의|해당 판들의}} <code>rev_id</code>가 이미 사용 중이기 때문에 {{PLURAL:$1|한 개의 판|$1개의 판}}을 복구할 수 없습니다.",
        "undelete-nodiff": "이전의 판이 없습니다.",
        "undeletebtn": "되살리기",
        "undeletelink": "보기/되살리기",
        "undeletedrevisions": "{{PLURAL:$1|판 1개|판 $1개}}를 되살렸습니다",
        "undeletedrevisions-files": "{{PLURAL:$1|판 1개|판 $1개}}와 {{PLURAL:$2|파일 1개|파일 $2개}}를 되살렸습니다",
        "undeletedfiles": "{{PLURAL:$1|파일 1개|파일 $1개}}를 되살렸습니다",
-       "cannotundelete": "되살리는 데 실패했습니다:\n$1",
+       "cannotundelete": "일부 또는 모든 항목의 되살리기를 실패했습니다:\n$1",
        "undeletedpage": "<strong>$1 문서를 되살렸습니다.</strong>\n\n[[Special:Log/delete|삭제 기록]]에서 최근의 삭제 및 되살리기 기록을 볼 수 있습니다.",
        "undelete-header": "최근에 삭제한 문서에 대한 기록은 [[Special:Log/delete|여기]]에서 볼 수 있습니다.",
        "undelete-search-title": "삭제된 문서 검색",
        "sp-contributions-newbies-sub": "새 사용자의 기여",
        "sp-contributions-newbies-title": "새 사용자의 기여",
        "sp-contributions-blocklog": "차단 기록",
-       "sp-contributions-suppresslog": "숨겨진 사용자 기여",
-       "sp-contributions-deleted": "삭제된 사용자 기여",
+       "sp-contributions-suppresslog": "숨겨진 {{GENDER:$1|사용자}} 기여",
+       "sp-contributions-deleted": "삭제된 {{GENDER:$1|사용자}} 기여",
        "sp-contributions-uploads": "올린 파일",
        "sp-contributions-logs": "기록",
        "sp-contributions-talk": "토론",
        "ipb-confirm": "차단 확인",
        "badipaddress": "잘못된 IP 주소",
        "blockipsuccesssub": "차단 완료",
-       "blockipsuccesstext": "[[Special:Contributions/$1|$1]] 사용자가 차단되었습니다.<br />\n차단된 사용자 목록은 [[Special:BlockList|여기]]에서 볼 수 있습니다.",
+       "blockipsuccesstext": "[[Special:Contributions/$1|$1]]님이 차단되었습니다.<br />\n차단된 사용자 목록은 [[Special:BlockList|여기]]에서 볼 수 있습니다.",
        "ipb-blockingself": "자기 자신을 차단하려고 합니다! 정말로 실행할까요?",
        "ipb-confirmhideuser": "사용자를 차단하면서 \"사용자 숨기기\" 설정을 선택했습니다. 모든 기록에서 이 사용자의 사용자 이름을 숨기게 됩니다. 정말로 계정을 숨기시겠습니까?",
        "ipb-confirmaction": "그것을 정말로 하기 원한다면 아래에 있는 \"{{int:ipb-confirm}}\" 부분에 체크하세요",
        "unblockip": "사용자 차단 해제",
        "unblockiptext": "아래의 양식에 차단 해제하려는 IP 주소나 사용자 이름을 입력하세요.",
        "ipusubmit": "차단 해제",
-       "unblocked": "[[User:$1|$1]] 사용자의 차단을 해제했습니다.",
+       "unblocked": "[[User:$1|$1]]의 차단을 해제했습니다.",
        "unblocked-range": "$1 대역이 차단 해제되었습니다.",
        "unblocked-id": "$1 차단이 해제되었습니다.",
-       "unblocked-ip": "[[Special:Contributions/$1|$1]] 사용자가 차단 해제되었습니다.",
+       "unblocked-ip": "[[Special:Contributions/$1|$1]]님이 차단 해제되었습니다.",
        "blocklist": "차단된 사용자 목록",
        "ipblocklist": "차단된 사용자",
        "ipblocklist-legend": "차단된 사용자 찾기",
        "createaccountblock": "계정 만들기 금지됨",
        "emailblock": "이메일 차단됨",
        "blocklist-nousertalk": "자신의 토론 문서 편집 불가",
-       "ipblocklist-empty": "차단된 사용자가 없습니다.",
+       "ipblocklist-empty": "차단 목록이 비어 있습니다.",
        "ipblocklist-no-results": "요청한 IP 주소나 사용자는 차단되지 않았습니다.",
        "blocklink": "차단",
        "unblocklink": "차단 해제",
        "change-blocklink": "차단 설정 바꾸기",
        "contribslink": "기여",
        "emaillink": "이메일 보내기",
-       "autoblocker": "당신의 IP 주소는 최근에 \"[[User:$1|$1]]\" 사용자가 사용하였기 때문에 자동으로 차단되었습니다.\n$1님이 차단된 이유는 다음과 같습니다: \"$2\"",
+       "autoblocker": "당신의 IP 주소는 최근에 \"[[User:$1|$1]]\"님이 사용하였기 때문에 자동으로 차단되었습니다.\n$1님이 차단된 이유는 다음과 같습니다: \"$2\"",
        "blocklogpage": "차단 기록",
        "blocklog-showlog": "이 사용자는 과거에 차단된 기록이 있습니다.\n해당 사용자의 차단 기록은 다음과 같습니다:",
        "blocklog-showsuppresslog": "이 사용자는 과거에 차단된 적이 있으며, 그 기록이 숨겨져 있습니다.\n해당 사용자의 차단 기록은 다음과 같습니다:",
        "lockfilenotwritable": "데이터베이스 잠금 파일에 쓰기 권한이 없습니다.\n데이터베이스를 잠그거나 잠금 해제하려면, 웹 서버에서 이 파일의 쓰기 권한을 설정해야 합니다.",
        "databaselocked": "데이터베이스가 이미 잠겨 있습니다.",
        "databasenotlocked": "데이터베이스가 잠겨 있지 않습니다.",
-       "lockedbyandtime": "({{GENDER:$1|$1}} 사용자가 $2 $3에 잠금)",
+       "lockedbyandtime": "({{GENDER:$1|$1}}님이 $2 $3에 잠금)",
        "move-page": "$1 이동",
        "move-page-legend": "문서 이동",
-       "movepagetext": "ì\95\84ë\9e\98 ì\96\91ì\8b\9dì\9d\84 ì±\84ì\9b\8c ë¬¸ì\84\9cì\9d\98 ì\9d´ë¦\84ì\9d\84 ë°\94꾸고 ëª¨ë\93  ì\97­ì\82¬ë¥¼ ì\83\88 ì\9d´ë¦\84ì\9c¼ë¡\9c ë\90\9c ë¬¸ì\84\9cë¡\9c ì\98®ê¸¸ 수 있습니다.\n원래의 문서는 새 문서로 넘겨주는 링크로만 남게 되고,\n원래 이름을 가리키는 넘겨주기는 자동으로 갱신됩니다.\n만약 이 설정을 선택하지 않았다면 [[Special:DoubleRedirects|이중 넘겨주기]]와 [[Special:BrokenRedirects|끊긴 넘겨주기]]를 확인해주세요.\n당신은 링크와 가리키는 대상이 서로 일치하도록 해야 할 책임이 있습니다.\n\n만약 이미 있는 문서의 이름을 새 이름으로 입력했을 때는 그 문서가 넘겨주기 문서이고 문서 역사가 없어야만 이동이 됩니다. 그렇지 않을 경우에는 이동되지 <strong>않습니다</strong>.\n이것은 실수로 이동한 문서를 되돌릴 수는 있지만, 이미 존재하는 문서 위에 덮어씌울 수는 없다는 것을 의미합니다.\n\n<strong>주의!</strong>\n자주 사용하는 문서를 이동하면 해결하기 어려운 문제를 일으킬 수도 있습니다.\n이동하기 전에 반드시 이 문서를 이동해도 문제가 없는지 확인해주세요.",
-       "movepagetext-noredirectfixer": "ì\95\84ë\9e\98 ì\96\91ì\8b\9dì\9d\84 ì±\84ì\9b\8c ë¬¸ì\84\9cì\9d\98 ì\9d´ë¦\84ì\9d\84 ë°\94꾸고 ëª¨ë\93  ì\97­ì\82¬ë¥¼ ì\83\88 ì\9d´ë¦\84ì\9c¼ë¡\9c ë\90\9c ë¬¸ì\84\9cë¡\9c ì\9d´ë\8f\99í\95  ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤.\nì\9b\90ë\9e\98ì\9d\98 ë¬¸ì\84\9cë\8a\94 ì\83\88 ë¬¸ì\84\9cë¡\9c ë\84\98겨주ë\8a\94 ë§\81í\81¬ë¡\9cë§\8c ë\82¨ê²\8c ë\90©ë\8b\88ë\8b¤.\n[[Special:DoubleRedirects|ì\9d´ì¤\91 ë\84\98겨주기]]ì\99\80 [[Special:BrokenRedirects|ë\81\8a긴 ë\84\98겨주기]]를 í\99\95ì\9d¸í\95´ì£¼ì\84¸ì\9a\94.\në\8b¹ì\8b ì\9d\80 ë§\81í\81¬ì\99\80 ê°\80리í\82¤ë\8a\94 ë\8c\80ì\83\81ì\9d´ ì\84\9cë¡\9c ì\9d¼ì¹\98í\95\98ë\8f\84ë¡\9d í\95´ì\95¼ í\95  ì±\85ì\9e\84ì\9d´ ì\9e\88ì\8aµë\8b\88ë\8b¤.\n\në§\8cì\95½ ì\9d´ë¯¸ ì\9e\88ë\8a\94 ë¬¸ì\84\9cì\9d\98 ì\9d´ë¦\84ì\9d\84 ì\83\88 ì\9d´ë¦\84ì\9c¼ë¡\9c ì\9e\85ë ¥í\96\88ì\9d\84 ë\95\8cë\8a\94 ê·¸ ë¬¸ì\84\9cê°\80 ë\84\98겨주기 ë¬¸ì\84\9cì\9d´ê³  ë¬¸ì\84\9c ì\97­ì\82¬ê°\80 ì\97\86ì\96´ì\95¼ë§\8c ì\9d´ë\8f\99ì\9d´ ë\90©ë\8b\88ë\8b¤. ê·¸ë \87ì§\80 ì\95\8aì\9d\84 ê²½ì\9a°ì\97\90ë\8a\94 ì\9d´ë\8f\99ë\90\98ì§\80 <strong>ì\95\8aì\8aµë\8b\88ë\8b¤</strong>.\nì\9d´ê²\83ì\9d\80 ì\8b¤ì\88\98ë¡\9c ì\98®ê¸´ 문서를 되돌릴 수는 있지만, 이미 존재하는 문서 위에 덮어씌울 수는 없다는 것을 의미합니다.\n\n<strong>주의!</strong>\n자주 사용하는 문서를 이동하면 해결하기 어려운 문제를 일으킬 수도 있습니다.\n이동하기 전에 반드시 이 문서를 이동해도 문제가 없는지 확인해주세요.",
+       "movepagetext": "ì\95\84ë\9e\98 ì\96\91ì\8b\9dì\9d\84 ì±\84ì\9b\8c ë¬¸ì\84\9cì\9d\98 ì\9d´ë¦\84ì\9d\84 ë°\94꾸고 ëª¨ë\93  ì\97­ì\82¬ë¥¼ ì\83\88 ì\9d´ë¦\84ì\9c¼ë¡\9c ë\90\9c ë¬¸ì\84\9cë¡\9c ì\9d´ë\8f\99í\95  수 있습니다.\n원래의 문서는 새 문서로 넘겨주는 링크로만 남게 되고,\n원래 이름을 가리키는 넘겨주기는 자동으로 갱신됩니다.\n만약 이 설정을 선택하지 않았다면 [[Special:DoubleRedirects|이중 넘겨주기]]와 [[Special:BrokenRedirects|끊긴 넘겨주기]]를 확인해주세요.\n당신은 링크와 가리키는 대상이 서로 일치하도록 해야 할 책임이 있습니다.\n\n만약 이미 있는 문서의 이름을 새 이름으로 입력했을 때는 그 문서가 넘겨주기 문서이고 문서 역사가 없어야만 이동이 됩니다. 그렇지 않을 경우에는 이동되지 <strong>않습니다</strong>.\n이것은 실수로 이동한 문서를 되돌릴 수는 있지만, 이미 존재하는 문서 위에 덮어씌울 수는 없다는 것을 의미합니다.\n\n<strong>주의!</strong>\n자주 사용하는 문서를 이동하면 해결하기 어려운 문제를 일으킬 수도 있습니다.\n이동하기 전에 반드시 이 문서를 이동해도 문제가 없는지 확인해주세요.",
+       "movepagetext-noredirectfixer": "ì\95\84ë\9e\98 ì\96\91ì\8b\9dì\9d\84 ì±\84ì\9b\8c ë¬¸ì\84\9cì\9d\98 ì\9d´ë¦\84ì\9d\84 ë°\94꾸고 ëª¨ë\93  ì\97­ì\82¬ë¥¼ ì\83\88 ì\9d´ë¦\84ì\9c¼ë¡\9c ë\90\9c ë¬¸ì\84\9cë¡\9c ì\9d´ë\8f\99í\95  ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤.\nì\9b\90ë\9e\98ì\9d\98 ë¬¸ì\84\9cë\8a\94 ì\83\88 ë¬¸ì\84\9cë¡\9c ë\84\98겨주ë\8a\94 ë§\81í\81¬ë¡\9cë§\8c ë\82¨ê²\8c ë\90©ë\8b\88ë\8b¤.\n[[Special:DoubleRedirects|ì\9d´ì¤\91 ë\84\98겨주기]]ì\99\80 [[Special:BrokenRedirects|ë\81\8a긴 ë\84\98겨주기]]를 í\99\95ì\9d¸í\95´ì£¼ì\84¸ì\9a\94.\në\8b¹ì\8b ì\9d\80 ë§\81í\81¬ì\99\80 ê°\80리í\82¤ë\8a\94 ë\8c\80ì\83\81ì\9d´ ì\84\9cë¡\9c ì\9d¼ì¹\98í\95\98ë\8f\84ë¡\9d í\95´ì\95¼ í\95  ì±\85ì\9e\84ì\9d´ ì\9e\88ì\8aµë\8b\88ë\8b¤.\n\në§\8cì\95½ ì\9d´ë¯¸ ì\9e\88ë\8a\94 ë¬¸ì\84\9cì\9d\98 ì\9d´ë¦\84ì\9d\84 ì\83\88 ì\9d´ë¦\84ì\9c¼ë¡\9c ì\9e\85ë ¥í\96\88ì\9d\84 ë\95\8cë\8a\94 ê·¸ ë¬¸ì\84\9cê°\80 ë\84\98겨주기 ë¬¸ì\84\9cì\9d´ê³  ë¬¸ì\84\9c ì\97­ì\82¬ê°\80 ì\97\86ì\96´ì\95¼ë§\8c ì\9d´ë\8f\99ì\9d´ ë\90©ë\8b\88ë\8b¤. ê·¸ë \87ì§\80 ì\95\8aì\9d\84 ê²½ì\9a°ì\97\90ë\8a\94 ì\9d´ë\8f\99ë\90\98ì§\80 <strong>ì\95\8aì\8aµë\8b\88ë\8b¤</strong>.\nì\9d´ê²\83ì\9d\80 ì\8b¤ì\88\98ë¡\9c ì\9d´ë\8f\99í\95\9c 문서를 되돌릴 수는 있지만, 이미 존재하는 문서 위에 덮어씌울 수는 없다는 것을 의미합니다.\n\n<strong>주의!</strong>\n자주 사용하는 문서를 이동하면 해결하기 어려운 문제를 일으킬 수도 있습니다.\n이동하기 전에 반드시 이 문서를 이동해도 문제가 없는지 확인해주세요.",
        "movepagetalktext": "이 칸에 체크하면, 딸린 토론 문서가 자동으로 이동됩니다. 다만 비어있지 않은 토론 문서가 있다면 이동되지 않습니다.\n\n이러한 경우에는 수동으로 이동하거나 합쳐야 합니다.",
        "moveuserpage-warning": "<strong>경고:</strong> 사용자 문서를 이동하려고 하고 있습니다. 사용자 문서만 이동되며 사용자 이름이 바뀌지 <strong>않는다</strong>는 점을 참고하세요.",
        "movecategorypage-warning": "<strong>경고:</strong> 분류 문서를 이동하려고 합니다. 해당 문서만 이동되고 옛 분류에 있는 문서는 새 분류 안에 다시 분류되지 <em>않음</em>을 참고하세요.",
        "movenologintext": "문서를 이동하려면 [[Special:UserLogin|로그인]]해야 합니다.",
-       "movenotallowed": "문ì\84\9c를 ì\98®ê¸¸ 권한이 없습니다.",
-       "movenotallowedfile": "í\8c\8cì\9d¼ì\9d\84 ì\98®ê¸¸ 권한이 없습니다.",
-       "cant-move-user-page": "ì\82¬ì\9a©ì\9e\90 ë¬¸ì\84\9c를 ì\98®ê¸¸ 권한이 없습니다 (하위 문서는 예외).",
-       "cant-move-to-user-page": "문ì\84\9c를 ì\82¬ì\9a©ì\9e\90 ë¬¸ì\84\9cë¡\9c ì\98®ê¸¸ 권한이 없습니다 (하위 문서는 예외).",
-       "cant-move-category-page": "ë¶\84ë¥\98 ë¬¸ì\84\9c를 ì\98®ê¸¸ 권한이 없습니다.",
-       "cant-move-to-category-page": "문ì\84\9c를 ë¶\84ë¥\98 ë¬¸ì\84\9cë¡\9c ì\98®ê¸¸ 권한이 없습니다.",
+       "movenotallowed": "문ì\84\9c를 ì\9d´ë\8f\99í\95  권한이 없습니다.",
+       "movenotallowedfile": "í\8c\8cì\9d¼ì\9d\84 ì\9d´ë\8f\99í\95  권한이 없습니다.",
+       "cant-move-user-page": "ì\82¬ì\9a©ì\9e\90 ë¬¸ì\84\9c를 ì\9d´ë\8f\99í\95  권한이 없습니다 (하위 문서는 예외).",
+       "cant-move-to-user-page": "문ì\84\9c를 ì\82¬ì\9a©ì\9e\90 ë¬¸ì\84\9cë¡\9c ì\9d´ë\8f\99í\95  권한이 없습니다 (하위 문서는 예외).",
+       "cant-move-category-page": "ë¶\84ë¥\98 ë¬¸ì\84\9c를 ì\9d´ë\8f\99í\95  권한이 없습니다.",
+       "cant-move-to-category-page": "문ì\84\9c를 ë¶\84ë¥\98 ë¬¸ì\84\9cë¡\9c ì\9d´ë\8f\99í\95  권한이 없습니다.",
        "newtitle": "새 제목:",
        "move-watch": "원래 문서와 대상 문서를 주시하기",
        "movepagebtn": "문서 이동",
        "movepage-moved-redirect": "넘겨주기 문서를 만들었습니다.",
        "movepage-moved-noredirect": "넘겨주기 문서를 남기지 않았습니다.",
        "articleexists": "문서가 이미 존재하거나 이름이 올바르지 않습니다.\n다른 이름을 선택하세요.",
-       "cantmove-titleprotected": "ì\83\88ë¡\9cì\9a´ ì \9c목ì\9c¼ë¡\9c ë¬¸ì\84\9c를 ë§\8cë\93\9cë\8a\94 ê²\83ì\9d´ ê¸\88ì§\80ë\90\98ì\96´ ì\9e\88ì\96´ ë¬¸ì\84\9c를 ì\98®ê¸¸ ì\88\98 ì\97\86ì\8aµë\8b\88ë\8b¤",
+       "cantmove-titleprotected": "ì\83\88ë¡\9cì\9a´ ì \9c목ì\9c¼ë¡\9c ë¬¸ì\84\9c를 ë§\8cë\93\9cë\8a\94 ê²\83ì\9d´ ê¸\88ì§\80ë\90\98ì\96´ ì\9e\88ì\96´ ë¬¸ì\84\9c를 ì\9d´ë\8f\99í\95  ì\88\98 ì\97\86ì\8aµë\8b\88ë\8b¤.",
        "movetalk": "딸린 토론 문서도 이동하기",
        "move-subpages": "하위 문서도 이동 ($1개까지)",
        "move-talk-subpages": "토론 문서의 하위 문서도 이동하기 ($1개까지)",
        "movepage-page-exists": "$1 문서가 이미 존재하므로 자동으로 덮어쓸 수 없습니다.",
        "movepage-page-moved": "\"$1\" 문서를 \"$2\" 문서로 이동했습니다.",
-       "movepage-page-unmoved": "$1 ë¬¸ì\84\9c를 $2 ë¬¸ì\84\9cë¡\9c ì\98®ê¸¸ 수 없습니다.",
+       "movepage-page-unmoved": "$1 ë¬¸ì\84\9c를 $2 ë¬¸ì\84\9cë¡\9c ì\9d´ë\8f\99í\95  수 없습니다.",
        "movepage-max-pages": "{{PLURAL:$1|문서}}를 최대 $1개 이동했으며 나머지 문서는 자동으로 이동하지 않습니다.",
        "movelogpage": "이동 기록",
-       "movelogpagetext": "ì\95\84ë\9e\98ë\8a\94 ì\98®ê²¨ì§\84 모든 문서의 목록입니다.",
+       "movelogpagetext": "ì\95\84ë\9e\98ë\8a\94 ì\9d´ë\8f\99ë\90\9c 모든 문서의 목록입니다.",
        "movesubpage": "{{PLURAL:$1|하위 문서}}",
        "movesubpagetext": "이 문서에는 다음 {{PLURAL:$1|하위 문서}} $1개가 있습니다.",
        "movenosubpage": "이 문서에는 하위 문서가 존재하지 않습니다.",
        "delete_and_move_text": "== 삭제 필요 ==\n이동하려는 제목으로 된 \"[[:$1]]\" 문서가 이미 존재합니다.\n삭제하고 이동할까요?",
        "delete_and_move_confirm": "네. 문서를 삭제합니다",
        "delete_and_move_reason": "\"[[$1]]\"에서 문서를 이동하기 위해 삭제함",
-       "selfmove": "ì\9b\90ë\9e\98 ì \9c목과 ì\9d´ë\8f\99í\95\98ë ¤ë\8a\94 ì \9c목ì\9d´ ê°\99ì\8aµë\8b\88ë\8b¤.\nê°\99ì\9d\80 ì \9c목ì\9c¼ë¡\9cë\8a\94 ë¬¸ì\84\9c룰 ì\98®ê¸¸ 수 없습니다.",
+       "selfmove": "ì\9b\90ë\9e\98 ì \9c목과 ì\9d´ë\8f\99í\95\98ë ¤ë\8a\94 ì \9c목ì\9d´ ê°\99ì\8aµë\8b\88ë\8b¤.\nê°\99ì\9d\80 ì \9c목ì\9c¼ë¡\9cë\8a\94 ë¬¸ì\84\9c룰 ì\9d´ë\8f\99í\95  수 없습니다.",
        "immobile-source-namespace": "\"$1\" 이름공간에 속한 문서는 이동시킬 수 없습니다.",
        "immobile-target-namespace": "\"$1\" 이름공간에 속한 문서는 이동시킬 수 없습니다.",
-       "immobile-target-namespace-iw": "ì\9d¸í\84°ì\9c\84í\82¤ ë§\81í\81¬ë¥¼ ë\84\98ì\96´ ë¬¸ì\84\9c를 ì\98®ê¸¸ 수 없습니다.",
-       "immobile-source-page": "ì\9d´ ë¬¸ì\84\9cë\8a\94 ì\98®ê¸¸ 수 없습니다.",
-       "immobile-target-page": "새 이름으로 옮길 수 없습니다.",
+       "immobile-target-namespace-iw": "ì\9d¸í\84°ì\9c\84í\82¤ ë§\81í\81¬ë¥¼ ë\84\98ì\96´ ë¬¸ì\84\9c를 ì\9d´ë\8f\99í\95  수 없습니다.",
+       "immobile-source-page": "ì\9d´ ë¬¸ì\84\9cë\8a\94 ì\9d´ë\8f\99í\95  수 없습니다.",
+       "immobile-target-page": "목표 제목으로 이동할 수 없습니다.",
        "bad-target-model": "원하는 대상은 다른 내용 모델을 사용합니다. $1에서 $2로 변환할 수 없습니다.",
-       "imagenocrossnamespace": "í\8c\8cì\9d¼ì\9d\84 í\8c\8cì\9d¼ì\9d´ ì\95\84ë\8b\8c ì\9d´ë¦\84ê³µê°\84ì\9c¼ë¡\9c ì\98®ê¸¸ 수 없습니다.",
-       "nonfile-cannot-move-to-file": "í\8c\8cì\9d¼ì\9d´ ì\95\84ë\8b\8c ë¬¸ì\84\9c를 í\8c\8cì\9d¼ ì\9d´ë¦\84ê³µê°\84ì\9c¼ë¡\9c ì\98®ê¸¸ 수 없습니다.",
+       "imagenocrossnamespace": "í\8c\8cì\9d¼ì\9d\84 í\8c\8cì\9d¼ì\9d´ ì\95\84ë\8b\8c ì\9d´ë¦\84ê³µê°\84ì\9c¼ë¡\9c ì\9d´ë\8f\99í\95  수 없습니다.",
+       "nonfile-cannot-move-to-file": "í\8c\8cì\9d¼ì\9d´ ì\95\84ë\8b\8c ë¬¸ì\84\9c를 í\8c\8cì\9d¼ ì\9d´ë¦\84ê³µê°\84ì\9c¼ë¡\9c ì\9d´ë\8f\99í\95  수 없습니다.",
        "imagetypemismatch": "새 파일의 확장자가 원래의 확장자와 일치하지 않습니다.",
        "imageinvalidfilename": "새 파일 이름이 잘못되었습니다.",
        "fix-double-redirects": "원래 제목을 가리키는 넘겨주기를 새로 고침",
-       "move-leave-redirect": "ì\98®ê¸´ 뒤 넘겨주기를 남기기",
+       "move-leave-redirect": "ì\9d´ë\8f\99í\95\9c 뒤 넘겨주기를 남기기",
        "protectedpagemovewarning": "<strong>경고:</strong> 이 문서는 관리자만 이동할 수 있도록 보호되어 있습니다.\n최근 기록을 참조를 위해 아래에 제공합니다:",
        "semiprotectedpagemovewarning": "<strong>참고:</strong> 이 문서는 등록된 사용자만이 이동할 수 있도록 보호되어 있습니다.\n최근 기록 내용이 참조를 위해 아래에 제공됩니다:",
        "move-over-sharedrepo": "== 파일이 존재함 ==\n[[:$1]] 파일이 공용 저장소에 있습니다. 이 이름으로 파일을 이동하면 공용의 파일을 덮어쓰게 될 것입니다.",
        "tooltip-pt-anontalk": "현재 사용하는 IP 주소에 대한 토론 문서",
        "tooltip-pt-preferences": "{{GENDER:|사용자}} 환경 설정",
        "tooltip-pt-watchlist": "주시문서에 대한 바뀜 목록",
-       "tooltip-pt-mycontris": "{{GENDER:|내}} 기여 목록",
+       "tooltip-pt-mycontris": "{{GENDER:|내}} 기여 목록",
        "tooltip-pt-anoncontribs": "이 IP 주소의 편집 목록",
        "tooltip-pt-login": "꼭 로그인해야 하는 것은 아니지만, 로그인을 권장합니다.",
        "tooltip-pt-logout": "로그아웃",
        "anonymous": "{{SITENAME}} 익명 {{PLURAL:$1|사용자}}",
        "siteuser": "{{SITENAME}} 사용자 $1",
        "anonuser": "{{SITENAME}} 익명 사용자 $1",
-       "lastmodifiedatby": "이 문서는 $3 사용자가 $1 $2에 마지막으로 바꾸었습니다.",
+       "lastmodifiedatby": "이 문서는 $3님이 $1 $2에 마지막으로 바꾸었습니다.",
        "othercontribs": "$1의 작업을 바탕으로 합니다.",
        "others": "기타",
        "siteusers": "{{SITENAME}} {{PLURAL:$2|{{GENDER:$1|사용자}}}} $1",
        "creditspage": "문서 기여자",
        "nocredits": "이 문서에서는 기여자 정보가 없습니다.",
        "spamprotectiontitle": "스팸 막기 필터",
-       "spamprotectiontext": "ì\8a¤í\8c¸ í\95\84í\84°ê°\80 ë¬¸ì\84\9c ì \80ì\9e¥ì\9d\84 ë§\89ì\95\98ì\8aµë\8b\88ë\8b¤.\në°\94ê¹¥ ì\82¬ì\9d´í\8a¸ë¡\9c ì\97°ê²°í\95\98ë\8a\94 ë§\81í\81¬ ì¤\91ì\97\90 ë¸\94ë\9e\99리ì\8a¤í\8a¸ì\97\90 í\8f¬í\95¨ë\90\9c ì\82¬ì\9d´í\8a¸ê°\80 ì\9e\88ì\9d\84 ê²\83ì\9e\85니다.",
+       "spamprotectiontext": "ì \80ì\9e¥í\95\98ë ¤ë\8d\98 ê¸\80ì\9d\80 ì\8a¤í\8c¸ í\95\84í\84°ì\97\90 ì°¨ë\8b¨ë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤.\në¸\94ë\9e\99리ì\8a¤í\8a¸ì\97\90 í\8f¬í\95¨ë\90\9c ì\99¸ë¶\80 ì\82¬ì\9d´í\8a¸ì\9d\98 ë§\81í\81¬ ë\95\8c문ì\9d¼ ì\88\98 ì\9e\88ì\8aµ니다.",
        "spamprotectionmatch": "문제가 되는 부분은 다음과 같습니다: $1",
        "spambot_username": "미디어위키 스팸 정리",
-       "spam_reverting": "$1(을)를 포함하지 않는 최신 버전으로 되돌림",
+       "spam_reverting": "$1에 대한 링크를 포함하지 않는 최신 버전으로 되돌림",
        "spam_blanking": "모든 버전에 $1 링크를 포함하고 있어 차단함",
        "spam_deleting": "모든 버전에 $1 링크를 포함하고 있어 삭제함",
        "simpleantispam-label": "스팸 방지 검사입니다.\n이것을 입력하지 <strong>마세요</strong>!",
        "pageinfo-header-edits": "편집 역사",
        "pageinfo-header-restrictions": "문서 보호",
        "pageinfo-header-properties": "문서 속성",
-       "pageinfo-display-title": "보여줄 제목",
+       "pageinfo-display-title": "표시 제목",
        "pageinfo-default-sort": "기본 정렬 키",
        "pageinfo-length": "문서 길이 (바이트)",
        "pageinfo-article-id": "문서 ID",
        "pageinfo-language": "문서 내용 언어",
        "pageinfo-content-model": "문서 내용 모델",
+       "pageinfo-content-model-change": "변경",
        "pageinfo-robot-policy": "로봇에 의한 색인",
        "pageinfo-robot-index": "허용됨",
        "pageinfo-robot-noindex": "불허됨",
        "confirmemail_success": "이메일 주소가 인증되었습니다.\n이제 [[Special:UserLogin|로그인]]해서 위키를 사용하세요.",
        "confirmemail_loggedin": "이메일 주소가 인증되었습니다.",
        "confirmemail_subject": "{{SITENAME}} 이메일 주소 인증",
-       "confirmemail_body": "$1 IP 주소를 사용하는 사용자가\n{{SITENAME}}의 \"$2\" 계정에 이메일 인증 신청을 했습니다.\n\n이 계정이 당신의 계정이고 {{SITENAME}}에서 이메일 기능을 활성화하려면\n아래 주소를 열어서 이메일 인증을 해 주세요:\n\n$3\n\n당신의 계정이 아니라면,\n이메일 인증 신청을 취소하기 위해 아래의 주소를 열어주세요:\n\n$5\n\n인증 코드는 $4에 만료됩니다.",
-       "confirmemail_body_changed": "$1 IP 주소를 사용하는 사용자가\n{{SITENAME}}의 \"$2\" 계정의 이메일 주소를 바꾸었습니다.\n\n이 계정이 당신의 계정이고 {{SITENAME}}에서 이메일 기능을 활성화하려면\n아래 주소를 열어서 이메일 인증을 해 주세요:\n\n$3\n\n당신의 계정이 아니라면,\n이메일 인증 신청을 취소하기 위해 아래의 주소를 열어주세요:\n\n$5\n\n인증 코드는 $4에 만료됩니다.",
-       "confirmemail_body_set": "$1 IP 주소를 사용하는 사용자가\n{{SITENAME}}의 \"$2\" 계정의 이메일 주소를 지정하였습니다.\n\n이 계정이 당신의 계정이고 {{SITENAME}}에서 이메일 기능을\n활성화하려면 아래 주소를 열어서 이메일 인증을 해 주세요:\n\n$3\n\n당신의 계정이 아니라면,\n이메일 인증 신청을 취소하기 위해 아래의 주소를 열어주세요:\n\n$5\n\n인증 코드는 $4에 만료됩니다.",
+       "confirmemail_body": "당신일 수도 있는 $1 IP 주소를 사용하는 사용자가\n{{SITENAME}}의 \"$2\" 계정에 이메일 인증 신청을 했습니다.\n\n이 계정이 당신의 계정이고 {{SITENAME}}에서 이메일 기능을 활성화하려면\n아래 주소를 열어서 이메일 인증을 해 주세요:\n\n$3\n\n당신의 계정이 아니라면,\n이메일 인증 신청을 취소하기 위해 아래의 주소를 열어주세요:\n\n$5\n\n인증 코드는 $4에 만료됩니다.",
+       "confirmemail_body_changed": "당신일 수도 있는 $1 IP 주소를 사용하는 사용자가\n{{SITENAME}}의 \"$2\" 계정의 이메일 주소를 바꾸었습니다.\n\n이 계정이 당신의 계정이고 {{SITENAME}}에서 이메일 기능을 활성화하려면\n아래 주소를 열어서 이메일 인증을 해 주세요:\n\n$3\n\n당신의 계정이 아니라면,\n이메일 인증 신청을 취소하기 위해 아래의 주소를 열어주세요:\n\n$5\n\n인증 코드는 $4에 만료됩니다.",
+       "confirmemail_body_set": "당신일 수도 있는 $1 IP 주소를 사용하는 사용자가\n{{SITENAME}}의 \"$2\" 계정의 이메일 주소를 지정하였습니다.\n\n이 계정이 당신의 계정이고 {{SITENAME}}에서 이메일 기능을\n활성화하려면 아래 주소를 열어서 이메일 인증을 해 주세요:\n\n$3\n\n당신의 계정이 아니라면,\n이메일 인증 신청을 취소하기 위해 아래의 주소를 열어주세요:\n\n$5\n\n인증 코드는 $4에 만료됩니다.",
        "confirmemail_invalidated": "이메일 확인이 취소됨",
        "invalidateemail": "이메일 확인 취소",
        "notificationemail_subject_changed": "{{SITENAME}}의 등록된 이메일 주소가 변경되었습니다",
        "notificationemail_subject_removed": "{{SITENAME}}의 등록된 이메일 주소가 제거되었습니다",
-       "notificationemail_body_changed": "IP 주소 $1에 속하는 누군가가 {{SITENAME}}의 사용자 \"$2\" 계정의 이메일 주소를 \"$3\"으로 변경하였습니다.\n\n지금 이 글을 보고 계신 사용자로 추정되지만 만약 본인이 아닌 경우, 지금 바로 사이트 관리자에게 문의하십시오.",
-       "notificationemail_body_removed": "IP 주소 $1에 속하는 누군가가 {{SITENAME}}의 사용자 \"$2\" 계정의 이메일 주소를 제거하였습니다.\n\n지금 이 글을 보고 계신 사용자로 추정되지만 만약 본인이 아닌 경우, 지금 바로 사이트 관리자에게 문의하십시오.",
+       "notificationemail_body_changed": "당신일 수도 있는 IP 주소 $1에 속하는 사용자가\n{{SITENAME}}의 사용자 \"$2\" 계정의 이메일 주소를 \"$3\"(으)로 바꾸었습니다.\n\n지금 이 글을 보고 계신 사용자로 추정되지만 만약 본인이 아닌 경우, 지금 바로 사이트 관리자에게 문의하십시오.",
+       "notificationemail_body_removed": "당신일 수도 있는 IP 주소 $1에 속하는 사용자가 {{SITENAME}}의 사용자 \"$2\" 계정의 이메일 주소를 제거하였습니다.\n\n지금 이 글을 보고 계신 사용자로 추정되지만 만약 본인이 아닌 경우, 지금 바로 사이트 관리자에게 문의하십시오.",
        "scarytranscludedisabled": "[인터위키가 비활성되어 있습니다]",
        "scarytranscludefailed": "[$1 틀을 불러오는 데에 실패했습니다]",
        "scarytranscludefailed-httpstatus": "[$1 틀을 가져오는 데 실패했습니다: HTTP $2]",
        "table_pager_limit_submit": "확인",
        "table_pager_empty": "결과 없음",
        "autosumm-blank": "문서를 비움",
-       "autosumm-replace": "문ì\84\9c ë\82´ì\9a©ì\9d\84 \"$1\"ì\9c¼로 바꿈",
+       "autosumm-replace": "ë\82´ì\9a©ì\9d\84 \"$1\"(ì\9c¼)로 바꿈",
        "autoredircomment": "[[$1]] 문서로 넘겨주기",
        "autosumm-new": "새 문서: $1",
        "autosumm-newblank": "빈 문서를 만듦",
        "tags-edit-success": "바뀜이 적용되었습니다.",
        "tags-edit-failure": "수정 사항이 적용될 수 없습니다: $1",
        "tags-edit-nooldid-title": "대상 판이 잘못되었습니다",
-       "tags-edit-nooldid-text": "이 기능을 수행할 특정 판을 제시하지 않았거나 해당 판이 없습니다.",
+       "tags-edit-nooldid-text": "이 기능을 수행할 대상 판을 지정하지 않았거나 해당 판이 존재하지 않습니다.",
        "tags-edit-none-selected": "추가하거나 제거할 최소 하나 이상의 태그를 선택하세요.",
        "comparepages": "문서 비교",
        "compare-page1": "첫 번째 문서",
        "logentry-suppress-delete": "$1님이 $3 문서를 {{GENDER:$2|숨겼습니다}}",
        "logentry-suppress-event": "$1님이 비공개적으로 $3의 {{PLURAL:$5|기록 $5개}}에 대해 보이기 설정을 {{GENDER:$2|바꾸었습니다}}: $4",
        "logentry-suppress-revision": "$1님이 비공개적으로 $3 문서의 {{PLURAL:$5|판 $5개}}에 대해 보이기 설정을 {{GENDER:$2|바꾸었습니다}}: $4",
-       "logentry-suppress-event-legacy": "$1님이 비공개적으로 $3의 항목에 대한 보이기 설정을 {{GENDER:$2|바꾸었습니다}}",
+       "logentry-suppress-event-legacy": "$1님이 비공개적으로 $3의 기록 항목에 대한 보이기 설정을 {{GENDER:$2|바꾸었습니다}}",
        "logentry-suppress-revision-legacy": "$1님이 비공개적으로 $3 문서의 특정 판에 대한 보이기 설정을 {{GENDER:$2|바꾸었습니다}}",
        "revdelete-content-hid": "내용 숨겨짐",
        "revdelete-summary-hid": "편집 요약 숨겨짐",
        "revdelete-restricted": "관리자에게 제한을 적용함",
        "revdelete-unrestricted": "관리자에 대한 제한을 해제함",
        "logentry-block-block": "$1님이 {{GENDER:$4|$3}}님을 $5 {{GENDER:$2|차단했습니다}} $6",
-       "logentry-block-unblock": "$1님이 {{GENDER:$4|$3}} 사용자의 {{GENDER:$2|차단을 해제했습니다}}",
-       "logentry-block-reblock": "$1 님이 {{GENDER:$4|$3}} 사용자의 차단 기간을 $5 설정으로 {{GENDER:$2|바꾸었습니다}} $6",
+       "logentry-block-unblock": "$1님이 {{GENDER:$4|$3}}의 {{GENDER:$2|차단을 해제했습니다}}",
+       "logentry-block-reblock": "$1님이 {{GENDER:$4|$3}}님의 차단 기간을 $5 설정으로 {{GENDER:$2|바꾸었습니다}} $6",
        "logentry-suppress-block": "$1님이 {{GENDER:$4|$3}} 사용자를 $5 {{GENDER:$2|차단했습니다}} $6",
-       "logentry-suppress-reblock": "$1 님이 {{GENDER:$4|$3}} 사용자의 차단 기간을 $5 설정으로 {{GENDER:$2|바꾸었습니다}} $6",
+       "logentry-suppress-reblock": "$1님이 {{GENDER:$4|$3}}님의 차단 기간을 $5 설정으로 {{GENDER:$2|바꾸었습니다}} $6",
        "logentry-import-upload": "$1님이 $3 문서를 파일 올리기로 {{GENDER:$2|가져왔습니다}}",
        "logentry-import-upload-details": "$1님이 $3 문서 ({{PLURAL:$4|판}} $4개)를 파일 올리기로 {{GENDER:$2|가져왔습니다}}",
        "logentry-import-interwiki": "$1님이 $3 문서를 다른 위키에서 {{GENDER:$2|가져왔습니다}}",
        "logentry-protect-protect-cascade": "$1님이 $3 문서를 {{GENDER:$2|보호했습니다}} $4 [연쇄적]",
        "logentry-protect-modify": "$1님이 $3 문서의 보호 수준을 {{GENDER:$2|바꾸었습니다}} $4",
        "logentry-protect-modify-cascade": "$1님이 $3 문서의 보호 수준을 {{GENDER:$2|바꾸었습니다}} $4 [연쇄적]",
-       "logentry-rights-rights": "$1님이 $3 사용자의 권한을 $4에서 $5(으)로 {{GENDER:$2|바꾸었습니다}}",
-       "logentry-rights-rights-legacy": "$1님이 $3 사용자의 권한을 {{GENDER:$2|바꾸었습니다}}",
-       "logentry-rights-autopromote": "$1님이 권한을 자동적으로 $4에서 $5로 {{GENDER:$2|바꾸었습니다}}",
+       "logentry-rights-rights": "$1님이 {{GENDER:$6|$3}}님의 권한을 $4에서 $5(으)로 {{GENDER:$2|바꾸었습니다}}",
+       "logentry-rights-rights-legacy": "$1님이 $3의 권한을 {{GENDER:$2|바꾸었습니다}}",
+       "logentry-rights-autopromote": "$1님이 권한을 자동적으로 $4에서 $5(으)로 {{GENDER:$2|바꾸었습니다}}",
        "logentry-upload-upload": "$1님이 $3 파일을 {{GENDER:$2|올렸습니다}}",
        "logentry-upload-overwrite": "$1님이 $3의 새 판을 {{GENDER:$2|올렸습니다}}",
        "logentry-upload-revert": "$1님이 $3 파일을 {{GENDER:$2|올렸습니다}}",
        "log-name-managetags": "태그 관리 기록",
        "log-description-managetags": "이 문서는 [[Special:Tags|태그]]에 관한 관리 작업의 목록입니다. 이 기록에는 관리자가 직접 실행한 동작만이 기록되며, 위키 소프트웨어에 의해 태그가 생성 및 삭제되는 경우는 기록되지 않습니다.",
        "logentry-managetags-create": "$1님이 \"$4\" 태그를 {{GENDER:$2|만들었습니다}}",
-       "logentry-managetags-delete": "$1ë\8b\98ì\9d´ \"$4\" í\83\9c그를 ì\82­ì \9cí\95\98ì\98\80습니다 ($5개 {{PLURAL:$5|판 및 기록}}에서 {{GENDER:$2|제거되었습니다}})",
+       "logentry-managetags-delete": "$1ë\8b\98ì\9d´ \"$4\" í\83\9c그를 ì\82­ì \9cí\96\88습니다 ($5개 {{PLURAL:$5|판 및 기록}}에서 {{GENDER:$2|제거되었습니다}})",
        "logentry-managetags-activate": "$1님이 \"$4\" 태그를 사용자나 봇이 사용하도록 {{GENDER:$2|활성화시켰습니다}}",
        "logentry-managetags-deactivate": "$1님이 \"$4\" 태그를 사용자나 봇이 사용하지 못하도록 {{GENDER:$2|비활성화시켰습니다}}",
        "log-name-tag": "태그 기록",
        "logentry-tag-update-add-logentry": "$1님이 $3 문서의 기록 항목 $5에 $6 {{PLURAL:$7|태그}}를 {{GENDER:$2|추가했습니다}}",
        "logentry-tag-update-remove-revision": "$1님이 $3 문서의 $4판에서 $8 {{PLURAL:$9|태그}}를 {{GENDER:$2|제거했습니다}}",
        "logentry-tag-update-remove-logentry": "$1님이 $3 문서의 기록 항목 $5에서 $8 {{PLURAL:$9|태그}}를 {{GENDER:$2|제거했습니다}}",
-       "logentry-tag-update-revision": "$1님이 $3 문서의 $4 판에서 태그를 {{GENDER:$2|업데이트했습니다}} ($6 {{PLURAL:$7|추가함}}; $8 {{PLURAL:$9|제거함}})",
+       "logentry-tag-update-revision": "$1님이 $3 문서의 $4판에서 태그를 {{GENDER:$2|업데이트했습니다}} ($6 {{PLURAL:$7|추가함}}; $8 {{PLURAL:$9|제거함}})",
        "logentry-tag-update-logentry": "$1님이 $3 문서의 기록 항목 $5에 있는 태그를 {{GENDER:$2|업데이트했습니다}} ($6 {{PLURAL:$7|추가함}}; $8 {{PLURAL:$9|제거함}})",
        "rightsnone": "(없음)",
        "revdelete-summary": "편집 요약",
        "expand_templates_generate_rawhtml": "원본 HTML 보이기",
        "expand_templates_preview": "미리 보기",
        "expand_templates_preview_fail_html": "<em>{{SITENAME}}에서 순수 HTML 입력을 허용한 상태에서 세션 데이터가 분실되었습니다. 그러므로 자바스크립트 공격을 예방하기 위해 미리 보기는 숨겨져 있습니다.</em>\n\n<strong>적합하게 미리 보기를 시도했다면 다시 시도해 주십시오.</strong>\n그래도 되지 않으면 [[Special:UserLogout|로그아웃]]한 다음 다시 로그인하여 사용자의 브라우저가 이 사이트에서 쿠키를 허용하는지 확인해 주십시오.",
-       "expand_templates_preview_fail_html_anon": "<em>{{SITENAME}}에서 순수 HTML 입력을 허용한 상태에서 사용자는 로그인되어 있지 않습니다. 그러므로 자바스크립트 공격을 예방하기 위해 미리 보기는 숨겨져 있습니다.</em>\n\n<strong>적합하게 미리 보기를 시도했다면 [[Special:UserLogin|로그인]]한 다음 다시 시도해 주십시오.",
+       "expand_templates_preview_fail_html_anon": "<em>{{SITENAME}}에서 순수 HTML 입력을 허용한 상태에서 사용자는 로그인되어 있지 않습니다. 그러므로 자바스크립트 공격을 예방하기 위해 미리 보기는 숨겨져 있습니다.</em>\n\n<strong>적합하게 미리 보기를 시도했다면 [[Special:UserLogin|로그인]]한 다음 다시 시도해 주십시오.</strong>",
        "expand_templates_input_missing": "전개할 내용을 입력해야 합니다.",
        "pagelanguage": "문서 언어 바꾸기",
        "pagelang-name": "문서",
        "mw-widgets-dateinput-no-date": "선택된 날짜 없음",
        "mw-widgets-titleinput-description-new-page": "문서가 존재하지 않습니다",
        "mw-widgets-titleinput-description-redirect": "$1 문서로 넘겨주기",
-       "api-error-blacklisted": "이 파일을 설명할 수 있는 다른 제목을 선택하세요.",
        "sessionmanager-tie": "여러 요청 인증 유형 결합할 수 없습니다: $1.",
        "sessionprovider-generic": "$1 세션",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "쿠키 기반 세션",
        "log-action-filter-newusers": "계정 생성 종류:",
        "log-action-filter-patrol": "점검 종류:",
        "log-action-filter-protect": "보호 종류:",
-       "log-action-filter-rights": "권한 변경 종류",
-       "log-action-filter-suppress": "숨기기 종류",
+       "log-action-filter-rights": "권한 변경 종류:",
+       "log-action-filter-suppress": "숨기기 종류:",
        "log-action-filter-upload": "업로드 종류:",
        "log-action-filter-all": "모두",
        "log-action-filter-block-block": "차단",
        "linkaccounts-submit": "계정 연결",
        "unlinkaccounts": "계정 연결 해제",
        "unlinkaccounts-success": "계정의 연결이 해제되었습니다.",
-       "authenticationdatachange-ignored": "인증 데이터 변경을 처리하지 못했습니다. 제공자를 설정하지 않으셨습니까?"
+       "authenticationdatachange-ignored": "인증 데이터 변경을 처리하지 못했습니다. 제공자를 설정하지 않으셨습니까?",
+       "userjsispublic": "주목해 주십시오: 자바스크립트의 하위 문서들은 다른 사용자들이 볼 수 있기 때문에 기밀 데이터를 포함해서는 안 됩니다.",
+       "usercssispublic": "주목해 주십시오: CSS의 하위 문서들은 다른 사용자들이 볼 수 있기 때문에 기밀 데이터를 포함해서는 안 됩니다."
 }
index 17f733d..6afca3d 100644 (file)
@@ -89,7 +89,7 @@
        "listingcontinuesabbrev": "jatkuu",
        "about": "Tietuo",
        "article": "Šivu",
-       "newwindow": "(avautuu uuvešša ikkunašša)",
+       "newwindow": "(avautuu uuvvešša ikkunašša)",
        "cancel": "Keškeytä",
        "moredotdotdot": "Lisyä...",
        "mypage": "Šivu",
        "retrievedfrom": "Lähte - \"$1\"",
        "youhavenewmessages": "{{PLURAL:$3|Šiula on}} $1 ($2).",
        "editsection": "kohentele",
-       "editold": "kohenna",
+       "editold": "kohentele",
        "viewsourceold": "näytä wikiteksti",
        "editlink": "kohentele",
        "viewsourcelink": "näytä lähtehkoodi",
        "nstab-main": "Šivu",
        "nstab-user": "Käyttäjäšivu",
        "nstab-media": "Mediašivu",
-       "nstab-special": "Toimintosivu",
+       "nstab-special": "Toimintošivu",
        "nstab-project": "Projektišivu",
        "nstab-image": "Faili",
        "nstab-template": "Malli",
        "databaseerror-query": "Kyšely: $1",
        "databaseerror-error": "Hairahuš: $1",
        "badtitle": "Šivun nimi ei kelpua",
-       "badtitletext": "Šiun annetti šivunimi oli virheellini, tyhjä tahi viärin linkitetty kielienvälini tahi wikienvälini nimi.\nSiinä suattau olla yksi tahi usiempi sellani merkki, kumpasta ei voi käyttyä šivujen nimissä.",
+       "badtitletext": "Šiun pyytämä šivunimi oli virhiellini, tyhjä tahi viärin linkitetty kielienvälini tahi wikienvälini nimi.\nŠiinä šuattau olla yksi tahi ušiempi šemmoni merkki, kumpaista ei voi käyttyä šivujen nimilöissä.",
        "viewsource": "näytä lähtehkoodi",
        "yourname": "Käyttäjänimi:",
        "userlogin-yourname": "Käyttäjänimi",
        "userlogin-yourname-ph": "Kirjuta käyttäjänimi",
        "yourpassword": "Tunnuššana:",
-       "userlogin-yourpassword": "Tunnuššana",
-       "userlogin-yourpassword-ph": "Kirjuta tunnuššana",
-       "createacct-yourpassword-ph": "Kirjuta tunnuššana",
-       "createacct-yourpasswordagain": "Vahvista tunnuššana",
-       "createacct-yourpasswordagain-ph": "Kirjuta tunnuššana uuvveštah",
+       "userlogin-yourpassword": "Šalašana",
+       "userlogin-yourpassword-ph": "Kirjuta šalašana",
+       "createacct-yourpassword-ph": "Kirjuta šalašana",
+       "createacct-yourpasswordagain": "Vahvista šalašana",
+       "createacct-yourpasswordagain-ph": "Kirjuta šalašana uuvveštah",
        "userlogin-remembermypassword": "Pijä miut kirjuttautunuona",
        "login": "Kirjauvu šiämeh",
        "nav-login-createaccount": "Kirjauvu šiämeh / rekisteriyvy",
        "userlogin-joinproject": "Liity {{SITENAME}}",
        "createaccount": "Luaji käyttäjätili",
        "gotaccountlink": "Kirjauvu šiämeh",
-       "userlogin-resetpassword-link": "Unohitko tunnuššanan?",
+       "userlogin-resetpassword-link": "Unohitko šalašanan?",
        "userlogin-helplink2": "Apu kirjuttautumiseh",
-       "createacct-emailoptional": "Sähköpostiadressi (ei tarviče vältämättäh kirjuttua)",
-       "createacct-email-ph": "Kirjuta oma sähkopostiadressi",
+       "createacct-emailoptional": "Šähköpoštiošoiteh (ei tarviče välttämättä kirjuttua)",
+       "createacct-email-ph": "Kirjuta oma šähköpoštiošoiteh",
        "createaccountreason": "Šyy:",
        "createacct-reason": "Šyy",
        "createacct-submit": "Luaji oma käyttäjätunnuš",
        "passwordreset": "Kirjuta šalašana uuvveštah",
        "passwordreset-username": "Käyttäjänimi:",
        "passwordreset-domain": "Domain:",
-       "bold_sample": "Sankie teksta",
-       "bold_tip": "Sankie teksta",
+       "bold_sample": "Lihavointa",
+       "bold_tip": "Lihavointa",
        "italic_sample": "Kuršivoitu teksti",
        "italic_tip": "Kuršivointa",
        "link_sample": "Linkki",
        "link_tip": "Šiämilinkki",
        "extlink_sample": "http://www.example.com linkin nimi",
-       "extlink_tip": "Ulkohini linkki (musta http:// alkuh)",
-       "headline_sample": "Rupriekkatekšta",
-       "headline_tip": "Tašon 2 rupriekka",
+       "extlink_tip": "Ulkopuolini linkki (muissa laittua http:// alkuh)",
+       "headline_sample": "Očikkoteksti",
+       "headline_tip": "Tašon 2 očikko",
        "nowiki_sample": "Lisyä täh teksti, kumpaista ei pijä korjata",
        "nowiki_tip": "Penkuo wiki-korjaušta",
        "image_tip": "Tallennettu faili",
        "media_tip": "Linkki failih",
        "sig_tip": "Teijän allakirjutuš ta aika",
-       "hr_tip": "Horišontualini viiva (älä käytä liijakši)",
+       "hr_tip": "Vuakašuora viiva (elä käytä liijakši)",
        "summary": "Yhtehveto",
-       "minoredit": "Tämä on pieni muutoš",
+       "minoredit": "Tämä on pieni kohennuš",
        "watchthis": "Tarkkaile tätä šivuo",
        "savearticle": "Tallenna šivu",
        "showpreview": "Esikačo",
        "blockednoreason": "ei šyytä annettu",
        "loginreqlink": "kirjauvu šiämeh",
        "newarticle": "(Uuši)",
-       "newarticletext": "Olet piätynyn šivulla, kumpasta ei ole. Ku luatie šivu, rupie kirjuttamah alla olijah juaššiekkah (kačo [$1 ohjavošivulta] lisätietuo).\nKu ollet piätynyn tänne petties, käytä livaimen <strong>mennä järilleh</strong>-toimintuo.",
+       "newarticletext": "Linkki toi šivulla, kumpaista ei vielä ole.\nVoit luuvva šivun kirjuttamalla alla olijah ikkunah (kačo [$1 ohješivulta] lisätietoja). \nJoš et haluo luuvva šivuo, käytä šelaimen paluutoimintuo.",
        "noarticletext": "Tällä šivulla ei ole juuri nyt tekstie.\nVoit [[Special:Search/{{PAGENAME}}|eččie šivun nimellä]] muilta šivuilta,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} eččie šitä koškijua logie],\ntahi [{{fullurl:{{FULLPAGENAME}}|action=edit}} luuvva tämän šivun]</span>.",
        "noarticletext-nopermission": "Tällä šivulla ei ole juuri nyt tekstie.\nVoit [[Special:Search/{{PAGENAME}}|eččie šivun nimellä]] muilta šivuilta, tahi <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} eččie šivuo koškijua logie]</span>, ka šiula ei ole oikeutta luuvva tätä šivuo.",
-       "editing": "Kohennellah sivuu $1",
-       "creating": "Luajitah šivuu \"$1\"",
+       "editing": "Kohennellah šivuo $1",
+       "creating": "Luajitah šivuo \"$1\"",
        "editingsection": "Kohennellah $1 (alaluku)",
        "templatesused": "Tällä šivulla {{PLURAL:$1|käytetty malli|käytetyt mallit}}:",
        "template-protected": "(šuojattu)",
        "template-semiprotected": "(ošittain šuojattu)",
        "hiddencategories": "Tämä šivu kuuluu {{PLURAL:$1|1 peitettyh kategorijah|$1 peitettylöih kategorijoih}}:",
        "permissionserrorstext-withaction": "Šiula ei ole oikeutta {{lcfirst:$2}} {{PLURAL:$1|šeuruavašta šyyštä|šeuruavista šyistä}}:",
-       "moveddeleted-notice": "Tämä šivu on otettu iäre. Šivun iäreotanta- ta siirtoistorija ollah annettu alla viittavuksena.",
+       "moveddeleted-notice": "Tämä šivu on poistettu.\nAlla on tämän šivun poisto- ta šiirtoistorija.",
        "viewpagelogs": "Näytä tämän šivun lokit",
        "currentrev-asof": "Nykyni versija $1",
        "revisionasof": "Versija $1",
        "difference-title": "Ero šivun ”$1” versijien välillä",
        "lineno": "Rivi $1:",
        "editundo": "lakauttua",
-       "diff-multi-sameuser": "({{PLURAL:$1|Yksi keskitason versija |$1 keskitason versijua}} samalta käyttäjältä ei näytetty)",
+       "diff-multi-sameuser": "({{PLURAL:$1|Yksi keškitašon versija |$1 keškitašon versijua}} šamalta käyttäjältä ei näytetty)",
        "searchresults": "Eččimisen tulokšet",
        "searchresults-title": "Eči \"$1\"",
        "prevn": "iellini {{PLURAL:$1|$1}}",
        "nextn-title": "Šeuruava $1 {{PLURAL:$1|tuloš|tulokšet}}",
        "shown-title": "Näytä $1 {{PLURAL:$1|tuloš|tulošta}} šivulla",
        "viewprevnext": "Näytä ($1 {{int:pipe-separator}} $2) ($3)",
-       "searchmenu-new": "<strong>Luaji šivu \"[[:$1]]\" täh wikih!</strong> {{PLURAL:$2|0=|Kačo niise šiun eččolla löyvetyt šivut.|Kačo niise löyvetyt ečon tulokšet.}}",
+       "searchmenu-new": "<strong>Luaji šivu \"[[:$1]]\" täh wikih!</strong> {{PLURAL:$2|0=|Kačo niise šiun ečolla löyvetyt šivut.|Kačo niise löyvetyt ečon tulokšet.}}",
        "searchprofile-articles": "Šisältöšivut",
        "searchprofile-images": "Multimedija",
        "searchprofile-everything": "Kaikki",
        "search-result-size": "$1 ({{PLURAL:$2|1 šana|$2 šanua}})",
        "search-redirect": "(šiirretty $1)",
        "search-section": "(alaluku $1)",
-       "search-suggest": "Tarkoititko: $1",
+       "search-suggest": "Tarkotitko: $1",
        "searchall": "kaikki",
        "search-showingresults": "{{PLURAL:$4|Tuloš <strong>$1</strong> / <strong>$3</strong>|Tulokšet <strong>$1 - $2</strong> / <strong>$3</strong>}}",
        "search-nonefound": "Kyšelyh ei löytyn tulokšie.",
        "newpageletter": "U",
        "boteditletter": "b",
        "rc-change-size-new": "$1 {{PLURAL:$1|baitti|baittie}} muutokšien jälkeh",
-       "recentchangeslinked": "Toini toišeh liittyjät kohennukšet",
+       "recentchangeslinked": "Toini toiseh liittyjät kohennukšet",
        "recentchangeslinked-toolbox": "Toini toiseh liittyjät kohennukšet",
        "recentchangeslinked-title": "Šivuo \"$1\" koškijat muutokšet",
        "recentchangeslinked-summary": "Tämä on niijen šivujen korjaukšien luvettelo, kumpasih viittuau tämä šivu (tahi šiih luokkah kuulujat). Šivut, kumpaset kuulutah [[Special:Watchlist|teijän valvontaluvetteloh]], ollah kirjutettu <strong>lihavalla</strong>.",
        "booksources-search-legend": "Eči kirjalähtehie",
        "booksources-search": "Eči",
        "log": "Lokit",
-       "allarticles": "Kai artikkelit",
+       "allarticles": "Kaikki šivut",
        "allpagessubmit": "Mäne",
        "categories": "Kategorijat",
        "linksearch-ok": "Ečindy",
        "watchlist": "Kaččuolistu",
        "mywatchlist": "Valvontaluvettelo",
        "watch": "Valvo",
-       "dellogpage": "Iäreotantaistorija",
+       "dellogpage": "Poistoloki",
        "rollbacklink": "Tuo entini versija",
        "rollbacklinkcount": "palauta $1 {{PLURAL:$1|muutoš|muutošta}}",
-       "protectlogpage": "Šuojavusistorija",
+       "protectlogpage": "Šuojaušloki",
        "protectcomment": "Šyy",
        "restriction-edit": "Kohennuš",
        "undelete-search-submit": "Ečindy",
        "tooltip-namespace_association": "Valiče tämä kohta, još haluot lisätä niise valittuh nimitilah liittyjät pakina- tahi aihenimitilat",
        "blanknamespace": "(Piä)",
        "contributions": "{{GENDER:$1|Käyttäjän}} panoš",
-       "mycontris": "Omat kohennukšet",
+       "mycontris": "Kirjutukšet",
        "anoncontribs": "Omat kohennukšet",
-       "month": "Täššä kuušša (tahi aijempi)",
+       "month": "Kuukauši",
        "year": "Vuosi",
        "sp-contributions-submit": "Ečindy",
        "whatlinkshere": "Linkit tänne",
-       "whatlinkshere-title": "Šivut, kumpaset košketah šivuu \"$1\"",
+       "whatlinkshere-title": "Šivut, kumpaset viitatah šivulla \"$1\"",
        "whatlinkshere-page": "Šivu:",
        "linkshere": "Šeuruavilta šivuilta on linkki šivulla <strong>[[:$1]]</strong>:",
        "isredirect": "ohjauššivu",
        "istemplate": "šisällytetty",
        "isimage": "failin linkki",
-       "whatlinkshere-prev": "{{PLURAL:$1|iellini|iellini $1}}",
-       "whatlinkshere-next": "{{PLURAL:$1|tulija|tulija $1}}",
+       "whatlinkshere-prev": "{{PLURAL:$1|iellini|iellistä $1}}",
+       "whatlinkshere-next": "{{PLURAL:$1|tulija|tulijua $1}}",
        "whatlinkshere-links": "← linkit",
        "whatlinkshere-hideredirs": "$1 ohjaukšet",
        "whatlinkshere-hidetrans": "$1 šisällytykšet",
        "allmessages-filter-translate": "Kiännä",
        "thumbnail-more": "Šuurenna",
        "tooltip-pt-userpage": "{{GENDER:|Oma käyttäjäšivu}}",
-       "tooltip-pt-mytalk": "Oma paginsivu",
-       "tooltip-pt-preferences": "{{GENDER:|Omat ašetukset}}",
+       "tooltip-pt-mytalk": "Oma pakinašivu",
+       "tooltip-pt-preferences": "{{GENDER:|Omat ašetukšet}}",
        "tooltip-pt-watchlist": "Lista šivuista, kumpasien muutokšie valvot",
        "tooltip-pt-mycontris": "Luvettelo {{GENDER:|šiun}} kirjutukšista",
        "tooltip-pt-login": "Täššä voit kirjuttautuo, ka še ei ole välttämätöintä",
        "tooltip-n-help": "Täštä voit kyšyö apuo",
        "tooltip-t-whatlinkshere": "Kaikkien šivujen luvettelo, kumpasista on linkki täh šivuh",
        "tooltip-t-recentchangeslinked": "Viimesimmät muokkaukšet šivuloilla, kumpasih viitatah tältä šivulta",
-       "tooltip-feed-atom": "Atom-šyöte tällä sivulla",
+       "tooltip-feed-atom": "Atom-šyöteh tällä šivulla",
        "tooltip-t-contributions": "Lista tämän käyttäjän kirjutukšista {{GENDER:$1|this user}}",
        "tooltip-t-upload": "Tallenna failija",
        "tooltip-t-specialpages": "Kaikkien erikoisien šivujen luvettelo",
        "tooltip-preview": "Esikačo muutokšet. Ole hyvä, luaji šitä aina ennen tallennušta.",
        "tooltip-diff": "Näytä luajitut muutokšet",
        "tooltip-rollback": "Pyyhi pois viimesen kohentelijan luatimat muutokšet yhellä kertua",
-       "tooltip-undo": "Kumoamini palauttau tämän muutokšen ta avuau artikkelin esikaččelussa. Yhtehvetokenttäh voi kirjuttua palautukšen šyyn.",
+       "tooltip-undo": "Kumuomini palauttau tämän muutokšen ta avuau artikkelin esikaččelušša. Yhtehvetokenttäh voi kirjuttua palautukšen šyyn.",
        "tooltip-summary": "Kirjuta lyhyt kuvauš",
        "simpleantispam-label": "Anti-spam-tarkissuš. \n<strong>älä</strong> täytä tätä!",
        "pageinfo-toolboxlink": "Šivun tiijot",
        "svg-long-desc": "SVG-faili, oletuštarkkuš $1 × $2 pikselie, failin koko: $3",
        "show-big-image": "Alkuperäni faili",
        "show-big-image-preview": "Tämän failin koko: $1.",
-       "show-big-image-other": "{{PLURAL:$2|Toini resolutija|Toiset resolutijat}}: $1.",
+       "show-big-image-other": "{{PLURAL:$2|Toini resol'uutijo|Toiset resol'uutijot}}: $1.",
        "show-big-image-size": "$1 × $2 kuvapistehtä",
        "ilsubmit": "Ečintä",
        "metadata": "Metatiijoštot",
        "metadata-help": "Failih kuulutah lisätiijot, kumpasie tavan mukah lisätäh digikameralla tahi skannerilla. Još kuvua on muokattu šen alkuperäsen luonnan jälkeh, niin eryähät parametrit voijah erota nykyseštä kuvašta.",
        "metadata-fields": "Šeuruavat metatietojen kentät ruvetah näkymäh kuvašivulla, konša metatietojen taulukko ei ole näkyvissä.\nMuut kentät ollah automattisešti piilotettuja.\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": "Šuunta",
-       "exif-xresolution": "Kuvan resoluutijo vuakašuorašša",
-       "exif-yresolution": "Vertikualini resolutsija",
+       "exif-xresolution": "Kuvan resol'uutijo vuakašuorašša",
+       "exif-yresolution": "Pistyšuora resol'uutijo",
        "exif-datetime": "Failin muutokšen päivä ta aika",
        "exif-make": "Kameran valmistaja",
        "exif-model": "Kameran malli",
index fb4c899..3586530 100644 (file)
        "tagline": "Uß {{GRAMMAR:Dativ | {{ucfirst:{{SITENAME}}}}}}",
        "help": "Hölp",
        "search": "Söhke",
+       "search-ignored-headings": " #<!-- Lohß heh di Reih jenou esu, wi se es --> <pre>\n# Övverschreffte, di beim Söhke överjange wähde.\n# Änderonge werke vun däm Momang aan, woh di Sigg met dä Övverschreff neu önersöhk weed.\n# Dat kann De sellver aanstivvelle, endäm dat De di Sigg afscheijschere deihß, derbei moß De nix draan verändert han.\n# Syntax:\n#   * Alles, wat en ener reih henger „#“ schteiht, bedüüg nix.\n#   * Läddeje Reihje bedüüge nix.\n#   * Reije met jät dren sin jenou de Övverschreffte, di nit opjenumme wääde, met jruuße un kleine Schreff.\nWäbsigge\nLohr och noh\n #</pre> <!-- Lohß heh di Reihj jenou esu, wi se es -->",
        "searchbutton": "em Tex",
        "go": "Lohß Jonn!",
        "searcharticle": "Sigg",
        "databaseerror-query": "Opdraach: $1",
        "databaseerror-function": "Fonxjuhn: $1",
        "databaseerror-error": "Fähler: $1",
+       "transaction-duration-limit-exceeded": "Domedd et Dahtebangk-Koppehre zoh doll henger her hengk, ham_mer heh dä Vörjang affjebroche, weijl dä met $1 de zweijte Jräns vun $2 övverschredde hät.\n\nWann De vill Saache op eijmohl aam Änndere bes, versöhk ens schtatt dämm en Aanzahl kleijnere Ännderonge.",
        "laggedslavemode": "<strong>Opjepass:</strong> Künnt sin, dat heh nit dä neuste Stand vun dä Sigg aanjezeich weed.",
        "readonly": "De Daatebank es jesperrt",
        "enterlockreason": "Jevv aan, woröm un för wie lang dat de Daatebank jesperrt wääde soll",
-       "readonlytext": "De Daatebank es jesperrt. Neu Saache dren avspeichere jeiht jrad nit, un ändere och nit. Dä Jrund: „$1“",
+       "readonlytext": "De Dahtebangk es jeschpächt. Neu Saache dren avschpeijschere jeiht jrahd nit, un et Ändere och nit. Künnt sin wähje en rutihnemähßejje Waadong, un dernoh es alles widder zerögg op Nommahl.\n\nDä Verantwootlesche doför hät beim Deeschmaache als Jrond aanjejovve: „$1“.",
        "missing-article": "Dä Tex för de Sigg „$1“ $2 kunnte mer nit en de Daatebank fenge.\nDe Sigg es velleisch fottjeschmeße udder ömjenannt woode.\nWann dat nidd esu sin sullt, dann had_Der velleich ene Fähler em Projramm vum Wikke jefonge.\nVerzälld et enem [[Special:ListUsers/sysop|Wiki_Köhbeß]],\nun doht em och dä <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Uniform Ressource Locator\">URL</i> vun dä Sigg heh sare.",
        "missingarticle-rev": "(Väsjohn Numero: $1)",
        "missingarticle-diff": "(Ongerscheed zwesche de Versione $1 un $2)",
        "readonly_lag": "De Daatebank es för en koote Zigg automattesch jesperrt, för de Daate vun de ongerjeodente Rääschner mem Houprääschner avzejliiche.",
+       "nonwrite-api-promise-error": "Di Kopp-Reihj „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Promise-Non-Write-API-Action</code>“ wood mem <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"HyperText Transfer Protocol\">HTTP</i> jeschek, ävver di Aanfrohch jingk aan e <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Application Programming Interface\">API</i>-Moduhl zom Schrihve.",
        "internalerror": "De Wiki-Soffwär hät ene Fähler jefunge",
        "internalerror_info": "Enne ennere Fäähler en de ẞoffwäer es opjetrodde: $1",
        "internalerror-fatal-exception": "Ene schlemme Fähler vun dä Zoot „$1“ es opjetrodde.",
        "mypreferencesprotected": "Do häs nit dat Rääsch, Ding Enschtällonge ze verändere.",
        "ns-specialprotected": "{{int:nstab-special}}e künne mer nit ändere.",
        "titleprotected": "Di Övverschreff för en Sigg eß fum [[User:$1|$1]] verbodde woode, un der Jrond wohr: „$2“",
-       "filereadonlyerror": "Mer künne di Dattei „$1“ nit ändere, weil dä Dateijebeshtand „$2“ bloß för ze Lässe doh es.\n\nDä Verantwootlesche doför hät beim Deeschmaache als Jrond aanjejovve: „$3“",
+       "filereadonlyerror": "Mer künne di Dattei „$1“ nit ändere, weil dä Dateijebeschtand „$2“ för bloß ze Lässe ennjeschtalld es.\n\nDä Verantwootlesche doför hät beim Deeschmaache als Jrond aanjejovve: „$3“.",
        "invalidtitle-knownnamespace": "„$3“ es en onjöltijje Övverschreff för em Appachtemang „$2“",
        "invalidtitle-unknownnamespace": "„$2“ es sn onjöltijje Övverschreff em onbikannte Appachtemang met dä Nommer $1",
        "exception-nologin": "Nit enjelogg",
        "createacct-email-ph": "Jiv Ding Addräß för de <i lang=\"en\">e-mail</i> en!",
        "createacct-another-email-ph": "Jivv en Addräß för de <i lang=\"en\" xml:lang=\"en\">e-mail</i> aan",
        "createaccountmail": "Scheck mer en <i lang=\"en\">e-mail</i> met enem neu ußjedaachte Paßwood op Zick",
+       "createaccountmail-help": "Kammer ennsäzze, öm ene Zohjang föe ene Anndere Metmaacher aanzelähje, der ohne dat Paßwoot känne ze lihre.",
        "createacct-realname": "Dinge börjerlesche Nahme, kans De och fott lohße",
        "createaccountreason": "Jrond:",
        "createacct-reason": "Der Jrond udder Aanlaß",
        "createacct-reason-ph": "Woröm deihs De noch ene Zohjang aanlääje?",
+       "createacct-reason-help": "Wadd em Logbohch för et Zohjäng Aanlähje schtonn sull",
        "createacct-submit": "Lohß Jonn!",
        "createacct-another-submit": "Donn jäz enne zohsäzlejje Zohjang aanlääje",
        "createacct-continue-submit": "Wigger maache mem Aanmällde",
        "nocookiesnew": "Dinge neue Metmaacher Name es enjerich, ävver dat automatisch Enlogge wor dann nix.\nSchad.\n{{ucfirst:{{GRAMMAR:Nom|{{SITENAME}}}}}} bruch Cookies, öm ze merke, wä enjelogg es.\nWann De Cookies avjeschald häs en Dingem Brauser, dann kann dat nit laufe.\nSök Der ene Brauser, dä et kann, dun se enschalte, un dann log Dich noch ens neu en, met Dingem neue Metmaacher Name un Passwood.",
        "nocookieslogin": "{{ucfirst:{{GRAMMAR:Nominativ|{{SITENAME}}}}}} bruch <i lang=\"en\">cookies</i> för et Enlogge. Et süht esu us, als hätts De de <i lang=\"en\">cookies</i> avjeschalt. Dun se aanschalte un dann versök et noch ens. Odder söök Der ene Brauser, dä et kann.",
        "nocookiesfornew": "Et wood keine Zohjang opjemaat, weil mer nit jeweß sin künne, woh de Daate her kohme.\nDinge Brauser moß <i lang=\"en\">cookies</i> enjeschalldt han.\nDonn dat prööfe, donn heh di Sigg norr_ens neu laade, un dann versöhk et norr_ens.",
+       "nocookiesforlogin": "{{int:nocookieslogin}}",
+       "createacct-loginerror": "Dä Zohjang es aanjelaat, ävver mer kunnts Desch nit automattesch  enlogge. Be sesu jood un jangk mohm [[Special:UserLogin|Ennlogge vun Hand]].",
        "noname": "Dat jeiht nit als ene Metmaacher Name. Jetz muss De et noch ens versöke.",
        "loginsuccesstitle": "Enjelogg",
        "loginsuccess": "'''Do bes jetz enjelogg {{GRAMMAR:en|{{SITENAME}}}}, un Dinge Name als ene Metmaacher es „$1“.'''",
-       "nosuchuser": "Dä Metmaacher Name „$1“ wor verkihrt.\nJroß- un Kleinboochshtabe maache ene Ungerscheid!\n<br />\nJetz muss De et noch ens versöke.\nUdder donn_[[Special:CreateAccount|ene neue Metmaacher aanmelde]].",
+       "nosuchuser": "Dä Metmaacher-Name „$1“ wohr verkihrt.\nJrohß- un Kleinbohchschtahbe maache ene Ongerscheid!\nSchrihv en eeschtesch, udder donn_[[Special:CreateAccount|ene neue Metmaacher aanmälde]].",
        "nosuchusershort": "Dä Metmaacher Name „$1“ wor verkihrt. Jetz muss De et noch ens versöke.",
        "nouserspecified": "Dat jeiht nit als ene Metmaacher Name",
        "login-userblocked": "Heh dä Kääl es jesperrt. Enlogge verbodde.",
        "noemail": "Dä Metmaacher „$1“ hät en dämm sing Ennschtällonge kein <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>-Adräß aanjejovve.",
        "noemailcreate": "Do moß en jöltijje Adräß för Ding <i lang=\"en\">e-mail</i> aanjävve",
        "passwordsent": "E neu Passwood es aan de E-Mail Adress vun däm Metmaacher „$1“ ungerwähs. Meld dich domet aan, wann De et häs. Dat ahle Passwood bliev erhalde un kann och noch jebruch wääde, bes dat De Dich et eetste Mol met däm Neue enjelogg häs.",
-       "blocked-mailpassword": "Ding IP Adress es blockeet.",
+       "blocked-mailpassword": "Ding IP Adress es blockeet, Änderonge em Wikki sin verbodde. Öm nix aanbränne ze lohße, es ed och verbode, vun heh dä <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Internet Protocol\">IP</i>-Adräß et Passwoot affrohe ze lohße.",
        "eauthentsent": "En <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> es jäz ungerwähs aan di Adräß en de Enschtällonge. Ih dat mih <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mails</i> verschek wääde künne, moß mer maache, wat en dä <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> dren schteiht, öm ze beschtähteje, dat di Adräß schtemmp.",
        "throttled-mailpassword": "En Erennerung för di Passwood es alld ongerwähs, un mieh wi eimol en {{PLURAL:$1|der Schtond|$1 Schtonde|nidd ens ener Schtond}} dommer kein schecke.",
        "mailerror": "Fähler beim E-Mail Verschecke: $1.",
        "changepassword-success": "Et Paßwood es jeändert.",
        "changepassword-throttled": "Do häs zoh öff versöhk, enzelogge. Waat $1 Ih dat De es widder probeers.",
        "botpasswords": "Bot-Paßwööter",
+       "botpasswords-summary": "<strong>Bot-Paßwööter</strong> lohße ene Zohjreff obb enem Metmaacher singem Zohjang övver de <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Application Programming Interface\">API</i> zoh, ohne dem Houp-Zohjang sing Dahte bwnözze ze möße.\nDi Rääschte, di mer kritt, wam_mer med enem Bot-Paßwood ennlogg, künnte beschängk sin.\n\nWam_mer nit weijß, woröm mer dat donn sullt, sullt mer et wascheijnlesch nit donn. Opjepaß: Keijne sulld jehmohls welle, dat De eijn dervon oplähschß, öm ed em dann wiggerzejävve.",
        "botpasswords-disabled": "Bot-Paßwööter sin ußjeschallt",
        "botpasswords-no-central-id": "Öm Bot-Paßwööter bruche ze künne, moß De övve en jemeinsamme Aanmälldong ennjelogg sin.",
        "botpasswords-existing": "Vörhande Bot-Paßwööter",
        "botpasswords-label-cancel": "Ophüre",
        "botpasswords-label-delete": "Fottschmiiße",
        "botpasswords-label-resetpassword": "Paßwoot neu säze",
+       "botpasswords-label-grants": "Aanwändba Rääschte:",
+       "botpasswords-help-grants": "Jehde Ennwellejong deihjd e Räsch wigger jävve, wad enem Metmaacher övver singe Zohjang alld zohschteihjt.\nLoor op de Sigg met de [[Special:ListGrants|Tabäll met de Rääschde un Enwellejonge]], wann De mih weße wells.",
        "botpasswords-label-restrictions": "Beschränkonge:",
        "botpasswords-label-grants-column": "Zohjelohße",
        "botpasswords-bad-appid": "„$1“ es keine jölltejje Nahme för ene Bot.",
        "botpasswords-updated-body": "Dat Bot-Paßwoot för dä Bot „$1“ {{GENDER:$2|vum|vum|vumm Metmaacher|vun dä|vum}} „$2“ wood veränndert.",
        "botpasswords-deleted-title": "Dat Bot-Paßwood es fott",
        "botpasswords-deleted-body": "Dat Bot-Paßwoot för dä Bot „$1“ {{GENDER:$2|vum|vum|vumm Metmaacher|vun dä|vum}} „$2“ wood fott jeschmeße.",
+       "botpasswords-newpassword": "Et neuje Passwoot zom Ennlogge met <strong>$1</strong> es <strong>$2</strong>. Bes esu johd, dat för de Zohkonnef faßzehallde.",
+       "botpasswords-restriction-failed": "Beschrängkonge för em Bot sing Paßwoot maache et Ennlogge onmüjjelesch.",
+       "botpasswords-invalid-name": "En äm aanjejovve Nahme vum Metmaacher fähld et Trännzeijsche „$1“ för dem Bot sing Paßwoot.",
+       "botpasswords-not-exist": "Dä Metmaacher „$1“ kät keijn Paßwoot „$2“ för ene Bot.",
        "resetpass_forbidden": "E Passwoot kann nit jeändert wääde.",
        "resetpass_forbidden-reason": "Paßwööter kam_mer nit änndere: $1",
        "resetpass-no-info": "Do mööts ad enjelogg sin, öm tiräk op di Sigg jonn ze dörve",
        "passwordreset-emailtext-ip": "Do künns et sällver jewääse sin, öhnswää em Internet hät vun dä IP-Adräß $1 öm\ne neu Paßwoot jefrooch, för Dinge Zohjäng op {{GRAMMAR:Akkusativ|{{SITENAME}}}}\n$4\nHeh {{PLURAL:$3|dä Metmaacher hät|di Metmaacher han|hät keine Metmaacher}} Ding e-mail Addräß:\n\n$2\n\n{{PLURAL:$3|Dat Zweschepaßwoot leuf|Di Zweschepaßwööter loufe|Kein Zweschepaßwoot leuf}} en {{PLURAL:$5|enem Daach|$5 Dääsch|keinem Daach}} uß.\nDonn Desch jäz enlogge, un e neu Paßwoot faßlääje. Wann ene Andere wi\nDo dat heh aanjestüßße hät, udder wann De Desch widder aan Ding Paßwoot\nentsenne kanns, un et nimmih ändere wells, udder es suwwisu weiß, dann\nmoß De jäz jaa nix donn, un kanns Ding Paßwoot wigger bruche.",
        "passwordreset-emailtext-user": "Dä Metmaacher $1 vun {{GRAMMAR:Dativ|{{SITENAME}}}} hät öm e neu Paßwoot jefrooch,\nför Dinge Zohjäng op {{GRAMMAR:Akkusativ|{{SITENAME}}}}\n$4\nHeh {{PLURAL:$3|dä Metmaacher hät|di Metmaacher han|hät keine Metmaacher}} Ding e-mail Addräß:\n\n$2\n\n{{PLURAL:$3|Dat Zweschepaßwoot leuf|Di Zweschepaßwööter loufe|Kein Zweschepaßwoot leuf}} en {{PLURAL:$5|enem Daach|$5 Dääsch|keinem Daach}} uß.\nDonn Desch jäz enlogge, un e neu Paßwoot faßlääje. Wann ene Andere wi\nDo dat heh aanjestüßße hät, udder wann De Desch widder aan Ding Paßwoot\nentsenne kanns, un et nimmih ändere wells, udder es suwwisu weiß, dann\nmoß De jäz jaa nix donn, un kanns Ding Paßwoot wigger bruche.",
        "passwordreset-emailelement": "Metmaacher Name: \n$1\n\nEijmohl-Paßwoot: \n$2",
-       "passwordreset-emailsentemail": "Wann dat en ennjedrahre <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>-Adräß vun Der es, dann weed en <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> för der Zohjang heh verschek, öm e neu Paßwoot ze krijje.",
-       "passwordreset-emailsent-capture": "En <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> met Aanjahbe zom neue Paßwoot för der Zohjang heh es verschek woode. Heh dronger kanns De se lässe.",
-       "passwordreset-emailerror-capture": "En <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> met Aanjahbe zom neue Paßwoot för der Zohjang heh sullt verschek wääde, ävver dat Verscheke aan {{GENDER:$2|dä|dat|dä Metmaacher|de|dat}} $2 hät nit jeflup: $1",
+       "passwordreset-emailsentemail": "Wann dat en <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>-Adräß vun Der es, dann weed en <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> för Dinge Zohjang verschek, öm e neu Paßwoot ze krijje.",
+       "passwordreset-emailsentusername": "Wann heh dä Metmaacher en <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>-Adräß hät, dann weed en <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> verschek, öm e neu Paßwoot ze krijje.",
+       "passwordreset-emailsent-capture2": "{{PLURAL:$1|En <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> es|De <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>s sin|Nix es}} verschek woode, öm e neu Paßwoot ze krijje. {{PLURAL:$1|Dä Nahme vum Metmaacher un dat Paßwood|Di Leß met dä Nahme un Paßwööter|Nix weed}} heh noh aanjezeijsch.",
+       "passwordreset-emailerror-capture2": "{{GENDER:$2|Däm|Däm|Däm Metmaacher|Dä|Däm}} $1 en <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> ze scheke hät nit jeflupp: {{PLURAL:$3|Dä Nahme vum Metmaacher un dat Paßwood|Di Leß met dä Nahme un Paßwööter|Nix weed}} heh noh aanjezeijsch.",
+       "passwordreset-nocaller": "Entärne Fähler: Ene Oprohfer moß aanjejovve sin.",
+       "passwordreset-nosuchcaller": "Entärne Fähler: Dä Oprohfer „$1“ känne mer nit.",
        "passwordreset-invalideamil": "Dat es en onjöltejje Addräß fö de <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>",
+       "passwordreset-nodata": "Keine Metmaacher_Nahme un kein Adräß för de <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> es aanjejovve woode.",
        "changeemail": "Donn en Adräß för de <i lang=\"en\">e-mail</i> ändere udder fott schmiiße",
        "changeemail-header": "Donn heh dat Fommulaa ußfölle, öm Ding Adräß för de <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> ze ändere. Wann De en Adräß loß wähde wells, maach dat Fäld läddesch, ih dat De dat Fommolaa loß scheks.",
-       "changeemail-passwordrequired": "Do moß Ding Paßwood enjävve, öm di Änderong ze beschtähteje.",
        "changeemail-no-info": "Do mööts ald enjelogg sin, öm tiräk op di Sigg jonn ze dörve",
        "changeemail-oldemail": "Ding Address för de <i lang=\"en\">e-mail</i> es jäz:",
        "changeemail-newemail": "Ding neue Address för de <i lang=\"en\">e-mail</i> sull wääde:",
        "accmailtext": "En automattesch un zofällesch neu ußjewörfelt Passwood för dä\nMetmaacher „[[User talk:$1|$1]]“ es noh „$2“ jescheck woode.\n\nDat Passwoot för dä neue Zojang kanns De op dä {{int:Specialpage}} zom\n„[[Special:ChangePassword|{{int:resetpass}}]]“ ändere,\nwann De wider enjelogg bes.",
        "newarticle": "(Neu)",
        "newarticletext": "Ene Link op en Sigg, wo noch nix drop steiht, weil et se noch jar nit jitt, hät Dich noh heh jebraht.\nÖm di Sigg aanzelähje, schriev heh unge en dat Feld eren, un dun dat dann avspeichere.\nLuur op de [$1 Sigge met Hölp] noh, wann De mih doh drövver weßße wells.\nWann De jar nit heh hen kumme wollts, dann jangk zeröck op di Sigg, wo De herjekumme bes, Dinge Brauser hät ene Knopp doför.",
-       "anontalkpagetext": "----\n<i>Dat heh es de Klaaf Sigg för ene namenlose Metmaacher. Dä hät sich noch keine Metmaacher Name jejovve un\nenjerich, ov deit keine bruche. Dröm bruche mer sing IP Adress öm It oder In en uns Lisste fasszehalde.\nSu en IP Adress kann vun janz vill Metmaacher jebruch wääde, un eine Metmaacher kann janz flöck\nzwesche de ungerscheidlichste IP Adresse wähßele, womöchlich ohne dat hä et merk. Wann Do jetz ene namenlose\nMetmaacher bes, un fings, dat heh Saache an Dich jeschrevve wääde, wo Do jar nix met am Hot häs, dann bes Do\nwahrscheinlich och nit jemeint. Denk villeich ens drüvver noh, datte Dich [[Special:CreateAccount|anmelde]] deis,\ndomet De dann donoh nit mieh met esu en Ömständ ze dun häs, wie de andere namenlose Metmaacher heh. Wann de aanjemelldt bes un deis [[Special:UserLogin|enlogge]], dann kam_mer Desch och fun alle andere Metmaacher ongerschejde.</i>",
+       "anontalkpagetext": "----\n<strong>Dat heh es de Klaaf Sigg för ene nahmelohse Metmaacher. Dä hät sich noch keine Metmaacher Name jejovve un enjerich, ov deit keine bruche.</strong> Dröm bruche mer sing \n<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Internet Protocol\">IP</i>-Adräß, öm It oder In en uns Lisste faßßzehalde.\nSu en <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Internet Protocol\">IP</i>-Adräß kann vun janz vill Metmaacher jebruch wääde, un eine Metmaacher kann flöck zwesche de ungerscheidleschste <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Internet Protocol\">IP</i>-Adräßw ähßele, womöchlich ohne dat hä et merk. Wann Do jetz ene nahmelohse Metmaacher bes, un fengs, dat heh Saache an Desch jeschrevve wääde, wo Do jar nix med am Hoht häs, dann bes Do\nwahrscheinlich och nit jemeijnt. Dängk velleisch ens drövver noh, datte Dich [[Special:CreateAccount|anmelds]],\ndomet De dann donoh nit mih met esu en Ömständ ze don häs, wi de andere namenlose Metmaacher heh. Wann de aanjemälldt bes un deis [[Special:UserLogin|enlogge]], dann kam_mer Desch och fun alle andere Metmaacher ongerscheijde.",
        "noarticletext": "<span class=\"plainlinks\">Em Momang es keine Täx op heh dä Sigg. Jangk en de Täxte vun ander Sigge [[Special:Search/{{PAGENAME}}|noh däm Titel söhke]], udder [{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} donn en de Logböscher donoh loore], udder [{{FULLURL:{{FULLPAGENAME}}|action=edit}} fang di Sigg aan] ze schrieve, udder jangk zeröck woh De heer kohms. Do hät Dinge Brauser ene Knopp för.</span>",
        "noarticletext-nopermission": "Op dä Sigg es em Momang nix drop.\nDo kanns noh däm Tittel vun heh dä Sigg [[Special:Search/{{PAGENAME}}|em Tex op ander Sigge söhke]],\nudder en dä zopaß <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} Logbööscher nohloore]</span>.",
        "missing-revision": "En Version $1 vun dä Sigg „{{FULLPAGENAME}}“ jidd_et nit.\n\nEsu jät kütt för jewöhnlesch, wam_mer enem övverhollte Lengk ob en Sigg follesch, di zweschedren fottjeschmeße woode es.\nMih doh drövver fengk mer em [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} Logbooch vum Sigge Fottschmiiße].",
        "previewnote": "<strong>Heh kütt blohß en Aanseesch vöraff — Ding Änderonge sin noch nidd em Wikki faßjehallde!</strong>",
        "continue-editing": "Jangk tiräk nohm Plaz zom Schrieve",
        "previewconflict": "Heh di Vör_Aanseesch zeisch dä Enhald vum bovvere Täxfäld.\nEsu wööd di Sigg ußsinn, wann De se jäz afschpeijschere dähts.",
-       "session_fail_preview": "<strong>Schahd: Met Dinge Änderonge kunnte mer su nix aanfange.\nVersöhk et jrahd noch ens.\nWann dat widder nit flupp, dann versöhk et ens met [[Special:UserLogout|Ußlogge]] un widder Enlogge.</strong>",
-       "session_fail_preview_html": "'''Schad: Ding Änderunge kunnte mer su nix met aanfange. De Daate vun Dinge Login-Säschen sin nit öntlich erüvver jekumme, oder einfach ze alt.'''\n\n''Dat Wiki heh hät rüh HTML zojelooße, dröm weed de Vör-Aansich nit jezeich. Domet solls De jeschötz wääde - hoffe mer - un Aanjreffe met Java_Skripp jäje Dinge Kompjuter künne Der nix aandun.''\n\n'''Falls för Dich söns alles jod ussüht, versök et jrad noch ens. Wann dat widder nit flupp, dann versök et ens met [[Special:UserLogout|Uslogge]] un widder Enlogge.'''",
+       "session_fail_preview": "Schahd: Met Dinge Änderonge kunnte mer su nix aanfange. De Sezongsdahte sin verschött jejange.\nDo künnts ußjelogg wohde sin.\n<strong>Bes sescher, dat De verhaftesc noch ennjelogg bes un dann versöhk et jrahd noch ens.</strong>\nWann dat widder nit flupp, dann versöhk et ens met [[Special:UserLogout|Ußlogge]] un widder Enlogge,\nun pröhf, dat Dinge Brauser <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„Plätzjer“\">cookies</i> vum Wikki aannemmp.",
+       "session_fail_preview_html": "<strong>Schahd: Ding Änderonge kunnte mer su nix met aanfange. De Daate vun Dinge Login-Swzong sin nit öntlich erövver jekumme, udder einfach zoh ahl un affjeloufe.</strong>\n\nDat Wiki heh hät rüh HTML zohjelohße, dröm weed de Vör-Aansesch nit aanjezeisch. Domet solls De jeschötz wääde - hoffe mer - un Aanjreffe met Java_Skripp jäje Dinge Kompjuter künne Der nix aandon.\n\n<strong>Falls för Dich söns alles jod ussüht, versök et jrad noch ens.</strong>\nWann dat widder nit flupp, dann versök et ens met [[Special:UserLogout|Uslogge]] un widder Enlogge,\nun pröhf, dat Dinge Brauser <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„Plätzjer“\">cookies</i> vum Wikki aannemmp.",
        "token_suffix_mismatch": "'''Ding Änderong ham_mer nit övvernomme. Dinge Brauser hät Sazzeijsche en dä verschtoche Makkehrong för et Ändere versout. Dat paßehrt och ens, wann enne <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„ene ẞööver en de Nohberschaff, ene Zweschedrääjer udder Zwescheschpeijscher, ene Vermeddelongsrääschner“\">proxy</i>-ẞööver nit fungkßjeneet. Et Affschpeischere wöhr doh jefährlesch, do künnt dä Sigge_Enhaldt kapott bei jon.'''",
        "edit_form_incomplete": "<strong>Ene Aandeil vun dämm Fommolaa es nit reeschtesch om ẞööver aanjekumme. Donn Ding Ennjahbe pröhve, reparehre, un versöhg et norrens.</strong>",
        "editing": "De Sigg „$1“ ändere",
        "template-semiprotected": "(halfjeschöz - tabu för neu Metmaacher un ohne Enlogge)",
        "hiddencategories": "Di Sigg heh is en {{PLURAL:$1|dä verschtoche Saachjropp: |dä $1 verschtoche Saachjroppe: |keij verschtoche Saachjroppe dren.}}",
        "edittools": "<!-- Dä Täx heh zeisch et Wikki onger däm Täxfeld zom „Ändere/Beärbeijde“ un beim Täxfeld vum „Huhlade“ ann. -->",
+       "edittools-upload": "-",
        "nocreatetext": "Sigge neu aanläje es nor müjjelich, wann de [[Special:UserLogin|enjelogg]] bes. Der ohne kanns De ävver Sigge ändere, di ald doh sin.",
        "nocreate-loggedin": "Do häs nit dat Rääch, neu Sigge aanzelääje.",
        "sectioneditnotsupported-title": "Afschnedde Ändere is nit zohjelohße",
        "content-model-json": "<i lang=\"en\" xml:lang=\"en\" title=\"JavaScript Object Notation\">JSON</i>",
        "content-json-empty-object": "Nix dren",
        "content-json-empty-array": "Nix dren",
+       "deprecated-self-close-category": "Sigge med onjölltejje <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"HyperText Markup Language\">HTML</i>-Befähle, di en sesch sällf ze Äng jonn.",
+       "deprecated-self-close-category-desc": "De Sigg hädd onjölltejje <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"HyperText Markup Language\">HTML</i>-Befähle, di en sesch sällf ze Äng jonn. Beijshpelle sin <code>&lt;b/></code> udder <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">&lt;span/></code>. Wat di donn, weed bahl esu verännert, dat se mem <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"HyperText Markup Language Version 5\">HTML5</i> zesamme paße, woh mer se en Wikkitäx it bruche sullt.",
        "duplicate-args-warning": "<strong>Opjepaß:</strong> [[:$1]] röhf [[:$2]] met mih wi eijnem Wäät för der Parramehter „$3“ op. Blohß der läzde Wäät vun dänne weed opjenumme un jebruch.",
        "duplicate-args-category": "Sigge met dubbelt aanjejovve Parramehtere för Schablohne.",
        "duplicate-args-category-desc": "Sigge met Oprohve vun Schablohne met dubbelt aanjejovve Parramehtere dren, alsu esu jät wi <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> un <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Di Änderong schingk ald retuur jemaat woode ze sin.",
        "undo-summary": "Di Änderong $1 wood {{GENDER:$2|vum|vum|vumm Metmaacher|vun dä|vum}} [[Special:Contributions/$2|$2]] ([[User talk:$2|Klaaf]]) zeröck jenomme.",
        "undo-summary-username-hidden": "Nemm di Väsjohn $1 vun enem verschtoche Metmaacher widder retuhr.",
-       "cantcreateaccounttitle": "Kann keine Zojang enrichte",
        "cantcreateaccount-text": "Dä [[User:$3|$3]] hät verbodde, dat mer sich vun dä IP-Adress '''$1''' uß als ene neue Metmaacher aanmelde könne soll.\n\nAls Jrund för et Sperre es enjedraare: ''$2''",
        "cantcreateaccount-range-text": "Ne neue Metmaacher aanmälde vun <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Internet Protocol\">IP</i>-Adräße em Berett vun <strong>$1</strong>, woh de Dinge dren es, wood vum {{GENDER:$3|vum|vum|vumm Metmaacher|vun dä|vum}} [[User:$3|$3]] verbodde.\nDer Jrond: <em><$2</em>\n\nDing <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Internet Protocol\">IP</i>-Adräß es: <strong>$4</strong>",
        "viewpagelogs": "De Logböcher för heh di Sigg beloore",
        "mergehistory-fail-bad-timestamp": "Dattom odder Zigg udder beeds sin nit jöltesch",
        "mergehistory-fail-invalid-source": "De Quällesigg es nit jöltesch",
        "mergehistory-fail-invalid-dest": "De Zihlsigg es nit jöltesch",
+       "mergehistory-fail-no-change": "Et sin kein verjange Väsjohne zesamme jelaat wohde. Doht de Parramehtere för Sigg un Zigg norr_ens pröhfe.",
        "mergehistory-fail-permission": "Kein Berääschtejong, de vörreje Väsjohne zesamme ze lähle.",
        "mergehistory-fail-self-merge": "De Quell-Sigg un de Ziel-Sigg sin deselve Sigg.",
        "mergehistory-fail-timestamps-overlap": "De Väjsohne vun de Quällesigg kumme övverkrüz udder noh de Väsjohne vun de Zihlsigg.",
        "mergehistory-comment": "„[[:$1]]“ zosamme jelaat met „[[:$2]]“ — $3",
        "mergehistory-same-destination": "De Quell-Sigg un de Ziel-Sigg dörve nit deselve Sigg sinn.",
        "mergehistory-reason": "Der Jrond:",
+       "mergehistory-revisionrow": "$1 ($2) $3 . . $4 $5 $6",
        "mergelog": "Logbohch fum Sigge zesamme Lähje",
        "revertmerge": "Dat Zosammelääje widder retuhr maache",
        "mergelogpagetext": "Dat heh es dat Logbohch fun de zesammejelaate Väsjohne fun Sigge",
        "youremail": "E-Mail *",
        "username": "{{GENDER:$1|Metmaacher|Metmaacherėnne|Metmaacher|Metmaacherėnne|Metmaacher}} Name:",
        "prefs-memberingroups": "{{GENDER:$2|Bes}} en {{PLURAL:$1|de Metmaacherjrupp:|$1 Metmaacherjruppe:|keijn Metmaacherjruppe.}}",
+       "prefs-memberingroups-type": "$1",
        "prefs-registration": "Aanjemäldt zigg",
        "prefs-registration-date-time": "dem $2 öm $3 Uhr",
        "yourrealname": "Dinge richtije Nahme *",
        "saveusergroups": "Donn {{GENDER:$1|däm|dem|däm Metmaacher|dä|däm}} [[User:$1|$1]] {{GENDER:$1|sing|singe|sing|ier|sing}} Jroppe faßhalle",
        "userrights-groupsmember": "Dä Metmaacher es en {{PLURAL:$1|dä Jropp:|dä Jroppe:|keine Jropp.}}",
        "userrights-groupsmember-auto": "Dä Metmaacher es automattesch en {{PLURAL:$1|dä Jropp:|dä Jroppe:|keine Jropp.}}",
+       "userrights-groupsmember-type": "$1",
        "userrights-groups-help": "Do kanns de Jroppe för dä Metmaacher heh änndere, ävver opjepaß:\n* E Käßje met Höksche bedüg, dat dä Metmaacher en dä Jropp es.\n* E Käßje ohne Höksche bedüg, dat dä Metmaacher nit en dä Jropp es.\n* E Käßje met Stähnsche donävve bedüg, dat De dat Rääsch zwa ändere, ävver de Änderong nit mih zeröck nämme kanns.",
        "userrights-reason": "Aanlaß odder Jrund:",
        "userrights-no-interwiki": "Do häs nit dat Rääsch, Metmaacher ier Rääschte in ander Wikis ze ändere.",
        "grant-patrol": "Änderonge aan Sigge nohkike",
        "grant-protect": "Sigge schöze un der Schoz wider ophävve",
        "grant-rollback": "Änderonge aan Sigge retuhr maache",
-       "grant-sendemail": "<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mails</i> aan ander Metmaacher schecke",
-       "grant-uploadeditmovefile": "Datteije uhlahde, ußtuusche un ömbenänne",
-       "grant-uploadfile": "Neu datteije huhlahde",
+       "grant-sendemail": "<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mails</i> aan annder Metmaacher schecke",
+       "grant-uploadeditmovefile": "Datteije huhlahde, ußtuusche un ömbenänne",
+       "grant-uploadfile": "Neu Datteije huhlahde",
        "grant-basic": "Jrondlähje Rääsch",
-       "grant-viewdeleted": "Fottjeschmeße Dahte un Sigge belohre",
-       "grant-viewmywatchlist": "De eije Oppaßleß ze belooere",
+       "grant-viewdeleted": "Fottjeschmeße Dahteije un Sigge belohre",
+       "grant-viewmywatchlist": "De eije Oppaßleß ze belohre",
        "newuserlogpage": "Logbohch för neu Metmaachere",
        "newuserlogpagetext": "He sin de Metmaacher opjelėßß, di sesh nöü aanjemäldt han.",
        "rightslog": "Logbohch för Änderonge aan Metmaacher-Rääschde",
        "rc_categories_any": "Öhndseijne vun dä aanjejovve Saachjroppe",
        "rc-change-size": "$1 {{PLURAL:$1|Byte|Bytes}}",
        "rc-change-size-new": "$1 {{PLURAL:$1|Byte|Bytes|Bytes}} noh dem Ändere",
-       "newsectionsummary": "Neu Avschnet /* $1 */",
+       "newsectionsummary": "Neuje Afschnet /* $1 */",
        "rc-enhanced-expand": "Einzelheite aanzeije",
        "rc-enhanced-hide": "Einzelheite versteiche",
        "rc-old-title": "ojinaal als „$1“ aanjelaat",
        "uploaded-hostile-svg": "Mer han onseescher <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Cascading Style Sheet\">CSS</i>-Befähle en enem „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">style</code>“-Ellemänt vun dä huhjelahde <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Dattei jefonge.",
        "uploaded-event-handler-on-svg": "Projramme för Ä'eijschneße ze behanndelle „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">&lt;$1=\"$2\"&gt;</code>“ ennzesäze es en <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Datteije verbodde.",
        "uploaded-href-attribute-svg": "En <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Datteije darrev övver <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">href</code> blohß op <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"HyperText Transfer Protocol\">HTTP://</i> udder <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"HyperText Transfer Protocol Secure - HTTP övver SSL - HTTP övver TSL\">HTTPS://</i> verlenk wähde. Heh es ävver <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code> dren.",
-       "uploaded-href-unsafe-target-svg": "Mer han ene „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">href</code>“-Befähl obb e onseescher Zihl „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code>“ en dä huhjelahde <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Dattei jefonge.",
+       "uploaded-href-unsafe-target-svg": "Mer han ene „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">href</code>“-Befähl obb e onseescher Zihl mem <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Uniform Ressource Identifier\">URI</i> „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code>“ en dä huhjelahde <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Dattei jefonge.",
        "uploaded-animate-svg": "Mer han dä Befähl „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">animate</code>“ en dä huhjelahde \n<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Dattei jefonge, dä ene „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">href</code>“-Befähl verändere künnt övver de „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">from</code>“-Eijeschaff „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code>“.",
        "uploaded-setting-event-handler-svg": "Ed es verbodde, Projramme för Ä'eijschneße ze behanndelle ennzesäze, un de Datteije, di dat donn, wähde jeschpächt. Mer han „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">&lt;$1 $2=\"$3\"&gt;</code>“ en dä huhjelahde <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Scalable Vector Graphics\">SVG</i>_Dattei jefonge.",
        "uploaded-setting-href-svg": "Dä Befähl „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">set</code>“ för de „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">href</code>“-Eijeschaff vun övverjeohdente Ällemänt ze säze es jeschpächt.",
        "upload-too-many-redirects": "Zoh vill Ömleitunge en däm <i lang=\"en\">URL</i>",
        "upload-http-error": "Ene <i lang=\"en\">HTTP</i>-Fäähler es opjetrodde: $1",
        "upload-copy-upload-invalid-domain": "Fun dä Domain künne mer nix noh heh huh laade. Di es nit zohjelohße.",
+       "upload-foreign-cant-upload": "Heh dat Wikki es nit esu ennjeschtallt, dat mer Datteije en dat jewönschte Repposetohrejom vun Datteije ußerhallef vum Wikki huhlahde künnt.",
+       "upload-foreign-cant-load-config": "Mer kunnte de Enschtälonge för et Datteije-Huhlahde en e Repposetohrejom vun Datteije ußerhallef vum Wikki nit lahde.",
+       "upload-dialog-disabled": "Op heh däm Wähsch Datteije huhzelahe es heh em Wikki affjeschalldt.",
        "upload-dialog-title": "Dateij huhlahde",
        "upload-dialog-button-cancel": "Ophühre!",
        "upload-dialog-button-done": "Jedonn",
        "upload-dialog-button-upload": "Lohß Jonn!",
        "upload-form-label-infoform-title": "Eijnzelheijte",
        "upload-form-label-infoform-name": "Nahme",
+       "upload-form-label-infoform-name-tooltip": "En eijndeutejje Titel för di Datei, di se beschrihv un als Dattejnahme dehnt. Mer kan jewöhnlejje Schprohch met Zweschreum zwesche de Wööter nämme. Donn keine Datteijnahme-Zohsaz derbeij.",
        "upload-form-label-infoform-description": "Äkliehrong",
+       "upload-form-label-infoform-description-tooltip": "Donn koot beschrihve, wat vun Belang es för dat Wärk.\nFör e Fotto, schrihv de Houpsaache op, di affjebelld sin, de Zigg un Jelähjeheid un der Plaz.",
        "upload-form-label-usage-title": "Der Jebruch",
        "upload-form-label-usage-filename": "Dä Dattei iehre Nahme",
        "upload-form-label-own-work": "Dat es ming eije Wärk",
        "upload-form-label-infoform-categories": "Saachjroppe",
        "upload-form-label-infoform-date": "Dattum",
+       "upload-form-label-own-work-message-generic-local": "Esch beschtähtejjen, dadd esch heh di Dattei aam Huhlahde ben un derbeij de Bedengonge för der Dehns un de Rähjelle för de Lezänze {{GRAMMAR:em|{{ucfirst:{{SITENAME}}}}}} ennhallden.",
        "upload-form-label-not-own-work-message-generic-local": "Wann De di Dattei nit en de jemeinsamme Sammlong vun Datteule huh lahde kanns un derbei och de Rähjelle {{GRAMMAR:vun|{{ucfirst:{{SITENAME}}}}}} ennhalde, dann maach heh nit wigger, un probehr ene anndere Wähsch.",
        "upload-form-label-not-own-work-local-generic-local": "Do künnts edd och ens met dä [[Special:Upload|Schtandatt-Sigg zom Huhlahde]] versöhke welle.",
        "upload-form-label-own-work-message-generic-foreign": "Esch verschtonn, dadd esch en en jemeinsamme Sammlong huh aam lahde ben un dadd sesch dat met dä Bedengonge un de Lezänzbedengonge heh verdräht.",
        "uploadstash-badtoken": "Dat ze donn hät nit jeflupp. Velleich wohre Ding Daate zom Deil verschött jejange udder afjeloufe. Versöhg et nor_rens.",
        "uploadstash-errclear": "Mer kunnte de Dateie nit fottschmieße.",
        "uploadstash-refresh": "De Leß met de Dateie op ene neue Shtand bränge",
+       "uploadstash-thumbnail": "et Minni-Belldsche aanlohre",
        "invalid-chunk-offset": "Ene onjöltijje Aanfangspungk för dä Rötsch",
        "img-auth-accessdenied": "Keine Zohjang",
        "img-auth-nopathinfo": "De <code xml:lang=\"en\" lang=\"en\" dir=\"rtl\">PATH_INFO</code> fäält.\nDä Webßööver es nit doför ennjerescht, di Ennfommazjuhn wigger ze jävve.\nHä künnd_op <code xml:lang=\"en\" lang=\"en\" dir=\"rtl\">CGI</code> opjebout sin, un dröm <code xml:lang=\"en\" lang=\"en\" dir=\"rtl\">img_auth</code> nit ongschtöze künne. Loor em [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization Handbohch] noh, wat domed es.",
        "apisandbox-jsonly": "Der ohne JavaSkrepp kam_mer de <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Application Programming Interface\">API</i> för zom erömprobehre nit bruche.",
        "apisandbox-api-disabled": "Dat <i lang=\"en\">API</i> es en heh dämm Wiki afjeschalldt.",
        "apisandbox-intro": "Op heh dä Sigg kanns De met dä <strong><i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Application Programming Interface\">API</i> vum MehdijaWikki singem Wäbdehns</strong> eröm schpelle.\nBeloor Der de Einzelheijte, un wi di jebruch weed, op dä iere [[mw:API:Main_page Sigg met de Verklieronge]].\nE Beischpell: [https://www.mediawiki.org/wiki/API#A_simple_example De Houpsigg holle].\nSöhk ene {{int:Apisb-label-action}} uß, öm mih Beischpelle aanjezeisch ze krijje.\nOch wann dat heh nor zom Ußprobehre es, kann dat, wat De heh mähß, et Wikki veränndere.",
-       "apisandbox-unfullscreen": "Sigg aanzeije",
+       "apisandbox-fullscreen": "Om jannze Scherrem zeije",
+       "apisandbox-fullscreen-tooltip": "Maach de Sandkeß esu jruhß wi et jannze Brauser-Finster.",
+       "apisandbox-unfullscreen": "De Sigg nommahl aanzeije",
+       "apisandbox-unfullscreen-tooltip": "Maach de Sandkeß kleijner, esu dat dem Mehdijawikki sing Navvijazjuhns-Lengks zohjänglesch wähde.",
        "apisandbox-submit": "Lohß jonn!",
        "apisandbox-reset": "Läddesch maache",
        "apisandbox-retry": "Norr_ens versöhke",
+       "apisandbox-loading": "Ben de Ennfommazjuhne för et Moduhl „$1“ vun de <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Application Programming Interface\">API</i> aam lahde&nbsp;&hellip;",
+       "apisandbox-load-error": "Ene Fähler es opjetrodde beim Lahde vun Enfommazjuhne för et \n<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Application Programming Interface\">API</i>-Moduhl „$1“: $2",
+       "apisandbox-no-parameters": "Heh dat <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Application Programming Interface\">API</i>Moduhl hät kein Parramehtere.",
        "apisandbox-helpurls": "Lengks för Hölp",
        "apisandbox-examples": "Bäijshpelle",
        "apisandbox-dynamic-parameters": "Zohsäzlejje Parrameetere",
        "apisandbox-dynamic-error-exists": "Ne Parramehter mem Nahme „$1“ ham_mer ald.",
        "apisandbox-deprecated-parameters": "Övverhollte Parramehtere",
        "apisandbox-submit-invalid-fields-title": "Paa Flder sin nit jöltesch",
+       "apisandbox-submit-invalid-fields-message": "Donn de makkehrte Fällder bereeschtejje un versöhg et norr_ens.",
        "apisandbox-results": "Erus jekumme:",
+       "apisandbox-sending-request": "Ben en Aanfrohch aan et <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Application Programming Interface\">API</i> aam schecke&nbsp;&hellip;",
+       "apisandbox-loading-results": "Ben en Antwoot vum <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Application Programming Interface\">API</i> aam krijje&nbsp;&hellip;",
+       "apisandbox-results-error": "Ene Fähler es opjetrodde beim Lahde vun dä Antwoot ob de Aanfrohch aan et <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Application Programming Interface\">API</i>: $1.",
        "apisandbox-request-url-label": "Dä <i lang=\"en\">URL</i> vun dä Aanfrooch:",
-       "apisandbox-request-time": "De Zigg vum Afroof: $1",
+       "apisandbox-request-time": "De Dooer vun däm Afroof: {{PLURAL:$1|ein Millisekond|$1 Millisekonde|kein Millisekond}}.",
        "apisandbox-alert-page": "Heh op dä Sigg sin onjölltejje Aanjahbe.",
        "apisandbox-alert-field": "Dä Wääd en dämm Fäld heh es onjölltesch.",
        "booksources": "Böcher",
        "log-title-wildcard": "Sök noh Titelle, di aanfange met …",
        "showhideselectedlogentries": "Ußjesöhk Endrääsch verschteische udder zeije",
        "log-edit-tags": "Donn de Makehronge vun de ußjesöhk Enndrähsch em Logbohch beärbeide",
+       "checkbox-select": "Söhk us: $1",
        "checkbox-all": "Alle",
        "checkbox-none": "Keine",
        "checkbox-invert": "Ußwahl ömdrihje",
        "listgrouprights-namespaceprotection-header": "Beschrängkonge för Appachtemangs",
        "listgrouprights-namespaceprotection-namespace": "Appachtemang",
        "listgrouprights-namespaceprotection-restrictedto": "Rääsch(de) zom Verändere",
+       "listgrants": "Berääschtejonge",
+       "listgrants-grant": "Berääschtejong",
        "listgrants-rights": "Rääschte",
        "trackingcategories": "Saachjroppe för täschnsche Saache ze verfollje.",
        "trackingcategories-summary": "Op hee dä {{int:nstab-special}} sin Saachjroppe opjeleß, di automattesch vum Wikki jevöllt wähde. Dä iehr Nahme künne övver Veränderonge aan beschtemmpte Täxte em Appachtemang {{ns:8}} faßjelaat wäde.",
        "trackingcategories-msg": "Saachjropp för täschnsche Saache ze verfollje.",
        "trackingcategories-name": "Dä Nohreesch udder däm Täxschtöck singe Nahme",
        "trackingcategories-desc": "Bedengonge för enjeschloße ze sin",
+       "restricted-displaytitle-ignored": "Sigge met övverjange „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">{<nowiki />{DISPLAYTITLE}}</code>“-Befähle",
+       "restricted-displaytitle-ignored-desc": "Dä Sigg iere „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">{<nowiki />{DISPLAYTITLE}}</code>“-Befähl weed övverjange. Hä kütt nit zopaß met dä aktoälle Övverschreff vun dä Sigg.",
        "noindex-category-desc": "Di Sigg sull vun de Wäbkrauler Robots un de Söhkmaschihne nit opjenumme wähde, weil dat Zauberwoot <code><nowiki>__NOINDEX__</nowiki></code> dren schteiht un se en enem Appachemang es, woh dat zohjelohße es.",
        "index-category-desc": "Di Sigg sull vun de Wäbkrauler Robots un de Söhkmaschihne opjenumme wähde, weil dat Zauberwoot <code><nowiki>__INDEX__</nowiki></code> dren schteiht un se en enem Appachemang es, woh dat zohjelohße es, un wat nommahlerwihs nit vun de Robots dorschsöhk weed.",
        "post-expand-template-inclusion-category-desc": "Nohdämm a paa Schablohne enjesaz woode sen, hät di Sigg mieh Dahte wi <code xml:lang=\"en\" lang=\"en\">$wgMaxArticleSize</code> zohlöhß. Et sin nit alle Oprohve vun Schablohne opjelöhß.",
        "wlnote": "{{PLURAL:$1|Hee es de läzde Änderong uß|Hee sin de läzde <strong>$1</strong> Änderonge uß|Mer han kein Änderonge en}} de läzde {{PLURAL:$2|Stund|<strong>$2</strong> Stunde|<strong>noll</strong> Stunde}} zigg em $3 öm $4 Uhr.",
        "wlshowlast": "Zeisch de läzde $1 Schtunde, $2 Dähsch aan.",
        "watchlist-hide": "Verschtisch",
+       "watchlist-submit": "Aanzeije!",
+       "wlshowtime": "De Aandeijl vun e Zigg zom Aanzeije:",
        "wlshowhideminor": "klein Minni-Änderonge",
        "wlshowhidebots": "de Bots ehr Änderonge",
        "wlshowhideliu": "de ennjeloggte Metmaacher ier Änderonge",
        "wlshowhideanons": "de nahmelohse Metmaacher ier Änderonge",
        "wlshowhidepatr": "de nohjelohrte Änderonge",
        "wlshowhidemine": "ming eije Änderonge",
+       "wlshowhidecategorization": "de Sigge ier Ennohdenong",
        "watchlist-options": "Eijeschaffte fun de Oppassless",
        "watching": "Drobb oppaßße…",
        "unwatching": "Nimmih drobb oppaßße",
        "delete-confirm": "„$1“ fottschmieße",
        "delete-legend": "Fottschmieße",
        "historywarning": "<strong>Opjepass:</strong> Di Sigg, di De fott schmiiße wells, hät {{PLURAL:$1|ein ällder Väsjohn|ald $1 ällder Väsjohne|jaa kein ällder Väsjohne}}.",
+       "historyaction-submit": "Aanzeije!",
        "confirmdeletetext": "Do bes koot dovör, en Sigg för iiwich fottzeschmiiße. Dobei verschwind och de janze Verjangeheit vun dä Sigg us de Dahtebangk, met all ehr Änderonge un Metmaacher Nahme, un all dä Opwand, dä do dren stich. Do moß heh jäz beschtähteje, dat de verschteihs, wat dat bedügg, un dat De weiß, wat Do do mähs.\n<strong>Dun et blohß, wann dat met de [[{{MediaWiki:Policy-url}}|Rääjelle]] verhaftech zosamme jeiht!</strong>",
        "actioncomplete": "Jedonn!",
        "actionfailed": "Dat es donevve jejange",
        "rollbacklinkcount": "{{PLURAL:$1|Ein Änderong|$1 Änderonge|Kein Änderonge}} schtantepee retuur nämme",
        "rollbacklinkcount-morethan": "{{PLURAL:$1|Mih wi ein Änderong|Övver $1 Änderonge|Kein Änderonge}} schtantepee retuur nämme",
        "rollbackfailed": "Dat Zeröcknemme jingk scheiv",
+       "rollback-missingparam": "Doh fähle nühdejje Parramehtere",
        "cantrollback": "De läzde Änderong zeröckzenemme es nit müjjelich. Dä läzde Schrihver es dä eijnzeje, dä aan dä Sigg heh jht jedonn hät!",
        "alreadyrolled": "Mer künne de letzte Änderonge vun dä Sigg „[[:$1]]“ vum Metmaacher „[[User:$2|$2]]“ ([[User talk:$2|Klaaf]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) nimieh zeröcknemme, dat hät ene Andere enzwesche ald jedon, udder de Sigg ömjeändert.\n\nDe Neuste Änderong aan dä Sigg es jetz vun däm Metmaacher „[[User:$3|$3]]“ ([[User talk:$3|Klaaf]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Bei dä Änderong schtundt: „$1“.",
        "revertpage": "Änderunge vun däm Metmaacher „[[Special:Contributions/$2|$2]]“ ([[User talk:$2|däm sing Klaafsigg]]) fottjeschmeße, un doför de lätzde Väsjohn vum „[[User:$1|$1]]“ widder zeröckjehollt",
        "revertpage-nouser": "Änderunge vun enem Metmaacher, däm singe Name vershtoche es, retuur jemaat op de letzte Version {{GENDER:$1|vum|vum|vumm Metmaacher|vun dä|vum}} [[User:$1|$1]]",
        "rollback-success": "De Änderungen vum $1 zeröckjenumme, un dobei de letzte Version vum $2 widder jehollt.",
+       "rollback-success-notify": "Änderonge {{GENDER:$1|vum|vum|vumm Metmaacher|vun dä|vum}} „$1“ sin zerök jenumme un di Sigg es op der Schtand vun doför {{GENDER:$2|vum|vum|vumm Metmaacher|vun dä|vum}} „$2“ jesaz. [$3 Belohr, wat derbeij veränndert wood]",
        "sessionfailure-title": "Fähler met dä Daate vum Enlogge",
        "sessionfailure": "Et jov wall e täschnesch Problehm met Dingem Login. Dröm ham_mer dat us Vörseesch jäz nix jemaht, domet mer nit velleich Ding Änderong däm verkihrte Metmaacher ongerjubele. Jangk zeröck un versöhk et noch ens.",
        "changecontentmodel": "Et Modäll vum Ennhald vun ene Sigg verändere",
        "block-log-flags-hiddenname": "Däm Metmaacher singe Name es för de Öffentleschkeit vershtoche",
        "range_block_disabled": "Adresse Jebeede ze sperre, es nit erlaub.",
        "ipb_expiry_invalid": "De Duur es Dress. Jevv se richtich aan.",
+       "ipb_expiry_old": "Di Zick för ed Ußloufe es ald eröm.",
        "ipb_expiry_temp": "Sperre för Metmaacher met verschtoche Nahme mößße för ihwish dohre.",
        "ipb_hide_invalid": "Mer künne dä Metmaacher nit verschteische. Dä hät övver {{PLURAL:$1|ein Änderong|$1 Änderong|kein Änderong}} jemaat.",
        "ipb_already_blocked": "„$1“ es ald jesperrt",
        "lockdbsuccesstext": "{{ucfirst:{{GRAMMAR:Genitive sing|{{SITENAME}}}}}} Daatebank jetz jesperrt.<br /> Dun se widder [[Special:UnlockDB|freijevve]], wann Ding Waadung eröm es.",
        "unlockdbsuccesstext": "De Daatebank es jetz freijejovve.",
        "lockfilenotwritable": "De Datei, wo de Daatebank met jesperrt wääde wööd, künne mer nit aanläje, oder nit dren schrieve. Esu ene Dress! Dat mööt dä Websörver ävver künne! Verzäll dat enem Verantwortliche för de Installation vun däm ẞööver oder repareer et selvs, wann De et kanns.",
+       "databaselocked": "De Dahtebangk es ald jeschpächt.",
        "databasenotlocked": "<strong>Opjepass:</strong> De Daatebank es <strong>nit</strong> jesperrt.",
        "lockedbyandtime": "(aam $2 öm $3 Uhr vum $1)",
        "move-page": "De Sigg „$1“ ömnenne",
        "move-page-legend": "Sigg Ömnenne",
-       "movepagetext": "Heh kanns De en Sigg ömnenne.\nDomet kritt di Sigg ene neue Name, un all vörherije Versione vun dä Sigg och.\nUnger däm ahle Tittel weed automatisch en Ömleidong op dä neue Tittel enjedrare.\n\nDo kannß dat Höksche säze domet Ömleidonge automattesch aanjepaß wääde, di op dä ahle Tittel zeije — dat weet ävver nur allmählesch pö a pö hengerher jemaat.\nLinks op dä ahle Tittel blieve ävver wi se wore, wann De dat Höksche nit säz.\nDat heiß, dann moß De selver nohluure, of do jäz [[Special:DoubleRedirects|dubbelde Ömleidonge]] udder [[Special:BrokenRedirects|kapodde Ömleiduoge]] bei eruskumme.\nWann De en Sigg ömnenne deis, häs Do och doför ze sorje, dat de betroffene Links do henjonn, wo se hen jonn solle.\nAlsu holl Der de Liss „Wat noh heh link“ fun dä Sigg heh un jangk se dorsch!\n\nDe Sigg weed '''nit''' ömjenannt, wann et met däm neue Name ald en Sigg jitt, '''ußer''' et es en Ömleidong un se es noch nie jeändert woode.\nEsu kam_mer en Sigg jlich widder zeröck ömbenänne, wam_mer sich bem Ömbenänne verdonn hät, un mer kann och kein Sigge kapottmaache, wo ald jet drop schteiht.\n\n'''Oppjepass!'''\nWat beim Ömnenne erus kütt, künnt en opfällije un villeisch stüürende Änderong aam Wiki sin, besönders bei öff jebruchte Sigge.\nAlsu bes secher, dat De verschteihs, wat De heh am maache bes, ih dat De et mähs!",
+       "movepagetext": "Heh kanns De en Sigg ömnenne.\nDomet kritt di Sigg ene neue Name, un all vörherije Versione vun dä Sigg och.\nUnger däm ahle Tittel weed automatisch en Ömleidong op dä neue Tittel enjedrare.\n\nDo kannß dat Höksche säze domet Ömleidonge automattesch aanjepaß wääde, di op dä ahle Tittel zeije — dat weet ävver nur allmählesch pö a pö hengerher jemaat.\nLinks op dä ahle Tittel blieve ävver wi se wore, wann De dat Höksche nit säz.\nDat heiß, dann moß De selver nohluure, of do jäz [[Special:DoubleRedirects|dubbelde Ömleidonge]] udder [[Special:BrokenRedirects|kapodde Ömleiduoge]] bei eruskumme.\nWann De en Sigg ömnenne deis, häs Do och doför ze sorje, dat de betroffene Links do henjonn, wo se hen jonn solle.\nAlsu holl Der de Liss „Wat noh heh link“ fun dä Sigg heh un jangk se dorsch!\n\nDe Sigg weed <strong>nit</strong> ömjenannt, wann et met däm neue Name ald en Sigg jitt, <strong>ußer</strong> et es en Ömleidong un se es noch nie jeändert woode.\nEsu kam_mer en Sigg jlich widder zeröck ömbenänne, wam_mer sich bem Ömbenänne verdonn hät, un mer kann och kein Sigge kapottmaache, wo ald jet drop schteiht.\n\n<strong>Oppjepass!</strong>\nWat beim Ömnenne erus kütt, künnt en opfällije un villeisch stüürende Änderong aam Wiki sin, besönders bei öff jebruchte Sigge.\nAlsu bes secher, dat De verschteihs, wat De heh am maache bes, ih dat De et mähs!",
        "movepagetext-noredirectfixer": "Heh kanns De en Sigg ömnenne.\nDomet kritt di Sigg ene neue Nahme, un all vörherije Väsjohne vun dä Sigg och.\nOnger däm ahle Tittel weed automattesch en Ömleidong op dä neue Tittel enjedrare.\n\nLenks op dä ahle Tittel bliive ävver, wie se wohre.\nDat heiß, Do moß selver nohloore, ov doh jetz [[Special:DoubleRedirects|dubbelde]] oder [[Special:BrokenRedirects|kapodde Ömleidonge]] bei eruskumme.\nWann De en Sigg ömnenne deiß, häs Do och doför ze sorje, dat de betroffe Links doh henjonn, wo se hen jonn solle.\nAlsu holl Der di Liss „Wat noh heh link“ fun dä Sigg heh un jangk se dorsch!\n\nDi Sigg weed '''nit''' ömjenannt, wann et met däm neue Tittel ald en Sigg jitt, '''ußer''' doh es nix drop, oder et es en Ömleijdong un se es noch nie jeändert woode.\nEsu kam_mer en Sigg jlich widder retuur ömnänne, wam_mer sich mem Ömnänne verdonn hät, un mer kann och kein Sigge kapottmaache, wo ald jet drop schteiht.\n\n<strong>Oppjepaß!</strong>\nWat beim Ömnänne erus kütt, künnt en opfällije un velleijsch stührende Änderong aam Wikki sin, besönders bei öff jebruchte Sigge.\nAlsu bes secher, dat De verschteihs, wat De heh am maache bes, ih dat De et mähs!",
        "movepagetalktext": "Wam_mer en däm Kääsje e Höhksche määt, weed heh di Sigg automattesch ömjenannd op di neuje Övverschreff, ußer wann en Klaafsigg met dä neuje Övverschrev ald do es, un et steiht och jet drop.\n\nEn dämm Fall mpß De Der dä Enhald vun dä Klaafsigge selvs vörnemme, un eröm kopeere wat De bruchs.",
        "moveuserpage-warning": "'''Opjepaß:''' Do wells en Metmaachersigg ömnänne, domet weed ävver dä Metmaacher sellver ''nit'' met ömjenannt.",
        "movenosubpage": "Di Sigg hät kei Ongersigge.",
        "movereason": "Aanlass:",
        "revertmove": "Et Ömnänne zerök_nämme",
-       "delete_and_move_text": "== Dä! Dubbelte Name ==\nDi Sigg „[[:$1]]“ jitt et ald. Wollts De se fottschmieße, öm heh di Sigg ömnenne ze künne?",
+       "delete_and_move_text": "Di Sigg „[[:$1]]“ jitt et ald. Wollts De se fottschmieße, öm heh di Sigg ömnenne ze künne?",
        "delete_and_move_confirm": "Jo, dun di Sigg fottschmieße.",
        "delete_and_move_reason": "Fottjeschmeße, öm di Sigg „[[$1]]“ ömbenänne ze künne.",
        "selfmove": "Du Doof! - dä ahle Name un dä neue Name es däselve - do hät et Ömnenne winnich Senn.",
        "move-leave-redirect": "Donn en Ömleidong doför ennreschte",
        "protectedpagemovewarning": "'''Opjepaß:''' Heh di Sigg es jespert su dat blooß de Wiki-Kööbeße se ömnänne künne.\nHeh kütt der neuste Enndrach em Logbooch doh drövver:",
        "semiprotectedpagemovewarning": "'''Opjepaß:''' Heh di Sigg es jespert su dat blooß aanjemeldte Metmaacher se ömnänne künne.\nHeh kütt der neuste Enndrach em Logbooch doh drövver:",
-       "move-over-sharedrepo": "==Di Dattei jidd_et ald==\nEn Dattei [[:$1]] jidd_et ald en enem jemeinsame Beschtand. En annder Dattei op dä Name ömzenänne sorresch doför, dat mer aan di Dattei em jemeinsame Beschtand vun heh uß donoh nit mieh draan kütt.",
+       "move-over-sharedrepo": "En Dattei [[:$1]] jidd_et ald en enem jemeinsame Beschtand. En annder Dattei op dä Name ömzenänne sorresch doför, dat mer aan di Dattei em jemeinsame Beschtand vun heh uß donoh nit mieh draan kütt.",
        "file-exists-sharedrepo": "Dinge Nahme för di Dattei weed ald jebruch, un zwa en enem jemeinsame Beschtand vun Dateije.\nDröm söhk ene andere Nahme uß.",
        "export": "Sigge Exporteere",
        "exporttext": "Heh exportees De dä Tex un de Eijeschaffte vun ener Sigg, oder vun enem Knubbel Sigge, de aktuelle Version, met oder ohne ehr ählere Versione.\nDat Janze es enjepack en XML.\nDat kam_mer en en ander Wiki — wann et och met dä MediaWiki-Soffwär läuf — övver de Sigg „[[Special:Import|Import]]“ do widder empotehre.\n\nSchriev de Titele vun dä Sigge en dat Feld för Tex enzejevve, unge, eine Titel en jede Reih.\nDann dun onoch ussöke, ov De all de vörherije Versione vun dä Sigge han wells, oder nor de aktuelle met dä Informazjuhne vun de läzde Änderong.\n\nEn däm Fall künns De, för en einzelne Sigg, och ene tirekte Link bruche, zom Beispill „[[{{#Special:Export}}/{{MediaWiki:Mainpage}}]]“ för de Sigg „[[{{MediaWiki:Mainpage}}]]“ ze exporteere.",
        "tooltip-pt-preferences": "De eije Ennschtällonge{{GENDER:|}}",
        "tooltip-pt-watchlist": "De Leß met de Sigge en Dinge eije Oppaßleß",
        "tooltip-pt-mycontris": "En Leß met Dinge eije Beijdrähsch{{GENDER:|}}",
+       "tooltip-pt-anoncontribs": "En Leß met de Verännderong, di vun heh dä <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Internet Protocol\">IP</i>-Adräß uß jemaat wode sin.",
        "tooltip-pt-login": "Do moß Desch nit Enlogge, kannz_E ävver jähn maache!",
        "tooltip-pt-logout": "Ußlogge",
        "tooltip-pt-createaccount": "mer schlonn vör, dat De Desch aanmällde deihs un ennloggs, ävver müüdesch es et nit.",
        "tooltip-feed-rss": "Dä RSS-Abonnomang-Kannal (Feed) för heh di Sigg",
        "tooltip-feed-atom": "Dä Atom-Abonnomang-Kannal (Feed) för heh di Sigg",
        "tooltip-t-contributions": "Donn en Leß met dä Bedrähsch {{GENDER:$1|vun heh däm|vun heh dämm|vun heh dämm Metmaacher|vun heh dä|vun heh däm}} belohre",
-       "tooltip-t-emailuser": "Scheck en E-Mail aan dä Metmaacher",
+       "tooltip-t-emailuser": "Scheck en E-Mail aan {{GENDER:$1|dä Metmaacher|de Metmaacherėn|dä Metmaacher|de Metmaacherėn|dä Metmaacher}}",
        "tooltip-t-info": "Mih Aanjahbe övver heh di Sigg",
        "tooltip-t-upload": "Dateie huhlade",
        "tooltip-t-specialpages": "Leß met de {{int:nstab-special}}e",
        "lastmodifiedatby": "Di Sigg heh wohd et läz aam $1 öm $2 Uhr vum $3 jeändert.",
        "othercontribs": "Bout op et Werk vun $1 op.",
        "others": "ander",
-       "siteusers": "{{PLURAL:$2|däm|de|keine}} {{PLURAL:$2|Metmaacher|Metmaachere|Metmaacher}} $1 aan {{GRAMMAR:Dat|{{SITENAME}}}}",
+       "siteusers": "{{PLURAL:$2|däm Metmaacher|dä Metmaacher|keijnem Metmaacher}} $1 {{GRAMMAR:vum|{{ucfirst:{{SITENAME}}}}}}",
        "anonusers": "{{PLURAL:$2|dä|de|keine}} nameloose Metmaacher $1 vun de translatewiki.net",
        "creditspage": "Övver de Metmaacher un dänne ehr Beijdrähsch för heh di Sigg",
        "nocredits": "För di Sigg ham_mer nix en de Leß.",
        "pageinfo-category-files": "De Aanzahl Dateie",
        "markaspatrolleddiff": "Nohjeluurt. Dun dat fasshallde.",
        "markaspatrolledtext": "De Änderong es nohjeluhrt, don dat faßhallde",
+       "markaspatrolledtext-file": "Makkehr heh di Väsjohn vun dä Dateij als nohjekik.",
        "markedaspatrolled": "Et Kennzeiche „Nohjeluurt“ speichere",
        "markedaspatrolledtext": "Et es jetz fassjehallde, dat de usjewählte Version vun dä Sigg „[[:$1]]“ nohjeluurt sin.",
        "rcpatroldisabled": "Et Nohluure vun de letzte Änderunge es avjeschalt",
        "newimages-legend": "Ußwähle",
        "newimages-label": "Dä Dattei ier Name udder e Stöck dofun:",
        "newimages-showbots": "Zeisch, wat de Bots huhjelaade han.",
+       "newimages-hidepatrolled": "Donn de nohjekik huhjelahde Dateije ußblännde.",
        "noimages": "Kein Dateie jefunge.",
        "ilsubmit": "Söhk",
        "bydate": "nohm Datum",
        "exif-compression-34712": "<i lang=\"en\">JPEG</i>2000",
        "exif-copyrighted-true": "Häd_en Urhävverrääsch",
        "exif-copyrighted-false": "Nix övver et Urhävverrääsch jesaat",
+       "exif-photometricinterpretation-1": "Schwazz un Wiiß (Schwazz es 0)",
        "exif-photometricinterpretation-2": "RJB",
        "exif-photometricinterpretation-6": "<i lang=\"en\">YCbCr</i>",
        "exif-unknowndate": "Dattum onbikannt",
        "confirmemail_body_set": "Künnt johd sin, Do wors et sällver. Vun dä IP-Adräß $1 hät op\njede Fall einer för dä Metmaacher \"$2\" op {{GRAMMAR:Akk bet|{{SITENAME}}}}\nheh di Adräß för däm sing e-mail aanjejovve.\n\nÖm jäz kloh ze kreje, dat di neu Adräß un dä Metmaacher och\nzosamme jehühre, un öm de e-mail op {{GRAMMAR:Akk bet|{{SITENAME}}}}\naanzschallde, moß dä Metmaacher en singem Brauser dä Lengk:\n\n$3\n\nopmaache. Noch för em $6 öm $7 Uhr. Alsu dun dat, wann dat sing\nReeschteschkeijt hät.\n\nWann nit Doh, sönders söns wä Ding Addräß för de e-Mail aanjejovve hät, bruchs\nDe jar nix ze don. Di Adräß weed nit jebruch, wann se nit beschtähtesch es.\nDo kanns ävver och op heh dä Lengk jon:\n\n$5\n\nDomet deiß De tirek vermällde, dat De di Adräß nit beschtähteje wells.",
        "confirmemail_invalidated": "Et Beschtähtejje för di <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>-Adräß es afjebroche wohde, un di Adräß es '''nit''' beschtähtesch.",
        "invalidateemail": "E-Mail-Adress nit bestätich",
+       "notificationemail_subject_changed": "{{SITENAME}} - De Addräß för de e-mail wood veränndert.",
+       "notificationemail_subject_removed": "{{SITENAME}} - De Addräß för de e-mail wood fott jeschmeße.",
+       "notificationemail_body_changed": "Velleijsch wohß De_t sällver. Eijne hät vun dä <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Internet Protocol\">IP</i>-Adräß $1 us {{ucfirst:{{GRAMMAR:em|{{ucfirst:{{SITENAME}}}}}}}} de \n<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>-Adräß {{GENDER:$2|vum|vum|vumm Metmaacher|vun dä|vum}} „$2“ op „<code>$3</code>“ ömjeschtallt.\n\nWann De et nit sällver wohs, saach tirägg enem Verantwootlijje för di Wäbßait bescheijd!",
+       "notificationemail_body_removed": "Velleijsch wohß De_t sällver. Eijne hät vun dä <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Internet Protocol\">IP</i>-Adräß $1 us {{ucfirst:{{GRAMMAR:em|{{ucfirst:{{SITENAME}}}}}}}} de \n<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>-Adräß {{GENDER:$2|vum|vum|vumm Metmaacher|vun dä|vum}} „$2“ fott jenumme.\n\nWann De et nit sällver wohs, saach tirägg enem Verantwootlijje för di Wäbßait bescheijd!",
        "scarytranscludedisabled": "[Et Enbinge per Interwiki es avjeschalt]",
        "scarytranscludefailed": "[De Schablohn „$1“ enzebenge hät nit jeflupp]",
        "scarytranscludefailed-httpstatus": "[De Schablohn „$1“ enzebenge hät nit jeflupp. Dä HTTP-Fähler es: $2]",
        "confirm-unwatch-button": "Lohß Jonn!",
        "confirm-unwatch-top": "Sulle mer di Sigg uß Dinger Oppaßleß erußnämme?",
        "confirm-rollback-button": "Lohß Jonn!",
+       "confirm-rollback-top": "Ännderonge aan dä Sigg heh zerök nämme?",
        "semicolon-separator": ";",
        "word-separator": "&#32;",
        "ellipsis": "&nbsp;…",
        "watchlistedit-raw-done": "Ding Oppaßßleß es fassjehallde.",
        "watchlistedit-raw-added": "{{PLURAL:$1|Ein Övverschreffför en Sigge wood|<strong>$1</strong> Övverschreffte för Sigge woodte|Kein Övverschreffte för Sigge}} dobeijedonn:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|Eine Endrach es eruß jefloore:|<strong>$1</strong> Endräsh es eruß jefloore:|Keine Endrach es eruß jefloore.}}",
-       "watchlistedit-clear-title": "Oppaßleß läddesch jemaad",
+       "watchlistedit-clear-title": "Oppaßleß läddesch maache",
        "watchlistedit-clear-legend": "Oppaßleß läddesch maache",
        "watchlistedit-clear-explain": "Alle vun heh dä Siggetettelle fleeje uß dä Oppaßless eruß.",
        "watchlistedit-clear-titles": "Siggetettelle",
        "expand_templates_generate_xml": "Och dä XML-Parser-Boum zeije",
        "expand_templates_generate_rawhtml": "Donn de Röh HTML Ußjaav aanzeije",
        "expand_templates_preview": "Vör-Aansich",
-       "expand_templates_preview_fail_html": "<em>Weil et Wiki rüh <i xml:lang=\"en\" title=\"HyperText Markup Language\" lang=\"en\">HTML</i> zohlöht un de Sezongsdahte verschött jejange sin, dom_mer de {{int:preview}} uß Vörseesch nit aanzeije, öm Aanjreffe övver JavaSkrep zevör ze kumme.</em>\n\n<strong>Wann dat heh en Ohdenong es, bes esu johd un versöhg et norr_ens.</strong>\nwann dat nix hellef, versöhg ens [[Special:UserLogout|ußzelogge]] un neu enzelogge.",
+       "expand_templates_preview_fail_html": "<em>Weil et Wiki rüh <i xml:lang=\"en\" title=\"HyperText Markup Language\" lang=\"en\">HTML</i> zohlöht un de Sezongsdahte verschött jejange sin, dom_mer de {{int:preview}} uß Vörseesch nit aanzeije, öm Aanjreffe övver JavaSkrep zevör ze kumme.</em>\n\n<strong>Wann dat heh en Ohdenong es, bes esu johd un versöhg et norr_ens.</strong>\nwann dat nix hellef, versöhg ens [[Special:UserLogout|ußzelogge]] un neu enzelogge, un donn pröhve dat Dinge Brauser <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„Plätzjer“\">cookies</i> vun heh däm Wikki aannemmp.",
        "expand_templates_preview_fail_html_anon": "<em>Weil et Wiki rüh <i xml:lang=\"en\" title=\"HyperText Markup Language\" lang=\"en\">HTML</i> zohlöht un Do nit ennjelogg bes, dom_mer de {{int:preview}} uß Vörseesch nit aanzeije, öm Aanjreffe övver JavaSkrep zevör ze kumme.</em>\n\n<strong>Wann dat heh en Ohdenong es, bes esu johd un donn [[Special:UserLogin|enlogge]] un versöhg et norr_ens.</strong>",
        "expand_templates_input_missing": "Mer mß winnischsdrens jät Täx ennjävve.",
        "pagelanguage": "De Schprohch för di Sigg faßlääje",
        "log-name-pagelang": "Logbohch vum Tuusche vun Sige iehr Schprohche",
        "log-description-pagelang": "Dat heh es et Logbohch vun de Veränderonge aan de Schprohch vun de Sigge.",
        "logentry-pagelang-pagelang": "{{GENDER:$2|Dä|Dat|Dä Metmaacher|De|Dat}} $1 hät de Schprohch vun dä Sigg „$3“ vun $4 op $5 verändert.",
-       "default-skin-not-found": "De schtandattmähßejje Bedehnbovverfläsch <code>$1</code> för et Wikki es nit ze fenge. Se weed övver dä Enndrahch <code lang=\"en\" xml:lang=\"en\">$wgDefaultSkin</code> en dä Dattei <code lang=\"en\" xml:lang=\"en\">LocalSettings.php</code> om ẞööver faßjelaat.\n\n{{PLURAL:$4|Heh di Bedehnbovverfläsch es|Heh di Bedehnbovverfläsche sin|Kein Bedehnbovverfläsche sin}} doh:\n\n$2\n\nLohr och en et [https://www.mediawiki.org/wiki/Manual:Skin_configuration/de Handbohch övver et Enschtälle vun Bedehnbovverfläsche].\n\n*'''Falls dat heh e fresch enjereesch MehdijaWikki es:'''\n*: MehdijaWikki wood velleisch övver <i lang=\"en\" xml:lang=\"en\">Git</i> enschtallehrt, udder der Quälltäx wood tiräk obb_en ander Manier enschtallehrt. Met däm Problehm heh wohr ze rääschne. Donn winneschßdens eine vun dä Bovverfläsche uss_em [https://www.mediawiki.org/wiki/Category:All_skins Verzeischneß vun de Bedehnbovverfläsche vum MehdijaWikki] enschtallehre. Dat jeihd, endämm dat De:\n*:* einzel veröffentleschte Bovverfläsche us [https://www.mediawiki.org/wiki/Special:SkinDistributor MediaWiki.org] erongerlähds un en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun dä MehdijaWikki_Enschtallazuhn holls,\n*:* winneschsdens eins vun dä Verzeischneße us <code lang=\"en\" xml:lang=\"en\">mediawiki/skins/*</code> met <i lang=\"en\" xml:lang=\"en\">Git</i> en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun Dinge MehdijaWikki_Enschtallazuhn holls,\n*:* de [https://www.mediawiki.org/wiki/Download Dattei vum MehdijaWikki] erongerlähds, woh ongerscheidlejje Bedehnbovverfläsche dren sin un Zohsäz derzoh. Uß däm Verzeischneß doh dren kam_mer Saache en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun dä MehdijaWikki_Enschtallazuhn holle.\n*: Dat sullt sesch nit met Dingem <i lang=\"en\" xml:lang=\"en\">git</i>-Verzeischneß schtühre, falls De och ene Äntweckler vum MehdijaWikki bes.\n*'''Falls dat MehdijaWikki heh jrahd obb ene neue Schtand jebraht wood:'''\n*: Bei MehdijaWikki en dä Väsjohn 1.24 un hüüter wääde de enschtallehrte Bedehnbovverfläsche nit mieh automattesch alle aanjemaat; süsch och em [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Handbohch] dernoh. Do kanns heh di {{PLURAL:$5|Reih|Reihje|kein Reihje}} en de Dattei <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">LocalSettings.php</code> eren koppehre, öm {{PLURAL:$5|di enschtallehrte Bedehnbovverfläsch|alle enschtallehrte Bedehnbovverfläsche|kein Bedehnbovverfläsch}} aanzeschallde:\n<pre lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$3</pre>\n* '''Falls de jrahd aan dä Dattei <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">LocalSettings.php</code> jät geändert häs:'''\n*: Donn de Nahme vun de Bedehnbovverfläsche en dä Dattei pröhve. Se künnte verhehrt jeschrevve sin.",
+       "default-skin-not-found": "De schtandattmähßejje Bedehnbovverfläsch <code>$1</code> för et Wikki es nit ze fenge. Se weed övver dä Enndrahch <code dir=\"ltr\">$wgDefaultSkin</code> en dä Dattei <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">LocalSettings.php</code> om ẞööver faßjelaat.\n\n{{PLURAL:$4|Heh di Bedehnbovverfläsch es|Heh di Bedehnbovverfläsche sin|Kein Bedehnbovverfläsche sin}} doh:\n\n$2\n\nLohr och en et [https://www.mediawiki.org/wiki/Manual:Skin_configuration/de Handbohch övver et Enschtälle vun Bedehnbovverfläsche].\n\n; Falls dat heh e fresch enjereesch MehdijaWikki es:\n: MehdijaWikki wood velleisch övver <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Git</i> enschtallehrt, udder der Quälltäx wood tiräk obb_en ander Manier enschtallehrt. Met däm Problehm heh wohr ze rääschne. Donn winneschßdens eine vun dä Bovverfläsche uss_em [https://www.mediawiki.org/wiki/Category:All_skins Verzeischneß vun de Bedehnbovverfläsche vum MehdijaWikki] enschtallehre. Dat jeihd, endämm dat De:\n:* einzel veröffentleschte Bovverfläsche us [https://www.mediawiki.org/wiki/Special:SkinDistributor MediaWiki.org] erongerlähds un en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun dä MehdijaWikki_Enschtallazuhn holls,\n:* winneschsdens eins vun dä Verzeischneße us <code lang=\"en\" xml:lang=\"en\">mediawiki/skins/*</code> met <i lang=\"en\" xml:lang=\"en\">Git</i> en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun Dinge MehdijaWikki_Enschtallazuhn holls,\n:* de [https://www.mediawiki.org/wiki/Download Dattei vum MehdijaWikki] erongerlähds, woh ongerscheidlejje Bedehnbovverfläsche dren sin un Zohsäz derzoh. Uß däm Verzeischneß doh dren kam_mer Saache en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun dä MehdijaWikki_Enschtallazuhn holle.\n: Dat sullt sesch nit met Dingem <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\">git</i>-Verzeischneß schtühre, falls De och ene Äntweckler vum MehdijaWikki bes.\n\n;Falls dat MehdijaWikki heh jrahd obb ene neue Schtand jebraht wood:\n: Bei MehdijaWikki en dä Väsjohn 1.24 un hüüter wääde de enschtallehrte Bedehnbovverfläsche nit mih automattesch alle aanjemaat; süsch och em [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Handbohch] dernoh. Do kanns heh di {{PLURAL:$5|Reih|Reihje|kein Reihje}} en de Dattei <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">LocalSettings.php</code> eren koppehre, öm {{PLURAL:$5|di enschtallehrte Bedehnbovverfläsch|alle enschtallehrte Bedehnbovverfläsche|kein Bedehnbovverfläsch}} aanzeschallde:\n\n<pre lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$3</pre>\n\n;Falls de jrahd aan dä Dattei <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">LocalSettings.php</code> jät geändert häs:\n: Donn de Nahme vun de Bedehnbovverfläsche en dä Dattei pröhve. Se künnte verhehrt jeschrevve sin.",
        "default-skin-not-found-no-skins": "De schtandattmähßejje Bedehnbovverfläsch <code>$1</code> för et Wikki es nit ze fenge. Se weed övver dä Enndraach <code lang=\"en\" xml:lang=\"en\">$wgDefaultSkin</code> en dä Dattei <code lang=\"en\" xml:lang=\"en\">LocalSettings.php</code> om ẞööver faßjelaat.\n\nEt sinn_er kein Bedehnbovverfläsche doh.\n\n*'''Falls dat heh e fresch enjereesch MehdijaWikki, es udder jrahd obb ene neue Schtand jebraht wood:'''\n*: MehdijaWikki wood velleisch övver <i lang=\"en\" xml:lang=\"en\">Git</i> enschtallehrt, udder der Quälltäx wood tiräk obb_en ander Manier enschtallehrt. Met däm Problehm heh wohr ze rääschne. Bei MehdijaWikki en dä Väsjohn 1.24 un hüüter sin kein Bedehnbovverfläsche mieh automattesch derbei. Donn winneschßdens eine vun dä Bovverfläsche uss_em [https://www.mediawiki.org/wiki/Category:All_skins Verzeischneß vun de Bedehnbovverfläsche] enschtallehre. Dat jeihd, endämm dat De:\n*:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins <i lang=\"en\" xml:lang=\"en\">Git</i> nemms, öm de Bedehnbovverfläsche eronger ze lahde].\n*:* einzel veröffentleschte Bovverfläsche us [https://www.mediawiki.org/wiki/Special:SkinDistributor MediaWiki.org] erongerlähds un en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun Dinge MehdijaWikki_Enschtallazuhn holls,\n*:* de [https://www.mediawiki.org/wiki/Download Dattei vum MehdijaWikki] erongerlähds, woh ongerscheidlejje Bedehnbovverfläsche dren sin un Zohsäz derzoh. Uß däm Verzeischneß doh dren kam_mer Saache en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun Dinge vun dä MehdijaWikki_Enschtallazuhn holle.\n*: Dat sullt sesch nit met Dingem <i lang=\"en\" xml:lang=\"en\">Git</i>-Verzeischneß schtühre, falls De och ene Äntweckler vum MehdijaWikki bes. Lohr em [https://www.mediawiki.org/wiki/Manual:Skin_configuration Handbohch] dernoh, wi mer Bedehnbovverfläsche aanmääd un ene Schtandatt faßlähsch.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (enjeschalldt)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>ußjeschalldt</strong>)",
        "special-characters-group-ipa": "IPA, et engernazjonal foneetesch Alfabeet",
        "special-characters-group-symbols": "Symbole",
        "special-characters-group-greek": "Jriischesch",
+       "special-characters-group-greekextended": "Jrihschesch met Zohsäz",
        "special-characters-group-cyrillic": "Kyrillesch",
        "special-characters-group-arabic": "Arabesch",
        "special-characters-group-arabicextended": "Araabesch met Extras",
        "mw-widgets-dateinput-no-date": "Kein Dattom es ußjewählt",
        "mw-widgets-titleinput-description-new-page": "di Sigg jidd_et noch nit",
        "mw-widgets-titleinput-description-redirect": "ömleijde op „$1“",
-       "api-error-blacklisted": "Söhk Der ene anndere Nahme uß, dä mih drövver säht.",
+       "sessionprovider-generic": "Sezonge övver $1",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„Plätzjer“\">cookies</i>",
+       "sessionprovider-nocookies": "<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„Plätzjer“\">Cookies</i> künnte affjeschalld sin. Schtäl sescher, dat se ennjeschalld sin un fang norr_ens aan.",
        "randomrootpage": "Zofällige Aanfangs-Sigg",
+       "log-action-filter-block": "Zoot vun Spärr",
+       "log-action-filter-delete": "Zoot vum Fottschmiiße:",
+       "log-action-filter-import": "Zoot vum Emmpoot:",
+       "log-action-filter-managetags": "Zoot vun Verwalldongsaxjuhn",
+       "log-action-filter-move": "Zoot vum Ömnänne:",
+       "log-action-filter-newusers": "Zoot vum Zohjang aanlähje:",
+       "log-action-filter-patrol": "Zoot vum Nohlohre:",
+       "log-action-filter-protect": "Zoot vum Schoz:",
+       "log-action-filter-rights": "De Zoot Ännderong aan de Rääschte:",
+       "log-action-filter-suppress": "De Zoot Ongerdrökong:",
+       "log-action-filter-upload": "Zoot vum Huhlahde:",
        "log-action-filter-all": "Alle",
        "log-action-filter-block-block": "Schpärre",
+       "log-action-filter-block-reblock": "Änderung vun ener Schpärr",
        "log-action-filter-block-unblock": "Sperr ophävve",
        "log-action-filter-delete-delete": "En Sigg wohd fott jeschmeße",
+       "log-action-filter-delete-restore": "Sigge-Zerökholle",
+       "log-action-filter-delete-event": "Logbohch-Fottschmiiße",
+       "log-action-filter-delete-revision": "Väsjohn-Fottschmiiße",
+       "log-action-filter-import-interwiki": "Emmpood us enem anndre Wikki",
+       "log-action-filter-import-upload": "Empood uß ene huhjelade <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Extensible Markup Language\">XML</i>-Datteij",
+       "log-action-filter-managetags-create": "Makkehrong aanjelaat",
+       "log-action-filter-managetags-delete": "Makkehrong fottjeschmeße",
+       "log-action-filter-managetags-activate": "Makkehrong aanjeschalldt",
+       "log-action-filter-managetags-deactivate": "Makkehrong affjeschalldt",
+       "log-action-filter-move-move": "Ömjenannt ohne en Ömleijdong ze övverschrihve",
+       "log-action-filter-move-move_redir": "Ömjenannt obb en övverschrihve Ömleijdong",
+       "log-action-filter-newusers-create": "Aanjelaat vun enem Nahmelohse",
+       "log-action-filter-newusers-create2": "Aanjelaat vun enem ennjelogg Metmaacher",
+       "log-action-filter-newusers-autocreate": "Aumattesch-Aanlähje",
+       "log-action-filter-newusers-byemail": "Aanjelaat mem Paßwood pä <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> jescheck",
+       "log-action-filter-patrol-patrol": "Vun Hand Nohjelohrt",
+       "log-action-filter-patrol-autopatrol": "Automattesch Nohjelohrt",
+       "log-action-filter-protect-protect": "Schoz",
+       "log-action-filter-protect-modify": "Schoz-Ännderong",
+       "log-action-filter-protect-unprotect": "Schoz-Ophävve",
+       "log-action-filter-protect-move_prot": "Schoz jähje et Ömbenänne",
+       "log-action-filter-rights-rights": "Vun Hand veränndert",
+       "log-action-filter-rights-autopromote": "Automattesch veränndert",
+       "log-action-filter-suppress-event": "Logbohch ongerdrök",
+       "log-action-filter-suppress-revision": "Väsjohn ongerdrök",
+       "log-action-filter-suppress-delete": "Sigg ongerdrök",
+       "log-action-filter-suppress-block": "Metmaacher ongerdrök pä Schpärr",
+       "log-action-filter-suppress-reblock": "Metmaacher ongerdrök pä Wider-Schpärr",
+       "log-action-filter-upload-upload": "Neu huhjelahde",
+       "log-action-filter-upload-overwrite": "Neu huhlahde",
+       "authmanager-authn-autocreate-failed": "Automattesch ene Zojang för heh et Wikki hät nit jeflup: $1",
        "authmanager-create-disabled": "Neu Aanmelde es afjeschalldt",
        "authmanager-create-from-login": "Öm Der ene Zohjang aanzelähje, bes esu johd, un föll heh di Fällder us:",
        "authmanager-authplugin-setpass-failed-title": "Dat Paßwoot ze änndere hät nit jeflupp",
+       "authmanager-authplugin-setpass-bad-domain": "Onjöltijje Domäijn",
+       "authmanager-autocreate-noperm": "Automattesch Zohjäng aanzelähje es nit zohjelohße.",
+       "authmanager-autocreate-exception": "Automattesch Zohjäng aanzelähje es wähje verjange Fähler för en Zigg nit zohjelohße.",
        "authmanager-userdoesnotexist": "Ene Metmaacher mem Nahme „$1“ es nit ennjedrahre.",
+       "authmanager-username-help": "Der Metmaacher_Nahme för et Enlloge.",
+       "authmanager-password-help": "Et Paßwootför et Enlogge.",
        "authmanager-domain-help": "De Domäijn för de Zohjangsdaht vun ußerhallef beschtähtech ze krijje.",
        "authmanager-retype-help": "Norr_ens dat Paßwoot zom beschähtejje",
        "authmanager-email-label": "<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>",
        "authmanager-email-help": "De Addräß för de <i lang=\"en\">e-mail</i>",
        "authmanager-realname-label": "Der „reeschteje“ Nahme",
+       "authmanager-realname-help": "Der ääschte Nahme vun däm Metmaaacher",
        "authmanager-provider-temporarypassword": "Zweschepasswood:",
+       "authprovider-confirmlink-request-label": "De Zohjäng, di verlengk wähde sulle",
+       "authprovider-confirmlink-success-line": "$1 es jäz verbonge.",
        "authprovider-confirmlink-failed": "Et Zohjang-Verlengke hät nit kumplätt jeflupp: $1",
+       "authprovider-confirmlink-ok-help": "Maach wigger nohdämm Fählernohreeschte övver et verbenge ußjejovve woode sin.",
        "authprovider-resetpass-skip-label": "Övverjonn",
+       "authprovider-resetpass-skip-help": "Övverjangk et Paßwood zerök ze säzze.",
+       "authform-nosession-login": "Et Ennlogge hät jeflupp, ävver Dinge Brauser kunnt sesch nit draan „äntsenne“, enjelogg ze sin.\n\n$1",
+       "authform-nosession-signup": "Dä Zohjang es ennjereescht, ävver Dinge Brauser kunnt sesch nit draan „äntsenne“, enjelogg ze sin.\n\n$1",
        "authform-newtoken": "Keij CSRF Makkehrong:FUZZY!!$1",
        "authform-notoken": "Keij CSRF Makkehrong!FUZZY!!",
        "specialpage-securitylevel-not-allowed-title": "Nit zohjelohße",
+       "authpage-cannot-login": "Mer künne et Ennlogge nit bejenne.",
+       "authpage-cannot-login-continue": "Mer kunnte mem Ennlogge nit wigger maache. Ding Sezong weed ußjeloufe sinn.",
+       "authpage-cannot-create": "Mer künne et Aanlähje vun enem Zohjang nit bejenne.",
+       "authpage-cannot-create-continue": "Mer kunnte mem Aanlähje vun däm Zohjang nit wigger maache. Ding Sezong weed ußjeloufe sinn.",
+       "authpage-cannot-link": "Mer künne mem Zohjang Verbenge nit bejenne.",
+       "authpage-cannot-link-continue": "Mer kunnte mem Zohjang Verbenge nit wigger maache. Ding Sezong weed ußjeloufe sinn.",
        "cannotauth-not-allowed-title": "Zohjang verbodde.",
        "cannotauth-not-allowed": "Do häs nit dat Rääsch, he di Sigg ze bruche.",
        "changecredentials-submit": "Ändere",
-       "removecredentials-submit": "Fott nämme"
+       "removecredentials-submit": "Fott nämme",
+       "credentialsform-account": "Der Nahme för der Zohjang:",
+       "cannotlink-no-provider-title": "Mer han kein zweij Zohjäng, di mer verbenge künnt.",
+       "cannotlink-no-provider": "Mer han kein zweij Zohjäng, di mer verbenge künnt.",
+       "linkaccounts": "Zohjäng verbenge",
+       "linkaccounts-success-text": "Der Zohjang es jäz verbonge.",
+       "linkaccounts-submit": "Lohß jonn!",
+       "unlinkaccounts": "Zohjäng tränne",
+       "unlinkaccounts-success": "De Zohjäng sin jäz jetrännt."
 }
index dc2f043..ebee06b 100644 (file)
        "botpasswords-label-cancel": "Betal bike",
        "botpasswords-label-delete": "Jê bibe",
        "botpasswords-bad-appid": "Navê bot \"$1\" ne derbasdar e.",
+       "botpasswords-deleted-title": "Şîfreya robot hate jêbirin",
        "resetpass_forbidden": "Şîfre nikarin werin guhertin",
        "resetpass-submit-loggedin": "Şîfreyê biguherîne",
        "resetpass-submit-cancel": "Betal bike",
        "changeemail-oldemail": "Navnîşana e-nameya niha:",
        "changeemail-newemail": "Navnîşana e-nameya nû:",
        "changeemail-none": "(nîne)",
+       "changeemail-password": "Şîfreya te ya {{SITENAME}}ê:",
        "changeemail-submit": "Enameyê biguherîne",
        "resettokens": "Mifteya jê bibe",
        "bold_sample": "Nivîsa stûr",
        "minoredit": "Ev guhertineke biçûk e",
        "watchthis": "Vê gotarê bişopîne",
        "savearticle": "Rûpelê tomar bike",
+       "savechanges": "Guherandinan tomar bike",
+       "publishpage": "Rûpelê biweşîne",
+       "publishchanges": "Guherandinan biweşîne",
        "preview": "Pêşdîtin",
        "showpreview": "Pêşdîtinê nîşan bide",
        "showdiff": "Guherandinan nîşan bide",
        "showingresults": "{{PLURAL:$1|Encamek|'''$1''' encam}}, bi #'''$2''' dest pê dike.",
        "search-nonefound": "Ti rûpelên wek ya daxwazkirî nînin.",
        "powersearch-legend": "Lêgerîna pêşketî",
-       "powersearch-ns": "Di valahiya navan de lêbigere:",
+       "powersearch-ns": "Di valahiya navan de lê bigere:",
        "powersearch-togglelabel": "Kontrol bike:",
        "powersearch-toggleall": "Hemû",
        "powersearch-togglenone": "Tune",
        "right-sendemail": "Ji bikarhênerên di re ename bişîne",
        "grant-editpage": "Rûpelên ku hene biguherîne",
        "grant-editprotected": "Rûpelên parastî bigûherîne",
+       "grant-uploadfile": "Dosyeyên nû bar bike",
        "grant-basic": "Mafên bingehîn",
        "newuserlogpage": "Çêkirina hesabê nû",
        "newuserlogpagetext": "Ev têketina hesabên bikarhêneriyê ye ên ku nû hatine afirandin.",
        "rightslogtext": "Ev guhertineke ji bo mafên bikarhêneriyê ye.",
        "action-read": "vê rûpelê bixwîne",
        "action-edit": "vê rûpelê biguherîne",
-       "action-createpage": "rûpelan çêke",
-       "action-createtalk": "rûpelên gotûbêjan çêke",
+       "action-createpage": "vê rûpelê çêke",
+       "action-createtalk": "vê rûpela gotûbêjê çêke",
        "action-createaccount": "vê hesabê bikarhênerîyê çêke",
        "action-minoredit": "vê weke guhertineke biçûk nîşan bide",
        "action-move": "vê rûpelê bigerîne",
        "recentchanges-label-plusminus": "Qebareya vê rûpelê bi ev qas biteyan hate guherandin",
        "recentchanges-legend-heading": "<strong>Ravekirina kurtenavan:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (here [[Special:NewPages|lîsteya rûpelên nû]])",
+       "recentchanges-submit": "Nîşan bide",
        "rclistfrom": "Guherandinên ji $3 $2 şûnde nîşan bide",
        "rcshowhideminor": "Guherandinên biçûk $1",
        "rcshowhideminor-show": "nîşan bide",
        "tooltip-ca-nstab-category": "Li rûpelê kategorîyê seke",
        "tooltip-minoredit": "Vê guherandinê weka biçûk îşaret bike",
        "tooltip-save": "Guherandinên xwe tomarbike",
+       "tooltip-publish": "Guherandinên xwe biweşîne",
        "tooltip-preview": "Guherandinên xwe bibîne, berî ku tu wî qeyd bikî!",
        "tooltip-diff": "Guherandinên ku te di nivîsê de kirîyî nîşan bide",
        "tooltip-compareselectedversions": "Cudatiyên guhertoyên hilbijartî yên vê rûpelê bibîne.",
        "pageinfo-category-pages": "Hejmara rûpelan",
        "pageinfo-category-subcats": "Hejmara binkategoriyan",
        "pageinfo-category-files": "Hejmara dosyeyan",
-       "markaspatrolleddiff": "Wek serrastkirî nîşan bide",
-       "markaspatrolledtext": "Vê rûpelê wek serrastkirî nîşan bide",
-       "markedaspatrolled": "Wek serrastkirî tê nîşandan",
+       "markaspatrolleddiff": "Wek sererastkirî nîşan bide",
+       "markaspatrolledtext": "Vê rûpelê wek sererastkirî nîşan bide",
+       "markedaspatrolled": "Wek sererastkirî tê nîşandan",
        "markedaspatrolledtext": "Guherandina rûpelê wek serrastkirî tê nîşandan.",
        "patrol-log-page": "Têketina kontrolkirinê",
        "deletedrevision": "Guhertoya berê $1 hate jêbirin.",
        "imagemaxsize": "Sînorê mezinahiya wêneyî:<br /><em>(bo rûpelên danasîna wêneyan)</em>",
        "thumbsize": "Mezinahiya wêne:",
        "widthheight": "$1 x $2",
-       "widthheightpage": "$1 × $2, $3 rûpel",
+       "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|rûpel}}",
        "file-info": "mezinbûna daneyê: $1, MIME type: $2",
        "file-info-size": "$1 × $2 pixel, mezinbûnê data: $3, MIME-typ: $4",
        "file-nohires": "Versyonekî jê mezintir tune.",
        "searchsuggest-search": "Lêgerîn",
        "searchsuggest-containing": "dihundirîne...",
        "api-error-filename-tooshort": "Navê dosyeyê pir kurt e.",
+       "api-error-unclassified": "Çewtiyeke nenas pêk hat.",
        "api-error-unknown-code": "Çewtiya nenas: \"$1\".",
        "api-error-unknownerror": "Çewtiya nenas: \"$1\".",
        "duration-years": "$1 {{PLURAL:$1|sal}}",
        "mw-widgets-titleinput-description-redirect": "beralî bike ber bi $1 ve",
        "log-action-filter-all": "Hemû",
        "log-action-filter-block-block": "Asteng bike",
+       "log-action-filter-upload-upload": "Barkirina nû",
        "authmanager-email-label": "E-name",
        "authmanager-email-help": "Navnîşana e-nameyê"
 }
index 7896792..0d7d482 100644 (file)
        "passwordreset-emailtitle": "{{SITENAME}} сайтындагы эсеп жазуусу жөнүндөгү маалымат",
        "passwordreset-emailelement": "Колдонуучу аты: \n$1\n\nУбактылуу сырсөз: \n$2",
        "passwordreset-emailsentemail": "Сырсөздү алмаштыруу эмейлге жөнөтүлдү.",
-       "passwordreset-emailsent-capture": "Төмөндө көрсөтүлгөн эмейлге сырсөздү алмаштыруучу кат жөнөтүлдү.",
-       "passwordreset-emailerror-capture": "Төмөндө көрсөтүлгөн дарекке сырсөздү алмаштыруу кат түзүлдү,бирок аны  {{GENDER:$2|катышуучуга}} жөнөтүү оңунан чыккан жок: $1",
        "changeemail": "E-mail даректи өзгөртүү",
        "changeemail-header": "Эл. почтанын дарегин өзгөртүү",
        "changeemail-no-info": "Бул баракка түз кайрылыш үчүн, сиз системага киришиңиз керек.",
        "post-expand-template-argument-warning": "'''Эскертүү:''' Бул барак, жок дегенде, абдан чоң көлөмдүү калыптын бир жүйөсүн камтыйт жана  жайылганда өлчөмү абдан чоң болуп кетет. \nУшул сыяктуу жүйөлөр аттатылды.",
        "post-expand-template-argument-category": "Калыптардын аттатылган жүйөлөрүн камтыган барактар",
        "parser-template-loop-warning": "Калыптарда илмек бар:[[$1]]",
-       "cantcreateaccounttitle": "Эсеп жазуусун түзүү мүмкүн эмес",
        "viewpagelogs": "Бул барактын журналдарын көрүү",
        "nohistory": "Бул барактын өзгөртүүлөр тарыхы жок",
        "currentrev": "Соңку версиясы",
        "watchlisttools-view": "Тийиштүү өзгөрүүлөрдү кароо",
        "watchlisttools-edit": "Көзөмөл тизмесин кароо жана оңдоо",
        "watchlisttools-raw": "Жетиле элек көзөмөл тизмени оңдоо",
-       "signature": "[[{{ns:колдонуучу}}:$1|$2]] ([[{{ns:колдонуучу_баарлашуу}}:$1|баарлашуу]])",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|баарлашуу]])",
        "duplicate-defaultsort": "'''Эскертүү:''' \"$2\" белгиленген ылгоочу ачкыч \"$1\" мурунку белгиленген ылгоочу ачкычты жокко чыгарат.",
        "version": "Версия",
        "version-extensions": "Орнотулган кеңейтүүлөр",
index b8ab58d..5c34a58 100644 (file)
        "yourpasswordagain": "Tesseram adfirmare:",
        "createacct-yourpasswordagain": "Tesseram confirmare",
        "createacct-yourpasswordagain-ph": "Tesseram iterum inscribe",
-       "remembermypassword": "Tesseram meam hoc in navigatro inter conventa memento ({{PLURAL:$1|die|diebus}} $1 tenus)",
        "userlogin-remembermypassword": "Nomen meum retineatur",
        "yourdomainname": "Regnum tuum:",
        "login": "Nomen dare",
        "minoredit": "Haec est recensio minor",
        "watchthis": "Hanc paginam observare",
        "savearticle": "Hanc redactionem servare",
+       "savechanges": "Mutationes divulgare",
+       "publishpage": "Hanc paginam divulgare",
+       "publishchanges": "Hanc recensionem divulgare",
        "preview": "Praevidere",
        "showpreview": "Prospectum ostendere",
        "showdiff": "Mutationes ostendere",
        "parser-template-loop-warning": "Ansa formulae detecta: [[$1]]",
        "undo-norev": "Recensio abrogari non potuit quia non est aut deleta est.",
        "undo-summary": "Abrogans recensionem $1 ab usore [[Special:Contributions/$2|$2]] ([[User talk:$2|Disputatio]])",
-       "cantcreateaccounttitle": "Non licuit nomen tibi imponere",
        "cantcreateaccount-text": "Ex hoc loco IP ('''$1''') nomen sibi imponere ab usore [[User:$3|$3]] interdicitur.\nQui hanc causam dedit: ''$2''",
        "viewpagelogs": "Vide acta huius paginae",
        "nohistory": "Huic paginae non est historia.",
        "shared-repo-from": "apud {{grammar:accusative|$1}}",
        "shared-repo": "repositorium commune",
        "shared-repo-name-wikimediacommons": "Vicimedia Communia",
+       "upload-disallowed-here": "Hunc fasciculum substituere tibi non licet.",
        "filerevert": "Revertere $1",
        "filerevert-legend": "Reverti fasciculum",
        "filerevert-intro": "Reversurus es '''[[Media:$1|$1]]''' ad [$4 redactionem quae $2, $3 facta erat].",
        "tooltip-t-recentchangeslinked": "Nuper mutata in paginis quibus haec pagina nectit",
        "tooltip-feed-rss": "Fluxus RSS huius paginae",
        "tooltip-feed-atom": "Atom feed",
-       "tooltip-t-contributions": "Videre conlationes huius usoris",
+       "tooltip-t-contributions": "Videre conlationes huius usoris ($1)",
        "tooltip-t-emailuser": "Mittere litteras electronicas huic usori",
        "tooltip-t-upload": "Fasciculos imponere",
        "tooltip-t-specialpages": "Index paginarum specialium",
        "tooltip-ca-nstab-category": "Videre paginam categoriae",
        "tooltip-minoredit": "Indicare hanc recensionem minorem",
        "tooltip-save": "Servare mutationes tuas",
+       "tooltip-publish": "Hanc recensionem divulgare",
        "tooltip-preview": "Sinet prospicere, quod mutationes tuas effecerint. Utere, quaesumus, hac facultate, antequam servas!",
        "tooltip-diff": "Comparabit hanc redactionem cum superiore earumque differentias notabit",
        "tooltip-compareselectedversions": "Inspicere, quatenus contenta redactionum selectarum inter se distent",
index 45bf74a..5781171 100644 (file)
@@ -53,6 +53,7 @@
        "tog-watchlisthidebots": "Ännerunge vu Botten op menger Iwwerwaachungslëscht verstoppen",
        "tog-watchlisthideminor": "Kleng Ännerungen op menger Iwwerwaachungslëscht verstoppen",
        "tog-watchlisthideliu": "Ännerunge vun ugemellte Benotzer verstoppen",
+       "tog-watchlistreloadautomatically": "D'Iwwerwaachungslëscht nei lueden esoubal wéi e Filter geännert ass (JavaScript gëtt gebraucht)",
        "tog-watchlisthideanons": "Ännerunge vun anonyme Benotzer (IP-Adressen) verstoppen",
        "tog-watchlisthidepatrolled": "Iwwerkuckten Ännerungen op der Iwwerwaachungslëscht verstoppen",
        "tog-watchlisthidecategorization": "Kategorisatioun vu Säite verstoppen",
        "hidden-category-category": "Verstoppt Kategorien",
        "category-subcat-count": "Dës Kategorie huet {{PLURAL:$2|nëmmen dës Ënnerkategorie.|dës {{PLURAL:$1|Ënnerkategorie|$1 Ënnerkategorien}}, vu(n) $2 am Ganzen.}}",
        "category-subcat-count-limited": "Dës Kategorie huet dës {{PLURAL:$1|Ënnerkategorie|$1 Ënnerkategorien}}.",
-       "category-article-count": "An dëser Kategorie {{PLURAL:$2|ass just dës Säit.|{{PLURAL:$1|ass just dës Säit|si(nn) $1 Säiten}}, vu(n) $2 am Ganzen.}}",
+       "category-article-count": "An dëser Kategorie {{PLURAL:$2|ass just dës Säit.|{{PLURAL:$1|ass just dës Säit|sinn dës $1 Säiten}}, vu(n) $2 am Ganzen.}}",
        "category-article-count-limited": "Dës {{PLURAL:$1|Säit ass|$1 Säite sinn}} an dëser Kategorie.",
        "category-file-count": "{{PLURAL:$2|Just dëse Fichier ass an dëser Kategorie.|{{PLURAL:$1|Just dëse Fichier ass|Dës $1 Fichiere sinn}} an dëser Kategorie, vu(n) $2 am Ganzen.}}",
        "category-file-count-limited": "{{PLURAL:$1|Dëse Fichier ass|Dës $1 Fichiere sinn}} an dëser Kategorie.",
        "tagline": "Vu {{SITENAME}}",
        "help": "Hëllef",
        "search": "Sichen",
+       "search-ignored-headings": " #<!-- dës Zeil net änneren --> <pre>\n# Iwwerschrëften, déi vun der Sich ignoréiert ginn.\n# Dës Ännerunge gi wirksam, soubal déi Säit mat der Iwwerschrëft indexéiert gouf.\n# Dir kënnt déi Säitenindexéierung erzwéngen, andeem dir eng Nullännerung maacht.\n# Syntax:\n# * Alles, wat no enger Raut („#“) bis zum Ënn vun der Zeil steet, ass eng Bemierkung.\n# * All net-eidel Zeil ass de geneeën Titel fir z'ignoréieren.\nReferenzen\nWeblinken\nKuckt och\n #</pre> <!-- dës Zeil net änneren -->",
        "searchbutton": "Volltext-Sich",
        "go": "Lass",
        "searcharticle": "Säit",
        "yourpasswordagain": "Passwuert nach eemol antippen:",
        "createacct-yourpasswordagain": "Passwuert confirméieren",
        "createacct-yourpasswordagain-ph": "Passwuert nach eng Kéier aginn",
-       "remembermypassword": "Meng Umeldung op dësem Computer (fir maximal $1 {{PLURAL:$1|Dag|Deeg}}) verhalen",
        "userlogin-remembermypassword": "Mech ageloggt halen",
        "userlogin-signwithsecure": "Eng sécher Verbindung benotzen",
+       "cannotlogin-title": "Aloggen ass net méiglech",
+       "cannotlogin-text": "Aloggen ass net méiglech.",
        "cannotloginnow-title": "Aloggen ass elo net méiglech",
        "cannotloginnow-text": "Aloggen ass net méiglech wann dir $1 benotzt.",
+       "cannotcreateaccount-title": "Benotzerkont kënnen net opgemaach ginn",
        "yourdomainname": "Ären Domän:",
        "password-change-forbidden": "Dir däerft op dëser Wiki Passwierder net änneren.",
        "externaldberror": "Entweder ass e Feeler bei der externer Authentifizéierung geschitt, oder Dir däerft Ären externe Benotzerkont net aktualiséieren.",
        "login": "Aloggen",
+       "login-security": "Iwwerpréift Är Idnetitéit",
        "nav-login-createaccount": "Aloggen / Benotzerkont uleeën",
        "userlogin": "Aloggen / Benotzerkont uleeën",
        "userloginnocreate": "Umellen",
        "userlogin-resetpassword-link": "Hutt Dir Äert Passwuert vergiess?",
        "userlogin-helplink2": "Hëllef beim Aloggen",
        "userlogin-loggedin": "Dir sidd schonn als {{GENDER:$1|$1}} ageloggt.\nBenotzt de Formulaire hei drënner fir Iech als een anere Benotzer anzeloggen.",
+       "userlogin-reauth": "Dir musst Iech nach emol aloggen fir z'iwwerpréiwen datt Dir {{GENDER:$1|$1}} sidd.",
        "userlogin-createanother": "Maacht een anere Benotzerkont op",
        "createacct-emailrequired": "E-Mail-Adress",
        "createacct-emailoptional": "E-Mailadress (fakultativ)",
        "createacct-email-ph": "Gitt Är E-Mail-Adress an",
        "createacct-another-email-ph": "E-Mailadress aginn",
        "createaccountmail": "En temporäert zoufällegt Passwuert benotzen an et per E-Mail un déi spezifizéiert E-Mailadress schécken",
+       "createaccountmail-help": "Ka benotzt gi fir e Benotzerkont fir eng aner Persoun unzeleeën ouni d'Passwuert gewuer ze ginn.",
        "createacct-realname": "Richtegen Numm (fakultativ)",
        "createaccountreason": "Grond:",
        "createacct-reason": "Grond",
        "createacct-reason-ph": "Fir wat Dir een anere Benotzerkonnt uleet",
+       "createacct-reason-help": "Message deen am Logbuch vun den ugeluechte Benotzerkonte gewise gëtt",
        "createacct-submit": "Äre Benotzerkont uleeën",
        "createacct-another-submit": "Benotzerkont uleeën",
+       "createacct-continue-submit": "Virufuere mam Uleeë vum Benotzerkont",
+       "createacct-another-continue-submit": "Virufuere mam Uleeë vum Benotzerkont",
        "createacct-benefit-heading": "{{SITENAME}} gëtt vu Leit wéi Iech gemaach.",
        "createacct-benefit-body1": "{{PLURAL:$1|Ännerung|Ännerungen}}",
        "createacct-benefit-body2": "{{PLURAL:$1|Säit|Säiten}}",
        "nocookiesnew": "De Benotzerkont gouf ugeluecht, awer Dir sidd net ageloggt.\n{{SITENAME}} brauch fir dës Funktioun Cookien.\nDir hutt d'Cookien desaktivéiert.\nAktivéiert déi w.e.g. a loggt Iech da mat Ärem neie Benotzernomm a mat dem respektive Passwuert an.",
        "nocookieslogin": "{{SITENAME}} benotzt Cookië beim Umelle vun de Benotzer.\nDir hutt Cookien ausgeschalt.\nAktivéiert d'Cookien w.e.g. a versicht et nach eng Kéier.",
        "nocookiesfornew": "De Benotzerkont gouf net ugeluecht, well mir seng Quell net bestëmme konnten.\nVergewëssert Iech datt Dir Cookien zouloosst, luet dës Säit nei a probéiert nach emol.",
+       "createacct-loginerror": "De Benotzerkont gouf ugeluecht, mä Dir konnt net automatesch ageloggt ginn. [[Special:UserLogin|Loggt Iech w.e.g. manuell an]].",
        "noname": "Dir hutt kee gëltege Benotzernumm uginn.",
        "loginsuccesstitle": "Ageloggt",
        "loginsuccess": "'''Dir sidd elo als \"$1\" op {{SITENAME}} ugemellt.'''",
        "createacct-another-realname-tip": "De richtegen Numm ass fakultativ.\n\nWann Dir en ugitt, gëtt e benotzt fir d'Benotzerattributiounen fir Är Aarbecht zouzeuerdnen.",
        "pt-login": "Aloggen",
        "pt-login-button": "Aloggen",
+       "pt-login-continue-button": "Virufuere mam Aloggen",
        "pt-createaccount": "Benotzerkont opmaachen",
        "pt-userlogout": "Ausloggen",
        "php-mail-error-unknown": "Onbekannte Feeler an der PHP-Mail-Funktioun",
        "botpasswords-label-cancel": "Ofbriechen",
        "botpasswords-label-delete": "Läschen",
        "botpasswords-label-resetpassword": "D'Passwuert zrécksetzen",
+       "botpasswords-help-grants": "All Berechtegung gëtt Zougang op déi Benotzerrechter déi e Benotzerkont schonn huet. Kuckt d'[[Special:ListGrants|Tabell vun de Berechtigunge]] fir méi Informatiounen.",
+       "botpasswords-label-restrictions": "Limite fir d'Benotzen:",
        "botpasswords-label-grants-column": "Accordéiert",
        "botpasswords-bad-appid": "Den Numm vum Bot \"$1\" ass net valabel.",
+       "botpasswords-insert-failed": "De Botnumm \"$1\" konnt net dobäigesat ginn. Gouf e schonn derbäigesat?",
+       "botpasswords-created-title": "Botpasswuert ugeluecht",
        "botpasswords-created-body": "D'Botpasswuert fir de Bot-Numm \"$1\" vum Benotzer ''$2'' gouf ugeluecht.",
        "botpasswords-updated-title": "Botpasswuert aktualiséiert",
        "botpasswords-updated-body": "D'Botpasswuert fir de Bot-Numm \"$1\" vum Benotzer ''$2'' gouf aktualiséiert.",
        "botpasswords-deleted-title": "Botpasswuert geläscht",
        "botpasswords-deleted-body": "D'Botpasswuert fir de Bot-Numm \"$1\" vum Benotzer ''$2'' gouf geläscht.",
+       "botpasswords-newpassword": "Dat neit Passwuert fir sech mat <strong>$1</strong> anzeloggen ass <strong>$2</strong>.\n<em>Versuergt dat fir sech spéider dorop ze referéieren.</em>",
        "botpasswords-not-exist": "De Benotzer \"$1\" huet kee Botpasswuert mam Numm \"$2\".",
        "resetpass_forbidden": "Passwierder kënnen net geännert ginn.",
        "resetpass_forbidden-reason": "Passwierder kënnen net geännert ginn: $1",
        "passwordreset-emailelement": "Benotzernumm: \n$1\n\nTemporärt Passwuert: \n$2",
        "passwordreset-emailsentemail": "Wann dës E-Mailadress mat Ärem Benotzerkont assoziéiert ass, da gëtt Eng E-Mail fir d'Passwuert zréckzesetze geschéckt.",
        "passwordreset-emailsentusername": "Wann eng E-Mailadress mat dësem Benotzernumm associéiert ass, da gëtt Eng E-Mail fir d'Passwuert zeréckzesetze geschéckt.",
-       "passwordreset-emailsent-capture": "Eng Mail fir d'Passwuert zréckzesetze gouf geschéckt, Dir gesitt se hei drënner.",
-       "passwordreset-emailerror-capture": "Eng Mail fir d'Passwuert zréckzesetze gouf geschéckt, Dir gesitt se hei drënner, awer de {{GENDER:$2|Benotzer}} konnt se net kréien: $1",
        "passwordreset-invalideamil": "Net-valabel E-Mail-Adress",
+       "passwordreset-nodata": "Et gouf weder e Benotzernumm nach e Passwuert uginn",
        "changeemail": "E-Mail-Adress änneren oder ewechhuelen",
        "changeemail-header": "Fëllt dëse Formulaire aus fir Är E-Mailadress z'änneren.  Wann Dir d'Verbindung tëscht Ärer E-Mailadress an Ärem Benotzerkont ewechhuele wëllt, da loosst d'Feld e-Mailadress eidel wann Dir de Formulaire späichert.",
-       "changeemail-passwordrequired": "Dir musst Äert Passwuert agi fir dës Ännerung ze konfirméieren.",
        "changeemail-no-info": "Dir musst ageloggt sinn, fir direkt op dës Säit ze kommen.",
        "changeemail-oldemail": "Aktuell Mailadress:",
        "changeemail-newemail": "Nei Mailadress:",
        "showpreview": "Kucken ouni ofzespäicheren",
        "showdiff": "Ännerunge weisen",
        "blankarticle": "<strong>Opgepasst:</strong> D'Säit déi Dir uleet ass eidel.\nWann Dir nach eng Kéier op \"{{int:savearticle}}\" klickt, da gëtt d'Säit ugeluecht.",
-       "anoneditwarning": "<strong>Opgepasst:</strong> Dir sidd net ageloggt. Dowéinst gëtt amplaz vun engem Benotzernumm Är IP Adress ëffentlech gewise wann Dir Ännerunge maacht. Wann Dir <strong>[$1 Iech aloggt]</strong> oder <strong>[$2 e Bnotzerkont opmaachen]</strong>, Är Ännerunge ginn dann Ärem Benotzerkont zougedeelt, genee wéi aner Avantagen.",
+       "anoneditwarning": "<strong>Opgepasst:</strong> Dir sidd net ageloggt. Dowéinst gëtt amplaz vun engem Benotzernumm Är IP Adress ëffentlech gewise wann Dir Ännerunge maacht. Wann Dir <strong>[$1 Iech aloggt]</strong> oder <strong>[$2 e Benotzerkont opmaachen]</strong>, Är Ännerunge ginn dann Ärem Benotzerkont zougedeelt, genee wéi aner Avantagen.",
        "anonpreviewwarning": "''Dir sidd net ageloggt. Wann Dir ofspäichert gëtt Är IP-Adress an der Lëscht vun de Versioune vun dëser Säit enregistréiert.''",
        "missingsummary": "'''Erënnerung:''' Dir hutt kee Resumé aginn.\nWann Dir nacheemol op \"{{int:savearticle}}\" klickt, gëtt Är Ännerung ouni Resumé ofgespäichert.",
        "selfredirect": "<strong>Opgepasst:</strong> Dir maacht eng Viruleedung vun dëser Aäit op sech selwer.\n\nEt ka sinn datt Dir déi falsch Zilsäit fir d'Viruleedung aginn hutt oder datt Dir déi falsch Säit ännert.\n\nWann Dir nach eng Kéier op \"{{int:savearticle}}\" klickt, da gëtt d'Viruleedung trotzdem ugeluecht.",
        "previewnote": "'''Denkt drun datt dëst nëmmen eng net gespäichert Versioun ass.'''\nÄr Ännerunge sinn nach net gespäichert!",
        "continue-editing": "Gitt weider an de Beräich fir z'änneren",
        "previewconflict": "Dir gesitt an dem ieweschten Textfeld wéi den Text ausgesi wäert, wann Dir späichert.",
-       "session_fail_preview": "<strong>Är Ännerung konnt net gespäichert gi well d'Date vun Ärer Sessioun verluergaange sinn.</strong>\nVersicht et w.e.g. nach eng Kéier.\nWann de Problem dann ëmmer nach bestoe sollt, da versicht Iech [[Special:UserLogout|auszeloggen]] an dann erëm anzeloggen.",
+       "session_fail_preview": "Är Ännerung konnt net gespäichert gi well d'Date vun Ärer Sessioun verluergaange sinn.\nDir gouft eventuell ausgeloggt. <strong>Iwwerpréift w.e.g. ob Dir nach ageloggt sidd a probéiert nach eng Kéier</strong>.\nWann de Problem dann ëmmer nach bestoe sollt, da versicht Iech [[Special:UserLogout|auszeloggen]] an dann erëm anzeloggen an iwwerpréift datt Äre Browser Cookië vun dësem Site  akzeptéiert.",
        "session_fail_preview_html": "Är Ännerung konnt net gespäichert gi well d'Date vun Ärer Sessioun verluergaange sinn.\n\n<em>Well op {{SITENAME}} 'raw HTML' aktivéiert ass, gouf d'Uweise vun der nach net gespäicherter Versioun ausgeblennt fir JavaScript-Attacken ze vermeiden.</em>\n\n<strong>Wann Dir eng berechtegt Ännerung maache wëllt, da versicht et w.e.g. nach eng Kéier.</strong>\n\nWann de Problem dann ëmmer nach bestoe sollt, versicht Iech [[Special:UserLogout|auszeloggen]] an dann erëm anzeloggen a vergewëssert Iech datt Äre Browser d^Späichere vu Cookieë vun dësem Site zouléisst.",
        "token_suffix_mismatch": "'''Är Ännerung gouf refuséiert, well Äre Browser Zeechen am Ännerungs-Identifiant verännert huet.'''\nD'Ännerung gouf refuséiert, fir ze verhënneren datt den Text op der Säit onliesbar gëtt.\nDëst geschitt heiansdo wann Dir en anonyme Proxy-Service um Internet benotzt.",
        "edit_form_incomplete": "'''En Deel vum Ännerungsformulaire koum net um Server un; iwwerpréift w.e.g ob Är Ännerunge komplett sinn a probéiert nach emol.'''",
        "undo-nochange": "D'Ännerung gouf anscheinend schonn zeréckgesat.",
        "undo-summary": "Ännerung $1 vu(n) [[Special:Contributions/$2|$2]] ([[User talk:$2|Diskussioun]] | [[Special:Contributions/$2|{{MediaWiki:Contribslink}}]]) annulléieren.",
        "undo-summary-username-hidden": "Versioun $1 vun engem verstoppte Benotzer zrécksetzen",
-       "cantcreateaccounttitle": "Benotzerkont konnt net opgemaach ginn",
        "cantcreateaccount-text": "D'Opmaache vu Benotzerkonten vun dëser IP Adress ('''$1''') gouf vum [[User:$3|$3]] gespaart.\n\nDe Benotzer $3 huet \"$2\" als Grond uginn.",
        "cantcreateaccount-range-text": "D'Uleeë vu Benotzerkonte vun IP-Adressen aus dem Beräich \"$1\", zu deem Är IP-Adress (<strong>$4</strong>) gehéiert, gouf vum [[User:$3|$3]] gespaart.\n\nDe Grond den den $3 uginn huet ass <em>$2</em>",
        "viewpagelogs": "Logbicher fir dës Säit weisen",
        "mergehistory-empty": "Et kënne keng Versioune zesummegeluecht ginn.",
        "mergehistory-done": "{{PLURAL:$3|1 Versioun gouf|$3 Versioune goufe}} vu(n) $1 op [[:$2]] zesummegeluecht.",
        "mergehistory-fail": "Versiounszesummeleeung war net méiglech, kuckt w.e.g. d'Säiten an d'Zäit-Parameter no.",
+       "mergehistory-fail-bad-timestamp": "Zäitstempel ass net valabel.",
+       "mergehistory-fail-invalid-source": "Quellsäit ass net valabel.",
+       "mergehistory-fail-invalid-dest": "Zilsäit ass net valabel.",
+       "mergehistory-fail-permission": "Net genuch Rechter fir den Historique vun de Versiounen ze fusionéieren.",
+       "mergehistory-fail-self-merge": "Quell an Zilsäit sinn déi selwecht.",
        "mergehistory-fail-toobig": "D'zesummeleeë vun der Lëscht vun de Versioune konnt net gemaach ginn well méi wéi d'Limite vun $1 {{PLURAL:$1|Versioun|Versioune}} geréckelt géife ginn",
        "mergehistory-no-source": "Originalsäit \"$1\" gëtt et net.",
        "mergehistory-no-destination": "Zilsäit \"$1\" gëtt et net.",
        "right-createpage": "Säiten uleeën (déi keng Diskussiounssäite sinn)",
        "right-createtalk": "Diskussiounssäiten uleeën",
        "right-createaccount": "Nei Benotzerkonten uleeën",
+       "right-autocreateaccount": "Automatesch alogge mat engem externe Benotzerkont",
        "right-minoredit": "Ännerungen als kleng markéieren",
        "right-move": "Säite réckelen",
        "right-move-subpages": "Säiten zesumme mat hiren Ënnersäite réckelen",
        "right-unwatchedpages": "Lëscht vun den net iwwerwaachte Säite weisen",
        "right-mergehistory": "Zesummeféierung vum Historique vun de Versioune vu Säiten",
        "right-userrights": "All Benotzerrechter änneren",
-       "right-userrights-interwiki": "Benotzerrechter vu Benotzer op anere Wiki-Siten änneren",
+       "right-userrights-interwiki": "Benotzerrechter vu Benotzer op anere Wiki-Sitten änneren",
        "right-siteadmin": "Datebank spären an d'Spär ophiewen",
        "right-override-export-depth": "Säiten exportéieren inklusiv de verlinkte Säite bis zu enger Déift vu 5",
        "right-sendemail": "Anere Benotzer E-Maile schécken",
        "grant-group-high-volume": "Massenaktivitéiten ausféieren",
        "grant-group-customization": "Upassungen an Astellungen",
        "grant-group-administration": "Administrativ Aktioune maachen",
+       "grant-group-private-information": "Op perséinlech Date vun Iech zougräifen",
        "grant-group-other": "Verschidden Aktivitéiten",
        "grant-blockusers": "Benotzer spären an d'Spären ophiewen",
        "grant-createaccount": "Benotzerkonten opmaachen",
        "grant-editmywatchlist": "Ännert Är Iwwerwaachungslëscht",
        "grant-editpage": "Säiten déi et gëtt änneren",
        "grant-editprotected": "Gespaart Säiten änneren",
+       "grant-highvolume": "Massenännerungen",
        "grant-oversight": "Benotzer verstoppen a Versioune läschen",
        "grant-patrol": "Ännerungen op Säiten kontrolléieren",
+       "grant-privateinfo": "Op perséinlech Informatiounen zougräifen",
        "grant-protect": "Säite spären an entspären",
        "grant-rollback": "Ännerungen op Säiten zrécksetzen",
        "grant-sendemail": "Anere Benotzer E-Maile schécken",
        "action-createpage": "dës Säit uleeën",
        "action-createtalk": "dës Diskussiounssäit uleeën",
        "action-createaccount": "dëse Benotzerkont unzeleeën",
+       "action-autocreateaccount": "automatesch dësen externe Benotzerkont opzemaachen",
        "action-history": "d'Versioune vun dëser Säit weisen",
        "action-minoredit": "dës Ännerung als kleng Ännerung ze markéieren",
        "action-move": "dës Säit ze réckelen",
        "action-viewmyprivateinfo": "Är privat Informatioune kucken",
        "action-editmyprivateinfo": "Är privat Informatiounen änneren",
        "action-editcontentmodel": "de Modell vum Inhalt vun enger Säit änneren",
+       "action-purge": "dës Säit eidelzemaachen",
        "nchanges": "$1 {{PLURAL:$1|Ännerung|Ännerungen}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|zanter dem leschte Passage}}",
        "enhancedrc-history": "Versiounen",
        "recentchanges-feed-description": "Verfollegt mat dësem Feed déi rezent Ännerungen op {{SITENAME}}.",
        "recentchanges-label-newpage": "Mat dëser Ännerung gouf eng nei Säit ugeluecht",
        "recentchanges-label-minor": "Dëst ass eng kleng Ännerung",
-       "recentchanges-label-bot": "Dës Ännerung gouf vun engem Bot gemaacht",
+       "recentchanges-label-bot": "Dës Ännerung gouf vun engem Bot gemaach",
        "recentchanges-label-unpatrolled": "Dës Ännerung gouf nach net nogekuckt",
        "recentchanges-label-plusminus": "D'Gréisst vun der Säit huet sech ëm déi Zuel vu Bytes geännert",
        "recentchanges-legend-heading": "<strong>Legend:</strong>",
        "upload-too-many-redirects": "Et waren zevill Viruleedungen fir d'URL do",
        "upload-http-error": "Et ass en HTTP-Feeler geschitt: $1",
        "upload-copy-upload-invalid-domain": "Vun dësem Domain ass d'Eropluede vu Kopien net méiglech.",
+       "upload-foreign-cant-upload": "Dës Wiki ass net agestallt fir Fichieren an den ugefrote frieme Repertoire fir Fichieren eropzelueden.",
+       "upload-dialog-disabled": "D'Eropluede vu Fichieren mat dësem Dialog ass op dëser Wiki desaktivéiert.",
        "upload-dialog-title": "Fichier eroplueden",
        "upload-dialog-button-cancel": "Ofbriechen",
        "upload-dialog-button-done": "Fäerdeg",
        "upload-dialog-button-upload": "Eroplueden",
        "upload-form-label-infoform-title": "Detailer",
        "upload-form-label-infoform-name": "Numm",
+       "upload-form-label-infoform-name-tooltip": "E kuerzen an uniquen Titel fir de Fichier, deen och als Numm vum Fichier benotzt gëtt. Dir kënnt derbäi Text mat Espace benotzen. D'Erweiderung vum Fichier soll net ugi ginn.",
        "upload-form-label-infoform-description": "Beschreiwung",
+       "upload-form-label-infoform-description-tooltip": "Beschreift w.e.g. kuerz dat Wichtegst vun dësem Wierk.\nFir eng Photo, ernimmt déi Haaptsaachen déi drop sinn, d'Geleeënheet oder d'Plaz.",
        "upload-form-label-usage-title": "Benotzung",
        "upload-form-label-usage-filename": "Numm vum Fichier",
        "upload-form-label-own-work": "Dëst ass mäin eegent Wierk",
        "upload-form-label-infoform-categories": "Kategorien",
        "upload-form-label-infoform-date": "Datum",
+       "upload-form-label-not-own-work-local-generic-local": "Dir kënnt och [[Special:Upload|d'Standardsäit vum Eroplueden]] ausprobéieren.",
        "backend-fail-stream": "De Fichier $1 konnt net iwwerdroe ginn.",
        "backend-fail-backup": "De Fichier $1 konnt net geséchert ginn.",
        "backend-fail-notexists": "De Fichier $1 gëtt et net.",
        "filerevert-submit": "Zrécksetzen",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> gouf op d'[$4 Versioun vum $2, $3 Auer] zréckgesat.",
        "filerevert-badversion": "Et gëtt keng vireg lokal Versioun vun deem Fichier mat der Zäitinformatioun déi Dir uginn hutt.",
+       "filerevert-identical": "Déi aktuell Versioun vum Fichier ass identesch mat där erausgesichter.",
        "filedelete": "Läsch \"$1\"",
        "filedelete-legend": "Fichier läschen",
        "filedelete-intro": "Dir läscht de Fichier '''[[Media:$1|$1]]''' mat all senge Versiounen (Historique).",
        "apihelp": "API-Hëllef",
        "apihelp-no-such-module": "Modul \"$1\" net fonnt.",
        "apisandbox": "API-Sandkëscht",
+       "apisandbox-jsonly": "Fir d'API-Sandkëscht ze benotze braucht Dir JavaScript.",
        "apisandbox-api-disabled": "API ass op dësem Site ausgeschalt.",
        "apisandbox-unfullscreen": "Säit weisen",
        "apisandbox-submit": "Ufro maachen",
        "apisandbox-reset": "Eidel maachen",
        "apisandbox-retry": "Nach eng Kéier probéieren",
+       "apisandbox-no-parameters": "Dësen API-Modul huet keng Parameteren.",
        "apisandbox-helpurls": "Hëllef-Linken",
        "apisandbox-examples": "Beispiller",
        "apisandbox-dynamic-parameters": "Zousätzlech Parameteren",
        "apisandbox-dynamic-parameters-add-label": "Parameter derbäisetzen:",
        "apisandbox-dynamic-parameters-add-placeholder": "Numm vum Parameter",
+       "apisandbox-dynamic-error-exists": "Et gëtt schonn e Parameter mam Numm \"$1\".",
        "apisandbox-deprecated-parameters": "Vereelst Parameter",
        "apisandbox-submit-invalid-fields-title": "E puer Felder sinn net valabel.",
        "apisandbox-results": "Resultater",
+       "apisandbox-sending-request": "Schécke vun der API-Ufro...",
+       "apisandbox-loading-results": "Ofruffe vun den API-Resultater...",
        "apisandbox-request-url-label": "URL fir Ufroen:",
        "apisandbox-request-time": "Dauer vun der Ufro: {{PLURAL:$1|$1 ms}}",
        "apisandbox-alert-page": "Felder op dëser Säit sinn net valabel.",
+       "apisandbox-alert-field": "De wäert vun dësem Feld ass net valabel.",
        "booksources": "Bicherreferenzen",
        "booksources-search-legend": "No Bicherreferenze sichen",
        "booksources-search": "Sichen",
        "listgrouprights-namespaceprotection-header": "Limitatioune vum Nummraum",
        "listgrouprights-namespaceprotection-namespace": "Nummraum",
        "listgrouprights-namespaceprotection-restrictedto": "Recht(er), déi dem Benotzer d'Änneren erlaben",
+       "listgrants": "Autorisatiounen",
+       "listgrants-grant": "Autorisatioun",
        "listgrants-rights": "Rechter",
        "trackingcategories": "Tracking-Kategorien",
        "trackingcategories-msg": "Tracking-Kategorie",
        "trackingcategories-name": "Numm vum Message",
+       "restricted-displaytitle-ignored": "Säite mat ignoréierten Säitentitele fir unzeweisen",
        "noindex-category-desc": "D'Säit gëtt net vu Botten indexéiert, well dat magescht Wuert <code><nowiki>__NOINDEX__</nowiki></code> dran ass a well se an engem Nummraum ass, an deem déi Markéierung erlaabt ass.",
        "index-category-desc": "D'Säit huet <code><nowiki>__INDEX__</nowiki></code> an ass an engem Nummraum, wou déi Markéierung erlaabt ass an dofir gëtt d'Säit vu Sichroboter indexéiert wou dat normalerweis net de Fall wier.",
        "post-expand-template-inclusion-category-desc": "D'Säit ass méi grouss wéi <code>$wgMaxArticleSize</code> nom expandéiere vun alle Schablounen, dofir goufen e puer Schablounen net expandéiert.",
        "rollbacklinkcount": "{{PLURAL:$1|Eng Ännerung|$1 Ännerungen}} zrécksetzen",
        "rollbacklinkcount-morethan": "méi wéi {{PLURAL:$1|Eng Ännerung|$1 Ännerungen}} zrécksetzen",
        "rollbackfailed": "Zrécksetzen huet net geklappt",
+       "rollback-missingparam": "An der Ufro feelen obligatoresch Parameteren.",
+       "rollback-missingrevision": "D'Donnéeë vun der Versioun konnten net geluede ginn.",
        "cantrollback": "Lescht Ännerung kann net zréckgesat ginn. De leschten Auteur ass deen eenzegen Auteur vun dëser Säit.",
        "alreadyrolled": "Déi lescht Ännerung vun der Säit [[:$1]] vum [[User:$2|$2]] ([[User talk:$2|talk]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); kann net zréckgesat ginn;\neen Aneren huet dat entweder scho gemaach oder nei Ännerungen agedroen.\n\nDéi lescht Ännerung vun der Säit war vum [[User:$3|$3]] ([[User talk:$3|Diskussioun]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "De Resumé vun der Ännerung war: <em>$1</em>.",
        "revertpage": "Ännerunge vum [[Special:Contributions/$2|$2]] ([[User talk:$2|Diskussioun]]) zréckgesat op déi lescht Versioun vum [[User:$1|$1]]",
        "revertpage-nouser": "Zréckgesaten Ännerungen duerch e verstoppte Benotzer op déi lescht Versioun vum {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "D'Ännerunge vum $1 goufen zréckgesat op déi lescht Versioun vum $2.",
+       "rollback-success-notify": "Zréckgesat Ännerunge vum $1:\nzréckgeännert op déi lescht Versioun vum $2. [$3 Ännerunge weisen]",
        "sessionfailure-title": "Setzungsfeeler",
        "sessionfailure": "Et schéngt e Problem mat Ärer Loginseance ze ginn;\nDës Aktioun gouf aus Sécherheetsgrënn ofgebrach, fir ze verhënneren datt Är Seance piratéiert ka ginn.\nKlickt w.e.g. op \"Zréck\" a luet déi Säit vun där Dir komm sidd nei, a versicht et dann nach eng Kéier.",
        "changecontentmodel": "De Modell vum Inhalt vun enger Säit änneren",
        "changecontentmodel-submit": "Änneren",
        "changecontentmodel-success-title": "De Modell vum Inhalt gouf geännert",
        "changecontentmodel-success-text": "Den Typ vum Inhalt vu(n) [[:$1]] gouf geännert.",
+       "changecontentmodel-cannot-convert": "Den Inhalt vu(n) [[:$1]] kann net op den Typ $2 ëmgewandelt ginn.",
+       "changecontentmodel-emptymodels-title": "Keng Modeller fir Inhalter disponibel",
        "logentry-contentmodel-change-revertlink": "zrécksetzen",
        "logentry-contentmodel-change-revert": "zrécksetzen",
        "protectlogpage": "Protektiounslogbuch",
        "undeletedrevisions": "$1 {{PLURAL:$1|Versioun gouf|$1 Versioune goufe}} restauréiert",
        "undeletedrevisions-files": "{{PLURAL:$1|1 Versioun|$1 Versiounen}} a(n) {{PLURAL:$2|1 Fichier|$2 Fichiere}} goufe restauréiert",
        "undeletedfiles": "$1 {{PLURAL:$1|Fichier gouf|Fichiere goufe}} restauréiert",
-       "cannotundelete": "D'Restauratioun huet net funktionéiert:\n$1",
+       "cannotundelete": "E puer oder all d'Restauratiounen hunn net funktionéiert:\n$1",
        "undeletedpage": "'''$1''' gouf restauréiert.\n\nAm [[Special:Log/delete|Läsch-Logbuch]] fannt Dir déi geläscht a restauréiert Säiten.",
        "undelete-header": "Kuckt [[Special:Log/delete|Läschlescht]] fir rezent geläscht Säiten.",
        "undelete-search-title": "Geläscht Säite sichen",
        "namespace": "Nummraum:",
        "invert": "Auswiel ëmdréinen",
        "tooltip-invert": "Klickt dës Këscht fir Ännerungen am erausgesichten Nummraum (an den associéierten Nummraim wa se markéiert sinn) ze verstoppen.",
+       "tooltip-whatlinkshere-invert": "Markéiert dës Këscht fir Linke vu Säiten am erausgesichten Nummraum ze verstoppen.",
        "namespace_association": "Associéierten Nummraum",
        "tooltip-namespace_association": "Dës Këscht uklicke fir den Diskussiouns oder den associéierten Nummraum mat dem erausgesichten Nummraum matanzebezéien",
        "blanknamespace": "(Haapt)",
        "anoncontribs": "Kontributiounen",
        "contribsub2": "Fir {{GENDER:$3|den $1|d'$1|de Benotzer $1}} ($2)",
        "contributions-userdoesnotexist": "De Benotzerkont \"$1\" ass net registréiert.",
-       "nocontribs": "Et goufe keng Ännerunge fonnt, déi dëse Kritèren entspriechen.",
+       "nocontribs": "Et goufe keng Ännerunge fonnt, déi dëse Critèren entspriechen.",
        "uctop": "(aktuell)",
        "month": "Vum Mount (a virdrun):",
        "year": "Vum Joer (a virdrun):",
        "sp-contributions-newbies-sub": "Fir déi Nei",
        "sp-contributions-newbies-title": "Kontributioune vun neie Benotzer",
        "sp-contributions-blocklog": "Spärlescht",
-       "sp-contributions-suppresslog": "geläscht Benotzerkontributiounen",
-       "sp-contributions-deleted": "geläscht Kontributiounen",
+       "sp-contributions-suppresslog": "geläscht {{GENDER:$1|Benotzerkontributiounen}}",
+       "sp-contributions-deleted": "geläscht {{GENDER:$1|Benotzerkontributiounen}}",
        "sp-contributions-uploads": "Eropgeluede Fichieren",
        "sp-contributions-logs": "Logbicher",
        "sp-contributions-talk": "diskutéieren",
        "lockdbsuccesstext": "D'{{SITENAME}}-Datebank gouf gespaart. <br />\nDenkt drun [[Special:UnlockDB|d'Spär erëm ewechzehuele]] soubaal d'Maintenance-Aarbechte fäerdeg sinn.",
        "unlockdbsuccesstext": "D'Spär vun der Datebank ass opgehuewen.",
        "lockfilenotwritable": "De Fichier mat de Späre vun der Datebank kann net geännert ginn.\nFir d'Datebank ze spären oder fir d'Spär opzehiewe muss dëse Fichier vum Webserver geännert kënne ginn.",
+       "databaselocked": "D'Datebank ass scho gespaart.",
        "databasenotlocked": "D'Datebank ass net gespaart.",
        "lockedbyandtime": "(vum $1 de(n) $2 ëm $3 Auer)",
        "move-page": "Réckel $1",
        "tooltip-pt-preferences": "{{GENDER:|Är}} Astellungen",
        "tooltip-pt-watchlist": "Lëscht vu Säiten, bei deenen Dir op Ännerungen oppasst",
        "tooltip-pt-mycontris": "Lëscht vun {{GENDER:|Äre}} Kontributiounen",
+       "tooltip-pt-anoncontribs": "Eng Lëscht vun Ännerungen déi vun dëser IP-Adress aus gemaach goufen",
        "tooltip-pt-login": "Sech umelle gëtt gäre gesinn, Dir musst et awer net maachen.",
        "tooltip-pt-logout": "Ofmellen",
        "tooltip-pt-createaccount": "Et gëtt Iech geroden e Benotzerkont unzeleeën an Iech anzeloggen; dat ass awer net obligatoresch",
        "pageinfo-article-id": "ID (Nummer) vun der Säit",
        "pageinfo-language": "Sprooch vum Inhalt vun der Säit",
        "pageinfo-content-model": "Modell vun enger Säit mat Inhalt",
+       "pageinfo-content-model-change": "änneren",
        "pageinfo-robot-policy": "Indexéierung duerch Botten",
        "pageinfo-robot-index": "Erlaabt",
        "pageinfo-robot-noindex": "Net erlaabt",
        "confirmemail_body_set": "Iergendeen, wahrscheinlech Dir selwer, vun der IP-Adress $1,\nhuet d'E-Mail-Adress vum Benotzerkont \"$2\" op dës Adress op {{SITENAME}} geännert.\n\nFir ze confirméieren datt dëse Benotzerkont Iech wierklech gehéiert a fir d'E-Mailfonctiounen op {{SITENAME}} ze reaktivéieren, maacht dës Link an Ärem Browser op:\n\n$3\n\nWann de Benotzerkont Iech *net* gehéiert, da klickt op dëse Link fir d'Confirmatioun vun der E-Mail-Adress auszeschalten:\n\n$5\n\nDëse Confirmatiounscode leeft den $4 of.",
        "confirmemail_invalidated": "Confirmatioun vun der E-Mail-Adress annulléiert",
        "invalidateemail": "Annulléier d'E-Mailconfirmation",
+       "notificationemail_subject_changed": "D'E-Mail-Adress déi op {{SITENAME}} enregistréiert war gouf geännert",
        "notificationemail_subject_removed": "D'E-Mail-Adress déi op {{SITENAME}} enregistréiert war gouf ewechgeholl",
        "scarytranscludedisabled": "[Interwiki-Abannung ass ausgeschalt]",
        "scarytranscludefailed": "[D'Siche no der Schabloun fir $1 huet net funktionéiert]",
        "specialpages-group-changes": "Rezent Ännerungen a Lëschten",
        "specialpages-group-media": "Medie-Rapporten an eropgeluede Fichieren",
        "specialpages-group-users": "Benotzer a Rechter",
-       "specialpages-group-highuse": "Dacks benotzte Säiten",
+       "specialpages-group-highuse": "Dacks benotzt Säiten",
        "specialpages-group-pages": "Lëschte vu Säiten",
        "specialpages-group-pagetools": "Handwierksgeschir fir Säiten",
        "specialpages-group-wiki": "Daten an Handwierksgeschir",
        "tags-edit-existing-tags-none": "<em>Keng</em>",
        "tags-edit-new-tags": "Nei Markéierungen (tags):",
        "tags-edit-add": "Dës Markéierungen (tags) dobäisetzen:",
+       "tags-edit-remove": "Dës Markéierungen (tags) ewechhuelen:",
        "tags-edit-remove-all-tags": "(all Markéierungen ewechhuelen)",
        "tags-edit-chosen-no-results": "Keng Markéierunge fonnt déi passen",
        "tags-edit-reason": "Grond:",
        "htmlform-cloner-delete": "Ewechhuelen",
        "htmlform-cloner-required": "Mindestens ee Wäert ass obligatoresch.",
        "htmlform-title-badnamespace": "[[:$1]] ass net am Nummraum \"{{ns:$2}}\".",
+       "htmlform-title-not-creatable": "\"$1\" ass kee Säitentitel deen ugeluecht ka ginn",
        "htmlform-title-not-exists": "$1 gëtt et net.",
        "htmlform-user-not-exists": "<strong>$1</strong> gëtt et net.",
        "htmlform-user-not-valid": "<strong>$1</strong> ass kee valabele Benotzernumm.",
        "api-error-unknownerror": "Onbekannte Feeler: \"$1\".",
        "api-error-uploaddisabled": "D'Eroplueden ass op dëser Wiki ausgeschalt.",
        "api-error-verification-error": "Dëse Fichier kéint korrupt sinn, oder en huet eng falsch Erweiderung.",
+       "api-error-was-deleted": "E Fichier mat dësem Numm gouf virdrun eropgelueden an duerno geläscht.",
        "duration-seconds": "$1 {{PLURAL:$1|Sekonn|Sekonnen}}",
        "duration-minutes": "$1 {{PLURAL:$1|Minutt|Minutten}}",
        "duration-hours": "$1 {{PLURAL:$1|Stonn|Stonnen}}",
        "mw-widgets-dateinput-placeholder-month": "JJJJ-MM",
        "mw-widgets-titleinput-description-new-page": "Säit gëtt et nach net",
        "mw-widgets-titleinput-description-redirect": "viruleeden op $1",
-       "api-error-blacklisted": "Sicht w.e.g. en aneren Titel, dee méi iwwer de Sujet ausseet.",
        "sessionprovider-generic": "$1-Sessiounen",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "cookie-baséiert Sessiounen",
+       "sessionprovider-nocookies": "Cookië sinn eventuell desaktivéiert. Vergewëssert Iech datt Dir d'Cookien aktivéiert hutt a probéiert nach eng Kéier.",
        "randomrootpage": "Zoufalls-Stammsäit",
        "log-action-filter-block": "Typ vun der Spär:",
+       "log-action-filter-delete": "Läschtyp:",
+       "log-action-filter-import": "Importtyp:",
+       "log-action-filter-move": "Réckeltyp:",
        "log-action-filter-protect": "Typ vu Spär",
+       "log-action-filter-upload": "Eropluedtyp:",
        "log-action-filter-all": "All",
        "log-action-filter-block-block": "Spären",
        "log-action-filter-block-reblock": "Ännere vun enger Spär",
        "log-action-filter-block-unblock": "Spär ophiewen",
        "log-action-filter-delete-delete": "Säite läschen",
+       "log-action-filter-delete-revision": "Läsche vun enger Versioun",
        "log-action-filter-import-interwiki": "Transwiki-Import",
+       "log-action-filter-import-upload": "Import duerch Eropluede vun engem XML",
        "log-action-filter-move-move_redir": "Réckele mat Iwwerschreiwe vu Viruleedungen",
+       "log-action-filter-newusers-create": "Ugeluecht vun engem anonyme Benotzer",
+       "log-action-filter-newusers-create2": "Ugeluecht vun engem registréierte Benotzer",
+       "log-action-filter-newusers-autocreate": "Automatesch ugeluecht",
        "log-action-filter-patrol-patrol": "Manuell Kontroll",
        "log-action-filter-patrol-autopatrol": "Automatesch Kontroll",
        "log-action-filter-protect-protect": "Spär",
        "log-action-filter-protect-modify": "Spär-pÄnnerung",
+       "log-action-filter-protect-unprotect": "Spär ophiewen",
        "log-action-filter-protect-move_prot": "Geréckelt Spär",
        "log-action-filter-rights-rights": "Manuell Ännerung",
        "log-action-filter-rights-autopromote": "Automatesch Ännerung",
        "log-action-filter-upload-upload": "Neien Upload",
        "log-action-filter-upload-overwrite": "Nees eroplueden",
+       "authmanager-create-disabled": "D'Opmaache vu Benotzerkonten ass gespaart.",
+       "authmanager-create-from-login": "Fir Äre Benotzerkont unzeleeën fëllt w.e.g. d'Felder hei drënner aus.",
        "authmanager-authplugin-setpass-failed-title": "Änner vum Passwuert huet net funktionéiert",
+       "authmanager-authplugin-setpass-bad-domain": "Net valabelen Domain.",
        "authmanager-userdoesnotexist": "De Benotzerkont \"$1\" ass net registréiert.",
        "authmanager-retype-help": "Passwuert nach eng Kéier fir ze konfirméieren",
        "authmanager-email-label": "E-Mail",
        "authmanager-provider-temporarypassword": "Temporäert Passwuert:",
        "authprovider-resetpass-skip-label": "Iwwersprangen",
        "authprovider-resetpass-skip-help": "D'Zrécksetze vum Passwuert iwwersprangen",
+       "authform-notoken": "Toke feelt",
+       "authform-wrongtoken": "Falschen Token",
        "specialpage-securitylevel-not-allowed-title": "Net erlaabt",
        "specialpage-securitylevel-not-allowed": "Leider däerft Dir dës Säit net benotze well Är Identitéit net konnt iwwerpréift ginn.",
        "cannotauth-not-allowed-title": "Erlaabnes refuséiert",
        "credentialsform-account": "Numm vum Kont:",
        "cannotlink-no-provider-title": "Et gëtt keng Benotzerkonte fir ze verlinken",
        "linkaccounts": "Benotzerkonte verbannen",
-       "linkaccounts-submit": "Benotzerkonte verbannen"
+       "linkaccounts-submit": "Benotzerkonte verbannen",
+       "userjsispublic": "DEnkt drun: Op JavaScript-Ënnersäite solle keng vertraulech Informatioune stoe well se vun anere Benotzer kënne gesi ginn."
 }
index 5c22e14..779c36c 100644 (file)
        "tog-hidepatrolled": "Gemarkeerde wieziginge verberge in recente wieziginge",
        "tog-newpageshidepatrolled": "Gemarkeerde pagina's verberge in de lies mit nuuj pagina's",
        "tog-extendwatchlist": "Oetgebreide volglies gebroeke óm alle verangeringe te zeen en neet allein de lèste",
-       "tog-usenewrc": "Tuun verangeringe per pagina in recènte verangeringe en volglies (Javascript nudig)",
+       "tog-usenewrc": "Tuun verangeringe per pagina in recènte verangeringe en volglies",
        "tog-numberheadings": "Köpkes automatisch nummere",
-       "tog-showtoolbar": "Laot edit toolbar zeen",
-       "tog-editondblclick": "Bewirk pagina's bie 'ne dobbelklik (JavaScript)",
-       "tog-editsectiononrightclick": "Secties bewirke mit 'ne rechtermoesklik op sectietitels (JavaScript nudig)",
+       "tog-showtoolbar": "Laot bewirkingwerkbalk zeen",
+       "tog-editondblclick": "Bewirk pagina's bie 'ne dobbelklik",
+       "tog-editsectiononrightclick": "Secties bewirke mit 'ne rechtermoesklik op sectietitels",
        "tog-watchcreations": "Volg autematis pagina's die ich aanmaak en bestenj die ich upload",
        "tog-watchdefault": "Voog pagina's em bestenj die se bewirks toe aan dien volglies",
        "tog-watchmoves": "Volg autematis pagina's en bestenj die ich verplaats",
        "tog-watchdeletion": "Volg autematis pagina's en bestenj die ich ewegsjaf",
+       "tog-watchuploads": "Nuuj besjtenj die ich upload toevoge aan mienn volglies",
+       "tog-watchrollback": "Pagina's boe-in ich get höb óngedaon gemaak autematis volge",
        "tog-minordefault": "Markeer sjtanderd alle bewirkinge es klein",
        "tog-previewontop": "Veurvertuin baove bewèrkingsveld tuine",
        "tog-previewonfirst": "Preview laote zien bie de ierste bewirking",
        "tog-shownumberswatching": "'t Aantal gebroekers tuine die dees pagina volg",
        "tog-oldsig": "Bestaonde ongerteikening:",
        "tog-fancysig": "Es wikiteks behanjele (zonder autematische verwiezing)",
-       "tog-uselivepreview": "\"live veurbesjouwing\" gebroeke (vereis JavaScript - experimenteel)",
+       "tog-uselivepreview": "\"live veurbesjouwing\" gebroeke",
        "tog-forceeditsummary": "'n Melding gaeve bie 'n laeg samevatting",
        "tog-watchlisthideown": "Eige bewirkinge verberge op mien volglies",
        "tog-watchlisthidebots": "Botbewirkinge op mien volglies verberge",
        "tog-watchlisthideminor": "Klein bewirkinge op mien volglies verberge",
        "tog-watchlisthideliu": "Bewirkinge van aangemelde gebroekers op mien volglies versjtaeke",
+       "tog-watchlistreloadautomatically": "Herlaaj de volglies autematis es 'n filter is verangerd (JavaScript vereis)",
        "tog-watchlisthideanons": "Bewirkinge van anonieme gebroekers op mien volglies versjtaeke",
        "tog-watchlisthidepatrolled": "Gemarkeerde wieziginge op mien volglies verberge",
+       "tog-watchlisthidecategorization": "Verberg categorisatie  van  pagina's",
        "tog-ccmeonemails": "'n Kopie nao mich versjikke van de e-mail dae ich nao anger gebroekers sjik",
        "tog-diffonly": "Pagina-inhaud zónger verangeringe neet tuine",
        "tog-showhiddencats": "Verbórge categorië tuine",
        "tog-norollbackdiff": "Wieziginge eweglaote nao trökdrieje",
-       "tog-useeditwarning": "Waorssjoew mich es ich 'n bewerkdje pagina die nag neet is opgeslage wil verlaote",
+       "tog-useeditwarning": "Waarsjoew mich es ich 'n bewerkde pagina die nog neet is opgeslage wil verlaote",
+       "tog-prefershttps": "Ummer 'n beveiligde verbinding gebroeke es ich ingelog bin",
        "underline-always": "Altied",
        "underline-never": "Noets",
        "underline-default": "Sjtanderd van de browser",
        "oct": "okt",
        "nov": "nov",
        "dec": "dec",
+       "january-date": "$1 jannewarie",
+       "february-date": "$1 fibberwarie",
+       "march-date": "$1 miert",
+       "april-date": "$1 april",
+       "may-date": "$1 mei",
+       "june-date": "$1 juni",
+       "july-date": "$1 juli",
+       "august-date": "$1 augustus",
+       "september-date": "$1 september",
+       "october-date": "$1 oktober",
+       "november-date": "$1 november",
+       "december-date": "$1 december",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categorie|Categorieë}}",
        "category_header": "Artikele in categorie \"$1\"",
        "subcategories": "Subcategorieë",
        "newwindow": "(in nuuj venster)",
        "cancel": "Aafbraeke",
        "moredotdotdot": "Miè...",
+       "morenotlisted": "Deze lies is neet compleet.",
        "mypage": "Mien gebroekerspagina",
        "mytalk": "Euverlèk",
        "anontalk": "Euverlèk veur dit IP adres",
        "actions": "Hanjeling",
        "namespaces": "Naamruumdes",
        "variants": "Anger vorme",
+       "navigation-heading": "Navigatiemenu",
        "errorpagetitle": "Fout",
        "returnto": "Truuk nao $1.",
        "tagline": "Van {{SITENAME}}",
        "permalink": "Permanente link",
        "print": "Aafdrukke",
        "view": "Bekieke",
-       "edit": "Bewèrk",
+       "view-foreign": "Betrach op $1",
+       "edit": "Bewirk",
+       "edit-local": "Lokaal besjrieving bewirke",
        "create": "Aanmake",
-       "editthispage": "Pagina bewirke",
+       "create-local": "Lokaal besjrieving toeveuge",
+       "editthispage": "Dees pagina bewirke",
        "create-this-page": "Dees pagina aanmake",
        "delete": "Wisse",
        "deletethispage": "Wisse",
+       "undeletethispage": "Dees pagina trökzètte",
        "undelete_short": "$1 {{PLURAL:$1|bewirking|bewirkinge}} trökzètte",
        "viewdeleted_short": "{{PLURAL:$1|ein eweggesjafde versie|$1 eweggesjafde versies}} bekieke",
        "protect": "Besjirm",
        "otherlanguages": "Anger tale",
        "redirectedfrom": "(Doorverweze van $1)",
        "redirectpagesub": "Doorverwiespagina",
+       "redirectto": "Doorverwieze nao:",
        "lastmodifiedat": "Dees pagina is 't lèts verangerd op $2, $1.",
        "viewcount": "Dees pagina is {{PLURAL:$1|1 kier|$1 kier}} bekeke.",
        "protectedpage": "Beveiligde pagina",
        "pool-errorunknown": "Ónbekènde fout",
        "aboutsite": "Euver {{SITENAME}}",
        "aboutpage": "Project:Info",
-       "copyright": "De inhawd is besjikbaar ónger de $1.",
+       "copyright": "De inhawd is besjikbaar ónger de $1 behauve is angers aangegeve.",
        "copyrightpage": "{{ns:project}}:Auteursrechte",
        "currentevents": "In 't nuujs",
        "currentevents-url": "Project:In 't nuujs",
        "disclaimers": "Aafwiezinge aansjprakelikheid",
        "disclaimerpage": "Project:Algemein aafwiezing aansjprakelikheid",
        "edithelp": "Hulp bie bewirke",
+       "helppage-top-gethelp": "Hölp",
        "mainpage": "Veurblaad",
        "mainpage-description": "Veurblaad",
        "policy-url": "Project:Beleid",
        "hidetoc": "versjtaek",
        "collapsible-collapse": "Inklappe",
        "collapsible-expand": "Oetklappe",
+       "confirmable-confirm": "Bis {{GENDER:$1|diech}} zeker?",
+       "confirmable-yes": "Jao",
+       "confirmable-no": "Nein",
        "thisisdeleted": "$1 tuine of trökzètte?",
        "viewdeleted": "$1 tuine?",
        "restorelink": "{{PLURAL:$1|ein eweggesjafde versie|$1 eweggesjafde versies}}",
        "nstab-template": "Sjabloon",
        "nstab-help": "Hulppagina",
        "nstab-category": "Categorie",
+       "mainpage-nstab": "Veurblaad",
        "nosuchaction": "Gevraogde hanjeling besjteit neet",
        "nosuchactiontext": "De opdrach in de URL is ongeldig.\nMäögelik höbs te 'n typefout gemaak in de URL, of 'n verkierde verwiezing gevolg.\n't Kan ouch wieze op 'n fout in de software van {{SITENAME}}.",
        "nosuchspecialpage": "D'r besjteit gein speciaal pagina mit deze naam",
        "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-emailelement": "Gebroekersnaam: \n$1\n\nTiedelik wachwaord: \n$2",
        "passwordreset-emailsentemail": "d'r Is per mail 'n herinnering versjik.",
-       "passwordreset-emailsent-capture": "d'r Is 'ne herinneringse-mail versjik. Deze weurt hieónger getuind.",
-       "passwordreset-emailerror-capture": "d'r Is 'ne herinneringse-mail aangemaak. Deze weurt hieónger getuind. 't Verzènje nao de gebroeker is mislök óm de volgende raeje: $1",
        "changeemail": "Veranger dien e-mailadres",
        "changeemail-header": "Veranger 't e-mailadres van miene gebroekersnaam",
        "changeemail-no-info": "Doe moos aangemeld zien ierdets doe dees pagina gebroeke kens.",
        "minoredit": "Dit is 'n klein verangering",
        "watchthis": "Volg dees pagina",
        "savearticle": "Pagina opsjlaon",
+       "publishpage": "Pagina publicere",
+       "publishchanges": "Verangeringe publicere",
        "preview": "Naokieke",
        "showpreview": "Betrach dees bewirking",
        "showdiff": "Toen verangeringe",
        "token_suffix_mismatch": "'''Dien bewerking is geweigerd omdat dien client de laesteikes in 't bewerkingstoken onjuist haet behandeld. De bewerking is geweigerd om verminking van de paginateks te veurkomme. Dit gebeurt soms es d'r een webgebaseerde proxydienst wurt gebroek die foute bevat.'''",
        "edit_form_incomplete": "'''Sommige ongerdeile van 't bewerkingsformuleer höbbe de server neet bereik. Controleer of dien bewerkinge intak zien en perbeer 't obbenuits.'''",
        "editing": "Bewirkingspagina: $1",
-       "creating": "$1 aanmakendj",
+       "creating": "Aanmake van $1",
        "editingsection": "Bewirke van sectie van $1",
        "editingcomment": "Bewirke $1 (commentair)",
        "editconflict": "Bewirkingsconflik: $1",
        "undo-failure": "De verangering kòs neet ongedaon gemaak waere waeges angere striedige verangeringe.",
        "undo-norev": "De bewerking kon neet ongedaan gemaak waere, omdat die neet besteet of is verwijderd.",
        "undo-summary": "Versie $1 van [[Special:Contributions/$2|$2]] ([[User talk:$2|euverlèk]]) óngedaon gemaak.",
-       "cantcreateaccounttitle": "Aanmake gebroeker misluk.",
        "cantcreateaccount-text": "'t Aanmake van gebroekers van dit IP-adres ('''$1''') is geblokkeerd door [[User:$3|$3]].\n\nDe door $3 opgegaeve reje is ''$2''",
        "viewpagelogs": "Logbeuk veur dees pagina tuine",
        "nohistory": "Dees pagina is nog neet bewirk.",
        "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",
-       "api-error-blacklisted": "Kees 'nen angere, besjrievendje naam."
+       "mw-widgets-dateinput-placeholder-month": "JJJJ-MM"
 }
index de75f5e..79cea0e 100644 (file)
        "tog-watchdefault": "Azonzi e paggine e i files che modiffico a-i mæ sotta oservaçion",
        "tog-watchmoves": "Azonzi e paggine e i file che mescio a-i mæ sotta oservaçion",
        "tog-watchdeletion": "Azonzi e paggine e i files che scancello a-i mæ sotta oservaçion",
+       "tog-watchuploads": "Azonzi i noeuvi file che metto in osservaçion",
        "tog-watchrollback": "Azonzi a-i sotta osservassion e paggine dovve ho fæto un rollback",
        "tog-minordefault": "Indica de longo comme menô e modiffiche",
        "tog-previewontop": "Veddi l'anteprimma de d'äto a-o spaçio pe cangiâ",
        "tog-previewonfirst": "Veddi l'anteprimma a-o primmo cangiamento",
        "tog-enotifwatchlistpages": "Famme savéi via e-mail quande 'na paggina o in file inti mæ osservæ a ven cangiâ.",
        "tog-enotifusertalkpages": "Màndime un'e-mail se gh'é de modìffiche inta mæ pagina de discuscion.",
-       "tog-enotifminoredits": "Inviami una email pe e modifiche menoî ascì de pagine e di file",
+       "tog-enotifminoredits": "Mandime una email ascì pe e modifiche menoî de pagine e di file",
        "tog-enotifrevealaddr": "Mostra o mæ addresso inte e-mail de notiffica",
        "tog-shownumberswatching": "Mostra o numero di utenti che tegnan d'oeuggio sta pagina",
        "tog-oldsig": "Firma attuale:",
        "underline-always": "Sempre",
        "underline-never": "Mâi",
        "underline-default": "Impostassioin predefinie do navegatô o da skin",
-       "editfont-style": "Stile do carattere de l'aera de modiffica",
+       "editfont-style": "Stile do carattere de l'area de modiffica",
        "editfont-default": "Predefinio do navegatô",
        "editfont-monospace": "Carattere a larghessa fissa",
        "editfont-sansserif": "Carattere sans-serif",
        "editfont-serif": "Carattere serif",
        "sunday": "Domenega",
-       "monday": "Lunedì",
-       "tuesday": "Martedì",
+       "monday": "Lunesdì",
+       "tuesday": "Matesdì",
        "wednesday": "Mäcordì",
        "thursday": "Zeuggia",
        "friday": "Venardì",
        "talkpage": "Paggina de discuscion",
        "talkpagelinktext": "Ciæti",
        "specialpage": "Pagina speçiâ",
-       "personaltools": "Strùmenti personâli",
+       "personaltools": "Strumenti personâli",
        "articlepage": "Veddi a voxe",
        "talk": "Discuscion",
        "views": "Vìxite",
        "viewcount": "'Sta paggina a l'è stæta vista {{PLURAL:$1|solo 'na vòtta|$1 vòtte}}.",
        "protectedpage": "Paggina protetta",
        "jumpto": "Vanni a:",
-       "jumptonavigation": "Navegaçión",
+       "jumptonavigation": "navegaçión",
        "jumptosearch": "çerca",
        "view-pool-error": "Scuza ma a-o momento i server sono stracarreghi.\nTroppi utenti çercan d' amiâ sta paggina.\nAspeta quarche menuto primma de çercâ torna d'accede a sta pagina.\n\n$1",
        "generic-pool-error": "Scuza ma a-o momento i server sono stracarreghi.\nTroppi utenti çercan d' amiâ sta risorsa.\nAspeta quarche menuto primma de çercâ torna d'accede a sta risorsa.",
        "databaseerror-query": "Query: $1",
        "databaseerror-function": "Fonsion: $1",
        "databaseerror-error": "Errô: $1",
+       "transaction-duration-limit-exceeded": "Pe evitâ un ato ritardo de replica, questa opiaçion a l'è stæta interotta perché a duata do tempo de scrittua ($1) a l'ha superou o limmite de $2 {{PLURAL:$2|segondo|segondi}}.\nSe t'ê aproeuvo a cangiâ tante cose inte'na votta sola, proeuva invece a cangiâ poche cose in tante votte.",
        "laggedslavemode": "'''Attension:''' a paggina a porriæ no riportâ i aggiornamenti ciù reçenti.",
        "readonly": "Database bloccòu",
        "enterlockreason": "Scrivi o motivo do blocco, e 'na stimma de quande o saiâ rimosso",
        "mypreferencesprotected": "No ti g'hæ o permisso pe modificâ e teu preferense.",
        "ns-specialprotected": "No se pœu modificâ e paggine speciali",
        "titleprotected": "A creaçion de 'na paggina con sto tittolo a l'è stæta bloccâ da [[User:$1|$1]].\nA raxon a l'è: <em>$2</em>.",
-       "filereadonlyerror": "N'ho posciuo modificâ o file \"$1\" perché o repository de file \"$2\" o l'è in modalitæ de sola lettua.\n\nL'amministratô ch'o l'ha bloccòu o l'ha fornio sta motivaçion: \"$3\".",
+       "filereadonlyerror": "N'ho posciuo modificâ o file \"$1\" perché o repository de file \"$2\" o l'è in modalitæ de sola lettua.\n\nL'amministratô de scistema ch'o l'ha bloccòu o l'ha fornio sta motivaçion: \"$3\".",
        "invalidtitle-knownnamespace": "Tittolo non vallido con namespace \"$2\" e testo \"$3\"",
        "invalidtitle-unknownnamespace": "Tittolo non vallido con namespace sconosciuo \"$1\" e testo \"$2\"",
        "exception-nologin": "No t'ê introu",
        "virus-scanfailed": "scansion fallia (codice $1)",
        "virus-unknownscanner": "antivirus sconosciuo:",
        "logouttext": "'''Sciortîa effettuâ.'''\n\nDanni a mente che gh'è de paggine che porrieivan continuâ a pai comme se a sciortîa a no foise avegnua, pe scin che no ti nettezzi a cache do to navegatô.",
+       "cannotlogoutnow-title": "Aoa no se poeu sciortî",
+       "cannotlogoutnow-text": "Quande s'adoeuvia $1 no se poeu sciortî.",
        "welcomeuser": "Benvegnuo, $1!",
        "welcomecreation-msg": "L'utensa a l'è stæta creâ correttamente.\nSe ti veu ti peu personalizzâ e [[Special:Preferences|preferençe de {{SITENAME}}]].",
        "yourname": "Nomme",
        "yourpasswordagain": "Riscrivi a pòula segrétta:",
        "createacct-yourpasswordagain": "Conferma a password",
        "createacct-yourpasswordagain-ph": "Conferma a password un'atra votta",
-       "remembermypassword": "Aregòrda a mæ login in sto navegatô (pe in mascimo de $1 {{PLURAL:$1|giórno|giórni}})",
        "userlogin-remembermypassword": "Mantegnime collegou",
        "userlogin-signwithsecure": "Adoeuvia una conescion segua",
+       "cannotloginnow-title": "Aoa no se poeu intrâ",
+       "cannotloginnow-text": "Quande s'adoeuvia $1 no se poeu intrâ.",
        "yourdomainname": "Indirisso do scito:",
        "password-change-forbidden": "No ti peu cangiâ poula segretta in questa wiki.",
-       "externaldberror": "Gh'è stæto un aro co-ol server de autenticaçion esterno, oppû no ti g'hæ i aotorizzaçioin pe aggiornâ o to accesso esterno.",
+       "externaldberror": "Gh'è stæto un aro co-o server de aotenticaçion esterno, oppû no ti g'hæ i aotorizzaçioin pe aggiornâ o to accesso esterno.",
        "login": "Intra",
+       "login-security": "Veifica a to identitæ",
        "nav-login-createaccount": "Intra / Registrate",
        "userlogin": "Intra / Registrite",
        "userloginnocreate": "Intra",
        "userlogin-resetpassword-link": "T'hæ miga ascordou a teu poula segretta?",
        "userlogin-helplink2": "Agiutto pe intrâ",
        "userlogin-loggedin": "Ti t'ê zà connesso comme {{GENDER:$1|$1}}.\nUsa o formulaio sottostante pe accede comme 'n atro utente.",
+       "userlogin-reauth": "Ti g'hæ da intrâ 'n'atra votta pe veificâ che ti t'ê {{GENDER:$1|$1}}.",
        "userlogin-createanother": "Crea 'n atra utensa",
        "createacct-emailrequired": "Addresso e-mail:",
        "createacct-emailoptional": "Adresso email (opsionale)",
        "createacct-email-ph": "Scrivi o teu adresso email",
        "createacct-another-email-ph": "Scrivi o teu adresso email",
        "createaccountmail": "Doeuvia una password temporanea abrettio e mandila a l'adresso de posta elettronica speçificou",
+       "createaccountmail-help": "O poeu ese doeuviou pe creâ un'utensa pe 'n'atra person-a sensa doveine conosce a password.",
        "createacct-realname": "Nomme reale (opçionâ)",
        "createaccountreason": "Raxon:",
        "createacct-reason": "Raxon",
        "createacct-reason-ph": "Perché t'ê apreuvo a creâ un'atra utensa",
+       "createacct-reason-help": "Messaggio vixualizou into registro da creaçion de l'utença",
        "createacct-submit": "Crea a to utensa",
        "createacct-another-submit": "Crea utensa",
+       "createacct-continue-submit": "Continnoa a creaçion de l'utença",
+       "createacct-another-continue-submit": "Continnoa a creaçion de l'utença",
        "createacct-benefit-heading": "{{SITENAME}} o l'è realizzou da de gente comme ti.",
        "createacct-benefit-body1": "{{PLURAL:$1|modiffica|modiffiche}}",
        "createacct-benefit-body2": "{{PLURAL:$1|paggina|paggine}}",
        "nocookiesnew": "L'utensa a l'è stæta creâ, ma ti no t'ê intròu. {{SITENAME}} o deuvia i cookie pe lasciâ intrâ i utenti e ti ti ghe l'hæ disattivæ.\nRipreuva a intrâ co-o to nomme utente e poula segretta apen-a creæ doppo avei attivòu i cookie.",
        "nocookieslogin": "Pe intrâ in {{SITENAME}} bezeugna aveighe i cookie attivæ. Ti ti ghe l'hæ disattivæ. Pe piaxei attîvili e preuva torna a intrâ.",
        "nocookiesfornew": "L'utensa a no l'è stæta creâ, perché n'emmo posciuo confermâ a so sorgente.\nAsseguite d'avei attivòu i cookie, recarrega sta paggina e ripreuva.",
+       "createacct-loginerror": "L'utença a l'è stæta creaa correttamente, ma no l'è stæto poscibbile fate accede in moddo aotomattico. Procedi co l'[[Special:UserLogin|accesso manoâ]].",
        "noname": "O nomme d'ûtente o l'è sballiòu.",
        "loginsuccesstitle": "Accesso effettuòu",
        "loginsuccess": "'''O collegamento a-o server de {{SITENAME}} co-o nomme d'ûtente \"$1\" o l'è attivo.'''",
        "nouserspecified": "Ti g'hæ da specificâ un nomme utente.",
        "login-userblocked": "St'utente o l'è bloccou. Accesso negou.",
        "wrongpassword": "Ti gh'æ scrîo 'na paròlla d'ordine sbaliâ. Tenta torna.",
-       "wrongpasswordempty": "No ti g'hæ scrîo nisciûnn-a paròlla d'ordine. Tenta torna.",
+       "wrongpasswordempty": "No ti g'hæ scrîto nisciûnn-a paròlla d'ordine. Ritenta.",
        "passwordtooshort": "E password devan aveighe aomanco {{PLURAL:$1|1 carattere|$1 caratteri}}.",
        "passwordtoolong": "A poula segretta a no peu contegnî ciù de {{PLURAL:$1|1 carattere|$1 caratteri}}.",
        "passwordtoopopular": "No se peu deuviâ de paole segrette troppo ordenaie. Pe piaxei çernitene un-a ciu particolâ.",
        "passwordremindertext": "Quarchedûn (probabilmente ti, con addresso IP $1) o l'ha domandòu l'invîo de 'na nêuva poula segretta pe l'accesso a {{SITENAME}} ($4).\nA poula segretta temporannia pe l'utente \"$2\" a l'è stæta impostâ a \"$3\".\nSe l'è questo che ti voeivi, intra òua e cangia subbito a poula segretta. A poula segretta temporannia a descazziâ doppo {{PLURAL:$5|un giorno|$5 giorni}}.\n\nSe no t'ê stæto ti a fâ 'sta recesta, oppûre se ti t'ê aregordòu da teu poula segretta e no ti veu ciu cangiâla , ti peu ignorâ sto messaggio e andâ avanti deuviando a vegia poula segretta.",
        "noemail": "No gh'è nisciûn indirisso e-mail registròu pe l'ûtente \"$1\".",
        "noemailcreate": "Ti devi dâ un addresso e-mail vallido.",
-       "passwordsent": "Ûnn-a nêuva paròlla d'ordine a l'è stæta inviâa a l'indirisso e-mail registròu pe l'ûtente \"$1\".\nPe piaxei, fa 'n accesso appenn-a ti a ghe reçeivi.",
-       "blocked-mailpassword": "O teu addresso IP o l'è bloccòu, e pe sta raxon non se peu usâ a funsion de recuppero da pòula segretta, pe prevegnî di abuxi.",
+       "passwordsent": "Ûnn-a nêuva paròlla d'ordine a l'è stæta inviâ a l'indirisso e-mail registròu pe l'ûtente \"$1\".\nPe piaxei, fa 'n accesso appenn-a ti a riçeivi.",
+       "blocked-mailpassword": "O teu addresso IP o l'è bloccòu a-e modiffiche, pe prevegnî di abuxi non se peu doeuviâ a fonçion de recuppero da pòula segretta da queseto addresso IP.",
        "eauthentsent": "Un messaggio e-mail de conferma o l'è stæto inviòu a l'addresso indicòu.\nPe abilitâ l'invîo de messaggi e-mail pe quest'utensa, se deve seguî e instrussioin indicæ, pe confermâ che ti t'ê o legittimo propietâio de l'utensa.",
        "throttled-mailpassword": "Un'e-mail de reimpostassione da poula segretta a l'è zà stæta inviâ da meno de {{PLURAL:$1|1 oa|$1 oe}}.\nPe prevegnî di abuxi, a fonsion de reimpostassion da poula segretta a peu vese deuviâ solo che 'na votta ogni {{PLURAL:$1|oa|$1 oe}}.",
        "mailerror": "Errô inte l'invio do messaggio: $1",
        "createacct-another-realname-tip": "O nomme veo o l'è opçionâ.\nSe ti çerni de inseilo, o saiâ deuviòu pe attribuî a l'aotô a paternitæ di contengnui inviæ.",
        "pt-login": "Intra",
        "pt-login-button": "Intra",
+       "pt-login-continue-button": "Continoa l'accesso",
        "pt-createaccount": "Registrite",
        "pt-userlogout": "Sciorti",
        "php-mail-error-unknown": "Errô sconosciuo intaa funçion PHP mail()",
        "newpassword": "Neuva poula segretta",
        "retypenew": "Ripette a nêuva paròlla d'ordine:",
        "resetpass_submit": "Çerni a poula segretta e intra",
-       "changepassword-success": "O cangio de password o l'é anæto ben!",
+       "changepassword-success": "A to password a l'è stæta cangiâ!",
        "changepassword-throttled": "T'hæ çercòu de intrâ troppe votte tutt'assemme.\nPe piaxei aspeta $1 primma de provâ torna.",
+       "botpasswords": "Password bot",
+       "botpasswords-summary": "<em>Password bot</em> o consente l'accesso a un'utença tramite API sença doeuviâ e credençiæ d'accesso prinçipæ de l'utença. I driti utente disponibili quande s'è effettuou l'accesso co-ina password bot poeuan ese limitæ.\n\nSe no ti conosci o motivo pe-o quæ ti porriesci voeilo fâ, alloa foscia no ti doviesci fâlo. Nisciun doviæ mai domandâte de generâ un de questi e dapoeu dâlo a liatri.",
+       "botpasswords-disabled": "E password bot son disabilitæ.",
+       "botpasswords-no-central-id": "Pe doeuviâ una password bot, l'è necessaio accede a un'utença çentralizzâ.",
+       "botpasswords-existing": "Password bot in existença",
+       "botpasswords-createnew": "Crea una noeuva password bot",
+       "botpasswords-editexisting": "Modifica una password bot existente",
+       "botpasswords-label-appid": "Nomme bot:",
+       "botpasswords-label-create": "Crea",
+       "botpasswords-label-update": "Aggiorna",
+       "botpasswords-label-cancel": "Anulla",
+       "botpasswords-label-delete": "Scassa",
+       "botpasswords-label-resetpassword": "Reimposta a poula segretta",
+       "botpasswords-label-grants": "Assegnaçioin applicabile:",
+       "botpasswords-help-grants": "Ogni assegnaçion a dà accesso a-i driti utente elencæ che un'utença a g'ha zà. Amia a [[Special:ListGrants|tabella d'e assegnaçioin]] pe de ulteioî informaçioin.",
+       "botpasswords-label-restrictions": "Restriçioin d'utilizzo:",
+       "botpasswords-label-grants-column": "Assegnaçioin",
+       "botpasswords-bad-appid": "O nomme bot \"$1\" o no l'è vallido.",
+       "botpasswords-insert-failed": "Imposcibile azonze o nomme bot \"$1\". O l'è za stæto azonto?",
+       "botpasswords-update-failed": "Imposcibile aggiornâ o nomme bot \"$1\". O l'è stæto scassou?",
+       "botpasswords-created-title": "Password bot creâ",
+       "botpasswords-created-body": "A password pe-o bot de nomme \"$1\" de l'utente \"$2\" a l'è stæta creâ.",
+       "botpasswords-updated-title": "Password bot aggiornâ",
+       "botpasswords-updated-body": "A password pe-o bot de nomme \"$1\" de l'utente \"$2\" a l'è stæta aggiornâ.",
+       "botpasswords-deleted-title": "Password bot scassâ",
+       "botpasswords-deleted-body": "A password pe-o bot de nomme \"$1\" de l'utente \"$2\" a l'è stæta scassâ.",
+       "botpasswords-newpassword": "A noeuva password pe accede con <strong>$1</strong> a l'è <strong>$2</strong>. <em>Marchitelo pe rifeimento futuo.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider o no l'è disponibbile.",
+       "botpasswords-restriction-failed": "E restriçioin de password bot impediscian questo accesso.",
+       "botpasswords-invalid-name": "O nomme utente indicou o no conten o separatô pe-o password bot (\"$1\").",
+       "botpasswords-not-exist": "L'utente \"$1\" o no dispon-e de 'na password bot ciamâ \"$2\".",
        "resetpass_forbidden": "No l'é poscìbile cangiâ e paròlle segrétte",
+       "resetpass_forbidden-reason": "No l'é poscìbile cangiâ e paole segrette: $1",
        "resetpass-no-info": "Pe anâ direttamente a sta paggina, primma ti g'hæ da intrâ .",
        "resetpass-submit-loggedin": "Cangia a password",
        "resetpass-submit-cancel": "Anulla",
        "passwordreset-email": "Addresso e-mail:",
        "passwordreset-emailtitle": "Dettaggi account sciu {{SITENAME}}",
        "passwordreset-emailtext-ip": "Quarcun (probabilmente ti, con adresso IP $1) o l'ha domandòu l'invio de 'na neuva poula segretta per l'accesso a {{SITENAME}} ($4). {{PLURAL:$3|L'utente associòu|I utenti associæ}} a sto addresso e-mail son:\n\n$2\n\n{{PLURAL:$3|Questa poula segretta temporannia a descazziâ|Queste poule segrette temporannie descazzian}} doppo {{PLURAL:$5|un giorno|$5 giorni}}.\nTi doviesci accede e çerne una neuva poula segretta oua. \n\nSe no t'ê stæto ti a fâ a domanda, ò se ti t'hæ aregordòu a poula segretta originale e no ti veu ciù cangiâla, ti peu ignorâ sto messaggio e continuâ a deuviâ a teu vegia poula segretta.",
+       "passwordreset-emailtext-user": "L'utente $1 de {{SITENAME}} o l'ha domandou l'inandio de 'na noeuva password pe l'accesso a {{SITENAME}} ($4). {{PLURAL:$3|L'utente associou|I utenti associæ}} a questo indriçço email son:\n\n$2\n\n{{PLURAL:$3|Questa password temporannia a descazziâ|Queste password temporannie descazian}} doppo {{PLURAL:$5|un giorno|$5 giorni}}.\nTi doviesci accede e çerne una noeuva password aoa. \n\nSe no t'ê stæto ti a fâ a recesta, o se ti t'ê aregordou a password originâ e no ti voeu ciu cangiala, ti poeu ignorâ sto messaggio e continoâ a doeuviâ a to vegia password.",
        "passwordreset-emailelement": "Nomme utente: \n$1\n\nPoula segretta temporannia: \n$2",
-       "passwordreset-emailsentemail": "Se questo o l'è un addresso de posta elettronnica registròu pe-a teu utensa, alloa saiâ inviâ un'e-mail pe reimpostâ a poula segretta.",
-       "passwordreset-emailsent-capture": "L'è stæto inviòu un'e-mail de reimpostaçion da poula segretta, o contegnuo o l'è riportòu chì appreuvo.",
-       "passwordreset-emailerror-capture": "L'è stæto generòu un'e-mail de reimpostaçion da poula segretta, riportà chì appreuvo. L'invio {{GENDER:$2|a l'utente}} o no l'è ariêscîo: $1",
+       "passwordreset-emailsentemail": "Se questo addresso de posta elettronnica o l'è associou a-a teu utença, alloa saiâ inviou un'e-mail pe rempostâ a poula segretta.",
+       "passwordreset-emailsentusername": "Se gh'è un adreçço de posta elettronica associou con questo nomme utente, alloa saiâ inviou una email pe rempostâ a password.",
+       "passwordreset-emailsent-capture2": "L'email de rempostaçion da password {{PLURAL:$1|a l'è stæta inviâ|son stæte inviæ}}. {{PLURAL:$1|O nomme|L'elenco di nommi}} utente e password o l'è mostrou chì de sotta.",
+       "passwordreset-emailerror-capture2": "Invio de email {{GENDER:$2|a l'utente}} non ariescio: $1. {{PLURAL:$3|O nomme|L'elenco di nommi}} utente e password o l'è mostrou chì de sotta.",
+       "passwordreset-nocaller": "Un chi ciamma ti g'hæ da dâlo",
+       "passwordreset-nosuchcaller": "O ciamante o no l'existe: $1",
+       "passwordreset-ignored": "A reimpostaçion da password a no l'è stæta gestia. Foscia n'è stæto configuou nisciun provider ?",
+       "passwordreset-invalideamil": "Addresso e-mail non vallido",
+       "passwordreset-nodata": "No è stæto fornio ni un nomme utente ni un adreçço de posta elettronica",
        "changeemail": "Cangia o elimmina l'adresso e-mail",
        "changeemail-header": "Completa sto formulaio pe cangiâ o to adresso e-mail. Se ti veu rimeuve l'associaçion de quasesegge addresso e-mail da-a teu utensa, lascia io neuvo addresso e-mail veuo quande ti invii o formulaio.",
-       "changeemail-passwordrequired": "Saiâ necessaio insei a poula segretta pe confermâ a modiffica.",
        "changeemail-no-info": "Pe anâ direttamente a sta paggina, primma ti g'hæ da intrâ .",
        "changeemail-oldemail": "Addresso e-mail corrente:",
        "changeemail-newemail": "Noeuvo adresso e-mail",
        "minoredit": "Cangiamento minô (m)",
        "watchthis": "Metti sotta oservaçion",
        "savearticle": "Sarva a pàgina",
+       "savechanges": "Sarva e modiffiche",
+       "publishpage": "Pubbrica a paggina",
+       "publishchanges": "Pubbrica e modiffiche",
        "preview": "Anteprìmma",
        "showpreview": "Veddi l'anteprimma",
        "showdiff": "Veddi i cangiamenti",
        "subject-preview": "Anteprimma do soggetto:",
        "previewerrortext": "Gh'è stæto un errô mentre se çercava de mostrâ l'anteprimma.",
        "blockedtitle": "L'utente o l'é bloccòu",
-       "blockedtext": "''''O to nomme utente ò adresso IP o l'è stæto bloccòu.'''\n\nO blòcco o l'è stæto fæto da $1. A raxon dæta a l'è ''$2''.\n\n* Iniçio do blocco: $8\n* Fin do blocco: $6\n* Utente blocou: $7\n\nL'è poscibbile contattâ $1 o un âtro [[{{MediaWiki:Grouppage-sysop}}|amministratô]] pe discûtte inscio blòcco.\nNo ti poeu doeuviâ o comando \"Manda un'e-mail a st'ûtente\" se no ti g'hæ 'n adresso e-mail registròu inte to [[Special:Preferences|preferençe]] e se no t'ê stæto bloccòu ascì.\nO to adresso IP o l'è $3, e o to blòcco ID o l'è #$5.\nPe piaxei, pe domandâ informaçioin, speçifficali tutti doî.",
+       "blockedtext": "''''O to nomme utente ò adresso IP o l'è stæto bloccòu.'''\n\nO blòcco o l'è stæto fæto da $1. A raxon dæta a l'è ''$2''.\n\n* Prinçippio do blocco: $8\n* Fin do blocco: $6\n* Utente blocou: $7\n\nL'è poscibbile contattâ $1 ò un atro [[{{MediaWiki:Grouppage-sysop}}|amministratô]] pe discûtte inscio blòcco.\nNo ti poeu doeuviâ o comando \"Manda un'e-mail a st'utente\" se no ti g'hæ 'n adreçço e-mail registròu inte to [[Special:Preferences|preferençe]] e se no t'ê stæto bloccòu ascì.\nO to adreçço IP o l'è $3, e o to blòcco ID o l'è #$5.\nPe piaxei, pe domandâ de informaçioin, speçifficali tutti doî.",
        "autoblockedtext": "O teu addresso IP o l'è stæto bloccòu outomaticamente perché o l'ea za usòu da 'n âtro utente, bloccòu da $1.\nA raxon dæta a l'è stæta:\n\n:''$2''\n\n* Prinsippio do blòcco: $8\n* Fin do blòcco: $6\n\nTi peu contattâ $1 ò un âtro\n[[{{MediaWiki:Grouppage-sysop}}|amministratô]] pe discutte o blòcco.\n\nDanni a mente a che no ti pêu ûsâ o comando \"manda 'na e-mail a sto utente\" se non ti g'hæ 'n addresso de posta elettronega registròu in te têu [[Special:Preferences|preferense]] e se ti no t'ê stæto bloccòu ascì.\n\nO to adresso IP o l'è $3, e o to blòcco ID o l'è #$5. Pe piaxei, pe domandâ informaçioin, speçifficali tutti doî.",
        "blockednoreason": "nisciun-a motivaçion dæta",
-       "whitelistedittext": "Pe cangia sta pagina devvi $1.",
+       "whitelistedittext": "Pe modificâ e paggine l'è necessaio $1.",
        "confirmedittext": "Pe ese abilitæ a-a modiffica de paggine bezeugna confermâ o proppio addresso e-mail. Pe impostâ e confermâ l'adresso servîse de [[Special:Preferences|preferençe]].",
        "nosuchsectiontitle": "Imposcibbile trovâ a seçion",
        "nosuchsectiontext": "T'hæ çercòu de modificâ una seçion inexistente.\nA porriæ ese stæta mesciâ ò eliminâ mentre ti t'amiavi a paggina.",
        "loginreqtitle": "Besêugna registrâse primma de modificâ 'sta paggina.",
-       "loginreqlink": "intra",
+       "loginreqlink": "intrâ",
        "loginreqpagetext": "Pe amiâ di atre paggine gh'è da $1",
        "accmailtitle": "Pòula segretta spedïa",
        "accmailtext": "Una poula segretta generâ abrettio pe [[User talk:$1|$1]] a l'è stæta mandâ a $2.\n\nSta poula segretta a peu ese cangiâ inta paggina pe ''[[Special:ChangePassword|cangiâ a poula segretta]]'' subbito doppo l'accesso.",
        "newarticle": "(Nêuvo)",
        "newarticletext": "Sto colegaménto o corisponde a 'na pàgina ch'a no l'existe ancon.\n\nSe se vêu creâ a pàgina òua, se pêu comensâ a scrive into spàçio chì sotta.\n(amia e [$1 paggine d'agiûtto] pe ciû informaçioìn).\n\nSe t'ê intròu chì pe sballio,  sciacca '''Inderê''' into navegatô.",
-       "anontalkpagetext": "----\n''Sta chì a l'è a paggina de discuscion de un utente anonnimo, ch'o no l'ha ancon creou un'utensa o comunque o no a doeuvia oua. Pe identificâlo l'è quindi necessaio doeuviâ o nummero do so adresso IP. I adresci IP poeuan però ese condivixi da ciù utenti. Se t'ê un utente anonimo e ti ritegni che i commenti inte sta pagina no se riferiscian a ti, [[Special:CreateAccount|crea una noeuva utensa]] o donque [[Special:UserLogin|intra con quella che ti g'hæ za]] pe evitâ de chì avanti de ese confuzo con di atri utenti anonnimi .''",
-       "noarticletext": "Po-u momento a pagina çercâ a l'è vêua. L'è poscibbile [[Special:Search/{{PAGENAME}}|çercâ 'sto tittolo]] inte âtre pagine do scîto opû [{{fullurl:{{FULLPAGENAME}}|action=edit}} cangiâ a pagina òua].",
+       "anontalkpagetext": "----\n<em>Sta chì a l'è a paggina de discuscion de un utente anonnimo, ch'o no l'ha ancon creou un'utensa o comunque o no a doeuvia oua.</em> Pe identificâlo l'è quindi necessaio doeuviâ o nummero do so adresso IP. I adresci IP poeuan però ese condivixi da ciù utenti. Se t'ê un utente anonnimo e ti ritegni che i commenti inte sta pagina no se riferiscian a ti, [[Special:CreateAccount|crea una noeuva utensa]] o donque [[Special:UserLogin|intra con quella che ti g'hæ za]] pe evitâ de chì avanti de ese confuzo con di atri utenti anonnimi .",
+       "noarticletext": "Po-u momento a pagina çercâ a l'è vêua. Ti poeu [[Special:Search/{{PAGENAME}}|çercâ sto tittolo]] inti atre pagine do scito, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} çercâ inti registri correlæ] oppû [{{fullurl:{{FULLPAGENAME}}|action=edit}} creâ questa pagina]</span>.",
        "noarticletext-nopermission": "Òua a pàgina çercâ a l'è vêua. L'è poscìbile [[Special:Search/{{PAGENAME}}|çercâ sto tìtolo]] inte di âtre pàgine do scîto o <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} çercâ inti registri corelæ]</span>, ma no ti gh'hæ i outorizzaçioin pe creâ sta paggina.",
        "missing-revision": "La verscion #$1 da paggina \"{{FULLPAGENAME}}\" a no l'esiste.\n\nQuesto succede solitamente se inta stoia ti sciacchi un vegio ingancio a una paggina scassâ.\n\nI dettaggi peuan ese attrovæ into [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de scançellaçioin].",
        "userpage-userdoesnotexist": "L'utensa \"$1\" a no corisponde a un utente registròu.\nTi veu davei creâ o modificâ sta paggina?",
        "userpage-userdoesnotexist-view": "L'utensa \"$1\" a no l'è registrâ.",
        "blocked-notice-logextract": "St'utente o l'è attualmente bloccòu.\nL'urtimo elemento into registro di blocchi o l'è riportòu chì appreuvo pe informassion:",
-       "clearyourcache": "<strong>Notta:</strong> doppo avei sarvou, poriæ ese necessaio nettezâ a cache do proppio navegatô pe vedde i cangiamenti. \n*<strong>Firefox / Safari:</strong> tegnî sciacou o tasto de maiuscole <em>Shift</em> e cliccâ <em>Riecarrega</em>, oppû premme <em>Ctrl-F5</em> ò <em>Ctrl-R</em> (<em>⌘-R</em> insce Mac)\n*<strong>Google Chrome:</strong> premme <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> insce un Mac)\n*<strong>Internet Explorer:</strong> tegnî sciacou o tasto <em>Ctrl</em> e cliccâ <em>Aggiorna</em>, oppû premme <em>Ctrl-F5</em>\n*<strong>Opera:</strong> svuâ completamente a cache da-o menu <em>Strumenti → Preferençe</em>",
+       "clearyourcache": "<strong>Notta:</strong> doppo avei sarvou, poriæ ese necessaio netezâ a cache do proppio browser pe vedde i cangiamenti. \n*<strong>Firefox / Safari:</strong> tegni sciacou o tasto de maiuscole <em>Shift</em> e clicca <em>Recarega</em>, oppû sciacca <em>Ctrl-F5</em> ò <em>Ctrl-R</em> (<em>⌘-R</em> su Mac)\n*<strong>Google Chrome:</strong> sciacca <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> insce un Mac)\n*<strong>Internet Explorer:</strong> tegni sciacou o tasto <em>Ctrl</em> e clicca <em>Aggiorna</em>, oppû sciacca <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Vanni into <em>Menu → Impostaçioin</em> (<em>Opera → Preferençe</em> insce 'n Mac) e dapoeu inte <em>Privacy & segueçça → Nettezza dæti do browser → Inmaggine e file inta cache</em>.",
        "usercssyoucanpreview": "'''Suggerimento:''' adeuvia o pomello 'Amia l'anteprimma' per provâ o to neuvo CSS primma de sarvâlo.",
        "userjsyoucanpreview": "'''Suggerimento:''' adeuvia o pomello 'Amia l'anteprimma' per provâ o to neuvo JavaScript primma de sarvâlo.",
        "usercsspreview": "'''Questa a l'è solo un'anteprimma do proppio CSS personâ. E modiffiche no son ancon stæte sarvæ!'''",
        "previewnote": "'''Questa chì a l'è solo 'n'anteprimma; i cangiamenti no son ancon stæti sarvæ!'''",
        "continue-editing": "Vanni a l'area de modiffica",
        "previewconflict": "L'anteprimma a mostra o scrito presente inta casella de modiffica de d'ato coscì comme o l'apaiâ se ti çerni de sarvalo òua.",
-       "session_fail_preview": "'''N'emmo posciuo elaborâ a modiffica a caosa da pèrdia di dæti relativi a-a sescion.'''\nRitenta.\nSe-o problema o persciste, preuva a [[Special:UserLogout|sciortî]] e a intrâ torna.'''",
+       "session_fail_preview": "Spiaxenti. No è stæto poscibile elaboâ a modifica perché son andæti persci i dæti relativi a-a sescion.\n\nFoscia t'ê stæto disconnesso. <strong>Verifica d'ese ancon collegou e riproeuva</strong>.\nSe o problema o persciste, ti poeu provâ a [[Special:UserLogout|scollegate]] e effettuâ un nuoeuvo accesso, controllando che o to browser o l'açette i cookie da questo scito.",
+       "session_fail_preview_html": "Spiaxenti. No è stæto poscibbile elaboâ a modifica perché son anæti persci i dæti relativi a-a sescion.\n\n<em>Scicomme {{SITENAME}} o g'ha de l'HTML sgroeuzzo attivou e gh'è stæto una perdia di dæti da sescion, l'anteprimma a l'è ascosa comme precaoçion contra i attacchi JavaScript.</em>\n\n<strong>Se se tratta de un normale tentativo d'anteprimma, riproeuva.</strong> \nSe o problema o persciste, ti poeu provâ a [[Special:UserLogout|scollegati]] e effettuâ un noeuvo accesso, controllando che o to browser o l'açette i cookie da questo scito.",
+       "token_suffix_mismatch": "'''A modiffica a no l'è stæta sarvâ perché o to client o l'ha mostrou de gestî in moddo errou i carattei de puntezatua into token associou a-a mæxima. Pe evitâ una poscibile corruçion do testo da pagina, l'è stæto refuou l'intrega modiffica. Questa scituaçion a poeu veificase, de votte, quande s'adoeuvia di serviççi de proxy anonnimi via web che presentan di bug.'''",
        "edit_form_incomplete": "'''De parte do formulaio de modiffica n'han razonto o server; controlla che e modiffiche seggian intatte e ripreuva.'''",
        "editing": "Modiffica de $1",
        "creating": "T'ê apreuvo a creâ $1",
        "editingold": "<strong>Attençion: t'ê apreuvo a modificâ una verscion non aggiornâ da paggina.</strong>\nSarvandola coscì, tutti i cangi fæti doppo sta verscion saian sorvescriti.",
        "yourdiff": "Differense",
        "copyrightwarning": "Nota: Tùtte e contribuçioìn a {{SITENAME}} van conscideræ comme rilasciæ drento a-i termini da licensa d'ûso $2 (veddi $1 pe savéine de ciù).\nSe no ti veu che i testi teu pêuan esse modificæ da quarchedùn sensa limitaçioìn, no mandâli a {{SITENAME}}.<br />\nInviando o testo ti diciâri, sott'a teu responsabilitæ, ch'o l'é stæto scrîto da ti personalmente oppure ch'o l'é stæto piggiòu da 'na fonte de pùbrico domìnio òu anàlogamente lìbea.<br />\n'''NO INVI MATERIÂLE COVERTO DA DRÎTI D'AUTÔ SENSA OUTORIZAÇION!'''",
+       "copyrightwarning2": "Pe piaxei tegni presente che tutti i contributi a {{SITENAME}} poeuan ese modificæ, alteræ ò scassæ da di atri contributoî.\nSe no ti voeu che se faççe ravaxo di to testi, alloa no stali manco a mette.<br />\nInviando o testo ti deciæi ascì, sotta a to responsabilitæ, ch'o l'è stæto scrito da ti personalmente oppù ch'o l'è stæto copiou da 'na fonte de pubbrico dominnio o scimilemente libera (vedi $1 pe dettaggi).\n'''No stanni a inviâ do mateiâ protetto da-o drito d'aotô sença aotorizzaçion!'''",
        "editpage-cannot-use-custom-model": "O modello do contegnuo de sta paggina o no peu ese modificòu.",
        "longpageerror": "'''Errô: o scrito inviou o l'è longo {{PLURAL:$1|1|$1}} kilobyte, ciu che-o mascimo consentio de ({{PLURAL:$2|1|$2}} kilobyte).'''\nNo se peu sarvâ.",
+       "readonlywarning": "<strong>Attençion</strong>: o database o l'è bloccou pe manutençion, no l'è momentaniamente poscibile sarvâ e modifiche effettuæ.\nPe no perdile, coppile in te'n file de testo e sarvilo in atteisa do sbrocco do database.\n\nL'amministratô de scistema ch'o l'ha misso l'abrocco o l'ha fornio questa spiegaçion: $1.",
        "protectedpagewarning": "'''Attençion: questa paggina a l'è stæta bloccâ de moddo che solo i utenti co-i privileggi d'amministratô possan modificala.'''\nL'urtimo elemento do registro o l'è riportou chì appreuvo pe referença:",
        "semiprotectedpagewarning": "'''Notta:''' Questa paggina a l'è stæta bloccä de moddo che solo i utenti registræ possan modificâla.\nL'urtimo elemento do registro o l'è riportou chì appreuvo pe referensa:",
        "cascadeprotectedwarning": "'''Attençion:''' Questa paggina a l'è stæta bloccâ de moddo che solo i utenti co-i privileggi d'amministratô possan modificala da-o momento ch'a l'é inclusa seleçionando a proteçion \"ricorsciva\" {{PLURAL:$1|inta paggina|inte paggine}}:",
        "permissionserrors": "No ti g'hæ o permisso",
        "permissionserrorstext": "No ti g'hæ i permissi pe fâlo, pe  {{PLURAL:$1|questa raxon|queste raxoin}}:",
        "permissionserrorstext-withaction": "No ti g'hæ i permìssi pe $2 pe {{PLURAL:$1|sta raxon|ste raxoìn}}:",
-       "contentmodelediterror": "No ti peu modificâ sta verscion da-o momento che o so modello de contegnuo o l'è <code>$1</code>, mentre o corrente modello de contegnuo da paggina o l'è <code>$2</code>.",
+       "contentmodelediterror": "No ti peu modificâ sta verscion da-o momento che o so modello de contegnuo o l'è <code>$1</code>, ch'o diffeisce da-o corrente modello de contegnuo da paggina <code>$2</code>.",
        "recreate-moveddeleted-warn": "Atençión: ti stæ pe ricreâ 'na pàgina zà scancelâ into passòu.'''\n\nConsciddera se l'è o caxo de continoâ  a cangiâ 'sta pàgina.\nPe comoditæ e cancellaçioìn e i stramui son pubricæ chì sotta:",
        "moveddeleted-notice": "Sta pàgina a l'é stæta scancelâ.\nA lista de scancelaçioìn e di stramui son riportæ chi de sotta pe informaçión.",
        "moveddeleted-notice-recent": "Spiaxenti, sta paggina a l'è stæta scassâ reçentemente (inte urtime 24 oe).\n\nE açioin de cançellaçion e spostamento pe questa paggina son disponibile chì appreuvo pe referença.",
        "content-model-javascript": "JavaScript",
        "content-json-empty-object": "Ogetto veuo",
        "content-json-empty-array": "Array veuo",
+       "deprecated-self-close-category": "Paggine ch'adoeuvian di tag HTML aoto-ciosi non vallidi",
+       "deprecated-self-close-category-desc": "A pagina a conten di tag HTML aoto-ciosi non vallidi, comme <code>&lt;b/></code> o <code>&lt;span/></code>. O comportamento de questi fito o cangiâ pe ese coerente co-e specifiche HTML5, pe questo o so utilizzo into wikitesto o l'è deprecou.",
        "duplicate-args-warning": "<strong>Avvertensa:</strong> [[:$1]] o ciamma [[:$2]] con ciù de un valô pe-o parammetro \"$3\".  Solo l'urtimo valô fornio o saiâ deuviou.",
        "duplicate-args-category": "Paggine che ciamman i template deuviando di parammetri duplicæ",
        "duplicate-args-category-desc": "A paggina a conten de ciamæ a di template ch'adeuvian di argomenti duplicæ, comme presempio <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> ò <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
-       "expensive-parserfunction-warning": "'''Attençion:''' Questa paggina a conten troppe ciamæ a-e fonçioin de parser .\n\nA doviæ aveighene meno de $2, a-o momento ghe n'è $1.",
+       "expensive-parserfunction-warning": "'''Attençion:''' Questa paggina a conten troppe ciamæ a-e fonçioin de parser .\n\nA doviæ aveighene meno de $2, a-o momento ghe {{PLURAL:$1|n'è $1}}.",
        "expensive-parserfunction-category": "Paggine con troppe ciamæ a-e fonçioin de parser .",
        "post-expand-template-inclusion-warning": "'''Atento:''' a dimensción di template che t'æ misso a l'é tròppo grande.\nQuarchedun di teu template o no saiâ incluzo.",
        "post-expand-template-inclusion-category": "Pàgine con di template che gh'àn a dimensción ciù âta do limite mascimo",
        "undo-nochange": "Pâ che-a modiffica a sæ zà stæta anullâ.",
        "undo-summary": "Annullou a modiffica $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|discuscion]])",
        "undo-summary-username-hidden": "Anullou a modiffica $1 de un utente ascoso",
-       "cantcreateaccounttitle": "Non se peu registrâ l'utente",
        "cantcreateaccount-text": "A registrassion da questo addresso IP (<b>$1</b>) a l'è stæta bloccâ da [[User:$3|$3]].\n\nA raxon dæta a l'è ''$2''",
        "cantcreateaccount-range-text": "A registraçion da di addressi IP inte l'intervallo <strong>$1</strong>, ch'o  l'includde o teu (<strong>$4</strong>), a l'è stæta bloccâ da [[User:$3|$3]].\n\nA raxon dæta da $3 a l'è <em>$2</em>",
        "viewpagelogs": "Veddi i log relativi a 'sta paggina.",
        "currentrevisionlink": "Ûrtima revixon",
        "cur": "cor",
        "next": "Proscimo",
-       "last": "Ûrtima",
+       "last": "prec",
        "page_first": "primma",
        "page_last": "ûrtima",
        "histlegend": "Confronto tra verscioîn: selession-a e cascette corispondenti a-e verscioîn dexidiæ e schissa Invio oppû o pomello da basso.\n\nLegenda: (corr) = differense co-a verscion corrente, (prec) = differense co-a verscion precedente, '''m''' = modiffica minô",
        "rev-deleted-no-diff": "No ti peu amiâ sto diff percose un-a de verscioin a l'è stæta '''scassâ'''.\nConsurta a [{{fullurl:{{#Special:Log}}/delete|page={{PAGENAMEE}}}} lista de cançellaçioin] pe-i dettaggi.",
        "rev-suppressed-no-diff": "No ti peu fâ sto confronto tra verscioin perché un-a a l'è stæta '''scassâ'''.",
        "rev-deleted-unhide-diff": "Un-a de verscioin de sto confronto a l'è stæta '''scassâ'''.\nConsurta a [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} lista de cançellaçioin] pe-i dettaggi.\nTi ti peu ancon [$1 fâ sto confronto] se necessaio.",
+       "rev-suppressed-unhide-diff": "Un-a de verscioin de sto confronto a l'è stæta '''sopressa'''.\nConsurta a [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} lista de cançellaçioin] pe-i dettaggi.\nTi peu ancon [$1 fâ sto confronto] se necessaio.",
+       "rev-deleted-diff-view": "Un-a de verscioin de sto confronto a l'è stæta '''scassâ'''.\nTi ti peu amiala; consurta o [{{fullurl:{{#Special:Log}}/suppress|page={{PAGENAMEE}}}} recistro de rimoçioin] pe-i dettaggi.",
+       "rev-suppressed-diff-view": "Un-a de verscioin de sto confronto a l'è stæta '''sopressa'''.\nTi ti peu amiâla; consurta o [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} registro de soprescioin] pe-i dettaggi.",
        "rev-delundel": "fanni védde/ascondi",
        "rev-showdeleted": "mostra",
        "revisiondelete": "Scassa ò ripristina verscioin",
        "revdelete-nooldid-title": "Verscion non specificâ",
+       "revdelete-nooldid-text": "No t'hæ speçificou nisciun-a revixon in sciâ quæ eseguî questa fonçion, oppû a revixon speçificâ a no l'existe, ò che donque t'ê aproeuvo a çercâ d'asconde a revixon attuale.",
        "revdelete-no-file": "O file specificou o no l'existe.",
        "revdelete-show-file-confirm": "T'ê seguo de voei amiâ a verscion scassâ do file \"<nowiki>$1</nowiki>\" do $2 a $3?",
        "revdelete-show-file-submit": "Sci",
        "revdelete-selected-text": "{{PLURAL:$1|Verscion seleçionâ|Verscioin seleçionæ}} de [[:$2]]:",
-       "revdelete-selected-file": "{{PLURAL:$1|Verscion seleçionata|Verscioin seleçionæ}} do file [[:$2]]:",
+       "revdelete-selected-file": "{{PLURAL:$1|Verscion seleçionâ|Verscioin seleçionæ}} do file [[:$2]]:",
        "logdelete-selected": "{{PLURAL:$1|Evento do registro seleçionou|Eventi do registro seleçionou}}:",
+       "revdelete-text-text": "E verscioin scassæ apparian ancon inta cronologia da pagina, ma parte do so contegnuo o saiâ inaccescibile a-o pubbrico.",
+       "revdelete-text-file": "E verscioin di file scassæ apparian ancon inta cronologia do file, ma parte do so contegnuo a saiâ inaccescibile a-o pubbrico.",
+       "logdelete-text": "I eventi scassæ apparian ancon inti registri, ma 'na parte do so contegnuo a saiâ inaccescibile a-o pubbrico.",
+       "revdelete-text-others": "Di atri amministratoî saian ancon in grou de accede a-i contegnui ascoxi e porian ripristinali, se no l'è stæto impostou de restriçioin azontive.",
+       "revdelete-confirm": "Pe piaxei conferma che questo l'è quante t'intendi fâ, che t'accapisci e conseguençe, e che ti o fæ into rispetto de [[{{MediaWiki:Policy-url}}|linnie guidda]].",
+       "revdelete-suppress-text": "A rimoçion a saiæ da doeuviâ '''solo''' che inti seguenti caxi:\n* informaçioin potençialmente diffamatoie\n* dæti personæ inoportun\n*: ''indiriççi, nummeri de teleffono, coddiçi fiscali, ecc.''",
        "revdelete-legend": "Imposta e limitaçioin de vixibilitæ:",
        "revdelete-hide-text": "Testo da verscion",
        "revdelete-hide-image": "Ascondi i contegnui do file",
        "revdelete-unsuppress": "Elimmina e limitaçioin in scê verscioin ripristinæ",
        "revdelete-log": "Raxon:",
        "revdelete-submit": "Applica {{PLURAL:$1|a-a verscion seleçionâ|a-e verscioin seleçionæ}}",
-       "revdelete-success": "'''Vixibilitæ da verscion agiornâ con successo.'''",
+       "revdelete-success": "'''Vixibilitæ da verscion agiornâ.'''",
        "revdelete-failure": "'''A vixibilitæ da verscion a no peu ese agiornâ:'''\n$1",
-       "logdelete-success": "'''Registro de vixibilitæ impostou con successo.'''",
+       "logdelete-success": "'''Registro de vixibilitæ impostou.'''",
        "logdelete-failure": "No s'è posciuo impostâ o registro de vixibilitæ: $1",
        "revdel-restore": "càngia a vixibilitæ",
        "pagehist": "Stoia da paggina",
        "revdelete-modify-no-access": "Imposcibbile modificâ l'ogetto con dæta $1 $2 in quanto o l'è stæto identificou comme \"riservou\" e no se dispon-e do relativo accesso.",
        "revdelete-modify-missing": "Imposcibbile modificâ l'ogetto con ID $1: into database o no gh'è!",
        "revdelete-no-change": "'''Attençion:''' l'ogetto con dæta $1 $2 o l'aveiva zà e impostaçioin de vixibilitæ domandæ.",
+       "revdelete-concurrent-change": "Imposcibile modificâ l'ogetto con dæta $1, $2: pâ che o so stato o segge stæto modificou da un atro utente dementre che ti ti çercavi de modificalo.",
+       "revdelete-only-restricted": "Errô inte l'asconde l'ogetto datou $1, $2: no ti poeu asconde i ogetti a-i amministratoî sença seleçionâ un-a di atre opçioin de vixibilitæ.",
        "revdelete-reason-dropdown": "* Raxoin ciù comun-e pe-o scassamento\n** Violaçion do drito d'outô\n** Commenti ò informaçioin personæ inappropiæ\n** Nomme utente inappropiou\n** Informaçion potençialmente diffamatoia",
        "revdelete-otherreason": "Un atro motivo:",
        "revdelete-reasonotherlist": "Un'atra raxon",
        "revdelete-edit-reasonlist": "Modiffica e raxoin do scassamento",
        "revdelete-offender": "Aotô da verscion:",
        "suppressionlog": "Registro de sopprescioin",
+       "suppressionlogtext": "De sotta gh'è 'na lista de scassatue e di brocchi con do contegnuo ascoso a-i amministratoî.\nAmia a [[Special:BlockList|lista di blocchi]] pe l'elenco di bandi e di blocchi attivi a-o momento.",
        "mergehistory": "Union de stoie da paggina",
+       "mergehistory-header": "Questa pagina a consente de unî e verscioin d'a cronologia de 'na pagina sorgente a 'na pagina ciù reçente.\nAsseguite che sto cangio o mantegne a continuitæ storica da pagina.",
        "mergehistory-box": "Unisci a stoia de doe paggine:",
        "mergehistory-from": "Paggina d'origgine:",
        "mergehistory-into": "Paggina de destinaçion:",
        "mergehistory-empty": "Nisciun-a verscion da unî.",
        "mergehistory-done": "{{PLURAL:$3|Una verscion de $1 a l'è stæta unia|$3 vercsioin de $1 son stæte unie}} a-a stoia de [[:$2]].",
        "mergehistory-fail": "Imposcibbile unî e stoie. Verificâ a paggina e i parammetri temporali.",
+       "mergehistory-fail-bad-timestamp": "O timestamp o no l'è vallido.",
+       "mergehistory-fail-invalid-source": "A pagina sorgente a no l'è vallida.",
+       "mergehistory-fail-invalid-dest": "A pagina de destinaçion a no l'è vallida.",
+       "mergehistory-fail-no-change": "L'union de cronologie a no l'ha unio arcun-a verscion. Ricontrolla e pagine e i parametri temporali.",
+       "mergehistory-fail-permission": "Aotorizzaçioin insuffixente pe unî e cronologie.",
+       "mergehistory-fail-self-merge": "A paggina d'origgine e quella de destinaçion son a mæxima",
+       "mergehistory-fail-timestamps-overlap": "E verscioin d'origine se sorvepon-an ò vegnan doppo e verscioin de destinaçion.",
        "mergehistory-fail-toobig": "Imposcibbile eseguî l'union da stoia essendoghe ciu che o limmite de $1 {{PLURAL:$1|verscion|verscioin}} da mesciâ.",
        "mergehistory-no-source": "A paggina d'origgine $1 a no l'existe.",
        "mergehistory-no-destination": "A paggina de destinaçion $1 a no l'existe.",
        "lineno": "Linia $1:",
        "compareselectedversions": "Confronta e verscioîn selessionæ",
        "showhideselectedversions": "Mostra/ascondi verscioin seleçionæ",
-       "editundo": "Anùlla",
+       "editundo": "anùlla",
        "diff-empty": "(Nisciun-a diferença)",
        "diff-multi-sameuser": "({{PLURAL:$1|Una verscion intermedia|$1 De verscioin intermedie}} de 'n mæximo utente {{PLURAL:$1|a no l'è mostrâ|no son mostræ}})",
        "diff-multi-otherusers": "({{PLURAL:$1|Una verscion intermedia|$1 De verscioin intermedie}} de {{PLURAL:$2|'n atro utente|$2 utenti}} {{PLURAL:$1|a no l'è mostrâ|no son mostræ}})",
        "diff-multi-manyusers": "({{PLURAL:$1|Una verscion intermedia|$1 verscioin intermedie}} de ciu che $2 {{PLURAL:$2|utente|utenti}} non {{PLURAL:$1|mostrâ|mostræ}})",
-       "searchresults": "Resultati da reçerca",
-       "searchresults-title": "Rezoltati da riçerca de \"$1\"",
+       "difference-missing-revision": "{{PLURAL:$2|Una verscion|$2 verscioin}} de questa differença ($1) {{PLURAL:$2|a no l'è stæta atrovâ|no son stæte atrovæ}}.\n\nQuesto succede a l'uso se inta stoia ti sciacchi un vegio ingancio a una paggina scassâ.\n\nI dettaggi ti-i peu attrovâ into [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de scançellaçioin].",
+       "searchresults": "Risultæ da riçerca",
+       "searchresults-title": "Risultæ da riçerca de \"$1\"",
        "titlematches": "Corispondençe into tittolo de paggine",
        "textmatches": "Corispondençe into scrito de paggine",
        "notextmatches": "Nisciun-a corispondença into scrito de paggine",
        "prev-page": "paggina precedente",
        "next-page": "paggina succesciva",
        "prevn-title": "{{PLURAL:$1|rezoltato precedénte|rezoltati precedénti}}",
-       "nextn-title": "Pròscimo $1 {{PLURAL:$1|rezoltato|rezoltati}}",
-       "shown-title": "Fanni védde {{PLURAL:$1|in rizoltato|$1 rizoltati}} pe pàgina",
+       "nextn-title": "{{PLURAL:$1|Risultou succescivo|$1 risultæ succescivi}}",
+       "shown-title": "Fanni védde {{PLURAL:$1|un rizultou|$1 rizultæ}} pe pàgina",
        "viewprevnext": "Veddi ($1 {{int:pipe-separator}} $2) ($3).",
        "searchmenu-exists": "'''Inte questa wiki gh'è za 'na pàgina co-o nómme \"[[:$1]]\"'''",
        "searchmenu-new": "<strong>Crea a paggina \"[[:$1]]\" insce questo wiki!</strong> {{PLURAL:$2|0=|Amia ascì a paggina trovâ co-a teu riçerca|Amia ascì i risultæ da riçerca}}",
-       "searchprofile-articles": "Pàgina di contegnûi",
+       "searchprofile-articles": "Pàgina de contegnûi",
        "searchprofile-images": "Moltimedia",
        "searchprofile-everything": "Tùtto",
        "searchprofile-advanced": "Avansæ",
        "powersearch-togglenone": "Nisciun",
        "powersearch-remember": "Aregordite a seleçion pe-e proscime riçerche",
        "search-external": "Riçerca esterna",
-       "searchdisabled": "La riçerca de {{SITENAME}} a no l'è attiva. Into fra tempo ti peu çercâ in sce Google. \nNotta che i seu indexi di contegnui de {{SITENAME}} porrieivan no ese aggiornæ.)",
+       "searchdisabled": "A riçerca de {{SITENAME}} a no l'è attiva. Into fratempo ti peu çercâ in sce Google. \nNotta che i so indexi di contegnui de {{SITENAME}} porrieivan no ese aggiornæ.",
        "search-error": "S'è verificou 'n errô durante a riçerca: $1",
        "preferences": "Preferençe",
        "mypreferences": "Preferençe",
        "recentchangesdays-max": "Mascimo $1 {{PLURAL:$1|giorno|giorni}}",
        "recentchangescount": "Nummero de modiffiche da mostrâ pe difetto:",
        "prefs-help-recentchangescount": "Comprende i urtime modiffiche, paggine de stoie e registri.",
+       "prefs-help-watchlist-token2": "Questa a l'è a ciave segretta pe-o feed web di to oservæ.\nChiunque a conosce saiâ in graddo de leze i to oservæ, quindi no stanni a condividdila. [[Special:ResetTokens|Clicca chi se ti g'hæ de besoeugno de rempostâla]].",
        "savedprefs": "E teu preferençe son stæte sarvæ.",
        "savedrights": "I driti utente de {{GENDER:$1|$1}} son stæti sarvæ.",
        "timezonelegend": "Fuso oraio:",
        "timezoneregion-europe": "Euiropa",
        "timezoneregion-indian": "Oçeano Indian",
        "timezoneregion-pacific": "Oçeano Paxiffego",
-       "allowemail": "Permitti a posta elettronega da ätri utenti",
+       "allowemail": "Permetti a posta elettronega da di ätri utenti",
        "prefs-searchoptions": "Çerca",
        "prefs-namespaces": "Namespace:",
        "default": "Predefinïo",
        "prefs-custom-css": "CSS personalizzou",
        "prefs-custom-js": "JavaScript personalizzou",
        "prefs-common-css-js": "CSS/JavaScript condiviso pe tutte e pelle:",
+       "prefs-reset-intro": "L'è poscibile doeuviâ sta pagina pe rimpostâ e propie preferençe a quelle predefinie do scito.\nL'opiaçion a no poeu ese annullâ.",
        "prefs-emailconfirm-label": "Conferma de l'e-mail:",
        "youremail": "Indirìsso email:",
        "username": "{{GENDER:$1|Nomme utente}}:",
        "yourvariant": "Variante da lengoa do contegnuo:",
        "prefs-help-variant": "A variante o grafia co-a quæ ti prefeisci che e paggine do wiki seggian mostræ.",
        "yournick": "Nomiagio:",
+       "prefs-help-signature": "I commenti inte pagine de discuscion devan ese firmæ con \"<nowiki>~~~~</nowiki>\" ch'o saiâ convertio inta proppia firma seguia da-a dæta.",
        "badsig": "Errô in ta firma; controlla i comandi HTML.",
        "badsiglength": "A firma scelta a l'è troppo longa.\nA non deve passâ $1 {{PLURAL:$1|carattere|caratteri}}.",
+       "yourgender": "Comme rifeise a ti?",
+       "gender-unknown": "Into mençunâte, o software o l'adoeuviâ de paole de genere noeütro ogni votta che saiâ poscibile",
        "gender-male": "O l'è registrou insce {{SITENAME}}",
        "gender-female": "A l'è registrâ insce {{SITENAME}}",
        "prefs-help-gender": "L'impostassion de sta preferensa a l'è opsionâ.\nO software o deuvia sto valô pe addressâse a ti e mensunate a-i atri deuviando o gennere grammaticale apropiou.\nQuesta informassion a saiâ pubblica.",
        "userrights": "Manezzo di driti di utenti",
        "userrights-lookup-user": "Gestisci i gruppi di utenti",
        "userrights-user-editname": "Scrivi o teu nomme utente:",
-       "editusergroup": "Modiffica i gruppi di utenti",
+       "editusergroup": "Modiffica groppi {{GENDER:$1|utente}}",
        "editinguser": "Apreuvo a cangiâ i driti de l'{{GENDER:$1|utente}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Modiffica i gruppi di utenti",
-       "saveusergroups": "Sarva i gruppi di utenti",
+       "saveusergroups": "Sarva groppi {{GENDER:$1|utente}}",
        "userrights-groupsmember": "Membro de:",
        "userrights-groupsmember-auto": "Membro impliçito de:",
+       "userrights-groups-help": "L'è poscibile modificâ i groppi de st'utente:\n* Una casella marcâ voeu dî che l'utente o l'è inte quello groppo.\n* Una casella smarcâ voeu dî che l'utente o no l'è inte quello groppo.\n* O scimbolo * o voeu dî che no ti poeu smarcâ o groppo una votta che ti l'hæ azonto (ò viçeversa).",
        "userrights-reason": "Raxon:",
        "userrights-no-interwiki": "No ti g'hæ i permissi pe modificâ i driti di utenti insce di atre wiki.",
        "userrights-nodatabase": "O database $1 o no l'esiste ò o no l'è un database locale.",
        "userrights-changeable-col": "Gruppi che ti peu modificâ",
        "userrights-unchangeable-col": "Gruppi che no ti peu modificâ",
        "userrights-conflict": "Conflito de modiffica di driti utente! Pe piaxei controlla e conferma e teu modiffiche.",
-       "userrights-removed-self": "T'hæ rimosso con successo i teu driti. E quindi, no ti saiæ ciù in grou de accede a questa paggina.",
+       "userrights-removed-self": "T'hæ rimosso i teu driti. E quindi, no ti saiæ ciù in grou de accede a questa paggina.",
        "group": "Gruppo:",
        "group-user": "Ûtenti",
        "group-autoconfirmed": "Utenti aotoconfermæ",
        "grouppage-sysop": "{{ns:project}}:Amministratoî",
        "grouppage-bureaucrat": "{{ns:project}}:Buroccrati",
        "grouppage-suppress": "{{ns:project}}:Soppressoî",
+       "right-read": "Leze paggine",
+       "right-edit": "Modiffica paggine",
+       "right-createpage": "Crea paggine (escluse e pagine de discuscion)",
+       "right-createtalk": "Crea pagine de discuscion",
+       "right-createaccount": "Crea noeuve utençe",
+       "right-autocreateaccount": "Accede aotomaticamente con un'utença esterna",
+       "right-minoredit": "Marca e modifiche comme menoî",
        "right-move": "Mescia e paggine",
+       "right-move-subpages": "Mescia e pagine insemme a-e relative sottopagine",
+       "right-move-rootuserpages": "Mescia e pagine prinçipæ di utenti",
+       "right-move-categorypages": "Mescia e categorie",
+       "right-movefile": "Mescia i file",
+       "right-suppressredirect": "O no crea un redirect aotomatico quande se mescia una pagina",
+       "right-upload": "Carrega file",
+       "right-reupload": "Sorvescrive un file existente",
+       "right-reupload-own": "Sorvescrive un file existente caregou da-o mæximo utente",
+       "right-reupload-shared": "Sorvescrive localmente di file presenti inte l'archivio condiviso",
+       "right-upload_by_url": "Carega un file da un addreçço URL",
+       "right-purge": "Nettezza a cache do scito sença conferma",
+       "right-autoconfirmed": "Non soggetto a-o limmite de açioin pe IP",
+       "right-bot": "Da trattâ comme processo aotomatico",
+       "right-nominornewtalk": "Fa scì che e modiffiche menoî a-e pagine de discuscion no faççan comparî l'avviso de noeuvo messaggio",
+       "right-apihighlimits": "Doeuvia di limmiti ciù erti pe-e interrogaçioin API",
        "right-writeapi": "Deuvia l'API in scrittua",
+       "right-delete": "Scassa e paggine",
+       "right-bigdelete": "Scassa e pagine con cronologie longhe",
+       "right-deletelogentry": "Scassa e ripristina voxe de registro specifiche",
+       "right-deleterevision": "Scassa e ripristina de verscion specifiche de pagine",
+       "right-deletedhistory": "Vixualizza e verscioin da cronologia scassæ, sença o so testo associou",
+       "right-deletedtext": "Vixualizza o testo scassou e-e modifiche fra de verscioin scassæ",
+       "right-browsearchive": "Çerca inte pagine scassæ",
+       "right-undelete": "Recuppera una paggina",
+       "right-suppressrevision": "Vedde, asconde e ripristina de verscioin specifiche de pagine a quæ-se-segge utente",
+       "right-viewsuppressed": "Vedde de verscioin ascose a qua-se-sæ utente",
+       "right-suppressionlog": "Vixualizza i registri privæ",
+       "right-block": "Blocca e modifiche di atri utenti",
+       "right-blockemail": "Impedisce a un utente de mandâ de email",
+       "right-hideuser": "Blocca un nomme utente, ascondendolo a-o pubbrico",
+       "right-ipblock-exempt": "Ignora i blocchi di IP, i blocchi aotomatichi e i blocchi de range de IP",
+       "right-unblockself": "Sblocca lê mæximo",
+       "right-protect": "Camgia i livelli de proteçion e modifica e pagine protette ricorscivamente",
+       "right-editprotected": "Modifica e pagine protette con \"{{int:protect-level-sysop}}\"",
+       "right-editsemiprotected": "Modifica e pagine protette con \"{{int:protect-level-autoconfirmed}}\"",
+       "right-editcontentmodel": "Modifica o modello de contegnuo de 'na paggina",
+       "right-editinterface": "Modiffica l'interfaccia utente",
+       "right-editusercssjs": "Modifica i file CSS e JS di atri utenti",
+       "right-editusercss": "Modifica i file CSS di atri utenti",
+       "right-edituserjs": "Modiffica i file JS di atri utenti",
+       "right-editmyusercss": "Modifica o file CSS do proppio utente",
+       "right-editmyuserjs": "Modifica o file JavaScript do proppio utente",
+       "right-viewmywatchlist": "Vixualizza i proppi osservæ speçiali",
+       "right-editmywatchlist": "Modifica i to osservæ speçiali. Da notâ che arcun-e açioin porian ancon azonze de pagine anche sença questo drito.",
+       "right-viewmyprivateinfo": "Vixualizza i proppi dæti personæ (presempio: adreçço email, nomme veo)",
+       "right-editmyprivateinfo": "Modiffica i proppi dæti personæ (presempio: addreçço email, nomme veo)",
+       "right-editmyoptions": "Modiffica e proppie preferençe",
+       "right-rollback": "Annulla rapidamente e modifiche de l'urtimo utente ch'o l'ha modificou una pagina in particolâ",
+       "right-markbotedits": "Marca e modifiche soggette a rollback comme effettuæ da bot",
+       "right-noratelimit": "No soggetto a-o limmite de açioin",
+       "right-import": "Importa de pagine da di atri wiki",
+       "right-importupload": "Importa paggine da un upload de file",
+       "right-patrol": "Marca e modifiche di altri utenti comme controllæ",
+       "right-autopatrol": "Marca aotomaticamente e proppie modifiche comme controllæ",
+       "right-patrolmarks": "Doeuvia a fonçion de verifica di urtime modiffiche",
+       "right-unwatchedpages": "Vixualizza una lista de pagine non öservæ",
+       "right-mergehistory": "Unisce a cronologia de paggine",
+       "right-userrights": "Modiffica tutti i driti de l'utente",
+       "right-userrights-interwiki": "Modiffica i driti di utenti insce di atre wiki",
+       "right-siteadmin": "Abbrocca e sbrocca o database",
+       "right-override-export-depth": "Esporta e paggine includendo e pagine collegæ scin a 'na profonditæ de 5",
+       "right-sendemail": "Manda de email a di atri utenti",
+       "right-passwordreset": "Vedde i messaggi de rempostaçion da password",
+       "right-managechangetags": "Crea e attiva/disattiva i [[Special:Tags|etichette]]",
+       "right-applychangetags": "Apprica di [[Special:Tags|etichette]] a-e proppie modiffiche",
+       "right-changetags": "Azonze e leva de specifiche [[Special:Tags|etichette]] insce scingole verscioin o voxe de registro",
+       "right-deletechangetags": "Scassa i [[Special:Tags|etichette]] da-o database",
+       "grant-generic": "Pacchetto diritti \"$1\"",
+       "grant-group-page-interaction": "Interagisce co-e paggine",
+       "grant-group-file-interaction": "Interagisce co-i file murtimediali",
+       "grant-group-watchlist-interaction": "Interagisce con i to oservæ speçiali",
+       "grant-group-email": "Invia email",
+       "grant-group-high-volume": "Esegue açioin mascive",
+       "grant-group-customization": "Personalizzaçion e preferençe",
+       "grant-group-administration": "Esegue açioin amministrative",
+       "grant-group-other": "Attivitæ varrie",
+       "grant-blockusers": "Blocca e sblocca utenti",
+       "grant-createaccount": "Crea un'utença",
+       "grant-createeditmovepage": "Crea, modiffica e mescia e pagine",
+       "grant-delete": "Scassa pagine, revixoin, e voxe de registro",
+       "grant-editinterface": "Modifica o namespace MediaWiki e i CSS/JavaScript di utenti",
+       "grant-editmycssjs": "Modifica i CSS/JavaScript da to utença",
+       "grant-editmyoptions": "Modifica e preferençe da to utença",
+       "grant-editmywatchlist": "Modiffica a to lista di öservæ",
+       "grant-editpage": "Modifica pagine existente",
+       "grant-editprotected": "Modifica pagine protette",
+       "grant-highvolume": "Modiffiche mascive",
+       "grant-oversight": "Asconde i utenti e soprimme e revixoin",
+       "grant-patrol": "Marca e modifiche a-e paggine comme veificæ",
+       "grant-protect": "Proteze e sproteze e paggine",
+       "grant-rollback": "Rollback de modifiche a-e pagine",
+       "grant-sendemail": "Manda di email a di atri utenti",
+       "grant-uploadeditmovefile": "Carega, sostituisce e mescia i file",
+       "grant-uploadfile": "Carrega di noeuvi file",
+       "grant-basic": "Driti de base",
+       "grant-viewdeleted": "Vedde i file e e pagine scassæ",
+       "grant-viewmywatchlist": "Vedde i to öservæ speçiali",
        "newuserlogpage": "Nêuvi utenti",
+       "newuserlogpagetext": "Questo o l'è un registro de creaçioin di utençe.",
        "rightslog": "Diritti d'ûtente",
+       "rightslogtext": "Questo o l'è un registro di cangi a-i driti di utenti.",
+       "action-read": "leze sta paggina",
        "action-edit": "càngia sta pàgina",
+       "action-createpage": "creâ sta paggina",
+       "action-createtalk": "creâ sta paggina de discuscion",
+       "action-createaccount": "creâ st'utença",
+       "action-autocreateaccount": "creâ aotomaticamente st'utença esterna",
+       "action-history": "vedde a cronologia de sta pagina",
+       "action-minoredit": "marcâ sta modifica comme menô",
+       "action-move": "mesciâ sta pagina",
+       "action-move-subpages": "mesciâ sta pagina e e relative sottopagine",
+       "action-move-rootuserpages": "mesciâ e paggine prinçipæ di utenti",
+       "action-move-categorypages": "mesciâ e categorie",
+       "action-movefile": "mesciâ sto file",
+       "action-upload": "caregâ sto file",
+       "action-reupload": "sorvescrive sto file existente",
+       "action-reupload-shared": "soviascrive sto file presente inte l'archivio condiviso",
+       "action-upload_by_url": "caregâ sto file da un addreçço URL",
+       "action-writeapi": "deuviâ l'API in scrittua",
+       "action-delete": "scassâ 'sta paggina",
+       "action-deleterevision": "scassâ sta verscion",
+       "action-deletedhistory": "vixualizzâ a cronologia scassâ de sta pagina",
+       "action-browsearchive": "çercâ paggine scassæ",
        "action-undelete": "Recuppera sta paggina",
        "action-suppressrevision": "rivedde e ripristinâ e modiffiche ascose",
+       "action-suppressionlog": "vedde questo registro privou",
+       "action-block": "bloccâ st'utente in scrittua",
+       "action-protect": "modificâ i livelli de proteçion pe questa pagina",
+       "action-rollback": "annullâ rapidamente e modifiche de l'urtimo utente ch'o l'ha modificou una pagina determinâ",
+       "action-import": "importâ de pagine da 'n'atra wiki",
+       "action-importupload": "importâ de pagine tramite upload da file",
+       "action-patrol": "marcâ e modifiche di atri utenti comme controllæ",
+       "action-autopatrol": "marcâ e proppie modifiche comme controllæ",
+       "action-unwatchedpages": "vixonâ a lista de pagine non öservæ",
+       "action-mergehistory": "unî a cronologia de sta pagina",
+       "action-userrights": "modificâ tutti i driti di utenti",
+       "action-userrights-interwiki": "modificâ i driti di utenti insce di atre wiki",
+       "action-siteadmin": "broccâ e sbroccâ o database",
+       "action-sendemail": "mandâ di e-mail",
+       "action-editmywatchlist": "modificâ a to lista di öservæ",
+       "action-viewmywatchlist": "vedde i to öservæ speçiali",
+       "action-viewmyprivateinfo": "vedde i proppi dæti personali",
+       "action-editmyprivateinfo": "modificâ i proppi dæti personali",
+       "action-editcontentmodel": "modificâ o modello de contegnuo de 'na paggina",
+       "action-managechangetags": "creâ e attivâ/disattivâ i etichette",
+       "action-applychangetags": "appricâ di etichette a-e to modiffiche",
+       "action-changetags": "Azonze e levâ de specifiche etichette sciu scingole verscioin o voxe de registro",
+       "action-deletechangetags": "scassâ i etichette da-o database",
        "nchanges": "$1 {{PLURAL:$1|modiffica|modiffiche}}",
+       "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|da l'urtima vixita}}",
        "enhancedrc-history": "cronologia",
        "recentchanges": "Ùrtimi cangiamenti",
        "recentchanges-legend": "Opçioin di ùrtimi cangiaménti",
-       "recentchanges-summary": "Questa pàgina a g'ha di càngi ciù reçenti a-i contegnûi do scîto.",
-       "recentchanges-feed-description": "Questo feed o g'ha di cangiaménti ciù reçenti a-i contegnûi do scîto.",
+       "recentchanges-summary": "Questa pagina a te mostra i cangiamenti ciu reçenti a-i contegnui do scito.",
+       "recentchanges-noresult": "Nisciun-a modiffica durante o periodo inseio ch'a soddisfe sti critei.",
+       "recentchanges-feed-description": "Questo feed o te mostra i cangiamenti ciu reçenti a-i contegnui do scito.",
        "recentchanges-label-newpage": "Sto cangiaménto o l'à creòu 'na pàgina nêuva",
        "recentchanges-label-minor": "Cangiamento minô (m)",
        "recentchanges-label-bot": "Sto cangiaménto o l'à fæto in bot",
        "recentchanges-label-plusminus": "Variassion da paggina in nummero de byte",
        "recentchanges-legend-heading": "<strong>Legenda:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (veddi e [[Special:NewPages|neuve paggine]])",
-       "rcnotefrom": "Chì sotta gh'è i cangiamenti fæti comensando da '''$2''' (scin a '''$1''').",
+       "recentchanges-submit": "Fanni vedde",
+       "rcnotefrom": "Chì sotta gh'è {{PLURAL:$5|o cangiamento|i cangiamenti}} a partî da <strong>$3, $4</strong> (scin a '''$1''').",
        "rclistfrom": "Fanni vedde e modiffiche apportæ partindo da $3 $2",
-       "rcshowhideminor": "$1 cangiaménti minoi",
+       "rcshowhideminor": "$1 cangiaménti minoî",
        "rcshowhideminor-show": "Fanni vedde",
        "rcshowhideminor-hide": "Ascondi",
        "rcshowhidebots": "$1 bot",
        "rcshowhidebots-show": "Fanni vedde",
        "rcshowhidebots-hide": "Ascondi",
        "rcshowhideliu": "$1 i utenti registræ",
+       "rcshowhideliu-show": "Fanni vedde",
        "rcshowhideliu-hide": "Ascondi",
        "rcshowhideanons": "$1 utenti anonnimi",
        "rcshowhideanons-show": "Fanni vedde",
        "rcshowhideanons-hide": "Ascondi",
        "rcshowhidepatr": "$1 i cangiaménti controllæ",
+       "rcshowhidepatr-show": "Fanni vedde",
+       "rcshowhidepatr-hide": "Ascondi",
        "rcshowhidemine": "$1 i mæ cangiamenti",
        "rcshowhidemine-show": "Fanni vedde",
        "rcshowhidemine-hide": "Ascondi",
+       "rcshowhidecategorization": "$1 categorizzaçion da pagina",
+       "rcshowhidecategorization-show": "Fanni vedde",
+       "rcshowhidecategorization-hide": "Ascondi",
        "rclinks": "Fanni vedde i $1 cangiaménti ciù reçenti fæti inti ùrtimi $2 giorni<br />$3",
        "diff": "diff",
        "hist": "stö",
        "minoreditletter": "m",
        "newpageletter": "N",
        "boteditletter": "b",
+       "number_of_watching_users_pageview": "[osservâ da {{PLURAL:$1|un utente|$1 utenti}}]",
+       "rc_categories": "Limite a-e categorie (separæ da \"|\"):",
        "rc_categories_any": "Quâ-se-sæ fra quelle indicæ",
        "rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} doppo a modiffica",
        "newsectionsummary": "/* $1 */ neuva seçion",
        "recentchangeslinked-page": "Nómme da pàgina:",
        "recentchangeslinked-to": "Fanni védde sôlo i cangiaménti a-e pàgine colegæ a-a pàgina specificâ",
        "recentchanges-page-added-to-category": "[[:$1]] azonto a-a categoria",
-       "recentchanges-page-added-to-category-bundled": "[[:$1]] e [[Special:WhatLinksHere/$1|{{PLURAL:$2|una paggina a l'è azonta|$2 paggine son azonte}}]] a-a categoria",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] azonta a-a categoria, [[Special:WhatLinksHere/$1|questa pagina a l'è inclusa a l'interno di atre pagine]]",
        "recentchanges-page-removed-from-category": "[[:$1]] rimosso da-a categoria",
-       "recentchanges-page-removed-from-category-bundled": "[[:$1]] e {{PLURAL:$2|una paggina a l'è rimossa|$2 paggine son rimosse}} da-a categoria",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] rimossa da-a categoria, [[Special:WhatLinksHere/$1|questa pagina a l'è inclusa a l'interno di atre pagine]]",
        "autochange-username": "Modiffica aotomattica MediaWiki",
        "upload": "Carrega 'n file",
        "uploadbtn": "Carrega 'n file",
        "reuploaddesc": "Torna a-o moddulo pe-o caregamento.",
        "upload-tryagain": "Invia a descrission do file modificou",
        "uploadnologin": "No t'ê introu",
+       "uploadnologintext": "Pe caregâ  di file bezoeugna $1.",
+       "upload_directory_missing": "A directory de upload ($1) a no l'existe e a no poeu ese creâ da-o server web.",
+       "upload_directory_read_only": "O server web o no l'è in graddo de scrive inta directory de upload ($1).",
+       "uploaderror": "Errô into caregamento",
+       "upload-recreate-warning": "<strong>Attençion: un file con questo nomme o l'è stæto scassou o mesciou.</strong>\nO registro de scassatue e di stramui de questa pagina o l'è riportou chì pe comoditæ:",
+       "uploadtext": "Doeuviâ o modulo sottostante pe caregâ di noeuvi file. Pe visualizzâ ò çercâ i file za caregæ, consultâ o [[Special:FileList|log di file caregæ]]. Caregamenti de file e de noeuve verscioin de file son registræ into [[Special:Log/upload|log di upload]], e scassatue into [[Special:Log/delete|log de scassatue]].\n\nPe insei un file a l'interno de 'na pagina, fanni un collegamento de questo tipo:\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></code>''' pe doeuviâ a verscion completa do file\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|testo alternativo]]</nowiki></code>''' pe doeuviâ una verscion larga 200 pixel inseia inte 'n box, alliniâ a scinistra e con 'testo alternativo' comme didascalia\n* '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code>''' pe generâ un collegamento diretto a-o file sença vixualizzâlo",
+       "upload-permitted": "{{PLURAL:$2|Tipo de file consentio|Tipi de file consentii}}: $1.",
+       "upload-preferred": "{{PLURAL:$2|Tipo de file consegiou|Tipi de file consegiæ}}: $1.",
+       "upload-prohibited": "{{PLURAL:$2|Tipo de file non consentio|Tipi de file non consentii}}: $1.",
        "uploadlogpage": "Log di file caregæ",
+       "uploadlogpagetext": "De sotta gh'è una lista di urtimi file caregæ.\nConsultâ a [[Special:NewFiles|galleria di noeuvi file]] pe 'na vixon d'insemme.",
        "filename": "Nomme do file",
        "filedesc": "Detaggi",
+       "fileuploadsummary": "Detaggi do file:",
+       "filereuploadsummary": "Cangiamenti a-o file:",
+       "filestatus": "Informaçioin in sciô copyright:",
        "filesource": "Reixe:",
+       "ignorewarning": "Ignora l'avviso e sarva comunque o file",
+       "ignorewarnings": "Ignora i messaggi de avvertimento do scistema",
+       "minlength1": "O nome do file o dev'ese a-o manco de una lettia.",
+       "illegalfilename": "O nomme \"$1\" o conten di caratteri non ammissi inti tittoli de paggine. Dagghe 'n atro nomme e proeuva torna a caregâlo.",
+       "filename-toolong": "I nommi di file no poeuan superâ i 240 byte.",
        "badfilename": "O nomme do file o l'è stæto cangiòu in \"$1\".",
-       "fileexists": "Un papê co sto nomme o existe de zà, pe piaxei da unn'euggiâ a <strong>[[:$1]]</strong> se non ti tei seguo de voleilo cangiâ.\n[[$1|thumb]]",
-       "fileexists-forbidden": "Un papê co sto nomme o existe de zà, pe piaxei vanni in derrê e carega sto papê co un ätro nomme. [[File:$1|thumb|center|$1]]",
+       "filetype-mime-mismatch": "L'estenscion do file \".$1\" a no corrisponde a-o tipo MIME rilevou da-o file ($2).",
+       "filetype-badmime": "No l'è consentio de caregâ di file de tipo MIME \"$1\".",
+       "filetype-bad-ie-mime": "Imposcibile caregâ o file perché Internet Explorer o rilevieiva comme \"$1\", ch'o l'è un tipo de file non consentio e potençialmente peigoso.",
+       "filetype-unwanted-type": "Caregâ di file de tipo '''\".$1\"''' l'è sconsegiou. {{PLURAL:$3|O tipo de file consegiou o l'è|I tipi de file consegiæ son}} $2.",
+       "filetype-banned-type": "'''\".$1\"''' {{PLURAL:$4|o no l'è un tipo de file consentio|no son di tipi de file consentii}}. {{PLURAL:$3|O tipo de file consentio o l'è|I tipi de file consentii son}} $2.",
+       "filetype-missing": "O file o no g'ha d'estenscion (pres. \".jpg\").",
+       "empty-file": "O file che t'hæ inviou o l'è voeuo.",
+       "file-too-large": "O file che t'hæ inviou o l'è troppo grande.",
+       "filename-tooshort": "O nomme do file o l'è troppo curto.",
+       "filetype-banned": "Questo tipo de file o l'è proibio.",
+       "verification-error": "Questo file o no l'ha superou a veriffica.",
+       "hookaborted": "A modiffica che t'hæ çercou de fâ a l'è stæta interrotta da un'estenscion.",
+       "illegal-filename": "O nomme do file o no l'è ammisso.",
+       "overwrite": "Soviascrive un file existente no l'è permisso.",
+       "unknown-error": "Gh'è stæto un aro sconosciuo.",
+       "tmp-create-error": "Imposcibbile creâ o file temporannio.",
+       "tmp-write-error": "Errô de scrittua do file temporannio.",
+       "large-file": "Se raccomanda de no superâ e dimenscioin de $1 pe ciascun file; questo file o l'è grande $2.",
+       "largefileserver": "O file o suppera e dimenscioin consentie da-a configuaçion do server.",
+       "emptyfile": "O file apen-a caregou pâ esee voeuo. Questo poriæ ese dovuo a un aro into nomme do file. Controlla se ti voeu davei caregâ sto file.",
+       "windows-nonascii-filename": "Questo wiki o no supporta di nommi de file con di caratteri speciali.",
+       "fileexists": "Un papê con sto nomme o l'existe za, pe piaxei danni 'n'euggiâ a <strong>[[:$1]]</strong> se no ti t'ê seguo de voeilo cangiâ.\n[[$1|thumb]]",
+       "filepageexists": "A pagina de descriçion de questo file a l'è za stæta creâ a l'adreçço <strong>[[:$1]]</strong>, sciben che no ghe segge ancon un file con questo nomme. A descriçion de l'oggetto inseia in fase de caregamento a no l'appariâ in scia pagina de descriçion. Pe fâ scì che l'oggetto o comparisce in sciâ pagina de descriçion, saiâ necessaio modificala manualmente.\n[[$1|thumb]]",
+       "fileexists-extension": "Un file co-in nomme scimile a questo o l'esiste za: [[$2|thumb]]\n* Nomme do file caregou: <strong>[[:$1]]</strong>\n* Nomme do file existente: <strong>[[:$2]]</strong>\nTi voeu miga çerne un nomme ciu caratteristego?",
+       "fileexists-thumbnail-yes": "O file caregou o pâ ese una miniatua ''(thumbnail)''. [[$1|thumb]]\nVerifica, pe confronto, il file <strong>[[:$1]]</strong>.\nSe se tratta da mæxima inmaggine, inte dimenscioin originale, no l'è necessaio caregâne un'atra in miniatua.",
+       "file-thumbnail-no": "O nomme do file comença con <strong>$1</strong>; pâ ch'o segge un'inmaggine de dimenscioin redute ''(miniatua)''.\nSe ti dispon-i del'immaggine inta risoluçion originale, carreghila. Sedunque, pe piaxei, cangighe o nomme.",
+       "fileexists-forbidden": "Un file con questo nomme o l'existe za e o no poeu ese soviascrito. Se ti voeu caregâ o to file, torna inderê e dagghe un atro nomme. [[File:$1|thumb|center|$1]]",
+       "fileexists-shared-forbidden": "Un file con questo nomme o l'esiste za inte l'archivio de risorse multimediæ condivise. Se ti dexiddei ancon caregâ o file, torna inderê e modifica o nomme co-o quæ caregâ o file. [[File:$1|thumb|center|$1]]",
+       "file-exists-duplicate": "Questo file o l'è un dupricou {{PLURAL:$1|do seguente|di seguenti}} file:",
+       "file-deleted-duplicate": "Un file identico a questo ([[:$1]]) o l'è stæto scassou into passou. Verifica a cronologia de scassatue primma de caregâlo torna.",
+       "file-deleted-duplicate-notitle": "Un file identico a questo o l'è stæto scassou into passou, e o tittolo o l'è stæto soppresso. Domanda a quarcun ch'o g'ha a poscibilitæ de vedde i file soppresci de esaminâ a scituaçion primma de procede torna a-o caregamento.",
+       "uploadwarning": "Avviso de caregamento",
+       "uploadwarning-text": "Pe piaxei modifica chi de sotta a descriçion do file e proeuva torna.",
        "savefile": "Sarva o file",
-       "uploaddisabledtext": "In {{SITENAME}} non se peu caregâ de papê.",
+       "uploaddisabled": "O caregamento di file o l'è disabilitou.",
+       "copyuploaddisabled": "O caricamento tramite URL o l'è disabilitou.",
+       "uploaddisabledtext": "O caricamento di file o l'è disabilitou.",
+       "php-uploaddisabledtext": "O caregamento di file tramite PHP o l'è disabilitou. Controlla a configuaçion de file_uploads.",
+       "uploadscripted": "Questo file o conten un codiçe HTML ò de script, ch'o poriæ ese interpretou erroniamente da un browser web.",
+       "upload-scripted-pi-callback": "Imposcibile caregâ un file ch'o conten un'instruçion de elaboaçion in XML-stylesheet.",
+       "uploaded-script-svg": "Trovou elemento de script \"$1\" into file caregou in formato SVG.",
+       "uploaded-hostile-svg": "Trovou CSS no seguo inte l'elemento de stile do file in formato SVG caregou.",
+       "uploaded-event-handler-on-svg": "Impostâ i attributi de gestion di eventi <code>$1=\"$2\"</code> no l'è consentio inti file SGV",
+       "uploaded-href-attribute-svg": "i attributi href inti file SVG poeuan collegâse solo verso e destinaçioin http:// o https://, trovou <code>&lt;$1 $2=\"$3\"&gt;</code>.",
+       "uploaded-href-unsafe-target-svg": "Trovou href a dæti non segui: destinaçion URI <code>&lt;$1 $2=\"$3\"&gt;</code> caregou into file SVG",
+       "uploaded-animate-svg": "Trovou o tag \"animate\" ch'o poriæ cangiâ href, doeuviando l'attributo \"from\" <code>&lt;$1 $2=\"$3\"&gt;</code> into file SVG caregou.",
+       "uploaded-setting-event-handler-svg": "A configuaçion di attributi pe-o gestô di eventi a l'è bloccâ, trovou <code>&lt;$1 $2=\"$3\"&gt;</code> into file SVG caregou.",
+       "uploaded-setting-href-svg": "L'utilizzo do tag \"set\" pe azonze l'attributo \"href\" a l'elemento parentâ o l'è bloccou.",
+       "uploaded-wrong-setting-svg": "L'uso de l'elemento \"set\" pe azonze una destinaçion remote/data/script pe qua-se-sæ attributo o l'è bloccou. Trovou <code>&lt;set to=\"$1\"&gt;</code> into file SVG caregou.",
+       "uploaded-setting-handler-svg": "o SVG ch'o l'imposta l'attributo \"handler\" con remote/data/script o l'è bloccou. Trovou <code>$1=\"$2\"</code>into file SVG caregou.",
+       "uploaded-remote-url-svg": "o SVG ch'o l'imposta qua-se-sæ attributo de stile con di URL remoti o l'è bloccato. Trovou <code>$1=\"$2\"</code> into file SVG caregou.",
+       "uploaded-image-filter-svg": "Trovou filtro immaggine con URL: <code>&lt;$1 $2=\"$3\"&gt;</code> into file in formato SVG caregou.",
+       "uploadscriptednamespace": "Questo file SVG o conten un namespace '$1' non consentio",
+       "uploadinvalidxml": "O codiçe XML into file caregou o no poeu ese elaboou.",
        "uploadvirus": "Questo file o conten un virus! Dettaggi: $1",
+       "uploadjava": "Questo file o l'è un file ZIP ch'o conten un file .class Java.\nCaregâ i file Java no l'è consentio, perché poeuan caosâ l'aggiamento de restriçioin de segueçça.",
+       "upload-source": "File de origine",
        "sourcefilename": "Nomme do file d'origgine:",
+       "sourceurl": "URL de origine:",
        "destfilename": "Nomme do file de destinassion:",
+       "upload-maxfilesize": "Dimenscion mascima do file: $1",
+       "upload-description": "Descriçion do file",
+       "upload-options": "Opçioin de caregamento",
+       "watchthisupload": "Metti sotta oservaçion",
+       "filewasdeleted": "Un file con questo nomme o l'è stæto za caregou e scassou into passou. Controlla $1 primma de caregâlo torna.",
+       "filename-thumb-name": "Sto chì o pâ un tittolo da miniatua. Pe piaxei no stanni a caregâ e miniatue in scia mæxima wiki. O dunque, corezi o nomme do file de moddo ch'o segge ciù scignificativo e o no l'agge o prefisso da miniatua.",
+       "filename-bad-prefix": "O nomme do file che t'ê aproeuvo a caregâ o comença con '''\"$1\"''', ch'o l'è un nomme generico scimile a quelli assegnæ aotomaticamente da-e fotocammie digitale. Pe piaxei çerni un nomme ciù descrittivo pe-o to file.",
+       "upload-proto-error": "Protocollo errou",
+       "upload-proto-error-text": "Pe l'upload remoto l'è necessaio speçificâ di URL che començan con <code>http://</code> oppû <code>ftp://</code>.",
        "upload-file-error": "Errô interno",
+       "upload-file-error-text": "S'è veificou un errô interno durante a creaçion de 'n file temporannio in sciô server. Contatta un [[Special:ListUsers/sysop|amministratô]].",
+       "upload-misc-error": "Errô de caregamento sconosciuo",
+       "upload-misc-error-text": "S'è veificou un errô non identificou durante o caregamento do file. Controlla che a URL a segge corretta e accescibile e proeuva torna. Se o problema o persciste, contatta un [[Special:ListUsers/sysop|amministratô]].",
+       "upload-too-many-redirects": "L'URL o contegniva troppi redirect",
+       "upload-http-error": "S'è verificou un errô HTTP: $1",
+       "upload-copy-upload-invalid-domain": "No l'è consentio o caregamento de coppie da questo dominnio.",
+       "upload-foreign-cant-upload": "Questo wiki o no l'è configuou pe caregâ i file into repository de file esterno domandou.",
+       "upload-foreign-cant-load-config": "Imposcibbile caregâ a configuaçion pe-i upload de file inti file repository esterni.",
+       "upload-dialog-disabled": "O caregamento di file tramite questo barcon de dialogo o l'è disabilitou inte questo wiki.",
+       "upload-dialog-title": "Carrega file",
+       "upload-dialog-button-cancel": "Anulla",
+       "upload-dialog-button-done": "Fæto",
+       "upload-dialog-button-save": "Sarva",
+       "upload-dialog-button-upload": "Carrega",
+       "upload-form-label-infoform-title": "Detaggi",
+       "upload-form-label-infoform-name": "Nomme",
+       "upload-form-label-infoform-name-tooltip": "Un tittolo unnico e descritivo pe-o file, ch'o serviâ da nomme. Doeuviâ pure e toeu parolle e i spaççi. No stagh'a mette l'estenscion.",
+       "upload-form-label-infoform-description": "Descriçion",
+       "upload-form-label-infoform-description-tooltip": "Descrivi scinteticamente tutto quanto sæ degno de notta a propoxito de quest'oeuvia.\nPe-e foto, indica e cose prinçipæ che gh'en rappresentæ, l'öcaxon e/ò o posto.",
+       "upload-form-label-usage-title": "Utilizzo",
+       "upload-form-label-usage-filename": "Nomme do file",
+       "upload-form-label-own-work": "Questo o l'è travaggio mæ",
+       "upload-form-label-infoform-categories": "Categorie",
+       "upload-form-label-infoform-date": "Dæta",
+       "upload-form-label-own-work-message-generic-local": "Confermo che son aproeuvo a caregâ sto file segondo e condiçioin de serviçio e e polittiche in sce e liçençe de {{SITENAME}}.",
+       "upload-form-label-not-own-work-message-generic-local": "Se non t'ê in grou de caregâ o file segondo e politiche de {{SITENAME}}, særa sto barcon e proeuva un atro mettodo.",
+       "upload-form-label-not-own-work-local-generic-local": "Proeuva ascì a [[Special:Upload|pagina de caregamento predefinia]].",
+       "upload-form-label-own-work-message-generic-foreign": "Ho acapio che son aproeuvo a caregâ questo file inte 'n archivio condiviso. Confermo che-o façço segondo e so condiçioin de serviççio e-e so polittiche in sce e liçençe.",
+       "upload-form-label-not-own-work-message-generic-foreign": "Se non t'ê in grou de caregâ o file segondo e politiche de l'archivvio condiviso, særa sto barcon e proeuva un atro mettodo.",
+       "upload-form-label-not-own-work-local-generic-foreign": "Ti poeu provâ ascì a doeuviâ a [[Special:Upload|pagina de caregamento insce {{SITENAME}}]], se questo file o poeu ese caregou segondo e soeu politiche.",
+       "backend-fail-stream": "Imposcibile trasmette o file $1.",
+       "backend-fail-backup": "Imposcibile eseguî o backup do file $1 .",
+       "backend-fail-notexists": "O file $1 o no l'existe.",
+       "backend-fail-hashes": "Imposcibile ötegnî l'hash di file pe 'n confronto.",
+       "backend-fail-notsame": "Existe za un file non identico a \"$1\".",
+       "backend-fail-invalidpath": "$1 o no l'è un percorso de archiviaçion vallido.",
+       "backend-fail-delete": "Imposcibile scassâ o file $1.",
+       "backend-fail-describe": "Imposcibile modificâ i metadæti do file \"$1\".",
+       "backend-fail-alreadyexists": "O file $1 o l'existe za.",
+       "backend-fail-store": "Imposcibbile memorizzâ o file  $1  in  $2 .",
+       "backend-fail-copy": "Imposcibile copiâ o file  $1  in  $2 .",
+       "backend-fail-move": "Imposcibile messciâ o file  $1  in  $2 .",
+       "backend-fail-opentemp": "Imposcibbile arvî o file temporannio.",
+       "backend-fail-writetemp": "Imposcibbile scrive o file temporannio.",
+       "backend-fail-closetemp": "Imposcibbile serâ o file temporannio.",
+       "backend-fail-read": "Imposcibile leze o file  $1 .",
+       "backend-fail-create": "Imposcibile scrive o file $1.",
+       "backend-fail-maxsize": "Imposcibile scrive o file $1 perché o l'è ciù grande de {{PLURAL:$2|un|$2}} byte.",
+       "backend-fail-readonly": "O backend de memoia \"$1\" o l'è attualmente de sola lettua. A raxon indicâ a l'è: <em>$2</em>",
+       "backend-fail-synced": "O file \"$1\" o l'è inte 'n stato non coerente inti backend de memoia interna.",
+       "backend-fail-connect": "Imposcibile connettise a-o backend de memoia \"$1\".",
+       "backend-fail-internal": "S'è verificou un errô sconosciuo into backend de memoia \"$1\".",
+       "backend-fail-contenttype": "Imposcibile determinâ a tipologia do file da archiviâ inte \"$1\".",
+       "backend-fail-batchsize": "O backend de memoia o l'ha programmou una serie de $1 {{PLURAL:$1|opiaçion|opiaçioin}} sciu file; o limmite o l'è de $2 {{PLURAL:$2|opiacion|opiacioin}}.",
+       "backend-fail-usable": "Imposcibile leze ò scrive o file \"$1\" a caosa de aetorizzaçioin insuffixenti ò directory/contegnitoî mancanti.",
+       "filejournal-fail-dbconnect": "Imposcibile connettise a-o database journal pe l'archiviaçion back-end \"$1\".",
+       "filejournal-fail-dbquery": "Imposcibile aggiornâ o database journal pe l'archiviaçion back-end \"$1\".",
+       "lockmanager-notlocked": "Imposcibile sbloccâ \"$1\"; o no l'è bloccou.",
+       "lockmanager-fail-closelock": "Imposcibbile serâ o file de blocco pe \"$1\".",
+       "lockmanager-fail-deletelock": "Imposcibile scassâ o file de blocco pe $1.",
+       "lockmanager-fail-acquirelock": "Imposcibbile aquixî o blocco pe $1.",
+       "lockmanager-fail-openlock": "Imposcibbile arvî o file de blocco pe \"$1\".",
+       "lockmanager-fail-releaselock": "Imposcibile revocâ o blocco pe $1.",
+       "lockmanager-fail-db-bucket": "Imposcibile contattâ i necessai database de blocco into bucket $1.",
+       "lockmanager-fail-db-release": "Imposcibile revocâ i blocchi in sciô database $1.",
+       "lockmanager-fail-svr-acquire": "Imposcibile acquixî i blocchi in sciô server $1.",
+       "lockmanager-fail-svr-release": "Imposcibile revocâ i blocchi in sciô server $1.",
+       "zip-file-open-error": "S'è verificou 'n errô durante l'avertua do file pe-i controlli ZIP.",
+       "zip-wrong-format": "O file specificou o no l'ea un file ZIP.",
+       "zip-bad": "O file o l'è un file ZIP corrotto ò atrimenti illezibbile.\nO no poeu ese controllou comme se dè pe-a segueçça.",
+       "zip-unsupported": "O file o l'è un file ZIP ch'o l'adoeuvia de caratteristeghe ZIP non supportæ da MediaWiki.\nO no poeu ese ben controllou pe-a segueçça.",
+       "uploadstash": "Carrega stash",
+       "uploadstash-summary": "Questa pagina a consente l'accesso a-i file che son stæti caregæ (o son in fase de caregamento) ma che n'en ancon stæti pubbricæ in sciô wiki. Questi file son vixibbili solo a l'utente ch'o i ha caregæ.",
+       "uploadstash-clear": "Elimmina i file in stash",
+       "uploadstash-nofiles": "No ti g'hæ de file into stash.",
+       "uploadstash-badtoken": "Esecuçion de l'açion non riuscia, foscia perché e to credençiale de modiffica son descheite. Proeuva ancon.",
+       "uploadstash-errclear": "O nettezzo di file o no l'è ariescio.",
+       "uploadstash-refresh": "Aggiorna l'elenco di file",
+       "uploadstash-thumbnail": "veddi miniatua",
+       "invalid-chunk-offset": "Offset d'a parte non vallido.",
+       "img-auth-accessdenied": "Accesso negou",
+       "img-auth-nopathinfo": "PATH_INFO mancante.\nO server o no l'è impostou pe passâ quest'informaçion.\nO poriæ ese basou insce CGI e o no poeu supportâ img_auth.\nAmia https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
+       "img-auth-notindir": "O percorso domandou o no l'è inta directory de upload configuâ.",
+       "img-auth-badtitle": "Imposcibile construe un tittolo vallido da \"$1\".",
+       "img-auth-nologinnWL": "No t'ê introu e \"$1\" o no l'è inta whitelist.",
+       "img-auth-nofile": "O file \"$1\" o no l'existe.",
+       "img-auth-isdir": "Ti çerchi d'accede a 'na directory \"$1\".\nL'è consentio solo l'accesso a-i file.",
+       "img-auth-streaming": "\"$1\" in streaming.",
+       "img-auth-public": "La fonçion de img_auth.php a l'è de dâ in output di file da un scito wiki privou.\nQuesto scito o l'è configuou comme un wiki pubbrico.\nPe 'na segueçça ottimale, img_auth.php o l'è disattivou.",
+       "img-auth-noread": "L'utente o no g'ha accesso a-a lettua de \"$1\".",
+       "http-invalid-url": "URL non vallido: $1",
+       "http-invalid-scheme": "Di URL co-o prefisso \"$1\" no son supportæ.",
+       "http-request-error": "Recesta HTTP fallia a caosa de 'n aro sconosciuo.",
+       "http-read-error": "Errô de lettua HTTP.",
+       "http-timed-out": "Recesta HTTP descheita.",
+       "http-curl-error": "Errô durante o recuppero de l'URL: $1",
+       "http-bad-status": "S'è verificou un problema durante a recesta HTTP: $1 $2",
+       "upload-curl-error6": "URL no razonzibbile",
+       "upload-curl-error6-text": "Imposcibile razonze a URL specificâ. Verifica che a URL a sæ scrita correttamente e che o scito in question o sæ attivo.",
+       "upload-curl-error28": "Tempo descheito pe l'upload",
+       "upload-curl-error28-text": "O scito remoto o l'ha impiegou troppo tempo a risponde. Verifica che o sito o sæ attivo, attendi quarche menuto e proeuva torna, se mai inte 'n momento con meno traffego.",
        "license": "Licensa:",
        "license-header": "Licensa",
        "nolicense": "Nisciûnn-a liçensa indicâa",
+       "licenses-edit": "Modiffica opçioin de liçença",
+       "license-nopreview": "(Anteprimma non disponibbile)",
+       "upload_source_url": "(un file da un URL vallido e accescibile pubbricamente)",
+       "upload_source_file": "(un file da-o to computer)",
+       "listfiles-delete": "scassa",
+       "listfiles-summary": "Questa pagina speciale a mostra tutti i file caregæ.",
        "listfiles_search_for": "Çerca pe nomme de l'imàgine:",
+       "listfiles-userdoesnotexist": "L'utença \"$1\" a no l'è registrâ.",
        "imgfile": "file",
-       "listfiles": "Lista d'archivvi",
+       "listfiles": "Lista di file",
+       "listfiles_thumb": "Miniatua",
        "listfiles_date": "Dæta",
+       "listfiles_name": "Nomme",
+       "listfiles_user": "Utente",
+       "listfiles_size": "Dimenscion",
+       "listfiles_description": "Descriçion",
+       "listfiles_count": "Verscioin",
+       "listfiles-show-all": "Includdi e vege verscioin de inmaggine",
+       "listfiles-latestversion": "Verscion corente",
+       "listfiles-latestversion-yes": "Sci",
+       "listfiles-latestversion-no": "No",
        "file-anchor-link": "file",
        "filehist": "Stöia do file",
        "filehist-help": "Sciacca insce dæta/ôa pe amiâ o file comm'o s'apresentâva into momento indicòu.",
+       "filehist-deleteall": "scassa tutto",
+       "filehist-deleteone": "scassa",
        "filehist-revert": "Ripristina",
        "filehist-current": "Corrente",
        "filehist-datetime": "Dæta/Ôa",
        "filehist-thumb": "Miniatua",
        "filehist-thumbtext": "Miniatua da versción de $1",
+       "filehist-nothumb": "Nisciun-a miniatua",
        "filehist-user": "Utente",
        "filehist-dimensions": "Dimenscioin",
-       "filehist-filesize": "Dimension de l'archivvio",
+       "filehist-filesize": "Dimenscion do file",
        "filehist-comment": "Coménti",
        "imagelinks": "Ûzo do file",
        "linkstoimage": "{{PLURAL:$1|A segoente pàgina a contegne|E segoenti $1 pàgine contegnan}} colegaménti a-o file:",
+       "linkstoimage-more": "Ciù de $1 {{PLURAL:$1|pagina aponta|pagine apontan}} a questo file.\nA seguente lista a mostra {{PLURAL:$1|a primma paggina ch'a l'aponta|e primme $1 paggine ch'apontan}} a sto file.\nL'è disponibile un [[Special:WhatLinksHere/$2|elenco completo]].",
        "nolinkstoimage": "No gh'è nisciûnn-a pàgina collegâ con 'sto file.",
-       "sharedupload": "'St'archivvio o l'è condiviso; sajeiva a dî c'o pêu ese dêuviòu da ciû progetti wiki.",
+       "morelinkstoimage": "Vixualizza [[Special:WhatLinksHere/$1|di atri inganci]] a questo file.",
+       "linkstoimage-redirect": "$1 (rendriççamento file) $2",
+       "duplicatesoffile": "{{PLURAL:$1|O seguente file o l'è un dupricou|I seguenti $1 file son di dupricæ}} de questo file ([[Special:FileDuplicateSearch/$2|urteioî detaggi]]):",
+       "sharedupload": "Questo file o proven da $1 e o poeu ese doeuviou da di atri progetti.",
+       "sharedupload-desc-there": "Questo file o proven da $1 e o poeu ese doeuviou da di atri progetti.\nConsurta a [$2 pagina de descriçion do file] pe di urteioî informaçioin.",
        "sharedupload-desc-here": "Sto file o vegne da $1 e o peu êse dêuviòu inti âtri progetti.\nChì apreuvo ti peu védde a descriçión inta [$2 pàgina de descriçión do file].",
+       "sharedupload-desc-edit": "Questo file o proven da $1 e o poeu ese doeuviou da di atri progetti.\nFoscia ti voeu modificâ a descriçion presente inta [$2 pagina de descriçion do file].",
+       "sharedupload-desc-create": "Questo file o proven da $1 e o poeu ese doeuviou da di atri progetti.\nFoscia ti voeu modificâ a descriçion presente inta [$2 pagina de descriçion do file].",
+       "filepage-nofile": "No existe un file con sto nomme.",
+       "filepage-nofile-link": "No existe un file con sto nomme, ma l'è poscibile [$1 caregâlo].",
        "uploadnewversion-linktext": "Carrega 'na neuva verscion de sto file",
+       "shared-repo-from": "da $1",
+       "shared-repo": "un archivio condiviso",
        "upload-disallowed-here": "Imposcibbile sorvescrive sto file.",
+       "filerevert": "Ripristina $1",
+       "filerevert-legend": "Ripristina file",
+       "filerevert-intro": "Ti stæ pe ripristinâ o file '''[[Media:$1|$1]]''' a-a [$4 verscion do $2, $3].",
+       "filerevert-comment": "Raxon:",
+       "filerevert-defaultcomment": "Ripristinou a verscion do $2, $1 ($3)",
+       "filerevert-submit": "Ripristina",
+       "filerevert-success": "'''O file [[Media:$1|$1]]''' o l'è stæto ripristinou a-a [$4 verscion do $2, $3].",
+       "filerevert-badversion": "No gh'è de verscioin locali precedenti do file co-o timestamp provisto.",
+       "filedelete": "Scassa \"$1\"",
+       "filedelete-legend": "Scassa o file",
+       "filedelete-intro": "Ti stæ pe scassâ o file '''[[Media:$1|$1]]''' con tutta a so cronologia.",
+       "filedelete-intro-old": "T'ê aproeuvo a scassâ a vercsion de '''[[Media:$1|$1]]''' do [$4 $2, $3].",
+       "filedelete-comment": "Raxon:",
        "filedelete-submit": "Scassa",
+       "filedelete-success": "O file '''$1''' o l'è stæto scassou.",
+       "filedelete-success-old": "A verscion do file '''[[Media:$1|$1]]''' do $2, $3  a l'è stæta scassaa.",
+       "filedelete-nofile": "'''$1''' o no l'existe.",
+       "filedelete-nofile-old": "Verscioin d'archivio de '''$1''' co-e caratteristeghe indicæ no ghe n'è.",
+       "filedelete-otherreason": "Atra motivaçion ò motivaçion azontiva:",
+       "filedelete-reason-otherlist": "Un'atra raxon",
+       "filedelete-reason-dropdown": "*Raxoin comun-e de scançellaçion\n** Violaçion do drito d'aotô\n** File dupricou",
+       "filedelete-edit-reasonlist": "Modiffica e raxoin da scassatua",
+       "filedelete-maintenance": "Scançellaçion e ricuppero di file temporaniamente disattivæ durante a manutençion.",
+       "filedelete-maintenance-title": "Imposcibbile eliminâ o file",
        "mimesearch": "Çerca MIME",
+       "mimesearch-summary": "Questa pagina a consente de filtrâ i file in base a-o tipo MIME.\nInsei a stringa de riçerca inta forma tipo/sottotipo o tipo/*, pr'es. <code>image/jpeg</code>.",
+       "mimetype": "Tipo MIME:",
+       "download": "scarrega",
+       "unwatchedpages": "Paggine non öservæ",
        "listredirects": "Lista de rindirissamenti",
-       "unusedtemplates": "Template no ûtilissæ",
+       "listduplicatedfiles": "Lista di file doggi",
+       "listduplicatedfiles-summary": "Questo o l'è un elenco di file, donde a verscion ciù reçente de 'n file a l'è un dupricou da verscion ciù reçente de 'n atro file. Se piggia in  conscideraçion solo che i file locali.",
+       "listduplicatedfiles-entry": "[[:File:$1|$1]] o g'ha [[$3|{{PLURAL:$2|un duplicou|$2 duplicæ}}]].",
+       "unusedtemplates": "Template no ûtilizæ",
+       "unusedtemplatestext": "Inte sta pagina l'è elencou e pagine do namespace {{ns:template}} che no son incluse inte nisciun-a pagina. Primma de scassâle l'è öportun veificâ che i scingoli template no g'aggian di atri collegamenti intranti.",
+       "unusedtemplateswlh": "atri collegamenti",
        "randompage": "Pagina a brettìo",
-       "randomredirect": "Ûn rindirissamento a brettîo",
+       "randompage-nopages": "Pagine {{PLURAL:$2|into seguente|inti seguenti}} namespace no ghe n'è: $1.",
+       "randomincategory": "Pagina a brettio inta categoria",
+       "randomincategory-invalidcategory": "\"$1\" o no l'è un nomme de categoria vallido.",
+       "randomincategory-nopages": "Paggine in [[:Category:$1]] no ghe n'è.",
+       "randomincategory-category": "Categoria:",
+       "randomincategory-legend": "Pagina a brettio inta categoria",
+       "randomincategory-submit": "Vanni",
+       "randomredirect": "Ûn rindirissamento a brettio",
+       "randomredirect-nopages": "Into namespace \"$1\" redirect no ghe n'è.",
        "statistics": "Statistiche",
+       "statistics-header-pages": "Statistiche de pagine",
+       "statistics-header-edits": "Statistiche de modifiche",
+       "statistics-header-users": "Statistiche di utenti",
+       "statistics-header-hooks": "Atre statistiche",
+       "statistics-articles": "Paggine de contegnui",
+       "statistics-pages": "Paggine",
+       "statistics-pages-desc": "Tutte e paggine do scito, compreiso e paggine de discuscion, i redirect, etç.",
+       "statistics-files": "File caregæ",
+       "statistics-edits": "Modifiche a partî dal l'installaçion de {{SITENAME}}",
+       "statistics-edits-average": "Meddia de modiffiche pe paggina",
+       "statistics-users": "[[Special:ListUsers|Utenti]] registræ",
+       "statistics-users-active": "Utenti attivi",
+       "statistics-users-active-desc": "Utenti che han effettuou un'açion {{PLURAL:$1|inte l'urtimo giorno|inti urtimi $1 giorni}}",
+       "pageswithprop": "Paggine co-ina propietæ de paggina",
+       "pageswithprop-legend": "Paggine co-ina propietæ de paggina",
+       "pageswithprop-text": "Questa pagina a fa 'na lista de paggine ch'adoeuvian una particolâ propietæ de paggina.",
+       "pageswithprop-prop": "Nomme da propietæ:",
+       "pageswithprop-submit": "Vanni",
+       "pageswithprop-prophidden-long": "valô testoale longo da propietæ ascoso ($1)",
+       "pageswithprop-prophidden-binary": "valô binaio da propietæ ascoso ($1)",
        "doubleredirects": "Rindirissamenti doggi",
+       "doubleredirectstext": "In questa pagina gh'è elencou e paggine che rendiriççan a di atre paggine de redirect.\nOgni riga a conten i collegamenti a-o primmo e a-o segondo redirect, oltre a-a primma riga de testo do segondo redirect che a l'uso o conten a paggina de destinaçion \"corretta\" a-a quæ doviæ puntâ o primmo redirect ascì.\nI redirect <del>scassæ</del> son stæti corretti.",
+       "double-redirect-fixed-move": "[[$1]] o l'è stæto mesciou.\nO l'è stato aggiornou aotomaticamente e oua o l'è un redirect a [[$2]].",
+       "double-redirect-fixed-maintenance": "Corretto aotomaticamente o redirect doggio da [[$1]] a [[$2]] into travaggio de manutençion.",
+       "double-redirect-fixer": "Correttô di redirect",
        "brokenredirects": "Rindirissamenti sballiæ",
-       "brokenredirectstext": "De sotta unn-a lista de reindirissi a pagine che non existàn:",
+       "brokenredirectstext": "I rendriççi chì de sotta colegan a de paggine inexistente:",
        "brokenredirects-edit": "cangia",
        "brokenredirects-delete": "scassa",
        "withoutinterwiki": "Paggine sensa interwiki",
-       "withoutinterwiki-summary": "'Ste paggine chì inzû no g'han nisciûn collegamento co-e verscioîn in âtre lengoe:",
-       "fewestrevisions": "Voxi con meno revixoîn",
+       "withoutinterwiki-summary": "E paggine chì de sotta no g'han nisciûn collegamento a-e verscioin in âtre lengoe:",
+       "withoutinterwiki-legend": "Prefisso",
+       "withoutinterwiki-submit": "Mostra",
+       "fewestrevisions": "Pagine con meno revixoin",
        "nbytes": "$1 {{PLURAL:$1|byte|byte}}",
+       "ncategories": "$1 {{PLURAL:$1|categoria|categorie}}",
+       "ninterwikis": "$1 {{PLURAL:$1|interwiki}}",
        "nlinks": "$1 {{PLURAL:$1|collegamento|collegamenti}}",
        "nmembers": "$1 {{PLURAL:$1|elemento|elementi}}",
+       "nmemberschanged": "$1 → $2 {{PLURAL:$2|elemento|elementi}}",
+       "nrevisions": "$1 {{PLURAL:$1|revixon|revixoin}}",
+       "nimagelinks": "Doeuviou inte $1 {{PLURAL:$1|paggina|paggine}}",
+       "ntransclusions": "Doeuviou inte $1 {{PLURAL:$1|paggina|paggine}}",
+       "specialpage-empty": "Questa paggina speciale a l'è attualmente voeua.",
        "lonelypages": "Paggine orfane",
+       "lonelypagestext": "E seguente paggine no son incluse ni colegæ a di atre paggine de {{SITENAME}}.",
        "uncategorizedpages": "Paggine sensa categorîa",
        "uncategorizedcategories": "Categorîe sensa categorîa",
        "uncategorizedimages": "Immaggini sensa categorîa",
        "uncategorizedtemplates": "Template sensa categorîa",
-       "unusedcategories": "Categorîe no ûtilissæ",
-       "unusedimages": "Archivvi no ûtilissæ",
+       "unusedcategories": "Categorie voeue",
+       "unusedimages": "File inutilizæ",
        "wantedcategories": "Categorîe domandæ",
        "wantedpages": "Paggine domandæ",
+       "wantedpages-summary": "Lista de paggine inexistente co-o ciu gran nummero de collegamenti a lô, escludendo e pagine ch'han solo che i rendiriççi che-e collegan. Pe 'n elenco de pagine inexistente che g'han di rendriççi che-e collegan, amia [[{{#special:BrokenRedirects}}|a lista di rendriççi erræ]].",
+       "wantedpages-badtitle": "Tittolo invallido into groppo di risultæ: $1",
+       "wantedfiles": "File domandæ",
+       "wantedfiletext-cat": "I seguenti file son in doeuvia, ma no existan. I file ospitæ inte di repository esterni porieivan esighe elencæ sciben che existan. Questi fasci poxitivi saian <del>barræ</del>. E pagine che incòrpoan i file che no existan son elencæ in [[:$1]].",
+       "wantedfiletext-cat-noforeign": "I seguenti file son in doeuvia, ma no existan. Inoltre, e pagine che incòrpoan questi file son elencæ inta [[:$1]].",
+       "wantedfiletext-nocat": "I seguenti file son reciamæ da wikilink, ma no existan. I file ospitæ inte di repository esterni porieivan ese elencæ sciben existenti. Questi fasci poxitivi saian <del>barræ</del>.",
+       "wantedfiletext-nocat-noforeign": "I seguenti file son in doeuvia, ma no existan.",
+       "wantedtemplates": "Template domandæ",
        "mostlinked": "Paggine ciû collegæ",
        "mostlinkedcategories": "Categorîe ciû collegæ",
-       "mostlinkedtemplates": "Template ciû dêuviæ",
-       "mostcategories": "Voxi con ciû categorîe",
+       "mostlinkedtemplates": "Paggine ciu incluse",
+       "mostcategories": "Voxe con ciû categorîe",
        "mostimages": "Immaggini con ciû collegamenti",
-       "mostrevisions": "Voxi con ciû revixoîn",
+       "mostinterwikis": "Paggine con ciu interwiki",
+       "mostrevisions": "Voxe con ciû revixoin",
        "prefixindex": "Indiçe arfabetico de voxe",
+       "prefixindex-namespace": "Tutte e paggine co-o prefisso do namespace $1",
+       "prefixindex-submit": "Mostra",
+       "prefixindex-strip": "Ascondi o prefisso inta lista",
        "shortpages": "Paggine ciû cûrte",
        "longpages": "Paggine ciû longhe",
        "deadendpages": "Paggine sensa sciortîa",
+       "deadendpagestext": "E seguente paggine no son collegæ a di atre paggine de {{SITENAME}}.",
        "protectedpages": "Paggine protette",
+       "protectedpages-indef": "Solo proteçioin indefinie",
+       "protectedpages-summary": "Questa paggina a l'elenca e paggine existente ch'en attoalmente protezue. Pe 'n elenco di tittoli protezui da-a creaçion, amia [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]].",
+       "protectedpages-cascade": "Solo proteçioin ricorscive",
+       "protectedpages-noredirect": "Ascondi redirect",
+       "protectedpagesempty": "Paggine protette con sti parametri pe-o momento no ghe n'è.",
+       "protectedpages-timestamp": "Dæta e oa",
+       "protectedpages-page": "Paggina",
+       "protectedpages-expiry": "Scadença",
+       "protectedpages-performer": "Protetta da l'utente",
+       "protectedpages-params": "Parammetri de proteçion",
+       "protectedpages-reason": "Raxon",
+       "protectedpages-submit": "Mostra paggine",
+       "protectedpages-unknown-timestamp": "Sconosciuo",
+       "protectedpages-unknown-performer": "Utente sconosciuo",
        "protectedtitles": "Tittoli protezûi",
+       "protectedtitles-summary": "Questa pagina a l'elenca i titoli che son attualmente protetti da-a creaçion. Pe 'n elenco de pagine existente che son protette, amia [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
+       "protectedtitlesempty": "Tittoli protezui con sti parammetri pe-o momento no ghe n'è.",
+       "protectedtitles-submit": "Mostra tittoli",
        "listusers": "Lista d'ûtenti",
+       "listusers-editsonly": "Mostra solo i utenti con di contributi",
+       "listusers-creationsort": "Ordina pe dæta de creaçion",
+       "listusers-desc": "Ordina in senso decrescente",
+       "usereditcount": "$1 {{PLURAL:$1|contributo|contributi}}",
        "usercreated": "{{GENDER:$3|Creòu/â}} o $1 a-a $2",
        "newpages": "Pagine ciù reçenti",
+       "newpages-submit": "Mostra",
+       "newpages-username": "Nomme utente",
        "ancientpages": "Paggine ciû vëgie",
        "move": "Mescia",
        "movethispage": "Mescia 'sta paggina",
+       "unusedimagestext": "I seguenti file existan ma no son doeuviæ inte nisciun-a paggina.\nNotta che di atri sciti web porieivan ese colegæ a 'n file co-in URL diretto, e coscì o poriæ ese inte sta lista sciuben ch'o segge in doeuvia.",
+       "unusedcategoriestext": "E seguente paggine de categoria existan, sciben che nisciun'atra paggina o categoria a-e doeuvie.",
+       "notargettitle": "Dæti mancanti",
+       "notargettext": "No t'hæ indicou una pagina o un utente con chi eseguî sta fonçion.",
+       "nopagetitle": "A pagina de destinaçion a no l'existe",
+       "nopagetext": "A pagina domandâ a no l'existe.",
        "pager-newer-n": "{{PLURAL:$1|1 ciù nêuvo|$1 ciù nêuvi}}",
        "pager-older-n": "{{PLURAL:$1|1 ciù vêgio|$1 ciù vêgi}}",
+       "suppress": "Soprimmi",
+       "querypage-disabled": "Questa pagina speciale a l'è disattivâ pe motivi de prestaçion.",
+       "apihelp": "Agiutto API",
+       "apihelp-no-such-module": "Moddulo \"$1\" non trovou.",
+       "apisandbox": "Paggina de proeuva API",
+       "apisandbox-jsonly": "Pe doeuviâ a paggina de proeuva API ghe voeu o JavaScript.",
+       "apisandbox-api-disabled": "E fonçionalitæ API son disabilitæ insce questo scito.",
+       "apisandbox-intro": "Doeuvia sta paggina pe fâ prattica co-e <strong>API web service MediaWiki</strong>.\nPe di urteioî detaggi de utilizzo de API, amia a [[mw:API:Main page|documentaçion API]]. Exempio: [https://www.mediawiki.org/wiki/API#A_simple_example ötegnî o contegnuo da paggina prinçipâ]. Seleçion-a un'açion pe vedde di atri exempi.\n\nNotta che, sciben che questa a segge 'na paggina pe-e proeuve, i açioin che ti esegui chì porieivan modificâ a wiki.",
+       "apisandbox-fullscreen": "Espandi pannello",
+       "apisandbox-fullscreen-tooltip": "Espandi o pannello sandbox pe impî o barcon do browser.",
+       "apisandbox-unfullscreen": "Mostra a pagina",
+       "apisandbox-unfullscreen-tooltip": "Reduxi o pannello sandbox, coscì che i collegamenti de navigaçion MediaWiki seggian disponibbili.",
+       "apisandbox-submit": "Inandia recesta",
+       "apisandbox-reset": "Nettezza",
+       "apisandbox-retry": "Ritenta",
+       "apisandbox-loading": "Caregamento de informaçioin pe-o moddulo API \"$1\"...",
+       "apisandbox-load-error": "S'è veificou un errô durante o caregamento de informaçioin pe-o moddulo API \"$1\": $2",
+       "apisandbox-no-parameters": "Questo modulo API o no g'ha de parammetri.",
+       "apisandbox-helpurls": "Collegamenti a-a guidda",
+       "apisandbox-examples": "Exempi",
+       "apisandbox-dynamic-parameters": "Parammetri azontivi",
+       "apisandbox-dynamic-parameters-add-label": "Azonzi parammetro:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Nomme do parammetro",
+       "apisandbox-dynamic-error-exists": "Un parammetro denominou \"$1\" o l'existe za.",
+       "apisandbox-deprecated-parameters": "Parammetri sconsegiæ",
+       "apisandbox-fetch-token": "Aoto-compilla o token",
+       "apisandbox-submit-invalid-fields-title": "Gh'è di campi che n'en vallidi",
+       "apisandbox-submit-invalid-fields-message": "Correzi i campi evidençiæ e riproeuva.",
+       "apisandbox-results": "Risultæ",
+       "apisandbox-sending-request": "Invio recesta de API...",
+       "apisandbox-loading-results": "Riceçion di risultæ de API in corso...",
+       "apisandbox-results-error": "S'è veificou un errô durante o caregamento da risposta a l'interrogaçion API: $1",
+       "apisandbox-request-url-label": "URL de recesta:",
+       "apisandbox-request-time": "Tempo richiesto: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Correzi token e reinvia",
+       "apisandbox-results-fixtoken-fail": "Imposcibile recuperâ o token \"$1\".",
+       "apisandbox-alert-page": "I campi insce questa pagina no son vallidi.",
+       "apisandbox-alert-field": "O valô de questo campo o no l'è vallido.",
        "booksources": "Fonte libraie",
        "booksources-search-legend": "Çerca e fonti",
        "booksources-isbn": "Codice ISBN:",
        "booksources-search": "Çerca",
-       "booksources-text": "De sotta unn-a lista de inganci a di ätri sciti che vendan libbri neuvi e vegi e che porrieivan avei ciu informaçioin in scî libbri che ti te çerchi",
-       "specialloguserlabel": "Ûtente:",
-       "speciallogtitlelabel": "Tittolo:",
+       "booksources-text": "De sotta unn-a lista d'inganci a di ätri sciti che vendan libbri neuvi e vegi e che porrieivan avei ciu informaçioin in scî libbri che ti çerchi",
+       "booksources-invalid-isbn": "O ISBN inserio pâ no ese vallido; controlla che no ghe segge stæto di ari into copiâlo da-a fonte originale.",
+       "specialloguserlabel": "Açion effettuâ da:",
+       "speciallogtitlelabel": "Açion effettuâ sciu (tittolo da paggina ò {{ns:user}}:Nomme utente):",
        "log": "Log",
-       "all-logs-page": "Tûtti i registri",
-       "alllogstext": "Presentaçion unega de tutti i registri do scito {{SITENAME}}.\nTi te peu strinza a vista se ti te çerni un tipo de registro, un nomme de un utente o de pagina.",
+       "logeventslist-submit": "Mostra",
+       "all-logs-page": "Tutti i registri pubbrichi",
+       "alllogstext": "Presentaçion combinaa de tutti i registri de {{SITENAME}}.\nL'è poscibile restrenze i critei de riçerca seleçionando o tipo de registro, l'utente ch'o l'ha eseguio l'açion, e/ò a pagina interessâ (entrambi i campi son senscibbili a-o maiuscolo/minuscolo).",
+       "logempty": "O registro o no conten di elementi corespondenti a-a riçerca.",
+       "log-title-wildcard": "Riçerca di titoli che començan con",
+       "showhideselectedlogentries": "Mostra/ascondi e voxe de registro seleçionæ",
+       "log-edit-tags": "Modifica i etichette de voxe de registro seleçionæ",
+       "checkbox-select": "Seleçion-a: $1",
+       "checkbox-all": "Tutto",
+       "checkbox-none": "Nisciun",
+       "checkbox-invert": "Inverti",
        "allpages": "Tûtte e paggine",
        "nextpage": "Proscima paggina ($1)",
        "prevpage": "Paggina preçedente ($1)",
        "allpagesfrom": "Fanni vedde e paggine comensando da:",
+       "allpagesto": "Mostra e paggine scin a:",
        "allarticles": "Tùtte e pàgine",
        "allinnamespace": "Tutte e pagine ($1 namespace)",
        "allpagessubmit": "Vanni",
        "allpagesprefix": "Fanni vedde e paggine che inissian con:",
        "allpagesbadtitle": "O tittolo dæto a-a paggina o non va ben, òpû o conten di prefissi inter-lengua o inter-wiki. O porriæ ascì contegnî un o ciù caratteri che inti tittoli no se peuan deuviâ.",
        "allpages-bad-ns": "\"$1\" o no ghe in {{SITENAME}}.",
+       "allpages-hide-redirects": "Ascondi i redirect",
+       "cachedspecial-viewing-cached-ttl": "T'ê aproeuvo a 'miâ 'na verscion de sta paggina memorizzâ inta cache, ch'a poeu ese vegia de $1 a-o mascimo.",
+       "cachedspecial-viewing-cached-ts": "T'ê aproeuvo a 'miâ 'na verscion de sta paggina memorizzâ inta cache, ch'a poriæ no ese do tutto aggiornâ.",
+       "cachedspecial-refresh-now": "Mostra a ciù reçente.",
        "categories": "Categorîe",
+       "categories-submit": "Mostra",
+       "categoriespagetext": "{{PLURAL:$1|A seguente categoria a conten|E seguente categorie contengnan}} de pagine o di file murtimediali.\nE [[Special:UnusedCategories|categorie voeue]] no son mostræ chì.\nAmia ascì e [[Special:WantedCategories|categorie richieste]].",
+       "categoriesfrom": "Mostra e categorie a partî da:",
+       "deletedcontributions": "Contributi utente scassæ",
+       "deletedcontributions-title": "Contributi utente scassæ",
+       "sp-deletedcontributions-contribs": "contribuçioin",
+       "linksearch": "Çerchia di inganci esterni",
+       "linksearch-pat": "Pattern de riçerca:",
+       "linksearch-ns": "Namespace:",
+       "linksearch-ok": "Çerca",
+       "linksearch-text": "L'è poscibbile doeuviâ di metacaratteri comme \"*.wikipedia.org\".\nL'è necessaio a-o manco un dominnio di primmo livello, presempio \"*.org\".<br />\n{{PLURAL:$2|Protocollo supportou|Protocolli supportæ}}: $1 (predefinio http:// se no l'è specificou nisciun protocollo).",
        "linksearch-line": "$1 colegòu a-a pagina $2",
+       "linksearch-error": "I metacarattei poeuan ese usati solo a-o prinçippio de l'indiriçço.",
+       "listusersfrom": "Mostra i utenti a partî da:",
        "listusers-submit": "Fanni vedde",
        "listusers-noresult": "Utente non trovöo.",
+       "listusers-blocked": "(bloccou)",
+       "activeusers": "Lista di utenti attivi",
+       "activeusers-intro": "Questo o l'è un elenco di utenti ch'han avuo quarche tipo d'attivitæ da $1 {{PLURAL:$1|giorno|giorni}} a questa parte.",
+       "activeusers-count": "$1 {{PLURAL:$1|açione|açioin}} {{PLURAL:$3|inte l'urtimo giorno|inti urtimi $3 giorni}}",
+       "activeusers-from": "Mostra i utenti a partî da:",
+       "activeusers-hidebots": "Ascondi i bot",
+       "activeusers-hidesysops": "Ascondi i amministratoî",
+       "activeusers-noresult": "Nisciun utente o risponde a-i critei impostæ.",
+       "activeusers-submit": "Mostra utenti attivi",
+       "listgrouprights": "Driti do groppo utente",
+       "listgrouprights-summary": "De seguito l'è elencou i groppi utente definii pe questo scito, co-i so driti d'accesso associæ.\nPorieiva esighe di [[{{MediaWiki:Listgrouprights-helppage}}|urteioî informaçioin]] in scî driti individoali.",
+       "listgrouprights-key": "Legenda:\n* <span class=\"listgrouprights-granted\">Drito assegnou</span>\n* <span class=\"listgrouprights-revoked\">Drito revocou</span>",
+       "listgrouprights-group": "Groppo",
+       "listgrouprights-rights": "Driti",
+       "listgrouprights-helppage": "Help:Driti do groppo",
        "listgrouprights-members": "(Elenco di membri)",
+       "listgrouprights-addgroup": "O poeu azonze {{PLURAL:$2|a-o groppo|a-i groppi}}: $1",
+       "listgrouprights-removegroup": "O poeu rimoeuve {{PLURAL:$2|da-o groppo|da-i groppi}}: $1",
+       "listgrouprights-addgroup-all": "O poeu azonze a tutti i groppi",
+       "listgrouprights-removegroup-all": "O poeu rimoeuve da tutti i groppi",
+       "listgrouprights-addgroup-self": "O poeu azonzise {{PLURAL:$2|a-o groppo|a-i groppi}}: $1",
+       "listgrouprights-removegroup-self": "O poeu rimoeuvise {{PLURAL:$2|da-o groppo|da-i groppi}}: $1",
+       "listgrouprights-addgroup-self-all": "O poeu azonzise a tutti i groppi",
+       "listgrouprights-removegroup-self-all": "O poeu rimoeuvise da tutti i groppi",
+       "listgrouprights-namespaceprotection-header": "Restriçioin pe namespace",
+       "listgrouprights-namespaceprotection-namespace": "Namespace",
+       "listgrouprights-namespaceprotection-restrictedto": "Drito ch'o consente a l'utente de modificâ",
+       "listgrants": "Assegnaçioin",
+       "listgrants-summary": "De seguito l'è riportou un elenco de concescioin, co-i so driti utente associæ. I utenti poeuan aotorizzâ e appricaçioin a doeuviâ a proppia utença, ma con di aotorizzaçioin limitæ in base a-e assegnaçioin che l'utente o l'ha dæto a l'appricaçion. Tuttavia, un'appricaçion ch'a l'agisce pe conto de 'n utente a no poeu effettivamente doeuviâ i driti di quæ l'utente o no dispon-e.\nGhe poriæ ese di [[{{MediaWiki:Listgrouprights-helppage}}|urteioî informaçioin]] in scî driti individoali.",
+       "listgrants-grant": "Assegnaçion",
+       "listgrants-rights": "Driti",
+       "trackingcategories": "Categorie de monitoraggio",
+       "trackingcategories-summary": "Questa pagina a l'elenca e categorie de monitoraggio che vegnan popolæ aotomaticamente da-o software MediaWiki. I so nommi poeuan ese cangiæ modificando i relativi messaggi de scistema into namespace {{ns:8}}.",
+       "trackingcategories-msg": "Categoria de monitoraggio",
+       "trackingcategories-name": "Nomme do messaggio",
+       "trackingcategories-desc": "Critei pe l'incluxon intaa categoria",
+       "restricted-displaytitle-ignored": "Pagine con di tittoli da vixualizzâ ignoræ",
+       "restricted-displaytitle-ignored-desc": "A pagina a g'ha un <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> ignorou perché a no l'è equivalente a l'effettivo tittolo da pagina.",
+       "noindex-category-desc": "A pagina a no l'è indiçizzâ da-i robot perché a conten a paola magica <code><nowiki>__NOINDEX__</nowiki></code> e a s'atroeuva inte 'n namespace donde tâ flag a l'è consentia.",
+       "index-category-desc": "A pagina a conten <code><nowiki>__INDEX__</nowiki></code> (e a s'atroeuva inte 'n namespace donde tâ flag a l'è consentia) e quindi a l'è indiçizâ da-i robot, sci ben che normalmente a no-o saieiva.",
+       "post-expand-template-inclusion-category-desc": "A dimenscion da pagina a saiâ ciù grande de <code>$wgMaxArticleSize</code> doppo avei espanso tutti i template, e coscì çerti template no son stæti espansci.",
+       "post-expand-template-argument-category-desc": "A pagina a saiâ ciù grande de <code>$wgMaxArticleSize</code> doppo aver espanso o parametro de un template (quarcosa tra træ parentexi graffe, comme <code>{{{Foo}}}</code>).",
+       "expensive-parserfunction-category-desc": "A pagina a l'adoeuvia troppe fonçioin parser (come <code>#ifexist</code>). Amia [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgExpensiveParserFunctionLimit Manual:$wgExpensiveParserFunctionLimit].",
+       "broken-file-category-desc": "A pagina a conten un collegamento interotto a un file (un collegamento pe incorpoâ un file quande questo o no l'existe).",
+       "hidden-category-category-desc": "Questa categoria a conten <code><nowiki>__HIDDENCAT__</nowiki></code> inta so pagina, o quæ o l'impedisce ch'a segge mostrâ, in moddo predefinio, into riquaddro di collegamenti a-e categorie de pagine.",
+       "trackingcategories-nodesc": "Nisciun-a descriçion disponibbile.",
+       "trackingcategories-disabled": "A categoria a l'è disabilitâ",
+       "mailnologin": "Nisciun adreçço a chi mandâ o messaggio",
+       "mailnologintext": "Pe inviâ di messaggi e-mail a di atri utenti l'è necessaio [[Special:UserLogin|accede a-o scito]] e avei registrou un adreçço vallido inte proppie [[Special:Preferences|preferençe]].",
        "emailuser": "Invia 'na email a st'utente chi",
-       "defemailsubject": "{{SITENAME}} posta elettronega",
-       "noemailtitle": "Nisciûn conto e-mail",
-       "emailfrom": "Da",
-       "emailto": "A",
-       "emailsubject": "Argumento",
-       "emailmessage": "Comunicaçion",
-       "emailsend": "Spèdi",
-       "emailccme": "Mandame unn-a copia do messagio co unn-a lettìa elettronega.",
-       "emailsent": "Lettìa elettronega spèdïa",
-       "emailsenttext": "A teua lettìa elettronega a l'è stæta spèdïa.",
+       "emailuser-title-target": "Invia un'email a questo {{GENDER:$1|utente}}",
+       "emailuser-title-notarget": "Invia una email a un utente",
+       "emailpagetext": "Doeuvia o moddulo sottostante pe inviâ un messaggio e-mail a l'{{GENDER:$1|utente}} indicou. L'adreçço speçificou inte [[Special:Preferences|preferençe]] do mittente o l'appariâ into campo \"Da:\" do messaggio pe consentî a-o destinataio de risponde direttamente.",
+       "defemailsubject": "Messaggio da {{SITENAME}} da l'utente \"$1\"",
+       "usermaildisabled": "e-mail utente disabilitâ",
+       "usermaildisabledtext": "No l'è poscibbile inviâ de e-mail a di atri utenti insce questo wiki",
+       "noemailtitle": "Nisciun adreçço e-mail",
+       "noemailtext": "Questo utente o no l'ha indicou un adreçço e-mail vallido.",
+       "nowikiemailtext": "Questo utente o l'ha scerto de no riçeive messaggi de posta elettronica da-i atri utenti.",
+       "emailnotarget": "Nomme utente do destinataio inexistente o non vallido.",
+       "emailtarget": "Inseisci o nomme utente do destinataio",
+       "emailusername": "Nomme utente",
+       "emailusernamesubmit": "Invia",
+       "email-legend": "Invia un messaggio e-mail a un atro utente de {{SITENAME}}",
+       "emailfrom": "Da:",
+       "emailto": "A:",
+       "emailsubject": "Sogetto:",
+       "emailmessage": "Messaggio:",
+       "emailsend": "Spedisci",
+       "emailccme": "Mandime una copia do messaggio a-o mæ adreçço.",
+       "emailccsubject": "Coppia do messaggio inviou a $1: $2",
+       "emailsent": "E-mail spedïa",
+       "emailsenttext": "A teu e-mail a l'è stæta spedïa.",
+       "emailuserfooter": "Questa email a l'è stæta {{GENDER:$1|inviâ}} da $1 a {{GENDER:$2|$2}} a traverso a fonçion \"{{int:emailuser}}\" insce {{SITENAME}}.",
+       "usermessage-summary": "Messaggio de scistema",
+       "usermessage-editor": "Messaggê de scistema",
        "watchlist": "Sotta osservassion",
        "mywatchlist": "Sotta oservaçion",
        "watchlistfor2": "Pe $1 $2",
-       "watchnologin": "Non ti t'æ entroö",
-       "addedwatchtext": "A paggina \"[[:$1]]\" a l'è stæta azzonta a-a pròpia [[Special:Watchlist|lista in osservaçion]]. De chì in avanti, i cangiamenti fæti a-a paggina e a-a sêu discûxon sajàn missi in lista lì; o tittolo da paggina o sajà scrîo in '''grascietto''' inta paggina di [[Special:RecentChanges|ûrtimi cangiamenti]] coscì ti o veddi megio. Se ti vêu eliminâla da-a lista in osservaçion ciû târdi, sciacca \"no seguî\" inscia barra de d'âto.",
-       "removedwatchtext": "A paggina \"[[:$1]]\" a l'è stæta scassâa da-a têu lista in osservaçion.",
+       "nowatchlist": "A lista di öservæ speciali a l'è voeua.",
+       "watchlistanontext": "Pe vixualizzâ e modificâ l'elenco di öservæ l'è necessaio eseguî l'accesso.",
+       "watchnologin": "Accesso non effettuou",
+       "addwatch": "Azonzi a-a lista sotta öservaçion",
+       "addedwatchtext": "\"[[:$1]]\" e a so paggina de discuscion son stæte azonte a-a proppia [[Special:Watchlist|lista di öservæ]].",
+       "addedwatchtext-short": "A pagina \"$1\" a l'è stæata azonta a-a proppia lista di öservæ.",
+       "removewatch": "Rimoeuvi da-i öservæ speciali",
+       "removedwatchtext": "\"[[:$1]]\" e a so paggina de discuscion son stæte rimosse da-a proppia [[Special:Watchlist|lista di öservæ]].",
+       "removedwatchtext-short": "A pagina \"$1\" a l'è stæata rimossa da-a proppia lista di öservæ.",
        "watch": "Metti sotta oservaçion",
        "watchthispage": "Vigilâ 'sta paggina",
        "unwatch": "Leva da sott'oservaçion",
-       "watchlist-details": "A lista d'oservaçión speçiâle a contegne {{PLURAL:$1|ina pàgina (co-a seu pàgina de discusción)|$1 de pàgine (co-e so pàgine de discusción)}}.",
-       "wlshowlast": "Famme vedde e ûrtime $1 ôe $2 giorni",
+       "unwatchthispage": "Smetti de öservâ",
+       "notanarticle": "Questa paggina a no l'è una voxe",
+       "notvisiblerev": "L'urtima revixon a l'è stæta scassâ",
+       "watchlist-details": "A lista di öservæ speciali a conten {{PLURAL:$1|una pagina (e a rispettiva pagina de discuscion)|$1 pagine (e e rispettive pagine de discuscion)}}.",
+       "wlheader-enotif": "A notiffica via email a l'è attiva.",
+       "wlheader-showupdated": "E paggine che son stæte modificæ doppo l'urtima vixita son evidençiæ in '''grascetto'''.",
+       "wlnote": "De sotta {{PLURAL:$1|a l'è elencâ a modifica ciù reçente apportâ|son elencæ e <strong>$1</strong> modifiche ciù reçente apportæ}} {{PLURAL:$2|inte l'urtima oa|inti urtime <strong>$2</strong> oe}}; i dæti son aggiornæ a-e $4 do $3.",
+       "wlshowlast": "Mostra i urtime $1 oe $2 giorni",
+       "watchlist-hide": "Ascondi",
+       "watchlist-submit": "Mostra",
+       "wlshowtime": "Periodo de tempo da vixualizzâ:",
+       "wlshowhideminor": "cangiamenti menoî",
+       "wlshowhidebots": "Bot",
+       "wlshowhideliu": "utenti registræ",
+       "wlshowhideanons": "utenti anonnimi",
+       "wlshowhidepatr": "cangiaménti controllæ",
+       "wlshowhidemine": "e mæ modiffiche",
+       "wlshowhidecategorization": "categorizzaçion da paggina",
        "watchlist-options": "Inpostaçioìn di oservæ speciâli",
        "watching": "Inti osservæ speçiâli...",
        "unwatching": "Scassâ da-i osservæ speçiâli",
+       "watcherrortext": "S'è veificou 'n errô durante a modifica di öservæ pe \"$1\".",
+       "enotif_reset": "Marca tutte-e paggine comme za vixitæ",
+       "enotif_impersonal_salutation": "Utente de {{SITENAME}}",
+       "enotif_subject_deleted": "A paggina $1 de {{SITENAME}} a l'è stæta scassâ da {{gender:$2|$2}}",
+       "enotif_subject_created": "A pagina $1 de {{SITENAME}} a l'è stæta creâ da {{gender:$2|$2}}",
+       "enotif_subject_moved": "A pagina $1 de {{SITENAME}} a l'è stæta mesciâ da {{gender:$2|$2}}",
+       "enotif_subject_restored": "A paggina $1 de {{SITENAME}} a l'è stæta ripristinâ da {{gender:$2|$2}}",
+       "enotif_subject_changed": "A pagina $1 de {{SITENAME}} a l'è stæta modificâ da {{gender:$2|$2}}",
+       "enotif_body_intro_deleted": "A pagina $1 de {{SITENAME}} a l'è stæta scassâ da {{gender:$2|$2}} o $PAGEEDITDATE (amia $3 pe-a verscion attoale).",
+       "enotif_body_intro_created": "A pagina $1 de {{SITENAME}} a l'è stæta creâ da {{gender:$2|$2}} o $PAGEEDITDATE, amia $3 pe-a verscion attoale.",
+       "enotif_body_intro_moved": "A pagina $1 de {{SITENAME}} a l'è stæta mesciâ da {{gender:$2|$2}} o $PAGEEDITDATE, amia $3 pe-a verscion attoale.",
+       "enotif_body_intro_restored": "A pagina $1 de {{SITENAME}} a l'è stæta ripristinâ da {{gender:$2|$2}} o $PAGEEDITDATE, amia $3 pe-a verscion attoale.",
+       "enotif_body_intro_changed": "A pagina $1 de {{SITENAME}} a l'è stæta modificâ da {{gender:$2|$2}} o $PAGEEDITDATE, amia $3 pe-a verscion attoale.",
+       "enotif_lastvisited": "Vixita $1 pe vedde tutte e modiffiche da l'urtima vixita.",
+       "enotif_lastdiff": "Vixita $1 pe vedde a modiffica.",
        "enotif_anon_editor": "ûtente anònnimo $1",
+       "enotif_body": "Gentî $WATCHINGUSERNAME,\n\n$PAGEINTRO $NEWPAGE\n\nÖgetto de l'intervento, inseio da l'aotô: $PAGESUMMARY $PAGEMINOREDIT\n\nContatta l'aotô:\nvia posta eletronnica: $PAGEEDITOR_EMAIL\nin sciô scito: $PAGEEDITOR_WIKI\n\nNo saiâ mandou atre notiffiche in caxo de urteioî attivitæ, se no ti vixiti a pagina doppo avei effettuou l'accesso. Inoltre, l'è poscibbile modificâ e impostaçioin de notiffica pe tutte e paggine inta lista dei öservæ speciali.\n\nO scistema de notiffica de {{SITENAME}}, a-o to serviççio\n\n--\nPe modificâ e impostaçioin de notiffiche via posta elettronnica, vixita \n{{canonicalurl:{{#special:Preferences}}}}\n\nPe modificâ a lista di öservæ speciali, vixita \n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nPe rimoeuve a pagina da-a lista di öservæ speciali, vixita\n$UNWATCHURL\n\nPe commentâ e riçeive agiutto:\n$HELPPAGE",
        "changed": "cangiâ",
        "deletepage": "Scassa a paggina",
+       "confirm": "Conferma",
+       "excontent": "o contegnuo o l'ea: '$1'",
+       "excontentauthor": "o contegnuo o l'ea: '$1', e l'unnico contributô o l'ea \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|msg]])",
+       "exbeforeblank": "O contegnuo primma do svoeuamento o l'ea: '$1'",
        "delete-confirm": "Scassa \"$1\"",
        "delete-legend": "Scassa",
-       "historywarning": "Attension: A paggina c'a se sta pe scassâ a g'ha 'na cronologîa:",
+       "historywarning": "'''Attençion:''' a paggina che ti stæ pe scassâ a g'ha una cronologia con $1 {{PLURAL:$1|verscion|verscioin}}:",
+       "historyaction-submit": "Mostra",
        "confirmdeletetext": "Ti stæ pe scassâ pe sempre da-o database 'na paggina ò 'n'immaggine, assemme a tûtta a sêu cronologîa. Pe cortexia, conferma che davvei ti vêu andâ avanti con quella cancellassion, che ti capisci perfettamente e conseguense de 'st'assion e che a s'adatta a-e linnie guidda stabilîe in [[{{MediaWiki:Policy-url}}]].",
        "actioncomplete": "Açion completâ",
        "actionfailed": "Açión falîa",
        "deletedtext": "A paggina \"$1\" a l'è stæta scassâ. Consultâ o $2 pe 'na lista de paggine scassæ de reçente.",
        "dellogpage": "Registro de cose scassæ",
+       "dellogpagetext": "De sotta gh'è 'na lista co-e paggine scassæ ciu de reçente.",
+       "deletionlog": "registro de scassatue",
+       "reverted": "Ripristinou a verscion precedente",
        "deletecomment": "Raxon:",
        "deleteotherreason": "Ûn âtro motivo",
        "deletereasonotherlist": "Ûnn'âtra raxon",
+       "deletereason-dropdown": "* Motivaçioin ciù comun-e pe-a scançellaçion\n** Spam\n** Vandalismo\n** Violaçion do drito d'aotô\n** Recesta de l'aotô\n** Redirect rotto",
+       "delete-edit-reasonlist": "Modiffica e raxoin do scassamento",
+       "delete-toobig": "A cronologia de questa pagina a l'è ben longa (oltre $1 {{PLURAL:$1|verscion|verscioin}}). A so scançellaçion a l'è stæta limitâ pe evitâ de creâ açidentalmente di problemi de fonçionamento a-o database de {{SITENAME}}.",
+       "delete-warning-toobig": "A cronologia de questa pagina a l'è ben longa (oltre $1 {{PLURAL:$1|verscion|verscioin}}). A so scançellaçion a poeu creâ di problemi de fonçionamento a-o database do {{SITENAME}}; procede con caotella.",
+       "deleteprotected": "No ti poeu scassâ questa paggina perché a l'è stæta protetta.",
+       "deleting-backlinks-warning": "<strong>Attençion:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|di atre pagine]] contengnan di collegamenti o de incluxoin a-a paggina che t'ê aproeuvo a scassâ.",
+       "rollback": "Annulla e modiffiche",
        "rollbacklink": "rollback",
        "rollbacklinkcount": "rollback de {{PLURAL:$1|una modiffica|$1 modiffiche}}",
+       "rollbacklinkcount-morethan": "rollback de ciù de {{PLURAL:$1|una modiffica|$1 modiffiche}}",
+       "rollbackfailed": "Rollback fallio",
+       "rollback-missingparam": "Parammetri obrigatoi mancanti inta recesta.",
        "cantrollback": "No se peu tornâ inderê; l'utente ch'o l'ha fæto quelle modiffiche o l'è stæto l'unico contribuente.",
-       "alreadyrolled": "O no se peû tornâ inderê a-i ûrtimi cangiamenti da pagina [[:$1]]\nda [[User:$2|$2]] ([[User talk:$2|Ciæti]]); quarche âtro\no l'à cangiâ ò o l'è zà tornòu inderê.\nL'ûrtimo cangiamento o ghe l'à fæto [[User:$3|$3]] ([[User talk:$3|Ciæti]]).",
-       "revertpage": "E modificaçioin de [[Special:Contributions/$2|$2]] ([[User talk:$2|Ciæti]]) son stæte eliminæ; riportæ a verscion de primma de [[User:$1|$1]]",
+       "alreadyrolled": "No l'è poscibbile annullâ e modiffiche apportæ a-a pagina [[:$1]] da parte de [[User:$2|$2]] ([[User talk:$2|discuscion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); un atro utente o l'ha zà modificou a pagina oppù o l'ha effettuou o rollback.\n\nA modifica ciù reçente a.a paggina a l'è stæta apportâ da [[User:$3|$3]] ([[User talk:$3|discuscion]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
+       "editcomment": "L'ogetto da modiffica o l'ea: <em>$1</em>.",
+       "revertpage": "Annullou e modiffiche de [[Special:Contributions/$2|$2]] ([[User talk:$2|discuscion]]), riportâ a-a verscion precedente de [[User:$1|$1]]",
+       "revertpage-nouser": "Annullou e modiffiche de un utente ascoso, riportâ a-a verscion precedente de {{GENDER:$1|[[User:$1|$1]]}}",
+       "rollback-success": "Annullou e modiffiche de $1; paggina riportâ a l'urtima verscion de $2.",
+       "rollback-success-notify": "Annullou e modiffiche de $1;\npaggina riportâ a l'urtima revixon de $2. [$3 Mostra e modiffiche]",
+       "sessionfailure-title": "Sescion fallia",
+       "sessionfailure": "S'è veificou un problema inta sescion ch'a l'identiffica l'accesso; o scistema o no l'ha eseguio o comando impartio pe precauçion. Torna a-a paggina precedente co-o tasto 'Inderê' do to browser, recarega a paggina e riproeuva.",
+       "changecontentmodel": "Cangia o modello de contegnuo de 'na paggina",
+       "changecontentmodel-legend": "Cangia o modello de contegnuo",
+       "changecontentmodel-title-label": "Tittolo da paggina",
+       "changecontentmodel-model-label": "Noeuvo modello de contegnuo",
+       "changecontentmodel-reason-label": "Raxon:",
+       "changecontentmodel-submit": "Cangia",
+       "changecontentmodel-success-title": "O modello de contegnuo o l'è stæto modificou",
+       "changecontentmodel-success-text": "O tipo de contegnuo de [[:$1]] o l'è stæto modificou.",
+       "changecontentmodel-cannot-convert": "O contegnuo de [[:$1]] o no poeu ese convertio inte 'n tipo de $2.",
+       "changecontentmodel-nodirectediting": "O modello de contegnuo $1 o no supporta a modiffica diretta",
+       "changecontentmodel-emptymodels-title": "Nisciun modello de contegnuo disponibbile",
+       "changecontentmodel-emptymodels-text": "O contegnuo de [[:$1]] o no poeu ese convertio inte nisciun tipo.",
+       "log-name-contentmodel": "Modiffiche do modello di contegnui",
+       "log-description-contentmodel": "Eventi relativi a-o modello de contegnuo de 'na paggina",
+       "logentry-contentmodel-new": "$1 {{GENDER:$2|l'ha creou}} a paggina $3 doeuviando un modello de contegnuo non predefinio \"$5\"",
+       "logentry-contentmodel-change": "$1 {{GENDER:$2|l'ha modificou}} o modello de contegnuo da paggina $3 da \"$4\" a \"$5\"",
+       "logentry-contentmodel-change-revertlink": "ripristina",
+       "logentry-contentmodel-change-revert": "ripristina",
        "protectlogpage": "Protessioin",
+       "protectlogtext": "De sotta gh'è 'na lista di cangi a-e proteçioin de paggine.\nAmia a [[Special:ProtectedPages|lista de pagine protette]] pe l'elenco de proteçioin de pagina attoalmente attive.",
        "protectedarticle": "o l'à protetto \"[[$1]]\"",
+       "modifiedarticleprotection": "ha modificou o livello de proteçion de \"[[$1]]\"",
+       "unprotectedarticle": "o l'ha sprotezuo \"[[$1]]\"",
+       "movedarticleprotection": "o l'ha mesciou a proteçion da \"[[$2]]\" a \"[[$1]]\"",
+       "protect-title": "Cangio do livello de proteçion pe \"$1\"",
+       "protect-title-notallowed": "Veddi o livello de proteçion de \" $1 \"",
        "prot_1movedto2": "[[$1]] mesciòu a [[$2]]",
+       "protect-badnamespace-title": "Namespace non protezibbile",
+       "protect-badnamespace-text": "E pagine de questo namespace no poeuan ese protezue.",
+       "protect-norestrictiontypes-text": "Questa pagina a no poeu ese protetta perché non gh'è arcun tipo de restriçion disponibbile.",
+       "protect-norestrictiontypes-title": "Paggina non protezibbile",
        "protect-legend": "Confermâ protession",
        "protectcomment": "Raxon:",
        "protectexpiry": "Scadensa:",
        "protect_expiry_invalid": "Scadensa invalida.",
        "protect_expiry_old": "Dæta de scadensa into passòu.",
+       "protect-unchain-permissions": "Desblocca di urteioî opçioin de proteçion",
        "protect-text": "Chì o l'è poscibbile vedde e modificâ o livello de protession pe-a paggina '''$1'''.",
+       "protect-locked-blocked": "No ti poeu cangiâ i livelli de proteçion quande gh'è un blocco. E impostaçioin corrente pe-a pagina son '''$1''':",
+       "protect-locked-dblock": "Imposcibile modificâ i livelli de proteçion durante un blocco do database.\nE impostaçioin corrente pe-a paggina son '''$1''':",
        "protect-locked-access": "No ti g'hæ permisso pe modificâ i livelli de protession da paggina.\nQueste son e impostassioîn correnti pe 'sta paggina ('''$1'''):",
-       "protect-cascadeon": "Pe-o momento 'sta paggina chì a l'è bloccâa perché a l'è inclûsa {{PLURAL:$1|inta paggina indicâa apprêuvo, pe-a quæ|inte paggine indicæ apprêuvo, pe-e quæ}} a l'è attiva a protession recorsciva. O se pêu modificâ o livello de protession individuâle da paggina, ma l'impostassioîn derivanti da-a protession recorsciva no sajàn modificæ.",
-       "protect-default": "(predefinîo)",
-       "protect-fallback": "Besêugna avei permisso \"$1\"",
-       "protect-level-autoconfirmed": "Solo ûtenti registræ",
-       "protect-level-sysop": "Solo amministratoî",
+       "protect-cascadeon": "A-o momento questa pagina a l'è bloccâ perché inclusa {{PLURAL:$1|inta pagina indicâ de seguito, pe-a quæ|inte pagine indicæ de seguito, pe-e quæ}} l'è attiva a proteçion ricorsciva.\nE modifiche a-o livello de proteçion individoale da pagina, no avian effetto in sce-e impostaçioin derivante da-a proteçion ricorsciva.",
+       "protect-default": "Aotorizza tutti i utenti",
+       "protect-fallback": "Consentio solo a-i utenti con permisso \"$1\"",
+       "protect-level-autoconfirmed": "Consentio solo a-i utenti aotoconvalidæ",
+       "protect-level-sysop": "Consentio solo a-i amministratoî",
        "protect-summary-cascade": "recorsciva",
        "protect-expiring": "scadensa: $1 (UTC)",
+       "protect-expiring-local": "o descazze o $1",
+       "protect-expiry-indefinite": "indefinio",
        "protect-cascade": "Protession recorsciva (estende a protession a tûtte e paggine inclûse in questa chì).",
        "protect-cantedit": "Ti no ti pêu modificâ i livelli de protession pe-a paggina se no ti g'hæ i permissi pe modificâ a paggina mæxima.",
-       "protect-expiry-options": "2 ôe:2 hours,1 giorno:1 day,3 giorni:3 days,1 settemann-a:1 week,2 settemann-e:2 weeks,1 meise:1 month,3 meixi:3 months,6 meixi:6 months,1 anno:1 year,infinîo:infinite",
+       "protect-othertime": "Duata non in elenco:",
+       "protect-othertime-op": "duata non in elenco",
+       "protect-existing-expiry": "Scadença attoale: $2, $3",
+       "protect-existing-expiry-infinity": "Scadença attoale: infinio",
+       "protect-otherreason": "Atri motivi/detaggi:",
+       "protect-otherreason-op": "Un'atra raxon",
+       "protect-dropdown": "*Motivi comun de proteçion\n** Reiteræ vandalismi\n** Reiteræ inseimenti de spam\n** Guæra de modiffiche contra-produxente\n** Paggina con ato traffego",
+       "protect-edit-reasonlist": "Modiffica e raxoin pe-a proteçion",
+       "protect-expiry-options": "1 oa:1 hour,1 giorno:1 day,1 setteman-a:1 week,2 setteman-e:2 weeks,1 meise:1 month,3 meixi:3 months,6 meixi:6 months,1 anno:1 year,infinio:infinite",
        "restriction-type": "Permisso",
        "restriction-level": "Livello de restrission",
+       "minimum-size": "Dimenscion minnima",
+       "maximum-size": "Dimenscion mascima:",
+       "pagesize": "(byte)",
        "restriction-edit": "Cangia",
        "restriction-move": "Mescia",
+       "restriction-create": "Crea",
+       "restriction-upload": "Carrega",
+       "restriction-level-sysop": "protetta",
+       "restriction-level-autoconfirmed": "semi-protezua",
        "restriction-level-all": "Tutti i livelli",
        "undelete": "Amîa e paggine scassæ",
+       "undeletepage": "Veddi e recuppera e pagine scançellæ",
+       "undeletepagetitle": "'''Quanto segue o l'è composto da de revixoin scassæ de [[:$1|$1]]'''.",
+       "viewdeletedpage": "Veddi e paggine scassæ",
+       "undeletepagetext": "{{PLURAL:$1|A seguente pagina a l'è stæta scassâ, ma a l'è ancon in archivio e pertanto a poeu ese recuperâ|Le seguente pagine son stæte scassæ, ma son ancon in archivio e pertanto poeuan ese recuperæ}}. L'archivio o poeu ese vuou periodicamente.",
+       "undelete-fieldset-title": "Ripristina revixoin",
+       "undeleteextrahelp": "Pe recuperâ l'intrega cronologia da pagina, lascia tutte e caselle deseleçionæ e fanni clic insce '''''{{int:undeletebtn}}'''''.\nPe effettuâ un ripristino selettivo, seleçion-a e caselle corrispondente a-e verscioin da ripristinâ e fanni clic insce '''''{{int:undeletebtn}}'''''.",
+       "undeleterevisions": "{{PLURAL:$1|Una revixon scassâ|$1 revixoin scassæ}}",
+       "undeletehistory": "Recuperando questa pagina, tutte e so verscioin saian ripristinæ inta relativa cronologia.\nSe doppo la scançellaçione l'è stato creou una noeuva pagina co-o mæximo tittolo, e verscioin recuperæ saian inseie inta cronologia precedente.",
+       "undeleterevdel": "O ripristino o no saiâ effettuou s'o determina a scançellaçion parçiâ da verscion attoale da pagina o do file interessou. In tâ caxo, l'è necessaio smarcâ o levâ l'oscuramento da-e verscioin scassæ ciù reçenti.",
+       "undeletehistorynoadmin": "Questa pagina a l'è stæta scassâ.\nO motivo da scassatua o l'è mostrou chì sotta, insemme a-i detaggi de l'utente ch'o l'ha modificou questa pagina primma da scassatua.\nO testo contegnuo inte verscioin scassæ o l'è disponibile solo a-i amministratoî.",
+       "undelete-revision": "Verscion scassâ da pagina $1, inseia o $4 a $5 da $3:",
+       "undeleterevision-missing": "Verscion errâ o mancante. O collegamento o l'è errou o dunque a verscion a l'è stæta zà ripristinâ ò eliminâ da l'archivvio.",
+       "undelete-nodiff": "No l'è stæto trovou nisciun-a verscion precedente.",
        "undeletebtn": "Ristorâ",
        "undeletelink": "fanni védde/repìggia",
        "undeleteviewlink": "fanni védde",
-       "cannotundelete": "O repiggio de i dæti o non l'è riuscïo (i peun ese za stæti repiggiæ da quarchedun ätro).",
+       "undeleteinvert": "Inverti a seleçion",
+       "undeletecomment": "Raxon:",
+       "undeletedrevisions": "{{PLURAL:$1|Una verscion recuperâ|$1 verscioin recuperæ}}",
+       "undeletedrevisions-files": "{{PLURAL:$1|Una verscion|$1 verscioin}} e $2 file recuperæ",
+       "undeletedfiles": "{{PLURAL:$1|Un file recuperou|$1 file recuperæ}}",
+       "cannotundelete": "Çerti ò tutti i ripristini non riuscii:\n$1",
+       "undeletedpage": "'''A pagina $1 a l'è stæta recuperâ'''\n\nConsurta o [[Special:Log/delete|registro de scançellaçioin]] pe vedde e scançellaçioin e i recupperi ciù reçente.",
+       "undelete-header": "Consurta o [[Special:Log/delete|registro de scançellaçioin]] pe vedde e scassatue ciù reçente.",
+       "undelete-search-title": "Çerca inte pagine scassæ",
+       "undelete-search-box": "Çerca e paggine scassæ",
+       "undelete-search-prefix": "Mostra e paggine che començan con:",
+       "undelete-search-submit": "Çerca",
+       "undelete-no-results": "Nisciun-a pagina corrispondente inte l'archivio de scançellaçioin.",
+       "undelete-filename-mismatch": "Imposcibbile annullâ a scançellaçion da verscion do file con timestamp $1: nomme do file non corrispondente.",
        "undelete-bad-store-key": "No se peu repiggiâ o file co-a dæta $1: o file o no gh'ea za ciu primma d'ese scassou.",
        "undelete-cleanup-error": "Errô into scassâ o file d'archivio non utilizzòu \"$1\".",
+       "undelete-missing-filearchive": "Imposcibile ripristinâ l'ID $1 de l'archivio file in quanto o no l'è presente into database. O poriæ ese stæto za ripristinou.",
+       "undelete-error": "Errô into ripristino da pagina",
        "undelete-error-short": "Errô repiggiando i dæti do file \"$1\".",
        "undelete-error-long": "Gh'è stæto di erroî inte l'annullâ a cançellaçion do file:\n\n$1",
+       "undelete-show-file-confirm": "T'ê seguo de voei amiâ a verscion scassâ do file \"<nowiki>$1</nowiki>\" do $2 a $3?",
+       "undelete-show-file-submit": "Sci",
        "namespace": "Namespace:",
        "invert": "Invertî a seleçión",
        "tooltip-invert": "Selession-a sta casella pe asconde e modiffiche a-e paggine a l'interno do namespace selessionou (e o namespace associou, se selessionou)",
+       "tooltip-whatlinkshere-invert": "Marca sta casella pe asconde i collegamenti da-e pagine a l'interno do namespace seleçionou",
        "namespace_association": "Namespace associou",
        "tooltip-namespace_association": "Selession-a sta casella pe includde ascì a paggina de discuscion ò l'oggetto do namespace associou co-o namespace selessionou",
        "blanknamespace": "(Prinçipâ)",
        "contributions-title": "Contribuçioìn de $1",
        "mycontris": "Contribuçioin",
        "anoncontribs": "Contribuçioin",
-       "contribsub2": "Pe $1 ($2)",
-       "uctop": "(ûrtima pe-a paggina)",
+       "contribsub2": "Pe {{GENDER:$3|$1}} ($2)",
+       "contributions-userdoesnotexist": "L'utença \"$1\" a no l'è registrâ.",
+       "nocontribs": "Cangi che soddisfan i critei de riçerca no se n'è atrovou.",
+       "uctop": "(attoale)",
        "month": "Partindo da-o meize (e precedénti):",
        "year": "Partindo da l'anno (e precedenti):",
        "sp-contributions-newbies": "Fanni védde sôlo e contribuçioìn di nêuvi utenti",
        "sp-contributions-newbies-sub": "Pe i nêuvi ûtenti",
+       "sp-contributions-newbies-title": "Contribuçioin di noeuvi utenti",
        "sp-contributions-blocklog": "Blòcchi",
+       "sp-contributions-suppresslog": "contributi {{GENDER:$1|utente}} soppresci",
+       "sp-contributions-deleted": "contributi {{GENDER:$1|utente}}  scassæ",
        "sp-contributions-uploads": "caregaménti",
        "sp-contributions-logs": "log",
        "sp-contributions-talk": "Ciæti",
+       "sp-contributions-userrights": "manezzo di driti di utenti",
+       "sp-contributions-blocked-notice": "St'utente o l'è attualmente bloccòu.\nL'urtimo elemento into registro di blocchi o l'è riportòu chì de sotta pe rifeimento:",
+       "sp-contributions-blocked-notice-anon": "St'addreçço IP o l'è attoalmente bloccòu.\nL'urtimo elemento into registro di blocchi o l'è riportòu chì de sotta pe rifeimento:",
        "sp-contributions-search": "Riçerca contribuçioìn",
        "sp-contributions-username": "Indirìsso IP ò nómme utente:",
        "sp-contributions-toponly": "Fanni védde sôlo i cangiaménti ch'en i ùrtime revixoìn da pàgina",
+       "sp-contributions-newonly": "Fanni védde sôlo i cangiaménti ch'en de creaçioin de pàgina",
+       "sp-contributions-hideminor": "Ascondi e modifiche menoî",
        "sp-contributions-submit": "Çerca",
        "whatlinkshere": "Cöse se colega chì",
        "whatlinkshere-title": "Pàgine c'apontàn a $1",
        "whatlinkshere-page": "Pàgina:",
        "linkshere": "E pàgine segoenti apontan a '''[[:$1]]''':",
        "nolinkshere": "Nisciùnn-a pàgina a se collega con '''[[:$1]]'''.",
+       "nolinkshere-ns": "Pagine ch'apontan a '''[[:$1]]''' into namespace seleçionou no ghe n'è.",
        "isredirect": "Paggina de rindirissamento",
        "istemplate": "Incluxon",
        "isimage": "Colegamento a file",
        "whatlinkshere-next": "{{PLURAL:$1|sûccescivo|sûccescivi $1}}",
        "whatlinkshere-links": "← colegaménti",
        "whatlinkshere-hideredirs": "$1 i rendirissamenti",
-       "whatlinkshere-hidetrans": "$1 Incluxon",
+       "whatlinkshere-hidetrans": "$1 Incluxoin",
        "whatlinkshere-hidelinks": "$1 colegaménti",
-       "whatlinkshere-hideimages": "$1 colegaménti di file",
+       "whatlinkshere-hideimages": "$1 colegamenti da file",
        "whatlinkshere-filters": "Filtri",
-       "blockip": "Blocca l'ûtente",
+       "whatlinkshere-submit": "Vanni",
+       "autoblockid": "Aotobrocco #$1",
+       "block": "Blocca utente",
+       "unblock": "Desblocca utente",
+       "blockip": "Blocca {{GENDER:$1|utente}}",
+       "blockip-legend": "Blocca l'utente",
+       "blockiptext": "Doeuvia o moddulo sottostante pe bloccâ l'accesso in scrittua a un speciffico addreçço IP ò a un utente registrou.\nO blocco o dev'ese doeuviou pe prevegnî di atti de vandalismo e in streita öservança de [[{{MediaWiki:Policy-url}}|reggole de {{SITENAME}}]].\nIndica o motivo speçiffico pe-o quæ se procede a-o blocco (presempio, çitando i tittoli di eventuæ paggine ögetto de vandalismo).\nTi poeu bloccâ di ntervalli de IP doeuviando a scintasci [https://it.wikipedia.org/wiki/CIDR CIDR]; l'intervallo ciù ampio consentio o l'è /$1 pe IPv4 e /$2 pe IPv6.",
+       "ipaddressorusername": "Adreçço IP ò nómme utente:",
+       "ipbexpiry": "Scadença:",
        "ipbreason": "Raxon:",
+       "ipbreason-dropdown": "*Motivaçioni ciù comun-e pe-i blocchi\n** Inseimento de informaçioin fase\n** Rimoçion di contegnti da-e paggine\n** Collegamenti promoçionæ a di sciti esterni\n** Inseimento di contegnui privi de senso\n** Comportamenti intimidatoi ò molestie\n** Uso indebbito de utençe murtiple\n** Nomme utente inaçettabbile",
+       "ipb-hardblock": "Impedisci a-i utenti registræ de contribuî da questo adreçço IP",
+       "ipbcreateaccount": "Impedisci a registraçion",
+       "ipbemailban": "Impedisci a l'utente l'invio di email",
+       "ipbenableautoblock": "Blocca aotomaticamente l'urtimo adreçço IP doeuviou da l'utente e i succescivi da-i quæ tentan e modiffiche",
+       "ipbsubmit": "Blocca st'utente",
+       "ipbother": "Duata non in elenco:",
        "ipboptions": "2 ôe:2 hours,1 giorno:1 day,3 giorni:3 days,1 settemann-a:1 week,2 settemann-e:2 weeks,1 meise:1 month,3 meixi:3 months,6 meixi:6 months,1 anno:1 year,infinîo:infinite",
+       "ipbhidename": "Ascondi o nomme utente da-e modiffiche e da-i elenchi.",
+       "ipbwatchuser": "Controlla e pagine e-e discuscioin utente de questo utente",
+       "ipb-disableusertalk": "Impedisci a questo utente de modificâ a proppia paggina de discuscion dementre ch'o l'è bloccou",
+       "ipb-change-block": "Re-blocca l'utente con queste impostaçioin",
+       "ipb-confirm": "Conferma o blocco",
        "badipaddress": "Indirisso IP non valido",
        "blockipsuccesssub": "Blocco ariêscîo",
-       "blockipsuccesstext": "[[Special:Contributions/$1|$1]] o l'è stæto bloccou.\n<br />Veddi [[Special:BlockList|Lista di addressi IP bloccæ]] pe vedde i blocchi attivi.",
+       "blockipsuccesstext": "[[Special:Contributions/$1|$1]] o l'è stæto bloccou.\n<br />Amia a [[Special:BlockList|lista di adreççi IP bloccæ]] pe vedde i blocchi attivi.",
+       "ipb-blockingself": "Ti stæ pe bloccâ ti mæximo! T'ê seguo die voeilo fâ?",
+       "ipb-confirmhideuser": "Ti stæ pe bloccâ un utente con l'opçion \"Ascondi utente\" abilitâ.\nDe sta mainea o nomme utente o saiâ sopresso da tutte le liste e-e voxe de registro.\nT'ê seguo de voeilo fâ?",
+       "ipb-confirmaction": "Se t'ê seguo de voeilo fâ davei, controlla o campo \"{{int:ipb-confirm}}\" in fondo.",
+       "ipb-edit-dropdown": "Modifica i motivi do blocco",
+       "ipb-unblock-addr": "Sblocca $1",
+       "ipb-unblock": "Sblocca un utente ò un adreçço IP",
+       "ipb-blocklist": "Elenca i blocchi attivi",
+       "ipb-blocklist-contribs": "Contribuçioin de {{GENDER:$1|$1}}",
+       "ipb-blocklist-duration-left": "$1 arestæ",
+       "unblockip": "Desblocca utente",
+       "unblockiptext": "Doeuvia o moddulo sottostante pe restituî l'accesso in scrittua a 'n utente ò adreçço IP bloccou.",
+       "ipusubmit": "Leva sto blocco",
+       "unblocked": "L'utente [[User:$1|$1]] o l'è stæto sbloccou",
+       "unblocked-range": "$1 o l'è stæto sbloccou",
+       "unblocked-id": "O blocco $1 o l'è stæto rimòsso",
+       "unblocked-ip": "[[Special:Contributions/$1|$1]] o l'è stæto sbloccou.",
+       "blocklist": "Utenti bloccæ",
        "ipblocklist": "Utenti blocæ",
+       "ipblocklist-legend": "Çerca un utente bloccou",
+       "blocklist-userblocks": "Ascondi i blocchi di utenti registræ",
+       "blocklist-tempblocks": "Ascondi i blocchi temporannei",
+       "blocklist-addressblocks": "Ascondi i blocchi de un solo IP",
+       "blocklist-rangeblocks": "Ascondi i blocchi de range",
+       "blocklist-timestamp": "Dæta e oa",
+       "blocklist-target": "Destinaçion",
+       "blocklist-expiry": "O descazze",
+       "blocklist-by": "Amministratô ch'o l'ha misso o blocco",
+       "blocklist-params": "Parammetri de blocco",
+       "blocklist-reason": "Raxon",
+       "ipblocklist-submit": "Çerca",
+       "ipblocklist-localblock": "Blocco locale",
+       "ipblocklist-otherblocks": "{{PLURAL:$1|Atro blocco|Atri blocchi}}",
+       "infiniteblock": "infinio",
+       "expiringblock": "o descazze o $1 a $2",
        "anononlyblock": "Solo anonnimi",
+       "noautoblockblock": "blocco aotomatico disabilitou",
+       "createaccountblock": "registraçion bloccâ",
        "emailblock": "posta elettronega bloccâ",
+       "blocklist-nousertalk": "o no poeu modificâ a proppia pagina de discuscion",
        "ipblocklist-empty": "A lista di blocchi a l'è veua.",
+       "ipblocklist-no-results": "L'adreçço IP ô o nomme utente domandou o no l'è bloccou.",
        "blocklink": "Blocca",
        "unblocklink": "sblòcca",
        "change-blocklink": "càngia blòcco",
        "contribslink": "Contribuçioìn",
-       "autoblocker": "Affermoö automaticamente perchè o teu indirisso IP o l'è stæto usöo da \"[[User:$1|$1]]\" neuvamente. A razon dæta pe affermâ $1 a l'è stæta:\n\"$2\"",
+       "emaillink": "manda e-mail",
+       "autoblocker": "Bloccou aotomaticamente perché l'adreçço IP o l'è condiviso con l'utente \"[[User:$1|$1]]\".\nO blocco de l'utente $1 o l'è stæto imposto pe-o seguente motivo: \"$2\".",
        "blocklogpage": "Blòcchi",
+       "blocklog-showlog": "Questo utente o l'è stæto bloccou in precedença. O registro di blocchi o l'è riportou de sotta pe rifeimento:",
+       "blocklog-showsuppresslog": "Questo utente o l'è stæto bloccou e nscoso in precedença. O registro de rimoçioin o l'è riportou de sotta pe rifeimento:",
        "blocklogentry": "blocòu [[$1]] pe in periodo de $2 $3",
-       "blocklogtext": "Sta chie a l'è unn-a lista de affermaçioin fæte e levæ.\nI indirissi IP affermæ automaticamente non son  consideræ.\nVeddi a [[Special:BlockList|Lista de i indirissi IP affermæ]] pe e informaçioin neuve.",
-       "block-log-flags-anononly": "Utenti anonimmi soö",
+       "reblock-logentry": "o l'ha cangiou e impostaçioin do blocco pe [[$1]] co-ina scadença de $2 $3",
+       "blocklogtext": "De sotta gh'è 'n registro di açioin de blocco e sblocco utenti.\nI adreççi IP bloccæ aotomaticamente no son elencæ.\nConsurta l'[[Special:BlockList|elenco di blocchi]] pe l'elenco di bandi o blocchi attoalmente opiativi.",
+       "unblocklogentry": "o l'ha sbloccou $1",
+       "block-log-flags-anononly": "solo utenti anonnimi",
        "block-log-flags-nocreate": "Neuve registrascioin non son permisse",
        "block-log-flags-noautoblock": "O blocco automatego o non l'è attïvo",
        "block-log-flags-noemail": "A posta elettronega a non l'è attïva",
+       "block-log-flags-nousertalk": "o no poeu modificâ a proppia pagina de discuscion",
+       "block-log-flags-angry-autoblock": "blocco aotomatico avançou attivo",
+       "block-log-flags-hiddenname": "nomme utente ascoso",
+       "range_block_disabled": "O drito d'aministratô de bloccâ di intervalli d'adreççi IP o l'è disabilitou.",
+       "ipb_expiry_invalid": "Scadença non vallida.",
+       "ipb_expiry_old": "Scadença za trascorsa.",
+       "ipb_expiry_temp": "I blocchi di nommi utenti ascoxi devan ese permanenti.",
+       "ipb_hide_invalid": "Imposcibbile soprimme questa utença; a gha ciù de {{PLURAL:$1|'na modiffica|$1 modiffiche}}.",
+       "ipb_already_blocked": "L'utente \"$1\" o l'è za bloccou",
+       "ipb-needreblock": "L'utente $1 o l'è za bloccou. Ti voeu miga cangiâ e impostaçioin?",
+       "ipb-otherblocks-header": "{{PLURAL:$1|Atro blocco|Atri blocchi}}",
+       "unblock-hideuser": "No ti poeu sbloccâ questo utente, perché o so nomme utente o l'è stæto ascoso.",
+       "ipb_cant_unblock": "Errô: Imposcibbile trovâ o blocco con ID $1. O blocco o poriæ ese za stæto rimosso.",
+       "ipb_blocked_as_range": "Errô: L'adreçço IP $1 o no l'è soggetto a blocco individoale e o no poeu ese sbloccou. O blocco o l'è invece attivo a livello de l'intervallo $2, ch'o poeu ese sbloccou.",
+       "ip_range_invalid": "Intervallo d'adreççi IP non vallido.",
+       "ip_range_toolarge": "No l'è poscibbile bloccâ de range supeioî a-o /$1",
+       "proxyblocker": "Blocco di proxy averti",
+       "proxyblockreason": "Questo adreçço IP o l'è stæto bloccou perché o l'è un proxy averto. Se prega de contattâ o proppio fornitô d'accesso a Internet ò o supporto tecnico e informâli de questo grave problema de segueça.",
+       "sorbsreason": "Questo adreçço IP o l'è elencou comme proxy averto inta lista DNSBL doeuviâ da {{SITENAME}}.",
+       "sorbs_create_account_reason": "No l'è poscibbile creâ de noeuve utençe con questo adreçço IP perché o l'è elencou comme proxy averto inta lista DNSBL doeuviâ da {{SITENAME}}.",
+       "xffblockreason": "Un adreçço IP presente inte l'intestaçion X-Forwarded-For, toeu ò do server proxy che ti doeuvi, o l'è stæto bloccou. A raxon originâ do blocco a l'è: $1",
+       "cant-see-hidden-user": "L'utente che ti çerchi de bloccâ o l'è stæto za bloccou e ascoso. Scicomme no ti g'hæ o permisso \"hideuser\", no l'è poscibbile vixualizâ ò modificâ o blòcco de l'utente.",
+       "ipbblocked": "No ti poeu bloccâ ò sbloccâ di atri utenti, perché ti mæximo t'ê bloccou",
+       "ipbnounblockself": "No ti poeu sbloccâ ti mæximo",
+       "lockdb": "Blocca o database",
+       "unlockdb": "Sblocca o database",
+       "lockdbtext": "O blocco do database o comporta l'interruçion, pe tutti i utenti, da poscibilitæ de modificâ e paggine o de creâne de noeuve, de cangiâ e preferençe e modificâ e liste di öservæ speciali, e in generâ de tutte i opiaçioin pe-e quæ öcore de modiffiche a-o database. Pe cortexia, conferma che questo l'è proppio quello che t'intendi de fâ e che a-a fin da manutençion ti provediæ a-o sblocco do database.",
+       "unlockdbtext": "O sblocco do database o consente torna a tutti i utenti de modificâ e paggine o de creâne de noeuve, de cangiâ e preferençe e modificâ le liste di öservæ speciali, e in generâ de compî tutte i öpiaçioin pe-e quæ öcore de modiffiche a-o database. Pe cortexia, conferma che questo l'è proppio quello che t'intendi de fâ.",
+       "lockconfirm": "Scì, intendo effettivamente bloccâ o database.",
+       "unlockconfirm": "Scì, intendo effettivamente sbloccâ o database.",
+       "lockbtn": "Blocca o database",
+       "unlockbtn": "Sblocca o database",
+       "locknoconfirm": "No t'hæ marcou a casella de conferma.",
+       "lockdbsuccesssub": "Blocco do database eseguio",
+       "unlockdbsuccesssub": "Sblocco do database eseguio",
+       "lockdbsuccesstext": "O database o l'è stæto bloccou.<br />\nAregordite de [[Special:UnlockDB|levâ o blocco]] doppo d'avei terminou i opiaçioin de manutençion.",
+       "unlockdbsuccesstext": "O database o l'è stæto sbloccou.",
+       "lockfilenotwritable": "Imposcibbile scrive in sciô file de ''lock'' do database. Pe bloccâ e sbloccâ il database l'è necessaio l'accesso in scrittua a questo file da parte do server web .",
+       "databaselocked": "O database o l'è za bloccou.",
        "databasenotlocked": "O database o no l'è bloccòu.",
+       "lockedbyandtime": "(da $1 o $2 a $3)",
+       "move-page": "Stramuo de $1",
        "move-page-legend": "Mescia a paggina",
-       "movepagetext": "Chì o se pêu dâ 'n nêuvo nomme a 'na paggina, stramûando tûtta a sêu cronologîa a-o nêuvo nomme.\nA paggina attuâle a fa outomaticamente 'n rindirissamento a-o nêuvo tittolo.\nI collegamenti escistenti no sajàn aggiornæ; veriffica che 'sto stramûo o no l'agge creòu doggi rindirissamenti ò rindirissamenti sballiæ.\nA responsabilitæ pe tegnî i collegamenti sempre donde deivan andâ a l'è têu.\n\nA paggina a '''no''' sajà stramûâa se ghe foisse zà ûnn-a co-o nêuvo nomme, a meno c'a no segge vêua ò fæta solo da 'n rindirissamento a-a vegia e a no l'agge verscioîn preçedenti.\nIn caso de stramûo sballiòu o se pêu tornâ sûbbito a-o vegio tittolo, e o no l'è poscibbile sorvescrive pe errô 'na paggina zà escistente.\n\n'''ATTENSION:'''\n'N cangiamento coscì grande o porieiva creâ di controtempi e problemmi, sorvetûtto pe-e paggine ciû viscitæ.\nPensa ben e conseguense de 'sto stramûo primma d'andâ avanti!",
+       "movepagetext": "Questo moddulo o consente de rinominâ una pagina, mesciando tutta a so cronologia a-o noeuvo nomme. A paggina attoale a vegniâ aotomaticamente un redirect a-o noeuvo tittolo. Controlla che o stramuo o no l'abbia creou di [[Special:DoubleRedirects|doggi redirect]] ò di [[Special:BrokenRedirects|redirect erræ]]. L'onere de garantî che i collegamenti a-a pagina arestan corretti o speta a chi a mescia.\n\nNotta che a pagina a <strong>no</strong> saiâ mesciaa se n'existe za un-a co-o noeuvo nomme, a meno che a no segge costituia solo che da un redirect e a segge priva de verscioin precedente. In caxo de stramuo errou se poeu tornâ subbito a-o vegio tittolo, e no l'è poscibbile soviascrive pe sballio una pagina za existente.\n\n<strong>Nota:</strong>\nUn cangiamento coscì drastego o poeu creâ di contrattempi e di problemi, soviatutto pe-e paggine ciù vixitæ. Asseguite d'avei valutou e conseguençe primma de procede.",
+       "movepagetext-noredirectfixer": "Questo moddulo o consente de rinominâ una pagina, mesciando tutta a so cronologia a-o noeuvo nomme. A paggina attoale a vegniâ aotomaticamente un redirect a-o noeuvo tittolo. Controlla che o stramuo o no l'abbia creou di [[Special:DoubleRedirects|doggi redirect]] ò di [[Special:BrokenRedirects|redirect erræ]]. L'onere de garantî che i collegamenti a-a pagina arestan corretti o speta a chi a mescia.\n\nNotta che a pagina a <strong>no</strong> saiâ mesciaa se n'existe za un-a co-o noeuvo nomme, a meno che a no segge costituia solo che da un redirect e a segge priva de verscioin precedente. In caxo de stramuo errou se poeu tornâ subbito a-o vegio tittolo, e no l'è poscibbile soviascrive pe sballio una pagina za existente.\n\n<strong>Nota:</strong>\nUn cangiamento coscì drastego o poeu creâ di contrattempi e di problemi, soviatutto pe-e paggine ciù vixitæ. Asseguite d'avei valutou e conseguençe do stramuo primma de procede.",
        "movepagetalktext": "Se ti seleçion-i questa casella, a corispondente paggina de discuscion a saiâ mesciâ aotomaticamente a-o neuvo tittolo, a meno che existe zà una paggina de discuscion ch'a no segge veua.\n\nInte sti caxi, ti doviæ mesciâ o unî manoalmente a paggina, se proppio ti veu.",
-       "newtitle": "Nêuvo tittolo:",
+       "moveuserpage-warning": "'''Attençione:''' Ti stæ pe mesciâ una pagina utente. Notta che solo a pagina a saiâ mesciâ. L'utente o ''no'' saiâ rinominou.",
+       "movecategorypage-warning": "<strong>Attençione:</strong> ti stæ pe mesciâ una categoria. Notta che solo sta paggina a saiâ mesciâ e tutte e pagine inta vegia categoria <em>no</em> saian inseie inta noeuva.",
+       "movenologintext": "O stramuo dee paggine o l'è consentio solo che a-i utenti registræ ch'han eseguio l'[[Special:UserLogin|accesso]] a-o scito.",
+       "movenotallowed": "No ti g'hæ o permisso pe mesciâ de paggine.",
+       "movenotallowedfile": "No ti g'hæ o permisso pe mesciâ di file.",
+       "cant-move-user-page": "No ti g'hæ o permisso pe mesciâ de pagine utente (escluse e sottopagine).",
+       "cant-move-to-user-page": "No ti g'hæ o permisso pe mesciâ a pagina insce 'na pagina utente (escluse e sottopagine utente).",
+       "cant-move-category-page": "No ti g'hæ o permisso pe mesciâ de categorie.",
+       "cant-move-to-category-page": "No ti g'hæ o permisso pe mesciâ a pagina insce 'na categoria.",
+       "newtitle": "Noeuvo tittolo:",
        "move-watch": "Azzonze a li osservæ speçiâli",
        "movepagebtn": "Stramûâ a paggina",
        "pagemovedsub": "Remescio fæto",
-       "articleexists": "Ghe n'æmmo zà 'na paggina con 'sto nomme, oppûre quello che ti g'hæ scelto o no l'è permisso. Cangia nomme.",
+       "movepage-moved": "'''\"$1\" a l'è stæta mesciâ a \"$2\"'''",
+       "movepage-moved-redirect": "L'è stæto creou un redirect.",
+       "movepage-moved-noredirect": "A creaçion de un redirect a l'è stæta sopressa.",
+       "articleexists": "Una paggina con sto nomme ghe l'emmo za, oppûre quello che t'hæ çernuo o no l'è vallido. Pe piaxei çerni 'n atro nomme.",
+       "cantmove-titleprotected": "O stramuo da paggina o no l'è poscibbile in quante o noeuvo tittolo o l'è stæto protezuo pe impedîne a creaçion.",
        "movetalk": "Mescia a paggina de discuscion ascì.",
+       "move-subpages": "Mescia e sottopaggine (scin a $1)",
+       "move-talk-subpages": "Mescia e sottopaggine de discuscion (scin a $1)",
+       "movepage-page-exists": "A paggina $1 a l'existe za e a no poeu ese aotomaticamente soviascrita.",
+       "movepage-page-moved": "A paggina $1 a l'è stæta mesciaa a $2.",
+       "movepage-page-unmoved": "A paggina $1 a no poeu ese mesciâ a $2.",
+       "movepage-max-pages": "L'è stæto mesciou o nummero mascimo de $1 {{PLURAL:$1|paggina|paggine}}: aotomaticamente no ne saiâ mesciou ciu.",
        "movelogpage": "Lista di stramûi",
+       "movelogpagetext": "De sotta una lista de tutti i stramui de paggina.",
+       "movesubpage": "{{PLURAL:$1|Sottopagina|Sottopagine}}",
+       "movesubpagetext": "Questa pagina a g'ha $1 {{PLURAL:$1|sottopagina mostrâ|sottopagine mostræ}} chì de sotta.",
+       "movenosubpage": "Questa pagina a no g'ha de sottopagine.",
        "movereason": "Raxon",
        "revertmove": "Ristorâ",
+       "delete_and_move_text": "A pagina specificâ comme destinaçion \"[[:$1]]\" a l'existe za. Ti voeu miga scassâla pe fâ röso pe-o stramuo?",
        "delete_and_move_confirm": "Scì, scassa a pagina",
-       "delete_and_move_reason": "Levoö pe fâ röso pe un remescio",
+       "delete_and_move_reason": "Scassâ pe fâ röso pe un stramuo da \"[[$1]]\"",
+       "selfmove": "O tittolo de destinaçion o l'è pægio de quello de proveniença, no l'è poscibbile mesciâ una paggina insce lê mæxima.",
+       "immobile-source-namespace": "No l'è poscibbile mesciâ de pagine do namespace \"$1\"",
+       "immobile-target-namespace": "No l'è poscibbile mesciâ de paggine into namespace \"$1\"",
+       "immobile-target-namespace-iw": "Un collegamento interwiki o no l'è una destinaçion vallida pe'n stramuo de paggina.",
+       "immobile-source-page": "Questa pagina a no poeu ese mesciâ.",
+       "immobile-target-page": "No l'è poscibbile fâ o stramuo inte quello tittolo de destinaçion.",
+       "bad-target-model": "A destinaçion dexidiâ a l'adoeuvia un modello de contegnui despægio. No l'è poscibbile convertî da $1 a $2.",
+       "imagenocrossnamespace": "No l'è poscibbile mesciâ un file foeua da-o relativo namespace.",
+       "nonfile-cannot-move-to-file": "No l'è poscibbile mesciâ un file foeua da-o relativo namespace.",
+       "imagetypemismatch": "A noeuva estenscion do file a no corisponde a-o so tipo.",
+       "imageinvalidfilename": "O nomme de l'immaggine o no l'è vallido",
+       "fix-double-redirects": "Aggiorna tutti i redirect ch'apontan a-o tittolo originâ",
+       "move-leave-redirect": "Lascighe derê un redirect",
+       "protectedpagemovewarning": "'''Attençion: questa paggina a l'è stæta bloccâ de moddo che solo i utenti co-i privileggi d'amministratô possan modificâla.'''\nL'urtimo elemento do registro o l'è riportou chì sotta pe referença:",
+       "semiprotectedpagemovewarning": "'''Notta:''' Questa paggina a l'è stæta bloccä de moddo che solo i utenti registræ possan mesciâla.\nL'urtimo elemento do registro o l'è riportou chì sotta pe referensa:",
+       "move-over-sharedrepo": "[[:$1]] a l'existe za inte 'n archivvio condiviso. O stramuo de 'n file a questo tittolo o comportiâ a soviascritua do file condiviso.",
+       "file-exists-sharedrepo": "O nomme che t'hæ çernuo pe-o file o l'è za in doeuvia.\nPe piaxei, çerni un atro nomme.",
        "export": "Espòrta pàgine",
-       "allmessages": "Messaggi do scistemma",
+       "exporttext": "L'è poscibbile esportâ o testo e a cronologia de modiffiche de una paggina ò de un groppo de pagine in formato XML pe importâle inte di atri sciti ch'adoeuvian o software MediaWiki, a traverso a [[Special:Import|paggina de importaçioin]].\n\nPe esportâ e paggine indica i tittoli inta casella de testo sottostante, un pe riga, e speciffica se ti dexiddei d'ötegnî l'urtima verscion e tutte e verscioin precedente, co-i dæti da cronologia da paggina, oppû soltanto l'urtima verscion e i dæti corispondenti a l'urtima modiffica.\n\nIn quest'urtimo caxo ti poeu doeuviâ ascì un collegamento, presempio [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] pe esportâ \"[[{{MediaWiki:Mainpage}}]]\".",
+       "exportall": "Esporta tutte e pagine",
+       "exportcuronly": "Includdi solo a verscion attoâ, non l'intrega cronologia",
+       "exportnohistory": "----\n'''Notta:''' l'esportaçion de l'intrega cronologia de paggine a traverso questa interfaccia a l'è stæta disattivâ pe di motivi ligæ a-e prestaçioin do scistema.",
+       "exportlistauthors": "Inciodi l'elenco completo di contributoî pe ogni paggina",
+       "export-submit": "Esporta",
+       "export-addcattext": "Azonzi pagine da-a categoria:",
+       "export-addcat": "Azonzi",
+       "export-addnstext": "Azonzi paggine da-o namespace:",
+       "export-addns": "Azonzi",
+       "export-download": "Sarva comme file",
+       "export-templates": "Inciodi i template",
+       "export-pagelinks": "Includdi paggine colegæ scin a 'na profonditæ de:",
+       "export-manual": "Azonzi paggine manoalmente:",
+       "allmessages": "Messaggi do scistema",
        "allmessagesname": "Nomme",
        "allmessagesdefault": "Testo predefinio",
        "allmessagescurrent": "Testo corrente",
-       "allmessagestext": "Sta chie a l'è unn-a lista de messaggi do scistema in ta MediaWiki.",
+       "allmessagestext": "Sta chie a l'è a lista de tutti i messaggi de scistema disponibili into namespace MediaWiki.\nVixita [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki Localisation] e [https://translatewiki.net translatewiki.net] se ti voeu contribuî a-a localizzaçion generrica de MediaWiki.",
        "allmessagesnotsupportedDB": "'''{{ns:special}}:Allmessages''' o non ti te peu vedde, perchè '''$wgUseDatabaseMessages''' o non l'è attivo.",
+       "allmessages-filter-legend": "Filtro",
+       "allmessages-filter": "Filtra pe stato de modiffica:",
+       "allmessages-filter-unmodified": "Non modificæ",
+       "allmessages-filter-all": "Tutti",
+       "allmessages-filter-modified": "Modificæ",
+       "allmessages-prefix": "Filtra pe prefisso:",
        "allmessages-language": "Lengua:",
+       "allmessages-filter-submit": "Vanni",
+       "allmessages-filter-translate": "Traduxi",
        "thumbnail-more": "Ciù grande",
        "filemissing": "O file o no gh'è!",
        "thumbnail_error": "Errô inta creassion da miniatûa: $1",
-       "thumbnail_invalid_params": "Parametri da a imàginetta non validi",
+       "thumbnail_error_remote": "Messaggio de errô da $1:\n$2",
+       "djvu_page_error": "Nummero de paggina DjVu errou",
+       "djvu_no_xml": "Imposcibbile ötegnî l'XML pe-o file DjVu",
+       "thumbnail-temp-create": "Imposcibbile creâ o file temporannio de miniatue",
+       "thumbnail-dest-create": "Imposcibbile sarvâ a miniatua inta destinaçion",
+       "thumbnail_invalid_params": "Parammetri da miniatua non vallidi",
+       "thumbnail_toobigimagearea": "File con de dimenscioin maggioe de $1",
+       "thumbnail_dest_directory": "Imposcibbile creâ a directory de destinaçion",
+       "thumbnail_image-type": "Tipo de immagine non supportou",
+       "thumbnail_gd-library": "Configuaçion incompleta da libraia GD: fonçion $1 mancante",
+       "thumbnail_image-missing": "Pâ ch'amanche o file: $1",
+       "thumbnail_image-failure-limit": "Gh'è stæto troppi tentativi reçenti fallii ($1 ò ciù) de generâ questa miniatua. Riproeuva ciù tardi.",
+       "import": "Importa pagine",
+       "importinterwiki": "Importaçion da un atro wiki",
+       "import-interwiki-text": "Seleçion-a un progetto wiki e o titolo da paggina da importâ.\nE dæte de pubbricaçion e i nommi di aotoî de varrie verscioin saian conservæ.\nTutte i opiaçioin de importaçion da di atri wiki son registræ into [[Special:Log/import|registro de importaçion]].",
+       "import-interwiki-sourcewiki": "Wiki de origgine:",
+       "import-interwiki-sourcepage": "Paggina d'origgine:",
+       "import-interwiki-history": "Coppia l'intrega cronologia de questa paggina",
+       "import-interwiki-templates": "Inciodi tutti i template",
+       "import-interwiki-submit": "Importa",
+       "import-mapping-default": "Importa inte destinaçioin predefinie",
+       "import-mapping-namespace": "Importa inte 'n namespace:",
+       "import-mapping-subpage": "Importa comme sottopaggine da paggina seguente:",
+       "import-upload-filename": "Nomme do file:",
+       "import-comment": "Coménti:",
+       "importtext": "Se prega de esportâ o file da-o scito wiki de origgine co-a [[Special:Export|fonçion de esportaçion]], sarvâlo in sciô proppio disco e dapoeu caregâlo chie.",
+       "importstart": "Importaçion de paggine in corso...",
+       "import-revision-count": "$1 {{PLURAL:$1|revixon|revixoin}}",
+       "importnopages": "Nisciun-a paggina da importâ.",
+       "imported-log-entries": "Importou $1 {{PLURAL:$1|voxe}} de registro.",
+       "importfailed": "Importaçion no ariescia: <nowiki>$1</nowiki>",
+       "importunknownsource": "Tipo de sorgente d'importaçion sconosciuo",
+       "importcantopen": "Imposcibbile arvî o file d'importaçion.",
+       "importbadinterwiki": "Collegamento inter-wiki errou",
+       "importsuccess": "Importaçion ariescia.",
+       "importnosources": "No l'è stæto definio una wiki da chi importâ e i uploads diretti da cronologia no son attivi.",
+       "importnofile": "No l'è stæto caregou nisciun file pe l'importaçion.",
+       "importuploaderrorsize": "Caregamento do file pe l'importaçion fallio. O file o suppera e dimenscioin mascime consentie pe l'upload.",
+       "importuploaderrorpartial": "Caregamento do file pe l'importaçion fallio. O file o l'è stæto caregou solo in parte.",
+       "importuploaderrortemp": "Caregamento do file pe l'importaçion fallio. Manca 'na cartella tempoannia.",
+       "import-parse-failure": "Errô d'anallixi inte l'importaçion XML",
+       "import-noarticle": "Paggine da importâ no ghe n'è!",
+       "import-nonewrevisions": "Nisciun-a verscion importâ (ean zà tutte presente, ò satæ pe via di erroî)",
+       "xml-error-string": "$1 a-a riga $2, colonna $3 (byte $4): $5",
+       "import-upload": "Carrega dæti XML",
+       "import-token-mismatch": "I dæti relativi a-a sescion son anæti persci. Riproeuva.\nFoscia t'ê stæto disconnesso. <strong>Verifica d'ese ancon collegou e riproeuva</strong>.\nSe o problema o persciste, ti poeu provâ a [[Special:UserLogout|scollegate]] e effettuâ un nuoeuvo accesso, controllando che o to browser o l'açette i cookie da questo scito.",
+       "import-invalid-interwiki": "Imposcibbile importâ da-o progetto wiki indicou.",
+       "import-error-edit": "A paggina \"$1\" a no l'è stæta importâ perché no t'ê aotorizzou a modificâla.",
+       "import-error-create": "A paggina \"$1\" a no l'è stæta importâ perché no t'ê aotorizzou a creâla.",
+       "import-error-interwiki": "A paggina \"$1\" a no l'è stæta importâ perché o so nomme o l'è riservou pe-o collegamento esterno (interwiki).",
+       "import-error-special": "A pagina \"$1\" a no l'è stæta importâ perché a l'apparten a un namespace speciale ch'o no permette de pagine.",
+       "import-error-invalid": "A paggina \"$1\" a no l'è stæta importâ perché o nomme a-o quæ a saiæ stæta importâ o no l'è vallido insce questo wiki.",
+       "import-error-unserialize": "A verscion $2 da paggina \"$1\" a no poeu ese de-serializzâ. A verscion a l'è stæta segnalâ pe doeuviâ o modello de contegnuo $3 serializzou comme $4.",
+       "import-error-bad-location": "A verscion $2 a l'adoeuvia un modello de contegnuo $3 ch'o no poeu ese memorizzou in \"$1\" de questo wiki, da-o momento che a paggina a no suporta questo modello.",
+       "import-options-wrong": "{{PLURAL:$2|Opçion sbaliâ|Opçioin sbaliæ}}: <nowiki>$1</nowiki>",
+       "import-rootpage-invalid": "A paggina prinçipâ fornia a no l'è un tittolo vallido.",
+       "import-rootpage-nosubpage": "O namespace \"$1\" da paggina prinçipâ o no permette d'avei de sottopagine.",
        "importlogpage": "Importassioîn",
-       "tooltip-pt-userpage": "A teu pagina utilizatô",
-       "tooltip-pt-mytalk": "E mæ discûscioîn",
-       "tooltip-pt-preferences": "E mæ preferense",
+       "importlogpagetext": "Importaçioin aministrative de paggine da di atre wiki complete de cronologia.",
+       "import-logentry-upload-detail": "{{PLURAL:$1|una verscion importâ|$1 verscioin importæ}}",
+       "import-logentry-interwiki-detail": "{{PLURAL:$1|una verscion importâ|$1 verscioin importæ}} da $2",
+       "javascripttest": "Sperimentaçion JavaScript",
+       "javascripttest-pagetext-unknownaction": "Açion sconosciua \"$1\".",
+       "javascripttest-qunit-intro": "Amia insce mediawiki.org a [$1 documentaçion riguardante i test].",
+       "tooltip-pt-userpage": "A {{GENDER:|to}} pagina utente",
+       "tooltip-pt-anonuserpage": "A paggina utente de questo adreçço IP che t'ê aproeuvo a modificâ comme",
+       "tooltip-pt-mytalk": "A {{GENDER:|to}} paggina de discuscion",
+       "tooltip-pt-anontalk": "Discuscioin insce e modiffiche fæte da questo adreçço IP",
+       "tooltip-pt-preferences": "E {{GENDER:|to}} preferençe",
        "tooltip-pt-watchlist": "A lista de pagine che ti g'hæ sotta osservaçion",
-       "tooltip-pt-mycontris": "E mæ contribuçioìn",
+       "tooltip-pt-mycontris": "A lista de {{GENDER:|to}} contribuçioin",
+       "tooltip-pt-anoncontribs": "Un elenco de modiffiche fæte da questo adreçço IP",
        "tooltip-pt-login": "Consegemmo a registraçión, ma a no l'è obrigatoia.",
        "tooltip-pt-logout": "Sciorti",
        "tooltip-pt-createaccount": "Se conseggia de registrase e de intrâ, sciben che no segge obligatoio",
        "tooltip-ca-edit": "Modiffica sta paggina.",
        "tooltip-ca-addsection": "Inça 'na seçión nêuva",
        "tooltip-ca-viewsource": "'Sta pagina a l'è protetta, ma ti peu védde o sêu còdice sorgente.",
-       "tooltip-ca-history": "Verscioìn précedenti da pàgina",
+       "tooltip-ca-history": "Verscioìn precedente da pàgina",
        "tooltip-ca-protect": "Protezi 'sta paggina",
+       "tooltip-ca-unprotect": "Càngia a proteçión de sta paggina",
        "tooltip-ca-delete": "Scancella sta pàgina",
+       "tooltip-ca-undelete": "Ripristina a paggina com'a l'ea primma da scancelaçion",
        "tooltip-ca-move": "Mescia 'sta paggina (cangia tittolo)",
        "tooltip-ca-watch": "Azónzi 'sta pagina a-a teu lista d'oservaçion",
        "tooltip-ca-unwatch": "Leva sta pàgina da sott'oservaçion.",
        "tooltip-search": "Çerca {{SITENAME}}",
        "tooltip-search-go": "Vànni inte 'na pàgina con sto tìtolo, s'a l'existe",
-       "tooltip-search-fulltext": "Çerca sto testo in scie pàgine",
+       "tooltip-search-fulltext": "Çerca sto testo in sce pàgine",
        "tooltip-p-logo": "Vìxita a pàgina prinçipâ",
        "tooltip-n-mainpage": "Vìxita a pagina prinçipâ",
        "tooltip-n-mainpage-description": "Vìxita a pàgina prinçipâ",
        "tooltip-n-help": "Pagine d'agiùtto",
        "tooltip-t-whatlinkshere": "Lista de tùtte e pagine che son colegæ a sta chì.",
        "tooltip-t-recentchangeslinked": "Ùrtimi càngi de pàgine colegæ a quésta",
+       "tooltip-feed-rss": "Feed RSS pe questa paggina",
        "tooltip-feed-atom": "Feed Atom pe sta pàgina",
-       "tooltip-t-contributions": "Lista de contribûssioîn de quest'utente",
-       "tooltip-t-emailuser": "Invia 'n messaggio e-mail a quest'utente",
+       "tooltip-t-contributions": "Lista de contribûssioin de {{GENDER:$1|questo|questa}} utente",
+       "tooltip-t-emailuser": "Invia un messaggio email a {{GENDER:$1|questo|questa}} utente",
+       "tooltip-t-info": "Urteioî informaçioin insce questa pagina",
        "tooltip-t-upload": "Carrega di file murtimediali",
        "tooltip-t-specialpages": "Lista de tùtte e pagine speçiâli",
-       "tooltip-t-print": "Versción da stànpa pe sta pàgina",
+       "tooltip-t-print": "Verscion stanpabbile de sta paggina",
        "tooltip-t-permalink": "Colegaménto fisso a sta revixión da pàgina",
        "tooltip-ca-nstab-main": "Véddi a vôxe",
        "tooltip-ca-nstab-user": "Veddi a pàgina d'utente",
+       "tooltip-ca-nstab-media": "Veddi a paggina do file murtimediâ",
        "tooltip-ca-nstab-special": "Sta chi l'è 'na pàgina speciâle e a no peu êse cangiâ",
        "tooltip-ca-nstab-project": "Veddi a paggina de servissio",
        "tooltip-ca-nstab-image": "Veddi a paggina do file",
+       "tooltip-ca-nstab-mediawiki": "Veddi o messaggio de scistema",
        "tooltip-ca-nstab-template": "Veddi o template",
        "tooltip-ca-nstab-help": "Veddi a paggina d'agiûtto",
        "tooltip-ca-nstab-category": "Veddi a paggina da categorîa",
        "tooltip-minoredit": "Marchilo comme cangiaménto minô",
        "tooltip-save": "Sarva i cangiaménti",
+       "tooltip-publish": "Pubbrica e to modiffiche",
        "tooltip-preview": "Anteprimma de modiffiche (fannila, primma de sarvâ!)",
        "tooltip-diff": "Ammîa e modiffiche che t'æ fæto a-o testo.",
        "tooltip-compareselectedversions": "Amia e diferense tra e doê verscioìn seleçionæ de sta paggina chì.",
-       "tooltip-watch": "Azónzi sta pàgina a-a têu lista d'osservæ speçiâli",
+       "tooltip-watch": "Azónzi sta pàgina a-a têu lista di öservæ speçiâli",
+       "tooltip-watchlistedit-normal-submit": "Leva i tittoli",
+       "tooltip-watchlistedit-raw-submit": "Aggiorna a lista di öservæ speciali",
+       "tooltip-recreate": "Ricrea a pagina sciben ch'a segge stæta scassâ",
+       "tooltip-upload": "Comença o caregamento",
        "tooltip-rollback": "\"Rollback\" annulla e modiffiche a sta pagina de l'urtimo contributô co-in solo clic.",
        "tooltip-undo": "\"Anùlla\" o permette de anulâ sto cangiaménto e o l'arve o modulo de cangiaménto inta modalitæ anteprìmma. Ti peu ascì métte a raxón inte l'ogetto do cangiaménto.",
+       "tooltip-preferences-save": "Sarva e preferençe",
        "tooltip-summary": "Scrîvi 'na scintexi",
        "common.css": "/** i stili css scriti chie se applicana tutte e skin */",
-       "anonymous": "Utente anonimmo de {{SITENAME}}",
+       "anonymous": "{{PLURAL:$1|Utente anonnimo|Utenti anonnimi}} de {{SITENAME}}",
+       "siteuser": "$1, utente de {{SITENAME}}",
+       "anonuser": "$1, utente anonnimo de {{SITENAME}}",
        "lastmodifiedatby": "Sta pagina a l'è stæta cangiâ l'urtima votta a e $2 do $1 da $3.",
+       "othercontribs": "O testo attoale o l'è basou in sciô travaggio de $1.",
+       "others": "atri",
+       "siteusers": "$1, {{PLURAL:$2|{{GENDER:$1|utente}}|utenti}} de {{SITENAME}}",
+       "anonusers": "$1, {{PLURAL:$2|utente anonnimo|utenti anonnimi}} de {{SITENAME}}",
+       "creditspage": "Aotoî da paggina",
+       "nocredits": "Nisciun-a informaçion in scî aotoî disponibbile pe questa paggina.",
+       "spamprotectiontitle": "Filtro anti-spam",
+       "spamprotectiontext": "A pagina che s'è tentou dr sarvâ  a l'è stæta bloccâ da-o filtro anti-spam. Questo l'è probabilmente dovuo a-a presença de 'n collegamento a 'n scito esterno presente inta blacklist.",
+       "spamprotectionmatch": "O filtro anti-spam o l'è stæto attivou da-o seguente scrito: $1",
+       "spambot_username": "MediaWiki - scistema de netezzo do spam",
+       "spam_reverting": "Ripristinata l'urtima verscion priva de collegamenti a $1",
+       "spam_blanking": "Paggina svuâ, tutte e verscioin contegnivan di collegamenti a $1",
+       "spam_deleting": "Paggina scassâ, tutte e verscioin contegnivan di collegamenti a $1",
        "simpleantispam-label": "Controllo anti-spam.\n<strong>NO</strong> impîlo!",
+       "pageinfo-title": "Informaçioin pe \"$1\"",
+       "pageinfo-not-current": "Spiaxente, ma l'è imposcibbile fornî quest'informaçion pe de vege verscioin.",
+       "pageinfo-header-basic": "Informaçion de base",
+       "pageinfo-header-edits": "Cronologia de modiffiche",
+       "pageinfo-header-restrictions": "Proteçion da paggina",
+       "pageinfo-header-properties": "Propietæ da paggina",
+       "pageinfo-display-title": "Tittolo vixualizzou",
+       "pageinfo-default-sort": "Criteio d'ordenamento predefinio",
+       "pageinfo-length": "Longheçça da paggina (in byte)",
+       "pageinfo-article-id": "ID da paggina",
        "pageinfo-language": "Lengua do contegnuo da paggina",
+       "pageinfo-content-model": "Modello do contegnuo da paggina",
+       "pageinfo-robot-policy": "Indiçizzaçion pe-i robot",
+       "pageinfo-robot-index": "Consentio",
+       "pageinfo-robot-noindex": "Non consentio",
+       "pageinfo-watchers": "Nummero di utenti che g'han a paggina in öservaçion",
+       "pageinfo-visiting-watchers": "Nummero di öservatoî da paggina ch'han vixitou e modiffiche reçente",
+       "pageinfo-few-watchers": "Meno de $1 {{PLURAL:$1|öservatô|öservatoî}}",
+       "pageinfo-few-visiting-watchers": "Ghe poriæ ese ò meno di öservatoî ch'han vixitou e modiffiche reçente",
+       "pageinfo-redirects-name": "Nummero de redirect a questa paggina",
+       "pageinfo-subpages-name": "Sottopaggine de questa paggina",
+       "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|redirect}}; $3 {{PLURAL:$3|non redirect}})",
+       "pageinfo-firstuser": "Creatô da paggina",
+       "pageinfo-firsttime": "Dæta de creaçion da paggina",
+       "pageinfo-lastuser": "Urtimo contributô",
+       "pageinfo-lasttime": "Dæta de l'urtima modiffica",
+       "pageinfo-edits": "Nummero totale de modiffiche",
+       "pageinfo-authors": "Nummero totale di aotoî distinti",
+       "pageinfo-recent-edits": "Nummero de modiffiche reçente (inti urtimi $1)",
+       "pageinfo-recent-authors": "Nummero di aotoî distinti reçenti",
+       "pageinfo-magic-words": "{{PLURAL:$1|Paola maggica|Paole maggiche}} ($1)",
+       "pageinfo-hidden-categories": "{{PLURAL:$1|Categoria ascosa|Categorie ascose}} ($1)",
+       "pageinfo-templates": "Template {{PLURAL:$1|incluso|incluxi}} ($1)",
+       "pageinfo-transclusions": "{{PLURAL:$1|Paggina inta|Paggine inte}} quæ l'è incluso ($1)",
        "pageinfo-toolboxlink": "Informassioin in sciâ paggina",
+       "pageinfo-redirectsto": "Rendiriçça a",
+       "pageinfo-redirectsto-info": "info",
+       "pageinfo-contentpage": "Contezâ comme una paggina de contegnuo",
        "pageinfo-contentpage-yes": "Sci",
+       "pageinfo-protect-cascading": "Proteçioin ricorscive da chie",
        "pageinfo-protect-cascading-yes": "Sci",
+       "pageinfo-protect-cascading-from": "Proteçioin ricorscive ereditæ da",
+       "pageinfo-category-info": "Informaçion in sciâ categoria",
+       "pageinfo-category-total": "Nummero totale di membri",
+       "pageinfo-category-pages": "Nummero de paggine",
+       "pageinfo-category-subcats": "Nummio de sottacategorie",
+       "pageinfo-category-files": "Nummero di file",
+       "markaspatrolleddiff": "Marca comme controlâ",
+       "markaspatrolledtext": "Marca sta paggina comme controlâ",
+       "markaspatrolledtext-file": "Marca a verscion de sto file comme controlâ",
+       "markedaspatrolled": "Marcâ comme controlâ",
+       "markedaspatrolledtext": "A modiffica de [[:$1]] seleçionâ a l'è stæta marcâ comme veificâ.",
+       "rcpatroldisabled": "A verifica di urtime modiffiche a l'è disattivâ",
+       "rcpatroldisabledtext": "A fonçion de controllo di urtime cangiamenti pe-o momento a no l'è attiva.",
+       "markedaspatrollederror": "Imposcibbile marcâ a voxe comme controlâ",
+       "markedaspatrollederrortext": "Ocore specificâ una revixon da marcâ comme controlâ.",
+       "markedaspatrollederror-noautopatrol": "No ti poeu marcâ e toeu modiffiche comme controllæ.",
+       "markedaspatrollednotify": "A modifica a $1 a l'è stæta contrassegnâ comme veificâ.",
+       "markedaspatrollederrornotify": "Errô durante a veifica.",
+       "patrol-log-page": "Modiffiche controlæ",
+       "patrol-log-header": "Questo o l'è 'n registro de revixoin controlæ.",
+       "log-show-hide-patrol": "$1 registro di controlli",
+       "log-show-hide-tag": "$1 registro di etichette",
+       "deletedrevision": "Scassou a vegia verscion de $1.",
+       "filedeleteerror-short": "Errô inta scassatua do file: $1",
+       "filedeleteerror-long": "Gh'è stæto di erroî into tentativo de scassâ o file:\n\n$1",
+       "filedelete-missing": "Imposcibbile scassâ o file \"$1\" perchè o no l'existe.",
+       "filedelete-old-unregistered": "A verscion do file indicâ, \"$1\", a no l'è contegnua into database.",
+       "filedelete-current-unregistered": "O file specificou, \"$1\", o no l'è contegnuo into database.",
+       "filedelete-archive-read-only": "O server web o no l'è in graddo de scrive inta directory d'archivvio ($1).",
        "previousdiff": "← Diferensa precedénte",
        "nextdiff": "Pròscima diferensa →",
-       "thumbsize": "Dimescion da a imàginetta:",
+       "mediawarning": "'''Attençione''': Questo file o poriæ contegnî un codiçe maligno. A so esecuçion a poriæ dannezzâ o to scistema.",
+       "imagemaxsize": "Dimenscion mascima de ibmaggine:<br />''(pe-e paggine de descriçion do file)''",
+       "thumbsize": "Dimescion de l'imaginetta:",
+       "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|paggina|paggine}}",
+       "file-info": "dimenscion do file: $1, tipo MIME: $2",
        "file-info-size": "$1 × $2 pixel, dimenscioîn: $3, tippo MIME: $4",
-       "file-nohires": "No ghe son verscioìn a rezoluçion ciù âta.",
+       "file-info-size-pages": "$1 × $2 pixel, dimenscion do file: $3, tipo MIME: $4, $5 {{PLURAL:$5|paggina|paggine}}",
+       "file-nohires": "Verscioìn co-a resoluçion ciù âta no ghe n'è.",
        "svg-long-desc": "file in formato SVG, dimenscioìn nominâli $1 × $2 pixel, dimenscioin do file: $3",
+       "svg-long-desc-animated": "file in formato SVG animou, dimenscion nominale $1 × $2 pixel, dimenscion do file: $3",
+       "svg-long-error": "File SVG non vallido: $1",
        "show-big-image": "File originâ",
        "show-big-image-preview": "Dimenscioin de questa anteprimma: $1.",
+       "show-big-image-preview-differ": "Dimension de questa anteprimma $3 pe questo file $2: $1.",
        "show-big-image-other": "{{PLURAL:$2|Atra risolussion|Atre risolussioin}}: $1.",
        "show-big-image-size": "$1 × $2 pixel",
-       "newimages": "Gallerîa de nêuvi archivvi",
+       "file-info-gif-looped": "cicclico",
+       "file-info-gif-frames": "$1 {{PLURAL:$1|frame}}",
+       "file-info-png-looped": "cicclico",
+       "file-info-png-repeat": "ripetuo $1 {{PLURAL:$1|votta|votte}}",
+       "file-info-png-frames": "$1 {{PLURAL:$1|frame}}",
+       "file-no-thumb-animation": "'''Notta: pe de limitaçioin tecniche, e miniatue de questo file no saian animæ.'''",
+       "file-no-thumb-animation-gif": "'''Notta: pe de limitaçioin tecniche, e miniatue de immaggine GIF a ata risoluçion comme questa no saian animæ.'''",
+       "newimages": "Galleria di nêuvi file",
+       "imagelisttext": "A lista presentâ chì de sotta, costituia da {{PLURAL:$1|un file|'''$1''' file}}, a l'è amerçâ $2.",
+       "newimages-summary": "Questa pagina speciale a mostra i urtimi file caregæ.",
+       "newimages-legend": "Filtro",
+       "newimages-label": "Nomme do file (o una parte de questo):",
+       "newimages-showbots": "Mostra i caregamenti fæti dai bot",
+       "newimages-hidepatrolled": "Ascondi i caregamenti controlæ",
+       "noimages": "No gh'è ninte da vedde.",
        "ilsubmit": "Çerca",
        "bydate": "pe dâta",
+       "sp-newimages-showfrom": "Mostra i file ciù reçenti a partî da-e oe $2 do $1",
+       "seconds": "{{PLURAL:$1|un segondo|$1 segondi}}",
+       "minutes": "{{PLURAL:$1|un menuto|$1 menuti}}",
+       "hours": "{{PLURAL:$1|un'oa|$1 oe}}",
+       "days": "{{PLURAL:$1|$1 giorno|$1 giorni}}",
+       "weeks": "{{PLURAL:$1|$1 setteman-a|$1 setteman-e}}",
+       "months": "{{PLURAL:$1|$1 meise|$1 meixi}}",
+       "years": "{{PLURAL:$1|$1 anno|$1 anni}}",
+       "ago": "$1 fa",
+       "just-now": "proppio oua",
+       "hours-ago": "$1 {{PLURAL:$1|oa|oe}} fa",
+       "minutes-ago": "$1 {{PLURAL:$1|menuto|menuti}} fa",
+       "seconds-ago": "$1 {{PLURAL:$1|segondo|segondi}} fa",
        "monday-at": "Lunesdì a $1",
        "tuesday-at": "Matesdì a $1",
        "wednesday-at": "Mâcordì a $1",
        "metadata-expand": "Fâ vedde dettaggi",
        "metadata-collapse": "Asconde dettaggi",
        "metadata-fields": "I cànpi relativi a-i metadæti EXIF elencæ inte 'sto messaggio saiàn in sciâ pàgina de l'inmàgine quande a tabella di metadæti a saiâ inta forma cùrta. Cómme predefinîo, i âtri cànpi saiàn ascoxi.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
+       "exif-imagewidth": "Largheçça",
+       "exif-imagelength": "Ateçça",
+       "exif-bitspersample": "Bit pe campion",
+       "exif-compression": "Meccanismo de comprescion",
+       "exif-photometricinterpretation": "Struttua di pixel",
        "exif-orientation": "Orientamento",
+       "exif-samplesperpixel": "Nummero di componenti",
+       "exif-planarconfiguration": "Dispoxiçion di dæti",
+       "exif-ycbcrsubsampling": "Rapporto de campionamento Y / C",
+       "exif-ycbcrpositioning": "Poxiçionamento di componenti Y e C",
        "exif-xresolution": "Risoluçion orizzontâ",
        "exif-yresolution": "Risoluçion verticâ",
+       "exif-stripoffsets": "Poxiçion di dæti de l'inmaggine",
+       "exif-rowsperstrip": "Nummero righe pe striscia",
+       "exif-stripbytecounts": "Nummero di byte pe striscia compressa",
+       "exif-jpeginterchangeformat": "Poxiçion byte SOI JPEG",
+       "exif-jpeginterchangeformatlength": "Nummero di byte di dæti JPEG",
+       "exif-whitepoint": "Coordinæ cromattiche do ponto de gianco",
+       "exif-primarychromaticities": "Coordinæ cromattiche di coî primai",
+       "exif-ycbcrcoefficients": "Coeffixenti de matrixe de transformaçion di spaççi de cô",
+       "exif-referenceblackwhite": "Cobbia di valoî de rifeimento (neigro e gianco)",
        "exif-datetime": "Dæta e öa da modiffica do file",
+       "exif-imagedescription": "Tittolo de l'inmaggine",
        "exif-make": "Produttô fotocamera",
        "exif-model": "Modello fotocamera",
        "exif-software": "Software",
        "exif-artist": "Autô",
        "exif-copyright": "Diritti d'autô de",
        "exif-exifversion": "Verscion do formato Exif",
+       "exif-flashpixversion": "Verscion Flashpix supportâ",
        "exif-colorspace": "Spassio di coî",
+       "exif-componentsconfiguration": "Scignificou de ogni componente",
+       "exif-compressedbitsperpixel": "Modalitæ de comprescion immaggine",
+       "exif-pixelxdimension": "Largheçça immaggine",
+       "exif-pixelydimension": "Ateçça immaggine",
+       "exif-usercomment": "Notte de l'utente",
+       "exif-relatedsoundfile": "File audio collegou",
        "exif-datetimeoriginal": "Dæta e oa de creassion di dæti",
        "exif-datetimedigitized": "Dæta e oa de digitalizzaçion",
+       "exif-subsectime": "Dæta e oa, fraçioin de segondo",
+       "exif-subsectimeoriginal": "Dæta e oa de creaçion, fraçioin de segondo",
+       "exif-subsectimedigitized": "Dæta e oa de digitalizzaçion, fraçioin dr segondo",
+       "exif-exposuretime": "Tempo de espoxiçion",
+       "exif-exposuretime-format": "$1 s ($2)",
+       "exif-fnumber": "Rapporto focâ",
+       "exif-exposureprogram": "Programma de espoxiçion",
+       "exif-spectralsensitivity": "Senscibilitæ spettrâ",
+       "exif-isospeedratings": "Senscibilitæ ISO",
+       "exif-shutterspeedvalue": "Veloçitæ de l'otturatô APEX",
+       "exif-aperturevalue": "Avertua APEX",
+       "exif-brightnessvalue": "Luminoxitæ APEX",
+       "exif-exposurebiasvalue": "Correçion espoxiçion",
+       "exif-maxaperturevalue": "Avertua mascima",
+       "exif-subjectdistance": "Distança do soggetto",
+       "exif-meteringmode": "Mettodo de mesuaçion",
+       "exif-lightsource": "Sorgente luminosa",
+       "exif-flash": "Caratteristeghe e stæto do flash",
+       "exif-focallength": "Distança focâ de l'obiettivo",
+       "exif-subjectarea": "Area inquadrante o soggetto",
+       "exif-flashenergy": "Potença do flash",
+       "exif-focalplanexresolution": "Risoluçion X in sciô cian focâ",
+       "exif-focalplaneyresolution": "Risoluçion Y in sciô cian focâ",
+       "exif-focalplaneresolutionunit": "Unitæ de mesua risoluçion in sciô cian focâ",
+       "exif-subjectlocation": "Poxiçion do soggetto",
+       "exif-exposureindex": "Senscibilitæ impostâ",
+       "exif-sensingmethod": "Mettodo de rilevaçion",
        "exif-filesource": "Origgine do file",
+       "exif-scenetype": "Tipo de inquadratua",
+       "exif-customrendered": "Elaboaçion personalizzâ",
+       "exif-exposuremode": "Modalitæ d'espoxiçion",
+       "exif-whitebalance": "Bançamento do gianco",
+       "exif-digitalzoomratio": "Rapporto zoom digitale",
+       "exif-focallengthin35mmfilm": "Longheçça focâ sciu peliccola 35 mm",
+       "exif-scenecapturetype": "Tipo de aquixiçion da scena",
+       "exif-gaincontrol": "Controllo inquadratua",
+       "exif-contrast": "Controllo contrasto",
+       "exif-saturation": "Controllo saturaçion",
+       "exif-sharpness": "Controllo nitideçça",
+       "exif-devicesettingdescription": "Descriçion de impostaçioin do dispoxitivo",
+       "exif-subjectdistancerange": "Scaa distança soggetto",
+       "exif-imageuniqueid": "ID univvoco immaggine",
+       "exif-gpsversionid": "Verscion di tag GPS",
+       "exif-gpslatituderef": "Latituddine nord/sud",
+       "exif-gpslatitude": "Latituddine",
+       "exif-gpslongituderef": "Lonxituddine est/ovest",
+       "exif-gpslongitude": "Lonxituddine",
+       "exif-gpsaltituderef": "Rifeimento pe l'ærtituddine",
+       "exif-gpsaltitude": "Ærtituddine",
+       "exif-gpstimestamp": "Oa GPS (reloeuio atommico)",
+       "exif-gpssatellites": "Satelliti doeuviæ pe-a mesuaçion",
+       "exif-gpsstatus": "Stato do riçevitô",
+       "exif-gpsmeasuremode": "Modalitæ de mesuaçion",
+       "exif-gpsdop": "Precixone da mezuaçion",
+       "exif-gpsspeedref": "Unitæ de mesua da veloçitæ",
+       "exif-gpsspeed": "Veloçitæ do riçevitô GPS",
+       "exif-gpstrackref": "Rifeimento pe-a direçion de movimento",
+       "exif-gpstrack": "Direçion do movimento",
+       "exif-gpsimgdirectionref": "Rifeimento pe-a direçion de l'immaggine",
+       "exif-gpsimgdirection": "Direçion de l'immaggine",
+       "exif-gpsmapdatum": "Rilevamento geodetico doeuviou",
+       "exif-gpsdestlatituderef": "Rifeimento pe-a latituddine da destinaçion",
+       "exif-gpsdestlatitude": "Latituddine da destinaçion",
+       "exif-gpsdestlongituderef": "Rifeimento pe-a lonxituddine da destinaçion",
+       "exif-gpsdestlongitude": "Lonxituddine da destinaçion",
+       "exif-gpsdestbearingref": "Rifeimento pe-a direçion da destinaçion",
+       "exif-gpsdestbearing": "Direçion da destinaçion",
+       "exif-gpsdestdistanceref": "Rifeimento pe-a distança da destinaçion",
+       "exif-gpsdestdistance": "Distança da destinaçion",
+       "exif-gpsprocessingmethod": "Nomme do mettodo de elaboaçion GPS",
+       "exif-gpsareainformation": "Nomme da zona GPS",
+       "exif-gpsdatestamp": "Data GPS",
+       "exif-gpsdifferential": "Correçion differençiâ GPS",
+       "exif-jpegfilecomment": "Commento do file JPEG",
+       "exif-keywords": "Paole ciave",
+       "exif-worldregioncreated": "Region do mondo donde l'è stæto scattou l'immaggine",
+       "exif-countrycreated": "Paise dovve l'è stæto scattou a foto",
+       "exif-countrycodecreated": "Codiçe do paise dovve l'è stæto scattou a foto",
+       "exif-provinceorstatecreated": "Provincia o stato dovve l'è stæto scattou a foto",
+       "exif-citycreated": "Çittæ dovve l'è stæto scattou a foto",
+       "exif-sublocationcreated": "Parte da çittæ donde l'è stæto scattou a foto",
+       "exif-worldregiondest": "Region do mondo vixualizzâ",
+       "exif-countrydest": "Naçion vixualizzâ",
+       "exif-countrycodedest": "Codiçe pe-o paise indicou",
+       "exif-provinceorstatedest": "Provincia o stato vixualizzou",
+       "exif-citydest": "Çittæ mostrâ",
+       "exif-sublocationdest": "Parte da çittæ visualizzâ",
+       "exif-objectname": "Tittolo curto",
+       "exif-specialinstructions": "Instruçioin speciale",
+       "exif-headline": "Tittolo",
+       "exif-credit": "Credditi",
+       "exif-source": "Fonte",
+       "exif-editstatus": "Stato d'ediçion de l'inmaggine",
+       "exif-urgency": "Urgença",
+       "exif-fixtureidentifier": "Nomme do rifeimento",
+       "exif-locationdest": "Localitæ raffiguâ",
+       "exif-locationdestcode": "Coddiçe do loeugo raffiguou",
+       "exif-objectcycle": "Parte do giorno a-o quæ o medium o l'è destinou",
+       "exif-contact": "Contatti",
+       "exif-writer": "Chi l'ha scrito",
        "exif-languagecode": "Lengua",
+       "exif-iimversion": "Verscion IIM",
+       "exif-iimcategory": "Categoria",
+       "exif-iimsupplementalcategory": "Categorie azontive",
+       "exif-datetimeexpires": "No doeuviâ doppo",
+       "exif-datetimereleased": "Pubricou o",
+       "exif-originaltransmissionref": "Coddiçe do loeugo de transmiscion originaia",
+       "exif-identifier": "Identificatô",
+       "exif-lens": "Obiettivo doeuviou",
+       "exif-serialnumber": "Nummero de serrie da fotocammia",
+       "exif-cameraownername": "Proprietaio da macchina fotograffica",
+       "exif-label": "Etichetta",
+       "exif-datetimemetadata": "Dæta inta quæ i metadata son stæti modificæ l'urtima votta",
+       "exif-nickname": "Nomme informale de l'immaggine",
+       "exif-rating": "Valutaçion (su 5)",
+       "exif-rightscertificate": "Çertificou de gestion di driti",
+       "exif-copyrighted": "Informaçioin in scî driti d'aotô:",
+       "exif-copyrightowner": "Detentô di driti d'aotô",
+       "exif-usageterms": "Termi d'utilizzo",
+       "exif-webstatement": "Deciaraçion online do copyright",
+       "exif-originaldocumentid": "ID univvoco do documento origin*a",
+       "exif-licenseurl": "URL pe-a liçença do copyright",
+       "exif-morepermissionsurl": "Informaçioin insce e liçençe alternative",
+       "exif-attributionurl": "Se quest'oeuvia ti l'adoeuvi torna, pe piaxei coleghite a",
+       "exif-preferredattributionname": "Se quest'oeuvia ti l'adoeuvi torna, pe piaxei attribuiscine a paternitæ a",
+       "exif-pngfilecomment": "Commento do file JPEG",
+       "exif-disclaimer": "Avertençe",
+       "exif-contentwarning": "Avviso in sciô contegnuo",
+       "exif-giffilecomment": "Commento do file GIF",
+       "exif-intellectualgenre": "Tipo d'elemento",
+       "exif-subjectnewscode": "Coddiçe de l'ögetto",
+       "exif-scenecode": "Coddiçe de scena IPTC",
+       "exif-event": "Evento raffiguou",
+       "exif-organisationinimage": "Organizzaçion raffiguâ",
+       "exif-personinimage": "Person-a raffiguâ",
+       "exif-originalimageheight": "Ateçça de l'inmaggine primma ch'a foise retagiâ",
+       "exif-originalimagewidth": "Largheçça de l'immaggine primma ch'a foise retagiâ",
+       "exif-compression-1": "Nisciun",
+       "exif-compression-2": "CCITT gruppo 3 monodimenscionâ - codifica run length di Huffman modificâ",
+       "exif-compression-3": "Codiffica fax CCITT Group 3",
+       "exif-compression-4": "Codiffica fax CCITT groppo 4",
+       "exif-copyrighted-true": "Protezuo da-o drito d'aotô",
+       "exif-copyrighted-false": "Stato do drito d'aotô non impostou",
+       "exif-photometricinterpretation-1": "Gianco e neigro (o neigro o l'è 0)",
+       "exif-unknowndate": "Dæta sconosciua",
        "exif-orientation-1": "Normale",
+       "exif-orientation-2": "Imbösòu orizontalmente",
+       "exif-orientation-3": "Curlou de 180°",
+       "exif-orientation-4": "Imbösòu verticalmente",
+       "exif-orientation-5": "Curlou 90° in senso antiorario e imbösòu verticalmente",
+       "exif-orientation-6": "Curlou di 90° in senso antioraio",
+       "exif-orientation-7": "Curlou 90° in senso oraio e imbosou verticalmente",
+       "exif-orientation-8": "Curlou di 90° in senso oraio",
+       "exif-planarconfiguration-1": "formato a tocchi",
+       "exif-planarconfiguration-2": "formato planare",
+       "exif-colorspace-65535": "Non calibrou",
+       "exif-componentsconfiguration-0": "assente",
+       "exif-exposureprogram-0": "Non definio",
+       "exif-exposureprogram-1": "Manoale",
+       "exif-exposureprogram-2": "Programma normale",
+       "exif-exposureprogram-3": "Prioritæ a-o diaframma",
+       "exif-exposureprogram-4": "Prioritæ a l'espoxiçion",
+       "exif-exposureprogram-5": "Artistego (orientou a-a profonditæ de campo)",
+       "exif-exposureprogram-6": "Sportivo (orientou a-a veloçitæ de ripreisa)",
+       "exif-exposureprogram-7": "Ritræto (soggetti vexin con sfondo sfocou)",
+       "exif-exposureprogram-8": "Panoramma (soggetti lonten con sfondo a foeugo)",
+       "exif-subjectdistance-value": "$1 metri",
+       "exif-meteringmode-0": "Sconosciuo",
+       "exif-meteringmode-1": "Meddia",
+       "exif-meteringmode-2": "Meddia pesâ into centro",
+       "exif-meteringmode-3": "Spot",
+       "exif-meteringmode-4": "Murti-Spot",
+       "exif-meteringmode-5": "Pattern",
+       "exif-meteringmode-6": "Parçiâ",
+       "exif-meteringmode-255": "Atro",
+       "exif-lightsource-0": "Sconosciua",
+       "exif-lightsource-1": "Luxe diurna",
+       "exif-lightsource-2": "Lampa a fluorescença",
+       "exif-lightsource-3": "Lampa a-o tungsteno (a incandescença)",
+       "exif-lightsource-4": "Flash",
+       "exif-lightsource-9": "Tempo bello",
+       "exif-lightsource-10": "Nuvio",
+       "exif-lightsource-11": "Ombra",
+       "exif-lightsource-12": "Diurno fluorescente (D 5700 - 7100K)",
+       "exif-lightsource-13": "Giorno gianco fluorescente (N 4600 - 5400K)",
+       "exif-lightsource-14": "Gianco freido fluorescente (W 3900 - 4500K)",
+       "exif-lightsource-15": "Gianco fluorescente (WW 3200 - 3700K)",
+       "exif-lightsource-17": "Luxe standard A",
+       "exif-lightsource-18": "Luxe standard B",
+       "exif-lightsource-19": "Luxe standard C",
+       "exif-lightsource-24": "Lampa da studdio ISO a-o tungsten",
+       "exif-lightsource-255": "Atra sorgente luminosa",
+       "exif-flash-fired-0": "O flash o no l'è scattou",
+       "exif-flash-fired-1": "O flash o l'è scattou",
+       "exif-flash-return-0": "nisciun-a fonçion de individuaçion do ritorno da luxe stroboscoppica",
+       "exif-flash-return-2": "luxe stroboscoppica de ritorno non individoâ",
+       "exif-flash-return-3": "luxe stroboscoppica de ritorno individoâ",
+       "exif-flash-mode-1": "attivaçion forçâ do flash",
+       "exif-flash-mode-2": "rimoçion forçâ do flash",
+       "exif-flash-mode-3": "modalitæ aotomattica",
+       "exif-flash-function-1": "Disattiva o flash",
+       "exif-flash-redeye-1": "modalitæ riduçion oeuggi rosci",
+       "exif-focalplaneresolutionunit-2": "polliçi",
+       "exif-sensingmethod-1": "Non definio",
+       "exif-sensingmethod-2": "Sensô area cô a 1 chip",
+       "exif-sensingmethod-3": "Sensô area cô a 2 chip",
+       "exif-sensingmethod-4": "Sensô area colô a 3 chip",
+       "exif-sensingmethod-5": "Sensô area cô sequençiale",
+       "exif-sensingmethod-7": "Sensô triliniare",
+       "exif-sensingmethod-8": "Sensô liniare de cô sequençiale",
+       "exif-filesource-3": "Fotocammia digitâ",
+       "exif-scenetype-1": "Fotografia diretta",
+       "exif-customrendered-0": "Processo normale",
+       "exif-customrendered-1": "Processo personalizzou",
+       "exif-exposuremode-0": "Espoxiçion aotomattica",
+       "exif-exposuremode-1": "Espoxiçion manoâ",
+       "exif-exposuremode-2": "Bracketing aotomattico",
+       "exif-whitebalance-0": "Bançamento do gianco aotomattico",
+       "exif-whitebalance-1": "Bançamento do gianco manoâ",
+       "exif-scenecapturetype-0": "Standard",
+       "exif-scenecapturetype-1": "Panorama",
+       "exif-scenecapturetype-2": "Ritræto",
+       "exif-scenecapturetype-3": "Notturna",
+       "exif-gaincontrol-0": "Nisciun",
+       "exif-gaincontrol-1": "Enfaxi pe basso guaagno",
+       "exif-gaincontrol-2": "Enfaxi pe ato guaagno",
+       "exif-gaincontrol-3": "Deenfaxi pe basso guaagno",
+       "exif-gaincontrol-4": "Deenfaxi pe ato guaagno",
+       "exif-contrast-0": "Normale",
+       "exif-contrast-1": "Ato contrasto",
+       "exif-contrast-2": "Basso contrasto",
+       "exif-saturation-0": "Normale",
+       "exif-saturation-1": "Bassa saturaçion",
+       "exif-saturation-2": "Ata saturaçion",
+       "exif-sharpness-0": "Normale",
+       "exif-sharpness-1": "Minô nitideçça",
+       "exif-sharpness-2": "Maggiô nitideçça",
+       "exif-subjectdistancerange-0": "Sconosciua",
+       "exif-subjectdistancerange-1": "Macro",
+       "exif-subjectdistancerange-2": "Soggetto vexin",
+       "exif-subjectdistancerange-3": "Soggetto lontan",
+       "exif-gpslatitude-n": "Latituddine nord",
+       "exif-gpslatitude-s": "Latituddine sud",
+       "exif-gpslongitude-e": "Lonxituddine est",
+       "exif-gpslongitude-w": "Lonxituddine ovest",
+       "exif-gpsaltitude-above-sealevel": "$1 {{PLURAL:$1|metro|metri}} in sciô livello do mâ",
+       "exif-gpsaltitude-below-sealevel": "$1 {{PLURAL:$1|metro|metri}} sotta o livello do mâ",
+       "exif-gpsstatus-a": "Mezuaçion in corso",
+       "exif-gpsstatus-v": "Mesuaçion interopiabile",
+       "exif-gpsmeasuremode-2": "Mesuaçion bidimenscionâ",
+       "exif-gpsmeasuremode-3": "Mesuaçion tridimenscionâ",
+       "exif-gpsspeed-k": "Chilommetri orai",
+       "exif-gpsspeed-m": "Miggia oraie",
+       "exif-gpsspeed-n": "Noeui",
+       "exif-gpsdestdistance-k": "Chilommetri",
+       "exif-gpsdestdistance-m": "Miggia",
+       "exif-gpsdestdistance-n": "Miggia naotiche",
+       "exif-gpsdop-excellent": "Eccelente ($1)",
+       "exif-gpsdop-good": "Bon ($1)",
+       "exif-gpsdop-moderate": "Moderâ ($1)",
+       "exif-gpsdop-fair": "Discreto ($1)",
+       "exif-gpsdop-poor": "Scarso ($1)",
+       "exif-objectcycle-a": "Solo a-a mattin",
+       "exif-objectcycle-p": "Solo a-a seia",
+       "exif-objectcycle-b": "Mattin e seia",
+       "exif-gpsdirection-t": "Direçion reale",
+       "exif-gpsdirection-m": "Direçion magnettica",
+       "exif-ycbcrpositioning-1": "Centrou",
+       "exif-ycbcrpositioning-2": "Co-scituou",
+       "exif-dc-contributor": "contributoî",
+       "exif-dc-coverage": "Ambito spaçiâ o tempoâ di meddia",
+       "exif-dc-date": "Dæta (e)",
+       "exif-dc-publisher": "Editô",
+       "exif-dc-relation": "File correlæ",
+       "exif-dc-rights": "Driti",
+       "exif-dc-source": "Fonte do file",
+       "exif-dc-type": "Tipologia do file",
+       "exif-rating-rejected": "Refuou",
+       "exif-isospeedratings-overflow": "Maggiô de 65535",
+       "exif-iimcategory-ace": "Arte, coltua e spetaccolo",
+       "exif-iimcategory-clj": "Crimmine e lezze",
+       "exif-iimcategory-dis": "Disastri e açidenti",
+       "exif-iimcategory-fin": "Economia e affæ",
+       "exif-iimcategory-edu": "Instruçion",
+       "exif-iimcategory-evn": "Ambiente",
+       "exif-iimcategory-hth": "Salute",
+       "exif-iimcategory-hum": "Interesse uman",
+       "exif-iimcategory-lab": "Travaggio",
+       "exif-iimcategory-lif": "Stile di vitta e tempo libbero",
+       "exif-iimcategory-pol": "Polittica",
+       "exif-iimcategory-rel": "Religion e fe'",
+       "exif-iimcategory-sci": "Sciença e tecnologia",
+       "exif-iimcategory-soi": "Questioin sociale",
+       "exif-iimcategory-spo": "Sport",
+       "exif-iimcategory-war": "Guæra, confliti e disordini",
+       "exif-iimcategory-wea": "Meteo",
+       "exif-urgency-normal": "Normale ($1)",
+       "exif-urgency-low": "Bassa ($1)",
+       "exif-urgency-high": "Ata ($1)",
+       "exif-urgency-other": "Prioritæ definie da l'utente ($1)",
        "namespacesall": "Tùtti",
        "monthsall": "tutti",
+       "confirmemail": "Conferma l'adreçço e-mail",
+       "confirmemail_noemail": "No t'hæ indicou un adreçço e-mail vallido inte to [[Special:Preferences|preferençe]].",
+       "confirmemail_text": "{{SITENAME}} o domanda a convallida de l'adreçço e-mail primma de poei doeouviâ e relative fonçioin. Sciacca o pulsante chì de sotta pe inviâ una recesta de conferma a-o proppio addreçço; into messaggio gh'è un collegamento ch'o conten un coddiçe. Vixita o collegamento co-o to navegatô pe confermâ che l'adreçço e-mail o l'è vallido.",
+       "confirmemail_pending": "O coddiçe de conferma o l'è za stæto spedio via posta eletronnica; se l'account o l'è stæto\ncreou de reçente, se prega de attende l'arivo do coddiçe pe quarche menuto primma\nde tentâ de domandâne un noeuvo.",
+       "confirmemail_send": "Invia un coddiçe de conferma via email.",
+       "confirmemail_sent": "Messaggio e-mail de conferma inviou.",
+       "confirmemail_oncreate": "Un coddiçe de conferma o l'è stæto spedio a l'adreçço de posta elettronnica indicou. O coddiçe o no l'è necessaio pe accede a-o scito,\nma l'è necessaio fornîlo pe poei abilitâ tutte e fonçioin do scito ch'adoeuvian a posta elettronnica.",
+       "confirmemail_sendfailed": "{{SITENAME}} o no poeu inviâ o messaggio e-mail de conferma. Controlla che o to adrçço e-mail o no contegne di caratteri non vallidi.\n\nMessaggio de errô do mailer: $1",
+       "confirmemail_invalid": "Coddiçe de conferma non vallido. O codiçe o poriæ ese descheito.",
+       "confirmemail_needlogin": "L'è necessaio $1 confermâ o to adreçço e-mail.",
+       "confirmemail_success": "L'adreçço e-mail o l'è stæto confermou. Oua ti poeu [[Special:UserLogin|intrâ]] e gödîte a wiki.",
+       "confirmemail_loggedin": "L'adreçço e-mail o l'è stæto confermou.",
+       "confirmemail_subject": "{{SITENAME}}: recesta de conferma de l'adreççoo",
+       "confirmemail_body": "Quarcun, foscia ti mæximo da l'adreçço IP $1, o l'ha registrou l'utença \"$2\" insce {{SITENAME}} indicando questo adreçço e-mail.\n\nPe confermâ che l'utença a t'apparten da vei e attivâ e fonçioin relative a l'invio di e-mail insce {{SITENAME}}, arvi o collegamento seguente co-o to navegatô:\n\n$3\n\nSe *no* t'ê stæto ti a registrâ l'utença, segui sto colegamento pe annulâ a conferma de l'adreçço e-mail:\n\n$5\n\nQuesto coddiçe de conferma o descaziâ aotomaticamente a $4.",
+       "confirmemail_body_changed": "Quarcun, foscia ti mæximo da l'adreçço IP $1, o l'ha modificou l'adreçço e-mail de l'utença \"$2\" insce {{SITENAME}} indicando questo adreçço e-mail.\n\nPe confermâ che l'utença a t'apparten da vei e riattivâ e fonçioin relative a l'invio di e-mail insce {{SITENAME}}, arvi o collegamento seguente co-o to navegatô:\n\n$3\n\nSe l'utença a *no* t'aparten, segui sto colegamento pe annulâ a conferma de l'adreçço e-mail:",
+       "confirmemail_body_set": "Quarcun, foscia ti mæximo da l'adreçço IP $1, o l'ha impostcou l'adreçço e-mail de l'utença \"$2\" insce {{SITENAME}} indicando questo adreçço e-mail.\n\nPe confermâ che l'utença a t'apparten da vei e attivâ e fonçioin relative a l'invio di e-mail insce {{SITENAME}}, arvi o collegamento seguente co-o to navegatô:\n\n$3\n\nSe l'utença a *no* t'aparten, segui sto colegamento pe annulâ a conferma de l'adreçço e-mail:",
+       "confirmemail_invalidated": "Recesta de conferma adreçço e-mail annulâ",
+       "invalidateemail": "Annulla a recesta de conferma e-mail",
+       "notificationemail_subject_changed": "L'adreçço de posta elettronica registrou insce {{SITENAME}} o l'è stæto modificou",
+       "notificationemail_subject_removed": "L'adreçço de posta elettronica registrou insce {{SITENAME}} o l'è stæto rimosso",
+       "notificationemail_body_changed": "Quarcun, foscia ti, da l'adreçço IP $1,\no l'ha modificou l'adreçço de posta elettronica de l'utenza \"$2\" in \"$3\" insce {{SITENAME}}.\n\nSe no t'ê stæto ti, contatta subbito un amministratô do scito.",
+       "notificationemail_body_removed": "Quarcun, foscia ti, da l'adreçço IP $1,\no l'ha levou l'adreçço de posta elettronica de l'utenza \"$2\" insce {{SITENAME}}.\n\nSe no t'ê stæto ti, contatta subbito un amministratô do scito.",
+       "scarytranscludedisabled": "[L'incluxone de paggine tra di sciti wiki a no l'è attiva]",
+       "scarytranscludefailed": "[Errô: Imposcibbile ötegnî o template $1]",
+       "scarytranscludefailed-httpstatus": "[Errô: imposcibbile ötegnî o template $1: HTTP $2]",
+       "scarytranscludetoolong": "[Errô: URL troppo longa]",
+       "deletedwhileediting": "'''Attençion''': questa pagina a l'è stæta scassâ doppo che t'hæ començou a modificâla!",
+       "confirmrecreate": "L'utente [[User:$1|$1]] ([[User talk:$1|discuscioin]]) o l'ha scassou sta pagina doppo che t'hæ començou a modificâla, pe-o seguente motivo: ''$2''\nPe piaxei, conferma che ti voeu da vei ricreâ sta paggina.",
+       "confirmrecreate-noreason": "L'utente [[User:$1|$1]] ([[User talk:$1|discuscioin]]){{GENDER:$1|o|a}} l'ha scassou sta pagina doppo che t'hæ començou a modificâla.\nPe piaxei, conferma che ti voeu da vei ricreâ sta paggina.",
+       "recreate": "Ricrea",
+       "confirm_purge_button": "OK",
+       "confirm-purge-top": "Ti voeu netezâ a cache de questa pagina?",
+       "confirm-purge-bottom": "O netezzo da cache de una pagina o consente de mostrâ a so verscion ciù aggiornâ.",
+       "confirm-watch-button": "OK",
+       "confirm-watch-top": "Azónze sta pàgina a-a têu lista di öservæ speçiâli?",
+       "confirm-unwatch-button": "OK",
+       "confirm-unwatch-top": "Leva sta paggina da-a to lista sott'oservaçion.",
+       "confirm-rollback-button": "OK",
+       "confirm-rollback-top": "Ripristinâ e modiffiche de questa paggina?",
+       "quotation-marks": "«$1»",
        "imgmultipageprev": "← Pagina de primma",
        "imgmultipagenext": "Proscima pagina →",
        "imgmultigo": "Vanni!",
+       "imgmultigoto": "Vanni a-a paggina $1",
+       "img-lang-default": "(lengua predefinia)",
+       "img-lang-info": "Converti questa immaggine in $1. $2",
+       "img-lang-go": "Vanni",
        "ascending_abbrev": "cresc",
+       "descending_abbrev": "decresc",
        "table_pager_next": "Proscima pagina",
        "table_pager_prev": "Pagina de primma",
        "table_pager_first": "Primma pagina",
        "table_pager_last": "Urtima pagina",
-       "table_pager_limit": "Fanni devve $1 elementi pe pagina",
+       "table_pager_limit": "Mostra $1 elementi pe paggina",
+       "table_pager_limit_label": "Elementi pe paggina:",
        "table_pager_limit_submit": "Vanni",
        "table_pager_empty": "Nisciun resultato",
        "autosumm-blank": "Pagina svuâ",
        "autosumm-replace": "Sostituçion da pagina con '$1'",
        "autoredircomment": "Reindirissoö a [[$1]]",
        "autosumm-new": "Paggina creâ con \"$1\"",
+       "autosumm-newblank": "Creou paggina voeua",
+       "lag-warn-normal": "E modifiche apportæ {{PLURAL:$1|inte l'urtimo segondo|inti urtimi $1 segondi}} porieivan no apparî inte questa lista.",
+       "lag-warn-high": "A caosa de un eccescivo ritardo inte l'aggiornamento do server de database, e modiffiche apportæ {{PLURAL:$1|inte l'urtimo segondo|inti urtimi $1 segondi}} porieivan no apparî inte questa lista.",
+       "watchlistedit-normal-title": "Modiffica a lista sotta oservaçion",
+       "watchlistedit-normal-legend": "Rimoeuvi di tittoli da-i öservæ speciali",
+       "watchlistedit-normal-explain": "De sotta l'è elencou tutte e pagine sott'öservaçion.\nPe levâ un-a o ciù paggine da-a lista, seleçion-a e caselle relative e clicca o pulsante \"{{int:Watchlistedit-normal-submit}}\" in fondo a l'elenco.\nTi poeu ascì [[Special:EditWatchlist/raw|modificâ a lista in formato testoâ]].",
+       "watchlistedit-normal-submit": "Leva di tittoli",
+       "watchlistedit-normal-done": "Da-a lista di öservæ speciali l'è stæto eliminou {{PLURAL:$1|una pagina|$1 pagine}}:",
+       "watchlistedit-raw-title": "Modiffica a lista sotta oservaçion in formato testo",
+       "watchlistedit-raw-legend": "Modiffica a lista sotta oservaçion in formato testo",
+       "watchlistedit-raw-explain": "De sotta l'è elencou tutte e pagine sott'öservaçion.\nPe cangiâ a lista, azonzi ò leva i rispetivi tittoli, un pe riga. Una votta terminou, clicca o pulsante \"{{int:Watchlistedit-raw-submit}}\" in fondo a l'elenco.\nTi poeu ascì [[Special:EditWatchlist|modificâ a lista co l'interfaccia standard]].",
+       "watchlistedit-raw-titles": "Tittoli:",
+       "watchlistedit-raw-submit": "Agiorna a lista di öservæ speciali",
+       "watchlistedit-raw-done": "A lista di öservæ speciali a l'è stæta aggiornâ.",
+       "watchlistedit-raw-added": "L'è stæto azonto {{PLURAL:$1|una paggina|$1 paggine}}:",
+       "watchlistedit-raw-removed": "L'è stæto eliminou {{PLURAL:$1|una paggina|$1 paggine}}:",
+       "watchlistedit-clear-title": "Svoeua a lista sott'öservaçion",
+       "watchlistedit-clear-legend": "Svoeua a lista sott'öservaçion",
+       "watchlistedit-clear-explain": "Tutti i tittoli saian rimossi da-i to öservæ speciali",
+       "watchlistedit-clear-titles": "Tittoli:",
+       "watchlistedit-clear-submit": "Svoeua a lista sott'öservaçion (Permanentemente!)",
+       "watchlistedit-clear-done": "A lista di öservæ speciali a l'è stæta svuâ.",
+       "watchlistedit-clear-removed": "L'è stæto eliminou {{PLURAL:$1|una paggina|$1 paggine}}:",
+       "watchlistedit-too-many": "Gh'è troppe paggine da visualizzâ chì.",
+       "watchlisttools-clear": "Svoeua a lista sott'öservaçion",
        "watchlisttools-view": "Veddi e modiffiche pertinenti",
        "watchlisttools-edit": "Veddi e modiffica a lista",
        "watchlisttools-raw": "Modiffica a lista in formato testo",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discuscioin]])",
+       "timezone-local": "Locale",
        "duplicate-defaultsort": "Atençión: a ciâve de ordinaménto predefinîa \"$2\" a sostitoisce quella de primma \"$1\".",
+       "duplicate-displaytitle": "<strong>Attençion:</strong> o tittolo visualizzou \"$2\" o sostituisce o precedente tittolo \"$1\".",
+       "restricted-displaytitle": "<strong>Attençion:</strong> o titolo visualizzou \"$1\" o l'è stæto ignorou perché non equivalente a-o tittolo attoâ da paggina.",
+       "invalid-indicator-name": "<strong>Errô:</strong> l'attributo <code>name</code> di indicatoî do stato da paggina o no poeu ese voeuo.",
        "version": "Verscion",
+       "version-extensions": "Estenscioin installæ",
+       "version-skins": "Temi installæ",
+       "version-specialpages": "Paggine speciale",
+       "version-parserhooks": "Hook do parser",
+       "version-variables": "Variabbile",
+       "version-antispam": "Prevençion do spam",
+       "version-other": "Atro",
+       "version-mediahandlers": "Gestoî di contegnui murtimediæ",
+       "version-hooks": "Hook",
+       "version-parser-extensiontags": "Tag riconosciui da-o parser introduti da di estenscioin",
+       "version-parser-function-hooks": "Hook pe de fonçioin do parser",
+       "version-hook-name": "Nomme de l'hook",
+       "version-hook-subscribedby": "Sottoscriçioin",
+       "version-no-ext-name": "[sença nomme]",
+       "version-license": "Liçença MediaWiki",
+       "version-ext-license": "Liçença",
+       "version-ext-colheader-name": "Estenscion",
+       "version-skin-colheader-name": "Tema",
+       "version-ext-colheader-version": "Verscion",
+       "version-ext-colheader-license": "Liçença",
+       "version-ext-colheader-description": "Descriçion",
+       "version-ext-colheader-credits": "Aotoî",
+       "version-license-title": "Liçença pe $1",
+       "version-license-not-found": "Pe st'estenscion no l'è stæto atrovou arcun-a informaçion dettagiâ in sciâ liçença.",
+       "version-credits-title": "Credditi pe $1",
+       "version-credits-not-found": "Pe st'estenscion no l'è stæto atrovou arcun-a informaçion dettagiâ in scî credditi.",
+       "version-poweredby-credits": "Questo wiki o l'è realizou con '''[https://www.mediawiki.org/ MediaWiki]''', copyright © 2001-$1 $2.",
+       "version-poweredby-others": "atri",
+       "version-poweredby-translators": "tradutoî de translatewiki.net",
+       "version-credits-summary": "A nostra riconoscença a-e seguente person-e pe avei contribuio a [[Special:Version|MediaWiki]].",
+       "version-license-info": "MediaWiki o l'è un software libbero; ti poeu redistribuîlo e/ò modificâlo segondo i termi da GNU General Public License, comme pubbricâ da-a Free Software Foundation; ò a verscion 2 da Liçença ò (a proppia scelta) qualunque verscion succesciva.\n\nMediaWiki o l'è distribuio inta spiança ch'a segge uttile, ma SENÇA ARCUN-A GARANÇIA; sença manco a garantia impliççita de NEGOÇIABILITÆ ò de APPRICABILITÆ PE 'N SCOPO PARTICOLÂ. Amia a GNU General Public License pe maggioî detaggi.\n\nQuesto programma o dev'ese distribuio assemme a  [{{SERVER}}{{SCRIPTPATH}}/COPYING 'na coppia da GNU General Public License]; in caxo contraio, se ne poeu ötegnî un-a scrivendo a-a Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA oppû [http://www.softwarelibero.it/gnudoc/gpl.it.txt lezila in sciâ re'].",
+       "version-software": "Software instalou",
+       "version-software-product": "Produto",
+       "version-software-version": "Verscion",
+       "version-entrypoints": "URL d'accesso",
+       "version-entrypoints-header-entrypoint": "Ponti d'accesso",
+       "version-entrypoints-header-url": "URL",
+       "version-libraries": "Libraie instalæ",
+       "version-libraries-library": "Libraia",
+       "version-libraries-version": "Verscion",
+       "version-libraries-license": "Liçença",
+       "version-libraries-description": "Descriçion",
+       "version-libraries-authors": "Aotoî",
+       "redirect": "Rendriçamento da file, utente, paggina, verscion ò ID registro",
+       "redirect-summary": "Questa pagina speciale a rendriçça a un file (specificando o nome do file), a una pagina (specificando un ID de verscion ò un ID pagina), a un utente (specificando un ID utente numerrico) ò a 'n elemento do registro (specificando l'ID registro).\nEsempi: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], [[{{#Special:Redirect}}/logid/186]].",
+       "redirect-submit": "Vanni",
+       "redirect-lookup": "Riçerca:",
+       "redirect-value": "Valô:",
+       "redirect-user": "ID utente",
+       "redirect-page": "ID da paggina",
+       "redirect-revision": "Verscion da paggina",
+       "redirect-file": "Nomme do file",
+       "redirect-logid": "ID registro",
+       "redirect-not-exists": "Valô non trovou",
+       "fileduplicatesearch": "Riçerca di file dupricæ",
+       "fileduplicatesearch-summary": "Riçerca di eventuali dupricæ do file in base a-o valô de ''hash''.",
+       "fileduplicatesearch-filename": "Nomme do file:",
+       "fileduplicatesearch-submit": "Çerca",
+       "fileduplicatesearch-info": "$1 × $2 pixel<br />Dimenscion do file: $3<br />Tipo MIME: $4",
+       "fileduplicatesearch-result-1": "Dupricæ identichi a-o file \"$1\" no ghe n'è.",
+       "fileduplicatesearch-result-n": "Existe {{PLURAL:$2|un dupricou identico|$2 dupricæ identichi}} a-o file \"$1\".",
+       "fileduplicatesearch-noresults": "Nisciun file de nomme \"$1\" trovou.",
        "specialpages": "Pagine speçiâli",
+       "specialpages-note-top": "Legenda",
+       "specialpages-note": "* Pagine speciali non riservæ.\n* <span class=\"mw-specialpagerestricted\">Pagine speciali riservæ a çerte categorie d'utenti.</span>",
+       "specialpages-group-maintenance": "Raporti de manutençion",
+       "specialpages-group-other": "Atre paggine speciale",
+       "specialpages-group-login": "Intra / Registrite",
+       "specialpages-group-changes": "Urtime modiffiche e registri",
+       "specialpages-group-media": "File murtimediæ - caregamento e reisoconti",
+       "specialpages-group-users": "Utenti e driti",
+       "specialpages-group-highuse": "Paggine a ato utilizzo",
+       "specialpages-group-pages": "Elenchi de paggine",
+       "specialpages-group-pagetools": "Strumenti pe-e paggine",
+       "specialpages-group-wiki": "Dæti e strumenti",
+       "specialpages-group-redirects": "Paggine speciale de rendriççamento",
+       "specialpages-group-spam": "Strumenti contra o spam",
+       "specialpages-group-developer": "Strumenti pe-i sviluppatoî",
+       "blankpage": "Paggina voeua",
+       "intentionallyblankpage": "Questa paggina a l'è stæta lasciâ voeua aposta.",
        "external_image_whitelist": " #Lascia sta riga comm'a l'é<pre>\n#Inseisci i pessi de esprescioìn regolari (sôlo a pàrte ch'a va fra e //) chì sotta\n#Ste chi saiàn misse a confronto co-i indirìssi URL de inmàgini esterne (hotlinked)\n#E corispondense saiàn mostræ cómme inmàgini, donca saiâ mostròu sôlo in colegaménto\n#E righe che començan con # son conscideræ coménti\n#A diferensa tra maioscole e minoscole a no l'è scignificatîva\n\n#Inseisci sovia sta rîga tùtti i frammenti de regex. Lascia sta rîga ezattamente comm'a l'é</pre>",
+       "tags": "Etichette de modiffiche vallide",
        "tag-filter": "Filtra pe [[Special:Tags|etichetta]]:",
+       "tag-filter-submit": "Filtro",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etichetta|Etichette}}]]: $2)",
+       "tags-title": "Etichette",
+       "tags-intro": "Questa pagina a l'elenca i etichette che o software o poriæ associâ a 'na modiffica e o so scignificou.",
+       "tags-tag": "Nomme de l'etichetta",
+       "tags-display-header": "Aspetto inta lista de modiffiche",
+       "tags-description-header": "Descriçion completa do scignificou",
+       "tags-source-header": "Sorgente",
+       "tags-active-header": "Attivo?",
+       "tags-hitcount-header": "Modiffiche co l'etichetta",
+       "tags-actions-header": "Açioin",
+       "tags-active-yes": "Sci",
+       "tags-active-no": "No",
+       "tags-source-extension": "Definio da 'n'estenscion",
+       "tags-source-manual": "Appricou manoalmente da utenti e bot",
+       "tags-source-none": "No ciù in doeuvia",
+       "tags-edit": "cangia",
+       "tags-delete": "scassa",
+       "tags-activate": "attiva",
+       "tags-deactivate": "disattiva",
+       "tags-hitcount": "$1 {{PLURAL:$1|modiffica|modiffiche}}",
+       "tags-manage-no-permission": "No ti g'hæ a permiscion pe manezâ i cangi d'etichetta.",
+       "tags-manage-blocked": "No ti poeu manezâ i cangi d'etichetta dementre che t'ê blocou.",
+       "tags-create-heading": "Crea un noeuvo tag",
+       "tags-create-explanation": "Pe impostaçion predefinia, i tag apen-a creæ saian disponibbili pe l'utilizzo di utenti e di bot.",
+       "tags-create-tag-name": "Nomme de l'etichetta:",
+       "tags-create-reason": "Raxon:",
+       "tags-create-submit": "Crea",
+       "tags-create-no-name": "L'è necessaio specificâ un nomme d'etichetta.",
+       "tags-create-invalid-chars": "I nommi di etichette no devan contegnî de virgole (<code>,</code>) ò de bare (<code>/</code>).",
+       "tags-create-invalid-title-chars": "I nommi di etichette no devan contegnî di caratteri che no poeuan ese doeuviæ inti tirtoli de paggine.",
+       "tags-create-already-exists": "L'etichetta $1 a l'existe za.",
+       "tags-create-warnings-above": "L'è stæto rilevou {{PLURAl:$2|o seguente peigo|i seguenti peighi}} mentre se tentava de creâ l'etichetta \"$1\":",
+       "tags-create-warnings-below": "Ti dexiddei continoâ a creâ l'etichetta?",
+       "tags-delete-title": "Scassa l'etichetta",
+       "tags-delete-explanation-initial": "Ti stæ pe eliminâ o tag \"$1\" da-o database.",
+       "tags-delete-explanation-in-use": "A saiâ rimossa da {{PLURAL:$2|$2 verscioin ò voxe de registro| tutte e $2 verscioin e/ò voxe de registro}} dovv'a se troeuva oua.",
+       "tags-delete-explanation-warning": "Questa açion a l'è <strong>ireverscibbile</strong> e a <strong>no poeu ese annullâ</strong>, manco da di amministratoî de database. Asegûite che quest'o segge da vei o tag che t'intendi eliminâ.",
+       "tags-delete-explanation-active": "<strong> L'etichetta \"$1\" a l'è ancon attiva, e a-o restiâ in futuo. </strong> Pe disattivâla, vanni a fâlo da-e voxe de registro dovve l'etichetta a l'è in doeuvia.",
+       "tags-delete-reason": "Raxon:",
+       "tags-delete-submit": "Elimmina irreverscibilmente questo tag",
+       "tags-delete-not-allowed": "I tag definii da 'n'estenscion no poeuan ese eliminæ a meno che questo no segge specificamente permisso da l'estenscion.",
+       "tags-delete-not-found": "O tag \"$1\" o no l'existe.",
+       "tags-delete-too-many-uses": "O tag \"$1\" o l'è applicou a ciù de $2 {{PLURAL:$2|revixon|revixion}}, saieiv'a dî, ch'o no poeu ese eliminou.",
+       "tags-delete-warnings-after-delete": "L'etichetta \"$1\" a l'è stæta scassâ, ma fanni attençion {{PLURAL:$2|a-o seguente avviso|a-i seguenti avvixi}}:",
+       "tags-delete-no-permission": "No ti g'hæ a permiscion pe scassâ i cangi d'etichetta.",
+       "tags-activate-title": "Attiva tag",
+       "tags-activate-question": "Ti stæ pe attivâ o tag \"$1\".",
+       "tags-activate-reason": "Raxon:",
+       "tags-activate-not-allowed": "No l'è poscibbile attivâ o tag \"$1\".",
+       "tags-activate-not-found": "O tag \"$1\" o no l'existe.",
+       "tags-activate-submit": "attiva",
+       "tags-deactivate-title": "Disattiva o tag",
+       "tags-deactivate-question": "Ti stæ pe disattivâ o tag \"$1\".",
+       "tags-deactivate-reason": "Raxon:",
+       "tags-deactivate-not-allowed": "No l'è poscibbile disattivâ o tag \"$1\".",
+       "tags-deactivate-submit": "disattiva",
+       "tags-apply-no-permission": "No ti dispon-i de l'aotorizzaçion pe appricâ a modiffica di tag insemme a-e to modiffiche.",
+       "tags-apply-blocked": "No ti poeu appricâ i etichette a-e modiffiche mentre t'ê bloccou.",
+       "tags-apply-not-allowed-one": "L'etichetta \"$1\" a no poeu ese appricâ manoalmente.",
+       "tags-apply-not-allowed-multi": "{{PLURAL:$2|A seguente etichetta a no poeu ese appricâ|E seguente etichette no poeuan ese appricæ}} manoalmente: $1",
+       "tags-update-no-permission": "Non ti dispon-i de permiscioin necessaie pe azonze ò rimoeuve i etichette de modiffica da-e scingole verscioin ò voxe de registro.",
+       "tags-update-blocked": "No ti poeu rimoeuve i cangi d'etichetta dementre che t'ê blocou.",
+       "tags-update-add-not-allowed-one": "L'etichetta \"$1\" a no poeu ese azonta manoalmente.",
+       "tags-update-add-not-allowed-multi": "{{PLURAL:$2|A seguente etichetta a no poeu ese azonta|E seguente etichette no poeuan ese azonte}} manoalmente: $1",
+       "tags-update-remove-not-allowed-one": "L'etichetta \"$1\" a no poeu ese rimossa.",
+       "tags-update-remove-not-allowed-multi": "{{PLURAL:$2|A seguente etichetta a no poeu ese rimossa|E seguente etichette no poeuan ese rimosse}} manoalmente: $1",
+       "tags-edit-title": "Modiffica etichette",
+       "tags-edit-manage-link": "Gestisci etichette",
+       "tags-edit-revision-selected": "{{PLURAL:$1|Verscion seleçionâ|Verscioin seleçionæ}} de [[:$2]]:",
+       "tags-edit-logentry-selected": "{{PLURAL:$1|Evento do registro seleçionou|Eventi do registro seleçionæ}}:",
+       "tags-edit-revision-legend": "Azonzi ò rimoeuvi i etichette da {{PLURAL:$1|questa verscion|tutte $1 e verscioin}}",
+       "tags-edit-logentry-legend": "Azonzi ò rimoeuvi o etichette da {{PLURAL:$1|questa voxe de registro|tutte $1 e voxe de registro}}",
+       "tags-edit-existing-tags": "Etichette existente:",
+       "tags-edit-existing-tags-none": "<em>Nisciun-a</em>",
+       "tags-edit-new-tags": "Noeuve etichette:",
+       "tags-edit-add": "Azonzi queste etichette:",
+       "tags-edit-remove": "Rimoeuvi queste etichette:",
+       "tags-edit-remove-all-tags": "(leva tutti i tag)",
+       "tags-edit-chosen-placeholder": "Seleçion-a arcun-e etichette",
+       "tags-edit-chosen-no-results": "Nisciun tag corrispondente trovou",
+       "tags-edit-reason": "Raxon:",
+       "tags-edit-revision-submit": "Apprica e modiffiche a {{PLURAL:$1|questa verscion|$1 verscioin}}",
+       "tags-edit-logentry-submit": "Apprica e modiffiche a {{PLURAL:$1|questa voxe de registro|$1 voxe de registro}}",
+       "tags-edit-success": "E modifiche son stæte appricæ.",
+       "tags-edit-failure": "No l'è stæto poscibbile effettoâ e seguente modiffiche:\n$1",
+       "tags-edit-nooldid-title": "Verscion specificâ non vallida",
+       "tags-edit-nooldid-text": "No t'hæ specificou arcun-a verscion da paggina in sciâ quæ exeguî questa fonçion, oppû a verscion specificâ a no l'existe.",
+       "tags-edit-none-selected": "Pe piaxei, seleçion-a a-o manco un tag da azonzge ò da levâ.",
+       "comparepages": "Confronta e paggine",
+       "compare-page1": "Pagina 1",
+       "compare-page2": "Pagina 2",
+       "compare-rev1": "Verscion 1",
+       "compare-rev2": "Verscion 2",
+       "compare-submit": "Confronta",
+       "compare-invalid-title": "O tittolo che t'hæ specificou o no l'è vallido.",
+       "compare-title-not-exists": "O tittolo specificou o no l'existe.",
+       "compare-revision-not-exists": "A verscion che t'hæ specificou a no l'existe.",
+       "dberr-problems": "Spiaxenti! Questo scito o g'ha di problemi tecnichi.",
+       "dberr-again": "Proeuva a aspêtâ quarche menuto e recarrega.",
+       "dberr-info": "(Imposcibbile accede a-o database: $1)",
+       "dberr-info-hidden": "(Imposcibbile accede a-o database)",
+       "dberr-usegoogle": "Ti poeu provâ a çercâ insce Google into fratempo.",
+       "dberr-outofdate": "Notta che a so indiçizaçion di nostri contegnui a poriæ no ese agiornâ.",
+       "dberr-cachederror": "Questa a l'è una coppia cache dea paggina domandâ, e a poriæ no ese agiornâ.",
+       "htmlform-invalid-input": "Gh'è di problemi co-i dæti inseii.",
+       "htmlform-select-badoption": "O valô specificou a no l'è un'opçion vallida.",
+       "htmlform-int-invalid": "O valô specificou o no l'è un intrego.",
+       "htmlform-float-invalid": "O valô specificou o no l'è un nummero.",
+       "htmlform-int-toolow": "O valô specificou o l'è infeiô a-o minnimo de $1",
+       "htmlform-int-toohigh": "O valô specificou o l'è supeiô a-o mascimo de $1",
+       "htmlform-required": "Questo valô o l'è öbrigatoio.",
+       "htmlform-submit": "Invia",
+       "htmlform-reset": "Anulla e modiffiche",
+       "htmlform-selectorother-other": "Atro",
+       "htmlform-no": "No",
+       "htmlform-yes": "Sci",
+       "htmlform-chosen-placeholder": "Seleçion-a un'opçion",
+       "htmlform-cloner-create": "Azonzi de l'atro",
+       "htmlform-cloner-delete": "Leva",
+       "htmlform-cloner-required": "Ghe voeu a-o manco un valô.",
+       "htmlform-title-badnamespace": "[[:$1]] a no se troeuva into namespace \"{{ns:$2}}\".",
+       "htmlform-title-not-creatable": "\"$1\" o l'è o tittolo de una paggina non creabile",
+       "htmlform-title-not-exists": "$1 a no l'existe.",
+       "htmlform-user-not-exists": "'''$1''' o no l'existe.",
+       "htmlform-user-not-valid": "<strong>$1</strong> o no l'è un nomme utente vallido.",
+       "sqlite-has-fts": "$1 co-a poscibilitæ de riçerca completa into testo",
+       "sqlite-no-fts": "$1 sença a poscibilitæ de riçerca completa into testo",
        "logentry-delete-delete": "$1 {{GENDER:$2|o l'ha scassou}} a paggina $3",
+       "logentry-delete-restore": "$1 {{GENDER:$2|o|a}} l'ha ripristinou a paggina $3",
+       "logentry-delete-event": "$1 {{GENDER:$2|o|a}} l'ha modificou a vixibilitæ de {{PLURAL:$5|un'açion do registro|$5 açioin do registro}} de \"$3\": $4",
+       "logentry-delete-revision": "$1 {{GENDER:$2|o l'ha modificou}} a vixibilitæ pe {{PLURAL:$5|una verscion|$5 verscioin}} da paggina $3: $4",
+       "logentry-delete-event-legacy": "$1 {{GENDER:$2|o l'ha modificou}} a vixibilitæ de quarche açion do registro de $3",
+       "logentry-delete-revision-legacy": "$1 {{GENDER:$2|o l'ha modificou}} a vixibilitæ pe-e verscioin da paggina $3",
+       "logentry-suppress-delete": "$1 {{GENDER:$2|o l'ha soppresso}} a paggina $3",
+       "logentry-suppress-event": "$1 {{GENDER:$2|o l'ha segretamente modificou}} a vixibilitæ de {{PLURAL:$5|un'açion do registro|$5 açioin do registro}} de $3: $4",
+       "logentry-suppress-revision": "$1 {{GENDER:$2|o l'ha segretamente modificou}} a vixibilitæ de {{PLURAL:$5|una verscion|$5 verscioin}} da paggina $3: $4",
+       "logentry-suppress-event-legacy": "$1 {{GENDER:$2|o l'ha segretamente modificou}} a vixibilitæ de quarche açion do registro de $3",
+       "logentry-suppress-revision-legacy": "$1 {{GENDER:$2|o l'ha segretamente modificou}} a vixibilitæ de quarche verscion da pagina $3",
+       "revdelete-content-hid": "contegnuo ascoso",
+       "revdelete-summary-hid": "ögetto da modiffica ascoso",
+       "revdelete-uname-hid": "nomme utente ascoso",
+       "revdelete-content-unhid": "contegnuo ripristinou",
+       "revdelete-summary-unhid": "ögetto da modiffica ripristinou",
+       "revdelete-uname-unhid": "nomme utente ripristinou",
+       "revdelete-restricted": "limitaçioin a-i soli amministratoî attivæ",
+       "revdelete-unrestricted": "limitaçioin a-i soli amministratoî rimosse",
+       "logentry-block-block": "$1 {{GENDER:$2|o l'ha bloccou}} {{GENDER:$4|$3}} co-ina scadença de $5 $6",
+       "logentry-block-unblock": "$1 {{GENDER:$2|o l'ha sbroccou}} {{GENDER:$4|$3}}",
+       "logentry-block-reblock": "$1 {{GENDER:$2|o l'ha modificou}} e impostaçioin do blocco pe {{GENDER:$4|$3}} co-ina scadença de $5 $6",
+       "logentry-suppress-block": "$1 {{GENDER:$2|o l'ha bloccou}} {{GENDER:$4|$3}} co-ina scadença de $5 $6",
+       "logentry-suppress-reblock": "$1 {{GENDER:$2|o l'ha modificou}} e impostaçioin do blocco pe {{GENDER:$4|$3}} co-ina scadença de $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|o l'ha importou}} $3 trammite caregamento",
+       "logentry-import-upload-details": "$1 {{GENDER:$2|o l'ha importou}} $3 trammite caregamento de file ($4 {{PLURAL:$4|verscion|verscioin}})",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|o l'ha importou}} $3 da un'atra wiki",
+       "logentry-import-interwiki-details": "$1 {{GENDER:$2|o l'ha importou}} $3 da $5 ($4 {{PLURAL:$4|verscione|verscioin}})",
+       "logentry-merge-merge": "$1 {{GENDER:$2|o l'ha unio}} $3 in $4 (verscioin scin a-o $5)",
        "logentry-move-move": "$1 {{GENDER:$2|o l'ha mesciou}} a paggina $3 a $4",
+       "logentry-move-move-noredirect": "$1 {{GENDER:$2|o l'ha mescioiu}} a paggina $3 a $4 sença lasciâ de redirect",
+       "logentry-move-move_redir": "$1 {{GENDER:$2|o l'ha mesciou}} a paggina $3 a $4 trammite redirect",
+       "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|o l'ha mesciou}} a paggina $3 a $4 a-o posto de 'n redirect sença lasciâ de redirect",
+       "logentry-patrol-patrol": "$1 {{GENDER:$2|o l'ha marcou}} a verscion $4 da paggina $3 comme controlâ",
+       "logentry-patrol-patrol-auto": "$1 {{GENDER:$2|o l'ha marcou}} aotomaticamente a verscion $4 da paggina $3 comme controlâ",
+       "logentry-newusers-newusers": "L'utensa $1 a l'è stæta {{GENDER:$2|creâ}}",
        "logentry-newusers-create": "L'utensa $1 a l'è stæta {{GENDER:$2|creâ}}",
+       "logentry-newusers-create2": "L'utença $3 a l'è stæta {{GENDER:$2|creâ}} da $1",
+       "logentry-newusers-byemail": "L'utença $3 a l'è stæta {{GENDER:$2|creâ}} da $1 e a password a l'è stæta inviâ via e-mail",
+       "logentry-newusers-autocreate": "L'utença $1 a l'è stæta {{GENDER:$2|creâ}} aotomaticamente",
+       "logentry-protect-move_prot": "$1 {{GENDER:$2|o l'ha mesciou}} e impostaçioin de proteçion da $4 a $3",
+       "logentry-protect-unprotect": "$1 {{GENDER:$2|o l'ha rimosso}} a proteçion da $3",
+       "logentry-protect-protect": "$1 {{GENDER:$2|o l'ha protezuo}} $3 $4",
+       "logentry-protect-protect-cascade": "$1 {{GENDER:$2|o l'ha protezuo}} $3 $4 [ricorsciva]",
+       "logentry-protect-modify": "$1 {{GENDER:$2|o l'ha modificou}} o livello de proteçion pe $3 $4",
+       "logentry-protect-modify-cascade": "$1 {{GENDER:$2|o l'ha modificou}} o livello de proteçion pe $3 $4 [ricorsciva]",
+       "logentry-rights-rights": "$1 {{GENDER:$2|o l'ha modificou}} l'appartenença de {{GENDER:$6|$3}} da-o groppo $4 a-o groppo $5",
+       "logentry-rights-rights-legacy": "$1 {{GENDER:$2|o l'ha modificou}} l'appartenença a di groppi de $3",
+       "logentry-rights-autopromote": "$1 o l'è {{GENDER:$2|stæto promosso|stæta promossa|stato/a promosso/a}} aotomaticamente da $4 a $5",
        "logentry-upload-upload": "$1 {{GENDER:$2|o l'ha caregou}} $3",
-       "searchsuggest-search": "Çerca"
+       "logentry-upload-overwrite": "$1 {{GENDER:$2|o l'ha caregou}} una noeuva verscion de $3.",
+       "logentry-upload-revert": "$1 {{GENDER:$2|o l'ha caregou}} $3",
+       "log-name-managetags": "Gestion etichette",
+       "log-description-managetags": "Questa paggina a l'elenca i açioin de gestion relative a-i [[Special:Tags|etichette]]. O registro o conten solo e açioin effettuæ manoalmente da 'n amministratoô; i etichette porieivan ese creæ ò scassæ da-o programma wiki sença che 'na voxe a segge registrâ chie.",
+       "logentry-managetags-create": "$1 {{GENERE:$2|o l'ha creou}}o tag \"$4\"",
+       "logentry-managetags-delete": "$1 {{GENDER:$2|o l'ha rimosso}} l'etichetta \"$4\" (da $5 {{PLURAL:$5|verscione ò voxe de registro|verscioin ò voxe de registro}})",
+       "logentry-managetags-activate": "$1 {{GENDER:$2|o l'ha inseio}} l'etichetta \"$4\" pe l'uso da parte d'utenti e bot",
+       "logentry-managetags-deactivate": "$1 {{GENDER:$2|o l'ha disattivou}} l'etichetta \"$4\" pe l'uso da parte d'utenti e bot",
+       "log-name-tag": "Etichette",
+       "log-description-tag": "Questa pagina a mostra quande i utenti han azonto ò rimosso di [[Special:Tags|etichette]] da de scingole verscioin ò voxe do registro. O registro o no l'elenca o etichettatue che avegnan co-ina modiffica, una cançelaçione ò un'atra açion scimmile.",
+       "logentry-tag-update-add-revision": "$1 {{GENDER:$2|o l'ha azonto}} {{PLURAL:$7|l'etichetta|i etichette}} $6 a-a verscion $4 da paggina $3",
+       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|o l'ha azonto}} {{PLURAL:$7|l'etichetta|i etichette}} $6 a-a voxe de registro $5 da paggina $3",
+       "logentry-tag-update-remove-revision": "$1 {{GENDER:$2|o l'ha rimosso}} {{PLURAL:$9|l'etichetta|i etichette}} $8 da-a verscion $4 da paggina $3",
+       "logentry-tag-update-remove-logentry": "$1 {{GENDER:$2|o l'ha rimosso}} {{PLURAL:$9|l'etichetta|i etichette}} $8 da-a voxe de registro $5 da paggina $3",
+       "logentry-tag-update-revision": "$1 {{GENDER:$2|o l'ha aggiornou}} i etichette da verscion $4 da paggina $3 ({{PLURAL:$7|azonta|azonte}} $6; {{PLURAL:$9|rimossa|rimosse}} $8)",
+       "logentry-tag-update-logentry": "$1 {{GENDER:$2|o l'ha aggiornou}} i etichette da voxe de registro $5 de paggina $3 ({{PLURAL:$7|azonta|azonte}} $6; {{PLURAL:$9|rimossa|rimosse}} $8)",
+       "rightsnone": "(nisciun)",
+       "revdelete-summary": "ögetto da modiffica",
+       "feedback-adding": "Inseimento do feedback inta paggina...",
+       "feedback-back": "Inderê",
+       "feedback-bugcheck": "Ottimo! Controlla solo ch'o no segge za fra-i [$1 bug conosciui].",
+       "feedback-bugnew": "Controllo effettuou. Segnalla un noeuvo bug",
+       "feedback-bugornote": "Se t'ê pronto a descrive o problema tecnico riscontrou de maibea precisa, [$1 segnalla o bug]. In alternativa, ti poeu doeuviâ o moddulo semplificou sottostante. O to commento inseio o saiâ azonto a-a paggina \"[$3 $2]\", insemme a-o to nomme utente.",
+       "feedback-cancel": "Anulla",
+       "feedback-close": "Fæto",
+       "feedback-external-bug-report-button": "Documenta un problema tecnico",
+       "feedback-dialog-title": "Invia un feedback",
+       "feedback-dialog-intro": "Doeuvia o moddulo sottostante pe inviâ o to feedback. O to commento o l'appariâ inta paggina \"$1\", assemme a-o to nomme utente.",
+       "feedback-error-title": "Errô",
+       "feedback-error1": "Errô: Da-a API l'è arrivou un risultou non riconosciuo",
+       "feedback-error2": "Errô: No l'è stæto poscibbile eseguî a modiffica",
+       "feedback-error3": "Errô: Nisciun-a risposta da-a API",
+       "feedback-error4": "Errô: imposcibbile inviâ o feedback a-o tittolo indicou",
+       "feedback-message": "Messaggio:",
+       "feedback-subject": "Sogetto:",
+       "feedback-submit": "Invia",
+       "feedback-terms": "Acapiscio che e mæ informaçioin insce l'user agent includdan de informaçioin in sciô mæ esatto navegatô e verscion do scistema operativo, che saian condivise pubricamente, assemme a-o mæ feedback.",
+       "feedback-termsofuse": "Accetto de fornî di feedback conformemente a-e Condiçioin d'Uso.",
+       "feedback-thanks": "Graççie! O to feedback o l'è stæto pubricou a-a paggina \"[$2 $1]\".",
+       "feedback-thanks-title": "Graççie!",
+       "feedback-useragent": "Agente utente:",
+       "searchsuggest-search": "Çerca",
+       "searchsuggest-containing": "ch'o conten...",
+       "api-error-autoblocked": "O to adreçço IP o l'è stæto bloccou aotomaticamente, perché o l'è stæto doeuviou da un utente bloccou.",
+       "api-error-badaccess-groups": "No t'ê aotorizzou a caregâ di file insce questa wiki.",
+       "api-error-badtoken": "Errô interno: token errou.",
+       "api-error-blocked": "T'ê stæto bloccou, no ti poeu fâ modiffiche.",
+       "api-error-copyuploaddisabled": "O caregamento trammite URL o l'è disabilitou insce questo server.",
+       "api-error-duplicate": "Gh'è za {{PLURAL:$1|un atro file|di atri files}} into scito co-o mæximo contegnuo.",
+       "api-error-duplicate-archive": "Gh'ea za {{PLURAL:$1|un atro file|di ltri file}} into scito co-o mæximo contegnuo, ma {{PLURAL:$1|o l'è stæto scassou|son stæti scassæ}}.",
+       "api-error-empty-file": "O file che t'hæ inviou o l'è voeuo.",
+       "api-error-emptypage": "A creaçion de noeuve pagine voeue a no l'è consentia.",
+       "api-error-fetchfileerror": "Errô interno: s'è verificou un problema durante o recuppero do file.",
+       "api-error-fileexists-forbidden": "Un file de nomme \"$1\" o l'existe za e o no poeu ese sorvescrito.",
+       "api-error-fileexists-shared-forbidden": "Un file de nomme \"$1\" o l'existe za into repository condiviso e o no poeu ese sorvescrito.",
+       "api-error-file-too-large": "O file che t'hæ inviou o l'ea troppo grande.",
+       "api-error-filename-tooshort": "O nomme do file o l'è troppo curto.",
+       "api-error-filetype-banned": "Questo tipo de file o l'è proibio.",
+       "api-error-filetype-banned-type": "\"$1\" {{PLURAL:$4|o no l'è un tipo de file consentio|no son di tipi de file consentii}}. {{PLURAL:$3|O tipo de file consentio o l'è|I tipi de file consentii son}} $2.",
+       "api-error-filetype-missing": "A-o file gh'amanca l'estenscion.",
+       "api-error-hookaborted": "A modiffica che t'hæ çercou de fâ a l'è stæta interrotta da un'estenscion.",
+       "api-error-http": "Errô interno: imposcibbile connettise a-o server.",
+       "api-error-illegal-filename": "O nomme do file o no l'è ammisso.",
+       "api-error-internal-error": "Errô interno: quarcosa o l'è anæto storto con l'elaboaçion do to caregamento in sciâ wiki.",
+       "api-error-invalid-file-key": "Errô interno: file non presente inta cartella di file temporannei.",
+       "api-error-missingparam": "Errô interno: parammetri da recesta mancanti.",
+       "api-error-missingresult": "Errô interno: imposcibbile determinâ se a coppia a l'è ariescîa.",
+       "api-error-mustbeloggedin": "Pe caregâ di file ti devi primma intrâ.",
+       "api-error-mustbeposted": "Errô interno: a recesta a richiede HTTP POST.",
+       "api-error-noimageinfo": "O caregamento o l'è ariescio, ma o server o no n'ha dæto arcun-a informaçion  in sciô file.",
+       "api-error-nomodule": "Errô interno: no l'è stæto impostou o moddulo de caregamento.",
+       "api-error-ok-but-empty": "Errô interno: nisciun-a risposta da-o server.",
+       "api-error-overwrite": "No l'è permisso soviascrive un file existente.",
+       "api-error-ratelimited": "Ti çerchi de caregâ ciù file in meno tempo de quante questo wiki o permette.\nRiproeuva tra pochi menuti.",
+       "api-error-stashfailed": "Errô interno: o server o no l'è ariescio a memorizzâ o documento temporannio.",
+       "api-error-publishfailed": "Errô interno: o server o no l'è ariescio a pubbricâ o documento temporannio.",
+       "api-error-stasherror": "S'è veificou un errô durante o caregamento do file inta stash.",
+       "api-error-stashedfilenotfound": "O file inta stash o no l'è stæto trovou durante o tentativo de caregâ da-a stash.",
+       "api-error-stashpathinvalid": "O percorso ch'o l'aviæ dovuo portâ a-o file inta stash o no l'ea vallido.",
+       "api-error-stashfilestorage": "S'è veificou un errô durante a memorizzaçion do file inta stash.",
+       "api-error-stashzerolength": "O server o no poeu insei o file inta stash, perché o g'ha longheçça zero.",
+       "api-error-stashnotloggedin": "Pe poei sarvâ di file inta stash de caregamento ti devi primma intrâ.",
+       "api-error-stashwrongowner": "O file a-o quæ ti çercavi d'accede inta stash o no t'apparten.",
+       "api-error-stashnosuchfilekey": "A ciave do file a-a quæ ti çercavi d'accede inta stash a no l'existe.",
+       "api-error-timeout": "O server o no l'ha risposto entro o tempo previsto.",
+       "api-error-unclassified": "Gh'è stæto un aro sconosciuo.",
+       "api-error-unknown-code": "Errô sconosciuo: \"$1\"",
+       "api-error-unknown-error": "Errô interno: quarcosa l'è anæto storto provando a caregâ o file.",
+       "api-error-unknown-warning": "Avviso sconosciuo: $1",
+       "api-error-unknownerror": "Errô sconosciuo: \"$1\"",
+       "api-error-uploaddisabled": "O caregamento o l'è disabilitou insce questa wiki.",
+       "api-error-verification-error": "Questo file o poriæ ese dannezou, o aveighe l'estenscion sbaliâ.",
+       "api-error-was-deleted": "Un file co-o mæximo nomme o l'è stæto precedentemente caregou e succescivamente eliminou.",
+       "duration-seconds": "$1 {{PLURAL:$1|segondo|segondi}}",
+       "duration-minutes": "$1 {{PLURAL:$1|menuto|menuti}}",
+       "duration-hours": "$1 {{PLURAL:$1|oa|oe}}",
+       "duration-days": "$1 {{PLURAL:$1|giorno|giorni}}",
+       "duration-weeks": "$1 {{PLURAL:$1|setteman-a|setteman-e}}",
+       "duration-years": "$1 {{PLURAL:$1|anno|anni}}",
+       "duration-decades": "$1 {{PLURAL:$1|deccade}}",
+       "duration-centuries": "$1 {{PLURAL:$1|seccolo|seccoli}}",
+       "duration-millennia": "$1 {{PLURAL:$1|milennio|milenni}}",
+       "rotate-comment": "Immaggine curlâ de $1 {{PLURAL:$1|grao|groei}} in senso oraio",
+       "limitreport-title": "Dæti de profî do parser:",
+       "limitreport-cputime": "Tempo de utilizzo CPU",
+       "limitreport-cputime-value": "$1 {{PLURAL:$1|segondo|segondi}}",
+       "limitreport-walltime": "Tempo de utilizzo reale",
+       "limitreport-walltime-value": "$1 {{PLURAL:$1|segondo|segondi}}",
+       "limitreport-ppvisitednodes": "Nummero di noeui do preprocessô vixitæ",
+       "limitreport-ppgeneratednodes": "Nummero di noeui do preprocessô generæ",
+       "limitreport-postexpandincludesize": "Dimenscion de incluxoin post-espanscion",
+       "limitreport-postexpandincludesize-value": "$1/$2 {{PLURAL:$2|byte}}",
+       "limitreport-templateargumentsize": "Dimenscion di parammetri do template",
+       "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|byte}}",
+       "limitreport-expansiondepth": "Mascima profonditæ d'espanscion",
+       "limitreport-expensivefunctioncount": "Nummero de fonçioin do parser dispendiose",
+       "expandtemplates": "Espanscion di template",
+       "expand_templates_intro": "Questa pagina speciale a l'elabboa un testo espandendo tutti i template presenti.\nA carcoa ascì o risultou de fonçioon supportæ da-o parser comme\n<code><nowiki>{{</nowiki>#language:…}}</code> e de variabbile de scistema quæ\n<code><nowiki>{{</nowiki>CURRENTDAY}}</code>,\nsaiv'a dî inta prattica tutto 'lo che s'attroeuva tra parentexi graffe dogge.",
+       "expand_templates_title": "Contesto (pe {{FULLPAGENAME}} eçç.):",
+       "expand_templates_input": "Testo da espande:",
+       "expand_templates_output": "Risultou",
+       "expand_templates_xml_output": "Output in formato XML",
+       "expand_templates_html_output": "Risultou HTML",
+       "expand_templates_ok": "OK",
+       "expand_templates_remove_comments": "Rimoeuvi i commenti",
+       "expand_templates_remove_nowiki": "Elimmina o tag <nowiki> into risultou",
+       "expand_templates_generate_xml": "Mostra l'ærbo scintattico XML",
+       "expand_templates_generate_rawhtml": "Mostra l'HTML sgroeuzzo",
+       "expand_templates_preview": "Anteprimma",
+       "expand_templates_preview_fail_html": "<em>Scicomme {{SITENAME}} o g'ha de l'HTML sgroeuzzo attivou e gh'è stæto una perdia di dæti da sescion, l'anteprimma a l'è ascosa comme precaoçion contra i attacchi JavaScript.</em>\n\n<strong>Se se tratta de 'n normale tentativo d'anteprimma, riproeuva.</strong> \nSe o problema o persciste, ti poeu provâ a [[Special:UserLogout|scollegate]] e effettoâ un noeuvo accesso, controllando che o to navegatô o l'açette i bescoeutti da questo scito.",
+       "expand_templates_preview_fail_html_anon": "<em>Scicomme {{SITENAME}} o g'ha de l'HTML sgroeuzzo attivou e ti no t'ê introu, l'anteprimma a l'è ascosa comme precaoçion contra i attacchi JavaScript.</em>\n\n<strong>Se se tratta de 'n normale tentativo d'anteprimma [[Special:UserLogin|intra]] e proeuvighe torna.</strong>",
+       "expand_templates_input_missing": "Quarcosa ti ghe-o devi scrive.",
+       "pagelanguage": "Cangia a lengua da paggina",
+       "pagelang-name": "Paggina",
+       "pagelang-language": "Lengua",
+       "pagelang-use-default": "Adoeuvia a lengua predefinia",
+       "pagelang-select-lang": "Seleçion-a a lengua",
+       "pagelang-submit": "Invia",
+       "right-pagelang": "Cangia a lengua da paggina",
+       "action-pagelang": "cangiâ a lengua da paggina",
+       "log-name-pagelang": "Registro di cangi de lengua",
+       "log-description-pagelang": "Questo o l'è un registro di cangiamenti de lengua de paggine.",
+       "logentry-pagelang-pagelang": "$1 {{GENDER:$2|o l'ha modificou}} a lengua de $3 da $4 a $5",
+       "default-skin-not-found": "Oops! O tema predefinio pe-o to wiki, definio in <code dir=\"ltr\">$wgDefaultSkin</code> comme <code>$1</code>, o no l'è disponibbile.\n\nA to installaçion a pariæ includde {{PLURAL:$4|o seguente tema|i seguenti temi}}. Amia [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manuale: configuaçion do tema] pe de informaçioin insce comme {{PLURAL:$4|abilitâlo|abilitâli}} e çerne quello predefinio.\n\n$2\n\n; Se t'hæ appen-a installou MediaWiki:\n: Foscia ti l'hæ installou da git, o direttamente da-o codiçe sorgente doeuviando quarch'atro mettodo. Questo o l'ea previsto. Proeuva a installâ di temi da-a [https://www.mediawiki.org/wiki/Category:All_skins directory insce mediawiki.org], de ste mainee chì:\n:* Scaregando o [https://www.mediawiki.org/wiki/Download programma de installaçion tarball], ch'o l'è fornio con diversci temi e estenscioin. Ti poeu fâ o coppia e incolla da directory <code dir=\"ltr\">skins/</code> da lì.\n:* Scaregando di tarball di scingoli temi da [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Doeuviando Git pe scaregâ i temi].\n: In questo moddo no doviæ interfei co-o to repository git se t'ê un sviluppatô MediaWiki.\n\n; Se t'hæ appen-a aggiornou MediaWiki:\n: MediaWiki 1.24 e verscioin succescive no abillitan ciù aotomaticamente i temi installæ (amia [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manoale: rilevamento aotomattico di temi]). Ti poeu copiâ {{PLURAL:$5|a seguente linnia|e seguente linne}} into <code>LocalSettings.php</code> pe abilitâ {{PLURAL:$5|o tema installou|tutti i temi installæ}}:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Se t'hæ appen-a modificou <code>LocalSettings.php</code>:\n: Ricontrolla i nommi di temi pe di ari de battitua.",
+       "default-skin-not-found-no-skins": "Oops! O tema predefinio pe-o to wiki, definio in <code>$wgDefaultSkin</code> comme <code>$1</code>, o no l'è disponibbile.\n\nTemi installæ no ti ghe n'hæ.\n\n; Se t'hæ appen-a installou ò aggiornou MediaWiki:\n: Foscia ti l'hæ installou da git, ò direttamente da-o coddiçe sorgente doeuviando quarch'atro mettodo. Questo o l'ea previsto. MediaWiki 1.24 e e verscioin succescive no includdan arcun tema into repository prinçipâ. Proeuva a installâ di temi da-a [https://www.mediawiki.org/wiki/Category:All_skins directory insce mediawiki.org], inte sti moddi:\n:* Scaregando o [https://www.mediawiki.org/wiki/Download programma de instalaçion tarball], ch'o l'è fornio con diversci temi e estenscioin. Ti poeu fâ o coppia e incolla da directory <code>skins/</code> da lì.\n:* Scaregando tarball di scingoli temi da [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Doeuviando Git pe scaregâ i temi].\n: Sto moddo o no doviæ interfei co-o to repository git se t'ê un sviluppatô MediaWiki. Amia [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manoale: configuaçion di temi] pe informaçioin insce comme abilitâle e scellie quello predefinio.",
+       "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (abilitâ)",
+       "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 (<strong>disabilitâ</strong>)",
+       "mediastatistics": "Statistiche relative a-i file murtimediæ",
+       "mediastatistics-summary": "Statistiche in scî tipi de file caregæ. L'è incluso solo che a verscion ciù reçente de 'n file. E verscioin vege ò scassæ di file son escluse.",
+       "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Dimenscione totâ di file pe questa seçion: {{PLURAL:$1|$1 byte}} ($2; $3%).",
+       "mediastatistics-allbytes": "Dimenscion totâ de tutti i file: {{PLURAL:$1|$1 byte}} ($2).",
+       "mediastatistics-table-mimetype": "Tipo MIME",
+       "mediastatistics-table-extensions": "Poscibbile estenscioin",
+       "mediastatistics-table-count": "Nummero di file",
+       "mediastatistics-table-totalbytes": "Dimenscion combinâ",
+       "mediastatistics-header-unknown": "Sconosciuo",
+       "mediastatistics-header-bitmap": "Immaggine bitmap",
+       "mediastatistics-header-drawing": "Disegni (immaggine vettoiæ)",
+       "mediastatistics-header-audio": "Audio",
+       "mediastatistics-header-video": "Video",
+       "mediastatistics-header-multimedia": "Contegnui murtimediæ",
+       "mediastatistics-header-office": "Öfiççio",
+       "mediastatistics-header-text": "Testoale",
+       "mediastatistics-header-executable": "File exeguibbili",
+       "mediastatistics-header-archive": "Formati compresci",
+       "mediastatistics-header-total": "Tutti i file",
+       "json-warn-trailing-comma": "$1 {{PLURAL:$1|virgola finâ a l'è stæta rimossa|virgole finæ son stæte rimosse}} da-o JSON",
+       "json-error-unknown": "Gh'è stæto un problema co-o JSON. Errô: $1",
+       "json-error-depth": "A profonditæ mascima do stack a l'è stæta superâ",
+       "json-error-state-mismatch": "JSON non vallido ò mäformou",
+       "json-error-ctrl-char": "Errô into carattere de controllo, poscibbile codiffica errâ",
+       "json-error-syntax": "Errô de scintasci",
+       "json-error-utf8": "Caratteri UTF-8 non vallidi, poscibbile codiffica errâ",
+       "json-error-recursion": "Un o ciù rifeimenti ricorscivi into valô da codificâ",
+       "json-error-inf-or-nan": "Un ò ciu valoî NAN o INF into valô da codificâ",
+       "json-error-unsupported-type": "L'è stæto fornio un valô de un tipo ch'o no poeu ese codificou",
+       "headline-anchor-title": "Colegamento a questa seçion",
+       "special-characters-group-latin": "Latin",
+       "special-characters-group-latinextended": "Latin esteiso",
+       "special-characters-group-ipa": "IPA",
+       "special-characters-group-symbols": "Scimboli",
+       "special-characters-group-greek": "Grego",
+       "special-characters-group-greekextended": "Grego esteiso",
+       "special-characters-group-cyrillic": "Çirillico",
+       "special-characters-group-arabic": "Arrabo",
+       "special-characters-group-arabicextended": "Arrabo esteiso",
+       "special-characters-group-persian": "Perscian",
+       "special-characters-group-hebrew": "Ebraico",
+       "special-characters-group-bangla": "Bengaleise",
+       "special-characters-group-tamil": "Tamil",
+       "special-characters-group-telugu": "Telugu",
+       "special-characters-group-sinhala": "Scingaleise",
+       "special-characters-group-gujarati": "Gujarati",
+       "special-characters-group-devanagari": "Devanagari",
+       "special-characters-group-thai": "Thailandeise",
+       "special-characters-group-lao": "Laotian",
+       "special-characters-group-khmer": "Khmer",
+       "special-characters-title-endash": "linieta enne",
+       "special-characters-title-emdash": "linieta emme",
+       "special-characters-title-minus": "segno meno",
+       "mw-widgets-dateinput-no-date": "Niscun-a dæta seleçionâ",
+       "mw-widgets-titleinput-description-new-page": "questa paggina o no l'existe ancon",
+       "mw-widgets-titleinput-description-redirect": "rendriçamento a $1",
+       "sessionmanager-tie": "No l'è poscibbile combinâ ciù tipi de receste de aotenticaçion: $1.",
+       "sessionprovider-generic": "sescioin $1",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "sescioin basæ in scî cookie",
+       "sessionprovider-nocookies": "I cookie poeuan ese disattivæ. Assegûite d'aveighe i cookie abilitæ e ricomença.",
+       "randomrootpage": "Paggina reixe a brettio",
+       "log-action-filter-block": "Tipo de blocco:",
+       "log-action-filter-contentmodel": "Tipo de modiffica do modello de contegnuo:",
+       "log-action-filter-delete": "Tipo de scassatua:",
+       "log-action-filter-import": "Tipo de importaçion:",
+       "log-action-filter-managetags": "Tipo d'açion de gestion d'etichetta:",
+       "log-action-filter-move": "Tipo de stramuo:",
+       "log-action-filter-newusers": "Tipo de creaçion d'utença:",
+       "log-action-filter-patrol": "Tipo de controllo:",
+       "log-action-filter-protect": "Tipo de proteçion:",
+       "log-action-filter-rights": "Tipo de modiffica di driti:",
+       "log-action-filter-suppress": "Tipo de soprescion:",
+       "log-action-filter-upload": "Tipo de caregamento:",
+       "log-action-filter-all": "Tutto",
+       "log-action-filter-block-block": "Blocco",
+       "log-action-filter-block-reblock": "Modiffica do blocco",
+       "log-action-filter-block-unblock": "Sblocco",
+       "log-action-filter-contentmodel-change": "Modiffica do modello do contegnuo",
+       "log-action-filter-contentmodel-new": "Creaçion de paggina con modello de contenuto non standard",
+       "log-action-filter-delete-delete": "Scancellaçion paggina",
+       "log-action-filter-delete-restore": "Ripristino paggina",
+       "log-action-filter-delete-event": "Scancellaçion registro",
+       "log-action-filter-delete-revision": "Scancellaçion verscion",
+       "log-action-filter-import-interwiki": "Importaçion transwiki",
+       "log-action-filter-import-upload": "Importaçion da XML caregou",
+       "log-action-filter-managetags-create": "Creaçion etichetta",
+       "log-action-filter-managetags-delete": "Scancellaçion etichetta",
+       "log-action-filter-managetags-activate": "Attivaçion etichetta",
+       "log-action-filter-managetags-deactivate": "Disattivaçion etichetta",
+       "log-action-filter-move-move": "Stramuo sença soviascrive di rendriçamenti",
+       "log-action-filter-move-move_redir": "Stramuo con soviascritua di rendriçamenti",
+       "log-action-filter-newusers-create": "Creaçion da utente anonnimo",
+       "log-action-filter-newusers-create2": "Creaçion da utente registrou",
+       "log-action-filter-newusers-autocreate": "Creaçion aotomattica",
+       "log-action-filter-newusers-byemail": "Creaçion con password inviâ via e-mail",
+       "log-action-filter-patrol-patrol": "Controllo manoâ",
+       "log-action-filter-patrol-autopatrol": "Cotrollo aotomattico",
+       "log-action-filter-protect-protect": "Proteçion",
+       "log-action-filter-protect-modify": "Modiffica proteçion",
+       "log-action-filter-protect-unprotect": "Desproteçion",
+       "log-action-filter-protect-move_prot": "Proteçion mesciâ",
+       "log-action-filter-rights-rights": "Modiffica manoâ",
+       "log-action-filter-rights-autopromote": "Modiffica aotomattica",
+       "log-action-filter-suppress-event": "Sopprescion de registro",
+       "log-action-filter-suppress-revision": "Sopprescion de verscion",
+       "log-action-filter-suppress-delete": "Sopprescion de paggina",
+       "log-action-filter-suppress-block": "Sopprescion utente da blocco",
+       "log-action-filter-suppress-reblock": "Sopprescion utente da re-blocco",
+       "log-action-filter-upload-upload": "Noeuvo caregamento",
+       "log-action-filter-upload-overwrite": "Ricaregamento",
+       "authmanager-authn-not-in-progress": "L'aotenticaçion a no l'è in corso ò i dæti da sescion son anæti persci. Se prega de recomençâ da cavo.",
+       "authmanager-authn-no-primary": "E credençiæ fornie no poeuan ese aotenticæ.",
+       "authmanager-authn-no-local-user": "E credençiæ fornie no son associæ a nisciun utente de questo wiki.",
+       "authmanager-authn-no-local-user-link": "E credençiæ fornie son vallide ma no son associæ a nisciun utente de questa wiki. Accedi inte 'n atro moddo ò crea un noeuvo utente, e ti gh'aviæ 'n'opçion pe collegâ e to credençiæ precedente a quell'utença.",
+       "authmanager-authn-autocreate-failed": "Creaçion aotomattica de 'n'utença locale fallia: $1",
+       "authmanager-change-not-supported": "E credençiæ fornie no poeuan ese modificæ, dæto che no saieivan doeuviæ da ninte.",
+       "authmanager-create-disabled": "A creaçion di utençe a l'è disabilitâ.",
+       "authmanager-create-from-login": "Pe creâ a to utença, completa i campi chì de sotta.",
+       "authmanager-create-not-in-progress": "A creaçion de un'utença a no l'è in corso ò i dæti da sescion son anæti perdui. Se prega de recomençâ da cavo.",
+       "authmanager-create-no-primary": "E credençiæ fornie no poeuan ese doeuviæ pe-a creaçion de l'utença.",
+       "authmanager-link-no-primary": "E credençiæ fornie no poeuan ese doeuviæ pe-o colegamento de l'utença.",
+       "authmanager-link-not-in-progress": "O colegamento de l'utença o no procede ò i dæti da sescion so-anæti perdui. Se prega de recomençâ da cavo.",
+       "authmanager-authplugin-setpass-failed-title": "Modiffica da password fallia",
+       "authmanager-authplugin-setpass-failed-message": "O plugin d'aotenticaçion o l'ha impedio a modiffica da password.",
+       "authmanager-authplugin-create-fail": "O plugin d'aotenticaçion o l'ha impedio a creaçion de l'utença.",
+       "authmanager-authplugin-setpass-denied": "O plugin d'aotenticaçion o no consente de cangiâ e password.",
+       "authmanager-authplugin-setpass-bad-domain": "Dominnio non vallido.",
+       "authmanager-autocreate-noperm": "A creaçion aotomattica del'utença a no l'è permissa.",
+       "authmanager-autocreate-exception": "A creaçion aotomattica di utençe a l'è temporaniamente disabilitâ a caosa di erroî precedenti.",
+       "authmanager-userdoesnotexist": "L'utença \"$1\" a no l'è registrâ.",
+       "authmanager-userlogin-remembermypassword-help": "Se a password a dev'ese aregordâ ciù a longo rispetto a-a duata da sescion.",
+       "authmanager-username-help": "Nome utente pe l'aotenticaçion.",
+       "authmanager-password-help": "Password pe l'aotenticaçion.",
+       "authmanager-domain-help": "Dominnio pe l'aotenticaçion esterna.",
+       "authmanager-retype-help": "Conferma torna a password.",
+       "authmanager-email-label": "E-mail",
+       "authmanager-email-help": "Addreçço e-mail:",
+       "authmanager-realname-label": "Nomme vêo:",
+       "authmanager-realname-help": "Nomme reale de l'utente",
+       "authmanager-provider-password": "Aotenticaçion basâ in sciâ password",
+       "authmanager-provider-password-domain": "Aotenticaçion con password ò con dominnio",
+       "authmanager-provider-temporarypassword": "Password temporannia",
+       "authprovider-confirmlink-message": "Basandose insce di reçenti tentativi d'accesso, e seguente utençe poeuan ese collegæ a-o to account wiki. Collegandole ti poeu effettuâ l'accesso con quelle ascì. Se prega de seleçionâ quelle che devan ese collegæ.",
+       "authprovider-confirmlink-request-label": "Utençe che dovieivan ese collegæ",
+       "authprovider-confirmlink-success-line": "$1: collegou correttamente.",
+       "authprovider-confirmlink-failed": "O collegamento de l'utença o no l'è pin-amente ariescio: $1",
+       "authprovider-confirmlink-ok-help": "Continnoa doppo a visualizzaçion di messaggi de errô de collegamento.",
+       "authprovider-resetpass-skip-label": "Sata",
+       "authprovider-resetpass-skip-help": "Sata a rempostaçion da password.",
+       "authform-nosession-login": "L'aotenticaçion a l'ha avuo successo, ma o to navegatô o no l'è in graddo de \"aregordâ\" che t'ê collegou.\n\n$1",
+       "authform-nosession-signup": "L'utença a l'è stæta creâ, ma o to navegatô o no l'è in graddo de \"aregordâ\" che t'ê collegou.\n$1",
+       "authform-newtoken": "Token mancante. $1",
+       "authform-notoken": "Token mancante",
+       "authform-wrongtoken": "Token errou",
+       "specialpage-securitylevel-not-allowed-title": "Non consentio",
+       "specialpage-securitylevel-not-allowed": "Spiaxenti, no t'ê aotorizzou a doeuviâ questa paggina perché a to identitæ a no poeu ese veificâ.",
+       "authpage-cannot-login": "Imposcibbile començâ co l'accesso.",
+       "authpage-cannot-login-continue": "Imposcibbile continoâ co l'accesso. L'è probabbile che a to sescion a segge descheita.",
+       "authpage-cannot-create": "Imposcibbile comença a creaçion de l'utença.",
+       "authpage-cannot-create-continue": "Imposcibbile continoâ co-a creaçion de l'utença. L'è probabbile che a to sescion a segge descheita.",
+       "authpage-cannot-link": "Imposcibbile inandiâ o collegamento de l'utença.",
+       "authpage-cannot-link-continue": "Imposcibbile continoâ co-o collegamento de l'utença. L'è probabbile che a to sescion a segge descheita.",
+       "cannotauth-not-allowed-title": "Permisso negou",
+       "cannotauth-not-allowed": "No t'ê aotorizou a doeuviâ questa paggina",
+       "changecredentials": "Modiffica credençiæ",
+       "changecredentials-submit": "Modiffica credençiæ",
+       "changecredentials-invalidsubpage": "$1 o no l'è 'na tipologia de credençiale vallida.",
+       "changecredentials-success": "E to credençiale son stæte modificæ.",
+       "removecredentials": "Rimoeuvi credençiæ",
+       "removecredentials-submit": "Rimoeuvi credençiæ",
+       "removecredentials-invalidsubpage": "$1 o no l'è 'na tipologia de credençiale vallida.",
+       "removecredentials-success": "E to credençiale son stæte rimosse.",
+       "credentialsform-provider": "Tipo de credençiale:",
+       "credentialsform-account": "Nomme utença:",
+       "cannotlink-no-provider-title": "Utençe collegabbile no ghe n'è",
+       "cannotlink-no-provider": "Utençe colegabbile no ghe n'è.",
+       "linkaccounts": "Collega utençe",
+       "linkaccounts-success-text": "L'utença a l'è stæta colegâ.",
+       "linkaccounts-submit": "Collega utençe",
+       "unlinkaccounts": "Scollega utençe",
+       "unlinkaccounts-success": "L'utença a l'è stæta scollegâ.",
+       "authenticationdatachange-ignored": "O cangiamento da dæta d'aotenticaçion o no l'è passou. Foscia che no gh'ea un provider configuou?"
 }
index 18ccf64..74b2357 100644 (file)
@@ -6,7 +6,9 @@
                        "Lakzon",
                        "Mjbmr",
                        "Macofe",
-                       "Huji"
+                       "Huji",
+                       "Miladrahimi",
+                       "Ebraminio"
                ]
        },
        "tog-underline": "کڕ(خط)کیشائن ژێر پیوندەل:",
        "october-date": "$1 اکتبر",
        "november-date": "$1 نوامبر",
        "december-date": "$1 دسامبر",
+       "period-am": "صو",
+       "period-pm": "ظور",
        "pagecategories": "{{PLURAL:$1|ڕِزگ|ڕِزگەل}}",
        "category_header": "\"وەڵگەل ڕزگ(رده) \"$1",
        "subcategories": "ژێر ڕزگەل",
        "tagline": "دۀربارۀ {{SITENAME}}",
        "help": "یاری کردن",
        "search": "مِنِی کِردِن(گێردین)",
+       "search-ignored-headings": "#<!-- این صفحه را درست همانطور که هست رها کنید --> <pre>\n#سر‌فصل‌هایی که توسط تحقیق نادیده گرفته خواهندشد.‌\n#به محض اینکه صفحه با سرفصل، فهرست شده‌است،تغییرات متاثر می‌شود.\n#شما می‌توانید با انجام یک ویرایش پوچ صفحه را وادار به دوباره فهرست کردن کنید.\n#نحو به شرح زیر است:\n#  *همه چیز از یک خصیصهٔ \"#\" گرفته تا آخر خط، یک نظر است\n#  *هر خط بدون فاصله، عنوان دقیق برای نادیده گرفتن،موضوع و همه چیز منابع است\nاتصالات خارجی\nهمچنین مشاهده کنید\n#</pre> <!-- leave this line exactly as it is -->",
        "searchbutton": "مِنِی کِردِن(گێردین)",
        "go": "بِچۆ",
        "searcharticle": "بِچۆ",
        "virus-scanfailed": "  )$1 پویش ناموفق (کد",
        "virus-unknownscanner": ":ضدویروس ناشناخته",
        "logouttext": "'''ایسة دةر چئن هؤمة ثبت بیة.'''\nتوجه داشته باشید که تا حافظهٔ نهان مرورگرتان را پاک نکنید، بعضی از صفحات ممکن است همچنان به گونه‌ای نمایش یابند که انگار وارد شده‌اید.",
+       "cannotlogoutnow-title": "نمه‌تونین ایسه بچینه‌در",
+       "cannotlogoutnow-text": "ئه‌دررچین نماو هنی $1",
        "welcomeuser": "خؤةش هةتینة/هاتینة $1!",
        "welcomecreation-msg": "حساوو کاربری هۆمە دؤرس بی.\nویرتان نەچوو(فراموش نشە) گإ [[Special:Preferences|تمارزووەل(ترجیحات) {{SITENAME}}]] ووِژت بگؤەڕنین( تغییر دهی).",
        "yourname": ":نۆم کاربەری",
        "remembermypassword": "رمزعبورت وة ئئ رایانة ئةویرت/یادت بو(تابیشترإژ$1{{PLURAL:$1|رووژةل|رووژ}})",
        "userlogin-remembermypassword": "مإ وارد  بی بیل",
        "userlogin-signwithsecure": "إژ ورود امن استفاده کةن",
+       "cannotloginnow-title": "ایسه نمه‌تونین باینه نوم",
        "yourdomainname": ":دامنهٔ شما",
        "password-change-forbidden": ".شما نمی‌توانید گذرواژه‌ها را در این ویکی تغییر دهید",
        "externaldberror": "خطایی در ارتباط با پایگاه داده رخ داده است یا اینکه شما اجازهٔ به‌روزرسانی حساب خارجی خود را ندارید.",
        "login": "إ نۆم هەتن سیستم",
+       "login-security": "وژت معرفی‌که",
        "nav-login-createaccount": " إ نؤم هةتن سیستم/ حساوو کاربةری سازین",
        "userlogin": " إ نؤم هةتن سیستم/ حساوو کاربةری سازین",
        "userloginnocreate": "نؤم هۀتن سیستم",
        "passwordreset-emailelement": "نام کاربری: \n$1\n\nگذرواژهٔ موقت: \n$2",
        "passwordreset-emailsentemail": "اگر نشانی پست الکترونیکی که وارد کردید برای حساب کاربریتان ثبت شده باشد، یک نامهٔ بازنشانی گذرواژه به آن فرستاده می‌شود.",
        "passwordreset-emailsentusername": "اگر نشانی پست الکترونیکی مرتبطی موجود باشد، یک نامه برای بازنشانی گذرواژه به آن ارسال خواهد شد.",
-       "passwordreset-emailsent-capture": "یک ایمیل بازنشانی که در پایین نمایش داده شده، فرستاده شده است.",
-       "passwordreset-emailerror-capture": "ایمیل بازنشانی، که در زیر نمایش داده شده، ایجاد شد، ولی ارسال آن به {{GENDER:$2|کاربر}} موفقیت‌آمیز نبود: $1",
        "changeemail": "تغییر یا حذف نشانی ایمیل",
        "changeemail-header": "برای تغییر ایمیلتان این فرم را کامل کنید. برای حذف ایملیتان کافی است بخش ایمیل را خالی رها کنید و فرم را ارسال کنید.",
-       "changeemail-passwordrequired": "برای تائید این تغییر باید گذرواژه‌تان را وارد کنید.",
        "changeemail-no-info": ".برای دسترسی مستقیم به این صفحه شما باید به سیستم وارد شده باشید",
        "changeemail-oldemail": ":نشانی ایمیل ایسة هؤمة",
        "changeemail-newemail": ":نشانی ایمیل تازة",
        "undo-nochange": "به نظر می‌رسد ویرایش از پیش واگردانی شده است.",
        "undo-summary": "خنثی‌سازی ویرایش $1 توسط [[Special:Contributions/$2|$2]] ([[User talk:$2|بحث]])",
        "undo-summary-username-hidden": "خنثی‌سازی نسخهٔ $1 به دست یک کاربر پنهان‌شده",
-       "cantcreateaccounttitle": "نمی‌توان حساب باز کرد",
        "cantcreateaccount-text": "امكان ساختن حساب کاربری از این این نشانی آی‌پی ('''$1''') توسط [[User:$3|$3]] سلب شده است.\n\nدلیل ارائه شده توسط $3 چنین است: $2",
        "cantcreateaccount-range-text": "ایجاد حساب از آدرس آی‌پی در مجموعه‌ی <strong>$1</strong>، که شامل آدرس آی‌پی شما (<strong>$4</strong>) است، توسط [[User:$3|$3]] متوقف شده‌است.\nدلیل ارائه شده توسط $3، $2 است.",
        "viewpagelogs": "نمایش سیاهه‌های این صفحه",
        "special-characters-title-minus": "علامت منفی",
        "mw-widgets-dateinput-no-date": "هیچ داده‌ای انتخاب نشده",
        "mw-widgets-titleinput-description-new-page": "اێ وەڵگە هەنی(هالی)وجود نِئرێ",
-       "mw-widgets-titleinput-description-redirect": "گؤەڕانن/تغییر مسیر به $1",
-       "api-error-blacklisted": "لطفاً یک عنوان توصیفی متفاوت انتخاب کنید."
+       "mw-widgets-titleinput-description-redirect": "گؤەڕانن/تغییر مسیر به $1"
 }
index ded9f62..8633f4f 100644 (file)
@@ -53,6 +53,7 @@
        "tog-watchdefault": "Pridėti puslapius, kuriuos aš redaguoju, į stebimų sąrašą",
        "tog-watchmoves": "Pridėti puslapius, kuriuos aš perkeliu, į stebimų sąrašą",
        "tog-watchdeletion": "Pridėti puslapius, kuriuos aš ištrinu, į stebimų sąrašą",
+       "tog-watchuploads": "Pridėti naujus failus, kurios aš įkeliu, į mano stebimųjų sąrašą",
        "tog-watchrollback": "Pridėti puslapius, kuriuose aš atlikau atmetimus į mano stebėjimo sąrašą",
        "tog-minordefault": "Pagal nutylėjimą pažymėti redagavimus kaip smulkius",
        "tog-previewontop": "Rodyti peržiūrą virš redagavimo lauko",
@@ -77,7 +78,7 @@
        "tog-ccmeonemails": "Siųsti man laiškų, kuriuos siunčiu kitiems naudotojams, kopijas",
        "tog-diffonly": "Nerodyti puslapio turinio po skirtumais",
        "tog-showhiddencats": "Rodyti paslėptas kategorijas",
-       "tog-norollbackdiff": "Nepaisyti skirtumo atlikus atmetimą",
+       "tog-norollbackdiff": "Nerodyti skirtumo atlikus atmetimą",
        "tog-useeditwarning": "Perspėti mane, kai palieku redagavimo puslapį, o jame yra neišsaugotų pakeitimų",
        "tog-prefershttps": "Prisiregistruojant visada naudokite saugų ryšį",
        "underline-always": "Visada",
        "tagline": "Iš {{SITENAME}}.",
        "help": "Pagalba",
        "search": "Paieška",
+       "search-ignored-headings": " #<!-- palikite šią eilutę tiksliai tokią, kokia ji yra --> <pre>\n# Antraštės, kurios bus ignoruojamos paieškų.\n# Pakeitimai įsigalios iš karto, kai bus suindeksuota puslapio antraštė.\n# Galite priversti perindeksuoti puslapį atlikdami tuščią pakeitimą.\n# Sintaksė tokia:\n#   * Viskas nuo simbolio \"#\" iki eilutės pabaigos yra komentaras.\n#   * Kiekviena netuščia eilutė yra tikslus pavadinimas, kurį reikia ignoruoti, tipas ir visa kita.\nNuorodos\nIšorinės nuorodos\nTaip pat žr.\n #</pre> <!-- palikite šią eilutę tiksliai tokią, kokia ji yra -->",
        "searchbutton": "Paieška",
        "go": "Rodyti",
        "searcharticle": "Rodyti",
        "yourpasswordagain": "Pakartokite slaptažodį:",
        "createacct-yourpasswordagain": "Patvirtinkite slaptažodį",
        "createacct-yourpasswordagain-ph": "Įveskite slaptažodį dar kartą",
-       "remembermypassword": "Prisiminti prisijungimo duomenis šiame kompiuteryje (daugiausiai $1 {{PLURAL:$1|dieną|dienas|dienų}})",
        "userlogin-remembermypassword": "Įsiminti mane",
        "userlogin-signwithsecure": "Naudoti saugią jungtį",
        "cannotloginnow-title": "Dabar negalima prisijungti",
        "password-change-forbidden": "Jus negalite keisti slaptažodžių šioje wiki.",
        "externaldberror": "Yra arba išorinė autorizacijos duomenų bazės klaida arba jums neleidžiama atnaujinti jūsų išorinės paskyros.",
        "login": "Prisijungti",
+       "login-security": "Patvirtinkite savo tapatybę",
        "nav-login-createaccount": "Prisijungti / sukurti paskyrą",
        "userlogin": "Prisijungti / sukurti paskyrą",
        "userloginnocreate": "Prisijungti",
        "userlogin-resetpassword-link": "Pamiršote savo slaptažodį?",
        "userlogin-helplink2": "Padėti prisijungti",
        "userlogin-loggedin": "Jūs jau prisijungęs kaip {{GENDER:$1|$1}}.\nNaudokite žemiau pateiktą pavidalą, kad prisijungtumėte kaip kitas naudotojas.",
+       "userlogin-reauth": "Turite vėl prisijungti patvirtinimui, kad esate {{GENDER:$1|$1}}.",
        "userlogin-createanother": "Sukurti kitą paskyrą",
        "createacct-emailrequired": "Elektroninio pašto adresas",
        "createacct-emailoptional": "Elektroninio pašto adresas (neprivaloma)",
        "createacct-email-ph": "Įveskite savo elektroninio pašto adresą",
        "createacct-another-email-ph": "Įveskite elektroninio pašto adresą",
        "createaccountmail": "Naudokite laikiną atsitiktinį slaptažodį ir nusiųskite jį į elektroninį paštą, nurodytą žemiau.",
+       "createaccountmail-help": "Gali būti naudojamas paskyros sukūrimui kitam asmeniui, neatskleidžiant slaptažodžio.",
        "createacct-realname": "Vardas (neprivaloma)",
        "createaccountreason": "Priežastis:",
        "createacct-reason": "Priežastis",
        "createacct-reason-ph": "Kodėl kuriate kitą paskyrą",
+       "createacct-reason-help": "Pranešimas rodomas paskyros sukūrimo žurnale",
        "createacct-submit": "Sukurkite savo paskyrą",
        "createacct-another-submit": "Sukurti paskyrą",
+       "createacct-continue-submit": "Tęsti paskyros kūrimą",
+       "createacct-another-continue-submit": "Tęsti paskyros kūrimą",
        "createacct-benefit-heading": "{{SITENAME}} sukurtas žmonių kaip jūs.",
        "createacct-benefit-body1": "{{PLURAL:$1|keitimas|keitimai|keitimų}}",
        "createacct-benefit-body2": "{{PLURAL:$1|puslapis|puslapiai}}",
        "nocookiesnew": "Naudotojo paskyra buvo sukurta, bet jūs nesate prisijungęs. {{SITENAME}} naudoja slapukus, kad prijungtų naudotojus. Jūs esate išjungę slapukus. Prašome įjungti juos, tada prisijunkite su savo naujuoju naudotojo vardu ir slaptažodžiu.",
        "nocookieslogin": "{{SITENAME}} naudoja slapukus, kad prijungtų naudotojus. Jūs esate išjungę slapukus. Prašome įjungti juos ir pamėginkite vėl.",
        "nocookiesfornew": "Paskyra nebuvo sukurta, nes mums nepavyko nustatyti jos šaltinio.\nĮsitikinkite, kad įjungti slapukai (angl. cookies) ir tada bandykite dar kartą.",
+       "createacct-loginerror": "Paskyra buvo sėkmingai sukurta, bet nepavyko jūsų automatiškai prijungti. Prašome tęsti [[Special:UserLogin|prisijungiant rankiniu būdu]].",
        "noname": "Jūs nesate nurodęs teisingo naudotojo vardo.",
        "loginsuccesstitle": "Sėkmingai prisijungėte",
        "loginsuccess": "'''Dabar jūs prisijungęs prie {{SITENAME}} kaip „$1“.'''",
        "noemail": "Nėra jokio el. pašto adreso įvesto naudotojui „$1“.",
        "noemailcreate": "Jūs turite nurodyti veikiantį el. pašto adresą",
        "passwordsent": "Naujas slaptažodis buvo nusiųstas į el. pašto adresą,\nužregistruotą naudotojo „$1“.\nPrašome prisijungti vėl, kai jūs jį gausite.",
-       "blocked-mailpassword": "Jūsų IP adresas yra užblokuotas nuo redagavimo, taigi neleidžiama naudoti slaptažodžio priminimo funkcijos, kad apsisaugotume nuo piktnaudžiavimo.",
+       "blocked-mailpassword": "Jūsų IP adresas yra užblokuotas nuo redagavimo. Kad užkirstume kelią piktnaudžiavimui neleidžiama naudoti slaptažodžio priminimo funkcijos iš šio IP adreso.",
        "eauthentsent": "Patvirtinimo laiškas buvo nusiųstas į paskirtąjį el. pašto adresą.\nPrieš išsiunčiant kitą laišką į jūsų dėžutę, jūs turite vykdyti nurodymus laiške, kad patvirtintumėte, kad dėžutė tikrai yra jūsų.",
        "throttled-mailpassword": "Slaptažodžio priminimas jau buvo išsiųstas, per {{PLURAL:$1|$1 paskutinę valandą|$1 paskutines valandas|$1 paskutinių valandų}}.\n\nNorint apsisaugoti nuo piktnaudžiavimo, slaptažodžio priminimas gali būti išsiųstas tik kas {{PLURAL:$1|$1 valandą|$1 valandas|$1 valandų}}.",
        "mailerror": "Klaida siunčiant laišką: $1",
        "createaccount-title": "{{SITENAME}} paskyros kūrimas",
        "createaccount-text": "Projekte {{SITENAME}} ($4) kažkas sukūrė paskyrą „$2“ su slaptažodžiu „$3“ panaudodamas jūsų el. pašto adresą.\nJūs turėtumėte prisijungti ir pasikeisti savo slaptažodį.\n\nJūs galite nekreipti dėmesio į laišką, jei ši paskyra buvo sukurta per klaidą.",
        "login-throttled": "Jūs pernelyg daug kartų bandėte prisijungti.\nPalaukite $1 prieš bandant vėl.",
-       "login-abort-generic": "Jūsų prisijungimas buvo nesėkmingas - Nutraukta",
+       "login-abort-generic": "Jūsų prisijungimas nepavyko - Nutraukta",
        "login-migrated-generic": "Jūsų paskyra buvo perkelta ir jūsų naudotojo vardo šioje wiki daugiau nebėra.",
        "loginlanguagelabel": "Kalba: $1",
        "suspicious-userlogout": "Jūsų prašymas atsijungti buvo atmestas, nes, atrodo, jį klaidingai išsiuntė naršyklė arba spartinantysis tarpinis serveris.",
        "createacct-another-realname-tip": "Tikrojo vardo nurodyti nebūtina.\nJei pasirinksite jį nurodyti, jis bus naudojamas parodymui, kas atliko straipsnio papildymus.",
        "pt-login": "Prisijungti",
        "pt-login-button": "Prisijungti",
+       "pt-login-continue-button": "Tęsti prisijungimą",
        "pt-createaccount": "Sukurti paskyrą",
        "pt-userlogout": "Atsijungti",
        "php-mail-error-unknown": "Nežinoma klaida PHP mail() funkcijoje",
        "newpassword": "Naujas slaptažodis:",
        "retypenew": "Pakartokite naują slaptažodį:",
        "resetpass_submit": "Nustatyti slaptažodį ir prisijungti",
-       "changepassword-success": "Jūsų slaptažodis pakeistas sėkmingai!",
+       "changepassword-success": "Jūsų slaptažodis buvo pakeistas!",
        "changepassword-throttled": "Jūs pastaruoju metu atlikote pernelyg daug bandymų prisijungti. Prašome luktelėti $1 prieš bandant iš naujo.",
        "botpasswords": "Boto slaptažodžiai",
        "botpasswords-summary": "<em>Boto slaptažodžiai</em> leidžia pasiekti naudotojo paskyrą per API, nenaudojant paskyros pagrindinio prisijungimo įgaliojimų. Naudotojo teisės prieinamos būnant prisijungus su boto slaptažodžiu gali būti apribotos.\n\nJeigu nežinote kodėl galite norėti tai daryti, jūs tikriausiai neturėtumėte to daryti. Niekas jūsų neturėtų prašyti sugeneruoti vieno ir perduoti jiems.",
        "botpasswords-invalid-name": "Nurodytame naudotojo varde nėra boto slaptažodžio skirtuko (\"$1\").",
        "botpasswords-not-exist": "Naudotojas \"$1\" neturi boto \"$2\" slaptažodžio.",
        "resetpass_forbidden": "Slaptažodžiai negali būti pakeisti",
+       "resetpass_forbidden-reason": "Slaptažodžiai negali būti pakeisti: $1",
        "resetpass-no-info": "Jūs turite būti prisijungęs, kad pasiektumėte puslapį tiesiogiai.",
        "resetpass-submit-loggedin": "Keisti slaptažodį",
        "resetpass-submit-cancel": "Atšaukti",
-       "resetpass-wrong-oldpass": "Klaidingas laikinas ar esamas slaptažodis.\nJūs galbūt jau sėkmingai pakeitėte savo slaptažodį ar gavote naują laikiną slaptažodį.",
+       "resetpass-wrong-oldpass": "Klaidingas laikinas ar esamas slaptažodis.\nJūs galbūt jau sėkmingai pakeitėte savo slaptažodį ar jau prašėte naujo laikino slaptažodžio.",
        "resetpass-recycled": "Atkurkite savo slaptažodį kitokiu, nei buvo prieš tai.",
        "resetpass-temp-emailed": "Jūs prisijungęs laikinu slaptažodžiu, gautu per elektroninį paštą. Kad baigtumėte jungtis, čia turite nustatyti naują slaptažodį:",
        "resetpass-temp-password": "Laikinas slaptažodis:",
        "passwordreset-emailelement": "Naudotojo vardas: \n$1\n\nLaikinas slaptažodis: \n$2",
        "passwordreset-emailsentemail": "Jeigu šis el. pašto adresas yra susietas su jūsų paskyra, tada slaptažodžio atkūrimo laiškas bus išsiųstas.",
        "passwordreset-emailsentusername": "Jeigu buvo el. paštas susietas su šiuo naudotojo vardu, tai slaptažodžio atkūrimo el. laiškas bus išsiųstas.",
-       "passwordreset-emailsent-capture": "Slaptažodžio priminimo laiškas bus išsiųstas, toks koks parodytas.",
-       "passwordreset-emailerror-capture": "Priminimo elektroninis laiškas buvo sukurtas, toks, koks parodytas žemiau, bet pasiuntimas naudotojui buvo nesėkmingas: $1",
+       "passwordreset-emailsent-capture2": "Slaptažodžio keitimo {{PLURAL:$1|el. laiškas buvo išsiųstas|el. laiškai buvo išsiųsti}}. {{PLURAL:$1|vartotojo vardas ir slaptažodis rodomi|vartotojų vardų ir slaptažodžių sąrašas rodomas}} žemiau.",
+       "passwordreset-emailerror-capture2": "El. laiško siuntimas {{GENDER:$2|vartotojui}} nepavyko: $1 {{PLURAL:$3|vartotojo vardas ir slaptažodis rodomi|vartotojų vardų ir slaptažodžių sąrašas rodomas}} žemiau.",
+       "passwordreset-nocaller": "Skambinantysis turi būti nurodytas",
+       "passwordreset-nosuchcaller": "Skambinantysis neegzistuoja: $1",
+       "passwordreset-invalideamil": "Neteisingas el. pašto adresas",
+       "passwordreset-nodata": "Vartotojo vardas ir el. paštas buvo nepateikti",
        "changeemail": "Pakeisti ar pašalinti el. pašto adresą",
        "changeemail-header": "Užpildykite šią formą, kad pakeistumėte savo el. pašto adresą. Jeigu norite pašalinti bet kurio el. pašto adreso susiejimą su savo paskyra, palikite naujojo el. pašto adreso lauką tuščią, kai pateiksite formą.",
-       "changeemail-passwordrequired": "Jums reikės įvesti savo slaptažodį, kad patvirtintumėte šį pakeitimą.",
        "changeemail-no-info": "Jūs turite būti prisijungęs, kad pasiektumėte puslapį tiesiogiai.",
        "changeemail-oldemail": "Dabartinis el. pašto adresas:",
        "changeemail-newemail": "Naujas el. pašto adresas:",
        "minoredit": "Tai smulkus pataisymas",
        "watchthis": "Stebėti šį puslapį",
        "savearticle": "Išsaugoti puslapį",
+       "savechanges": "Išsaugoti pakeitimus",
        "publishpage": "Skelbti puslapį",
+       "publishchanges": "Skelbti pakeitimus",
        "preview": "Peržiūra",
        "showpreview": "Rodyti peržiūrą",
        "showdiff": "Rodyti skirtumus",
        "undo-nochange": "Panašu, kad keitimas jau buvo atšauktas.",
        "undo-summary": "Atšauktas [[Special:Contributions/$2|$2]] ([[User talk:$2|Aptarimas]] | [[Special:Contributions/$2|{{MediaWiki:Contribslink}}]]) keitimas ($1 versija)",
        "undo-summary-username-hidden": "Atmesti versiją $1, atliktą paslėpto naudotojo",
-       "cantcreateaccounttitle": "Paskyrų kūrimas negalimas",
        "cantcreateaccount-text": "Paskyrų kūrimą iš šio IP adreso ('''$1''') užblokavo [[User:$3|$3]].\n\n$3 nurodyta priežastis yra ''$2''",
        "cantcreateaccount-range-text": "Naudotojas [[User:$3|$3]] nustatė draudimą kurti paskyras iš IP adresų plotmės <strong>$1</strong>, į kurią patenka ir jūsiškis IP adresas (<strong>$4</strong>).",
        "viewpagelogs": "Rodyti šio puslapio specialiuosius veiksmus",
        "mergehistory-empty": "Versijos negali būti sujungtos",
        "mergehistory-done": "$3 $1 {{PLURAL:$3|versija|versijos|versijų}} sėkmingai {{PLURAL:$3|sujungta|sujungtos|sujungta}} su [[:$2]].",
        "mergehistory-fail": "Nepavyksta atlikti istorijų sujungimo, prašome patikrinti puslapio ir laiko parametrus.",
+       "mergehistory-fail-bad-timestamp": "Laiko nuoroda negalima.",
+       "mergehistory-fail-invalid-source": "Šaltinio puslapis negalima.",
        "mergehistory-fail-invalid-dest": "Paskirties puslapis yra neteisingas.",
+       "mergehistory-fail-permission": "Nepakanka teisių istorijai sujungti.",
+       "mergehistory-fail-self-merge": "Šaltinio ir tikslo puslapiai yra vienodi",
        "mergehistory-fail-toobig": "Nepavyksta sulieti istorijos, nes būtina pernešti daugiau, nei leidžia $1 riba, {{PLURAL:$1|versijos|versijų}}.",
        "mergehistory-no-source": "Šaltinio puslapis $1 neegzistuoja.",
        "mergehistory-no-destination": "Rezultato puslapis $1 neegzistuoja.",
        "userrights-changeable-col": "Grupės, kurias galite keisti",
        "userrights-unchangeable-col": "Grupės, kurių negalite keisti",
        "userrights-conflict": "Naudotojo teisių konfliktas! Prašome dar kartą taikyti savo keitimus.",
-       "userrights-removed-self": "Jūs sėkmingai panaikinote savo paties teises. Taigi, daugiau nebegalite pasiekti šio puslapio.",
+       "userrights-removed-self": "Jūs pašalinote savo paties teises. Taigi, nebegalite pasiekti šio puslapio.",
        "group": "Grupė:",
        "group-user": "Naudotojai",
        "group-autoconfirmed": "Automatiškai patvirtinti naudotojai",
        "right-override-export-depth": "Eksportuoti puslapius įtraukiant susietus puslapius iki 5 lygio gylio",
        "right-sendemail": "Siųsti el. laišką kitiems naudotojams",
        "right-passwordreset": "Peržiūrėti slaptažodžio pakeitimo e-mail laiškus",
-       "right-managechangetags": "Kurti ir ištrinti [[Special:Tags|žymes]] iš duomenų bazės",
+       "right-managechangetags": "Kurti ir (de)aktyvuoti [[Special:Tags|žymes]]",
        "right-applychangetags": "Taikyti [[Special:Tags|žymes]] kartu su pokyčiais",
        "right-changetags": "Pridėti ir ištrinti savavališkus [[Special:Tags|žymes]] individualiuose pakeitimuose ir žurnalo įrašuose",
+       "right-deletechangetags": "Ištrinti [[Special:Tags|žymes]] iš duomenų bazės",
        "grant-generic": "\"$1\" teisių rinkinys",
        "grant-group-page-interaction": "Sąveikauti su puslapiais",
        "grant-group-file-interaction": "Sąveikauti su medija",
        "rightslogtext": "Pateikiamas naudotojų teisių pakeitimų sąrašas.",
        "action-read": "skaityti šį puslapį",
        "action-edit": "keisti šį puslapį",
-       "action-createpage": "kurti puslapius",
-       "action-createtalk": "kurti aptarimų puslapius",
+       "action-createpage": "kurti šį puslapį",
+       "action-createtalk": "sukurti šį diskusijų puslapį",
        "action-createaccount": "kurti šią naudotojo paskyrą",
        "action-autocreateaccount": "Automatiškai sukurti šią išorinę naudotojo paskyrą",
        "action-history": "peržiūrėti šio puslapio istoriją",
        "action-viewmyprivateinfo": "peržiūrėti jūsų privačią informaciją",
        "action-editmyprivateinfo": "redaguoti savo privačią informaciją",
        "action-editcontentmodel": "redaguoti puslapio turinio modelį",
-       "action-managechangetags": "sukurti ir ištrinti žymes duomenų bazėje",
+       "action-managechangetags": "kurti ir (de)aktyvuoti žymes",
        "action-applychangetags": "taikyti žymes kartu su savo pokeitymais",
        "action-changetags": "pridėti ir ištrinti savavališkas žymes individualiuose pakeitimuose ir žurnalo įrašuose",
+       "action-deletechangetags": "trinti žymes iš duomenų bazės",
        "nchanges": "$1 {{PLURAL:$1|pakeitimas|pakeitimai|pakeitimų}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|nuo paskutinio apsilankymo}}",
        "enhancedrc-history": "istorija",
        "show": "Rodyti",
        "minoreditletter": "S",
        "newpageletter": "N",
-       "boteditletter": "R",
+       "boteditletter": "r",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|stebintis naudotojas|stebintys naudotojai|stebinčių naudotojų}}]",
        "rc_categories": "Riboti kategorijoms (atskirkite su „|“)",
        "rc_categories_any": "Bet kuris iš pasirinktųjų",
        "apisandbox-dynamic-parameters-add-placeholder": "Parametro pavadinimas",
        "apisandbox-dynamic-error-exists": "Parametras, pavadinimu „$1“ jau yra.",
        "apisandbox-deprecated-parameters": "Nebenaudojami parametrai",
+       "apisandbox-fetch-token": "Automatiškai užpildyti žetoną",
        "apisandbox-submit-invalid-fields-title": "Kai kurie laukai yra neteisingi",
+       "apisandbox-submit-invalid-fields-message": "Prašome pataisyti pažymėtus laukus ir bandyti dar kartą.",
        "apisandbox-results": "Rezultatai",
+       "apisandbox-sending-request": "API prašymas siunčiamas...",
+       "apisandbox-loading-results": "API rezultatai gaunami...",
        "apisandbox-request-url-label": "Prašyti URL:",
-       "apisandbox-request-time": "Užklausos laikas: $1",
+       "apisandbox-request-time": "Užklausos laikas: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Pataisykite žetoną ir pateikite iš naujo",
+       "apisandbox-results-fixtoken-fail": "Nepavyko gauti „$1“ žetono.",
+       "apisandbox-alert-page": "Laukai šiame puslapyje yra negalimi.",
        "apisandbox-alert-field": "Šio lauko reikšmė yra neteisinga.",
        "booksources": "Knygų šaltiniai",
        "booksources-search-legend": "Knygų šaltinių paieška",
        "log-edit-tags": "Redaguoti žymes pasirinktuose žurnalo įrašuose",
        "checkbox-select": "Pasirinkti: $1",
        "checkbox-all": "Visi",
-       "checkbox-none": "Nieko",
-       "checkbox-invert": "Invertuoti",
+       "checkbox-none": "Nieka",
+       "checkbox-invert": "Apmainītė vėituom",
        "allpages": "Visi puslapiai",
        "nextpage": "Kitas puslapis ($1)",
        "prevpage": "Ankstesnis puslapis ($1)",
        "changecontentmodel-success-text": "Turinys, kurio tipas [[:$1]], buvo atnaujintas.",
        "changecontentmodel-cannot-convert": "Turinys [[:$1]] negali būti konvertuotas į $2 tipą.",
        "changecontentmodel-nodirectediting": "$1 turinio modelis nepalaiko tiesioginio redagavimo",
+       "changecontentmodel-emptymodels-title": "Nėra prieinamų turinio modelių",
        "log-name-contentmodel": "Turinio modelio kaitos istorija",
        "log-description-contentmodel": "Įvykiai susiję su puslapio turinio modeliu",
        "logentry-contentmodel-change": "$1 {{GENDER:$2|atnaujino}} puslapio $3 turinio modelį iš $4 į $5",
        "undeletedrevisions": "{{PLURAL:$1|atkurta $1 versija|atkurtos $1 versijos|atkurta $1 versijų}}",
        "undeletedrevisions-files": "{{PLURAL:$1|atkurta $1 versija|atkurtos $1 versijos|atkurta $1 versijų}} ir $2 {{PLURAL:$2|failas|failai|failų}}",
        "undeletedfiles": "{{PLURAL:$1|atkurtas $1 failas|atkurti $1 failai|atkurta $1 failų}}",
-       "cannotundelete": "Atkūrimas nepavyko:\n$1",
+       "cannotundelete": "Visi arba kai kurie atkūrimai nepavyko:\n$1",
        "undeletedpage": "'''$1 buvo atkurtas'''\n\nPeržiūrėkite [[Special:Log/delete|trynimų sąrašą]], norėdami rasti paskutinių trynimų ir atkūrimų sąrašą.",
        "undelete-header": "Kad sužinotumėte, kurie puslapiai paskiausiai ištrinti, žiūrėkite [[Special:Log/delete|šalinimų sąrašą]].",
        "undelete-search-title": "Panaikintų puslapių paieška",
        "sp-contributions-username": "IP adresas arba naudotojo vardas:",
        "sp-contributions-toponly": "Rodyti tik paskutinius keitimus",
        "sp-contributions-newonly": "Rodyti tik tuos keitimus, kuriais sukurti nauji straipsniai",
+       "sp-contributions-hideminor": "Slėpti nedidelius pakeitimus",
        "sp-contributions-submit": "Ieškoti",
        "whatlinkshere": "Susiję puslapiai",
        "whatlinkshere-title": "Puslapiai, kurie nurodo į „$1“",
        "lockdbsuccesstext": "Duomenų bazė buvo užrakinta.\n<br />Nepamirškite [[Special:UnlockDB|pašalinti užraktą]], kai techninė profilaktika bus baigta.",
        "unlockdbsuccesstext": "Duomenų bazė buvo atrakinta.",
        "lockfilenotwritable": "Duomenų bazės užrakto failas nėra įrašomas. Norint užrakinti ar atrakinti duomenų bazę, tinklapio serveris privalo turėti įrašymo teises šiam failui.",
+       "databaselocked": "Duomenų bazėje jau yra užrakinta.",
        "databasenotlocked": "Duomenų bazė neužrakinta.",
        "lockedbyandtime": "(užrakino {{GENDER:$1|$1}}, diena $2, laikas $3)",
        "move-page": "Pervadinti $1",
        "confirmemail_body_set": "Kažkas (tikriausiai jūs) iš IP adreso $1,\nnustatė svetainės {{SITENAME}} paskyros „$2“ elektroninio pašto adresą į jūsiškį.\n\nKad patvirtintumėte, kad ši paskyra tikrai priklauso jums ir tokiu būdu aktyvuotumėte\nelektroninio pašto galimybes svetainėje {{SITENAME}}, atverkite šią nuorodą savo naršyklėje:\n\n$3\n\nJei paskyra jums *nepriklauso*, spauskite šią nuorodą,\nkad atšauktumėte elektroninio pašto adreso patvirtinimą:\n\n$5\n\nŠis patvirtinimo kodas baigs galioti $4.",
        "confirmemail_invalidated": "El. pašto adreso patvirtinimas atšauktas",
        "invalidateemail": "El. pašto patvirtinimo atšaukimas",
+       "notificationemail_subject_changed": "{{SITENAME}} užregistruotas el. pašto adresas buvo pakeistas",
+       "notificationemail_subject_removed": "{{SITENAME}} užregistruotas el. pašto adresas buvo pašalintas",
        "scarytranscludedisabled": "[Tarpprojektinis įterpimas yra išjungtas]",
        "scarytranscludefailed": "[Šablono gavimas iš $1 nepavyko]",
        "scarytranscludefailed-httpstatus": "[Šablono iškviesti nepavyko $1: HTTP $2]",
        "confirm-watch-top": "Pridėti šį puslapį į stebimųjų sąrašą?",
        "confirm-unwatch-button": "Gerai",
        "confirm-unwatch-top": "Pašalinti šį puslapį iš jūsų stebimųjų sąrašo?",
+       "confirm-rollback-button": "Gerai",
        "quotation-marks": "„$1“",
        "imgmultipageprev": "← ankstesnis puslapis",
        "imgmultipagenext": "kitas puslapis →",
        "pagelang-submit": "Pateikti",
        "right-pagelang": "Keisti puslapio kalbą",
        "action-pagelang": "keisti puslapio kalbą",
-       "log-name-pagelang": "Keisti kalbos žurnalą",
+       "log-name-pagelang": "Kalbos keitimų žurnalas",
        "log-description-pagelang": "Tai pakeitimų žurnalas puslapio kalbomis.",
-       "logentry-pagelang-pagelang": "$1 {{GENDER:$2|pakeitė}} puslapio kalbą $3 iš $4 į $5.",
+       "logentry-pagelang-pagelang": "$1 {{GENDER:$2|pakeitė}} $3 kalbą iš $4 į $5",
        "default-skin-not-found": "Ups! Jūsų viki numatytoji išvaizda, nustatyta <code dir=\"ltr\">$wgDefaultSkin</code> kaip <code>$1</code>, yra negalima.\n\nPanašu, kad Jūsų instaliacija turi {{PLURAL:$4|šią išvaizdą|šias išvaizdas}}. Žiūrėkite [https://www.mediawiki.org/wiki/Manual:Skin_configuration Instrukcija: Išvaizdos konfigūracija] dėl informacijos kaip įgalinti {{PLURAL:$4|ją|jas ir pasirinkti numatytąją}}.\n\n$2\n\n; Jei ką tik įsidiegėte MediaWiki:\n: Jūs tikriausiai įsidiegėte iš git arba tiesiai iš kodo naudodami kitą metodą. To ir buvo tikimasi. Pabandykite įdiegti išvaizdų iš [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org išvaizdų katalogo]:\n:* Atsisiųsti [https://www.mediawiki.org/wiki/Download tvarkyklę], kuri turi keletą išvaizdų ir plėtinių. Jūs galėsite nukopijuoti ir įklijuoti <code>skins/</code> katalogą iš jo.\n:* Atsisiųsti individualias išvaizdas iš [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Naudoti Git išvaizdoms atsisiųsti].\n: Tai neturėtų trukdyti jūsų git saugyklai jei Jūs esate MediaWiki kūrėjas.\n\n; Jei Jūs ką tik atnaujinote MediaWiki:\n: MediaWiki 1.24 ir naujesnės versijos daugiau automatiškai nebeįgalina įdiegtų išvaizdų (žiūrėkite [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Instrukcija: Išvaizdų automatinis aptikimas]). Jūs galite įklijuoti {{PLURAL:$5|šią eilutę|šias eilutes}} į <code>LocalSettings.php</code>, kad įgalintumėte {{PLURAL:$5||visus}} šiuo metu {{PLURAL:$5|įdiegtą išvaizdą|įdiegtas išvaizdas}}:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Jei Jūs ką tik pakeitėte <code>LocalSettings.php</code>:\n: Dar kartą patikrinkite išvaizdos pavadinimą ar nepadarėte spausdinimo klaidos.",
        "default-skin-not-found-no-skins": "Ups! Jūsų viki numatytoji išvaizdą, nurodyta <code>$wgDefaultSkin</code> <code>$1</code>, yra negalima.\n\nJūs neturite įdiegtų išvaizdų.\n\n; Jei ką tik įsidiegėte MediaWiki:\n: Jūs tikriausiai įsidiegėte iš git arba tiesiai iš kodo naudodami kitą metodą. To buvo tikimasi. Pabandykite įsidiegti išvaizdų iš [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org išvaizdų katalogo] taip:\n:* Parsisiųsdami  [https://www.mediawiki.org/wiki/Download tvarkyklę], kuri turi kelias išvaizdas ir plėtinius. Jūs galite nukopijuoti ir įklijuoti <code>skins/</code> katalogą iš jos.\n:* Persiųsdami individualias išvaizdas iš [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Naudodami Git išvaizdų parsisiuntimui].\n: Tai neturėtų trukdyti Jūsų git saugyklai jei Jūs esate MediaWiki kūrėjas. Žiūrėkite [https://www.mediawiki.org/wiki/Manual:Skin_configuration Instrukcija: Išvaizdos konfigūracija] dėl informacijos kaip įgalinti išvaizdas ir pasirinkti numatytąją.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (įgalinta)",
        "special-characters-group-ipa": "IPA",
        "special-characters-group-symbols": "Simboliai",
        "special-characters-group-greek": "Graikų",
+       "special-characters-group-greekextended": "Graikų išplėstinis",
        "special-characters-group-cyrillic": "Kirilica",
        "special-characters-group-arabic": "Arabų",
        "special-characters-group-arabicextended": "Arabic extended",
        "mw-widgets-dateinput-no-date": "Nepasirinkta data",
        "mw-widgets-titleinput-description-new-page": "puslapis dar neegzistuoja",
        "mw-widgets-titleinput-description-redirect": "nukreipti į $1",
-       "api-error-blacklisted": "Prašome pasirinkti kitą, aprašomąją antraštę.",
        "sessionmanager-tie": "Negalima kombinuoti kelių užklausų autentikacijos tipų: $1.",
        "sessionprovider-generic": "$1 sesijos",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sesijos su slapukais",
        "sessionprovider-nocookies": "Slapukai gali būti neaktyvuoti. Įsitikinkite, kad slapukai yra aktyvuoti ir pradėkite vėl.",
        "randomrootpage": "Atsitiktinis šakninis puslapis",
+       "log-action-filter-block": "Blokavimo tipas:",
+       "log-action-filter-delete": "Trynimo tipas:",
+       "log-action-filter-import": "Importo tipas:",
+       "log-action-filter-managetags": "Žymės tvarkymo veiksmo tipas:",
+       "log-action-filter-move": "Kėlimo tipas:",
+       "log-action-filter-newusers": "Paskyros kūrimo tipas:",
+       "log-action-filter-protect": "Apsaugos tipas:",
+       "log-action-filter-rights": "Teisių tipo keitimas:",
+       "log-action-filter-upload": "Įkėlimo tipas:",
        "log-action-filter-all": "Visi",
+       "log-action-filter-block-block": "Blokas",
+       "log-action-filter-block-reblock": "Blokavimo pakeitimas",
+       "log-action-filter-block-unblock": "Atblokuoti",
+       "log-action-filter-delete-delete": "Puslapio trynimas",
+       "log-action-filter-delete-restore": "Puslapio trynimo atšaukimas",
+       "log-action-filter-delete-event": "Žurnalo trynimas",
+       "log-action-filter-delete-revision": "Peržiūros trynimas",
+       "log-action-filter-managetags-create": "Žymės kūrimas",
+       "log-action-filter-managetags-delete": "Žymės trynimas",
+       "log-action-filter-managetags-activate": "Žymės aktyvavimas",
+       "log-action-filter-managetags-deactivate": "Žymės deaktyvavimas",
        "log-action-filter-newusers-autocreate": "Automatinis kūrimas",
-       "log-action-filter-protect-protect": "Apsauga"
+       "log-action-filter-protect-protect": "Apsauga",
+       "log-action-filter-protect-modify": "Apsaugos keitimas",
+       "log-action-filter-protect-move_prot": "Apsauga perkelta",
+       "log-action-filter-rights-rights": "Rankinis keitimas",
+       "log-action-filter-rights-autopromote": "Automatinis keitimas",
+       "log-action-filter-upload-upload": "Naujas įkėlimas",
+       "log-action-filter-upload-overwrite": "Kelti iš naujo",
+       "authmanager-create-disabled": "Paskyros kūrimas yra išjungtas.",
+       "authmanager-create-from-login": "Norėdami sukurti paskyrą užpildykite laukelius žemiau.",
+       "authmanager-authplugin-setpass-failed-title": "Slaptažodžio keitimas nepavyko",
+       "authmanager-authplugin-setpass-bad-domain": "Negalimas domenas.",
+       "authmanager-autocreate-noperm": "Automatinis paskyros kūrimas neleidžiamas.",
+       "authmanager-autocreate-exception": "Automatinis paskyros kūrimas laikinai neleidžiamas dėl ankstesnių klaidų.",
+       "authmanager-userdoesnotexist": "Vartotojo paskyrą „$1“ nėra registruota.",
+       "authmanager-userlogin-remembermypassword-help": "Ar slaptažodis turėtų būti išsaugotas ilgesniam laikui nei ši sesija.",
+       "authmanager-username-help": "Vartotojo vardas dėl autentikacijos.",
+       "authmanager-password-help": "Slaptažodis dėl autentikacijos.",
+       "authmanager-domain-help": "Domenas dėl išorinės autentikacijos.",
+       "authmanager-retype-help": "Slaptažodį dar kartą, kad patvirtintumėte.",
+       "authmanager-email-label": "El. paštas",
+       "authmanager-email-help": "El. pašto adresas",
+       "authmanager-realname-label": "Tikras vardas",
+       "authmanager-realname-help": "Tikras vartotojo vardas",
+       "authmanager-provider-password": "Autentifikacija slaptažodžiu",
+       "authmanager-provider-password-domain": "Autentifikavimas slaptažodžiu ir domenu",
+       "authmanager-provider-temporarypassword": "Laikinas slaptažodis",
+       "authprovider-confirmlink-request-label": "Paskyros, kurios turėtų būti susietos",
+       "authprovider-confirmlink-success-line": "$1: Susieta sėkmingai.",
+       "authprovider-confirmlink-failed": "Paskyros susiejimas nebuvo visiškai sėkmingas: $1",
+       "authprovider-confirmlink-ok-help": "Tęsti po susiejimo klaidos žinutės parodymo.",
+       "authprovider-resetpass-skip-label": "Praleisti",
+       "authprovider-resetpass-skip-help": "Praleisti slaptažodžio perstatymą.",
+       "specialpage-securitylevel-not-allowed-title": "Neleidžiama",
+       "cannotauth-not-allowed-title": "Teisė nesuteikta",
+       "cannotauth-not-allowed": "Jūs negalite naudotis šiuo puslapiu",
+       "credentialsform-account": "Paskyros vardas:",
+       "cannotlink-no-provider-title": "Nėra paskyrų, kurias galima susieti",
+       "cannotlink-no-provider": "Nėra paskyrų, kurias galima susieti",
+       "linkaccounts": "Susieti paskyras",
+       "linkaccounts-success-text": "Paskyra buvo susieta.",
+       "linkaccounts-submit": "Susieti paskyras",
+       "unlinkaccounts": "Atsieti paskyras",
+       "unlinkaccounts-success": "Paskyra buvo atsieta."
 }
index 3301f8a..e528dde 100644 (file)
        "tagline": "No ''{{grammar:ģenitīvs|{{SITENAME}}}}''",
        "help": "Palīdzība",
        "search": "Meklēt",
+       "search-ignored-headings": " #<!-- atstāt šo rindiņu tādu, kāda tā ir --> <pre>\n#Virsraksti, kas tiks ignorēti meklējot.\n#Izmaiņas stāsies spēkā, kad lapa ar virsrakstiem ir indeksēta.\n#Jūs varat piespiest lapai tikt indeksētai vēlreiz ar nulles labojumu.\n#Sintakse:\n# * Jebkas no \"#\" zīmes līdz rindiņas beigām ir komentārs.\n# * Katra līnija ir precīzs ignorējamais virsraksts, ieskaitot lielos / mazos burtus.\nAtsauces\nĀrējās saites\nSkatīt arī\n #</pre> <!-- atstāt šo rindiņu tādu, kāda tā ir -->",
        "searchbutton": "Meklēt",
        "go": "Aiziet!",
        "searcharticle": "Aiziet!",
        "yourpasswordagain": "Atkārto paroli",
        "createacct-yourpasswordagain": "Apstipriniet paroli",
        "createacct-yourpasswordagain-ph": "Vēlreiz ievadiet paroli",
-       "remembermypassword": "Atcerēties pēc pārlūka aizvēršanas (spēkā ne vairāk kā $1 {{PLURAL:$1|dienas|diena|dienas}}).",
        "userlogin-remembermypassword": "Atcerēties mani",
        "userlogin-signwithsecure": "Izmantot drošu savienojumu",
        "yourdomainname": "Tavs domēns",
        "createacct-reason-ph": "Kāpēc jūs veidojat citu kontu",
        "createacct-submit": "Izveidot savu kontu",
        "createacct-another-submit": "Izveidot citu dalībnieka kontu",
+       "createacct-continue-submit": "Turpināt konta izveidi",
+       "createacct-another-continue-submit": "Turpināt konta izveidi",
        "createacct-benefit-heading": "{{SITENAME}} darbojas ar tādu cilvēku kā Tu ieguldījumu.",
        "createacct-benefit-body1": "{{PLURAL:$1|labojumi|labojums|labojumi}}",
        "createacct-benefit-body2": "{{PLURAL:$1|lapas|lapa|lapas}}",
        "newpassword": "Jaunā parole",
        "retypenew": "Atkārto jauno paroli",
        "resetpass_submit": "Uzstādīt paroli un ieiet",
-       "changepassword-success": "Jūsu parole tika nomainīta veiksmīgi!",
+       "changepassword-success": "Tava parole tika nomainīta!",
        "botpasswords": "Botu paroles",
+       "botpasswords-disabled": "Botu paroles ir atspējotas.",
+       "botpasswords-existing": "Esošās botu paroles",
        "botpasswords-createnew": "Izveidot jaunu bota paroli",
        "botpasswords-editexisting": "Rediģētu esošu bota paroli",
        "botpasswords-label-appid": "Bota nosaukums:",
        "botpasswords-label-resetpassword": "Atiestatīt paroli",
        "botpasswords-label-restrictions": "Lietošanas ierobežojumi:",
        "botpasswords-label-grants-column": "Piešķirts",
+       "botpasswords-created-title": "Bota parole izveidota",
+       "botpasswords-updated-title": "Bota parole atjaunināta",
        "botpasswords-deleted-title": "Bota parole dzēsta",
        "resetpass_forbidden": "Paroles nav iespējams nomainīt",
        "resetpass-no-info": "Jums ir nepieciešams ieiet, lai tūlīt piekļūtu šai lapai.",
        "passwordreset-emailtitle": "Konta informācija {{SITENAME}}",
        "passwordreset-emailelement": "Lietotājvārds: \n$1\n\nPagaidu parole: \n$2",
        "passwordreset-emailsentemail": "Paroles atiestatīšanas e-pasts ir nosūtīts.",
-       "passwordreset-emailsent-capture": "Atgādinājuma e-pasta ziņojums ir nosūtīts, tas parādīts zemāk.",
-       "passwordreset-emailerror-capture": "Atgādinājuma e-pasta ziņojums tika izveidots, tas parādīts zemāk, bet nosūtīšana lietotājam neizdevās: $1",
-       "changeemail": "Mainīt e-pasta adresi",
+       "passwordreset-nosuchcaller": "Izsaucējs nepastāv: $1",
+       "passwordreset-invalideamil": "Nederīga e-pasta adrese",
+       "changeemail": "Mainīt vai noņemt e-pasta adresi",
        "changeemail-header": "Mainīt konta e-pasta adresi",
        "changeemail-oldemail": "Pašreizējā e-pasta adrese:",
        "changeemail-newemail": "Jaunā e-pasta adrese:",
        "minoredit": "Maznozīmīgs labojums",
        "watchthis": "Uzraudzīt šo lapu",
        "savearticle": "Saglabāt lapu",
+       "savechanges": "Saglabāt izmaiņas",
        "publishpage": "Saglabāt lapu",
+       "publishchanges": "Publicēt izmaiņas",
        "preview": "Pirmskats",
        "showpreview": "Rādīt pirmskatu",
        "showdiff": "Rādīt izmaiņas",
        "undo-failure": "Šo labojumu nevar atcelt, jo ir veikti nozīmīgi labojumi vēl pēc šī labojuma izdarīšanas.",
        "undo-norev": "Šo izmaiņu nevar atcelt, jo tādas nav vai tā ir izdzēsta.",
        "undo-summary": "Atcēlu [[Special:Contributions/$2|$2]] ([[User talk:$2|Diskusija]]) izdarīto izmaiņu $1",
-       "cantcreateaccounttitle": "Nevar izveidot dalībnieku",
        "cantcreateaccount-text": "[[Lietotājs:$3|$3]] ir bloķējis lietotāja izveidošanu no šīs IP adreses ('''$1''').\n\n$3 norādītais iemesls ir ''$2''",
        "viewpagelogs": "Apskatīt ar šo lapu saistītos reģistru ierakstus",
        "nohistory": "Šai lapai nav pieejama versiju hronoloģija.",
        "history-feed-empty": "Pieprasītā lapa nepastāv.\nIespējams, tā ir izdzēsta vai pārdēvēta.\nMēģiniet [[Special:Search|meklēt]], lai atrastu saistītas lapas!",
        "rev-deleted-comment": "(labojuma kopsavilkums dzēsts)",
        "rev-deleted-user": "(lietotāja vārds nodzēsts)",
-       "rev-deleted-event": "(reģistra ieraksts nodzēsts)",
+       "rev-deleted-event": "(reģistra detaļas noņemtas)",
        "rev-deleted-user-contribs": "[lietotājvārds vai IP adrese ir dzēsta — izmaiņa slēpta no devuma]",
        "rev-deleted-text-permission": "Šī lapas versija ir '''dzēsta'''.\nSīkāku informāciju var atrast [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} dzēšanas reģistrā].",
        "rev-deleted-text-view": "Šī lapas versija ir '''dzēsta'''.\nTo varat apskatīt, jo esat administrators. Sīkāku informāciju var atrast [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} dzēšanas reģistrā].",
        "mergehistory-submit": "Apvienot versijas",
        "mergehistory-empty": "Neviena versija nevar tikt apvienota",
        "mergehistory-fail": "Nav iespējams apvienot hronoloģiju, lūdzu, pārbaudiet vēlreiz lapu un laika parametrus.",
+       "mergehistory-fail-bad-timestamp": "Laika zīmogs ir nederīgs.",
+       "mergehistory-fail-invalid-source": "Avota lapa ir nederīga.",
+       "mergehistory-fail-invalid-dest": "Mērķa lapa ir nederīga.",
        "mergehistory-no-source": "Avota lapa $1 nepastāv.",
        "mergehistory-no-destination": "Mērķa lapa $1 nepastāv.",
        "mergehistory-invalid-source": "Avota lapas nosaukumam jābūt derīgam.",
        "prefs-resetpass": "Mainīt paroli",
        "prefs-changeemail": "Mainīt vai noņemt e-pastu",
        "prefs-setemail": "Uzstādīt e-pasta adresi",
-       "prefs-email": "E-pasta uzstādījumi",
+       "prefs-email": "E-pasta iestatījumi",
        "prefs-rendering": "Izskats",
        "saveprefs": "Saglabāt",
        "restoreprefs": "Atjaunot noklusētos uzstādījumus (visās sadaļās)",
        "right-passwordreset": "Apskatīt paroles atiestatīšanas e-pasta ziņojumus",
        "grant-group-email": "Sūtīt e-pastu",
        "grant-createaccount": "Izveidot kontu",
+       "grant-editmywatchlist": "Labot uzraugāmo rakstu sarakstu",
+       "grant-editpage": "Labot esošās lapas",
+       "grant-editprotected": "Labot aizsargātās lapas",
+       "grant-basic": "Pamattiesības",
+       "grant-viewdeleted": "Skatīt dzēstos failus un lapas",
        "newuserlogpage": "Jauno dalībnieku reģistrs",
        "newuserlogpagetext": "Jauno lietotājvārdu reģistrs.",
        "rightslog": "Dalībnieku tiesību reģistrs",
        "rightslogtext": "Šis ir dalībnieku tiesību izmaiņu reģistrs.",
        "action-read": "lasīt šo lapu",
        "action-edit": "labot šo lapu",
-       "action-createpage": "izveidot lapas",
-       "action-createtalk": "izveidot diskusiju lapas",
+       "action-createpage": "izveidot šo lapu",
+       "action-createtalk": "izveidot šo diskusiju lapu",
        "action-createaccount": "izveidot šo dalībnieka kontu",
        "action-history": "apskatīt šīs lapas vēsturi",
        "action-minoredit": "atzīmēt šo labojumu kā maznozīmīgu",
        "rcshowhidemine": "$1 manus",
        "rcshowhidemine-show": "Rādīt",
        "rcshowhidemine-hide": "Slēpt",
+       "rcshowhidecategorization": "$1 lapu kategorizēšanu",
        "rcshowhidecategorization-show": "Rādīt",
        "rcshowhidecategorization-hide": "Paslēpt",
        "rclinks": "Parādīt pēdējās $1 izmaiņas pēdējās $2 dienās.<br />$3",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[šo lapu uzrauga $1 {{PLURAL:$1|dalībnieki|dalībnieks|dalībnieki}}]",
        "rc_categories": "Ierobežot uz kategorijām (atdalīt ar \"|\"):",
-       "rc_categories_any": "Jebkas",
+       "rc_categories_any": "Jebkas no izvēlētā",
        "rc-change-size-new": "$1 {{PLURAL:$1|baiti|baits|baiti}} pēc izmaiņām",
        "newsectionsummary": "/* $1 */ jauna sadaļa",
        "rc-enhanced-expand": "Skatīt detaļas",
        "recentchangeslinked-summary": "Šiet ir nesen izdarītās izmaiņas lapās, uz kurām ir saites no norādītās lapas (vai norādītajā kategorijā ietilpstošās lapas).\nLapas, kas ir tavā [[Special:Watchlist|uzraugāmo rakstu sarakstā]] ir '''treknas'''.",
        "recentchangeslinked-page": "Lapas nosaukums:",
        "recentchangeslinked-to": "Rādīt izmaiņas lapās, kurās ir saites uz šo lapu (nevis lapās uz kurām ir saites no šīs lapas)",
+       "autochange-username": "MediaWiki automātiskā izmaiņa",
        "upload": "Augšupielādēt failu",
        "uploadbtn": "Augšupielādēt",
        "reuploaddesc": "Atcelt augšupielādi un atgriezties pie augšupielādes veidnes.",
        "uploaderror": "Augšupielādes kļūda",
        "upload-recreate-warning": "<strong>Brīdinājums: Fails ar šādu nosaukumu ir dzēsts vai pārvietots.</strong>\n\nDzēšanas un pārvietošanas reģistri šai lapai ir pieejami šeit:",
        "uploadtext": "Pirms tu kaut ko augšupielādē, noteikti izlasi un ievēro [[Project:Attēlu izmantošanas noteikumi|attēlu izmantošanas noteikumus]].\n\nLai aplūkotu vai meklētu agrāk augšupielādētus attēlus,\ndodies uz [[Special:FileList|augšupielādēto attēlu sarakstu]].\nAugšupielādes un dzēšanas tiek reģistrētas [[Special:Log/upload|augšupielādes reģistrā]] un [[Special:Log/delete|dzēšanas reģistrā]].\n\nIzmanto šo veidni, lai augšupielādētu jaunus attēlu failus, ar kuriem ilustrēt tevis izmainītās lapas.\nGandrīz visos pārlūkos tev vajadzētu redzēt pogu '''\"Choose...\",''' kuru spiežot parādīsies faila atvēršanas dialogs.\nIzvēloties kādu failu, tā adrese parādīsies ailītē blakus šai pogai.\nTev ir arī jāatzīmē ailīte, kas apstiprina, ka tu nepārkāp nekādas autortiesības, augšupielādējot šo failu.\nSpied pogu '''Augšupielādēt''', lai pabeigtu augšupielādi.\nTas var ieilgt, ja tavs interneta pieslēgums ir lēns.\n\nIeteicamie formāti ir:\n* JPEG - ja tā ir fotogrāfija,\n* PNG - ja tas ir zīmējums vai kāda ikona, un\n* OGG - ja tas ir skaņas fails.\n\nLūdzu, pārliecinies, ka faila nosaukums ir pietiekami aprakstošs, lai izvairītos no neskaidrībām. Lai attēlu pēc tam ievietotu kādā lapā, izmanto šādi noformētu linkus:\n* '''<nowiki>[[</nowiki>{{ns:file}}<nowiki>:Fails.jpg|paskaidrojošs teksts]]</nowiki>'''\n* '''<nowiki>[[</nowiki>{{ns:file}}<nowiki>:Fails.png|paskaidrojošs teksts]]</nowiki>'''\nvai skaņām\n* '''<nowiki>[[</nowiki>{{ns:media}}<nowiki>:Fails.ogg]]</nowiki>'''\n\nLūdzu, ņem vērā, ka tāpat kā citas wiki lapas arī tevis augšupielādētos failus citi var mainīt vai dzēst, ja uzskata, ka tas nāktu par labu šim projektam, kā arī atceries, ka tev var tikt liegta augšupielādes iespēja, ja tu šo sistēmu.",
-       "upload-permitted": "Atļautie failu tipi: $1.",
-       "upload-preferred": "Ieteicamie failu tipi: $1.",
-       "upload-prohibited": "Aizliegtie failu tipi: $1.",
+       "upload-permitted": "Atļautie failu {{PLURAL:$2|tipi|tips|tipi}}: $1.",
+       "upload-preferred": "Ieteicamie failu {{PLURAL:$2|tipi|tips|tipi}}: $1.",
+       "upload-prohibited": "Aizliegtie failu {{PLURAL:$2|tipi|tips|tipi}}: $1.",
        "uploadlogpage": "Augšupielādes reģistrs",
        "uploadlogpagetext": "Zemāk ir redzams jaunāko augšuplādēto failu saraksts.\nPārskatāmāka versija ir pieejama [[Special:NewFiles|jauno attēlu galerijā]].",
        "filename": "Faila nosaukums",
        "upload-dialog-button-save": "Saglabāt",
        "upload-dialog-button-upload": "Augšupielādēt",
        "upload-form-label-infoform-title": "Papildinformācija",
+       "upload-form-label-infoform-name": "Nosaukums",
        "upload-form-label-infoform-description": "Apraksts",
        "upload-form-label-usage-title": "Pielietojums",
        "upload-form-label-usage-filename": "Faila nosaukums",
+       "upload-form-label-own-work": "Šis ir manis paša darbs",
        "upload-form-label-infoform-categories": "Kategorijas",
        "upload-form-label-infoform-date": "Datums",
        "backend-fail-stream": "Nevar straumēt failu $1.",
        "backend-fail-read": "Nevar lasīt failu $1.",
        "backend-fail-create": "Nevar izveidot failu $1.",
        "zip-wrong-format": "Norādītais fails nebija ZIP fails.",
-       "uploadstash-errclear": "Failu tīrīšana bija neveiksmīga.",
+       "uploadstash-errclear": "Failu tīrīšana neizdevās.",
        "uploadstash-refresh": "Atsvaidzināt failu sarakstu",
        "img-auth-accessdenied": "Pieeja liegta",
        "img-auth-nopathinfo": "Trūkst PATH_INFO.\nJūsu serveris nav konfigurēts nodot šo informāciju.\nTas var būt bāzēts uz CGI un neatbalstīt img_auth.\nSkatīt https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "listfiles_thumb": "Sīktēls",
        "listfiles_date": "Datums",
        "listfiles_name": "Nosaukums",
-       "listfiles_user": "Lietotājs",
+       "listfiles_user": "Dalībnieks",
        "listfiles_size": "Izmērs",
        "listfiles_description": "Apraksts",
        "listfiles_count": "Versijas",
        "filehist-thumb": "Attēls",
        "filehist-thumbtext": "$1 versijas sīktēls",
        "filehist-nothumb": "Nav sīktēla",
-       "filehist-user": "Lietotājs",
+       "filehist-user": "Dalībnieks",
        "filehist-dimensions": "Izmēri",
        "filehist-filesize": "Faila izmērs",
        "filehist-comment": "Komentārs",
        "wantedtemplates": "Vajadzīgās veidnes",
        "mostlinked": "Lapas, uz kurām ir visvairāk norāžu",
        "mostlinkedcategories": "Kategorijas, uz kurām ir visvairāk saišu",
-       "mostlinkedtemplates": "Visvairāk izmantotās veidnes",
+       "mostlinkedtemplates": "Visvairāk iekļautās lapas",
        "mostcategories": "Raksti ar visvairāk kategorijām",
        "mostimages": "Attēli, uz kuriem ir visvairāk saišu",
        "mostinterwikis": "Lapas ar visvairāk starpviki saitēm",
        "protectedpages-indef": "Tikai bezgalīgas aizsardzības",
        "protectedpages-cascade": "Tikai kaskādes aizsardzības",
        "protectedpages-noredirect": "Paslēpt pāradresācijas",
+       "protectedpages-timestamp": "Laika zīmogs",
        "protectedpages-page": "Lapa",
        "protectedpages-reason": "Iemesls",
        "protectedpages-unknown-timestamp": "Nav zināms",
        "usercreated": "{{GENDER:$3|Izveidoja}} $1 plkst. $2",
        "newpages": "Jaunas lapas",
        "newpages-submit": "Rādīt",
-       "newpages-username": "Lietotājs:",
+       "newpages-username": "Dalībnieks:",
        "ancientpages": "Vecākās lapas",
        "move": "Pārvietot",
        "movethispage": "Pārvietot šo lapu",
        "apisandbox-api-disabled": "API ir atspējots šajā tīmekļa vietnē.",
        "apisandbox-reset": "Notīrīt",
        "apisandbox-retry": "Mēģināt vēlreiz",
+       "apisandbox-no-parameters": "Šo API modulim nav parametru.",
+       "apisandbox-helpurls": "Palīdzības saites",
        "apisandbox-examples": "Piemēri",
+       "apisandbox-dynamic-parameters": "Papildu parametri",
+       "apisandbox-dynamic-parameters-add-label": "Pievienot parametru:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Parametra nosaukums",
        "apisandbox-results": "Rezultāti",
        "apisandbox-request-url-label": "Pieprasījuma URL:",
-       "apisandbox-request-time": "Pieprasījuma izpildes laiks: $1",
+       "apisandbox-request-time": "Pieprasījuma izpildes laiks: {{PLURAL:$1|$1 ms}}",
        "booksources": "Grāmatu avoti",
        "booksources-search-legend": "Meklēt grāmatu avotus",
        "booksources-search": "Meklēt",
        "alllogstext": "Visi pieejamie {{grammar:akuzatīvs{{SITENAME}}}} reģistri.\nTu vari sašaurināt aplūkojamo reģistru, izvēloties reģistra veidu, lietotāja vārdu vai reģistrēto lapu. Visi teksta lauki izšķir lielos un mazos burtus.",
        "logempty": "Reģistrā nav atbilstošu ierakstu.",
        "log-title-wildcard": "Meklēt virsrakstus, kas sākas ar šo tekstu",
+       "checkbox-select": "Izvēlēties: $1",
+       "checkbox-all": "Visus",
+       "checkbox-none": "Nevienu",
+       "checkbox-invert": "Otrādi",
        "allpages": "Visas lapas",
        "nextpage": "Nākamā lapa ($1)",
        "prevpage": "Iepriekšējā lapa ($1)",
        "listgrouprights-removegroup-self-all": "Noņemt visas grupas no sava konta",
        "listgrouprights-namespaceprotection-header": "Vārdtelpas ierobežojumi",
        "listgrouprights-namespaceprotection-namespace": "Vārdtelpa",
+       "listgrants-rights": "Tiesības",
        "trackingcategories-nodesc": "Apraksts nav pieejams.",
        "trackingcategories-disabled": "Kategorija ir atslēgta",
        "mailnologin": "Nav adreses, uz kuru sūtīt",
        "mailnologintext": "Tev jābūt [[Special:UserLogin|iegājušam]], kā arī tev jābūt [[Special:Preferences|norādītai]] derīgai e-pasta adresei, lai sūtītu e-pastu citiem lietotājiem.",
        "emailuser": "Sūtīt e-pastu šim lietotājam",
-       "emailuser-title-target": "Nosūtīt e-pastu {{GENDER:$1|šim lietotājam|šai lietotājai}}",
+       "emailuser-title-target": "Nosūtīt e-pastu {{GENDER:$1|šim dalībniekam|šai dalībniecei}}",
        "emailuser-title-notarget": "Sūtīt e-pastu lietotājam",
        "emailpagetext": "Ar šo veidni ir iespējams nosūtīt e-pastu šim {{GENDER:$1|lietotājam}}.\nTā e-pasta adrese, kuru tu esi norādījis [[Special:Preferences|savā izvēļu lapā]], parādīsies e-pasta \"From\" lauciņā, tādejādi saņēmējs varēs tev atbildēt.",
        "defemailsubject": "{{SITENAME}} e-pasts no lietotāja \"$1\"",
        "wlheader-enotif": "E-pasta paziņojumi ir ieslēgti.",
        "wlheader-showupdated": "Lapas, kuras ir tikušas izmainītas, kopš tu tās pēdējoreiz apskatījies, te rādās ar '''pustrekniem''' burtiem",
        "wlshowlast": "Parādīt izmaiņas pēdējo $1 stundu laikā vai $2 dienu laikā, vai arī .",
+       "watchlist-hide": "Slēpt",
+       "watchlist-submit": "Rādīt",
+       "wlshowhidebots": "boti",
+       "wlshowhideliu": "reģistrēti lietotāji",
+       "wlshowhideanons": "anonīmi lietotāji",
+       "wlshowhidepatr": "pārbaudīti labojumi",
+       "wlshowhidemine": "mani labojumi",
+       "wlshowhidecategorization": "lapu kategorizēšana",
        "watchlist-options": "Uzraugāmo rakstu saraksta opcijas",
        "watching": "Uzrauga...",
        "unwatching": "Neuzrauga...",
        "rollback-success": "Novērsu izmaiņas, ko izdarīja $1;\natjaunoju versiju, ko saglabāja $2.",
        "sessionfailure-title": "sesijas kļūda",
        "sessionfailure": "Ir radusies problēma ar sesijas autentifikāciju;\nšī darbība ir atcelta, lai novērstu lietotājvārda iespējami ļaunprātīgu izmantošanu.\nLūdzu, spied \"''back''\" un atjaunini iepriekšējo lapu. Tad mēģini vēlreiz.",
+       "changecontentmodel-title-label": "Lapas nosaukums",
        "changecontentmodel-reason-label": "Iemesls:",
        "changecontentmodel-submit": "Mainīt",
        "protectlogpage": "Aizsargāšanas reģistrs",
        "contributions-title": "Dalībnieka $1 devums",
        "mycontris": "Devums",
        "anoncontribs": "Devums",
-       "contribsub2": "Lietotājs: $1 ($2)",
+       "contribsub2": "{{GENDER:$3|Dalībnieks|Dalībniece}}: $1 ($2)",
        "nocontribs": "Netika atrastas izmaiņas, kas atbilstu šiem kritērijiem.",
        "uctop": "(pēdējā izmaiņa)",
        "month": "No mēneša (un senāki):",
        "sp-contributions-newbies": "Rādīt jauno lietotāju devumu",
        "sp-contributions-newbies-sub": "Jaunie lietotāji",
        "sp-contributions-blocklog": "Bloķēšanas reģistrs",
-       "sp-contributions-deleted": "dzēstais dalībnieka devums",
+       "sp-contributions-deleted": "dzēstais {{GENDER:$1|dalībnieka|dalībnieces}} devums",
        "sp-contributions-uploads": "augšupielādes",
        "sp-contributions-logs": "reģistri",
        "sp-contributions-talk": "diskusija",
        "whatlinkshere-next": "{{PLURAL:$1|nākamos $1|nākamo|nākamos $1}}",
        "whatlinkshere-links": "← saites",
        "whatlinkshere-hideredirs": "$1 pāradresācijas",
-       "whatlinkshere-hidetrans": "$1 lapas, kurās šī lapa izmantota kā veidne",
+       "whatlinkshere-hidetrans": "$1 iekļāvumi",
        "whatlinkshere-hidelinks": "$1 saites",
        "whatlinkshere-hideimages": "$1 failu saites",
        "whatlinkshere-filters": "Filtri",
        "recreate": "Izveidot no jauna",
        "confirm_purge_button": "Labi",
        "confirm-purge-top": "Iztīrīt šīs lapas kešu (''cache'')?",
+       "confirm-purge-bottom": "Lapas atjaunināšana iztīra kešatmiņu un liek parādīt lapas jaunāko versiju.",
        "confirm-watch-button": "Labi",
        "confirm-watch-top": "Pievienot šo lapu uzraugāmo lapu sarakstam?",
        "confirm-unwatch-button": "Labi",
        "special-characters-group-gujarati": "Gudžarati",
        "mw-widgets-dateinput-no-date": "Nav izvēlēts datums",
        "mw-widgets-titleinput-description-new-page": "lapa vēl nepastāv",
-       "api-error-blacklisted": "Lūdzu, izvēlieties citu, aprakstošu nosaukumu!",
        "authmanager-email-label": "E-pasts",
        "authmanager-email-help": "E-pasta adrese",
        "authmanager-realname-label": "Tavs īstais vārds",
index 0999c3e..153ab06 100644 (file)
@@ -21,7 +21,8 @@
                        "飞舞回堂前",
                        "Macofe",
                        "Bowleerin",
-                       "SolidBlock"
+                       "SolidBlock",
+                       "Suchichi02"
                ]
        },
        "tog-underline": "鏈墊線:",
        "editthispage": "纂",
        "create-this-page": "立",
        "delete": "刪",
-       "deletethispage": "刪",
+       "deletethispage": "刪是頁",
        "undeletethispage": "反刪此頁",
        "undelete_short": "還$1已刪",
        "viewdeleted_short": "察$1已刪",
        "edit-conflict": "纂突。",
        "edit-no-change": "爾之纂已略,由字無改也。",
        "postedit-confirmation-created": "其頁已建。",
-       "postedit-confirmation-saved": "汝之纂已成",
+       "postedit-confirmation-saved": "汝之纂已成",
        "edit-already-exists": "不建新頁。\n已存也。",
        "defaultmessagetext": "慣話文",
        "content-model-text": "純文本",
        "rcshowhidemine": "$1吾纂",
        "rcshowhidemine-show": "示",
        "rcshowhidemine-hide": "藏",
+       "rcshowhidecategorization-show": "示",
+       "rcshowhidecategorization-hide": "藏",
        "rclinks": "$2日內$1近易。<br />$3",
        "diff": "辨",
        "hist": "誌",
        "rc_categories_any": "任",
        "rc-change-size-new": "既纂,本文有$1字節",
        "newsectionsummary": "/* $1 */ 新節",
-       "rc-enhanced-expand": "示細(要 JavaScript)",
+       "rc-enhanced-expand": "示細",
        "rc-enhanced-hide": "藏細",
        "recentchangeslinked": "援引",
        "recentchangeslinked-feed": "援引",
        "contributions": "功績",
        "contributions-title": "$1之功績",
        "mycontris": "吾績",
+       "anoncontribs": "績",
        "contribsub2": "$1勛($2)",
        "nocontribs": "無勛及也。",
        "uctop": "(至頂)",
        "whatlinkshere-prev": "前$1",
        "whatlinkshere-next": "次$1",
        "whatlinkshere-links": "←佐",
-       "whatlinkshere-hideredirs": "$1",
+       "whatlinkshere-hideredirs": "$1",
        "whatlinkshere-hidetrans": "$1含",
        "whatlinkshere-hidelinks": "$1佐",
        "whatlinkshere-hideimages": "$1檔佐",
        "intentionallyblankpage": "此頁為白也,試速之用",
        "external_image_whitelist": " #同留<pre>\n#下(中之//)乃正表式\n#乃外(連)圖配之\n#配乃成像,非配則成連\n#有 # 之為注\n#無為大小之異也\n\n#入正表式。同留</pre>",
        "tag-filter": "[[Special:Tags|標]] 之濾:",
-       "tag-list-wrapper": "([[Special:Tags|簽]]: $2)",
+       "tag-list-wrapper": "([[Special:Tags|$1簽]]: $2)",
        "tags-title": "標",
        "tags-tag": "標名",
        "tags-source-header": "源",
        "sqlite-no-fts": "$1 不含全文之尋",
        "revdelete-restricted": "應限至有秩",
        "revdelete-unrestricted": "除限自有秩",
+       "logentry-newusers-create": "簿$1已{{GENDER:$2|增}}。",
        "rightsnone": "(凡)",
        "revdelete-summary": "摘",
        "searchsuggest-search": "尋",
index f7332f9..595fdf5 100644 (file)
@@ -22,7 +22,8 @@
                        "सरोज कुमार ढकाल",
                        "Bijay chaurasia",
                        "Tulsi Bhagat",
-                       "Macofe"
+                       "Macofe",
+                       "राम प्रसाद जोशी"
                ]
        },
        "tog-underline": "लिङ्कके रेखाङ्कित करी:",
        "passwordreset-emailtext-user": "प्रयोक्ता $1 {{अन्तर्जाल}} पर अहाँक खाता विवरणक {{SITENAME}} लेल फेरसँ ($4) आग्रह केने छथि। ई प्रयोक्ता {{PLURAL:$3|खाता अछि|खाता सभ अछि}} ऐ ई-पत्र संकेतसँ जुड़ल: $2\n{{PLURAL:$3| ई अस्थायी कूटशब्द|ई सभ अस्थायी कूटशब्द}} खतम भऽ जाएत {{PLURAL:$5|एक दिन|$5 दिन}} मे।\nअहाँ सम्प्रवेश करू आ एकटा नव कूटशब्द आब चुनू। जँ कियो दोसर ई आग्रह केने छथि, वा जँ अहाँकेँ अपन मूल कूटशब्द मोन पड़ि गेल अछि, आ अहाँ आब ओइ कूटशब्दकेँ नै बदलऽ चाहै छी, अहाँ ऐ संदेशकेँ बिसरि सकै छी आ अपन पुरान कूटशब्दक प्रयोग जारी राखि सकै छी।",
        "passwordreset-emailelement": "प्रयोक्ता: \n$1\n\nअस्थायी कूटशब्द: \n$2",
        "passwordreset-emailsentemail": "एकटा ई-पत्र मोन पाड़बा लेल पठाओल गेल अछि।",
-       "passwordreset-emailsent-capture": "एकटा स्मरण ई-पत्र पठाएल गेल अछि, जे नीचाँ देखाएल अछि।",
-       "passwordreset-emailerror-capture": "एकटा स्मरण ई-पत्र बनाएल गेल अछि, जे नीचाँ देखाएल अछि, मुदा प्र्योक्ताकेँ एकरा पठेबाक प्रयास विफल भेल: $1",
        "changeemail": "ई-मेल पता परिवर्तित करी",
        "changeemail-header": "अपन ईमेल पता परिवर्तन हेतु एकरा पुरा करी। यदि अहाँ अपन वर्तमान ईमेल पता हटाबैलेल चाहैत छी, तँ एकरा खाली छोडि दी आ एकरा भेजी।",
        "changeemail-no-info": "अहाँक ई पन्नाक सोझे प्रयोग करबालेल सम्प्रवेशित हुअए पडत।",
        "undo-nochange": "ऐना लगया की ई सम्पादन कें पहील से पूर्ववत करई देन अछि।",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|वार्ता]])द्वारा कएल अन्तर $1 के पूर्ववत कएलक",
        "undo-summary-username-hidden": "नुकाएल गेल प्रयोक्ताद्वारा केल गेल परिवर्तन $1 के पूर्ववत केल गेल",
-       "cantcreateaccounttitle": "खाता नै बना सकल",
        "cantcreateaccount-text": "(<strong>$1</strong>) अनिकेत पतासँ खाता निर्माण प्रतिबन्धित कएल गेल [[User:$3|$3]]।\n$3 द्वारा देल कारण अछि ''$2''",
        "cantcreateaccount-range-text": "<strong>$1</strong> के श्रेणी में आबई वाला आई॰पी पता सऽ, जएमें आहाँ कें आई॰पी पता (<strong>$4</strong>) शामिल अछि, नया खाता के रचना [[User:$3|$3]] द्वारा अवरोधित केल गेल अछि। \n\n$3 द्वारा देल गेल कारण अछि: \"$2\"",
        "viewpagelogs": "ई पन्नाक वृत्तलेखसभ देखी",
        "sqlite-has-fts": "$1 पूर्ण-पाठ खोज सहायता युक्त",
        "sqlite-no-fts": "$1 बिन पूर्ण-पाठ खोज सहायताक",
        "logentry-delete-delete": "$1 पृष्ठ $3 {{GENDER:$2|मेटौलक}}",
-       "logentry-delete-restore": "$1 {{लिंग:$2|restored}} page $3",
-       "logentry-delete-event": "$1 {{लिंग:$2|changed}} एकर दृश्य{{PLURAL:$5| एकटा वृत्तलेख|$5 वृत्तलेख}}  $3: $4 केँ",
-       "logentry-delete-revision": "$1 {{लिंग:$2|changed}} एकर दृश्य{{PLURAL:$5| एकटा संशोधन|$5 संशोधन}}  पन्ना $3: $4 पर",
-       "logentry-delete-event-legacy": "$1 {{लिंग:$2|changed}}  $3 पर वृत्तलेख दृश्य",
-       "logentry-delete-revision-legacy": "$1 {{लिंग:$2|changed}}  $3 पर वृत्तलेख संशोधन",
-       "logentry-suppress-delete": "$1 {{लिंग:$2|दबाएल}} page $3",
-       "logentry-suppress-event": "$1 चोरिसँ {{लिंग:$2|changed}} एकर दृश्य{{PLURAL:$5| एकटा वृत्तलेख|$5 वृत्तलेख}}  $3: $4 पर",
-       "logentry-suppress-revision": "$1 चोरिसँ {{लिंग:$2|changed}} एकर दृश्य{{PLURAL:$5| एकटा संशोधन|$5 संशोधन}}  $3: $4 पर",
-       "logentry-suppress-event-legacy": "$1 नुका कऽ {{लिंग:$2|changed}}  $3 पर वृत्तलेख दृश्य",
-       "logentry-suppress-revision-legacy": "$1 नुका कऽ {{लिंग:$2|changed}}  $3 पर संशोधन दृश्य",
+       "logentry-delete-restore": "$1 {{GENDER:$2|restored}} page $3",
+       "logentry-delete-event": "$1 {{GENDER:$2|changed}} एकर दृश्य{{PLURAL:$5| एकटा वृत्तलेख|$5 वृत्तलेख}}  $3: $4 केँ",
+       "logentry-delete-revision": "$1 {{GENDER:$2|परिवर्तन कियल गैल}} एकर दृश्य{{PLURAL:$5| एकटा संशोधन|$5 संशोधन}}  पन्ना $3: $4 पर",
+       "logentry-delete-event-legacy": "$1 {{GENDER:$2|changed}}  $3 पर वृत्तलेख दृश्य",
+       "logentry-delete-revision-legacy": "$1 {{GENDER:$2|changed}}  $3 पर वृत्तलेख संशोधन",
+       "logentry-suppress-delete": "$1 {{GENDER:$2|दबाएल}} page $3",
+       "logentry-suppress-event": "$1 चोरिसँ {{GENDER:$2|परिवर्तन कियल गैल}} एकर दृश्य{{PLURAL:$5| एकटा वृत्तलेख|$5 वृत्तलेख}}  $3: $4 पर",
+       "logentry-suppress-revision": "$1 चोरिसँ {{GENDER:$2|changed}} एकर दृश्य{{PLURAL:$5| एकटा संशोधन|$5 संशोधन}}  $3: $4 पर",
+       "logentry-suppress-event-legacy": "$1 नुका क {{GENDER:$2|परिवर्तन}}  $3 पर वृत्तलेख दृश्य",
+       "logentry-suppress-revision-legacy": "$1 नुका कऽ {{GENDER:$2|changed}}  $3 पर संशोधन दृश्य",
        "revdelete-content-hid": "सामिग्री नुकाएल",
        "revdelete-summary-hid": "नुकाएल सारांश सम्पादन",
        "revdelete-uname-hid": "प्रयोक्तानाम नुकाएल",
        "logentry-import-interwiki": "$1 {{GENDER:$2|आयात केल गेल}} $3 कोनो और विकि सँ",
        "logentry-merge-merge": "$1 {{GENDER:$2|विलय केल गेल}} $3 के $4 में (संशोधन $5 धरि)",
        "logentry-move-move": "$1 हटाएल पन्ना $3 सँ $4",
-       "logentry-move-move-noredirect": "$1 {{लिंग:$2|हटाएल}} पन्ना $3 सँ $4 घुमौआकेँ बिना छोड़ने",
-       "logentry-move-move_redir": "$1 {{लिंग:$2|हटाएल}} पन्ना $3 सँ $4 घुमौआक अतिरिक्त",
-       "logentry-move-move_redir-noredirect": "$1 {{लिंग:$2|हटाएल}} पन्ना $3 सँ $4 घुमौआक अतितिक्त घुमौआकेँ बिना छोड़ने",
+       "logentry-move-move-noredirect": "$1 {{GENDER:$2|हटाएल}} पन्ना $3 सँ $4 घुमौआकेँ बिना छोड़ने",
+       "logentry-move-move_redir": "$1 {{GENDER:$2|हटाएल}} पन्ना $3 सँ $4 घुमौआक अतिरिक्त",
+       "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|हटाएल}} पन्ना $3 सँ $4 घुमौआक अतितिक्त घुमौआकेँ बिना छोड़ने",
        "logentry-patrol-patrol": "$1 {{GENDER:$2|चिन्हित}} संशोधन $4 $3 पन्नाक निरीक्षित",
        "logentry-patrol-patrol-auto": "$1 स्वतः {{GENDER:$2|चिन्हित}} संशोधन $4 $3 पन्नाक निरीक्षित",
-       "logentry-newusers-newusers": "$1 {{लिंग:$2|बनाएल}} एकटा प्रयोक्ता खाता",
+       "logentry-newusers-newusers": "$1 {{GENDER:$2|बनाएल}} एकटा प्रयोक्ता खाता",
        "logentry-newusers-create": "प्रयोगकर्ता खाता $1 {{GENDER:$2|बनाएल}} गेल",
-       "logentry-newusers-create2": "$1 {{लिंग:$2|बनाएल}} {{लिंग:$4|एकटा प्रयोक्ता खाता}} $3",
+       "logentry-newusers-create2": "$1 {{GENDER:$2|बनाएल}} {{GENDER:$4|एकटा प्रयोक्ता खाता}} $3",
        "logentry-newusers-byemail": "$1 द्वारा प्रयोक्ता खाता $3 {{GENDER:$2|बनाओल}} गेल आ कूटशब्द ई-पत्र द्वारा भेजल गेल",
-       "logentry-newusers-autocreate": "खाता $1 छल {{लिंग:$2|बनाएल}} स्वतः",
+       "logentry-newusers-autocreate": "खाता $1 छल {{GENDER:$2|बनाएल}} स्वतः",
        "logentry-upload-upload": "$1 {{GENDER:$2|ए}} $3 अपलोड केलक",
        "log-name-tag": "ट्याग लग",
        "rightsnone": "(कोनो नै)",
index b180b5f..2cc972a 100644 (file)
        "passwordreset-emailtitle": "Detil akun nang {{SITENAME}}",
        "passwordreset-emailelement": "Jeneng panganggo: \n$1\n\nTembung sandhi sauntara: \n$2",
        "passwordreset-emailsentemail": "Imel nggo nyetel maning tembung sandhi uwis dikirim.",
-       "passwordreset-emailsent-capture": "Imel kanggo nyetel maning tembung sandhi uwis dikirim, kaya sing ditidokna nang ngisor kiye.",
-       "passwordreset-emailerror-capture": "Imel nggo nyetel maning tembung sandhi uwis digawe, kaya sing ditidokna nang ngisor kiye, ningen gole ngirim maring {{GENDER:$2|panganggo}} ora teyeng: $1",
        "changeemail": "Ganti alamat imel",
        "changeemail-header": "Ganti alamat imel-e akun",
        "changeemail-no-info": "Rika kudu mlebu log kanggo ngakses kaca kiye sacara langsung.",
        "subject": "Subyek/judhul:",
        "minoredit": "Kiye suntingan cilik",
        "watchthis": "Awasi kaca kiyé",
-       "savearticle": "Simpen",
+       "savearticle": "Terbitna Kaca",
        "preview": "Pra tayang",
        "showpreview": "Pra tayang",
        "showdiff": "Ndeleng bedané",
        "undo-failure": "Suntingan kiye ora teyeng dibatalna jalaran ana konflik panyuntingan antara.",
        "undo-norev": "Suntingan kiye ora teyeng dibatalna jalaran wis ora ana utawa anu wis dibusek.",
        "undo-summary": "Mbatalna revisi $1 sekang [[Special:Contributions/$2|$2]] ([[User talk:$2|dopokan]])",
-       "cantcreateaccounttitle": "Ora teyeng gawe akun",
        "cantcreateaccount-text": "Ngawe akun sekang alamat IP kiye ('''$1''') wis diblokir nang [[User:$3|$3]].\n\nAlesane miturut $3 yakuwe ''$2''",
        "viewpagelogs": "Deleng log-e kaca kiye",
        "nohistory": "Ora ana sajarah panyuntingan kanggo kaca kiye.",
index e1722ba..94699ba 100644 (file)
        "category-file-count-limited": "Anatin'ity sokajy ity ireo rakitra ireo. ($1 no aseho) {{PLURAL:}}",
        "listingcontinuesabbrev": " manaraka.",
        "index-category": "pejy voasokajy",
-       "noindex-category": "Pejy tsy voasikajy",
+       "noindex-category": "Pejy tsy voatondro",
        "broken-file-category": "Pejy misy rohin-drakitra tapaka",
        "about": "Mombamomba",
        "article": "Votoatin'ny pejy",
        "passwordreset-emailtext-user": "Nisy mpikambana mitondra anarana $1 eo amin'i {{SITENAME}} nangataka fampatsiahivana mikasika ny kaontinao eo amin'i {{SITENAME}} ($4). Manana io adiresy imailaka {{PLURAL:$3|io kaontim-pikambana io|ireo kaontim-pikambana ireo}} :\n\n$2\n\nHitsahatra afaka {{PLURAL:$5|iray|$5}} andro {{PLURAL:$3|io|ireo}} tenimiafina {{PLURAL:$3|io|ireo}}. Mila miditra dien'izao ianao izao ary mifidy tenimiafina vaovao. Raha tsy avy aminao ity hataka ity na efa nahatadidy ny tenimiafinao taloha ianao, ary raha tsy tianao hovaina intsony ilay tenimiafinao, dia azonao tsy raharahiana ity hafatra ity ary mampiasa ny tenimiafinao taloha.",
        "passwordreset-emailelement": "Anaram-pikambana : \n$1\n\nTenimiafina miserana : \n$2",
        "passwordreset-emailsentemail": "Lasa ny mailaka famerenana tenimiafina.",
-       "passwordreset-emailsent-capture": "Lasa ilay mailaka famerenana tenimiafina, izay aseho eo ambany.",
-       "passwordreset-emailerror-capture": "Nosoratana ilay mailaka famerenana tenimiafina, izay aseho eo ambany, fa tsy tafalefa tany amin'ilay mpikambana ilay izy : $1{{GENDER:$2}}",
        "changeemail": "Hanova ny adiresy imailaka",
        "changeemail-header": "Hanova ny adiresy imailak'ilay kaonty",
        "changeemail-no-info": "Mila tafiditra ianao vao avaka mijery ity pejy ity.",
        "undo-nochange": "Hoatry ny efa nofoanana ilay fanovana.",
        "undo-summary": "Niala ny fanovàna $1 nataon'i [[Special:Contributions/$2|$2]] ([[User talk:$2|resaho]])",
        "undo-summary-username-hidden": "Namafa ny famerenana $1 nataom-pikambana afenina",
-       "cantcreateaccounttitle": "Tsy afaka manokatra kaonty ianao.",
        "cantcreateaccount-text": "Voasakan'i [[User:$3|$3]] ny fanokafana kaonty avy amin'ity adiresy IP (<b>$1</b>)\n\n''$2'' ny antony.",
        "cantcreateaccount-range-text": "Nosakanan'i [[User:$3|$3]] ny fanokafana kaonty avy amin'ny adiresy IP ao amin'ny elanelana <strong>$1</strong> izay ahitana ny adiresy IP-nao (<strong>$4</strong>).",
        "viewpagelogs": "Hijery ny fanovan'ity pejy ity",
index 6780a3f..868df89 100644 (file)
        "passwordreset-emailtext-user": "Sasaurang (mungkin Sanak, dari alamaik IP $1) mamintak parubahan kato sandi untuak {{SITENAME}} ($4).\n{{PLURAL:$3|Akun}} barikuik takaik jo alamaik surel ko:\n\n$2\n\n{{PLURAL:$3|Sandi samantaro}} barikuik akan habih masonyo dalam {{PLURAL:$5|$5 ari}}.\nSanak harus masuak dan mamiliah sandi baru. Jikok urang lain mambuek pamintaan ko atau jikok Sanak ingek sandi awal dan indak nio maubahnyo, Sanak dapek mangacuahkan pasan ko dan taruih manggunoan kato sandi lamo.",
        "passwordreset-emailelement": "Namo pangguno: \n$1\n\nSandi samantaro: \n$2",
        "passwordreset-emailsentemail": "Surel parubahan kato sandi alah dikirim.",
-       "passwordreset-emailsent-capture": "Surel parubahan kato sandi alah dikirim, nan nampak di bawah ko.",
-       "passwordreset-emailerror-capture": "Surel parubahan kato sandi nan ditampilan di bawah, alah dibuek, tapi pangirimannyo ka {{GENDER:$2|pangguno}} gagal: $1",
        "changeemail": "Tuka alamaik surel.",
        "changeemail-header": "Ganti alamaik surel.",
        "changeemail-no-info": "Sanak harus masuak log untuak mangakses laman ko.",
        "minoredit": "Suntiangan ketek",
        "watchthis": "Pantau laman ko",
        "savearticle": "Simpan",
+       "publishpage": "Tabikan laman",
+       "publishchanges": "Tabikan parubahan",
        "preview": "Caliak",
        "showpreview": "Pratonton",
        "showdiff": "Parubahan",
        "undo-failure": "Suntiangan ko indak dapek dibatalan dek konflik panyuntiangan antaro.",
        "undo-norev": "Suntiangan ko indak dapek dibatalan dek laman indak ditamukan atau lah dihapuih.",
        "undo-summary": "Mambatalan revisi $1 oleh [[Special:Contributions/$2|$2]] ([[User talk:$2|maota]])",
-       "cantcreateaccounttitle": "Indak dapek mambuek akun",
        "cantcreateaccount-text": "Mambuek akun dari alamat IP ko ('''$1''') alah diblok jo [[User:$3|$3]].\n\nAlasan nan diagiah jo $3 adolah ''$2''",
        "viewpagelogs": "Caliak log untuak laman ko",
        "nohistory": "Indak ado sajarah panyuntiangan untuak laman ko",
index 19df826..87cc76a 100644 (file)
@@ -18,7 +18,8 @@
                        "Milicevic01",
                        "Macofe",
                        "Nemo bis",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Kaldari"
                ]
        },
        "tog-underline": "Потцртување на врски:",
        "hidden-category-category": "Скриени категории",
        "category-subcat-count": "{{PLURAL:$2|Оваа категорија ја содржи само следнава поткатегорија.|Оваа категорија {{PLURAL:$1|ја содржи следнава поткатегорија|ги содржи следниве $1 поткатегории}} од вкупно $2.}}",
        "category-subcat-count-limited": "Оваа категорија {{PLURAL:$1|ја содржи следнава поткатегорија|ги содржи следниве $1 поткатегории}}.",
-       "category-article-count": "{{#ifeq:$2|Оваа категорија содржи само една страница.|{{PLURAL:$1|Прикажана е една|Прикажани се $1}} од вкупно $2 страници во категоријата.}}",
+       "category-article-count": "{{PLURAL:$2|Оваа категорија содржи само една страница.|{{PLURAL:$1|Прикажана е една|Прикажани се $1}} од вкупно $2 страници во категоријата.}}",
        "category-article-count-limited": "{{PLURAL:$1|Следната страница е|Следните $1 страници се}} во оваа категорија.",
-       "category-file-count": "{{#ifeq:$2|Оваа категорија содржи само една податотека.|{{PLURAL:$1|Прикажана е една|Прикажани се $1}} од вкупно $2 податотеки во категоријата.}}",
+       "category-file-count": "{{PLURAL:$2|Оваа категорија содржи само една податотека.|{{PLURAL:$1|Прикажана е една|Прикажани се $1}} од вкупно $2 податотеки во категоријата.}}",
        "category-file-count-limited": "{{PLURAL:$1|Следнава податотека е|Следниве $1 податотеки се}} во оваа категорија.",
        "listingcontinuesabbrev": "продолжува",
        "index-category": "Индексирани страници",
        "tagline": "Од {{SITENAME}}",
        "help": "Помош",
        "search": "Пребарај",
+       "search-ignored-headings": " #<!-- не менувајте ништо во овој ред --> <pre>\n# Заглавија што ќе се занемарат при пребарувањето.\n# Измените во ова ќе стапат на сила штом ќе се индексира страницата со заглавието.\n# Можете да наметнете преиндексирање на страницата ако извршите празно уредување.\n# Синтаксата е следнава:\n#   * Сето она што од знакот „#“ до крајот на редот е коментар\n#   * Секој непразен ред е точниот наслов што треба да се занемари, разликувајќи големи од мали букви и сето останато\nНаводи\nНадворешни врски\nПоврзано\n #</pre> <!-- не менувајте ништо во овој ред -->",
        "searchbutton": "Пребарај",
        "go": "Дај",
        "searcharticle": "Дај",
        "badarticleerror": "Ова дејство не може да се спроведе на оваа страница.",
        "cannotdelete": "Страницата или податотеката „$1“ не можеше да се избрише.\nМожеби некој друг веќе ја избришал.",
        "cannotdelete-title": "Не можам да ја избришам страницата „$1“",
-       "delete-hook-aborted": "Ð\91Ñ\80иÑ\88еÑ\9aеÑ\82о Ðµ Ð¿Ñ\80екинаÑ\82о Ñ\81о ÐºÑ\83ка.\nНе е дадено никакво образложение.",
+       "delete-hook-aborted": "Ð\91Ñ\80иÑ\88еÑ\9aеÑ\82о Ðµ Ð¿Ñ\80екинаÑ\82о Ñ\81о Ð¿Ñ\80еÑ\81Ñ\80еÑ\82ник.\nНе е дадено никакво образложение.",
        "no-null-revision": "Не можев да направам нова ништовна преработка на страницата „$1“",
        "badtitle": "Неисправен наслов",
        "badtitletext": "Бараниот наслов е грешен, празен или неисправно поврзан меѓујазичен или меѓупроектен наслов. \nМоже да содржи недопуштени знаци.",
        "title-invalid-magic-tilde": "Побараниот наслов содржи низа неважечки тилди (<nowiki>~~~</nowiki>).",
        "title-invalid-too-long": "Бараниот наслов е предолг. Не смее да биде поголем од  $1 {{PLURAL:$1|бајт|бајти}} шифриран според UTF-8.",
        "title-invalid-leading-colon": "Бараниот наслов содржи неважечки две точки на почетокот.",
-       "perfcached": "Следните податоци се меѓускладирани и може да не се тековни. Во меѓускладот {{PLURAL:$1|е достапен највеќе еден резултат|се достапни највеќе $1 резултати}}.",
-       "perfcachedts": "Следните податоци се меѓускладирани, последен пат подновени на $1. Во меѓускладот {{PLURAL:$4|е достапен највеќе еден резултат|се достапни највеќе $4 резултати}}.",
+       "perfcached": "Следните податоци се меѓускладирани и може да не се тековни. Во меѓускладот {{PLURAL:$1|е достапен највеќе еден запис|се достапни највеќе $1 записи}}.",
+       "perfcachedts": "Следните податоци се меѓускладирани, последен пат подновени на $1. Во меѓускладот {{PLURAL:$4|е достапен највеќе еден запис|се достапни највеќе $4 записи}}.",
        "querypage-no-updates": "Подновите на оваа страница моментално се оневозможени.\nПодатоците овде во моментов нема да се подновуваат.",
        "viewsource": "Преглед",
        "viewsource-title": "Преглед на кодот на $1",
        "yourpasswordagain": "Повторете ја лозинката:",
        "createacct-yourpasswordagain": "Потврда на лозинката",
        "createacct-yourpasswordagain-ph": "Повторно внесете ја лозинката",
-       "remembermypassword": "Запомни ме на овој сметач (највеќе $1 {{PLURAL:$1|ден|дена}})",
        "userlogin-remembermypassword": "Запомни ме",
        "userlogin-signwithsecure": "Користи безбеден опслужувач",
+       "cannotlogin-title": "Не можам да ве најавам",
+       "cannotlogin-text": "Најавата не е возможна.",
        "cannotloginnow-title": "Во моментов не можам да ве најавм",
        "cannotloginnow-text": "Не можам да ве најавам кога се користи $1.",
+       "cannotcreateaccount-title": "Не можам да создавам сметки",
+       "cannotcreateaccount-text": "Непосредното создавање на сметки не е овозможено на ова вики.",
        "yourdomainname": "Вашиот домен:",
        "password-change-forbidden": "Не можете да ја менувате лозинката на ова вики.",
        "externaldberror": "Настана грешка при надворешното најавување на базата или пак немате дозвола да ја подновите вашата надворешна сметка.",
        "passwordreset-emailelement": "Корисничко име: \n$1\n\nПривремена лозинка: \n$2",
        "passwordreset-emailsentemail": "Ако ова е регистрираната е-пошта поврзана со вашата сметка, тогаш ќе ви биде испратено писмо за задавање на нова лозинка.",
        "passwordreset-emailsentusername": "Ако има соодветна регистрирана е-пошта поврзана со ова корисничко име, тогаш ќе ви биде испратена порака за промена на лозинката.",
-       "passwordreset-emailsent-capture": "Испратено е писмо за измена на лозинката (прикажано подолу).",
-       "passwordreset-emailerror-capture": "Создадено е писмо за измена на лозинката (прикажано подолу), но не успеав да го испратам на {{GENDER:$2|корисникот}}: $1",
        "passwordreset-emailsent-capture2": "{{PLURAL:$1|Е-поштата за задавање на нова лозинка|Е-поштата за задавање на нови лозинки}} е испратена. Подолу е {{PLURAL:$1|е прикажано корисничкото име и лозинката|прикажан список на кориснички имиња и лозинки}}.",
        "passwordreset-emailerror-capture2": "Испраќањето е-пошта на {{GENDER:$2|корисникот}} не успеа: $1 Подолу е {{PLURAL:$3|прикажано корисничкото име и лозинката|прикажан список на кориснички имиња и лозинки}}.",
        "passwordreset-nocaller": "Мора да се укаже повикувач",
        "passwordreset-nodata": "Немате укажано ни корисничко име, ни е-пошта.",
        "changeemail": "Смени или отстрани е-пошта",
        "changeemail-header": "Пополнете го образецов за да ја смените е-поштата. Ако сакате да ја отстраните адресата од вашата сметка, оставете го празно полето за нова е-пошта.",
-       "changeemail-passwordrequired": "Ќе треба да ја внесете лозинката за да ја потврдите измената.",
        "changeemail-no-info": "Мора да бидете најавени ако сакате да имате директен пристап до оваа страница.",
        "changeemail-oldemail": "Тековна е-пошта:",
        "changeemail-newemail": "Нова е-пошта:",
        "savearticle": "Зачувај",
        "savechanges": "Зачувај промени",
        "publishpage": "Објави ја страницата",
-       "publishchanges": "Ð\9eбÑ\98ави Ð¿Ñ\80омени",
+       "publishchanges": "Ð\9eбÑ\98ави Ð³Ð¸ Ð¿Ñ\80омениÑ\82е",
        "preview": "Преглед",
        "showpreview": "Преглед",
        "showdiff": "Прикажи промени",
        "moveddeleted-notice": "Оваа страница била претходно бришена.\nДневникот на бришења и преместувања за оваа страница е прикажан подолу за ваше дополнително информирање.",
        "moveddeleted-notice-recent": "За жал, страницава беше неодамна избришана (во последниве 24 часа).\nПодолу можете да го погледате дневникот на бришење и преместување.",
        "log-fulllog": "Преглед на целиот дневник",
-       "edit-hook-aborted": "УÑ\80едÑ\83ваÑ\9aеÑ\82о Ðµ Ð¿Ñ\80екинаÑ\82о Ñ\81о ÐºÑ\83ка.\nНе е дадено никакво образложение.",
+       "edit-hook-aborted": "УÑ\80едÑ\83ваÑ\9aеÑ\82о Ðµ Ð¿Ñ\80екинаÑ\82о Ñ\81о Ð¿Ñ\80еÑ\81Ñ\80еÑ\82ник.\nНе е дадено никакво образложение.",
        "edit-gone-missing": "Не можев да ја подновам страницата.\nВеројатно е избришана.",
        "edit-conflict": "Спротиставеност во уредувањето.",
        "edit-no-change": "Вашите уредувања беа игнорирани, бидејќи не се направени промени врз текстот.",
        "content-model-css": "CSS",
        "content-json-empty-object": "Празен објект",
        "content-json-empty-array": "Празна низа",
+       "deprecated-self-close-category": "Страници со неважечки самозатворени HTML-ознаки",
+       "deprecated-self-close-category-desc": "Страницава содржи неважечки самозатворени HTML-ознаки, како што се <code>&lt;b/></code> или <code>&lt;span/></code>. Нивното поведение наскоро ќе биде сменето, за да бидат во склад со определбите на HTML5. Ова значи дека се застарени и не треба да се употребуваат во викитекст.",
        "duplicate-args-warning": "<strong>Предупредување:</strong> [[:$1]] го повикува [[:$2]] со повеќе од една вредност за параметарот „$3“. Ќе се употреби само последната вредност.",
        "duplicate-args-category": "Страници што користат дуплирани аргументи во повикувања на шаблони",
        "duplicate-args-category-desc": "Страницава содржи повикувања на шаблони кои се дупликати на аргументи, како што се <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> или <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Се чини дека измената (уредувањето) е веќе вратена.",
        "undo-summary": "Откажано уредувањето $1 на уредникот [[Special:Contribs/$2|$2]] ([[User talk:$2|разговор]])",
        "undo-summary-username-hidden": "Поништи ја преработката $1 на скриен корисник",
-       "cantcreateaccounttitle": "Не може да се создаде корисничка сметка",
        "cantcreateaccount-text": "Создавањето на корисничка сметка од оваа IP-адреса ('''$1''') е блокирано од страна на [[User:$3|$3]].\n\nОбразложението дадено од страна на $3 е ''$2''",
        "cantcreateaccount-range-text": "Создавањето на сметки од IP-адреси во опсегот <strong>$1</strong> каде спаѓа вашата IP-адреса (<strong>$4</strong>) е блокирано од корисникот [[User:$3|$3]].\n\n$3 ја наведе следнава причина: <em>$2</em>",
        "viewpagelogs": "Преглед на дневници за оваа страница",
        "difference-title-multipage": "$1 и $2: Разлика помеѓу страниците",
        "difference-multipage": "(Разлики помеѓу страници)",
        "lineno": "Ред $1:",
-       "compareselectedversions": "СпоÑ\80еди Ð¸Ð·Ð±Ñ\80ани преработки",
-       "showhideselectedversions": "Ð\9fÑ\80икажи/Ñ\81кÑ\80иÑ\98 Ð¸Ð·Ð±Ñ\80ани преработки",
+       "compareselectedversions": "СпоÑ\80еди Ð³Ð¸ Ð¸Ð·Ð±Ñ\80аниÑ\82е преработки",
+       "showhideselectedversions": "Ð\9fÑ\80икажи/Ñ\81кÑ\80иÑ\98 Ð³Ð¸ Ð¸Ð·Ð±Ñ\80аниÑ\82е преработки",
        "editundo": "откажи",
        "diff-empty": "(нема разлика)",
        "diff-multi-sameuser": "({{PLURAL:$1|Не е прикажана една меѓувремена преработка|Не се прикажани $1 меѓувремени преработки}} од истиот корисник)",
        "search-nonefound-thiswiki": "Нема резултати што одговараат на бараното на ова мрежно место.",
        "powersearch-legend": "Напредно пребарување",
        "powersearch-ns": "Пребарај во следниве именски простори:",
-       "powersearch-togglelabel": "Ð\9eдбеÑ\80и:",
+       "powersearch-togglelabel": "Ð\98збоÑ\80:",
        "powersearch-toggleall": "Сè",
        "powersearch-togglenone": "Ништо",
        "powersearch-remember": "Запoмни го изборот за идни пребарувања",
        "prefs-skin": "Руво",
        "skin-preview": "Преглед",
        "datedefault": "Небитно",
-       "prefs-labs": "Ð\95кÑ\81пеÑ\80именÑ\82ални можности",
+       "prefs-labs": "Ð\9fÑ\80обни можности",
        "prefs-user-pages": "Кориснички страници",
        "prefs-personal": "Кориснички профил",
        "prefs-rc": "Скорешни промени",
        "right-applychangetags": "Задавање на [[Special:Tags|ознаки]] заедно со направените измени",
        "right-changetags": "Додавате и отстранување на произволни [[Special:Tags|ознаки]] во поединечни преработки и дневнички записи",
        "right-deletechangetags": "Бришење [[Special:Tags|ознаки]] од базата",
-       "grant-generic": "Ð\93Ñ\80Ñ\83па права „$1“",
+       "grant-generic": "Ð\97биÑ\80 права „$1“",
        "grant-group-page-interaction": "Опходување со страници",
        "grant-group-file-interaction": "Опходување со слики и снимки",
        "grant-group-watchlist-interaction": "Опходување со набљудуваните",
        "grant-group-high-volume": "Вршење на активности од голем обем",
        "grant-group-customization": "Прилагодувања и поставки",
        "grant-group-administration": "Вршење на административни дејства",
+       "grant-group-private-information": "Пристап до лични податоци за вас",
        "grant-group-other": "Разни активности",
        "grant-blockusers": "Блокирање и одблокирање корисници",
        "grant-createaccount": "Правење сметки",
        "grant-highvolume": "Високообемно уредување",
        "grant-oversight": "Скривање на корисници и преработки",
        "grant-patrol": "Патрола на измени во страници",
+       "grant-privateinfo": "Пристап до лични информации",
        "grant-protect": "Заштита на незаштитени страници",
        "grant-rollback": "Отповикување на измени во страници",
        "grant-sendemail": "Испраќање на е-пошта до други корисници",
        "action-applychangetags": "ставање на ознаки заедно со напревените промени",
        "action-changetags": "додавање и отстранување на произволни ознаки во поединечни преработки и дневнички записи",
        "action-deletechangetags": "бришење ознаки од базата",
+       "action-purge": "превчитување на оваа страница",
        "nchanges": "$1 {{PLURAL:$1|промена|промени}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|од последната посаета}}",
        "enhancedrc-history": "историја",
        "filename-tooshort": "Името на податотеката е прекратко.",
        "filetype-banned": "Овој тип на податотека е забранет.",
        "verification-error": "Оваа податотека не ја помина потврдата успешно.",
-       "hookaborted": "Ð\98змениÑ\82е Ñ\88Ñ\82о Ñ\81акаÑ\82е Ð´Ð° Ð³Ð¸ Ð½Ð°Ð¿Ñ\80авиÑ\82е Ñ\81е Ð¾Ñ\82кажани Ð¾Ð´ ÐºÑ\83ка на додатокот.",
+       "hookaborted": "Ð\98змениÑ\82е Ñ\88Ñ\82о Ñ\81акаÑ\82е Ð´Ð° Ð³Ð¸ Ð½Ð°Ð¿Ñ\80авиÑ\82е Ñ\81е Ð¾Ñ\82кажани Ð¾Ð´ Ð¿Ñ\80еÑ\81Ñ\80еÑ\82ник на додатокот.",
        "illegal-filename": "Такво име за податотеката на е дозволено.",
        "overwrite": "Не е дозволено запишување врз постоечка податотека.",
        "unknown-error": "Се појави непозната грешка.",
        "file-thumbnail-no": "Името на податотеката почнува со <strong>$1</strong>.\nИзгледа дека е слика со намалена големина ''(мини, thumbnail)''.\nАко ја имате оваа слика во изворна големина, подигнете ја неја. Во спротивно сменете го името на податотеката.",
        "fileexists-forbidden": "Податотека со тоа име веќе постои и не може да биде заменета.\nАко и понатаму сакате да ја подигнете вашата податотеката, ве молиме вратете се назад и подигнете ја под друго име. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Во заедничкото складиште веќе постои податотека со ова име.\nАко и понатаму сакате да ја подигнете, вратете се и подигнете ја под друго име. \n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Подигањето е истоветен дупликат на тековната верзија на <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Подигањето е истоветен дупликат на {{PLURAL:$2|постара верзија|постари верзии}} на <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Оваа податотека е дупликат со {{PLURAL:$1|следнава податотека|следниве податотеки}}:",
        "file-deleted-duplicate": "Податотека индентична со податотеката ([[:$1]]) претходно била избришана. Треба да проверите во дневникот на бришења за оваа податотека пред повторно да ја подигнете.",
        "file-deleted-duplicate-notitle": "Податотека сосем иста како оваа била претходно избришана, а насловот бил притаен.\nТреба да побарате од некој што има можност да гледа податоци за притаени податотеки да ја разгледа ситуацијата пред да продолжите со преподигањето.",
        "backend-fail-connect": "Не можев да се поврзам со складишната основа „$1“.",
        "backend-fail-internal": "Се појави непозната грешка во складишната основа „$1“.",
        "backend-fail-contenttype": "Не можев да утврдам каква содржина има податотеката што треба да ја складирам во „$1“.",
-       "backend-fail-batchsize": "Складишната основа доби блок од $1 податочна {{PLURAL:$1|операција|операции}}, а ограничувањето е $2 {{PLURAL:$2|операција|операции}}.",
+       "backend-fail-batchsize": "Складишната основа доби блок од $1 {{PLURAL:$1|податотечна постапка|податотечни постапки}}, а ограничувањето е $2 {{PLURAL:$2|постапка|постапки}}.",
        "backend-fail-usable": "Не можев да ја прочитам или запишам податотеката „$1“ бидејќи немате доволно дозволи или поради тоа што недостасуваат именици/содржатели.",
        "filejournal-fail-dbconnect": "Не можев да се поврзам со дневничката база за складишната основа „$1“.",
        "filejournal-fail-dbquery": "Не можев да ја подновам дневничката база за складишната основа „$1“.",
        "uploadstash-errclear": "Чистењето на податотеките не успеа.",
        "uploadstash-refresh": "Превчитај го списокот на податотеки",
        "uploadstash-thumbnail": "погл. минијатура",
+       "uploadstash-exception": "Не можев да го складирам подигнатото во складиштето ($1): „$2“.",
        "invalid-chunk-offset": "Неважечка појдовна точка",
        "img-auth-accessdenied": "Оневозможен пристап",
        "img-auth-nopathinfo": "Недостасува PATH_INFO.\nВашиот опслужувач не е нагоден за да ја предаде оваа информација.\nМожеби се заснова на CGI, и така не подржува img_auth.\nПогл. https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "filerevert-submit": "Врати",
        "filerevert-success": "'''[[Media:$1|$1]]''' е вратен на [$4 верзијата од $3, $2].",
        "filerevert-badversion": "Нема претходна месна верзија на оваа податотека со даденото време.",
+       "filerevert-identical": "Тековната верзија на податотеката е веќе истоветна на избраната.",
        "filedelete": "Избриши го $1",
        "filedelete-legend": "Избриши податотека",
        "filedelete-intro": "Ја бришете податотеката '''[[Media:$1|$1]]''' заедно со нејзината историја.",
        "log-title-wildcard": "Пребарај наслови кои почнуваат со овој текст",
        "showhideselectedlogentries": "Прикажи/скриј одбрани записи",
        "log-edit-tags": "Измени ознаки на одредени дневнички записи",
-       "checkbox-select": "Ð\9eдбеÑ\80и: $1",
+       "checkbox-select": "Ð\98збоÑ\80: $1",
        "checkbox-all": "Сите",
        "checkbox-none": "Ништо",
        "checkbox-invert": "Избери обратно",
        "watchnologin": "Не сте најавени",
        "addwatch": "Додај во набљудувани",
        "addedwatchtext": "„[[:$1]]“ и нејзината страница за разговор се додадени во вашите [[Special:Watchlist|набљудувања]].",
+       "addedwatchtext-talk": "„[[:$1]]“ и нејзината поврзана страница се додадени во вашите [[Special:Watchlist|набљудувања]].",
        "addedwatchtext-short": "Страницата „$1“ е додадена во вашите набљудувања.",
        "removewatch": "Отстрани набљудуваните",
        "removedwatchtext": "„[[:$1]]“ и нејзината страница за разговор се отстранети од вашите [[Special:Watchlist|набљудувања]].",
+       "removedwatchtext-talk": "„[[:$1]]“ и нејзината поврзана страница се отстранети од вашите [[Special:Watchlist|набљудувања]].",
        "removedwatchtext-short": "Страницата „$1“ е отстранета од вашите набљудувања.",
        "watch": "Набљудувај",
        "watchthispage": "Набљудувај ја страницава",
        "rollbacklinkcount-morethan": "отповикај повеќе од $1 {{PLURAL:$1|уредување|уредувања}}",
        "rollbackfailed": "Отповикувањето не успеа",
        "rollback-missingparam": "Недостасуваат задолжителни параметри за барањето.",
+       "rollback-missingrevision": "Не можам да ги вчитам податоците за преработката.",
        "cantrollback": "Уредувањето не може да се отповика.\nПоследниот уредник е воедно и единствениот автор на страницата.",
        "alreadyrolled": "Не може да се отповика последното уредување на страницата „[[:$1]]“ извршено од  [[User:$2|$2]] ([[User talk:$2|разговор]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nнекој друг веќе ја изменил или отповикал страницата.\n\nПоследното уредување го изврши [[User:$3|$3]] ([[User talk:$3|разговор]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Коментарот на уредувањето беше: <em>$1</em>.",
        "undeletehistorynoadmin": "Оваа статија е избришана. Причината за бришењето е наведена подолу,\nзаедно со информации за корисникот кој ја уредувал страницата пред бришењето. Целиот текст\nод избришаните верзии е достапен само за администраторите.",
        "undelete-revision": "Избришана преработка на $1 (од $4, во $5) од уредникот $3:",
        "undeleterevision-missing": "Грешна или непостоечка преработка.\nМожеби имате лоша врска, преработката била обновена или избришана од архивата.",
+       "undeleterevision-duplicate-revid": "Не можев да повратам {{PLURAL:$1|една преработка|$1 преработки}} бидејќи {{PLURAL:$1|нејзината|нивните}} <code>rev_id</code> се веќе зафатени.",
        "undelete-nodiff": "Не постои постара преработка.",
        "undeletebtn": "Врати",
        "undeletelink": "погледај/врати",
        "undeletedrevisions": "{{PLURAL:$1|1 измена е обновена|$1 измени се обновени}}",
        "undeletedrevisions-files": "{{PLURAL:$1|1 измена|$1 измени}} и {{PLURAL:$2|1 податотека|$2 податотеки}} се вратени",
        "undeletedfiles": "{{PLURAL:$1|1 податотека е вратена|$1 податотеки се вратени}}",
-       "cannotundelete": "Враќањето не успеа:\n$1",
+       "cannotundelete": "Враќањето не успеа делумно или целосно:\n$1",
        "undeletedpage": "'''$1 беше обновена'''\n\nПогледнете го [[Special:Log/delete|дневникот на бришења]] за попис на претходни бришења и обновувања.",
        "undelete-header": "Списокот на неодамна избришани страници ќе го најдете на [[Special:Log/delete|дневникот на бришења]].",
        "undelete-search-title": "Пребарување на избришани страници",
        "sp-contributions-newbies-sub": "За нови кориснички сметки",
        "sp-contributions-newbies-title": "Придонеси на нови корисници",
        "sp-contributions-blocklog": "Дневник на блокирања",
-       "sp-contributions-suppresslog": "притаени придонеси на корисникот",
-       "sp-contributions-deleted": "избÑ\80иÑ\88ани ÐºÐ¾Ñ\80иÑ\81ниÑ\87ки Ð¿Ñ\80идонеÑ\81и",
+       "sp-contributions-suppresslog": "притаени придонесите на {{GENDER:$1|корисникот|корисничката}}",
+       "sp-contributions-deleted": "избÑ\80иÑ\88ани Ð¿Ñ\80идонеÑ\81иÑ\82е Ð½Ð° {{GENDER:$1|коÑ\80иÑ\81никоÑ\82|коÑ\80иÑ\81ниÑ\87каÑ\82а}}",
        "sp-contributions-uploads": "подигања",
        "sp-contributions-logs": "дневници",
        "sp-contributions-talk": "разговор",
        "pageinfo-article-id": "Назнака на страницата",
        "pageinfo-language": "Јазик на содржината на страницата",
        "pageinfo-content-model": "Модел на содржината на страницата",
+       "pageinfo-content-model-change": "смени",
        "pageinfo-robot-policy": "Индексирање со роботи",
        "pageinfo-robot-index": "Дозволено",
        "pageinfo-robot-noindex": "Недозволено",
        "unit-pixel": "п",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Да го исчистам меѓускладот на страницава?",
-       "confirm-purge-bottom": "Со Ð¾Ð²Ð°Ð° Ð¾Ð¿ÐµÑ\80аÑ\86иÑ\98а се чисти опслужувачкиот меѓусклад и се прикажува најновата верзија.",
+       "confirm-purge-bottom": "Со Ð¾Ð²Ð°Ð° Ð¿Ð¾Ñ\81Ñ\82апка се чисти опслужувачкиот меѓусклад и се прикажува најновата верзија.",
        "confirm-watch-button": "ОК",
        "confirm-watch-top": "Да ја додадам страницава во набљудуваните?",
        "confirm-unwatch-button": "ОК",
        "version-extensions": "Воспоставени додатоци",
        "version-skins": "Воспоставени рува",
        "version-specialpages": "Службени страници",
-       "version-parserhooks": "РаÑ\81Ñ\87ленÑ\83ваÑ\87ки ÐºÑ\83ки",
+       "version-parserhooks": "РаÑ\81Ñ\87ленÑ\83ваÑ\87ки Ð¿Ñ\80еÑ\81Ñ\80еÑ\82ниÑ\86и",
        "version-variables": "Променливи",
        "version-antispam": "Спречување на спам",
        "version-api": "Извршници",
        "version-other": "Друго",
        "version-mediahandlers": "Ракувачи со мултимедијални содржини",
-       "version-hooks": "Ð\9aÑ\83ки",
+       "version-hooks": "Ð\9fÑ\80еÑ\81Ñ\80еÑ\82ник",
        "version-parser-extensiontags": "Ознаки за расчленувачки додатоци",
-       "version-parser-function-hooks": "Ð\9aÑ\83ки на расчленувачки функции",
-       "version-hook-name": "Ð\98ме Ð½Ð° ÐºÑ\83ка",
+       "version-parser-function-hooks": "Ð\9fÑ\80еÑ\81Ñ\80еÑ\82ниÑ\86и на расчленувачки функции",
+       "version-hook-name": "Ð\9dазив Ð½Ð° Ð¿Ñ\80еÑ\81Ñ\80еÑ\82никоÑ\82",
        "version-hook-subscribedby": "Претплатено од",
        "version-version": "($1)",
        "version-no-ext-name": "[нема име]",
        "api-error-filetype-banned": "Овој тип на податотека е забранет.",
        "api-error-filetype-banned-type": "$1 не {{PLURAL:$4|е допуштен тип на податотека|се допуштени типови на податотека}}. {{PLURAL:$3|Допуштен е|Допуштени се}} $2.",
        "api-error-filetype-missing": "На податотеката ѝ недостасува наставка.",
-       "api-error-hookaborted": "Ð\98зменаÑ\82а Ñ\88Ñ\82о Ñ\81е Ð¾Ð±Ð¸Ð´Ð¾Ð²Ñ\82е Ð´Ð° Ñ\98а Ð½Ð°Ð¿Ñ\80авиÑ\82е Ðµ Ð¾Ñ\82кажана Ð¾Ð´ ÐºÑ\83ка за наставки.",
+       "api-error-hookaborted": "Ð\98зменаÑ\82а Ñ\88Ñ\82о Ñ\81е Ð¾Ð±Ð¸Ð´Ð¾Ð²Ñ\82е Ð´Ð° Ñ\98а Ð½Ð°Ð¿Ñ\80авиÑ\82е Ðµ Ð¾Ñ\82кажана Ð¾Ð´ Ð¿Ñ\80еÑ\81Ñ\80еÑ\82ник за наставки.",
        "api-error-http": "Внатрешна грешка: не можам да се поврзам со опслужувачот.",
        "api-error-illegal-filename": "Податотеката има недозволено име.",
        "api-error-internal-error": "Внатрешна грешка: нешто тргна наопаку при обработката на она што го подигате на викито.",
        "mw-widgets-dateinput-placeholder-month": "ГГГГ-ММ",
        "mw-widgets-titleinput-description-new-page": "страницата сè уште не постои",
        "mw-widgets-titleinput-description-redirect": "пренасочување кон $1",
-       "api-error-blacklisted": "Одберете поинаков, описен наслов.",
        "sessionmanager-tie": "Не можете истовремено да користите повеќе типови барања за заверка: $1.",
        "sessionprovider-generic": "$1 седници",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "седници со колачиња",
        "log-action-filter-newusers": "Тип на создавање сметка:",
        "log-action-filter-patrol": "Тип на патролирање:",
        "log-action-filter-protect": "Тип на заштита:",
-       "log-action-filter-rights": "Тип на промена во правата",
-       "log-action-filter-suppress": "Тип на скривање",
+       "log-action-filter-rights": "Тип на промена во правата:",
+       "log-action-filter-suppress": "Тип на скривање:",
        "log-action-filter-upload": "Тип на подигање:",
        "log-action-filter-all": "Сите",
        "log-action-filter-block-block": "блокирање",
        "linkaccounts-submit": "Поврзи сметки",
        "unlinkaccounts": "Одврзи сметки",
        "unlinkaccounts-success": "Сметката е одврзана.",
-       "authenticationdatachange-ignored": "Промената на податоците во заверката не е обработена. Можеби не е поставен услужник?"
+       "authenticationdatachange-ignored": "Промената на податоците во заверката не е обработена. Можеби не е поставен услужник?",
+       "userjsispublic": "Напомена: потстраниците со JavaScript не треба да содржат дсоверливи податоци бидејќи истите се видливи и за други корисници.",
+       "usercssispublic": "Напомена: потстраниците со CSS не треба да содржат дсоверливи податоци бидејќи истите се видливи и за други корисници."
 }
index 5d9c024..389d307 100644 (file)
        "tagline": "{{SITENAME}} സംരംഭത്തിൽ നിന്ന്",
        "help": "സഹായം",
        "search": "തിരയൂ",
+       "search-ignored-headings": " #<!-- ഈ വരി ഇതേ പോലെ വിടുക --> <pre>\n# തിരച്ചിലിൽ അവഗണിക്കപ്പെടുന്ന തലക്കെട്ടുകൾ.\n# താളിന്റെ തലക്കെട്ടുകളുടെ സൂചികവത്കരണം നടന്നാലുടൻ ഇവിടെ വരുത്തുന്ന മാറ്റങ്ങൾ ഫലത്തിൽ വരുന്നതാണ്.\n# ശൂന്യമായ തിരുത്ത് ചെയ്ത് താൾ വീണ്ടും സൂചികാവത്കരിക്കാവുന്നതാണ്.\n# എഴുത്തുരീതി ഇനി കൊടുക്കുന്നു:\n#   * \"#\" അക്ഷരത്തിൽ തുടങ്ങി വരിയുടെ അവസാനം വരെയുള്ള എന്തും സൂചനാക്കുറിപ്പ് ആയിരിക്കും.\n#   * ശൂന്യമല്ലാത്ത ഓരോ വരിയും അക്ഷരവ്യത്യാസമില്ലാതെ അവഗണിക്കപ്പെടാനുള്ള തലക്കെട്ടായിരിക്കും\nഅവലംബം\nപുറത്തേക്കുള്ള കണ്ണികൾ\nഇതും കാണുക\n #</pre> <!-- ഈ വരി ഇതേ പോലെ വിടുക-->",
        "searchbutton": "തിരയൂ",
        "go": "പോകൂ",
        "searcharticle": "പോകൂ",
        "passwordreset-emailelement": "ഉപയോക്തൃനാമം: \n$1\n\nതാത്കാലിക രഹസ്യവാക്ക്: \n$2",
        "passwordreset-emailsentemail": "താങ്കളുടെ അംഗത്വത്തിന് നൽകിയിട്ടുള്ള ഇമെയിൽ വിലാസം ഇതാണെങ്കിൽ,  രഹസ്യവാക്ക് പുനർസജ്ജീകരണ ഇമെയിൽ അയക്കുന്നതാണ്.",
        "passwordreset-emailsentusername": "ഈ ഉപയോക്തൃനാമത്തിന് ഒരു ഇമെയിൽ വിലാസം ചേർത്തിട്ടുണ്ടെങ്കിൽ,  രഹസ്യവാക്ക് പുനർസജ്ജീകരണ ഇമെയിൽ അയക്കുന്നതാണ്.",
-       "passwordreset-emailsent-capture": "രഹസ്യവാക്ക് പുനർസജ്ജീകരണ ഇമെയിൽ അയച്ചിട്ടുണ്ട്, അത് താഴെക്കൊടുക്കുന്നു.",
-       "passwordreset-emailerror-capture": "താഴെക്കൊടുത്തിരിക്കുന്ന, രഹസ്യവാക്ക് പുനർസജ്ജീകരണ ഇമെയിൽ സൃഷ്ടിക്കാനായെങ്കിലും, അത് {{GENDER:$2|ഉപയോക്താവിന്}} അയയ്ക്കുന്നത് പരാജയപ്പെട്ടു: $1",
        "changeemail": "ഇമെയിൽ വിലാസം മാറ്റുക അല്ലെങ്കിൽ നീക്കംചെയ്യുക",
        "changeemail-header": "താങ്കളുടെ ഇമെയിൽ വിലാസത്തിൽ മാറ്റംവരുത്താൻ ഈ ഫോം പൂരിപ്പിച്ചു നൽകുക.താങ്കളുടെ അംഗത്വവുമായി ഏതെങ്കിലുമൊരു ഇമെയിൽ വിലാസത്തിനുള്ള ബന്ധം ഒഴിവാക്കാൻ ഫോം പൂരിപ്പിക്കുമ്പോൾ പുതിയ ഇമെയിൽ വിലാസത്തിനുള്ള ഭാഗം ഒഴിച്ചിടുക.",
-       "changeemail-passwordrequired": "ഈ മാറ്റം സ്ഥിരീകരിക്കാൻ താങ്കളുടെ രഹസ്യവാക്ക് നൽകുക.",
        "changeemail-no-info": "ഈ താൾ നേരിട്ടു കാണുന്നതിന് താങ്കൾ ലോഗിൻ ചെയ്തിരിക്കണം.",
        "changeemail-oldemail": "ഇപ്പോഴത്തെ ഇമെയിൽ വിലാസം:",
        "changeemail-newemail": "പുതിയ ഇമെയിൽ വിലാസം:",
        "undo-nochange": "തിരുത്ത് മുമ്പേ തന്നെ ഒഴിവാക്കിയതായി കാണുന്നു.",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|സംവാദം]]) ചെയ്ത നാൾപ്പതിപ്പ് $1 നീക്കം ചെയ്യുന്നു",
        "undo-summary-username-hidden": "മറയ്ക്കപ്പെട്ട ഉപയോക്താവ് ചെയ്ത നാൾപ്പതിപ്പ് $1 തിരസ്കരിക്കുക",
-       "cantcreateaccounttitle": "അംഗത്വം സൃഷ്ടിക്കാൻ സാധിച്ചില്ല",
        "cantcreateaccount-text": "ഈ ഐ.പി. ('''$1''') വിലാസത്തിൽ നിന്നു അംഗത്വം സൃഷ്ടിക്കുന്നത് [[User:$3|$3]] നിരോധിച്ചിരിക്കുന്നു.\n\n$3 അതിനു കാണിച്ചിരിക്കുന്ന കാരണം ''$2'' ആണ്‌.",
        "cantcreateaccount-range-text": "താങ്കളുടെ ഐ.പി. വിലാസം (<strong>$4</strong>) ഉൾപ്പെടുന്ന <strong>$1</strong> എന്ന പരിധിയിലെ ഐ.പി. വിലാസങ്ങളിൽ നിന്ന് അംഗത്വമെടുക്കുന്നത് [[User:$3|$3]] തടഞ്ഞിരിക്കുകയാണ്.\n\n$3 നൽകിയിരിക്കുന്ന കാരണം <em>$2</em> എന്നാണ്",
        "viewpagelogs": "ഈ താളുമായി ബന്ധപ്പെട്ട രേഖകൾ കാണുക",
        "mw-widgets-dateinput-no-date": "തീയതി ഒന്നും തിരഞ്ഞെടുത്തിട്ടില്ല",
        "mw-widgets-titleinput-description-new-page": "താൾ ഇപ്പോൾ നിലവിലില്ല",
        "mw-widgets-titleinput-description-redirect": "$1 എന്ന താളിലേക്കുള്ള തിരിച്ചുവിടൽ",
-       "api-error-blacklisted": "ദയവായി മറ്റൊരു വിവരണാത്മകമായ തലക്കെട്ട് തിരഞ്ഞെടുക്കുക.",
        "sessionmanager-tie": "വ്യത്യസ്ത തരത്തിലുള്ള അനുമതി നൽകൽ തരങ്ങൾ സംയോജിപ്പിക്കാനാവില്ല: $1.",
        "sessionprovider-generic": "$1 സെഷനുകൾ",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "കൂക്കി-അധിഷ്ഠിത സെഷനുകൾ",
index 4d45b82..533ebef 100644 (file)
@@ -67,6 +67,7 @@
        "tog-watchdefault": "मी संपादित केलेली पाने आणि संचिका माझ्या निरीक्षणसूचीत टाका",
        "tog-watchmoves": "मी स्थानांतर केलेली पाने आणि संचिका माझ्या निरीक्षणसूचीत टाका",
        "tog-watchdeletion": "मी वगळलेली पाने आणि संचिका माझ्या निरीक्षणसूचीत टाका",
+       "tog-watchuploads": "मी अपभारीलेल्या नविन संचिका माझ्या निरीक्षणसूचीत जोडा",
        "tog-watchrollback": "यात ज्या पानात मी माझ्या निरीक्षणसूचीत पुनर्परतीची (रोलबॅक) क्रिया केलेली आहे.",
        "tog-minordefault": "सर्व संपादने 'छोटा बदल' म्हणून आपोआप जतन करा.",
        "tog-previewontop": "झलक संपादन खिडकीच्या आधी दाखवा",
        "yourpasswordagain": "तुमचा परवलीचा शब्द पुन्हा टंका:",
        "createacct-yourpasswordagain": "परवलीच्या शब्दाची निश्चिती करा",
        "createacct-yourpasswordagain-ph": "पुन्हा परवलीचा शब्द टाका",
-       "remembermypassword": "माझा सनोंदप्रवेश (लॉग-ईन) या न्याहाळकावर लक्षात ठेवा (जास्तीत जास्त $1 {{PLURAL:$1|दिवसासाठी|दिवसांसाठी}})",
        "userlogin-remembermypassword": "मला नोंदीकृतच(लॉग्ड-ईन) ठेवा",
        "userlogin-signwithsecure": "सुरक्षित अनुबंध(सेक्युअर कनेक्शन) वापरा",
        "cannotloginnow-title": "आता सनोंद प्रवेश घेऊ शकत नाही",
        "passwordreset-emailelement": "सदस्यनाव: \n$1\n\nअस्थायी परवलीचा शब्द: \n$2",
        "passwordreset-emailsentemail": "जर हा विपत्रपत्ता आपल्या खात्याशी संलग्न असेल तर, परवलीच्या शब्दाच्या पुनर्स्थापनेबाबत एक विपत्र पाठवण्यात येईल.",
        "passwordreset-emailsentusername": "जर या सदस्यनावाशी संलग्न विपत्रपत्ता असेल तर, परवलीचा शब्द पुनर्स्थापनाबाबत विपत्र पाठविल्या जाईल.",
-       "passwordreset-emailsent-capture": "'परवलीचा शब्द' पुनर्स्थापनेबाबत एक विपत्र पाठवण्यात आले आहे जे खाली दर्शविण्यात आले आहे.",
-       "passwordreset-emailerror-capture": "'परवलीचा शब्द' पुनर्स्थापनेबाबत एक विपत्र निर्माण करण्यात आले, जे खाली दर्शविण्यात आले आहे.परंतु,{{GENDER:$2|सदस्य}}ला पाठविणे असफल झाले: $1",
        "changeemail": "विपत्रपत्ता बदला किंवा हटवा",
        "changeemail-header": "आपला विपत्रपत्ता बदलण्यास हे आवेदन पूर्ण करा.जर आपणास आपल्या खात्याशी संलग्न कोणताही विपत्रपत्ता हटवायचा असेल तर,आवेदन सादर करण्यापूर्वी, नविन विपत्रपत्त्यासाठी असलेली जागा कोरी ठेवा.",
-       "changeemail-passwordrequired": "हे बदल नक्की करण्यासाठी आपणास आपला परवलीचा शब्द टाकावा लागेल.",
        "changeemail-no-info": "हे पान थेट बघण्यासठी तुम्हाला सनोंद-प्रवेशित असावे लागेल.",
        "changeemail-oldemail": "सध्याचा ईमेल पत्ता :",
        "changeemail-newemail": "नवा ईमेल पत्ता:",
        "minoredit": "हा एक छोटा बदल आहे",
        "watchthis": "या लेखावर लक्ष ठेवा",
        "savearticle": "हा लेख साठवा",
+       "savechanges": "बदल जतन करा",
+       "publishchanges": "बदल प्रकाशित करा",
        "preview": "झलक",
        "showpreview": "झलक दाखवा",
        "showdiff": "बदल दाखवा",
        "undo-nochange": "असे दिसते कि हे संपादन पूर्ववत केल्या गेले आहे.",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|चर्चा]])यांची आवृत्ती $1 परतवली.",
        "undo-summary-username-hidden": "अज्ञात सदस्याची $1 आवृत्ती परतवा",
-       "cantcreateaccounttitle": "खाते उघडू शकत नाही",
        "cantcreateaccount-text": "('''$1''')या आंतरजाल अंकपत्त्याकडूनच्या खाते निर्मितीस [[User:$3|$3]]ने अटकाव केला आहे.\n\n$3ने ''$2'' कारण दिले आहे.",
        "cantcreateaccount-range-text": "<strong>$1</strong>आवाक्यातील आंतरजाल अंकपत्ते,ज्यात आपल्या (<strong>$4</strong>) या अंकपत्त्याचा समावेश आहे, [[User:$3|$3]] ने त्यांच्या खाते निर्मितीस प्रतिबंध केला आहे.\n\n$3 ने <em>$2</em>कारण दिले आहे.",
        "viewpagelogs": "या पानाच्या नोंदी पहा",
        "prefs-email": "विपत्र पर्याय",
        "prefs-rendering": "देखावा",
        "saveprefs": "जतन करा",
-       "restoreprefs": "सरà¥\8dव à¤¡à¤¿à¤«à¥\89लà¥\8dà¤\9f मांडणी पूर्ववत करा (सर्व विभागात)",
+       "restoreprefs": "सरà¥\8dव à¤\85विà¤\9aल(डिफà¥\89लà¥\8dà¤\9f) मांडणी पूर्ववत करा (सर्व विभागात)",
        "prefs-editing": "संपादन",
        "rows": "ओळी:",
        "columns": "स्तंभ:",
        "filerevert-submit": "पूर्वपदास न्या",
        "filerevert-success": "[$3, $2 प्रमाणे आवर्तन $4]कडे<strong>[[Media:$1|$1]]</strong>उलटवण्यात आली.",
        "filerevert-badversion": "दिलेलेल्या वेळ मापनानुसार,या संचिकेकरिता कोणतीही पूर्वीची स्थानिक आवृत्ती नाही.",
+       "filerevert-identical": "या संचिकेची सध्याची आवृत्ती ही निवड केलेल्या आवृत्तीसमच आहे.",
        "filedelete": "$1 वगळा",
        "filedelete-legend": "संचिका वगळा",
        "filedelete-intro": "तुम्ही<strong>[[Media:$1|$1]]</strong>त्याच्या सर्व इतिहासासह,वगळण्याच्या तयारीत आहात.",
        "rollbacklinkcount": "उलटवा $1 {{PLURAL:$1|संपादन|संपादने}}",
        "rollbacklinkcount-morethan": "$1 पेक्षा अधिक {{PLURAL:$1|संपादन|संपादने}} उलटवा",
        "rollbackfailed": "द्रूतमाघार फसली",
+       "rollback-missingrevision": "आवृत्ती डाटा भारण करु शकत नाही.",
        "cantrollback": "जुन्या आवृत्तीकडे परतवता येत नाही; शेवटचा संपादक या पानाचा एकमात्र लेखक आहे.",
        "alreadyrolled": "[[User:$2|$2]] ([[User talk:$2|Talk]] [[Special:Contributions/$2|{{int:contribslink}}]])चे शेवटाचे [[:$1]]वे संपादन माघारी परतवता येत नाही; पान आधीच कुणी माघारी परतवले आहे किंवा संपादित केले आहे.\n\nशेवटचे संपादन [[User:$3|$3]] ([[User talk:$3|Talk]] [[Special:Contributions/$3|{{int:contribslink}}]])-चे होते.",
        "editcomment": "संपादन सारांश <em>$1</em> होता.",
        "rollback-success": "$1 ने उलटवलेली संपादने;$2 च्या आवृत्तीस परत नेली.",
        "sessionfailure-title": "सत्र त्रुटी",
        "sessionfailure": "तुमच्या दाखल सत्रात काही समस्या दिसते;सत्र अपहारणापासून \nवाचविण्याचे दृष्टीने ही कृती रद्द केल्या गेली आहे.कृपया आपल्या विचरकाच्या \"back\" कळीवर टिचकी मारा आणि तुम्ही ज्या पानावरून आला ते पुन्हा चढवा,आणि परत प्रयत्न करा.",
+       "changecontentmodel": "पानाचा आशय नमूना (कंटेंट मॉडेल) बदला",
        "changecontentmodel-title-label": "लेखपान शीर्ष",
        "changecontentmodel-reason-label": "कारण:",
+       "changecontentmodel-submit": "बदला",
        "logentry-contentmodel-change-revertlink": "उलटवा",
        "logentry-contentmodel-change-revert": "उलटवा",
        "protectlogpage": "सुरक्षा नोंदी",
        "undeletedrevisions": "{{PLURAL:$1|1 आवर्तन|$1 आवर्तने}} पुनर्स्थापित",
        "undeletedrevisions-files": "{{PLURAL:$1|1 आवर्तन|$1 आवर्तने}}आणि {{PLURAL:$2|1 संचिका|$2 संचिका}} पुनर्स्थापित",
        "undeletedfiles": "{{PLURAL:$1|1 संचिका|$1 संचिका}} पुनर्स्थापित",
-       "cannotundelete": "उलटवणे फसले:$1",
+       "cannotundelete": "à¤\95ाहà¥\80 à¤\95िà¤\82वा à¤¸à¤°à¥\8dवà¤\9a à¤\89लà¤\9fवणà¥\87 à¤«à¤¸à¤²à¥\87:$1",
        "undeletedpage": "<strong>$1ला पुनर्स्थापित केले</strong>\n\nअलिकडिल वगळलेल्या आणि पुनर्स्थापितांच्या नोंदीकरिता [[Special:Log/delete|वगळल्याच्या नोंदी]] पहा .",
        "undelete-header": "अलीकडील वगळलेल्या पानांकरिता [[Special:Log/delete|वगळलेल्या नोंदी]] पहा.",
        "undelete-search-title": "वगळलेली पाने शोधा",
        "sp-contributions-newbies-sub": "नवशिक्यांसाठी",
        "sp-contributions-newbies-title": "नवीन खात्यांसाठी सदस्य योगदान",
        "sp-contributions-blocklog": "रोध नोंदी",
-       "sp-contributions-suppresslog": "सदस्य योगदानाचे दमन केले",
-       "sp-contributions-deleted": "वगळलेली सदस्य संपादने",
+       "sp-contributions-suppresslog": "{{GENDER:$1|सदस्य}} योगदानाचे दमन केले",
+       "sp-contributions-deleted": "वगळलेली {{GENDER:$1|सदस्य}} संपादने",
        "sp-contributions-uploads": "अपभारणे",
        "sp-contributions-logs": "नोंदी",
        "sp-contributions-talk": "चर्चा",
        "mw-widgets-dateinput-no-date": "कोणताही दिनांक निवडला नाही",
        "mw-widgets-titleinput-description-new-page": "अद्याप पान अस्तित्वात नाही",
        "mw-widgets-titleinput-description-redirect": "$1ला पुनर्निर्देशित करा",
-       "api-error-blacklisted": "कुपया वेगळे वर्णनात्मक शीर्षक निवडा",
        "sessionmanager-tie": "हे एकत्रित करु शकत नाही,बहुविध विनंती अधिप्रमाणन प्रकार:$1",
        "sessionprovider-generic": "$1 सत्रे",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "कुकी-आधारीत सत्रे",
        "sessionprovider-nocookies": "कुकिज अक्षम असू शकतात. याची खात्री करा कि कुकिज सक्षम केल्या आहेत व पुन्हा सुरुवात करा.",
        "randomrootpage": "अविशिष्ट मूळ पान",
-       "log-action-filter-suppress-block": "रोधामार्फत सदस्य दाबणे"
+       "log-action-filter-suppress-block": "रोधामार्फत सदस्य दाबणे",
+       "changecredentials": "अधिकारपत्रे (क्रेडेंटियल्स)बदला",
+       "removecredentials": "अधिकारपत्रे (क्रेडेंटियल्स) हटवा"
 }
index bd9d358..305228d 100644 (file)
@@ -21,7 +21,8 @@
                        "Pizza1016",
                        "Macofe",
                        "Matma Rex",
-                       "Nemo bis"
+                       "Nemo bis",
+                       "Mbrt"
                ]
        },
        "tog-underline": "Garis bawah pautan:",
        "tagline": "Daripada {{SITENAME}}.",
        "help": "Bantuan",
        "search": "Cari",
+       "search-ignored-headings": " #<!-- jangan usik baris ini --> <pre>\n# Tajuk yang akan diabaikan oleh pencarian.\n# Suntingannya diperlakukan sebaik sahaja laman yang bertajuk ini diindekskan.\n# Anda boleh memaksakan pengindeksan semula laman dengan melakukan suntingan nol (null edit).\n# Sintaks adalah seperti berikut:\n#   * Semuanya dari aksara \"#\" ke hujung baris dikira komen.\n#   * Setiap baris tak kosong adalah tajuk yang setepatnya untuk diabaikan.\nRujukan\nPautan luar\nLihat juga\n #</pre> <!-- jangan usik baris ini -->",
        "searchbutton": "Cari",
        "go": "Pergi",
        "searcharticle": "Pergi",
        "passwordreset-emailtext-user": "Pengguna $1 telah memohon supaya kata laluan diset semula untuk {{SITENAME}} anda ($4). {{PLURAL:$3|Akaun|Akaun-akaun}} pengguna yang berikut\ndikaitkan dengan alamat e-mel ini:\n\n$2\n\n{{PLURAL:$3|Kata|Kata-kata}} laluan sementara ini akan luput dalam masa $5 hari. Anda harus log masuk dan membuat kata laluan yang baru sekarang. Jika permohonan ini dibuat oleh orang lain, atau jika anda teringat kembali kata laluan asal anda dan anda tidak lagi berhasrat untuk mengubahnya, anda boleh mengabaikan pesanan ini dan terus menggunakan kata laluan lama anda.",
        "passwordreset-emailelement": "Nama pengguna: \n$1\n\nKata laluan sementara: \n$2",
        "passwordreset-emailsentemail": "Jika ini ialah alamat e-mel yang terdaftar untuk akaun anda, maka e-mel set semula kata laluan akan dihantar.",
-       "passwordreset-emailsent-capture": "E-mel set semula kata laluan telah dihantar, seperti yang dipaparkan di bawah.",
-       "passwordreset-emailerror-capture": "E-mel set semula kata laluan telah dihasilkan, seperti yang dipaparkan di bawah, tetapi tidak berjaya dihantar kepada {{GENDER:$2|pengguna}} berkenaan: $1",
        "changeemail": "Tukar atau padamkan alamat e-mel",
        "changeemail-header": "Lengkapkan borang ini untuk menukar alamat e-mel anda. Jika anda ingin memutuskan sebarang hubungan alamat e-mel daripada akaun anda, biarkan ruangan alamat e-mel kosong ketika menghantar borang.",
-       "changeemail-passwordrequired": "Anda akan dikehendaki memasukkan kata laluan untuk mengesahkan perubahan ini.",
        "changeemail-no-info": "Anda hendaklah log masuk terlebih dahulu untuk mencapai laman ini secara terus.",
        "changeemail-oldemail": "Alamat e-mel sekarang:",
        "changeemail-newemail": "Alamat e-mel baru:",
        "subject": "Perkara:",
        "minoredit": "Ini adalah suntingan kecil",
        "watchthis": "Pantau laman ini",
-       "savearticle": "Simpan",
+       "savearticle": "Paparkan Laman",
+       "publishpage": "Terbitkan",
+       "publishchanges": "Terbit perubahan",
        "preview": "Pralihat",
        "showpreview": "Paparkan pralihat",
        "showdiff": "Lihat perubahan",
        "undo-nochange": "Suntingan itu nampaknya sudah dibatalkan.",
        "undo-summary": "Membatalkan semakan $1 oleh [[Special:Contributions/$2|$2]] ([[User talk:$2|Perbincangan]])",
        "undo-summary-username-hidden": "Buat asal semakan $1 oleh pengguna tersembunyi",
-       "cantcreateaccounttitle": "Akaun tidak dapat dibuka",
        "cantcreateaccount-text": "Pembukaan akaun daripada alamat IP ini (<b>$1</b>) telah disekat oleh [[User:$3|$3]].\n\nSebab yang diberikan oleh $3 ialah ''$2''",
        "cantcreateaccount-range-text": "Pembukaan akaun dari alamat-alamat IP dalam julat <strong>$1</strong>, termasuk alamat IP anda (<strong>$4</strong>), telah disekat oleh [[User:$3|$3]].\n\nSebab yang diberikan oleh $3 ialah <em>$2</em>",
        "viewpagelogs": "Lihat log bagi laman ini",
        "mw-widgets-dateinput-no-date": "Tarik belum dipilih",
        "mw-widgets-titleinput-description-new-page": "laman belum wujud",
        "mw-widgets-titleinput-description-redirect": "melencong ke $1",
-       "api-error-blacklisted": "Sila pilih tajuk yang berbeza dan deskriptif.",
        "randomrootpage": "Laman akar rawak"
 }
index 9cae000..fb7b918 100644 (file)
        "yourpasswordagain": "စကားဝှက် ပြန်​ရိုက်​ပါ -",
        "createacct-yourpasswordagain": "စကားဝှက်ကို အတည်ပြုပါ",
        "createacct-yourpasswordagain-ph": "စကားဝှက်ကို ထပ်မံ ရိုက်ထည့်ပါ",
-       "remembermypassword": "ဤ​ကွန်​ပျူ​တာ​တွင်​ ကျွန်ုပ်ကို ​မှတ်​ထား​ရန် (အများဆုံး $1 {{PLURAL:$1|ရက်|ရက်}}ကြာ)",
        "userlogin-remembermypassword": "Log in ဝင်ထားမည်",
        "userlogin-signwithsecure": "လုံခြုံသော ဆက်သွယ်မှုကို သုံးမည်",
        "yourdomainname": "သင့်ဒိုမိန်း -",
        "minoredit": "အရေးမကြီးသော ​ပြင်​ဆင်​မှု ​ဖြစ်​သည်​",
        "watchthis": "ဤစာမျက်နှာကို စောင့်ကြည့်ရန်",
        "savearticle": "ဤစာမျက်နှာကို သိမ်းရန်",
+       "savechanges": "ပြောင်းလဲမှုများကို သိမ်းရန်",
+       "publishpage": "စာမျက်နှာကို လွှင့်တင်ရန်",
+       "publishchanges": "ပြောင်းလဲမှုများကို လွှင့်တင်ရန်",
        "preview": "နမူနာ",
        "showpreview": "န​မူ​နာ​ပြ​ရန်",
        "showdiff": "ပြင်​ဆင်​ထား​သည်​များ​ကို​ ပြရန်",
        "shown-title": "စာမျက်နှာတစ်ခုလျှင် ရလဒ် $1 {{PLURAL:$1|ခု|ခု}} ပြရန်",
        "viewprevnext": "($1 {{int:မှ}} $2) အထိကြား ရလဒ် ($3) ခုကို ကြည့်ရန်",
        "searchmenu-exists": "'''ဤဝီကီတွင် \"[[:$1]]\" အမည်နှင့် စာမျက်နှာတစ်ခုရှိသည်။'''",
-       "searchmenu-new": "<strong>á\80¤á\80\9dá\80®á\80\80á\80®á\80\90á\80½á\80\84á\80º \"[[:$1]]\" á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\96á\80\94á\80ºá\80\90á\80®á\80¸á\80\95á\80«!</strong> {{PLURAL:$2|0=|á\80\9eá\80\84á\80·á\80ºá\80\9bá\80¾á\80¬á\80\96á\80½á\80±á\80\99á\80¾á\80¯á\80\94á\80¾á\80\84á\80·á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯á\80\9cá\80\8aá\80ºá\80¸ á\80\80á\80¼á\80\8aá\80·á\80ºá\80\95á\80«á\81\8b\80\9bá\80¾á\80¬á\80\96á\80½á\80±á\80\99á\80¾á\80¯ á\80\9bá\80\9cá\80\92á\80ºá\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯á\80\9cá\80\8aá\80ºá\80¸ á\80\80á\80¼á\80\8aá\80ºá\80«ပါ။}}",
+       "searchmenu-new": "<strong>á\80¤á\80\9dá\80®á\80\80á\80®á\80\90á\80½á\80\84á\80º \"[[:$1]]\" á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\96á\80\94á\80ºá\80\90á\80®á\80¸á\80\95á\80«!</strong> {{PLURAL:$2|0=|á\80\9eá\80\84á\80·á\80ºá\80\9bá\80¾á\80¬á\80\96á\80½á\80±á\80\99á\80¾á\80¯á\80\94á\80¾á\80\84á\80·á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯á\80\9cá\80\8aá\80ºá\80¸ á\80\80á\80¼á\80\8aá\80·á\80ºá\80\95á\80«á\81\8b\80\9bá\80¾á\80¬á\80\96á\80½á\80±á\80\99á\80¾á\80¯ á\80\9bá\80\9cá\80\92á\80ºá\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯á\80\9cá\80\8aá\80ºá\80¸ á\80\80á\80¼á\80\8aá\80·á\80ºပါ။}}",
        "searchprofile-articles": "မာတိကာစာမျက်နှာများ",
        "searchprofile-images": "မာလတီမီဒီယာ",
        "searchprofile-everything": "အားလုံး",
index cb59019..d60887a 100644 (file)
        "thursday": "Īcmācuīlilhuitl",
        "friday": "Īcchicuacemilhuitl",
        "saturday": "Īcchicōmilhuitl",
-       "sun": "ilhui",
-       "mon": "ilhui",
-       "tue": "ilhui",
-       "wed": "ilhui",
-       "thu": "ilhui",
+       "sun": "Cemilhui",
+       "mon": "Ōmilhui",
+       "tue": "Ēyilhui",
+       "wed": "Nāuhilhui",
+       "thu": "Mācuililhui",
        "fri": "6 ilhui",
        "sat": "7 ilhui",
-       "january": "Īccēmētztli",
-       "february": "Īcōmēmētztli",
-       "march": "Īcyēyimētztli",
-       "april": "Īcnāuhtetlmētztli",
-       "may_long": "Īcmācuīllimētztli",
-       "june": "Īcchicuacemmētztli",
-       "july": "Īcchicōmemētztli",
-       "august": "Īcchicuēyimētztli",
-       "september": "Īcchiucnāhuimētztli",
-       "october": "Īcmahtlāctlimētztli",
-       "november": "Īcmahtlāctlioncēmētztli",
-       "december": "Īcmahtlāctliomōmemētztli",
-       "january-gen": "Īccēmētztli",
+       "january": "Ic cē mētztli",
+       "february": "Ic ōmemētztli",
+       "march": "Ic ēyi mētztli",
+       "april": "Ic nāuhtetl mētztli",
+       "may_long": "Ic mācuīlli mētztli",
+       "june": "Ic chicuacē mētztli",
+       "july": "Ic chicōme mētztli",
+       "august": "Ic chicuēyi mētztli",
+       "september": "Ic chiucnāhui mētztli",
+       "october": "Ic mahtlāctli mētztli",
+       "november": "Ic mahtlāctli oncē mētztli",
+       "december": "Ic mahtlāctli omōme mētztli",
+       "january-gen": "Ic cē mētztli",
        "february-gen": "Īcōmemētztli",
        "march-gen": "Īcyēyimētztli",
-       "april-gen": "Īcnāuhtetlmētztli",
+       "april-gen": "Ic nāuhtetl mētztli",
        "may-gen": "Īcmācuīllimētztli",
-       "june-gen": "Īcchicuacemmētztli",
-       "july-gen": "Īcchicōmemētztli",
+       "june-gen": "Ic chicuacemmētztli",
+       "july-gen": "Ic chicōme mētztli",
        "august-gen": "Īcchicuēyimētztli",
        "september-gen": "Īcchiucnāhuimētztli",
        "october-gen": "Īcmahtlāctetlmētztli",
        "november-gen": "Īcmahtlāctetloncēmētztli",
        "december-gen": "Īcmahtlāctetlomōmemētztli",
-       "jan": "1 Mētz",
-       "feb": "2 Mētz",
-       "mar": "3 Mētz",
-       "apr": "4 Mētz",
-       "may": "5 Mētz",
-       "jun": "6 Mētz",
-       "jul": "7 Mētz",
-       "aug": "8 Mētz",
-       "sep": "9 Mētz",
-       "oct": "10 Mētz",
-       "nov": "11 Mētz",
-       "dec": "12 Mētz",
+       "jan": "Ic cē",
+       "feb": "Ic ōme",
+       "mar": "Ic ēyi",
+       "apr": "Nāhui",
+       "may": "Mācuilli",
+       "jun": "Chicuacē",
+       "jul": "Chicōme",
+       "aug": "Chicuēyi",
+       "sep": "Chiucnāhui",
+       "oct": "Mahtlāctli",
+       "nov": "Mahtlāctlioncē",
+       "dec": "Mahtlāctliomōme",
        "january-date": "Īccēmētztli $1",
        "february-date": "Īcōmemētztli $1",
        "march-date": "Īquēyimētztli $1",
        "moredotdotdot": "Huehca ōmpa...",
        "mypage": "Noāmauh",
        "mytalk": "Nozānīl",
-       "anontalk": "Inīn IP ītēixnāmiquiliz",
+       "anontalk": "Tēixnāmiquiliztli",
        "navigation": "Nēnemōhualiztli",
        "and": "&#32;īhuān",
        "qbfind": "Xicahci",
        "tagline": "Īhuīcpa {{SITENAME}}",
        "help": "Tēpalēhuiliztli",
        "search": "Mà motèmo",
-       "searchbutton": "Xictēmo",
+       "searchbutton": "Tictēmōz",
        "go": "Xiyauh",
        "searcharticle": "Xiyauh",
        "history": "Tlaīxtli ītlahtōllo",
        "viewsource": "Xiquitta mēyalli",
        "viewsource-title": "Xiquitta in $1 īmēyal",
        "actionthrottled": "Tlachīhualiztli ōmotzacuili",
-       "viewsourcetext": "Tihuelīti tiquitta auh ticcopīna inīn zāzanilli ītlahtōlcaquiliztilōni:",
+       "viewsourcetext": "Tihuelīti tiquittaz auh ticcopīnaz inīn zāzanilli īmachiyōnecaquilizmēyal.",
        "namespaceprotected": "Ahmo tiquihuelīti tiquimpatla zāzaniltin īpan '''$1'''.",
        "ns-specialprotected": "In nònkuâkìskàtlaìxtlapaltìn awel ìmpan nemàtilòs mokinyèktlàlis.",
        "titleprotected": "Inīn zāzanilli ōmoquīxti ic tlachīhualiztli ic [[User:$1|$1]].\nŌquihto: <em>$2</em>",
        "userexists": "In tlatequitiltilīltōcāitl in ōquipehpen ye ia.\nTimitztlātlauhtiah xicpehpena occē.",
        "loginerror": "Ahcuallōtl tlacalaquiliztechcopa",
        "noname": "Ahmo ōtiquihto cualli tlatequitiltilīlli tōcāitl.",
-       "loginsuccesstitle": "Cualli calaquiliztli",
+       "loginsuccesstitle": "Ōticalac",
        "loginsuccess": "'''Ōticalac {{SITENAME}} quemeh \"$1\".'''",
-       "nosuchuser": "Ayāc tlatequitiltilīlli motōcāitia \"$1\".\nIn tlatequitiltilīltōcāitl quimati in huēyimachiyōtlahtōliztli.\nXiquitta in yēquihcuilōlli, ahnozo [[Special:CreateAccount|xicchīhua yancuīc cuenta]].",
+       "nosuchuser": "Ayāc tlatequitiltilīlli motōcāitīlo «$1».\nn tlatequitiltilīltōcāitl quimati in huēyimachiyōtlahtōliztli.\nXiquitta moyēquihcuilōl, ahnozo [[Special:CreateAccount|xicchīhua yancuīc cuentah]].",
        "nosuchusershort": "Ayāc tlatequitiltilīlli motōcāitia \"$1\". Xiquitta in tlein ōtitlahcuiloh melāhuacā cah.\nXiquitta moyēquihcuilōl.",
        "nouserspecified": "Mohuīquilia tiquihtoa cualli tlatequitiltilīltōcāitl.",
        "wrongpassword": "Ahcualli motlahtōlichtacāyo.\nTimitztlātlauhtia xicchīhua occeppa.",
        "newpassword": "Yancuīc motlahtōlichtacayo:",
        "retypenew": "Occeppa xiquihcuiloa yancuīc motlahtōlichtacayo:",
        "resetpass_submit": "Xicpatlāz motlahtōlichtacāyo auh xicalaquīz",
-       "changepassword-success": "Mochtacātlahtōl cualli ōtlapatlalo.",
+       "changepassword-success": "Moichtacātlahtōl ōmopatlac.",
        "resetpass_forbidden": "Tlahtōlichtacayōtl ahmo mohuelītih mopatlah",
        "resetpass-submit-loggedin": "Ticpatlāz motlahtōlichtacāyo",
        "resetpass-submit-cancel": "Xiccāhua",
        "sig_tip": "Motōcā īca cāhuitl",
        "hr_tip": "Pāntli",
        "summary": "Mopatlaliz:",
-       "subject": "Tōcāitl/Āmoxmachiyōtl:",
+       "subject": "Ītechpa:",
        "minoredit": "Ca tepitōn inīn tlapatlaliztli",
        "watchthis": "Xicpiya inīn tlaīxtli",
        "savearticle": "Xicpiya tlaīxtli",
        "accmailtext": "Ōquiyōcox zāzochtacātlahtōlli in [[User talk:$1|$1]] auh ōmoquitītlan īhuīc $2. Tihueliti ticpatlaz īpan ''[[Special:ChangePassword|Ticpatlaz in ]]'' in ōticalaco achtopa.",
        "newarticle": "(Yancuic)",
        "newarticletext": "Ōtictocac cētiliztli cē zāzanilhuīc oc ahmo ia. Intlā quiēlēhuia quichīhua, xitlahcuiloa niman (nō xiquitta [$1 tēpalēhuiliztli zāzanilli] huehca ōmpa tlapatlaliztli). Intlā ahmo, yāuh achtopa zāzanilli.",
-       "noarticletext": "In āxcān, ahmō onca tlahcuilōlli inīn zāzanilpan.\nTihuelīti [[Special:Search/{{PAGENAME}}|tictēmoa inīn zāzaniltōcācopa]] occequīntīn zāzanilpan,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} machiyōmacalpan], ahnozo [{{fullurl:{{FULLPAGENAME}}|action=edit}} ticpatla inīn zāzanilli]</span>.",
+       "noarticletext": "In āxcān, ahmō onca tlahcuilōlli inīn zāzanilpan.\nTihuelīti [[Special:Search/{{PAGENAME}}|tictēmōz inīn zāzanilli ītōca]] occequīntīn zāzanilpan,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} xictēmoa īpan in tlapōhualāmatechpa],\nahnozo [{{fullurl:{{FULLPAGENAME}}|action=edit}} xichīhua inīn zāzanilli]</span>.",
        "userpage-userdoesnotexist": "Ahmo ia cuentah \"<nowiki>$1</nowiki>\" ītōca. Timitztlātlauhtiah xitēchquinōtza intlā ticchīhuāz intlā nozo ticpatlāz inīn zāzanilli.",
        "usercsspreview": "'''Ca inīn moachtochīhualiz ītechcopa moCSS.'''\n'''¡Ahmo ōmochīuh nozan!'''",
        "userjspreview": "'''Ca inīn moachtochīhualiz ītechcopa moJavaScript.'''\n'''¡Ahmo ōmochīuh nozan!'''",
        "edit-conflict": "Tlapatlaliztli yāōyōtōn",
        "edit-already-exists": "Ahmo mohuelīti mochīhua yancuīc zāzanilli.\nYe ia.",
        "content-model-javascript": "JavaScript",
-       "cantcreateaccounttitle": "Ahmo huelītih mochīhua cuentah",
        "cantcreateaccount-text": "[[User:$3|$3]] ōcquīxti cuentah tlachīhualiztli īpal inīn IP ('''$1''').\n\nĪxtlamatiliztli īpal $3 cah ''$2''",
        "viewpagelogs": "Tiquinttāz tlahcuilōlloh inīn zāzaniltechcopa",
        "nohistory": "Nicān ahmō oncah tlaīxtlapatlaliztlahtōllōtl.",
        "currentrev": "Āxcān tlapatlaliztli",
        "currentrev-asof": "Āxcān tlachiyaliztli īpan $1",
        "revisionasof": "Tlachiyaliztli īpan $1",
-       "revision-info": "Tlachiyaliztli īpan $1; $2",
+       "revision-info": "Tlachiyaliztli īpan $1 īpal {{GENDER:$6|$2}}$7",
        "previousrevision": "← Huēhueh tlapatlaliztli",
        "nextrevision": "Yancuīc tlapatlaliztli →",
        "currentrevisionlink": "Āxcān tlapatlaliztli",
        "shown-title": "Quinēxiltīz $1 {{PLURAL:$1|mochīhualiztli}} cece āmac",
        "viewprevnext": "Xiquintta ($1 {{int:pipe-separator}} $2) ($3).",
        "searchmenu-exists": "'''Ye ia zāzanilli ītōca \"[[$1]]\" inīn huiquipan'''",
-       "searchmenu-new": "'''Tihuelīti ticchīhuāz zāzanilli ītōca \"[[:$1]]\" inīn huiquipan'''",
+       "searchmenu-new": "<strong>Ticchīhuāz in zāzanilli «[[:$1]]» inīn huiquipan.</strong> {{PLURAL:$2|0=|Nō xiquitta in tlanāmiquiliztli in mochīhualiztli.}}",
        "searchprofile-articles": "Tlapiyaliztli zāzanilli",
        "searchprofile-images": "Nepapan media",
        "searchprofile-everything": "Mochi",
        "gender-male": "Oquichtli",
        "gender-female": "Cihuātl",
        "email": "E-mail",
-       "prefs-help-realname": "Melāhuac motōca.\nIntlā ticnequi, tlācah quimatīzqueh motequi.",
+       "prefs-help-realname": "In melāhuac tōcāitl mopehpenaliztli.\nIntlā ticmaca, momitztlaīxcoyantīz in motequi.",
        "prefs-help-email-required": "Tihuīquilia quihcuiloa mo e-mailcān.",
        "prefs-signature": "Motōcā",
        "userrights-user-editname": "Xihcuiloa cē tlatequitiltilīltōcāitl:",
-       "editusergroup": "Tiquimpatlāz tlatequitiltilīlli olōlli",
+       "editusergroup": "Tiquimpatlāz {{GENDER:$1|tlatequitiltilīlli}} īolōl",
        "userrights-editusergroup": "Tiquimpatlāz tlatequitiltilīlli olōlli",
-       "saveusergroups": "Tiquimpiyāz tlatequitiltilīlli olōlli",
+       "saveusergroups": "Tiquimpiyāz {{GENDER:$1|tlatequitiltilīlli}} īolōl",
        "userrights-groupsmember": "Olōlco:",
        "userrights-reason": "Īxtlamatiliztli:",
        "userrights-no-interwiki": "Ahmo tihuelīti ticpatla tlatequitiltilīlli huelītiliztli occequīntīn huiquipan.",
        "rightslog": "Tlatequitiltilīlli huelītiliztli tlahcuilōlloh",
        "action-read": "xāmapōhua inīn tlaīxtli",
        "action-edit": "xicpatla inīn tlaīxtli",
-       "action-createpage": "xicchīhua tlaīxtli",
-       "action-createtalk": "tiquinchīhuāz tēixnāmiquiliztli zāzaniltin",
+       "action-createpage": "xicchīhua inīn āmatl",
+       "action-createtalk": "xicchīhuā inīn tēixnāmiquiliztli zāzaniltin",
        "action-createaccount": "ticchīhuaz inīn tlatequitiltilīlli īcuentah",
        "action-move": "ticpatlāz inīn zāzanilli",
        "action-move-subpages": "tiquimpatlāz inīn zāzanilli īhuān zāzaniltōn",
        "newpageletter": "Y",
        "boteditletter": "T",
        "number_of_watching_users_pageview": "[$1 tlatequitiltilīlli {{PLURAL:$1|tlachiya|tlachiyah}}]",
-       "rc_categories_any": "Zāzo",
+       "rc_categories_any": "Zāzo in tlaihittalli",
        "newsectionsummary": "Yancuīc tlahtōltzintli: /* $1 */",
        "recentchangeslinked": "Tlapatlaliztli tzonhuilizpan",
        "recentchangeslinked-feed": "Tlapatlaliztli tzonhuilizpan",
        "whatlinkshere-next": "{{PLURAL:$1|niman|$1 niman}}",
        "whatlinkshere-links": "← tzòwilistìn",
        "whatlinkshere-hideredirs": "$1 tlacuepaliztli",
-       "whatlinkshere-hidelinks": "$1 tzòwilistìn",
+       "whatlinkshere-hidelinks": "$1 tzonhuiliztli",
        "whatlinkshere-hideimages": "$1 tlahcuilōltzonhuīliztli",
        "whatlinkshere-filters": "Tlatzetzelōni",
        "blockip": "Tiquitzacuilīz tlatequitiltilīlli",
        "importbadinterwiki": "Ahcualli interhuiqui tzonhuiliztli",
        "import-upload": "Tiquinquetzāz XML tlahcuilōlli",
        "importlogpage": "Tiquincōhuāz tlahcuilōlloh",
-       "tooltip-pt-userpage": "Notlatequitiltilīlzāzanil",
-       "tooltip-pt-mytalk": "Mozānīl",
-       "tooltip-pt-preferences": "Mopanitlatlālīl",
+       "tooltip-pt-userpage": "{{GENDER:|Motlatequitiltilīlzāzanil}}",
+       "tooltip-pt-mytalk": "{{GENDER:|Motēīxnāmiquiliztli}}",
+       "tooltip-pt-preferences": "{{GENDER:|Motlaēlēhuiliz}}",
        "tooltip-pt-watchlist": "Zāzaniltin tiquintlachiya ic tlapatlaliztli",
-       "tooltip-pt-mycontris": "Notlahcuilōl",
+       "tooltip-pt-mycontris": "{{GENDER:|Motlahcuilōl}}",
        "tooltip-pt-login": "Tihuelīti timocalaqui, tēl ahmo tihuīquilia.",
        "tooltip-pt-logout": "Tiquīzāz",
        "tooltip-ca-talk": "Inīn tlahcuilōlli zānīllī ītechcopa",
        "tooltip-t-recentchangeslinked": "Yancuic tlapatlaliztli inīn zāzanilhuīcpa moquintzonhuilia",
        "tooltip-feed-rss": "RSS tlachicāhualiztli inīn zāzaniltechcopa",
        "tooltip-feed-atom": "Atom tlachicāhualiztli inīn zāzaniltechcopa",
-       "tooltip-t-contributions": "Xiquitta inīn tlatequitiltilīlli ītlahcuilōl",
+       "tooltip-t-contributions": "Tlapōhualmatl ītechpa {{GENDER:$1|inīn tlatequitiltilīlli}} ītlahcuilōl",
        "tooltip-t-emailuser": "Tiquihcuilōz inīn tlatequitiltililhuīc",
        "tooltip-t-upload": "Tiquinquetzāz tlahcuilōlli",
        "tooltip-t-specialpages": "Ìntlapòpòwaltekpànal mochtìn in nònkuâkìskàtlaìxtlapaltìn",
        "tooltip-t-print": "Tepoztlahcuilōlli",
        "tooltip-ca-nstab-main": "Xiquitta in tlamantlaīxtli",
        "tooltip-ca-nstab-user": "Xiquitta tlatequitiltilīlli īzāzanil",
-       "tooltip-ca-nstab-special": "Inìn sè nònkuâkìskàtlaìxtlapalli, yêìka awel nemàtilòs moyêyèktlàlis in tlaìxtlapalli",
+       "tooltip-ca-nstab-special": "Inīn nōncuahquīzqui āmatl, auh ahmohuelitizpatla",
        "tooltip-ca-nstab-project": "Xiquitta in tlatequipanōllaīxtli",
        "tooltip-ca-nstab-image": "Xiquittāz īxipzāzanilli",
        "tooltip-ca-nstab-mediawiki": "Xiquitta in tlahcuilōltzin",
index 9420fe4..bf9f083 100644 (file)
        "yourpasswordagain": "Têng phah bi̍t-bé:",
        "createacct-yourpasswordagain": "Khak-jīn bi̍t-bé",
        "createacct-yourpasswordagain-ph": "Koh phah chi̍t-pái bi̍t-bé",
-       "remembermypassword": "Tī chit ê liû-lám-khì kì góa ê teng-ji̍p chu-liāu.(siōng chē kì $1 {{PLURAL:$1|kang|kang}})",
        "userlogin-remembermypassword": "Kì-lo̍k goá teng-ji̍p--ê chu-liāu",
        "userlogin-signwithsecure": "用安全連線",
        "yourdomainname": "你的網域:",
index 6624844..5a1a330 100644 (file)
        "tagline": "'A {{SITENAME}}.",
        "help": "Ajùto",
        "search": "Truova",
+       "search-ignored-headings": " #<!-- lassa sta linea comme sta --> <pre>\n# Testate ca se sarranno gnurate int' 'a ricerca.\n# Cagnamiente a chesto addeventarranno affettive quanno 'a paggena sarrà innecizzata.\n# Vuje putite forzà 'a reinnecezzazzione d' 'a paggena facenno nu cagnamiento abbacante.\n# 'A sintasse è 'a seguente:\n#   * Ogneccosa 'a 'o carattere \"#\" 'nzegna 'a fine d' 'a linea è 'nu cummanno\n#   * Ogne linea chiena è 'o titolo esatto 'a gnurà, case e tutteccose\nRiferimente\nJonte 'e fore\nVide pure\n #</pre> <!-- lassa sta linea comme sta  -->",
        "searchbutton": "Truova",
        "go": "Vàje",
        "searcharticle": "Vàje",
        "yourpasswordagain": "Ripete 'a password:",
        "createacct-yourpasswordagain": "Cunferma password",
        "createacct-yourpasswordagain-ph": "'Nserisce 'e nuovo 'a password",
-       "remembermypassword": "Allicuordate d\"a password (for a maximum of $1 {{PLURAL:$1|day|days}})",
        "userlogin-remembermypassword": "Mantienime cullegato",
        "userlogin-signwithsecure": "Usa na conessione sicura",
+       "cannotlogin-title": "Nun se pò trasì",
+       "cannotlogin-text": "Trasì nun è possibbele mò.",
        "cannotloginnow-title": "Nun se pò trasì mò",
        "cannotloginnow-text": "'A connessione nun è possibbele quanno s'ausa $1.",
+       "cannotcreateaccount-title": "Nun se ponno crià cunte",
+       "cannotcreateaccount-text": "'A criazione diretta 'e cunte è stutata int'a stu wiki.",
        "yourdomainname": "Spiecà 'o dumminio",
        "password-change-forbidden": "Nun se ponno cagnà 'e password ncopp'a sta wiki.",
        "externaldberror": "Ce sta n'errore ch' 'e server d'autenticazione esterno, o pure nun v'è permesso accedere all'aghiurnamento d' 'o cunto sterno vuosto.",
        "passwordreset-emailelement": "Nomme utente: \n$1\n\nPassword temporanea: \n$2",
        "passwordreset-emailsentemail": "Si chesto fosse nu cunto e-mail suoccio a 'o cunto vuost, allora buò dicere ca se mannarrà na mmasciata e-mail pe' riabbià 'a password.",
        "passwordreset-emailsentusername": "Si esistesse nu cunto e-mail suòccio a stu nomme utente, allora se mannarrà na mmasciata pe' riabbià 'a password.",
-       "passwordreset-emailsent-capture": "Na mmasciata e-mail pe' riabbià 'a password è stata mannata, chista mmasciata 'a putite vedé ccà abbascio.",
-       "passwordreset-emailerror-capture": "Na mmasciata e-mail pe' riabbià 'a password è stata mannata, 'a putite vedé ccà abbascio, ma aita sapé ca nun s'è mannata a {{GENDER:$2|l'utente}} pecché c'è stato cocch'errore: $1",
        "passwordreset-emailsent-capture2": "L'email 'e reimpostazione d' 'a password {{PLURAL:$1|è stata mannata|so' state mannate}}. {{PLURAL:$1|'O nomme|L'elenco 'e nomme}} utente e password è mmustato ccà.",
        "passwordreset-emailerror-capture2": "'O mannà 'email {{GENDER:$2|a ll'utente}} guastaje: $1. {{PLURAL:$3|'O nomme|L'elenco 'e nomme}} utente e password se ffà vedé ccà.",
        "passwordreset-nocaller": "Nu chiammate s'avess'a dà",
        "passwordreset-nodata": "Nun è stato fornito né nomme utente né indirizzo 'e posta email",
        "changeemail": "Cagna o lèva l'indirizzo e-mail",
        "changeemail-header": "Ghienchete stu modulo pe' puté cagnà 'o indirizzo e-mail d' 'o vuosto. Si vuje vulite luvà e 'o scucchià l'associazione 'e cocche cunto mail d' 'o cunto vuosto, lassate 'o cunto e-mail nuovo abbacante quanno mannarrate stu modulo.",
-       "changeemail-passwordrequired": "Avit'a miette 'a password vuosto pe' dà a cunferma 'e stu cagnamiento.",
        "changeemail-no-info": "Avite 'a trasì ('o login) pe ffà l'acciesso a sta paggena direttamente.",
        "changeemail-oldemail": "Indirizzo email 'e mmò:",
        "changeemail-newemail": "Indirizzo e-mail nuovo:",
        "content-model-css": "CSS",
        "content-json-empty-object": "Oggetto abbacante",
        "content-json-empty-array": "Array abbacante",
+       "deprecated-self-close-category": "Paggene ausanno nu tag HTML auto-nchiuse nun valido",
+       "deprecated-self-close-category-desc": "'A paggena cuntenesse tag HTML auto-nchiuse nun valide, comme <code>&lt;b/></code> o <code>&lt;span/></code>. 'O cumpurtamento 'e chiste cagnarrà priesto pe' se ffà cuerente a le specifiche HTML5, è pecchesto ca mò nun è cunzigliato (deprecato) ll'uso 'e chiste dint' 'o wikitesto.",
        "duplicate-args-warning": "<strong>Attenziò:</strong> [[:$1]] sta chiammanno [[:$2]] cu cchiù 'e nu volore p' 'o parametro \"$3\". Surtanto ll'urdemo valore s'auserrà.",
        "duplicate-args-category": "Paggene c'ausano argomiente dupprecate dint' 'e chiammate a 'e mudelle",
        "duplicate-args-category-desc": "'A paggena tene chiammate a mudelle c'ausassero argomiente dupprecate, comme p'esempio <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> o <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Pare c' 'o cagnamiento s'è già fatto turnà arreto.",
        "undo-summary": "Canciella 'o cagnamiento $1 'e [[Special:Contributions/$2|$2]] ([[User talk:$2|Chiàcchiera]])",
        "undo-summary-username-hidden": "Annullato 'o cagnamiento $1 pe n'utente annascunnuto",
-       "cantcreateaccounttitle": "Nun se può crià cunto",
        "cantcreateaccount-text": "'A riggistraziona è stata bloccata 'a l'utente [[User:$3|$3]] 'a st'innerizzo IP ('''$1''').\n\n'O mutivo dato 'a $3 è chistu ccà: ''$2''",
        "cantcreateaccount-range-text": "'A criazione 'e cunte 'a ll'indirizze int'o ntervallo <strong>$1</strong>, che cummiglia 'o vuosto (<strong>$4</strong>), è stata bloccata 'a l'utente [[User:$3|$3]].\n\n'O mutivo dato 'a $3 è chistu ccà: <em>$2</em>",
        "viewpagelogs": "Vide 'e log azzeccate a sta paggena",
        "grant-group-high-volume": "Secuta attività 'e volume massivo",
        "grant-group-customization": "Personalizzaziona e preferenze",
        "grant-group-administration": "Secuta aziune ammenistrative",
+       "grant-group-private-information": "Tràse dint' 'e date private ncopp'a tte",
        "grant-group-other": "Attività differénte",
        "grant-blockusers": "Blocca e sblocca utente",
        "grant-createaccount": "Crìa cunte",
        "grant-highvolume": "Cagnamiente massive",
        "grant-oversight": "Annascunne utente e scancèlla 'e verziune",
        "grant-patrol": "Nzègna 'e cagnamiente a 'e paggene comme verificate",
+       "grant-privateinfo": "Tràse 'a 'e nfurmaziune private",
        "grant-protect": "Prutegge e sprutegge paggene",
        "grant-rollback": "Torna arrèto 'e cagnamiente a 'e paggene",
        "grant-sendemail": "Manna na mail a ll'at'utente",
        "action-applychangetags": "appreca tag pe' tramente ca se fanno 'e cagnamiente vuoste",
        "action-changetags": "azzecca o lèva tag a caso dint'a verziune nnividuale e riggistre 'e log",
        "action-deletechangetags": "scancellare 'e tag d' 'o database",
+       "action-purge": "agghiuorna sta paggena",
        "nchanges": "$1 {{PLURAL:$1|cagnamiento|cagnamiente}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|'a ll'urdema visita}}",
        "enhancedrc-history": "cronologgia",
        "file-thumbnail-no": "Stu filename accummencia pe' <strong>$1</strong>.\nPare ca ce sta n'immaggene piccerilla <em>(thumbnail)</em>.\nSi tiene st'immaggene 'n risoluzione origginale, pe' piacere carrecatela. Si nò, vedite 'e cagnà 'o nomme d' 'o file.",
        "fileexists-forbidden": "Nu file cu stu nomme esiste già, e nun se può sovrascrivere.<br/>\nPe' piacere turnat'arreto e cagnàte 'o nomme p' 'o turnà a carrecà.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Nu file cu stu nomme esiste già dint'a l'archivio 'e risorse multimediale spartute. Si vulite carrecà 'o file ancora, turnat'arreto e cagnate 'o nomme p' 'o turnà a carrecà.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "'O file carrecato è nu dupprecato eguale eguale d' 'a verziona 'e mò 'e <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "'O file carrecato è nu duprecato eguale eguale 'a {{PLURAL:$2|na verziona 'e primma|na quantità 'e verziune 'e primma}} 'e <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Stu file è nu duplicato {{PLURAL:$1|d' 'o|d' 'e}} file ccà abbascio:",
        "file-deleted-duplicate": "Nu file identico a chesto ([[:$1]]) è stato scancellato prima. Cuntrullate 'a cronologgia d' 'e scancellamiente apprimma d' 'o carrecà n'ata vota.",
        "file-deleted-duplicate-notitle": "Nu file eguale a stu file è stato previamente scancellato, e 'o titolo è stato sbaccantato. Chierete a coccheruno ca tenesse 'a posibbelità 'e vedé file luvate e sbaccantate pe' sapé nquale situazione ve truvate apprimma d' 'o ffà carrecà n'ata vota.",
        "uploadstash-errclear": "'A pulezzia d' 'e file scassaje.",
        "uploadstash-refresh": "Agghiuorna l'elenco d' 'e file",
        "uploadstash-thumbnail": "vide miniatura",
+       "uploadstash-exception": "Nun s'è pututo sarvà 'a càrreca dint' 'a stash ($1): \"$2\".",
        "invalid-chunk-offset": "Distanza d' 'a parte nun valida",
        "img-auth-accessdenied": "Acciesso negato",
        "img-auth-nopathinfo": "PATH_INFO mancante.\n'O server nun è mpustato pe' passà sta nfurmazione.\nPuò darse ca, essenno basato ncopp'a CGI, nun putesse suppurtà img_auth.\nVide https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization",
        "filerevert-submit": "Arrepiglia",
        "filerevert-success": "'''[[Media:$1|$1]]''' è stat'arripigliato â verziona [$4 d' 'e $3 d' 'o $2].",
        "filerevert-badversion": "Nun ce sta na virziona lucale 'e stu file cu l'orario addimannato.",
+       "filerevert-identical": "'A verziona 'e mò d' 'o file è già eguale eguale a chilla scigliuta.",
        "filedelete": "Scancella $1",
        "filedelete-legend": "Scancella 'o file",
        "filedelete-intro": "State pe' scancellà 'o file '''[[Media:$1|$1]]''' cu tutta 'a cronologgia 'e chisto.",
        "watchnologin": "Acciesso nun affettuato",
        "addwatch": "Miette dint' 'a l'elenco 'e paggene cuntrullate",
        "addedwatchtext": "'A paggena \"[[:$1]]\" e 'a paggena 'e chiacchiera è stata azzeccata dint'a l'elenco 'e [[Special:Watchlist|paggene cuntrullate]].",
+       "addedwatchtext-talk": "\"[[:$1]]\" e 'a paggena 'e chiacchiera suòccia è stata azzeccata dint'a l'elenco 'e [[Special:Watchlist|paggene cuntrullate]] vuosto.",
        "addedwatchtext-short": "Chista paggena \"$1\" è stata azzeccata a l'elenco 'e paggene cuntrullate.",
        "removewatch": "Leva 'a l'elenco 'e paggene cuntrullate",
-       "removedwatchtext": "\"[[:$1]]\" 'e 'a paggena 'e chiacchiera soja so' state scancellata 'a l'elenco [[Special:Watchlist|'e paggene cuntrullate]] vuosto.",
+       "removedwatchtext": "\"[[:$1]]\" e 'a paggena 'e chiacchiera soja so' state scancellate 'a l'elenco [[Special:Watchlist|'e paggene cuntrullate]] vuosto.",
+       "removedwatchtext-talk": "\"[[:$1]]\" e 'a paggena 'e chiacchiera soja so' state luvate 'a l'elenco [[Special:Watchlist|'e paggene cuntrullate]] vuosto.",
        "removedwatchtext-short": "Chista paggena \"$1\" è stata luvata a l'elenco 'e paggene cuntrullate.",
        "watch": "Secuta",
        "watchthispage": "Tiene d'uocchio sta paggena",
        "rollbacklinkcount-morethan": "annulla cchiù 'e {{PLURAL:$1|nu cagnamiento|$1 cagnamiente}}",
        "rollbackfailed": "Annullamento fallito",
        "rollback-missingparam": "Parammetre obbligate mancante int' 'a richiesta.",
+       "rollback-missingrevision": "Nun se ponno carrecà 'e date d' 'a verziuna.",
        "cantrollback": "Nun se può annullà stu cagnamiento;\nsapite ca l'urdemo autore è stato pure sul'isso a faticà dint'a sta paggena (nun ce sta n'at'autore).",
        "alreadyrolled": "Nun se può turna arreto a l'urdemo cagnamiento [[:$1]] 'a [[User:$2|$2]] ([[User talk:$2|Chiacchiera]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\ncocch'ato ha cagnato o annullato 'a paggena già.\n\nL'urdemo cangamiento d' 'a paggena fuje 'a [[User:$3|$3]] ([[User talk:$3|Chiacchiera]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "'O riepilego d' 'o cagnamiento era: <em>$1</em>.",
        "mw-widgets-dateinput-no-date": "Nisciuna data scigliuta",
        "mw-widgets-titleinput-description-new-page": "'a pàggene nun esiste ancore",
        "mw-widgets-titleinput-description-redirect": "redirezionate ncopp' a $1",
-       "api-error-blacklisted": "Pe' piacere sciglite nu titolo differente e descrittivo.",
        "sessionmanager-tie": "Nun se ponno cumbinà 'e tipe 'e richiesta 'autenticaziona: $1.",
        "sessionprovider-generic": "$1 sessiune",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sessiune basate ncopp' 'e cookie",
        "log-action-filter-newusers": "Tipo e' criazione utenza:",
        "log-action-filter-patrol": "Tipo 'e verifica:",
        "log-action-filter-protect": "Tipo 'e protezione:",
-       "log-action-filter-rights": "Tipo 'e cagnamiento 'e deritte",
-       "log-action-filter-suppress": "Tipo 'e suppressione",
+       "log-action-filter-rights": "Tipo 'e cagnamiento 'e deritte:",
+       "log-action-filter-suppress": "Tipo 'e suppressione:",
        "log-action-filter-upload": "Tipo 'e carreca:",
        "log-action-filter-all": "Tutte",
        "log-action-filter-block-block": "Blocco",
        "authprovider-confirmlink-message": "Verenno 'e tentative d'acciesso mò, l'utente ccà putessero avé nu cullegamento c' 'o cunto wiki d' 'o vuosto. A ffà cullegamento premmettesse appiccià 'o sistema 'e trasuta pe' bbìa 'e sti cunte. Pe' piacere sciglite 'e cunte addò vulite fà cullegamento.",
        "authprovider-confirmlink-request-label": "Cunte ca s'avesser'a cullegà",
        "authprovider-confirmlink-success-line": "$1: cullegato e apposto.",
-       "authprovider-confirmlink-failed": "'O cullegamento 'e ll'utenza nun è ngarrato sano sano: $1"
+       "authprovider-confirmlink-failed": "'O cullegamento 'e ll'utenza nun è ngarrato sano sano: $1",
+       "authprovider-confirmlink-ok-help": "Cuntinuà aropp'a fà veré 'e mmasciate 'errore 'e cullegamento.",
+       "authprovider-resetpass-skip-label": "Passa 'a vacca",
+       "authprovider-resetpass-skip-help": "Passa 'a vacca 'mpustazion' 'e password.",
+       "authform-nosession-login": "'O prucesso 'autenticazione iette buono, ma 'o navigatóre d' 'o tuojo nun \"s'arricuorda\" si site trasuto/a.\n\n$1",
+       "authform-nosession-signup": "'O cunto è stato criato buono, ma 'o navigatóre d' 'o tuojo nun \"s'arricuorda\" si site trasuto/a.\n\n$1",
+       "authform-newtoken": "Token mancante. $1",
+       "authform-notoken": "Token mancante",
+       "authform-wrongtoken": "Token errato",
+       "specialpage-securitylevel-not-allowed-title": "Nun permesso",
+       "authpage-cannot-login": "Nun se pò accummencià a trasì.",
+       "authpage-cannot-login-continue": "Nun se pò cuntinuà a trasì. 'A sessione vosta fosse scaduta.",
+       "authpage-cannot-create": "Nun se pò accummencià 'a criazione 'utenza."
 }
index c381250..0eb17e9 100644 (file)
@@ -48,7 +48,8 @@
                        "Tarjeimo",
                        "Matma Rex",
                        "SuperPotato",
-                       "Nemo bis"
+                       "Nemo bis",
+                       "Telaneo"
                ]
        },
        "tog-underline": "Strek under lenker:",
@@ -66,6 +67,7 @@
        "tog-watchdefault": "Legg til sider og filer jeg endrer på i min overvåkingsliste",
        "tog-watchmoves": "Legg til sider og filer jeg flytter til min overvåkingsliste",
        "tog-watchdeletion": "Legg til sider og filer jeg sletter i min overvåkingsliste",
+       "tog-watchuploads": "Legg til nye filer jeg laster opp i overvåkningslisten min",
        "tog-watchrollback": "Legg til sider hvor jeg har utført tilbakestilling i min overvåkningsliste",
        "tog-minordefault": "Merk i utgangspunktet alle redigeringer som mindre",
        "tog-previewontop": "Vis forhåndsvisningen over redigeringsboksen",
        "moredotdotdot": "Mer …",
        "morenotlisted": "Denne lista er ufullstendig.",
        "mypage": "Min brukerside",
-       "mytalk": "Min diskusjonsside",
+       "mytalk": "Diskusjon",
        "anontalk": "Brukerdiskusjon",
        "navigation": "Navigasjon",
        "and": "&#32;og",
        "tagline": "Fra {{SITENAME}}",
        "help": "Hjelp",
        "search": "Søk",
+       "search-ignored-headings": " #<!-- la denne linjen stå akkurat som den er --> <pre>\n# Overskrifter som vil bli ignorert ved søking.\n# Endringer på denne siden trer i kraft ved neste indeksering.\n# Du kan fremtvinge en reindeksering av en gitt side ved å gjøre en nullredigering.\n# Syntaksen er som følger:\n#   * Alt fra et \"#\"-tegn til slutten av en linje er en kommentar.\n#   * Enhver ikke-tom linje regnes som en ordrett tittel (inkludert skille mellom store og små bokstaver) som skal ignoreres.\nReferanser\nKilder\nEksterne lenker\nSe også\n #</pre> <!-- la denne linjen stå akkurat som den er -->",
        "searchbutton": "Søk",
        "go": "Gå",
        "searcharticle": "Gå",
        "yourpasswordagain": "Gjenta passord",
        "createacct-yourpasswordagain": "Bekreft passord",
        "createacct-yourpasswordagain-ph": "Gjenta passordet",
-       "remembermypassword": "Husk meg på denne datamaskinen (i maks $1 {{PLURAL:$1|dag|dager}})",
        "userlogin-remembermypassword": "Hold meg innlogget",
        "userlogin-signwithsecure": "Logg inn med sikker tjener",
        "cannotloginnow-title": "Kan ikke logge inn nå",
        "password-change-forbidden": "Du kan ikke endre passord på denne wikien.",
        "externaldberror": "Det var en ekstern autentifiseringsfeil, eller du kan ikke oppdatere din eksterne konto.",
        "login": "Logg inn",
+       "login-security": "Bekreft identiteten din",
        "nav-login-createaccount": "Logg inn eller opprett en konto",
        "userlogin": "Logg inn eller opprett en konto",
        "userloginnocreate": "Logg inn",
        "userlogin-resetpassword-link": "Glemt passordet?",
        "userlogin-helplink2": "Hjelp med innlogging",
        "userlogin-loggedin": "Du er allerede logget inn som {{GENDER:$1|$1}}.\nBruk skjemaet nedenfor for å logge inn som en annen bruker.",
+       "userlogin-reauth": "Du må logge inn igjen for å bekrefte at du er {{GENDER:$1|$1}}.",
        "userlogin-createanother": "Opprett ny konto",
        "createacct-emailrequired": "E-postadresse",
        "createacct-emailoptional": "E-postadresse (valgfritt)",
        "createacct-email-ph": "Fyll inn e-postadressen din",
        "createacct-another-email-ph": "Fyll inn epostadressen",
        "createaccountmail": "Bruk et midlertidig, tilfeldig passord, og send det til angitt e-postadresse",
+       "createaccountmail-help": "Kan brukes til å opprette en konto for en annen person uten at du får vite passordet.",
        "createacct-realname": "Virkelig navn (valgfritt)",
        "createaccountreason": "Årsak:",
        "createacct-reason": "Årsak",
        "createacct-reason-ph": "Hvorfor lager du en annen bruker",
+       "createacct-reason-help": "Beskjed vist i kontoopprettelsesloggen",
        "createacct-submit": "Opprett konto",
        "createacct-another-submit": "Opprett konto",
+       "createacct-continue-submit": "Fortsett kontooppretting",
+       "createacct-another-continue-submit": "Fortsett kontooppretting",
        "createacct-benefit-heading": "{{SITENAME}} er laget av folk som deg.",
        "createacct-benefit-body1": "{{PLURAL:$1|redigering|redigeringer}}",
        "createacct-benefit-body2": "{{PLURAL:$1|side|sider}}",
        "nocookiesnew": "Din brukerkonto er nå opprettet, men du har ikke logget på. {{SITENAME}} bruker informasjonskapsler («cookies») for å logge brukere på og du har slått dem av. Slå dem på for å kunne logge på med ditt nye brukernavn og passord.",
        "nocookieslogin": "{{SITENAME}} bruker informasjonskapsler («cookies») for å logge brukere på og du har slått dem av. Slå dem på og prøv igjen.",
        "nocookiesfornew": "Brukerkontoen ble ikke opprettet siden vi ikke kunne bekrefte dens kilde.\nKontroller at du har aktivert informasjonskapsler, oppdater siden og prøv igjen.",
+       "createacct-loginerror": "Kontoen ble opprettet, men du kunne ikke logges inn automatisk. Gå videre til [[Special:UserLogin|manuell innlogging]].",
        "noname": "Du har ikke oppgitt et gyldig brukernavn.",
-       "loginsuccesstitle": "Du er nå logget inn",
+       "loginsuccesstitle": "Logget inn",
        "loginsuccess": "Du er nå logget inn på {{SITENAME}} som «$1».",
        "nosuchuser": "Det eksisterer ingen bruker ved navn «$1».\nMerk at det skilles mellom store og små bokstaver.\nSjekk stavemåten eller [[Special:CreateAccount|opprett en ny konto]].",
        "nosuchusershort": "Det finnes ingen bruker ved navn «$1». Kontroller stavemåten.",
        "createacct-another-realname-tip": "Det er frivillig å oppgi ditt egentlige navn.\nHvis du velger å oppgi det, vil det blir anvendt for å gi deg som bruker anerkjennelse",
        "pt-login": "Logg inn",
        "pt-login-button": "Logg inn",
+       "pt-login-continue-button": "Fortsett innlogging",
        "pt-createaccount": "Opprett konto",
        "pt-userlogout": "Logg ut",
        "php-mail-error-unknown": "Ukjent feil i PHPs mail()-funksjon",
        "newpassword": "Nytt passord:",
        "retypenew": "Gjenta nytt passord:",
        "resetpass_submit": "Angi passord og logg inn",
-       "changepassword-success": "Passordet ditt ble korrekt endret!",
+       "changepassword-success": "Passordet ditt er endret!",
        "changepassword-throttled": "Du har foretatt for mange nylige innloggingsforsøk.\nVær vennlig å vente $1 før du prøver igjen.",
        "botpasswords": "Robotpassord",
-       "botpasswords-summary": "<em>Robotpassord</em> gir tilgang til en brukerkonto via API uten å bruke hovedpassordet til kontoen. Brukerrettighetene kan bli begrenset ved bruk av dette passordet.\n\nHvis du ikke vet om du vil benytte dette, er det sannsynlig at du ikke bør fylle det ut. Det skal ikke være nødvendig for andre personer å be deg om å fylle ut dette for å gi det til de.",
+       "botpasswords-summary": "<em>Robotpassord</em> gir tilgang til en brukerkonto via API uten å bruke hovedpassordet til kontoen. Brukerrettighetene kan bli begrenset ved bruk av dette passordet.\n\nHvis du ikke vet om du vil benytte dette, er det sannsynlig at du ikke bør fylle det ut. Det skal ikke være nødvendig for andre personer å be deg om å fylle ut dette for å gi det til dem.",
        "botpasswords-disabled": "Robotpassord er deaktivert.",
        "botpasswords-no-central-id": "For å bruke robotpassord må du være logget inn med en sentralisert konto.",
        "botpasswords-existing": "Eksisterende robotpassord",
        "botpasswords-insert-failed": "Kunne ikke legge til robotnavnet \"$1\". Har det allerede blitt lagt til?",
        "botpasswords-update-failed": "Kunne ikke oppdatere robotnavnet \"$1\". Er det slettet?",
        "botpasswords-created-title": "Robotpassord opprettet",
-       "botpasswords-created-body": "Robotpassordet \"$1\" ble opprettet.",
+       "botpasswords-created-body": "Robotpassordet for boten «$1» til brukeren «$2» ble opprettet.",
        "botpasswords-updated-title": "Robotpassord oppdatert",
-       "botpasswords-updated-body": "Robotpassordet \"$1\" ble oppdatert.",
+       "botpasswords-updated-body": "Robotpassordet for boten «$1» til brukeren «$2» ble oppdatert.",
        "botpasswords-deleted-title": "Robotpassord slettet",
-       "botpasswords-deleted-body": "Robotpassordet \"$1\" ble slettet.",
+       "botpasswords-deleted-body": "Robotpassordet for boten «$1» til brukeren «$2» ble slettet.",
        "botpasswords-newpassword": "Det nye passordet for å logge inn med <strong>$1</strong> er <strong>$2</strong>. <em>Vennligst lagre dette for fremtidig referanse.</em>",
        "botpasswords-no-provider": "BotPasswordsSessionProvider er ikke tilgjengelig.",
        "botpasswords-restriction-failed": "Begrensninger for robotpassord tillater ikke denne innloggingen.",
        "botpasswords-invalid-name": "Det angitte brukernavnet inneholder ikke separasjonstegnet for robotpassord (\"$1\").",
        "botpasswords-not-exist": "Brukeren \"$1\" har ikke noe robotpassord for \"$2\".",
        "resetpass_forbidden": "Passord kan ikke endres",
+       "resetpass_forbidden-reason": "Passordene kan ikke endres: $1",
        "resetpass-no-info": "Du må være logget inn for å gå til denne siden direkte",
        "resetpass-submit-loggedin": "Endre passord",
        "resetpass-submit-cancel": "Avbryt",
-       "resetpass-wrong-oldpass": "Ugyldig midlertidig eller nåværende passord.\nDu kan ha allerede byttet passordet, eller bedt om et nytt midlertidig passord.",
+       "resetpass-wrong-oldpass": "Ugyldig midlertidig eller aktivt passord.\nDet kan tenkes at allerede har gjennomført et vellykket bytte av passord, eller bedt om et nytt midlertidig passord.",
        "resetpass-recycled": "Vær vennlig å endre passordet til noe annen enn gjeldende passord.",
        "resetpass-temp-emailed": "Du logget inn med en midlertidig kode sendt på e-post.\nFor å avslutte innloggingen må du angi et nytt passord her:",
        "resetpass-temp-password": "Midlertidig passord:",
        "passwordreset-emailelement": "Brukernavn: \n$1\n\nMidlertidig passord: \n$2",
        "passwordreset-emailsentemail": "Hvis denne epostadressen er koblet til din konto, så vil det bli sendt en epost om tilbakestilling av passord.",
        "passwordreset-emailsentusername": "Hvis det finnes en epostadresse knyttet til dette brukernavnet, vil en epost med informasjon om tilbakestilling av passord bli sendt.",
-       "passwordreset-emailsent-capture": "Passordtilbakestillingseposten vist under har blitt sendt ut.",
-       "passwordreset-emailerror-capture": "En passordtilbakestillingsepost ble laget, men det lyktes ikke å sende denne til {{GENDER:$2|brukeren}}: $1",
+       "passwordreset-emailsent-capture2": "{{PLURAL:$1|E-post}} om passordtilbakestilling har blitt sendt. {{PLURAL:$1|Brukernavnet og passordet|Listen over brukernavn og passord}} vises under.",
+       "passwordreset-emailerror-capture2": "Kunne ikke sende e-post til {{GENDER:$2|brukeren}}: $1 {{PLURAL:$3|Brukernavnet og passordet|Listen over brukernavn og passord}} vises under.",
+       "passwordreset-nocaller": "En bruker må angis",
+       "passwordreset-nosuchcaller": "Brukeren finnes ikke: $1",
+       "passwordreset-ignored": "Passordtilbakestillingen ble ikke håndtert. Har ingen leverandør blitt konfigurert?",
+       "passwordreset-invalideamil": "Ugyldig e-postadresse",
+       "passwordreset-nodata": "Verken et brukernavn eller en e-postadresse ble oppgitt",
        "changeemail": "Endre eller fjerne epostadresse",
        "changeemail-header": "Fyll ut dette skjemaet for å bytte din epost-adresse. Hvis du vil fjerne epostadressen fra din konto, kan du la ny epostadresse-feltet være tomt når.",
-       "changeemail-passwordrequired": "Du må skrive inn passordet ditt for å bekrefte denne endringen.",
        "changeemail-no-info": "Du må være innlogget for å få direkte tilgang til denne siden.",
        "changeemail-oldemail": "Nåværende e-postadresse:",
        "changeemail-newemail": "Ny e-postadresse:",
        "minoredit": "Dette er en mindre endring",
        "watchthis": "Overvåk denne siden",
        "savearticle": "Lagre siden",
+       "savechanges": "Lagre endringer",
+       "publishpage": "Publiser siden",
+       "publishchanges": "Publiser endringer",
        "preview": "Forhåndsvisning",
        "showpreview": "Forhåndsvisning",
        "showdiff": "Vis endringer",
        "accmailtext": "Et tilfeldig passord for [[User talk:$1|$1]] har blitt sendt til $2. Det kan endres på [[Special:ChangePassword|passordendringssiden]] under innlogging.",
        "newarticle": "(Ny)",
        "newarticletext": "Du har fulgt en lenke til en side som ikke finnes ennå.\nFor å opprette siden, begynn å skrive i boksen under (se [$1 hjelpesiden] for mer informasjon).\nOm du havnet her ved en feil, trykk '''tilbake''' i nettleseren.",
-       "anontalkpagetext": "----\n''Dette er en diskusjonsside for en uregistrert bruker som ikke har opprettet konto eller ikke er logget inn.\nVi er derfor nødt til å bruke den numeriske IP-adressen til å identifisere ham eller henne.\nEn IP-adresse kan være delt mellom flere brukere.\nHvis du er en uregistrert bruker og synes at du har fått irrelevante kommentarer på en slik side, [[Special:CreateAccount|opprett en konto]] eller [[Special:UserLogin|logg inn]] så vi unngår fremtidige forvekslinger med andre uregistrerte brukere.''",
+       "anontalkpagetext": "----\n<em>Dette er en diskusjonsside for en anonym bruker som ikke har opprettet konto enda, eller som ikke bruker den.</em>\nVi er derfor nødt til å bruke den numeriske IP-adressen til å identifisere ham eller henne.\nEn IP-adresse kan være delt mellom flere brukere.\nHvis du er en anonym bruker og opplever å få irrelevante kommentarer rettet mot deg, [[Special:CreateAccount|opprett en konto]] eller [[Special:UserLogin|logg inn]] for å unngå fremtidige forvekslinger med andre anonyme brukere.",
        "noarticletext": "Det er for tiden ingen tekst på denne siden.\nDu kan [[Special:Search/{{PAGENAME}}|søke etter denne sidetittelen]] på andre sider,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} søke i relaterte logger],\neller [{{fullurl:{{FULLPAGENAME}}|action=edit}} opprette siden]</span>.",
        "noarticletext-nopermission": "Det er for tiden ingen tekst på denne siden.\nDu kan [[Special:Search/{{PAGENAME}}|søke etter sidens tittel]] blant andre sider, eller <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} søke i relevante logger]</span>, men du har ikke tillatelse til å opprette denne siden.",
        "missing-revision": "Revisjonen #$1 av siden med navnet \"{{FULLPAGENAME}}\" eksisterer ikke.\n\nDette skyldes som regel at en gammel historikklenke er fulgt til en side som er slettet.\nDetaljer kan finnes i [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} sletteloggen].",
        "userpage-userdoesnotexist": "Brukerkontoen «$1» er ikke registrert.\nSjekk om du ønsker å opprette/redigere denne siden.",
        "userpage-userdoesnotexist-view": "Kontoen «$1» er ikke registrert.",
        "blocked-notice-logextract": "Denne brukeren er for tiden blokkert.\nSiste blokkeringsloggelement kan sees nedenfor.",
-       "clearyourcache": "'''Merk:''' Etter lagring vil det kanskje være nødvendig at nettleseren sletter hurtiglageret sitt for at endringene skal tre i kraft.\n* '''Firefox / Safari:''' Hold ''Shift'' mens du klikker på ''Oppdater'' eller trykk ''Ctrl-F5'' eller ''Ctrl-R'' (''⌘-R'' på en Mac)\n* '''Google Chrome:''' Trykk ''Ctrl-Shift-R'' (''⌘-Shift-R'' på en Mac)\n* '''Internet Explorer:''' Hold ''Ctrl'' mens du klikker på ''Oppdater'' eller trykk ''Ctrl-F5''\n* '''Opera:''' Tøm hurtiglageret i ''Verktøy → Innstillinger''",
+       "clearyourcache": "<strong>Merk:</strong> Etter lagring vil det kanskje være nødvendig at nettleseren sletter hurtiglageret sitt for at endringene skal tre i kraft.\n* <strong>Firefox / Safari:</strong> Hold <em>Shift</em> mens du klikker på <em>Oppdater</em> eller trykk <em>Ctrl-F5</em> eller <em>Ctrl-R</em> (<em>⌘-R</em> på en Mac)\n* <strong>Google Chrome:</strong> Trykk <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> på en Mac)\n* <strong>Internet Explorer:</strong> Hold <em>Ctrl</em> mens du klikker på <em>Oppdater</em> eller trykk <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Tøm hurtiglageret i <em>Meny → Innstillinger</em> og deretter <em>Personvern & sikkerhet → Slett surfedata → Mellomlagrede bilder og filer</em>.",
        "usercssyoucanpreview": "'''Tips:''' Bruk «{{int:showpreview}}»-knappen for å teste din nye CSS før du lagrer.",
        "userjsyoucanpreview": "'''Tips:''' Bruk «{{int:showpreview}}»-knappen for å teste ditt nye JS før du lagrer.",
        "usercsspreview": "'''Husk at dette bare er en forhåndsvisning av din bruker-CSS og at den ikke er lagret!'''",
        "continue-editing": "Gå til redigeringsfeltet",
        "previewconflict": "Slik vil teksten i redigeringsvinduet se ut dersom du lagrer den.",
        "session_fail_preview": "'''Beklager! Klarte ikke å lagre redigeringen din på grunn av tap av øktdata.'''\n\nDu kan ha blitt logget ut. <strong>Sjekk at du fortsatt er innlogget og prøv igjen.</strong>\nOm det fortsetter å gå galt, prøv å [[Special:UserLogout|logge ut]] og så inn igjen, og sjekk at nettleseren din godtar informasjonskapsler fra denne siden.",
-       "session_fail_preview_html": "'''Beklager! Klarte ikke å lagre redigeringen din på grunn av tap av øktdata.'''\n\n''Fordi {{SITENAME}} har rå HTML slått på, er forhåndsvisningen skjult for å forhindre JavaScript-angrep.''\n\n'''Om dette er et legitimt redigeringsforsøk, prøv igjen. Om det da ikke fungerer, prøv å [[Special:UserLogout|logge ut]] og logge inn igjen.'''",
+       "session_fail_preview_html": "Beklager! Klarte ikke å lagre redigeringen din på grunn av tap av øktdata.\n\n<em>Fordi {{SITENAME}} har rå HTML slått på, er forhåndsvisningen skjult for å forhindre JavaScript-angrep.</em>\n\n<strong>Om dette er et legitimt redigeringsforsøk, prøv igjen.</strong> Om det fortsatt ikke fungerer, prøv å [[Special:UserLogout|logge ut]] og logge inn igjen og sjekk at nettleseren din godtar informasjonskapsler fra dette nettstedet.",
        "token_suffix_mismatch": "'''Redigeringen din har blitt avvist fordi klienten din ikke hadde punktasjonstegn i redigeringsteksten. Redigeringen har blitt avvist for å hindre ødeleggelse av artikkelteksten. Dette forekommer av og til når man bruker vevbaserte anonyme proxytjenester.'''",
        "edit_form_incomplete": "'''Deler av redigeringsskjemaet nådde ikke tjeneren; dobbelsjekk at redigeringen er korrekt og prøv igjen.'''",
        "editing": "Redigerer $1",
        "content-model-css": "CSS",
        "content-json-empty-object": "Tomt objekt",
        "content-json-empty-array": "Tom matrise",
+       "deprecated-self-close-category": "Sider som bruker ugyldige, balanserte HTML-tagger.",
+       "deprecated-self-close-category-desc": "Siden inneholder balanserte HTML-tagger slike som <code>&lt;b/></code> or <code>&lt;span/></code>. Oppførselen til disse vil snart endret for å være i samsvar med HTML-spesifikasjonen, og da vil de ikke kunne brukes som wikitekst.",
        "duplicate-args-warning": "<strong>Advarsel:</strong> [[:$1]] kaller [[:$2]] med flere enn en verdi for \"$3\"-parameteren. Bare den sist angitte verdien vil brukes.",
        "duplicate-args-category": "Sider med duplikate argumenter i malkall",
        "duplicate-args-category-desc": "Denne siden inneholder malkall med duplikate parametre, slik som <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> eller <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Det ser ut til at redigeringen allerede er tilbakestilt.",
        "undo-summary": "Fjerner revisjon $1 av [[Special:Contributions/$2|$2]] ([[User talk:$2|diskusjon]])",
        "undo-summary-username-hidden": "Fjern revisjon $1 av en skjult bruker",
-       "cantcreateaccounttitle": "Kan ikke opprette konto",
        "cantcreateaccount-text": "Kontooppretting fra denne IP-adressen ('''$1''') har blitt blokkert av [[User:$3|$3]].\n\nGrunnen som ble oppgitt av $3 er ''$2''",
        "cantcreateaccount-range-text": "Opprettelsen av en brukerkonto fra IP-adresser i intervallet <strong>$1</strong>, som inneholder din IP-adresse (<strong>$4</strong>), er blitt blokkert av [[User:$3|$3]].\n\nÅrsaken angitt av $3 er <em>$2</em>",
        "viewpagelogs": "Vis logger for denne siden",
        "revdelete-unsuppress": "Fjern betingelser på gjenopprettede revisjoner",
        "revdelete-log": "Årsak:",
        "revdelete-submit": "Utfør på {{PLURAL:$1|valgt revisjon|valgte revisjoner}}",
-       "revdelete-success": "'''Revisjonssynlighet vellykket oppdatert.'''",
+       "revdelete-success": "Revisjonssynlighet ble oppdatert.",
        "revdelete-failure": "'''Kunne ikke endre versjonssynligheten:'''\n$1",
-       "logdelete-success": "'''Hendelsessynlighet satt.'''",
+       "logdelete-success": "Hendelsessynlighet ble satt.",
        "logdelete-failure": "'''Loggens synlighet kunne ikke bli stilt inn:'''\n$1",
        "revdel-restore": "endre synlighet",
        "pagehist": "Sidehistorikk",
        "mergehistory-fail-bad-timestamp": "Tidsangivelsen er ugyldig.",
        "mergehistory-fail-invalid-source": "Kildesiden er ugyldig.",
        "mergehistory-fail-invalid-dest": "Målsiden er ugyldig.",
+       "mergehistory-fail-no-change": "Historieflettingen flettet ingen revisjoner. Vennligst sjekk siden og tidsparameterne igjen.",
        "mergehistory-fail-permission": "Utilstrekkelige tillatelser for å flette historikk.",
        "mergehistory-fail-self-merge": "Kilde- og målsiden er den samme.",
        "mergehistory-fail-timestamps-overlap": "Kilderevisjoner overlapper eller kommer etter målrevisjoner.",
        "userrights-unchangeable-col": "Grupper du ikke kan endre",
        "userrights-irreversible-marker": "$1 *",
        "userrights-conflict": "En konflikt med endringen av brukerrettigheter! Vær vennlig å sjekke og på nytt bekrefte endringene dine.",
-       "userrights-removed-self": "Du har fjernet dine egne rettigheter. Du har derfor ikke lengere adgang til denne siden.",
+       "userrights-removed-self": "Du har fjernet dine egne rettigheter. Du har derfor ikke lengre adgang til denne siden.",
        "group": "Gruppe:",
        "group-user": "Brukere",
        "group-autoconfirmed": "Autobekreftede brukere",
        "right-override-export-depth": "Eksporter sider inkludert lenkede sider til en dypde på 5",
        "right-sendemail": "Send e-post til andre brukere",
        "right-passwordreset": "Vis e-poster over tilbakestilte passord",
-       "right-managechangetags": "Opprette og slette [[Special:Tags|tagger]] fra databasen",
+       "right-managechangetags": "Opprette og (de)aktivere [[Special:Tags|tagger]]",
        "right-applychangetags": "Legg til [[Special:Tags|merker]] sammen med ens endringer",
        "right-changetags": "Legg til og fjern vilkårlige [[Special:Tags|merker]] på individuelle revisjoner og loggposter",
+       "right-deletechangetags": "Slette [[Special:Tags|tagger]] fra databasen",
        "grant-generic": "Rettighetspakken «$1»",
        "grant-group-page-interaction": "Interagere med sider",
        "grant-group-file-interaction": "Interagere med media",
        "grant-group-high-volume": "Utføre høyvolumaktivitet",
        "grant-group-customization": "Tilpasninger og innstillinger",
        "grant-group-administration": "Utføre administrative handlinger",
+       "grant-group-private-information": "Få tilgang til private data om deg",
        "grant-group-other": "Andre ting",
        "grant-blockusers": "Blokkere og avblokkere brukere",
        "grant-createaccount": "Opprette kontoer",
        "grant-highvolume": "Høy&shy;volum&shy;redigering",
        "grant-oversight": "Skjule brukere og undertrykke revisjoner",
        "grant-patrol": "Patruljere sideendringer",
+       "grant-privateinfo": "Få tilgang til privat informasjon",
        "grant-protect": "Beskytte og avbeskytte sider",
        "grant-rollback": "Tilbakestille side&shy;endringer",
        "grant-sendemail": "Sende e-post til andre brukere",
        "rightslogtext": "Dette er en logg over endringer av brukerrettigheter.",
        "action-read": "se denne siden",
        "action-edit": "redigere denne siden",
-       "action-createpage": "opprette sider",
-       "action-createtalk": "opprette diskusjonssider",
+       "action-createpage": "opprette denne siden",
+       "action-createtalk": "opprette denne diskusjonssiden",
        "action-createaccount": "opprette denne kontoen",
+       "action-autocreateaccount": "automatisk opprette denne eksterne brukerkontoen",
        "action-history": "se historikken til denne siden",
        "action-minoredit": "merke denne redigeringen som mindre",
        "action-move": "flytte denne siden",
        "action-viewmyprivateinfo": "vise din private informasjon",
        "action-editmyprivateinfo": "rediger din private informasjon",
        "action-editcontentmodel": "rediger innholdsmodellen til en side",
-       "action-managechangetags": "opprette og slette tagger fra databasen",
+       "action-managechangetags": "opprette og (de)aktivere tagger",
        "action-applychangetags": "bruk merker sammen med dine endringer",
        "action-changetags": "legg til og fjern vilkårlige merker på individuelle revisjoner og loggposter",
+       "action-deletechangetags": "slette tagger fra databasen",
+       "action-purge": "gjenoppfriske denne siden",
        "nchanges": "$1 {{PLURAL:$1|endring|endringer}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|siden forrige besøk}}",
        "enhancedrc-history": "historikk",
        "recentchangeslinked-page": "Sidenavn:",
        "recentchangeslinked-to": "Vis endringer på sider som lenker til den gitte siden istedet",
        "recentchanges-page-added-to-category": "[[:$1]] lagt til kategori",
-       "recentchanges-page-added-to-category-bundled": "[[:$1]] og [[Special:WhatLinksHere/$1|{{PLURAL:$2|én side|$2 sider}}]] lagt til kategori",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] lagt til i kategori, [[Special:WhatLinksHere/$1|denne siden er inkludert i andre sider]]",
        "recentchanges-page-removed-from-category": "[[:$1]] fjernet fra kategori",
-       "recentchanges-page-removed-from-category-bundled": "[[:$1]] og {{PLURAL:$2|én side|$2 sider}} fjernet fra kategori",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] fjernet fra kategori, [[Special:WhatLinksHere/$1|denne siden er inkludert i andre sider]]",
        "autochange-username": "Automatisk MediaWiki-endring",
        "upload": "Last opp fil",
        "uploadbtn": "Last opp fil",
        "file-thumbnail-no": "Filnavnet begynner med <strong>$1</strong>.\nDet virker som om det er et bilde av redusert størrelse ''(miniatyrbilde)''.\nOm du har dette bildet i stor utgave, last opp det, eller endre filnavnet på denne filen.",
        "fileexists-forbidden": "En fil med dette navnet finnes fra før, og kan ikke erstattes.\nOm du fortsatt ønsker å laste opp filen, gå tilbake og last den opp under et nytt navn. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Ei fil med dette navnet finnes fra før i det delte fillageret.\nOm du fortsatt ønsker å laste opp filen, gå tilbake og last den opp under et nytt navn. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Opplastingen er et eksakt duplikat av følgende versjon av <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Opplastingen er et eksakt duplikat av {{PLURAL:$2|en eldre versjon|eldre versjoner}} av <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Denne filen er en dublett av følgende {{PLURAL:$1|fil|filer}}:",
        "file-deleted-duplicate": "En fil identisk med denne filen ([[:$1]]) har tidligere blitt slettet. Du bør sjekke denne filens slettehistorikk før du prøver å laste den opp på nytt.",
        "file-deleted-duplicate-notitle": "En annen fil identisk med denne filen har tidligere blitt slettet og tittelen har blitt fjernet. Du bør sjekke med noen som kan se på fjernede fildata å vurdere saken før filen lastes opp igjen.",
        "upload-scripted-pi-callback": "Det er ikke tillatt å laste opp en fil som inneholder et kjørbart XML-stilark.",
        "uploaded-script-svg": "Fant et skriptelement \"$1\" i den opplastede SVG-koden.",
        "uploaded-hostile-svg": "Fant usikker CSS i stilelementet til opplastet SVG-fil",
+       "uploaded-event-handler-on-svg": "Å sette event-handler-attributtene <code>$1=\"$2\"</code> tillates ikke i SVG-filer.",
        "uploaded-href-attribute-svg": "href-attributter i SVG-filer tillates kun for http://- eller https://-mål; fant <code>&lt;$1 $2=\"$3\"%gt;</code>.",
        "uploaded-href-unsafe-target-svg": "Fant href til usikre data: URI-mål <code>&lt;$1 $2=\"$3\"&gt;</code> i den opplastede SVG-filen.",
+       "uploaded-animate-svg": "Fant en «animate»-tagg som kan endre href, bruk attributtet «from» <code>&lt;$1 $2=\"$3\"&gt;</code> i den opplastede SVG-fila.",
+       "uploaded-setting-event-handler-svg": "Setting av event-handler-attributter er blokkert, fant <code>&lt;$1 $2=\"$3\"&gt;</code> i den opplastede SVG-fila.",
+       "uploaded-setting-href-svg": "Bruk av «set»-taggen for å legge til «href»-attributt til foreldreelementet er blokkert.",
+       "uploaded-wrong-setting-svg": "Bruk av «set»-taggen for å legge til et eksternt/data- eller skriptmål til attributter er blokkert. Fant <code>&lt;set to=\"$1\"&gt;</code> i den opplastede SVG-fila.",
+       "uploaded-setting-handler-svg": "SVG-er som setter «handler»-attributtet med remote/data/script er blokkert. Fant <code>$1=\"$2\"</code> i den opplastede SVG-fila.",
+       "uploaded-remote-url-svg": "SVG-er som setter et stilattributt med ekstern URL er blokkert. Fant <code>$1=\"$2\"</code> i den opplastede SVG-fila.",
+       "uploaded-image-filter-svg": "Fant bildefilter med URL: <code>&lt;$1 $2=\"$3\"&gt;</code> i den opplastede SVG-fila.",
        "uploadscriptednamespace": "Denne SVG-filen inneholder et ulovlig navnerom \"$1\"",
        "uploadinvalidxml": "XML-en i den opplastede filen kunne ikke tolkes.",
        "uploadvirus": "Denne filen inneholder virus! Detaljer: $1",
        "upload-too-many-redirects": "URL-en inneholdt for mange omdirigeringer",
        "upload-http-error": "En HTTP-feil oppstod: $1",
        "upload-copy-upload-invalid-domain": "Opplasting av kopier er ikke tilgjengelig fra dette domenet.",
+       "upload-foreign-cant-upload": "Denne wikien er ikke konfigurert til å laste opp filer til det forespurte eksterne fillageret.",
+       "upload-foreign-cant-load-config": "Lasting av konfigurasjonen for filopplastinger til det eksterne fillageret mislyktes.",
+       "upload-dialog-disabled": "Filopplastinger med denne dialogen er slått av for denne wikien.",
        "upload-dialog-title": "Last opp fil",
        "upload-dialog-button-cancel": "Avbryt",
        "upload-dialog-button-done": "Utført",
        "upload-dialog-button-upload": "Last opp",
        "upload-form-label-infoform-title": "Detaljer",
        "upload-form-label-infoform-name": "Navn",
+       "upload-form-label-infoform-name-tooltip": "En unik beskrivende tittel for fila, som vil brukes som filnavn. Du kan bruke vanlig språk med mellomrom. Ikke ta med filendelsen.",
        "upload-form-label-infoform-description": "Beskrivelse",
+       "upload-form-label-infoform-description-tooltip": "Beskriv kort alt som er bemerkelsesverdig med verket.\nFor bilder, nevn hovedtingene som avbildes, anledningen eller stedet.",
        "upload-form-label-usage-title": "Bruk",
        "upload-form-label-usage-filename": "Filnavn",
        "upload-form-label-own-work": "Dette er mitt eget verk",
        "backend-fail-read": "Klarte ikke lese filen $1.",
        "backend-fail-create": "Kunne ikke opprette filen $1.",
        "backend-fail-maxsize": "Kunne ikke skrive filen $1 fordi den er større enn {{PLURAL:$2|én byte|$2 bytes}}.",
-       "backend-fail-readonly": "Underliggende \"$1\" er satt skrivebeskyttet fordi: \"$2\"",
+       "backend-fail-readonly": "Lagringssystemet «$1» er midlertidig skrivebeskyttet fordi: <em>$2</em>",
        "backend-fail-synced": "Fila «$1» er i en inkonsistent status innen de interne bakstykkene",
        "backend-fail-connect": "Kunne ikke koble til filbackend «$1».",
        "backend-fail-internal": "En ukjent feil oppsto i filbackend «$1».",
        "uploadstash-summary": "Denne siden gir tilgang til filer som har blitt lastet opp (eller er i ferd med å bli lastet opp) men som ennå ikke er publisert til wikien. Disse filene er ikke synlige for andre enn brukeren som lastet dem opp.",
        "uploadstash-clear": "Fjern stashede filer",
        "uploadstash-nofiles": "Du har ingen stashede filer.",
-       "uploadstash-badtoken": "Utføringen av den handlingen var mislykket, kanskje fordi redigeringsrettighetene dine har utløpt. Prøv igjen.",
-       "uploadstash-errclear": "Fjerning av filene var mislykket.",
+       "uploadstash-badtoken": "Utføringen av handlingen feilet, kanskje fordi redigeringsrettighetene dine har utløpt. Prøv igjen.",
+       "uploadstash-errclear": "Filene lot seg ikke fjerne.",
        "uploadstash-refresh": "Oppdater listen over filer",
+       "uploadstash-thumbnail": "vis miniatyrbilde",
+       "uploadstash-exception": "Kunne ikke lagre opplastingen i stashen ($1): «$2».",
        "invalid-chunk-offset": "Ugyldig delforskyvning",
        "img-auth-accessdenied": "Ingen tilgang",
        "img-auth-nopathinfo": "Manglende PATH_INFO.\nTjeneren din er ikke satt opp til å gi denne informasjonen.\nDen er kanskje CGI-basert og støtter ikke img_auth.\nhttps://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization Se bildeautorisasjon.",
        "filerevert-submit": "Tilbakestill",
        "filerevert-success": "'''[[Media:$1|$1]]''' ble tilbakestilt til [$4 versjonen à $2, $3].",
        "filerevert-badversion": "Det er ingen tidligere lokal versjon av denne filen med det gitte tidstrykket.",
+       "filerevert-identical": "Den nåværende versjonen av fila er allerede identisk med den valgte.",
        "filedelete": "Slett $1",
        "filedelete-legend": "Slett fil",
        "filedelete-intro": "Du er i ferd med å slette filen '''[[Media:$1|$1]]''' sammen med hele dens historikk.",
        "apihelp": "API hjelp",
        "apihelp-no-such-module": "Modulen «$1» ikke funnet.",
        "apisandbox": "API-sandkasse",
+       "apisandbox-jsonly": "JavaScript kreves for å bruke API-sandkassa.",
        "apisandbox-api-disabled": "API er deaktivert på dette nettstedet.",
-       "apisandbox-intro": "Bruk denne siden for å eksperimentere med '''MediaWiki web service APIet'''.\nSjekk [https://www.mediawiki.org/wiki/API:Main_page API-dokumentasjonen] for mer informasjon om bruk av APIet. Eksempel: [https://www.mediawiki.org/wiki/API#A_simple_example hente innholdet til en hovedside]. Velg en handling for å se flere eksempler.\n\nMerk at du kan utføre handlinger her som fører til endringer på wikien.",
+       "apisandbox-intro": "Bruk denne siden for å eksperimentere med <strong>MediaWiki webtjeneste-APIet</strong>.\nSjekk [[mw:API:Main page|API-dokumentasjonen]] for mer informasjon om bruk av APIet. Eksempel: [https://www.mediawiki.org/wiki/API#A_simple_example hente innholdet til en hovedside]. Velg en handling for å se flere eksempler.\n\nMerk at du kan utføre handlinger her som fører til endringer på wikien.",
+       "apisandbox-fullscreen": "Utvid panelet",
+       "apisandbox-fullscreen-tooltip": "Utvid sandkassepanelet så det dekker nettleservinduet.",
+       "apisandbox-unfullscreen": "Vis siden",
+       "apisandbox-unfullscreen-tooltip": "Reduser størrelsen på sandkassepanelet, så MediaWikis navigasjonslenker er tilgjengelige.",
        "apisandbox-submit": "Foreta en forespørsel",
        "apisandbox-reset": "Tilbakestill",
-       "apisandbox-examples": "Eksempel",
-       "apisandbox-results": "Resultat",
+       "apisandbox-retry": "Prøv igjen",
+       "apisandbox-loading": "Laster informasjon for API-modulen «$1»...",
+       "apisandbox-load-error": "En feil oppsto under lasting av informasjon for API-modulen «$1»: $2",
+       "apisandbox-no-parameters": "Denne API-modulen har ingen parametre.",
+       "apisandbox-helpurls": "Hjelpelenker",
+       "apisandbox-examples": "Eksempler",
+       "apisandbox-dynamic-parameters": "Ekstra parametre",
+       "apisandbox-dynamic-parameters-add-label": "Legg til parameter:",
+       "apisandbox-dynamic-parameters-add-placeholder": "Parameternavn",
+       "apisandbox-dynamic-error-exists": "En parameter med navn «$1» finnes fra før.",
+       "apisandbox-deprecated-parameters": "Utgåtte parametre",
+       "apisandbox-fetch-token": "Fyll inn nøkkelen automatisk",
+       "apisandbox-submit-invalid-fields-title": "Noen felt er ugyldige",
+       "apisandbox-submit-invalid-fields-message": "Fiks de markerte feltene og prøv igjen.",
+       "apisandbox-results": "Resultater",
+       "apisandbox-sending-request": "Sender API-forespørsel...",
+       "apisandbox-loading-results": "Mottar API-resultater...",
+       "apisandbox-results-error": "En feil oppsto under lasting av API-spørringssvaret: $1.",
        "apisandbox-request-url-label": "Forespurt URL:",
-       "apisandbox-request-time": "Forespørselstid: $1",
+       "apisandbox-request-time": "Forespørselstid: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Fiks nøkkelen og send på nytt",
+       "apisandbox-results-fixtoken-fail": "Henting av nøkkelen «$1» mislyktes.",
+       "apisandbox-alert-page": "Felter på denne siden er ugyldige.",
+       "apisandbox-alert-field": "Verdien til dette feltet er ugyldig.",
        "booksources": "Bokkilder",
        "booksources-search-legend": "Søk etter bokkilder",
        "booksources-search": "Søk",
        "listgrouprights-namespaceprotection-header": "Navneromsbegrensinger",
        "listgrouprights-namespaceprotection-namespace": "Navnerom",
        "listgrouprights-namespaceprotection-restrictedto": "Rettighet(er) som tillater at brukeren redigerer",
-       "listgrants-summary": "Følgende er en liste over OAuth-tildelinger og hvilke brukerrettigheter de gir tilgang til. Brukere kan autorisere applikasjoner til å bruke kontoen deres, med rettigheter begrenset til de gitt av tildelingene brukeren har godkjent. En applikasjon som handler på vegne av en bruker kan imidlertid aldri benytte seg av rettigheter brukeren ikke selv har.\nDet kan finnes [[{{MediaWiki:Listgrouprights-helppage}}|ytterligere informasjon]] om de ulike rettighetene.",
+       "listgrants-summary": "Følgende er en liste over tildelinger samt hvilke brukerrettigheter de gir tilgang til. Brukere kan autorisere applikasjoner til å bruke kontoen deres, med rettigheter begrenset til de gitt av tildelingene brukeren har godkjent. En applikasjon som handler på vegne av en bruker kan imidlertid aldri benytte seg av rettigheter brukeren ikke selv har.\nDet kan finnes [[{{MediaWiki:Listgrouprights-helppage}}|ytterligere informasjon]] om de ulike rettighetene.",
        "listgrants-rights": "Rettigheter",
        "trackingcategories": "Sporingskategori",
        "trackingcategories-summary": "Denne siden lister sporingskategorier som er automatisk befolket av Mediawiki-programvaren. Navnene deres kan endres ved å redigere de tilhørende systembeskjedene i {{ns:8}}-navnerommet.",
        "delete-toobig": "Denne siden har en stor redigeringshistorikk, med over {{PLURAL:$1|$1&nbsp;revisjon|$1&nbsp;revisjoner}}. Muligheten til å slette slike sider er begrenset for å unngå utilsiktet forstyrring av {{SITENAME}}.",
        "delete-warning-toobig": "Denne siden har en stor redigeringshistorikk, med over {{PLURAL:$1|$1&nbsp;revisjon|$1&nbsp;revisjoner}}. Sletting av denne siden kan forstyrre databasen til {{SITENAME}}; vær varsom.",
        "deleteprotected": "Du kan ikke slette denne siden fordi den er beskyttet.",
-       "deleting-backlinks-warning": "'''Advarsel:''' [[Special:WhatLinksHere/{{FULLPAGENAME}}|Andre sider]] lenker til eller inkluderer siden du er i ferd med å slette.",
+       "deleting-backlinks-warning": "<strong>Advarsel:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Andre sider]] lenker til eller inkluderer siden du er i ferd med å slette.",
        "rollback": "Fjern redigeringer",
        "rollbacklink": "tilbakestill",
        "rollbacklinkcount": "tilbakestill {{PLURAL:$1|én endring|$1 endringer}}",
        "undeletedrevisions": "{{PLURAL:$1|Én revisjon|$1 revisjoner}} gjenopprettet",
        "undeletedrevisions-files": "{{PLURAL:$1|Én revisjon|$1 revisjoner}} og {{PLURAL:$2|én fil|$2 filer}} gjenopprettet",
        "undeletedfiles": "{{PLURAL:$1|Én fil|$1 filer}} gjenopprettet",
-       "cannotundelete": "Gjennoppretting feilet:\n$1",
+       "cannotundelete": "Deler av eller hele gjennopprettingen feilet:\n$1",
        "undeletedpage": "'''$1 ble gjenopprettet'''\n\nSjekk [[Special:Log/delete|slettingsloggen]] for en liste over nylige slettinger og gjenopprettelser.",
        "undelete-header": "Se [[Special:Log/delete|slettingsloggen]] for nylig slettede sider.",
        "undelete-search-title": "Søk i slettede sider",
        "lastmodifiedatby": "Denne siden ble sist redigert $1 kl. $2 av $3.",
        "othercontribs": "Basert på arbeid av $1.",
        "others": "andre",
-       "siteusers": "{{SITENAME}}-{{PLURAL:$2|bruker|brukere}} $1",
+       "siteusers": "{{SITENAME}}-{{PLURAL:$2|{{GENDER:$1|bruker}}|brukere}} $1",
        "anonusers": "{{SITENAME}}s {{PLURAL:$2|anonyme bruker|anonyme brukere}} $1",
        "creditspage": "Sidekrediteringer",
        "nocredits": "Ingen krediteringer er tilgjengelig for denne siden.",
        "scarytranscludefailed-httpstatus": "[Henting av mal for $1 feilet: HTTP $2]",
        "scarytranscludetoolong": "[URL-en er for lang]",
        "deletedwhileediting": "'''Advarsel:''' Denne siden har blitt slettet etter at du begynte å redigere den!",
-       "confirmrecreate": "«[[User:$1|$1]]» ([[User talk:$1|diskusjon]]) slettet siden etter at du begynte å redigere den, med begrunnelsen «$2». Vennligst bekreft at du vil gjenopprette siden.",
-       "confirmrecreate-noreason": "Brukeren [[User:$1|$1]] ([[User talk:$1|diskusjon]]) slettet denne siden etter at du begynte å redigere. Bekreft at du virkelig ønsker å gjenopprette denne siden.",
+       "confirmrecreate": "Brukeren [[User:$1|$1]] ([[User talk:$1|diskusjon]]) {{GENDER:$1|slettet}} siden etter at du begynte å redigere den, med begrunnelsen:\n: <em>$2</em>\nVennligst bekreft at du vil gjenopprette siden.",
+       "confirmrecreate-noreason": "Brukeren [[User:$1|$1]] ([[User talk:$1|diskusjon]]) {{GENDER:$1|slettet}} denne siden etter at du begynte å redigere. Bekreft at du virkelig ønsker å gjenopprette denne siden.",
        "recreate": "Gjenopprett",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Vil du slette tjenerens mellomlagrede versjon (''cache'') av denne siden?",
        "watchlistedit-raw-done": "Overvåkningslisten din er oppdatert.",
        "watchlistedit-raw-added": "{{PLURAL:$1|Én tittel|$1 titler}} ble lagt til:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|Én tittel|$1 titler}} ble fjernet:",
-       "watchlistedit-clear-title": "Rensket overvåkningslisten",
+       "watchlistedit-clear-title": "Tøm overvåkningslisten",
        "watchlistedit-clear-legend": "Rensk overvåkninslisten",
        "watchlistedit-clear-explain": "Alle titlene blir fjernet fra overvåkningslisten din",
        "watchlistedit-clear-titles": "Titler:",
        "tags-edit-revision-legend": "Legg til eller fjern fra {{PLURAL:$1|denne revisjonen|alle revisjoner}}",
        "tags-edit-logentry-legend": "Legg til eller fjern fra {{PLURAL:$1|denne loggposten|alle loggposter}}",
        "tags-edit-existing-tags": "Eksisterende merker:",
-       "tags-edit-existing-tags-none": "«Ingen»",
+       "tags-edit-existing-tags-none": "<em>Ingen</em>",
        "tags-edit-new-tags": "Nye merker:",
        "tags-edit-add": "Legg til disse merkene:",
        "tags-edit-remove": "Fjern disse merkene:",
        "tags-edit-reason": "Årsak:",
        "tags-edit-revision-submit": "Utfør endringene på {{PLURAL:$1|denne revisjonen|$1 revisjoner}}",
        "tags-edit-logentry-submit": "Utfør endringene på {{PLURAL:$1|denne loggposten|$1 loggposter}}",
-       "tags-edit-success": "Endringene ble suksessfullt utført.",
+       "tags-edit-success": "Endringene ble utført.",
        "tags-edit-failure": "Denne endringen kunne ikke bli utført:\n$1",
        "tags-edit-nooldid-title": "Ugyldig målrevisjon",
        "tags-edit-nooldid-text": "Du har enten ikke angitt noen målversjon for denne funksjonen, eller så har du angitt en revisjon som ikke finnes.",
        "logentry-protect-protect-cascade": "$1 {{GENDER:$2|beskyttet}} $3 $4 [cascading]",
        "logentry-protect-modify": "$1 {{GENDER:$2|endret}} beskyttelsesnivå for $3 $4",
        "logentry-protect-modify-cascade": "$1 {{GENDER:$2|endret}} beskyttelsesnivå for $3 $4 [cascading]",
-       "logentry-rights-rights": "$1 {{GENDER:$2|endret}} gruppemedlemskap for $3 fra $4 til $5",
+       "logentry-rights-rights": "$1 {{GENDER:$2|endret}} gruppemedlemskap for {{GENDER:$6|$3}} fra $4 til $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|endret}} gruppemedlemskap for $3",
        "logentry-rights-autopromote": "$1 ble automatisk {{GENDER:$2|forfremmet}} fra $4 til $5",
        "logentry-upload-upload": "$1 {{GENDER:$2|lastet opp}} $3",
        "feedback-close": "Utført",
        "feedback-external-bug-report-button": "Registrer en teknisk sak",
        "feedback-dialog-title": "Send tilbakemelding",
-       "feedback-dialog-intro": "Du kan benytte det enkle skjemaet under for å gi din tilbakemelding. Kommentaren din vil bli lagt til siden «$1» sammen med ditt brukernavn.",
+       "feedback-dialog-intro": "Bruk det enkle skjemaet under om du vil gi tilbakemelding. Kommentaren din vil bli lagt ut på siden «$1» sammen med brukernavnet ditt.",
        "feedback-error-title": "Feil",
        "feedback-error1": "Feil: Ukjent resultat fra API",
        "feedback-error2": "Feil: Redigering feilet",
        "feedback-message": "Melding:",
        "feedback-subject": "Emne:",
        "feedback-submit": "Send",
-       "feedback-terms": "Jeg er innforstått med at min nettleserinformasjon inkluderer nøyaktig informasjon om versjonen til min nettleser og mitt operativsystem og at dette vil bli delt offentlig sammen med min tilbakemelding.",
-       "feedback-termsofuse": "Jeg godtar å gi tilbakemelding i henhold til brukervilkårene.",
+       "feedback-terms": "Jeg er innforstått med at brukeragentinformasjonen min inkluderer nøyaktig informasjon om versjonen til nettleseren min og operativsystemet mitt, og at dette vil bli delt offentlig sammen med tilbakemeldingen min.",
+       "feedback-termsofuse": "Jeg godtar å gi tilbakemelding i tråd med bruksvilkårene.",
        "feedback-thanks": "Din tilbakemelding til siden \"[ $2  $1 ]\" er sendt. Takk skal du ha!",
        "feedback-thanks-title": "Takk!",
        "feedback-useragent": "Brukeragent",
        "expand_templates_generate_xml": "Vis parsetre som XML",
        "expand_templates_generate_rawhtml": "Vis ubehandlet HTML",
        "expand_templates_preview": "Forhåndsvisning",
-       "expand_templates_preview_fail_html": "<em>Fordi {{SITENAME}} har slått på rå HTML og sesjonsdata ble tapt er forhåndsvisningen skjult for å beskytte mot JavaScript-angrep.</em>\n\n<strong>Om dette er et legitimt forsøk på å forhåndsvise, prøv igjen.</strong> Om det fortsatt ikke fungerer, prøv å [[Special:UserLogout|logge ut]] og logge inn igjen.",
+       "expand_templates_preview_fail_html": "<em>Fordi {{SITENAME}} har slått på rå HTML og sesjonsdata ble tapt er forhåndsvisningen skjult for å beskytte mot JavaScript-angrep.</em>\n\n<strong>Om dette er et legitimt forsøk på å forhåndsvise, prøv på nytt.</strong> Om det fortsatt ikke fungerer, prøv å [[Special:UserLogout|logge ut]] og logge inn igjen, og sjekk at nettleseren din godtar nettkapsler fra dette nettstedet.",
        "expand_templates_preview_fail_html_anon": "<em>Fordi {{SITENAME}} har slått på rå HTML og du ikke er logget inn er forhåndsvisningen skjult for å beskytte mot JavaScript-angrep.</em>\n\n<strong>Om dette er et legitimt forsøk på å forhåndsvise, [[Special:UserLogin|logg inn]] og prøv igjen.</strong>",
-       "pagelanguage": "Valg av sidespråk",
+       "pagelanguage": "Endre sidespråk",
        "pagelang-name": "Side",
        "pagelang-language": "Språk",
        "pagelang-use-default": "Bruk standardspråk",
        "pagelang-submit": "Lagre",
        "right-pagelang": "Endre sidespråk",
        "action-pagelang": "endre sidespråket",
-       "log-name-pagelang": "Endre språklogg",
+       "log-name-pagelang": "Logg for språkendringer",
        "log-description-pagelang": "Dette er en logg som viser endringer i sidespråk",
-       "logentry-pagelang-pagelang": "$1 {{GENDER:$2|endret}} sidespråk for $3 fra $4 til $5.",
+       "logentry-pagelang-pagelang": "$1 {{GENDER:$2|endret}} språk for $3 fra $4 til $5.",
        "default-skin-not-found": "Ops! Standarddrakten for wikien din, definert i <code dir=\"ltr\">$wgDefaultSkin</code> som <code>$1</code>, er ikke tilgjengelig.\n\nInstallasjonen din ser ut til å inneholde følgende {{PLURAL:$4|drakt|drakter}}. Se [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] for informasjon om hvordan du kan slå {{PLURAL:$4|denne på|disse på og velge en standarddrakt}}.\n\n$2\n\n; Om du nettopp har installert MediaWiki:\n: Du har trolig installert fra git, eller direkte fra kildekoden med en annen metode. Dette er forventet. Prøv å installere noen drakter fra [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org sin draktbase] ved å\n:* laste ned [https://www.mediawiki.org/wiki/Download tarball-installereren], som kommer med flere drakter og utvidelser. Du kan kopiere og lime inn <code>skins/</code>-mappen fra denne.\n:* laste ned individuelle drakter fra [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* klone en av <code>mediawiki/skins/*</code>-lagrene via git inn i <code>skins/</code> -mappen av din MediaWiki-installasjon.\n: Å gjøre dette skal ikke forstyrre git-mappen din om du er en MediaWiki-utvikler.\n\n; Om du nettopp har oppgradert MediaWiki:\n: MediaWiki 1.24 og nyere slår ikke lenger på automatisk installerte drakter (se [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). Du kan lime inn følgende {{PLURAL:$5|linje|linjer}} i <code>LocalSettings.php</code> for å slå på {{PLURAL:$5|den|alle}} nåværende installerte {{PLURAL:$5|drakten|drakter}}:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Om du nettopp har endret <code>LocalSettings.php</code>:\n: Dobbelsjekk draktnavnene for skrivefeil.",
        "default-skin-not-found-no-skins": "Ops! Standarddrakten for wikien din, definert i <code>$wgDefaultSkin</code> som <code>$1</code>, er ikke tilgjengelig.\n\nDu har ingen installerte drakter.\n\n;Om du nettopp har installert eller oppgradert MediaWiki:\n: Du installerte trolig fra git, eller direkte fra kildekoden med en annen metode. Dette er forventet. MediaWiki 1.24 og nyere inkluderer ingen drakter i hovedarkivet. Prøv å installere noen drakter fra [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.orgs draktmappe], ved å:\n:* laste ned [https://www.mediawiki.org/wiki/Download tarball-installereren], som kommer med mange drakter og tillegg. Du kan kopiere og lime inn <code>skins/</code>-mappen fra denne.\n:* laste ned individuelle drakt-tarballer fra [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* klone en av <code>mediawiki/skins/*</code>-arkivene via git til <code dir=\"ltr\">skins/</code>-mappa i din MediaWiki-installasjon.\n: Å gjøre dette vil ikke forstyrre ditt git-arkiv om du er en MediaWiki-utvikler. Se [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual:Skin configuration] for informasjon om hvordan du slår på drakter og velger en standarddrakt.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (slått på)",
        "mediastatistics": "Mediestatistikk",
        "mediastatistics-summary": "Statistikk over opplastede filtyper. Dette inkluderer bare den nyeste versjonen av hver fil. Eldre eller slettede versjoner av filene er eksludert.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte}} ($2; $3 %)",
-       "mediastatistics-bytespertype": "Total filstørrelse for denne seksjonen: $1 byte.",
-       "mediastatistics-allbytes": "Total filstørrelse for alle filer: $1 byte.",
+       "mediastatistics-bytespertype": "Total filstørrelse for denne seksjonen: {{PLURAL:$1|$1 byte}} ($2; $3 %).",
+       "mediastatistics-allbytes": "Total filstørrelse for alle filer: {{PLURAL:$1|$1 byte}} ($2).",
        "mediastatistics-table-mimetype": "MIME-type",
        "mediastatistics-table-extensions": "Mulige filtyper",
        "mediastatistics-table-count": "Antall filer",
        "mw-widgets-dateinput-placeholder-month": "ÅÅÅÅ-MM",
        "mw-widgets-titleinput-description-new-page": "siden eksisterer ikke ennå",
        "mw-widgets-titleinput-description-redirect": "omdiriger til $1",
-       "api-error-blacklisted": "Vennligst velg en annen beskrivende tittel.",
        "sessionprovider-generic": "$1 sesjoner",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "informasjons&shy;kapsel-baserte sesjoner",
        "randomrootpage": "Tilfeldig rotside"
index 5910f85..7beefc3 100644 (file)
        "minoredit": "kleine wieziging",
        "watchthis": "volg disse zied",
        "savearticle": "Zied opslaon",
+       "savechanges": "Wiezigingen opslaon",
+       "publishpage": "Zied uutbrengen",
+       "publishchanges": "Wiezigingen uutbrengen",
        "preview": "Naokieken",
        "showpreview": "Bewarking naokieken",
        "showdiff": "Verschil bekieken",
        "content-model-text": "tekste zonder opmaak",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
+       "content-json-empty-object": "Leeg objekt",
+       "content-json-empty-array": "Lege reeks",
        "expensive-parserfunction-warning": "Waorschuwing: disse zied gebruukt te veule kostbaore parserfunksies.\n\nNoen {{PLURAL:$1|is|bin}} t der $1, terwiel t der minder as $2 {{PLURAL:$2|mut|mutten}} ween.",
        "expensive-parserfunction-category": "Ziejen die te veule kostbaore parserfunksies gebruken",
        "post-expand-template-inclusion-warning": "Waorschuwing: de grootte van de in-evoegden mal is te groot.\nSommigen mallen wörden niet in-evoegd.",
        "history-feed-empty": "De op-evreugen zied besteet niet. t Kan ween dat disse zied vortedaon is of dat t herneumd is. Probeer te [[Special:Search|zeuken]] naor soortgelieke nieje ziejen.",
        "rev-deleted-comment": "(bewarkingsopmarking vortedaon)",
        "rev-deleted-user": "(gebrukersnaam vortedaon)",
-       "rev-deleted-event": "(antekening vortedaon)",
+       "rev-deleted-event": "(logboekregel vortedaon)",
        "rev-deleted-user-contribs": "[gebrukersnaam of IP-adres vortedaon - bewarking verbörgen in biedragen]",
        "rev-deleted-text-permission": "Disse bewarking is '''vortedaon'''.\nAs der meer informasie is, ku'j t vienen in t [{{fullurl:{{#Special:Log}}/delete|page={{PAGENAMEE}}}} vortdologboek].",
        "rev-deleted-text-unhide": "Disse bewarking is '''vortedaon'''.\nAs der meer informasie is, ku'j t vienen in t [{{fullurl:{{#Special:Log}}/delete|page={{PAGENAMEE}}}} vortdologboek].\nJe kunnen [$1 disse versie bekieken] a'j willen.",
        "revdelete-legend": "Stel versiebeparkingen in:",
        "revdelete-hide-text": "Versietekste",
        "revdelete-hide-image": "Verbarg bestaandsinhoud",
-       "revdelete-hide-name": "Verbarg logboekaksie",
+       "revdelete-hide-name": "Verbarg haandeling en doel",
        "revdelete-hide-comment": "Bewarkingssamenvatting",
        "revdelete-hide-user": "Gebrukersnaam/IP-adres van disse gebruker",
        "revdelete-hide-restricted": "Gegevens veur beheerders en aander volk onderdrokken",
        "revdelete-unsuppress": "Beparkingen veur weerummezetten versies vortdoon",
        "revdelete-log": "Reden:",
        "revdelete-submit": "Toepassen op de ekeuzen {{PLURAL:$1|bewarking|bewarkingen}}",
-       "revdelete-success": "'''De zichtbaorheid van de wieziging is bie-ewörken.'''",
+       "revdelete-success": "De zichtbaorheid van de wieziging is bie-ewörken.",
        "revdelete-failure": "'''De zichtbaorheid veur de wieziging kon niet bie-ewörken wörden:'''\n$1",
-       "logdelete-success": "'''Zichtbaorheid van de gebeurtenisse is suksesvol in-esteld.'''",
+       "logdelete-success": "Zichtbaorheid van de gebeurtenisse is in-esteld.",
        "logdelete-failure": "'''De zichtbaorheid van de logboekregel kon niet in-esteld wörden:'''\n$1",
        "revdel-restore": "Zichtbaorheid wiezigen",
        "pagehist": "Ziedgeschiedenisse",
        "mergehistory-go": "Bekiek bewarkingen die bie mekaar edaon kunnen wörden",
        "mergehistory-submit": "Versies bie mekaar doon",
        "mergehistory-empty": "Der bin gien versies die samenevoegd kunnen wörden.",
-       "mergehistory-done": "$3 {{PLURAL:$3|versie|versies}} van $1 bin suksesvol samenevoegd naor [[:$2]].",
+       "mergehistory-done": "$3 {{PLURAL:$3|versie|versies}} van $1 {{PLURAL:$3|is|bin}} suksesvol samenevoegd naor [[:$2]].",
        "mergehistory-fail": "Kan gien geschiedenisse samenvoegen, kiek opniej de zied- en tiedparameters nao.",
        "mergehistory-no-source": "Bronzied $1 besteet niet.",
        "mergehistory-no-destination": "Bestemmingszied $1 besteet niet.",
        "badsig": "Ongeldige haandtekening; HTML naokieken.",
        "badsiglength": "Joew haandtekening is te lang.\nt Mut minder as {{PLURAL:$1|letter|letters}} hebben.",
        "yourgender": "Geslacht:",
-       "gender-unknown": "Geet joe niks an",
+       "gender-unknown": "De programmatuur gebruukt zoveul meugelik geslachtsneutrale woorden as t over joe geet.",
        "gender-male": "Keerl",
        "gender-female": "Deerne",
        "prefs-help-gender": "Disse instelling is opsioneel.\n\nDe programmatuur gebruukt disse weerde um joe op de juuste maniere an te spreken en veur aandere gebrukers um joew geslacht an te geven.\nDisse informasie is zichtbaor veur aandere gebrukers.",
        "email": "Privéberichten",
-       "prefs-help-realname": "* Echte naam (niet verplicht): a'j disse opsie invullen zu'w joew echte naam gebruken um erkenning te geven veur joew warkzaamheen.",
+       "prefs-help-realname": "Echte naam is keuzevrie.\nA'j disse opsie invullen zu'w joew echte naam gebruken um erkenning te geven veur joew wark.",
        "prefs-help-email": "n Netpostadres is niet verplicht, mer zo ku'w wel joew wachtwoord toesturen veur a'j t vergeten bin.",
        "prefs-help-email-others": "Je kunnen oek aandere meensen de meugelikheid geven um kontakt mit joe op te nemen mit n verwiezing op joew gebrukers- en overlegzied zonder da'j de identiteit pries hoeven te geven.",
        "prefs-help-email-required": "Hier he'w n netpostadres veur neudig.",
        "userrights": "Gebrukersrechtenbeheer",
        "userrights-lookup-user": "Beheer gebrukersgroepen",
        "userrights-user-editname": "Vul n gebrukersnaam in:",
-       "editusergroup": "Bewark gebrukersgroepen",
+       "editusergroup": "Bewark {{GENDER:$1|gebrukersgroepen}}",
        "editinguser": "Doonde mit t wiezigen van de gebrukersrechten van '''[[User:$1|$1]]''' $2",
        "userrights-editusergroup": "Bewark gebrukersgroep",
        "saveusergroups": "Gebrukergroepen opslaon",
        "rightslogtext": "Dit is n logboek mit veraanderingen van gebrukersrechten",
        "action-read": "disse zied lezen",
        "action-edit": "disse zied bewarken",
-       "action-createpage": "ziejen schrieven",
-       "action-createtalk": "overlegziejen anmaken",
+       "action-createpage": "disse zied anmaken",
+       "action-createtalk": "disse overlegzied anmaken",
        "action-createaccount": "disse gebrukerskonto anmaken",
        "action-minoredit": "disse bewarking as klein markeren",
        "action-move": "disse zied herneumen",
        "nopagetext": "De zied die'j herneumen willen besteet niet.",
        "pager-newer-n": "{{PLURAL:$1|1 niejere|$1 niejere}}",
        "pager-older-n": "{{PLURAL:$1|1 ouwere|$1 ouwere}}",
-       "suppress": "Toezichte",
+       "suppress": "Toezicht",
        "querypage-disabled": "Disse spesiale zied is uutezet um prestasieredens.",
        "apisandbox": "API-zaandkule",
        "booksources": "Boekinformasie",
        "duplicate-defaultsort": "Waorschuwing: de standardsortering \"$2\" krig veurrang veur de sortering \"$1\".",
        "version": "Versie",
        "version-extensions": "Uutbreidingen die installeerd bin",
-       "version-skins": "Vormgevingen",
+       "version-skins": "Geïnstalleerden vormgevingen",
        "version-specialpages": "Spesiale ziejen",
        "version-parserhooks": "Parserhoeken",
        "version-variables": "Variabels",
        "version-entrypoints": "Webadressen veur ingangen",
        "version-entrypoints-header-entrypoint": "Ingang",
        "version-entrypoints-header-url": "Webadres",
-       "redirect": "Deurverwiezen op bestaandsnaam, gebrukersnummer, ziednummer of versienummer",
+       "redirect": "Deurverwiezen op bestaandsnaam, gebrukers-, zied-, versie- of logboekregelnummer",
        "redirect-summary": "Disse spesiale zied verwis deur naor n bestaand (as de bestaandsnaam op-egeven wördt), n zied (as n zied- of versienummer op-egeven wördt) of n gebrukerszied (as t gebrukersnummer op-egeven wördt). Gebruuk: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], of [[{{#Special:Redirect}}/user/101]].",
        "redirect-submit": "Zeuk",
        "redirect-lookup": "Opzeuken:",
        "special-characters-group-khmer": "Khmer",
        "special-characters-title-endash": "liggend streepjen",
        "special-characters-title-emdash": "gedachtenstreepjen",
-       "special-characters-title-minus": "minteken",
-       "api-error-blacklisted": "Kies n aandere, beschrievende naam."
+       "special-characters-title-minus": "minteken"
 }
index 73f9631..3fc66f5 100644 (file)
        "minoredit": "Blots lütte Ännern",
        "watchthis": "Op disse Siet oppassen",
        "savearticle": "Siet spiekern",
+       "publishpage": "Siet nee afspiekern",
+       "publishchanges": "ännerte Siet afspiekern",
        "preview": "Vörschau",
        "showpreview": "Vörschau wiesen",
        "showdiff": "Ünnerscheed wiesen",
        "undo-failure": "Kunn de Siet nich op de vörige Version trüchdreihn. De Afsnitt is twischendör al wedder ännert worrn.",
        "undo-norev": "De Ännern kunn nich trüchdreiht warrn, de gifft dat nich oder is wegsmeten worrn.",
        "undo-summary": "Ännern $1 vun [[Special:Contributions/$2|$2]] ([[User talk:$2|Diskuschoon]]) trüchdreiht.",
-       "cantcreateaccounttitle": "Brukerkonto kann nich anleggt warrn",
        "cantcreateaccount-text": "Dat Opstellen vun Brukerkonten vun de IP-Adress '''$1''' ut is vun [[User:$3|$3]] sperrt worrn.\n\nDe Grund weer: ''$2''",
        "viewpagelogs": "Logbook för disse Siet",
        "nohistory": "Disse Siet hett keen Vörgeschicht.",
        "expand_templates_remove_comments": "Kommentaren rutnehmen",
        "expand_templates_generate_xml": "XML-Parser-Boom wiesen",
        "expand_templates_preview": "Vörschau",
-       "pagelang-language": "Spraak"
+       "pagelang-language": "Spraak",
+       "special-characters-group-latin": "Latiensch",
+       "special-characters-group-latinextended": "Latiensch verwiedert",
+       "special-characters-group-ipa": "Internatschonal Phoneetsch Alphabet",
+       "special-characters-group-symbols": "Symbolen",
+       "special-characters-group-greek": "Greeksch",
+       "special-characters-group-greekextended": "Greeksch verwiedert",
+       "special-characters-group-cyrillic": "Kyrillisch",
+       "special-characters-group-arabic": "Araabsch",
+       "special-characters-group-arabicextended": "Araabsch verwiedert",
+       "special-characters-group-persian": "Persisch",
+       "special-characters-group-hebrew": "Hebrääsch",
+       "special-characters-group-bangla": "Bengaalsch",
+       "special-characters-group-tamil": "Tamilsch",
+       "special-characters-group-telugu": "Telugu",
+       "special-characters-group-sinhala": "Singaleesch",
+       "special-characters-group-gujarati": "Gujarati",
+       "special-characters-group-devanagari": "Devanagari",
+       "special-characters-group-thai": "Thailändsch",
+       "special-characters-group-lao": "Laotisch",
+       "special-characters-group-khmer": "Khmer"
 }
index 2e34b74..fd3ccea 100644 (file)
        "faqpage": "Project:धैरै सोधिएका प्रश्नहरु",
        "actions": "कार्यहरु",
        "namespaces": "नेमस्पेस",
-       "variants": "बहà¥\81रà¥\81पहरà¥\81",
+       "variants": "बहà¥\81रà¥\81पहरà¥\82",
        "navigation-heading": "नेविगेशन मेनू",
        "errorpagetitle": "त्रुटि",
        "returnto": "$1 मा फर्कनुहोस् ।",
        "yourpasswordagain": "पासवर्ड फेरि टाईप गर्नुहोस्",
        "createacct-yourpasswordagain": "पासवर्ड निश्चित गर्नुहोस्",
        "createacct-yourpasswordagain-ph": "फेरि पासवर्ड लेख्नुहोस्",
-       "remembermypassword": "यो कम्प्युटरमा मेरो प्रवेश याद राख्ने (धेरैमा $1 {{PLURAL:$1|दिन|दिनहरू}})",
        "userlogin-remembermypassword": "मलाई प्रवेश गराइराख्ने",
        "userlogin-signwithsecure": "सुक्षित जडान प्रयोग गर्ने",
        "yourdomainname": "तपाईंको ज्ञानक्षेत्र(डोमेन):",
        "passwordreset-emailtext-user": "{{SITENAME}} को $1 प्रयोगकर्ताले  {{SITENAME}} ($4)को लागि खाता विवरणको निम्ति एउटा अनुस्मारकको अनुरोध गरेको छ। निम्न प्रयोगकर्ता {{PLURAL:$3|खाता यस इमेल ठेगानासित सम्बन्धित छ|खाताहरू यस इमेल ठेगानासित सम्बन्धित छन्}}:\n\n$2\n\n{{PLURAL:$3|यो अस्थाई पासवर्डको|यी अस्थाई पासवर्डहरूको}} समय {{PLURAL:$5|एक दिन|$5 दिन}}मा सकिनेछ।\nतपाईंले प्रवेश गरेर अहिले नैं नयाँ पासवर्ड छान्नुहोस्। यदि अरु कसैले अनुरोध गरेको भए अथवा यदि तपाईंलाई मूल पासवर्ड याद भए अनि यसलाई परिवर्तन गर्न चाहनुहुन्न भनें, तपाईंले यस सन्देशलाई अनदेखा गर्नुहोस् र पुरानै पासवर्डलाई चालू राख्नुहोस्।",
        "passwordreset-emailelement": "प्रयोगकर्ताको नाम: \n$1\n\nअस्थाई पासवर्ड: \n$2",
        "passwordreset-emailsentemail": "पासवर्ड परिवर्तनको लागि इमेल पठाइएको छ।",
-       "passwordreset-emailsent-capture": "पासवर्ड परिवर्तनको लागि इमेल पठाइयो, जुन तल देखाइएकोछ।",
-       "passwordreset-emailerror-capture": "पासवर्ड रिसेट इमेल जारि गरिएको छ, जुन तल देखाइएको छ, तर यसलाई {{GENDER:$2|प्रयोगकर्ता}}मा पठाउन विफल भयो: $1",
        "changeemail": "इमेल ठेगाना परिवर्तन गर्नुहोस",
        "changeemail-header": "खाताको इमेल ठेगाना परिवर्तन गर्नुहोस",
        "changeemail-no-info": "यस पृष्ठमा सिधै जानको लागि प्रवेश गर्नु पर्ने हुन्छ ।",
        "image_tip": "इम्बेडेड(जडान गरिएको) फाइल",
        "media_sample": "उदाहरण.ogg",
        "media_tip": "फाइल लिङ्क",
-       "sig_tip": "तपाà¤\88à¤\81को समयछाप सहितको दस्तखत",
+       "sig_tip": "तपाà¤\88à¤\82को समयछाप सहितको दस्तखत",
        "hr_tip": "क्षितिजिय रेखा (कम प्रयोग गर्नुहोस्)",
        "summary": "सारांश:",
        "subject": "विषय/शीर्षक:",
        "showpreview": "पूर्वालोकन देखाउनुहोस्",
        "showdiff": "परिवर्तन देखाउनुहोस्",
        "blankarticle": "<strong>चेतावनी:</strong> तपाईं एउटा खालि पृष्ठको निर्माण गर्दै हुनुहुन्छ।\nयदि तपाईं \"{{int:savearticle}}\" लाई पुनः थिच्नुहुन्छ भने पृष्ठ बिना कुनै सामग्री नै निर्मित गरिनेछ।",
-       "anoneditwarning": "<strong>à¤\9aà¥\87तावनà¥\80:</strong> à¤¤à¤ªà¤¾à¤\88à¤\81लà¥\87 à¤ªà¥\8dरवà¥\87श à¤\97रà¥\8dनà¥\81 à¤­à¤\8fà¤\95à¥\8b à¤\9bà¥\88न à¥¤ à¤¤à¤ªà¤¾à¤\88à¤\81à¤\95à¥\8b à¤\86à¤\87पि à¤ à¥\87à¤\97ाना à¤ªà¥\83षà¥\8dठ à¤¸à¤®à¥\8dपादन à¤\87तिहासमा à¤¦à¤°à¥\8dता à¤\97रिनà¥\87 à¤\9b à¤° à¤¯à¥\8b à¤¸à¤¬à¥\88लà¥\87 à¤¹à¥\87रà¥\8dन à¤¸à¤\95à¥\8dà¤\9bन à¥¤ à¤¯à¤¦à¤¿ à¤¤à¤ªà¤¾à¤\88à¤\82 <strong>[$1 à¤²à¤\97à¤\88न]</strong> à¤µà¤¾ <strong>[$2 à¤¨à¤¯à¤¾à¤\81 à¤\96ाता à¤¬à¤¨à¤¾à¤\89नà¥\87] à¤\97रà¥\8dनà¥\81भयà¥\8b à¤­à¤¨à¥\87 à¤¤à¤ªà¤¾à¤\88à¤\82दà¥\8dवारा à¤\97रिà¤\8fà¤\95à¥\8b à¤¸à¤®à¥\8dपादन à¤¤à¤ªà¤¾à¤\88à¤\82à¤\95à¥\8b à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dतानाममा à¤\9cà¥\8bडिनà¥\87à¤\9b।",
+       "anoneditwarning": "<strong>à¤\9aà¥\87तावनà¥\80:</strong> à¤¤à¤ªà¤¾à¤\88à¤\82लà¥\87 à¤ªà¥\8dरवà¥\87श à¤\97रà¥\8dनà¥\81 à¤­à¤\8fà¤\95à¥\8b à¤\9bà¥\88न à¥¤ à¤¤à¤ªà¤¾à¤\88à¤\82à¤\95à¥\8b à¤\86à¤\87पि à¤ à¥\87à¤\97ाना à¤ªà¥\83षà¥\8dठ à¤¸à¤®à¥\8dपादन à¤\87तिहासमा à¤¦à¤°à¥\8dता à¤\97रिनà¥\87 à¤\9b à¤° à¤¯à¥\8b à¤¸à¤¬à¥\88लà¥\87 à¤¹à¥\87रà¥\8dन à¤¸à¤\95à¥\8dà¤\9bनà¥\8d à¥¤ à¤¯à¤¦à¤¿ à¤¤à¤ªà¤¾à¤\88à¤\82 <strong>[$1 à¤²à¤\97à¤\88न]</strong> à¤µà¤¾ <strong>[$2 à¤¨à¤¯à¤¾à¤\81 à¤\96ाता à¤¬à¤¨à¤¾à¤\89नà¥\87] à¤\97रà¥\8dनà¥\81भयà¥\8b à¤­à¤¨à¥\87 à¤¤à¤ªà¤¾à¤\88à¤\82दà¥\8dवारा à¤\97रिà¤\8fà¤\95à¥\8b à¤¸à¤®à¥\8dपादन à¤¤à¤ªà¤¾à¤\88à¤\82à¤\95à¥\8b à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dतानाममा à¤\9cà¥\8bडिनà¥\87à¤\9b ।",
        "anonpreviewwarning": "''तपाईंले प्रवेश गर्नु भएको छैन। संग्रह (Save) गरेको खण्डमा पृष्ठको इतिहासमा तपाईंको IP ठेगाना अंकित गरिनेछ।''",
        "missingsummary": "'''यादगर्नुहोस् :''' तपाईंले सम्पादन सारांश दिनुभएको छैन ।\nयदि तपाईंले \"{{int:savearticle}}\"  थिच्नुभयो भने , सारांश बिना नै संग्रहित गरिने छ ।",
        "selfredirect": "<strong>चेतावनी:</strong> तपाईं यस पृष्ठलाई आफुमा पुनः निर्देशित गर्दै हुनुहुन्छ।\nहुनसक्छ तपाईं अनुप्रेषितको लागि गलत लक्ष्य निर्दिष्ट गर्दै हुनुहुन्छ, वा गलत पृष्ठको सम्पादन गर्दै हुनुहुन्छ।\nतपाईं पुनः एकपटक \"{{int:savearticle}}\" क्लिक गर्नुहुन्छ, पुनः निर्देशित त्यसै पनि बनाइनेछ।",
        "note": "'''सूचना:'''",
        "previewnote": "'''याद राख्नुहोस् यो केवल पूर्वावलोकन मात्र हो; तपाईंका परिवर्तनहरू संग्रहित भएका छैनन्!'''",
        "continue-editing": "सम्पादन क्षेत्रमा जानुहोस",
-       "previewconflict": "यस à¤ªà¥\82रà¥\8dवावलà¥\8bà¤\95नलà¥\87 à¤¸à¤\82पादन à¤\95à¥\8dषà¥\87तà¥\8dर à¤\95à¥\8b à¤®à¤¾à¤¥à¤¿à¤²à¥\8dलà¥\8b à¤­à¤¾à¤\97à¤\95à¥\8b à¤ªà¤¾à¤  à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dनà¥\87 à¤ à¤¾à¤\89à¤\81à¤\95à¥\8b à¤ªà¤¾à¤ à¤²à¤¾à¤\87 à¤¦à¥\87à¤\96ाà¤\89à¤\81à¤\9b à¤\85नि à¤¤à¤ªà¤¾à¤\87लà¥\87 à¤¯à¤¸à¤²à¤¾à¤\87 सेभ गरेपछि देखापर्छ।",
+       "previewconflict": "यस à¤ªà¥\82रà¥\8dवावलà¥\8bà¤\95नलà¥\87 à¤¸à¤®à¥\8dपादन à¤\95à¥\8dषà¥\87तà¥\8dर à¤\95à¥\8b à¤®à¤¾à¤¥à¤¿à¤²à¥\8dलà¥\8b à¤­à¤¾à¤\97à¤\95à¥\8b à¤ªà¤¾à¤  à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dनà¥\87 à¤ à¤¾à¤\89à¤\81à¤\95à¥\8b à¤ªà¤¾à¤ à¤²à¤¾à¤\87 à¤¦à¥\87à¤\96ाà¤\89à¤\81à¤\9b à¤\85नि à¤¤à¤ªà¤¾à¤\88à¤\82लà¥\87 à¤¯à¤¸à¤²à¤¾à¤\88 सेभ गरेपछि देखापर्छ।",
        "session_fail_preview": "'''माफ गर्नुहोस्! सत्र-आँकड़ा (session data) हराउनाले हामीले तपाईंको सम्पादन प्रक्रिया अघि बढाउन सकेनौं।.'''\nकृपया पुनः प्रयास गर्नुहोस्।\nयदि फेरि पनि काम भएन भनें, [[Special:UserLogout|बाहिर गई(लग आउट गरी)]]  फेरि प्रवेश गर्नुहोस्।",
-       "session_fail_preview_html": "'''माफ गर्नुहोला! सत्र को डेटा को नोकसान को कारण ले गर्दा तपाइको सम्पादन लाइ जारी राख्न सकिएन।'''\n\n''जावास्क्रिप्ट हमलाहरु रोक्नको लागि यो पूर्वावलोकन लाइ देखाइएको छैन किन कि {{SITENAME}} मा काँचो HTML को प्रयोग गर्न मिल्ने बनाइएको छ।''\n\n'''यदि यो एक वैध प्रयास हो भने, कृपया पुन: प्रयास गर्नुहोला.'''\nयदि अझै पनि काम गरेन भने [[Special:UserLogout|निर्गमन(logging out)]] र पुन:आगमन(login) गर्ने प्रयास गर्नुहोला।",
+       "session_fail_preview_html": "माफ गर्नुहोला ! सेशन डाटा नष्ट भएको कारण तपाईंको परिवर्तन शुरक्षित गर्न सकिएन ।\n\n<em>किनकी {{SITENAME}}मा raw HTML सक्षम छ, जावास्क्रिप्ट हमहरूबाट बचाउनको लागि झलक नहीं देखाइएको छैन ।</em>\n\n<strong>यदी यो तपाईंको वैध सम्पादन यत्न थियो भने कृपया पुनः प्रयास गर्नुहोस् ।</strong>\nयदी यस पनि यस्तै भयो भने कृपया [[Special:UserLogout|लग आउट]] गरेर पुनः लग इन गर्नुहोस् तथा तपाईंको ब्राउजरले यस साइटसँग कुकीजको अनुमति दिन्छ दिन्न जाँच गर्नुहोस् ।",
        "token_suffix_mismatch": "'''सम्पादन टोकनमा विराम चिह्न र वर्ण सम्बन्धित गड़बड़ीको कारण तपाईंको सम्पादन अस्वीकार गरिएको छ'''\nपृष्ठको पाठ बचाउन सम्पादन अस्वीकार गरिएको हो।\nयस्तो त्यसबेला हुन्छ जब तपाईंले बगी वेवमा आधारित अज्ञात प्रोक्सी सेवा प्रयोग गर्नुहुन्छ।",
        "edit_form_incomplete": "'''सम्पादनको केहि भाग सर्वरसम्म पुग्न सकेन, दुइपल्ट जाँच गर्नुहोस्, तपाईंको सम्पादन यथावत रहे पुनः प्रयास गर्नुहोस्'''",
-       "editing": "$1 à¤¸à¤®à¥\8dपादन à¤\97रिà¤\81दà¥\88",
+       "editing": "$1 सम्पादन गरिदै",
        "creating": "$1 बनाइँदै",
-       "editingsection": "$1 (à¤\96णà¥\8dड) à¤¸à¤®à¥\8dपादन à¤\97रिà¤\81दà¥\88",
+       "editingsection": "$1 (खण्ड) सम्पादन गरिदै",
        "editingcomment": "$1 सम्पादन गर्दै(नयाँ खण्ड)",
        "editconflict": "सम्पादन बाँझियो: $1",
        "explainconflict": "तपाईंले सम्पादन कार्य सुरु गरेपछि कसैले यस पृष्टलाई परिवर्तन गरेकोछ।\nमाथिल्लो पाठक्षेत्रमा पृष्ठको वर्तमान पाठ छ।\nतपाईंको परिवर्तन तल्लो भागमा दर्शाइएकोछ। \nतपाईंले गर्नुभएको परिवर्तनलाई वर्तमान पाठसित मिसाउनु पर्नेछ, यदि तपाईंले \"{{int:savearticle}}\" थिच्नु भयो भनें पाठको माथिल्लो भाग '''मात्र''' संग्रह गरिनेछ।",
        "undo-nochange": "सम्पादन पहिला नै विफल गरिएको छ ।",
        "undo-summary": "  $1द्वारा पुनरावलोकन [[Special:Contributions/$2|$2]] ([[User talk:$2|वार्तालाप]]) खारेज गर्नुहोस",
        "undo-summary-username-hidden": "अदृश्य प्रयोगकर्ताको संशोधन $1 लाई पहिला जस्तै बनाउने",
-       "cantcreateaccounttitle": "खाता बनाउन सकिएन",
        "cantcreateaccount-text": "IP ठेगाना ('''$1''')बाट खाता खोल्न  [[User:$3|$3]]द्वारा बन्देज लगाइएको छ।\n\n $3ले दिनुभएको कारण  ''$2'' हो",
        "cantcreateaccount-range-text": "<strong>$1</strong> को श्रेणीमा आउने आइपि ठेगानाबाट, जसमा तपाईंको आइपि ठेगाना (<strong>$4</strong>) सामेल छ, नयाँ खाता सृजना [[User:$3|$3]]द्वारा अवरोधित गरिएको छ। \n\n$3 द्वारा दिइएको कारण: \"$2\"",
        "viewpagelogs": "यस पृष्ठका लगहरू हेर्नुहोस्",
        "diff-multi-manyusers": "($2 {{PLURAL:$2|भन्दा अधिक प्रयोगकर्ता|भन्दा अधिक प्रयोगकर्ताहरू}}द्वारा {{PLURAL:$1|एउटा मध्यवर्ती संशोधन|$1 मध्यवर्ती संशोधनहरू}} नदेखाइएको)",
        "difference-missing-revision": "यस अन्तर {{PLURAL:$2|को एक अवतरण|को $2 अवतरण}} ($1)  {{PLURAL:$2|भेटिएन|खोज्न सकिएन}}।\n\nयो सामान्य रूपमा एउटा हताइएको पृष्ठको अवतरणहरूमा अन्तर खोज्दा हुन्छ । अधिक जानकारी [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} हटाइएको लग]मा हेर्न सकिन्छ।",
        "searchresults": "खोज नतिजाहरू",
-       "searchresults-title": " \"$1\"à¤\95à¥\8b à¤²à¤¾à¤\97ि à¤\96à¥\8bà¤\9c à¤¨à¤¤à¤¿à¤\9cाहरà¥\81",
+       "searchresults-title": " \"$1\"à¤\95à¥\8b à¤²à¤¾à¤\97ि à¤\96à¥\8bà¤\9c à¤¨à¤¤à¤¿à¤\9cाहरà¥\82",
        "titlematches": "पृष्ठ शिर्षक मिल्छ",
        "textmatches": "पृष्ठ पाठ मिल्छ",
        "notextmatches": "अक्षरस् पेज भेटिएन",
        "next-page": "अर्को पृष्ठ",
        "prevn-title": "पहिलेको  $1 {{PLURAL:$1|नतिजा|नतिजाहरु}}",
        "nextn-title": "यस पछिको $1 {{PLURAL:$1|नतिजा |नतिजाहरु}}",
-       "shown-title": "दà¥\87à¤\96ाà¤\89नà¥\87 $1 {{PLURAL:$1|नतिà¤\9cा|नतिà¤\9cाहरà¥\81}} प्रति पृष्ठ",
+       "shown-title": "दà¥\87à¤\96ाà¤\89नà¥\87 $1 {{PLURAL:$1|नतिà¤\9cा|नतिà¤\9cाहरà¥\82}} प्रति पृष्ठ",
        "viewprevnext": "हेर्नुहोस् ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "''' \"[[:$1]]\" नाम गरेको पृष्ठ  यो विकीमा रहेको छ'''",
        "searchmenu-new": "<strong>\"[[:$1]]\" पृष्ठ यस विकिमा बनाउनुहोस्!</strong> {{PLURAL:$2|0=|तपाईंले खोज गरी भटिएको पृष्ठ पनि मिलान गर्नुहोस्।|तपाईंको खोज परिणाम पनि हेर्नुहोस।}}",
        "showingresults": "देखाउँदै  {{PLURAL:$1|'''१''' नतिजा|'''$1''' नतिजाहरू }} , #'''$2''' बाट सुरुहुने ।",
        "showingresultsinrange": "देखाई रहेको छ{{PLURAL:$1|<strong>1</strong> result|<strong>$1</strong> परिणाम}} सम्म पहुँच  #<strong>$2</strong> देखि #<strong>$3</strong> मा।",
        "search-showingresults": "{{PLURAL:$4|<strong>$3</strong> मा बाट <strong>$1</strong> परिणाम|<strong>$3</strong> मा बाट परिणाम <strong>$1 - $2</strong>}}",
-       "search-nonefound": "तपाà¤\88à¤\81को क्वेरीसँग मेल खाने नतिजाहरू भेटिएनन्",
+       "search-nonefound": "तपाà¤\88à¤\82को क्वेरीसँग मेल खाने नतिजाहरू भेटिएनन्",
        "powersearch-legend": "उन्नत खोज",
        "powersearch-ns": "नेमस्पेसेजहरूमा खोज्ने :",
        "powersearch-togglelabel": "जाँच्ने :",
        "recentchangeslinked-feed": "सम्बन्धित परिवर्तनहरू",
        "recentchangeslinked-toolbox": "सम्बन्धित परिवर्तनहरू",
        "recentchangeslinked-title": "\"$1\" सँग सम्बन्धित परिवर्तन",
-       "recentchangeslinked-summary": "यो सूची निर्दिष्ट पृष्ठ (वा निर्दिष्ट श्रेणी)सित जोडिएका भर्खरै परिवर्तन भएका पृष्ठको  हो। [[Special:Watchlist|तपाईँको निगरानी सूची]]का पृष्ठहरू <strong>गाढा अक्षरमा</strong> छन्।",
+       "recentchangeslinked-summary": "यो सूची निर्दिष्ट पृष्ठ (वा निर्दिष्ट श्रेणी)सित जोडिएका भर्खरै परिवर्तन भएका पृष्ठको  हो । [[Special:Watchlist|तपाईंको निगरानी सूची]]का पृष्ठहरू <strong>गाढा अक्षरमा</strong> छन् ।",
        "recentchangeslinked-page": "पृष्ठ नाम:",
        "recentchangeslinked-to": "यसको सट्टा यो पृष्ठसँग जोडिएका पृष्ठहरूको परिवर्तन देखाउने",
        "upload": "फाइल उर्ध्वभरण",
        "filehist-dimensions": "आकारहरू",
        "filehist-filesize": "फाइल आकार",
        "filehist-comment": "टिप्पणी",
-       "imagelinks": "फाà¤\87लà¤\95à¥\8b à¤ªà¥\8dरयà¥\8bà¤\97हरà¥\81",
+       "imagelinks": "फाà¤\87लà¤\95à¥\8b à¤ªà¥\8dरयà¥\8bà¤\97हरà¥\82",
        "linkstoimage": "यस फाइलमा निम्न{{PLURAL:$1|पृष्ठ जोडिन्छ|$1 पृष्ठहरू जोडिन्छन्}}:",
        "linkstoimage-more": "$1 भन्दा अधिक {{PLURAL:$1|पृष्ठ लिङ्क|पृष्ठ लिङ्कहरू}} यस फाइलसँग जोडिएको छ। \nनिम्नलिखित सूची फाइलसँग {{PLURAL:$1|पहिलो पृष्ठ लिङ्क|पहिलो $1 पृष्ठ लिङ्कहरू}} जोडिने देखाउँछ।\n[[Special:WhatLinksHere/$2|पूर्ण सूची]] पनि उपलब्ध छ।",
        "nolinkstoimage": "यो फाईलसंग लिंकभएको कुनै पृष्ठ छैन.",
        "duplicatesoffile": "निम्नलिखित {{PLURAL:$1|फाइलको प्रतिलिपि हो|$1 फाइलहरूको प्रतिलिपि हो}} ([[Special:FileDuplicateSearch/$2|अधिक जानकारीहरू]]):",
        "sharedupload": "यो फाइल $1 को हो र अन्य परियोजनामा प्रयोग गरिएको हुनसक्छ।",
        "sharedupload-desc-there": "यो फाइल $1 बाट हो र अन्य परियोजनाहरू द्वारा पनि प्रयोग गर्न सकिनेछ। अधिक जानकारीको लागि कृपया [$2 फाइल विवरण पृष्ठ] हेर्नुहोस।",
-       "sharedupload-desc-here": "यà¥\8b à¤«à¤¾à¤\87ल $1 à¤¬à¤¾à¤\9f à¤¹à¥\8b à¤° à¤\85नà¥\8dय à¤ªà¤°à¤¿à¤¯à¥\8bà¤\9cनाहरà¥\82 à¤¦à¥\8dवारा à¤ªà¤¨à¤¿ à¤ªà¥\8dरयà¥\8bà¤\97 à¤\97रà¥\8dन à¤¸à¤\95िनà¥\8dà¤\9b। \nतà¥\8dयहाà¤\81 à¤¨à¥\87र à¤¯à¤¸à¤\95à¥\8b [$2 à¤«à¤¼à¤¾à¤\87ल à¤µà¤¿à¤µà¤°à¤£ à¤ªà¥\83षà¥\8dठ]मा à¤°à¤¹à¥\87à¤\95à¥\8b à¤µà¤¿à¤µà¤°à¤£ à¤¤à¤² à¤¦à¤¿à¤\87à¤\8fà¤\95à¥\8b à¤\9b।",
+       "sharedupload-desc-here": "यो फाइल $1 बाट हो र अन्य परियोजनाहरू द्वारा पनि प्रयोग गर्न सकिन्छ। \nत्यहाँ नेर यसको [$2 फाइल विवरण पृष्ठ]मा रहेको विवरण तल दिइएको छ।",
        "sharedupload-desc-edit": "यो फाइल $1 बाट हो र अन्य परियोजनाहरू द्वारा पनि प्रयोग गर्न सकिन्छ। \nशायद तपाईं [$2 त्यहाँ यसको फाइल विवरण पृष्ठ]लाई सम्पादित गर्न चाहनुहुन्छ।",
        "sharedupload-desc-create": "यो फाइल $1 बाट हो र अन्य परियोजनाहरू द्वारा पनि प्रयोग गर्न सकिन्छ। \nशायद तपाईं [$2 त्यहाँ यसको फाइल विवरण पृष्ठ]लाई सम्पादित गर्न चाहनुहुन्छ।",
        "filepage-nofile": "यस नामको फाइल छैन।",
        "shared-repo-from": " $1 बाट",
        "shared-repo": "एल साझा भण्डार",
        "shared-repo-name-wikimediacommons": "विकिमीडिया कमन्स",
-       "upload-disallowed-here": "तपाà¤\88à¤\81ले यो फाइल अधिलेखन गर्न सक्नुहुन्न ।",
+       "upload-disallowed-here": "तपाà¤\88à¤\82ले यो फाइल अधिलेखन गर्न सक्नुहुन्न ।",
        "filerevert": "पूर्वस्थिति $1 मा फर्काउने",
        "filerevert-legend": " फाइल पूर्वस्थितीमा फर्काउने",
        "filerevert-intro": "तपाईं <strong>[[Media:$1|$1]]</strong>लाई [$4 $2 मा $3 बजेको अवतरण] लाई पूर्ववत गर्दै हुनुहुन्छ।",
        "double-redirect-fixed-maintenance": "[[$1]]बाट [[$2]]मा दोहोरो अनुप्रेषण स्वत तय गरिंदै।",
        "double-redirect-fixer": "अनुप्रेषण तय गर्ने",
        "brokenredirects": "टुटेका रिडाइरेक्टहरू",
-       "brokenredirectstext": "तलà¤\95ा à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\81 à¤²à¥\87 à¤¹à¥\81दà¥\88 à¤¨à¤­à¤\8fà¤\95ा à¤ªà¥\83षà¥\8dठहररसँग जोडिन्छन्:",
+       "brokenredirectstext": "तलà¤\95ा à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\82लà¥\87 à¤¹à¥\81à¤\81दà¥\88 à¤¨à¤­à¤\8fà¤\95ा à¤ªà¥\83षà¥\8dठहरà¥\82सँग जोडिन्छन्:",
        "brokenredirects-edit": "सम्पादन",
        "brokenredirects-delete": "मेट्ने",
        "withoutinterwiki": "भाषा नभएको पृष्ठहरू",
        "protectedpages-timestamp": "समय चिन्ह",
        "protectedpages-page": "पृष्ठ",
        "protectedpages-expiry": "सकिनेछ",
-       "protectedpages-performer": "पà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता à¤¸à¥\81रà¤\95à¥\8dषित à¤\97रिà¤\81दà¥\88",
+       "protectedpages-performer": "प्रयोगकर्ता सुरक्षित गरिदै",
        "protectedpages-params": "सुरक्षा प्यारामेटर",
        "protectedpages-reason": "कारण",
        "protectedpages-submit": "पानाहरू देखाउनुहोस्",
        "listgrouprights-group": "समूह",
        "listgrouprights-rights": "अधिकारहरु",
        "listgrouprights-helppage": "Help:सामूहिक अधिकारहरु",
-       "listgrouprights-members": "(सदसà¥\8dयहरà¥\81को सूची)",
+       "listgrouprights-members": "(सदसà¥\8dयहरà¥\82को सूची)",
        "listgrouprights-addgroup": "{{PLURAL:$2|समूह|समूहहरु}}: $1 थप्ने",
        "listgrouprights-removegroup": "{{PLURAL:$2|समूह|समूहहरु}}: $1 हटाउने",
        "listgrouprights-addgroup-all": "सबै समूह थप्ने",
        "watchlist-options": "निगरानि सूची विकल्प",
        "watching": "निगरानी गर्दै...",
        "unwatching": "निगरानीबाट हटाउँदै...",
-       "watcherrortext": "\"$1\"à¤\95à¥\8b à¤²à¤¾à¤\97ि à¤¤à¤ªà¤¾à¤\87à¤\81à¤\95à¥\8b à¤¨à¤¿à¤\97रानà¥\80 à¤¸à¥\81à¤\9aà¥\80 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dनà¥\87 à¤\95à¥\8dरममा à¤¯à¥\8cà¤\9fा à¤¤à¥\8dरà¥\81à¤\9fà¥\80 à¤­à¤\8fà¤\95à¥\8b à¤\9b।",
+       "watcherrortext": "\"$1\"à¤\95à¥\8b à¤²à¤¾à¤\97ि à¤¤à¤ªà¤¾à¤\88à¤\82à¤\95à¥\8b à¤¨à¤¿à¤\97रानà¥\80 à¤¸à¥\81à¤\9aà¥\80 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रà¥\8dनà¥\87 à¤\95à¥\8dरममा à¤\8fà¤\89à¤\9fा à¤¤à¥\8dरà¥\81à¤\9fà¥\80 à¤­à¤\8fà¤\95à¥\8b à¤\9b ।",
        "enotif_reset": "सबै पृष्ठहरू भनी दाग दिने",
        "enotif_impersonal_salutation": "{{SITENAME}} प्रयोगकर्ता",
        "enotif_subject_deleted": "{{SITENAME}} पृष्ठ $1 $2 ले {{GENDER:$2|मेटाउनु}} भयो ।",
        "contributions": "{{GENDER:$1|प्रयोगकर्ता}}का योगदानहरू",
        "contributions-title": "$1को प्रयोगकर्ता योगदानहरू",
        "mycontris": "योगदानहरू",
+       "anoncontribs": "योगदानहरू",
        "contribsub2": " {{GENDER:$3|$1}} ($2)को लागि",
        "contributions-userdoesnotexist": "प्रयोगकर्ता \"$1\" दर्ता गरिएको छैन् ।",
        "nocontribs": "यस मापदण्ड अनुसार परिवर्त पाइएन।",
        "whatlinkshere-links": "← लिंकहरू",
        "whatlinkshere-hideredirs": "$1 अनुप्रेषित हुन्छ",
        "whatlinkshere-hidetrans": "$1 पारदर्शन",
-       "whatlinkshere-hidelinks": "$1 à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\81",
+       "whatlinkshere-hidelinks": "$1 à¤²à¤¿à¤\99à¥\8dà¤\95हरà¥\82",
        "whatlinkshere-hideimages": "$1 फाइल लिंकहरू",
        "whatlinkshere-filters": "फिल्टरहरू",
        "whatlinkshere-submit": "जानुहोस्",
        "ipb-edit-dropdown": "निषेध कारण सम्पादन गर्नुहोस्",
        "ipb-unblock-addr": "$1 निषेध खारेज गर्ने",
        "ipb-unblock": "प्रयोगकर्ता वा IP माथिको निषेध खारेज गर्ने",
-       "ipb-blocklist": "हाल à¤°à¤¹à¥\87à¤\95ा à¤¨à¤¿à¤·à¥\87धहरà¥\81 हेर्नुहोस्",
+       "ipb-blocklist": "हाल à¤°à¤¹à¥\87à¤\95ा à¤¨à¤¿à¤·à¥\87धहरà¥\82 हेर्नुहोस्",
        "ipb-blocklist-contribs": "{{GENDER:$1|$1}}को लागि योगदान",
        "unblockip": "प्रयोगकर्ताको निषेध खारेज गर्नुहोस्",
        "unblockiptext": "IP ठेगाना अथवा प्रयोगकर्तामाथि पहिले लगाइएको रोक फुकुवा गर्न तलको प्रपत्र प्रयोग गर्नुहोस्।",
        "import-upload-filename": "फाइल नाम:",
        "import-comment": "टिप्पणी :",
        "importtext": "कृपया स्रोत विकिबाट फाइल निर्यात गर्नका लागि [[Special:Export|निर्यात सुविधा]]को प्रयोग गर्नुहोस। यसलाई आफ्नो कम्प्युटरमा सङ्ग्रह गरे यहाँ अपलोड गर्नुहोस।",
-       "importstart": "पà¥\83षà¥\8dठ à¤\86यात à¤\97रिà¤\81दà¥\88...",
+       "importstart": "पृष्ठ आयात गरिदै...",
        "import-revision-count": "$1 {{PLURAL:$1|पुनरावलोकन|पुनरावलोकनहरु}}",
        "importnopages": "आयातगर्नको लागि कुनै पृष्ठ छैन।",
        "imported-log-entries": "आयातित $1 {{PLURAL:$1|लग प्रविष्टी|लग प्रविष्टीहरू}}",
        "javascripttest": "JavaScript जाँच गरिदै",
        "javascripttest-pagetext-unknownaction": "अज्ञात कारवाही \"$1\" ।",
        "javascripttest-qunit-intro": "mediawiki.org मा [$1 जाँचको कागजात] हेर्नुहोस् ।",
-       "tooltip-pt-userpage": "तपाईंको प्रयोगकर्ता पृष्ठ",
+       "tooltip-pt-userpage": "{{GENDER:| तपाईंको प्रयोगकर्ता}} पृष्ठ",
        "tooltip-pt-anonuserpage": "तपाईँ जुन IP ठेगानाको रुपमा सम्पादन गर्दै हुनुहुन्छ , त्यसको प्रयोगकर्ता पृष्ठ निम्न छ :",
-       "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-login": "तपाà¤\88à¤\81लाà¤\88 à¤ªà¥\8dरवà¥\87शगर्न सुझाव दिइन्छ ; तर यो जरुरी भने छैन",
+       "tooltip-pt-mycontris": "{{GENDER:|तपाईंका}} योगदानहरूको सूची",
+       "tooltip-pt-login": "तपाà¤\88à¤\82लाà¤\88 à¤ªà¥\8dरवà¥\87स गर्न सुझाव दिइन्छ ; तर यो जरुरी भने छैन",
        "tooltip-pt-logout": "निर्गमन (लग आउट) गर्नुहोस्",
-       "tooltip-pt-createaccount": "तपाईंलाई खाता बनाउन र लग इन गर्न हामि प्रोत्साहित गर्छौ; तथापि, यो अनिवार्य भने छैन।",
+       "tooltip-pt-createaccount": "तपाईंलाई खाता बनाउन र लग इन गर्न हामि प्रोत्साहित गर्छौ; तथापि, यो अनिवार्य भने छैन ।",
        "tooltip-ca-talk": "सामग्री पृष्ठबारेमा छलफल",
        "tooltip-ca-edit": "यो पृष्ठ सम्पादन गर्ने",
        "tooltip-ca-addsection": "नयाँ खण्ड सुरु गर्नुहोस्",
        "tooltip-ca-viewsource": "यो पृष्ठ सुरक्षित गरिएको छ। यसको श्रोत हेर्न सक्नुहुन्छ।",
-       "tooltip-ca-history": "यस à¤ªà¥\83षà¥\8dठà¤\95à¥\8b à¤ªà¤¹à¤¿à¤²à¥\87à¤\95ा à¤ªà¥\81नरावलà¥\8bà¤\95नहरà¥\81",
+       "tooltip-ca-history": "यस à¤ªà¥\83षà¥\8dठा à¤ªà¤¹à¤¿à¤²à¥\87à¤\95ा à¤ªà¥\81नरावलà¥\8bà¤\95नहरà¥\82",
        "tooltip-ca-protect": "यो पृष्ठलाई संरक्षित गर्नुहोस्",
        "tooltip-ca-unprotect": "यस पृष्ठको सुरक्षा परिवर्तन गर्ने",
        "tooltip-ca-delete": "यो पृष्ठ मेटाउनुहोस्",
        "tooltip-ca-undelete": "मेटिएको भए पनि यो पृष्ठको सम्पादनहरू पुन:प्राप्त गर्नुहोस्",
        "tooltip-ca-move": "यो पृष्ठलाई सार्नुहोस्",
-       "tooltip-ca-watch": "यà¥\8b à¤ªà¥\83षà¥\8dठलाà¤\88 à¤¤à¤ªà¤¾à¤\88à¤\81को अवलोकनसूचीमा थप्नुहोस्",
+       "tooltip-ca-watch": "यà¥\8b à¤ªà¥\83षà¥\8dठलाà¤\88 à¤¤à¤ªà¤¾à¤\88à¤\82को अवलोकनसूचीमा थप्नुहोस्",
        "tooltip-ca-unwatch": "यो पृष्ठलाई तपाईँको अवलोकनसूचीबाट हटाउनुहोस्",
        "tooltip-search": "{{SITENAME}} मा खोज्नुहोस्",
        "tooltip-search-go": "यदि यो नामको पृष्ठ रहेको छ भने त्यसमा जाने",
        "tooltip-search-fulltext": "यो पाठको लागि पृष्ठहरू खोज्नुहोस्",
        "tooltip-p-logo": "मुख्य पृष्ठ",
        "tooltip-n-mainpage": "मुख्य पृष्ठमा जाने",
-       "tooltip-n-mainpage-description": "मà¥\81à¤\96à¥\8dय à¤ªà¥\83षà¥\8dठमा à¤\9cानà¥\81हà¥\8bà¥\8dसà¥\8d",
-       "tooltip-n-portal": "à¤\86यà¥\8bà¤\9cनाà¤\95ा à¤¬à¤¾à¤°à¥\87मा, à¤¤à¤ªà¤¾à¤\88à¤\81 के गर्न सक्नुहुन्छ, सामग्री कहाँ भेट्टाउने",
+       "tooltip-n-mainpage-description": "मुख्य पृष्ठमा जानुहोस्",
+       "tooltip-n-portal": "à¤\86यà¥\8bà¤\9cनाà¤\95ा à¤¬à¤¾à¤°à¥\87मा, à¤¤à¤ªà¤¾à¤\88à¤\82 के गर्न सक्नुहुन्छ, सामग्री कहाँ भेट्टाउने",
        "tooltip-n-currentevents": "हालैको घटनाको बारेमा पृष्ठभूमि जानकारी पत्ता लगाउनुहोस्",
-       "tooltip-n-recentchanges": "विà¤\95िमा à¤\97रिà¤\8fà¤\95ा à¤¹à¤¾à¤²à¥\88à¤\95ा à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनहरà¥\81को सूची",
-       "tooltip-n-randompage": "à¤\9cà¥\81न à¤\95à¥\81नà¥\88 पृष्ठ खोल्ने",
+       "tooltip-n-recentchanges": "विà¤\95िमा à¤\97रिà¤\8fà¤\95ा à¤¹à¤¾à¤²à¥\88à¤\95ा à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनहरà¥\82को सूची",
+       "tooltip-n-randompage": "à¤\95à¥\81नà¥\88 à¤\8fà¤\95 पृष्ठ खोल्ने",
        "tooltip-n-help": "पत्तालगाउनु पर्ने स्थान",
        "tooltip-t-whatlinkshere": "यो सँग जोडिएका सबै विकि पृष्ठहरूको सूची",
        "tooltip-t-recentchangeslinked": "यस पृष्ठमा जोडिएका पृष्ठहरूमा हालैको परिवर्तन",
        "tooltip-feed-rss": "यो पृष्ठको लागि RSS फिड",
        "tooltip-feed-atom": "यो पृष्ठको लागि एटम फिड",
-       "tooltip-t-contributions": "यस प्रयोगकर्ताका योगदानहरूको सूची हेर्नुहोस्",
+       "tooltip-t-contributions": "{{GENDER:$1|यस प्रयोगकर्ता}}का योगदानहरूको सूची हेर्नुहोस्",
        "tooltip-t-emailuser": "यो प्रयोगकर्तालाई इमेल पठाउनुहोस्",
        "tooltip-t-info": "यस पृष्ठको बारेमा थप जानकारी",
        "tooltip-t-upload": "फाइल अपलोड गर्ने",
        "tooltip-ca-nstab-help": "सहायता पृष्ठ हेर्नुहोस्",
        "tooltip-ca-nstab-category": "श्रेणी पृष्ठ हेर्ने",
        "tooltip-minoredit": "यसलाई सामान्य सम्पादनको रुपमा चिनो लगाउने",
-       "tooltip-save": "तपाà¤\88à¤\81लà¥\87 à¤\97रà¥\87à¤\95ा à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनहरà¥\82 à¤¸à¤\82ग्रह गर्नुहोस्",
-       "tooltip-preview": "तपाà¤\88à¤\81à¤\95à¥\8b à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनà¤\95à¥\8b à¤ªà¥\82रà¥\8dवरà¥\82प , à¤\95à¥\83पया à¤¸à¤\82ग्रह गर्नु अघि यो प्रयोग गर्नुहोला !",
-       "tooltip-diff": "तपाà¤\88à¤\81ले पाठमा के के परिवर्तन गर्नुभयो भनेर देखाउने",
+       "tooltip-save": "तपाà¤\88à¤\82à¤\95ा à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनहरà¥\82 à¤¸à¤\99à¥\8dग्रह गर्नुहोस्",
+       "tooltip-preview": "तपाà¤\88à¤\82à¤\95à¥\8b à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनà¤\95à¥\8b à¤ªà¥\82रà¥\8dवरà¥\82प , à¤\95à¥\83पया à¤¸à¤\99à¥\8dग्रह गर्नु अघि यो प्रयोग गर्नुहोला !",
+       "tooltip-diff": "तपाà¤\88à¤\82ले पाठमा के के परिवर्तन गर्नुभयो भनेर देखाउने",
        "tooltip-compareselectedversions": "यस पृष्ठको छानिएका दुई पुनरावलोकन बीच फरक हेर्नुहोस्",
        "tooltip-watch": "यो पृष्ठलाई तपाईँको अवलोकनसूचीमा थप्नुहोस्",
        "tooltip-watchlistedit-normal-submit": "शीर्षकहरू हटाउने",
        "widthheight": "$1 × $2",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|पृष्ठ|पृष्ठहरू}}",
        "file-info": "फाइल आकार: $1, MIME प्रकार: $2",
-       "file-info-size": "$1 Ã\97 $2 à¤ªà¤¿à¤\95à¥\8dसà¥\87लहरà¥\81, फाइल आकार: $3, MIME प्रकार: $4",
+       "file-info-size": "$1 Ã\97 $2 à¤ªà¤¿à¤\95à¥\8dसà¥\87लहरà¥\82, फाइल आकार: $3, MIME प्रकार: $4",
        "file-info-size-pages": "$1 × $2 पिक्सेलहरू, फाइल आकार: $3, MIME प्रकार: $4, $5 {{PLURAL:$5|पृष्ठ|पृष्ठहरू}}",
        "file-nohires": "उच्च रिजोल्युशन अनुपलब्ध",
        "svg-long-desc": "SVG फाइल,साधारण $1 × $2 पिक्सेलहरु, फाइल आकार: $3",
        "svg-long-error": "अमान्य एसभिजी फाइल: $1",
        "show-big-image": "मूल फाइल",
        "show-big-image-preview": "यस पूर्व रुपको आकार: $1।",
-       "show-big-image-other": "à¤\85रà¥\81 {{PLURAL:$2|resolution|रिà¤\9cà¥\8bलà¥\8dयà¥\81शनहरà¥\81}}: $1।",
+       "show-big-image-other": "à¤\85रà¥\81 {{PLURAL:$2|resolution|रिà¤\9cà¥\8bलà¥\8dयà¥\81शनहरà¥\82}}: $1।",
        "show-big-image-size": "$1 × $2 पिक्सल",
        "file-info-gif-looped": "चकृय गरिएको",
        "file-info-gif-frames": "$1 {{PLURAL:$1|फ्रेम|फ्रेमहरु}}",
        "yesterday-at": "हिजो $1मा",
        "bad_image_list": "(* बाट शुरु हुने पंक्ति)को  विषय सूची मात्र मान्य छ।  पंक्तिको पहिलो लिङ्क नराम्रो फाइलसित लिङ्क हुनैपर्छ । एउटै पंक्तिमा कुनै पछिबाट हुने लिंकलाई अपवाद मानिनेछ अर्थात् जुन पृष्ठमा फाइल इन-लाइन हुनसक्छ।",
        "metadata": "मेटाडेटा",
-       "metadata-help": "यस à¤«à¤¾à¤\87लमा à¤\85तिरिà¤\95à¥\8dत à¤\9cानà¤\95ारà¥\80हरà¥\81 à¤\9bनà¥\8d, à¤¯à¤¸à¤²à¤¾à¤\88 à¤¬à¤¨à¤¾à¤\89न à¤¸à¤®à¥\8dभवतà¤\83 à¤¡à¤¿à¤\9cिà¤\9fल à¤\95à¥\8dयामà¥\87रा à¤\85थवा à¤¸à¥\8dà¤\95à¥\8dयानर à¤ªà¥\8dरयà¥\8bà¤\97 à¤\97रिà¤\8fà¤\95à¥\8b à¤¹à¥\81नà¥\81परà¥\8dà¤\9b। à¤¯à¤¦à¤¿ à¤¯à¤¸ à¤«à¤¾à¤\87ललाà¤\88 à¤®à¥\82ल à¤\85वसà¥\8dथाबाà¤\9f à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रिà¤\8fà¤\95à¥\8b à¤¹à¥\8b à¤­à¤¨à¥\87à¤\82  यस फाइलले  सम्पूर्ण विवरण प्रतिबिम्बित गर्न सक्नेछैन ।",
+       "metadata-help": "यस à¤«à¤¾à¤\87लमा à¤\85तिरिà¤\95à¥\8dत à¤\9cानà¤\95ारà¥\80हरà¥\82 à¤\9bनà¥\8d, à¤¯à¤¸à¤²à¤¾à¤\88 à¤¬à¤¨à¤¾à¤\89न à¤¸à¤®à¥\8dभवतà¤\83 à¤¡à¤¿à¤\9cिà¤\9fल à¤\95à¥\8dयामà¥\87रा à¤\85थवा à¤¸à¥\8dà¤\95à¥\8dयानर à¤ªà¥\8dरयà¥\8bà¤\97 à¤\97रिà¤\8fà¤\95à¥\8b à¤¹à¥\81नà¥\81परà¥\8dà¤\9b à¥¤ à¤¯à¤¦à¤¿ à¤¯à¤¸ à¤«à¤¾à¤\87ललाà¤\88 à¤®à¥\82ल à¤\85वसà¥\8dथाबाà¤\9f à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन à¤\97रिà¤\8fà¤\95à¥\8b à¤¹à¥\8b à¤­à¤¨à¥\87  यस फाइलले  सम्पूर्ण विवरण प्रतिबिम्बित गर्न सक्नेछैन ।",
        "metadata-expand": "लामो विबरण हेर्ने",
        "metadata-collapse": "लामो विवरण लुकाउने",
-       "metadata-fields": "Image metadata fields listed in this message will be included on image page display when the metadata table is collapsed.\nOthers will be hidden by default.\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",
+       "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-imagewidth": "चौडाइ",
        "exif-imagelength": "उचाई",
        "exif-bitspersample": "घटक प्रति बिट्स",
        "revdelete-content-unhid": "सामग्री देखाइएको",
        "revdelete-summary-unhid": "सम्पादन सारांस देखाइएको",
        "revdelete-uname-unhid": "प्रयोगकर्ता देखाइएको",
-       "revdelete-restricted": "पà¥\8dरबनà¥\8dधà¤\95हरà¥\81माथि सीमितता लागू गरियो",
-       "revdelete-unrestricted": "प्रवन्धककोलागि निषेधहरु हटाइयो ।",
+       "revdelete-restricted": "पà¥\8dरबनà¥\8dधà¤\95हरà¥\82 माथि सीमितता लागू गरियो",
+       "revdelete-unrestricted": "प्रवन्धकको लागि निषेधहरू हटाइयो ।",
        "logentry-block-block": "$1 {{GENDER:$2|प्रतिबन्धित}}{{GENDER:$4|$3}} जसमा समय समाप्तिको अवधि छ $5 $6",
        "logentry-block-unblock": "$1 {{GENDER:$2|खुल्ला गरिएो}} {{GENDER:$4|$3}}",
        "logentry-block-reblock": "$1 {{GENDER:$2|परिवर्तन गर्यो}} प्रतिबन्ध सेटिङ्ग {{GENDER:$4|$3}} को लागि जसमा समय समाप्तिको अवधि छ $5 $6",
        "logentry-move-move": "$1 {{GENDER:$2|द्वारा}} $3 पृष्ठलाई $4 मा सारियो",
        "logentry-move-move-noredirect": "$1 ले $3 मा पुनर्निर्देश नछोडि त्यसलाई $4 मा {{GENDER:$2|सारेको}} हो",
        "logentry-move-move_redir": "$1 ले $4 बाट पुनर्निर्देश हटाएर $3 लाई त्यसमाथि {{GENDER:$2|सारेको}} हो",
-       "logentry-move-move_redir-noredirect": "$1 à¤²à¥\87 $4 à¤¬à¤¾à¤\9f à¤ªà¥\81नारà¥\8dनिरà¥\8dदà¥\87श à¤¹à¤\9fाà¤\8fर $3 à¤®à¤¾ à¤ªà¥\81नरà¥\8dनिरà¥\8dदà¥\87श à¤¨à¥\8dनाà¤\9bà¥\8bडि $3 लाई $4 मा {{GENDER:$2|सारेको}} हो",
+       "logentry-move-move_redir-noredirect": "$1 à¤²à¥\87 $4 à¤¬à¤¾à¤\9f à¤ªà¥\81नारà¥\8dनिरà¥\8dदà¥\87श à¤¹à¤\9fाà¤\8fर $3 à¤®à¤¾ à¤ªà¥\81नरà¥\8dनिरà¥\8dदà¥\87श à¤¨à¤\9bà¥\8bडà¥\80 $3 लाई $4 मा {{GENDER:$2|सारेको}} हो",
        "logentry-patrol-patrol": "$1 ले $3 पृष्ठको $4 अवतरणलाई गस्ती गरिएको {{GENDER:$2|चिन्हित}} गरेको हो",
        "logentry-patrol-patrol-auto": "$1 ले $3 पृष्ठको $4 अवतरणलाई स्वचालित रूपले गस्ती गरिएको {{GENDER:$2|चिन्हित}} गरेको हो",
        "logentry-newusers-newusers": "प्रयोगकर्ता खाता $1 {{GENDER:$2|खोलियो}}",
        "special-characters-title-emdash": "इएम ड्यास",
        "special-characters-title-minus": "घटाउने चिन्ह",
        "mw-widgets-titleinput-description-new-page": "हालसम्म पृष्ठ उपलब्ध छैन्",
-       "mw-widgets-titleinput-description-redirect": "$1 मा जाने",
-       "api-error-blacklisted": "एउटा फरक वर्णनात्मक शीर्षक चयन गर्नुहोस् ।"
+       "mw-widgets-titleinput-description-redirect": "$1 मा जाने"
 }
index bfdff5a..442c685 100644 (file)
@@ -78,7 +78,8 @@
                        "Lemondoge",
                        "Dinosaur918",
                        "Jdforrester",
-                       "Jeleniccz"
+                       "Jeleniccz",
+                       "MrLeopold"
                ]
        },
        "tog-underline": "Koppelingen 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 -->",
        "searchbutton": "Zoeken",
        "go": "OK",
        "searcharticle": "OK",
        "history": "Geschiedenis",
        "history_short": "Geschiedenis",
        "updatedmarker": "bewerkt sinds mijn laatste bezoek",
-       "printableversion": "Printervriendelijke versie",
+       "printableversion": "Printvriendelijke versie",
        "permalink": "Permanente koppeling",
        "print": "Afdrukken",
        "view": "Lezen",
        "yourpasswordagain": "Geef uw wachtwoord opnieuw in:",
        "createacct-yourpasswordagain": "Bevestig wachtwoord",
        "createacct-yourpasswordagain-ph": "Geef het wachtwoord opnieuw in",
-       "remembermypassword": "Aanmeldgegevens onthouden (maximaal $1 {{PLURAL:$1|dag|dagen}})",
        "userlogin-remembermypassword": "Aangemeld blijven",
        "userlogin-signwithsecure": "Beveiligde verbinding gebruiken",
        "cannotloginnow-title": "Niet mogelijk om aan te melden",
        "createacct-reason-ph": "Waarom u een andere account aanmaakt",
        "createacct-submit": "Account aanmaken",
        "createacct-another-submit": "Account aanmaken",
+       "createacct-continue-submit": "Doorgaan met het maken van een account",
+       "createacct-another-continue-submit": "Doorgaan met het maken van een account",
        "createacct-benefit-heading": "{{SITENAME}} wordt gemaakt door mensen zoals u.",
        "createacct-benefit-body1": "bewerking{{PLURAL:$1||en}}",
        "createacct-benefit-body2": "pagina{{PLURAL:$1||'s}}",
        "botpasswords-created-title": "Botwachtwoord aangemaakt",
        "botpasswords-created-body": "Het botwachtwoord voor botnaam \"$1\" van gebruiker \"$2\" is gemaakt.",
        "botpasswords-updated-title": "Botwachtwoord bijgewerkt",
-       "botpasswords-updated-body": "Het botwachtwoord \"$1\" is succesvol bijgewerkt.",
+       "botpasswords-updated-body": "Het botwachtwoord voor de bot \"$1\" van gebruiker \"$2\" is succesvol bijgewerkt.",
        "botpasswords-deleted-title": "Botwachtwoord verwijderd",
-       "botpasswords-deleted-body": "Het botwachtwoord \"$1\" is verwijderd.",
+       "botpasswords-deleted-body": "Het botwachtwoord voor de bot \"$1\" van gebruiker \"$2\" is verwijderd.",
        "botpasswords-newpassword": "Het nieuwe wachtwoord om aan te melden met <strong>$1</strong> is nu <strong>$2</strong>. <em>Bewaar dit goed voor toekomstig gebruik.</em>",
        "botpasswords-no-provider": "BotPasswordsSessionProvider is niet beschikbaar.",
        "botpasswords-restriction-failed": "Botwachtwoordbeperkingen maken het aanmelden onmogelijk.",
        "passwordreset-emailelement": "Gebruikersnaam: \n$1\n\nTijdelijk wachtwoord: \n$2",
        "passwordreset-emailsentemail": "Als dit e-mailadres aan uw account gekoppeld is, dan wordt er een e-mail verzonden om uw wachtwoord opnieuw in te stellen.",
        "passwordreset-emailsentusername": "Als er een e-mailadres geregistreerd is voor die gebruikersnaam, dan wordt er een e-mail verzonden om uw wachtwoord opnieuw in te stellen.",
-       "passwordreset-emailsent-capture": "Er is een e-mail voor het opnieuw instellen van een wachtwoord verzonden. Deze wordt hieronder weergegeven.",
-       "passwordreset-emailerror-capture": "Er is een e-mail voor het opnieuw instellen van een wachtwoord aangemaakt. Deze wordt hieronder weergegeven. Het verzenden naar de {{GENDER:$2|gebruiker}} is mislukt om de volgende reden: $1",
        "passwordreset-invalideamil": "Ongeldig e-mailadres",
        "changeemail": "E-mailadres wijzigen of verwijderen",
        "changeemail-header": "Vul dit formulier in om uw e-mailadres te wijzigen. Als u het e-mailadres wilt ontkoppelen van uw account, laat het e-mailadres dan leeg als u het formulier opslaat.",
-       "changeemail-passwordrequired": "U  moet uw wachtwoord invoeren om deze wijziging te bevestigen.",
        "changeemail-no-info": "U moet aangemeld zijn om rechtstreeks toegang te hebben tot deze pagina.",
        "changeemail-oldemail": "Huidig e-mailadres:",
        "changeemail-newemail": "Nieuw e-mailadres:",
        "minoredit": "Dit is een kleine bewerking",
        "watchthis": "Deze pagina volgen",
        "savearticle": "Pagina opslaan",
+       "savechanges": "Wijzigingen opslaan",
        "publishpage": "Pagina publiceren",
+       "publishchanges": "Wijzigingen publiceren",
        "preview": "Voorvertoning",
        "showpreview": "Bewerking ter controle bekijken",
        "showdiff": "Wijzigingen bekijken",
        "userpage-userdoesnotexist": "U bewerkt een gebruikerspagina van een gebruiker die niet bestaat (gebruiker \"$1\").\nControleer of u deze pagina wel wilt aanmaken of bewerken.",
        "userpage-userdoesnotexist-view": "De gebruiker \"$1\" is niet geregistreerd.",
        "blocked-notice-logextract": "Deze gebruiker is op het moment geblokkeerd.\nDe laatste regel uit het blokkeerlogboek wordt hieronder ter referentie weergegeven:",
-       "clearyourcache": "'''Let op!''' Nadat u de wijzigingen hebt opgeslagen is het wellicht nodig uw browsercache te legen.\n* '''Firefox / Safari:''' houd ''Shift'' ingedrukt terwijl u op ''Vernieuwen'' klikt of druk op ''Ctrl-F5'' of ''Ctrl-R'' (''⌘-Shift-R'' op een Mac)\n* '''Google Chrome:''' druk op ''Ctrl-Shift-R'' (''⌘-Shift-R'' op een Mac)\n* '''Internet Explorer:''' houd ''Ctrl'' ingedrukt terwijl u op ''Vernieuwen'' klikt of druk op ''Ctrl-F5''\n* '''Opera:''' leeg uw cache in ''Extra → Voorkeuren''",
+       "clearyourcache": "<strong>Opmerking:</strong> nadat u de wijzigingen hebt opgeslagen is het wellicht nodig uw browsercache te legen.\n* <strong>Firefox / Safari:</strong> houd <em>Shift</em> ingedrukt terwijl u op <em>Vernieuwen</em> klikt of druk op <em>Ctrl-F5</em> of <em>Ctrl-R</em> (<em>⌘-Shift-R</em> op een Mac)\n* <strong>Google Chrome:</strong> druk op <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> op een Mac)\n* <strong>Internet Explorer:</strong> houd <em>Ctrl</em> ingedrukt terwijl u op <em>Vernieuwen</em> klikt of druk op <em>Ctrl-F5</em>\n* '''Opera:''' ga naar <em>Menu → Instellingen</em> (<em>Opera → Voorkeuren</em> op een Mac) en daarna naar <em>Privacy & beveiliging → Browsegegevens wissen... →  Tijdelijk opgeslgen afbeeldingen en bestanden</em>.",
        "usercssyoucanpreview": "'''Tip:''' gebruik de knop \"{{int:showpreview}}\" om uw nieuwe CSS te testen alvorens op te slaan.",
        "userjsyoucanpreview": "'''Tip:''' gebruik de knop \"{{int:showpreview}}\" om uw nieuwe JavaScript te testen alvorens op te slaan.",
        "usercsspreview": "'''Dit is alleen een voorvertoning van uw persoonlijke CSS.'''\n'''Deze is nog niet opgeslagen!'''",
        "content-model-css": "CSS",
        "content-json-empty-object": "Leeg object",
        "content-json-empty-array": "Lege reeks",
+       "deprecated-self-close-category": "Pagina's met ongeldige zelfsluitende HTML-tags",
        "duplicate-args-warning": "<strong>Waarschuwing:</strong> [[:$1]] roept [[:$2]] aan met meer dan één waarde voor de parameter \"$3\". Alleen de laatste waarde wordt gebruikt.",
        "duplicate-args-category": "Pagina's met dubbele sjabloonparameters",
        "duplicate-args-category-desc": "De pagina bevat aanroepen van sjablonen waarin hetzelfde argument meerdere keren wordt gebruikt, bijvoorbeeld <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> of <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "De bewerking lijkt al ongedaan gemaakt te zijn.",
        "undo-summary": "Versie $1 van [[Special:Contributions/$2|$2]] ([[User talk:$2|overleg]]) ongedaan gemaakt",
        "undo-summary-username-hidden": "Versie $1 door een verborgen gebruiker ongedaan gemaakt",
-       "cantcreateaccounttitle": "Registreren is mislukt.",
        "cantcreateaccount-text": "Registreren vanaf dit IP-adres ('''$1''') is geblokkeerd door [[User:$3|$3]].\n\nDe door $3 opgegeven reden is ''$2''",
        "cantcreateaccount-range-text": "Het aanmaken van gebruikers vanaf IP-adressen in de range <strong>$1</strong> is niet mogelijk doordat dit is ingesteld door [[User:$3|$3]]. Uw IP-adres $4 bevindt zich in deze range.\n\nDe reden voor de blokkade is <em>$2</em>",
        "viewpagelogs": "Logboek voor deze pagina bekijken",
        "rightslogtext": "Hieronder staan de wijzigingen in gebruikersrechten.",
        "action-read": "deze pagina te bekijken",
        "action-edit": "deze pagina te bewerken",
-       "action-createpage": "pagina's aan te maken",
-       "action-createtalk": "overlegpagina's aan te maken",
+       "action-createpage": "deze pagina aan te maken",
+       "action-createtalk": "deze overlegpagina aan te maken",
        "action-createaccount": "deze gebruiker aan te maken",
        "action-autocreateaccount": "dit externe gebruikersaccount automatisch aanmaken",
        "action-history": "de geschiedenis van deze pagina te bekijken",
        "action-viewmyprivateinfo": "uw eigen privégegevens te bekijken",
        "action-editmyprivateinfo": "uw eigen privégegevens te bewerken",
        "action-editcontentmodel": "het paginainhoudmodel te bewerken",
-       "action-managechangetags": "labels aan te maken en te verwijderen",
+       "action-managechangetags": "labels aan te maken en te (de)activeren",
        "action-applychangetags": "labels aan uw bewerkingen toe te voegen",
        "action-changetags": "willekeurige labels toe te voegen aan en te verwijderen van versies en logboekregels",
+       "action-deletechangetags": "labels uit de database te verwijderen",
+       "action-purge": "Schoon deze pagina op",
        "nchanges": "$1 {{PLURAL:$1|bewerking|bewerkingen}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|sinds uw laatste bezoek}}",
        "enhancedrc-history": "geschiedenis",
        "mw-widgets-dateinput-placeholder-month": "JJJJ-MM",
        "mw-widgets-titleinput-description-new-page": "pagina bestaat nog niet",
        "mw-widgets-titleinput-description-redirect": "doorverwijzing naar $1",
-       "api-error-blacklisted": "Kies een andere, beschrijvende naam.",
        "sessionmanager-tie": "Het is niet mogelijk om meerdere authenticatietypen voor verzoeken te combineren: $1.",
        "sessionprovider-generic": "$1-sessies",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "op cookies gebaseerde sessies",
        "log-action-filter-newusers": "Type accountaanmaak:",
        "log-action-filter-patrol": "Soort markering:",
        "log-action-filter-protect": "Soort beveiliging:",
+       "log-action-filter-rights": "Soort verandering van rechten:",
+       "log-action-filter-upload": "Soort upload:",
        "log-action-filter-all": "Alles",
        "log-action-filter-block-block": "Blokkade",
        "log-action-filter-block-reblock": "Aanpassing van blokkade",
        "log-action-filter-block-unblock": "Opheffing van blokkade",
        "log-action-filter-delete-delete": "Verwijderen van pagina",
        "log-action-filter-delete-restore": "Terugplaatsen van pagina",
+       "log-action-filter-managetags-create": "Aanmaken van label",
+       "log-action-filter-managetags-delete": "Verwijderen van label",
+       "log-action-filter-managetags-activate": "Activeren van label",
+       "log-action-filter-managetags-deactivate": "Deactiveren van label",
+       "log-action-filter-move-move": "Verplaatsing zonder overschrijven van doorverwijzingen",
+       "log-action-filter-move-move_redir": "Verplaatsing met overschrijven van doorverwijzingen",
        "log-action-filter-newusers-create": "Aangemaakt door een anonieme gebruiker",
        "log-action-filter-newusers-create2": "Aangemaakt door een geregistreerde gebruiker",
        "log-action-filter-newusers-autocreate": "Automatische aanmaak",
        "log-action-filter-protect-move_prot": "Beveiliging verplaatst",
        "log-action-filter-rights-rights": "Handmatige aanpassing",
        "log-action-filter-rights-autopromote": "Automatische aanpassing",
+       "log-action-filter-suppress-event": "Verbergen van logboekregel",
+       "log-action-filter-suppress-revision": "Verbergen van versie",
+       "log-action-filter-suppress-delete": "Verbergen van pagina",
        "log-action-filter-upload-upload": "Nieuwe upload",
        "log-action-filter-upload-overwrite": "Herupload",
        "authmanager-authn-autocreate-failed": "Het automatisch aanmaken van een lokaal account is mislukt: $1",
index 2e9dfae..77ca16e 100644 (file)
@@ -25,7 +25,8 @@
                        "Macofe",
                        "Chameleon222",
                        "Matma Rex",
-                       "Mortensson"
+                       "Mortensson",
+                       "Danmichaelo"
                ]
        },
        "tog-underline": "Strek under lenkjer:",
        "nstab-category": "Kategori",
        "mainpage-nstab": "Hovudside",
        "nosuchaction": "Funksjonen finst ikkje",
-       "nosuchactiontext": "Handlinga som er oppgjeven i adressa er ugyldig.\nDu har kanskje stava adressa feil, eller følgt ei feil lenkja.\nDette kan òg skuldast ein feil i programvara som er nytta av {{SITENAME}}.",
+       "nosuchactiontext": "Handlinga som er oppgjeven i adressa er ugyldig.\nDu har kanskje stava adressa feil, eller følgt ei feil lenkje.\nDette kan òg skuldast ein feil i programvara som er nytta av {{SITENAME}}.",
        "nosuchspecialpage": "Det finst inga slik spesialside",
        "nospecialpagetext": "Du har bede om ei spesialside som ikkje finst. Lista over spesialsider finn du [[Special:SpecialPages|her]].",
        "error": "Feil",
        "yourpasswordagain": "Skriv opp att passordet",
        "createacct-yourpasswordagain": "Stadfest passord",
        "createacct-yourpasswordagain-ph": "Skriv inn passordet på nytt",
-       "remembermypassword": "Hugs innlogginga mi på denne datamaskinen (høgst {{PLURAL:$1|éin dag|$1 dagar}})",
        "userlogin-remembermypassword": "Hald meg innlogga",
        "userlogin-signwithsecure": "Nytt trygg kopling",
        "yourdomainname": "Domenet ditt",
        "passwordreset-emailtext-user": "Brukaren $1 på {{SITENAME}} bad om ei påminning for kontodetaljane dine for {{SITENAME}} ($4). {{PLURAL:$3|Den fylgjande brukarkontoen|Dei fylgjande brukarkontoane}} er assosierte med denne e-postadressa:\n\n$2\n\n{{PLURAL:$3|Dette mellombels passordet|Desse mellombels passorda}} vil verta ugilde om {{PLURAL:$5|éin dag|$5 dagar}}.\nDu bør logga inn og velja eit nytt passord no. Om nokon andre enn deg bad om denne påminninga, eller du har kome i hug det opphavlege passordet og ikkje lenger ynskjer å endra det, kan du sjå bort frå denne meldinga og halda fram med å nytta det gamle passordet ditt.",
        "passwordreset-emailelement": "↓Brukarnamn: \n$1\n\nMellombels passord: \n$2",
        "passwordreset-emailsentemail": "Ein e-post for attendestilling av passord er vorten send",
-       "passwordreset-emailsent-capture": "Ein e-post om attendestilling av passord - vist under - er vorten send",
-       "passwordreset-emailerror-capture": "Ein e-post om attendestilling av passord vart oppretta, og er vist nedanfor; men det lukkast ikkje å senda han til {{GENDER:$2|brukaren}}: $1",
        "changeemail": "↓Endre e-postadresse",
        "changeemail-header": "↓Endre kontoen si e-postadresse",
        "changeemail-no-info": "↓Du må vera pålogga for å få tilgang direkte til denne sida.",
        "minoredit": "Småplukk",
        "watchthis": "Overvak sida",
        "savearticle": "Lagra sida",
+       "savechanges": "Publiser endringane",
+       "publishpage": "Publiser sida",
+       "publishchanges": "Publiser endringar",
        "preview": "Førehandsvising",
        "showpreview": "Førehandsvis",
        "showdiff": "Sjå skilnader",
        "sectioneditnotsupported-text": "Endring av bolkar er ikkje støtta på denne sida.",
        "permissionserrors": "Løyvefeil",
        "permissionserrorstext": "Du har ikkje tilgang til å gjere dette, {{PLURAL:$1|grunnen|grunnane}} til det finn du her:",
-       "permissionserrorstext-withaction": "Du har ikkje løyve til å $2 {{PLURAL:$1|på grunn av|av desse grunnane}}:",
+       "permissionserrorstext-withaction": "Du har ikkje løyve til å $2 {{PLURAL:$1|av di|av desse grunnane}}:",
        "recreate-moveddeleted-warn": "'''Åtvaring: Du attopprettar ei side som tidlegare har vorte sletta.'''\n\nDu bør tenkje over om det er høveleg å halde fram med å endre denne sida.\nSletteloggen for sida finn du her:",
        "moveddeleted-notice": "Sida er vorten sletta. Sletteloggen og flytteloggen er viste nedanfor for referanse.",
        "log-fulllog": "Sjå full loggføring",
        "undo-failure": "Endringa kunne ikkje attenderullast grunna konflikt med endringar som er gjorde i mellomtida.",
        "undo-norev": "Endringa kunne ikkje fjernast fordi han ikkje finst eller vart sletta",
        "undo-summary": "Rullar attende versjon $1 av [[Special:Contributions/$2|$2]] ([[User talk:$2|diskusjon]])",
-       "cantcreateaccounttitle": "Kan ikkje opprette brukarkonto",
        "cantcreateaccount-text": "Kontooppretting frå denne IP-adressa ('''$1''') er blokkert av [[User:$3|$3]].\n\nGrunnen som vart gjeven av $3 er ''$2''",
        "viewpagelogs": "Vis loggane for sida",
        "nohistory": "Det finst ikkje nokon historikk for denne sida.",
        "search-relatedarticle": "Relatert",
        "searchrelated": "relatert",
        "searchall": "alle",
-       "showingresults": "Nedanfor er opp til {{PLURAL:$1|'''eitt'''|'''$1'''}} resultat som byrjar med nummer '''$2''' vist{{PLURAL:$1||e}}.",
+       "showingresults": "Nedanfor er opp til {{PLURAL:$1|<strong>eitt</strong>|<strong>$1</strong>}} resultat som byrjar med nummer <strong>$2</strong> vist{{PLURAL:$1||e}}.",
        "search-showingresults": "Resultat <strong>{{PLURAL:$4|$1|$1–$2}}</strong> av <strong>$3</strong>",
        "search-nonefound": "Ingen resultat svarte til førespurnaden.",
        "search-nonefound-thiswiki": "Det var ingen resultat som passa til spørjinga på denne nettstaden.",
        "emailuser-title-target": "Send epost åt {{GENDER:$1|brukaren}}",
        "emailuser-title-notarget": "Send e-post åt brukar",
        "emailpagetext": "Du kan nytte skjemaet nedanfor til å sende ein e-post til denne {{GENDER:$1|brukaren}}.\nE-postadressa du har sett i [[Special:Preferences|innstillingane dine]] vil dukke opp i «frå»-feltet på denne e-posten, så mottakaren er i stand til å svare.",
-       "defemailsubject": "{{SITENAME}} epost frå brukar \"$1\"",
+       "defemailsubject": "{{SITENAME}}-e-post frå brukar «$1»",
        "usermaildisabled": "Brukare-post slegen av",
        "usermaildisabledtext": "Du kan ikkje senda e-postar til andre brukarar på wikien",
        "noemailtitle": "Inga e-postadresse",
        "protect-otherreason": "Anna/ytterlegare årsak:",
        "protect-otherreason-op": "Anna årsak",
        "protect-dropdown": "*Vanlege verneårsaker\n** Gjenteke hærverk\n** Gjenteke spam\n** Endringskrig\n** Side med mange vitjande",
-       "protect-edit-reasonlist": "Endrar verneårsaker",
+       "protect-edit-reasonlist": "Endra verneårsaker",
        "protect-expiry-options": "1 time:1 hour,1 dag:1 day,1 veke:1 week,2 veker:2 weeks,1 månad:1 month,3 månader:3 months,6 månader:6 months,1 år:1 year,endelaus:infinite",
        "restriction-type": "Tilgang:",
        "restriction-level": "Avgrensingsnivå:",
        "recreate": "Attopprett",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Vil du slette tenarane sin mellomlagra versjon av denne sida?",
-       "confirm-purge-bottom": "Reinsing av ei side slettar mellomlageret og tvinger fram den nyaste versjonen.",
+       "confirm-purge-bottom": "Reinsing av ei side slettar mellomlageret og tvingar fram den nyaste versjonen.",
        "confirm-watch-button": "OK",
        "confirm-watch-top": "Legg denne sida til i overvakingslista di?",
        "confirm-unwatch-button": "OK",
        "duration-centuries": "$1 {{PLURAL:$1|hundreår|hundreår}}",
        "duration-millennia": "$1 {{PLURAL:$1|tusenår|tusenår}}",
        "rotate-comment": "Biletet vart dreitt $1{{PLURAL:$1|°}} med klokka",
-       "limitreport-title": "Profildata for analysatoren:",
+       "limitreport-title": "Profildata for parseren:",
        "limitreport-cputime": "CPU-tidsbruk",
        "limitreport-cputime-value": "{{PLURAL:$1|eitt sekund|$1 sekund}}",
        "limitreport-walltime-value": "{{PLURAL:$1|eitt sekund|$1 sekund}}",
        "mw-widgets-dateinput-placeholder-month": "ÅÅÅÅ-MM",
        "mw-widgets-titleinput-description-new-page": "sida finst ikkje enno",
        "mw-widgets-titleinput-description-redirect": "omdiriger til $1",
-       "api-error-blacklisted": "Vel eit anna namn som er meir skildrande.",
        "randomrootpage": "Tilfeldig rotsida"
 }
index 75c020f..4048ef8 100644 (file)
        "tog-watchlisthidebots": "Amagar los cambiaments faits pels bòts dins la lista de seguiment",
        "tog-watchlisthideminor": "Amagar las modificacions menoras dins la lista de seguiment",
        "tog-watchlisthideliu": "Amaga, de la lista, las modificacions pels utilizaires connectats",
+       "tog-watchlistreloadautomatically": "Recargar automaticament la lista de seguiment quand las opcions de filtratge son modificadas (JavaScript requesit)",
        "tog-watchlisthideanons": "Amaga, de la lista, las modificacions anonimas",
        "tog-watchlisthidepatrolled": "Amagar las modificacions susvelhadas de la lista de seguiment",
+       "tog-watchlisthidecategorization": "Amagar la categorizacion de las paginas",
        "tog-ccmeonemails": "Me mandar una còpia dels corrièrs electronics que mandi als autres utilizaires",
        "tog-diffonly": "Far pas veire lo contengut de las paginas jos las difs",
        "tog-showhiddencats": "Afichar las categorias amagadas",
        "viewsource": "Vejatz lo tèxte font",
        "viewsource-title": "Veire la font de $1",
        "actionthrottled": "Accion limitada",
-       "actionthrottledtext": "Per luchar contra lo spam, l’utilizacion d'aquesta accion es limitada a un cèrt nombre de còps dins una sosta pro corta. S'avèra qu'avètz depassat aqueste limit. Ensajatz tornamai dins qualques minutas.",
+       "actionthrottledtext": "Per lutar contra lo spam, l’utilizacion d'aquesta accion es limitada a un cèrt nombre de còps dins un periòde pro cort. S'avèra qu'avètz despassat aqueste limit. Ensajatz tornamai dins qualques minutas.",
        "protectedpagetext": "Aquesta pagina es estada protegida per empachar sa modificacion o d'autras accions.",
-       "viewsourcetext": "Podètz veire e copiar lo contengut de l’article per poder trabalhar dessús :",
-       "viewyourtext": "Podètz veire e copiar lo contengut de '''vòstras modificacions''' a aquesta pagina :",
+       "viewsourcetext": "Podètz veire e copiar lo contengut d'aquesta pagina.",
+       "viewyourtext": "Podètz veire e copiar lo contengut de <strong>vòstras modificacions</strong> a aquesta pagina.",
        "protectedinterface": "Aquesta pagina provesís de tèxte d’interfàcia pel logicial susaqueste wiki, e es protegida per evitar los abuses.\nPer apondre o modificar de traduccions sus totes los wikis, utilizatz [https://translatewiki.net/ translatewiki.net], lo projècte de localizacion de MediaWiki.",
        "editinginterface": "<strong>Atencion :<strong> sètz a mand de modificar una pagina utilizada per crear lo tèxte de l’interfàcia del logicial.\nLos cambiaments sus aquesta pagina se repercutaràn sus l'aparéncia de l'interfàcia d'utilizaire pels autres utilizaires d'aqueste wiki.",
        "cascadeprotected": "Aquesta pagina es actualament protegida perque es inclusa dins {{PLURAL:$1|la pagina seguenta|las paginas seguentas}}, {{PLURAL:$1|qu'es estada protegida|que son estadas protegidas}} amb l’opcion « proteccion en cascada » activada :\n$2",
        "yourpasswordagain": "Confirmar lo senhal :",
        "createacct-yourpasswordagain": "Confirmatz lo senhal",
        "createacct-yourpasswordagain-ph": "Entratz lo senhal tornarmai",
-       "remembermypassword": "Me reconnectar automaticament a las visitas venentas (al maximum $1 {{PLURAL:$1|jorn|jorns}})",
        "userlogin-remembermypassword": "Gardar ma session activa",
        "userlogin-signwithsecure": "Utilizar una connexion securizada",
        "cannotloginnow-title": "Impossible de se connectar ara",
        "newpassword": "Senhal novèl :",
        "retypenew": "Confirmar lo senhal novèl :",
        "resetpass_submit": "Cambiar lo senhal e s’enregistrar",
-       "changepassword-success": "Vòstre senhal es estat cambiat amb succès !",
+       "changepassword-success": "Vòstre senhal es estat modificat !",
        "changepassword-throttled": "Avètz ensajat un tròp grand nombre de connexions darrièrament.\nEsperatz $1 abans d’ensajar tornarmai.",
        "botpasswords": "Senhals de robòts",
        "resetpass_forbidden": "Los senhals pòdon pas èsser cambiats",
        "passwordreset-emailtext-user": "L'utilizaire $1 sus {{SITENAME}} a demandat una reïnicializacion de vòstre senhal per {{SITENAME}} ($4). {{PLURAL:$3|Lo compte d'utilizaire seguent es associat|Los comptes d'utilizaires seguents son associats}} a aquesta adreça de corrièr electronic :\n\n$2\n\n{{PLURAL:$3|Aqueste senhal temporari expirarà|Aquestes senhals temporaris expiraràn}} dins {{PLURAL:$5|un jorn|$5 jorns}}. Ara, vos cal vos connectar e causir un senhal novèl. Se aquesta demanda proven pas de vos, o que vos sètz remembrat de vòstre senhal inicial, e que lo volètz pas mai modificar, podètz ignorar aqueste messatge e contunhar d'utilizar vòstre ancian senhal.",
        "passwordreset-emailelement": "Utilizaire: \n$1\n\nSenhal temporari: \n$2",
        "passwordreset-emailsentemail": "Un corrièr electronic de reïnicializacion de senhal es estat mandat.",
-       "passwordreset-emailsent-capture": "Un corrièr electronic de reïnicializacion senhal es estat mandat, qu'es afichat çaijós.",
-       "passwordreset-emailerror-capture": "Un corrièr electronic de reïnicializacion de senhal es estat generat, qu'es afichat çaijós, mas lo mandadís a l'{{GENDER:$2|utilizaire}} a fracassat : $1",
        "changeemail": "Cambiar o suprimir l'adreça electronica",
        "changeemail-header": "Cambiar l'adreça electronica del compte",
        "changeemail-no-info": "Vos cal èsser connectat per aver accès a aquesta pagina.",
        "minoredit": "Aquò es un cambiament menor",
        "watchthis": "Seguir aquesta pagina",
        "savearticle": "Salvar",
+       "savechanges": "Enregistrar los cambiaments",
+       "publishpage": "Publicar la pagina",
+       "publishchanges": "Publicar las modificacions",
        "preview": "Previsualizar",
-       "showpreview": "Previsualizacion",
+       "showpreview": "Previsualizar",
        "showdiff": "Veire los cambiaments",
        "blankarticle": "<strong>Atencion :</strong> La pagina que creatz es voida.\nSe clicatz tornarmai sus « {{int:savearticle}} », la pagina serà creada sens cap de contengut.",
        "anoneditwarning": "<strong>Atencion :<strong> sètz pas connectat.\nVòstra adreça IP serà visibla per tot lo monde se fasètz de modificacions. Se <strong>[$1 vos connectatz]</strong> o <strong>[$2 creatz un compte]</strong>, vòstras modificacions seràn atribuidas a vòstre nom d’utilizaire, entre autres avantatges.",
        "undo-nochange": "Sembla que la modificacion es ja estada anullada.",
        "undo-summary": "Anullacion de las modificacions $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|discutir]] | [[Special:Contributions/$2|{{MediaWiki:Contribslink}}]])",
        "undo-summary-username-hidden": "Anullar la revision $1 per un utilizaire amagat",
-       "cantcreateaccounttitle": "Podètz pas crear de compte.",
        "cantcreateaccount-text": "La creacion de compte dempuèi aquesta adreça IP ('''$1''') es estada blocada per [[User:$3|$3]].\n\nLa rason balhada per $3 èra ''$2''.",
        "cantcreateaccount-range-text": "La creacion de compte dempuèi las adreças IP dins la plaja <strong>$1</strong>, que compren vòstra agreça IP (<strong>$4</strong>) son estadas blocadas per [[User:$3|$3]].\n\nLo motiu provesit per $3 es <em>$2</em>",
        "viewpagelogs": "Vejatz las operacions per aquesta pagina",
        "prefs-watchlist": "Lista de seguiment",
        "prefs-editwatchlist": "Modificar la lista de seguiment",
        "prefs-editwatchlist-label": "Modificar de títols de vòstra lista de seguiment",
+       "prefs-editwatchlist-edit": "Veire e levar de títols de vòstra lista de seguiment",
        "prefs-editwatchlist-raw": "Modificar la lista de seguiment en mòde brut",
        "prefs-editwatchlist-clear": "Escafar la lista de seguiment",
        "prefs-watchlist-days": "Nombre de jorns d'afichar dins la lista de seguiment :",
        "rows": "Rengadas :",
        "columns": "Colomnas :",
        "searchresultshead": "Recèrca",
-       "stub-threshold": "Limit superior pels <a href=\"#\" class=\"stub\">ligams cap als esbòsses</a> (octets) :",
+       "stub-threshold": "Limit pel formatatge dels ligams d’esbòs ($1) :",
        "stub-threshold-disabled": "Desactivat",
        "recentchangesdays": "Nombre de jorns d'afichar dins los darrièrs cambiaments :",
        "recentchangesdays-max": "(maximum $1 {{PLURAL:$1|jorn|jorns}})",
        "right-applychangetags": "Aplicar [[Special:Tags|las balisas]] amb sas pròprias modificacions",
        "grant-generic": "ensemble de dreits « $1 »",
        "grant-blockusers": "Blocar e desblocar d'utilizaires",
-       "grant-patrol": "Marcar de paginas coma patrolhadas",
+       "grant-patrol": "Verificar las modificacions de paginas",
        "newuserlogpage": "Istoric de las creacions de comptes",
        "newuserlogpagetext": "Jornal de las creacions de comptes d'utilizaires.",
        "rightslog": "Istoric de las modificacions d'estatut",
        "rightslogtext": "Aquò es un jornal dels cambiaments d'estatut d’utilizaire.",
        "action-read": "legir aquesta pagina",
        "action-edit": "modificar aquesta pagina",
-       "action-createpage": "crear de paginas",
-       "action-createtalk": "crear de paginas de discussion",
+       "action-createpage": "crear aquesta pagina",
+       "action-createtalk": "crear aquesta pagina de discussion",
        "action-createaccount": "crear aqueste compte d'utilizaire",
        "action-history": "afichar l’istoric d'aquesta pagina",
        "action-minoredit": "marcar aqueste cambiament coma menor",
        "newpageletter": "N",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|utilizaire seguent|utilizaires seguents}}]",
-       "rc_categories": "Limit de las categorias (separacion amb « | »)",
+       "rc_categories": "Limitar a las categorias (separadas per « | ») :",
        "rc_categories_any": "Una de las seleccionadas",
        "rc-change-size-new": "$1 {{PLURAL:$1|octet|octets}} aprèp cambiament",
        "newsectionsummary": "/* $1 */ seccion novèla",
        "wlheader-showupdated": "Las paginas que son estadas modificadas dempuèi vòstra darrièra visita son afichadas en '''gras'''.",
        "wlnote": "Çaijós {{PLURAL:$1|figura la darrièra modificacion efectuada|figuran las <strong>$1</strong> darrièras modificacions efectuadas}} pendent {{PLURAL:$2|la darrièra ora|las <strong>$2</strong> darrièras oras}}, dempuèi $3, $4.",
        "wlshowlast": "Far veire las darrièras $1 oras, los darrièrs $2 jorns",
+       "wlshowhideminor": "cambiaments menors",
        "watchlist-options": "Opcions de la lista de seguiment",
        "watching": "Seguit...",
        "unwatching": "Fin del seguit...",
        "deletepage": "Suprimir la pagina",
        "confirm": "Confirmar",
        "excontent": "contenent '$1'",
-       "excontentauthor": "lo contengut èra : « $1 » (e l'unic contributor èra « [[Special:Contributions/$2|$2]] »)",
+       "excontentauthor": "conteniá « $1 » e son sol contributor èra «[[Special:Contributions/$2|$2]]» ([[User talk:$2|talk]])",
        "exbeforeblank": "lo contengut abans blanquiment èra :'$1'",
        "delete-confirm": "Escafar «$1»",
        "delete-legend": "Escafar",
        "undeletepagetext": "{{PLURAL:$1|Aquesta pagina es estada escafada e se tròba|Aquestas paginas son estadas escafadas e se tròban}} dins l'archiu. {{PLURAL:$1|Figura|Figuran}} encara dins la basa de donada e {{PLURAL:$1|pòt èsser restablida|pòdon èsser restablidas}}.\nL'archiu pòt èsser escafat periodicament.",
        "undelete-fieldset-title": "Restablir las versions",
        "undeleteextrahelp": "Per restablir l'istoric complet d'aquesta pagina, daissatz vèrjas totas las casas de marcar, puèi clicatz sus '''''Restablir'''''.\nPer efectuar un restabliment parcial, marcatz las casas que correspondon a las versions que son de restablir, puèi clicatz sus '''''Restablir'''''.",
-       "undeleterevisions": "$1 {{PLURAL:$1|revision archivada|revisions archivadas}}",
+       "undeleterevisions": "{{PLURAL:$1|Una revision suprimida|$1 revisions suprimidas}}",
        "undeletehistory": "Se restablissètz la pagina, totas las revisions seràn plaçadas tornamai dins l'istoric.\n\nS'una pagina novèla amb lo meteis nom es estada creada dempuèi la supression, las revisions restablidas apareisseràn dins l'istoric anterior e la version correnta serà pas automaticament remplaçada.",
        "undeleterevdel": "Lo restabliment serà pas efectuat se, fin finala, la version mai recenta de la pagina es parcialament suprimida. Dins aqueste cas, vos cal deseleccionatz las versions mai recentas (en naut). Las versions dels fichièrs a las qualas avètz pas accès seràn pas restablidas.",
        "undeletehistorynoadmin": "Aqueste article es estat suprimit. Lo motiu de la supression es indicat dins lo resumit çaijós, amb los detalhs dels utilizaires que l’an modificat abans sa supression. Lo contengut d'aquestas versions es pas accessible qu’als administrators.",
        "undeletedrevisions": "{{PLURAL:$1|1 revision restablida|$1 revisions restablidas}}",
        "undeletedrevisions-files": "{{PLURAL:$1|1 revision|$1 revisions}} e {{PLURAL:$2|1 fichièr restablit|$2 fichièrs restablits}}",
        "undeletedfiles": "$1 {{PLURAL:$1|fichièr restablit|fichièrs restablits}}",
-       "cannotundelete": "Fracàs del restabliment :\n$1",
+       "cannotundelete": "Certanas o totas las restitucions an fracassat :\n$1",
        "undeletedpage": "<strong>La pagina $1 es estada restablida</strong>.\n\nConsultatz l’[[Special:Log/delete|istoric de las supressions]] per veire la lista de las supressions e dels restabliments recents.",
        "undelete-header": "Consultatz l’[[Special:Log/delete|istoric de las supressions]] per veire las paginas recentament suprimidas.",
        "undelete-search-title": "Recercar las paginas suprimidas",
        "sp-contributions-newbies-sub": "Lista de las contribucions dels utilizaires novèls. Las paginas que son estadas suprimidas son pas afichadas.",
        "sp-contributions-newbies-title": "Las contribucions de l’utilizaire pels comptes novèls",
        "sp-contributions-blocklog": "Istoric dels blocatges",
-       "sp-contributions-suppresslog": "contribucions suprimidas d’un utilizaire",
-       "sp-contributions-deleted": "contribucions suprimidas",
+       "sp-contributions-suppresslog": "contribucions de l'{{GENDER:$1|utilizaire|utilizaira}} suprimidas",
+       "sp-contributions-deleted": "contribucions de l'{{GENDER:$1|utilizaire|utilizaira}} suprimidas",
        "sp-contributions-uploads": "impòrts",
        "sp-contributions-logs": "jornals",
        "sp-contributions-talk": "Discutir",
        "isredirect": "pagina de redireccion",
        "istemplate": "inclusion",
        "isimage": "ligam cap al fichièr",
-       "whatlinkshere-prev": "{{PLURAL:$1|precedent|$1 precedents}}",
+       "whatlinkshere-prev": "{{PLURAL:$1|precedenta|$1 precedentas}}",
        "whatlinkshere-next": "{{PLURAL:$1|seguent|$1 seguents}}",
        "whatlinkshere-links": "← ligams",
        "whatlinkshere-hideredirs": "$1 las redireccions",
        "whatlinkshere-hidetrans": "$1 las inclusions",
        "whatlinkshere-hidelinks": "$1 ligams",
-       "whatlinkshere-hideimages": "$1 los fichièrs ligats",
+       "whatlinkshere-hideimages": "$1 los ligams cap al fichièr",
        "whatlinkshere-filters": "Filtres",
        "autoblockid": "Blocatge automatic #$1",
        "block": "Blocar un utilizaire",
        "tooltip-feed-rss": "Flux RSS per aquesta pagina",
        "tooltip-feed-atom": "Flux Atom per aquesta pagina",
        "tooltip-t-contributions": "Veire la lista de las contribucions d'{{GENDER:$1|aqueste utilizaire|aquesta utilizaira}}",
-       "tooltip-t-emailuser": "Mandar un corrièr electronic a aqueste utilizaire",
+       "tooltip-t-emailuser": "Mandar un corrièr electronic a {{GENDER:$1|aqueste utilizaire|aquesta utilizaira}}",
        "tooltip-t-info": "Mai d’informacion sus aquesta pagina",
        "tooltip-t-upload": "Mandar un imatge o fichièr mèdia sul servidor",
        "tooltip-t-specialpages": "Lista de totas las paginas especialas",
        "tooltip-ca-nstab-category": "Vejatz la pagina de la categoria",
        "tooltip-minoredit": "Marcar mas modificacions coma un cambiament menor",
        "tooltip-save": "Salvar vòstras modificacions",
+       "tooltip-publish": "Publicar vòstras modificacions",
        "tooltip-preview": "Mercé de previsualizar vòstras modificacions abans de salvar!",
        "tooltip-diff": "Aficha los cambiaments qu'avètz aportats al tèxte",
        "tooltip-compareselectedversions": "Afichar las diferéncias entre doas versions d'aquesta pagina",
        "table_pager_limit": "Far veire $1 elements per pagina",
        "table_pager_limit_label": "Resultats per pagina :",
        "table_pager_limit_submit": "Accedir",
-       "table_pager_empty": "Cap de resultat",
+       "table_pager_empty": "Pas cap de resultat",
        "autosumm-blank": "Blanquiment de la pagina",
        "autosumm-replace": "Resumit automatic : contengut remplaçat per « $1 ».",
        "autoredircomment": "Redireccion cap a [[$1]]",
        "watchlistedit-raw-done": "Vòstra lista de seguiment es estada mesa a jorn.",
        "watchlistedit-raw-added": "{{PLURAL:$1|Una pagina es estada aponduda|$1 paginas son estadas apondudas}} :",
        "watchlistedit-raw-removed": "{{PLURAL:$1|Una pagina es estada levada|$1 paginas son estadas levadas}} :",
-       "watchlistedit-clear-title": "Lista de seguiment voidada",
+       "watchlistedit-clear-title": "Voidar la lista de seguiment",
        "watchlistedit-clear-legend": "Escafar la lista de seguiment",
        "watchlistedit-clear-explain": "Totes los títols seràn suprimits de vòstra lista de seguiment",
        "watchlistedit-clear-titles": "Títols :",
        "version-libraries-license": "Licéncia",
        "version-libraries-description": "Descripcion",
        "version-libraries-authors": "Autors",
-       "redirect": "Redirigit per fichièr, utilizaire, pagina o ID de revision.",
+       "redirect": "Redirigir per ID de fichièr, utilizaire, pagina, revision o jornal.",
        "redirect-submit": "Validar",
        "redirect-lookup": "Recèrca :",
        "redirect-value": "Valor :",
        "tags-edit-title": "Modificar las balisas",
        "tags-edit-manage-link": "Gerir las balisas",
        "tags-edit-existing-tags": "Balisas existentas :",
-       "tags-edit-existing-tags-none": "\"Pas cap\"",
+       "tags-edit-existing-tags-none": "<em>Pas cap</em>",
        "tags-edit-new-tags": "Balisas novèlas :",
        "tags-edit-add": "Apondre aquestas balisas :",
        "tags-edit-remove": "Suprimir aquestas balisas :",
        "special-characters-title-emdash": "jonhent em",
        "special-characters-title-minus": "signe mens",
        "randomrootpage": "Pagina raiç aleatòria",
+       "log-action-filter-rights": "Tipe de cambiament de dreits :",
+       "log-action-filter-suppress": "Tipe de supression :",
        "changecredentials": "Modificar las informacions d’identificacion"
 }
index 18d8a84..750bc46 100644 (file)
        "jumpto": "Siirry",
        "jumptonavigation": "navigatsii",
        "jumptosearch": "eči",
-       "view-pool-error": "Pahakse mielekse palvelimet ollah ylikuormittunnuot täl hetkel. Liijan moni käyttäi oppiu tarkastella tädä sivuu. Vuota kodvaine enne gu opit uvvessah.",
+       "view-pool-error": "Pahakse mielekse palvelimet ollah ylikuormittunnuot täl hetkel. Liijan moni käyttäi oppiu tarkastella tädä sivuu. Vuota kodvaine, enne gu opit uvvessah.\n\n$1",
        "generic-pool-error": "Pahakse mielekse palvelimet ollah ylikuormittunnuot nygöi. Liijan moni käyttäi oppiu tarkastella tädä sivuu.",
        "pool-timeout": "Lukittumizen vuotanduaigu on loppenuhes.",
        "pool-queuefull": "Ečindykyzymykzien tallendustila on täyzi",
        "privacypage": "Project:Luottamuksen periuateh",
        "badaccess": "Ei oigevuksii",
        "badaccess-group0": "Sinul ei ole lubua suorittua tädä toiminduo.",
-       "badaccess-groups": "Tämän toimindon voijah suorittua vai {{PLURAL:$2|täh joukkoh|nämmih joukkoloih}} kuulujat käyttäjät.",
+       "badaccess-groups": "Tämän toimindon voijah suorittua vai {{PLURAL:$2|täh joukkoh|nämmih joukkoloih}}:$1 kuulujat käyttäjät.",
        "versionrequired": "MediiWikis pidäy vähimyölleh versii $1",
        "versionrequiredtext": "MediiWikis pidäy vähimyölleh versii $1 tädä sivuu kaččojes. Kačo [[Special:Version|versii]].",
        "ok": "OK",
        "revdelete-radio-same": "(älä vaihta)",
        "revdelete-radio-set": "Peitetty",
        "revdelete-radio-unset": "Nägövil",
-       "logdelete-success": "'''Tapahtumuhistourien nägyvytty on muutettu.'''",
+       "logdelete-success": "Tapahtumuhistourien nägyvytty on muutettu.",
        "logdelete-failure": "'''Tapahtumuhistourien nägyvytty ei voidu azettua:'''\n$1",
        "revdel-restore": "vaihta nägyvys",
        "pagehist": "Sivuhistourii",
index c0c1588..ba5db3b 100644 (file)
        "yourpasswordagain": "ପାସୱାର୍ଡ଼ ଆଉଥରେ:",
        "createacct-yourpasswordagain": "ପାସୱାର୍ଡ଼ ନିଶ୍ଚିତ କରିବେ",
        "createacct-yourpasswordagain-ph": "ଆଉଥରେ ପାସୱାର୍ଡ଼ ଦିଅନ୍ତୁ",
-       "remembermypassword": "ଏହି ବ୍ରାଉଜରରେ (ସବୁଠୁ ଅଧିକ ହେଲେ $1 {{PLURAL:$1|day|ଦିନ}}) ପାଇଁ ମୋ ଲଗଇନ ମନେ ରଖିଥିବେ",
        "userlogin-remembermypassword": "ମୋତେ ଲଗ-ଇନ କରି ରଖିଥାନ୍ତୁ",
        "userlogin-signwithsecure": "ନିରାପଦ କନେକସନ ବ୍ୟବ‌ହାର କରନ୍ତୁ",
        "cannotloginnow-title": "ଏବେ ଲଗ ଇନ ହୋଇପାରିବ ନାହିଁ",
        "noname": "ଆପଣ ଗୋଟିଏ ବୈଧ ଇଉଜର ନାମ ଦେଇନାହାନ୍ତି ।",
        "loginsuccesstitle": "ଠିକଭାବେ ଲଗ-ଇନ ହେଲା",
        "loginsuccess": "'''ଆପଣ {{SITENAME}}ରେ \"$1\" ନାମରେ ଲଗ-ଇନ କରିଛନ୍ତି ।'''",
-       "nosuchuser": "\"$1\" à¬¨à¬¾à¬®à¬°à­\87 à¬\95à­\87ହି à¬\9cଣà­\87 à¬¬à¬¿ à¬¸à¬­à­\8dà­\9f à¬¨à¬¾à¬¹à¬¾à¬¨à­\8dତି à¥¤\nà¬\87à¬\89à¬\9cର à¬¨à¬¾à¬® à¬\87à¬\82ରାà¬\9cà­\80 à¬\9bà­\8bà¬\9f à¬\93 à¬¬à¬¡à¬¼ à¬\85à¬\95à­\8dଷର à¬ªà­\8dରତି à¬¸à¬®à­\8dବà­\87ଦନଶà­\80ଳ à¥¤\nà¬\86ପଣ à¬¨à¬¿à¬\9cର à¬¬à¬¨à¬¾à¬¨ à¬ªà¬°à¬\96ି à¬¨à¬¿à¬\85ନà­\8dତà­\81, à¬\85ଥବା [[Special:CreateAccount|ନà­\82à¬\86 à¬\96ାତାà¬\9fିà¬\8f à¬¤à¬¿à¬\86ରି à¬\95ରନà­\8dତà­\81]] à¥¤",
+       "nosuchuser": "\"$1\" ନାମରେ କେହି ଜଣେ ବି ସଭ୍ୟ ନାହାନ୍ତି ।\nଇଉଜର ନାମ ଇଂରାଜୀ ଛୋଟ ଓ ବଡ଼ ଅକ୍ଷର ପ୍ରତି ସମ୍ବେଦନଶୀଳ ।\nବନାନ ପରଖି ନିଅନ୍ତୁ, ଅଥବା [[Special:CreateAccount|ନୂଆ ଖାତାଟିଏ ତିଆରି କରନ୍ତୁ]] ।",
        "nosuchusershort": "\"$1\" ନାମରେ କେହି ଜଣେ ବି ସଭ୍ୟ ନାହାନ୍ତି ।\nଆପଣ ବନାନ ପରଖି ନିଅନ୍ତୁ ।",
        "nouserspecified": "ଆପଣଙ୍କୁ ଇଉଜର ନାମଟିଏ ଦେବାକୁ ପଡ଼ିବ ।",
        "login-userblocked": "ଏହି ସଭ୍ୟଙ୍କୁ ଅଟକାଯାଇଛି । ଲଗ ଇନ କରିବାକୁ ଅନୁମତି ନାହିଁ ।",
        "passwordreset-emailtext-user": "$1 ନାମକ ସଭ୍ୟଜଣକ {{SITENAME}}ରେ {{SITENAME}} ($4) ପାଇଁ ଆପଣଙ୍କ ପାସ ୱାର୍ଡ଼ ରିସେଟ କରିବାର ଅନୁରୋଧ କରିଛନ୍ତି । ତଳ {{PLURAL:$3|ଖାତାଟି|ଖାତାମାନ}} ଉକ୍ତ ଇମେଲ ସହିତ ସମ୍ବନ୍ଧିତ:\n\n$2\n\n{{PLURAL:$3|ଏହି ଅସ୍ଥାୟୀ ପାସୱାର୍ଡ଼ଟି|ଏହି ଅସ୍ଥାୟୀ ପାସୱାର୍ଡ଼ମାନ}} {{PLURAL:$5|ଦିନକ|$5 ଦିନ}}ରେ ଅଚଳ ହୋଇଯିବ ।\nଆପଣ ଲଗ ଇନ କରି ନୂଆ ପାସୱାର୍ଡ଼ଟିଏ ବାଛନ୍ତୁ । ଯଦି ଆଉ କେହି ଏହି ଅନୁରୋଧଟି କରିଥାନ୍ତି କିମ୍ବା ଆପଣଙ୍କର ନିଜ ପୁରୁଣା ପାସୱାର୍ଡ଼ଟି ମନେପଡ଼ିଗଲା ତେବେ ଆପଣଙ୍କୁ ଆଉ ପାସୱାର୍ଡ଼ ବଦଳାଇବାର ଆବଶ୍ୟକତା ନାହିଁ । ଆପଣ ଏହି ମେସେଜଟିକୁ ଅଣଦେଖା କରି ନିଜର ପୁରୁଣା ପାସୱାର୍ଡ଼ ବ୍ୟବହାର କରୁଥାନ୍ତୁ ।",
        "passwordreset-emailelement": "ଇଉଜର ନାମ: \n$1\n\nଅସ୍ଥାୟୀ ପାସୱାର୍ଡ଼: \n$2",
        "passwordreset-emailsentemail": "ଏକ ପାସୱାର୍ଡ଼ ପୁନଃସ୍ଥାପନ ଇମେଲ ପଠାଇଦିଆଯାଇଅଛି ।",
-       "passwordreset-emailsent-capture": "ତଳେ ଦେଖାଯାଉଥିବା ଭଳି, ପାସୱାର୍ଡ଼ ପୁନଃସ୍ଥାପନ ଇମେଲଟିଏ ପଠାଇଦିଆଯାଇଛି ।",
-       "passwordreset-emailerror-capture": "ପାସୱାର୍ଡ଼ ବଦଳାଇବା ସୂଚନା ସହ ଇମେଲଟିଏ ତିଆରି ହୋଇଛି, ଯାହା ତଳେ ଦେଖିପାରିବେ । କିନ୍ତୁ ଏହାକୁ {{GENDER:$2|ସଭ୍ୟ}}ଙ୍କୁ ପଠାଇବାରେ ବିଫଳ ହେଲୁ, କାରଣ: $1",
        "changeemail": "ଇ-ମେଲ ଠିକଣା ବଦଳାଇବେ କିମ୍ବା କାଢିବେ",
        "changeemail-header": "ଖାତା ଇ-ମେଲ ଠିକଣା ବଦଳାଇବେ",
        "changeemail-no-info": "ଏହି ପୃଷ୍ଠାଟିକୁ ସିଧା ଖୋଲିବା ନିମନ୍ତେ ଆପଣଙ୍କୁ ଲଗ ଇନ କରିବାକୁ ପଡ଼ିବ ।",
        "undo-nochange": "ଏହି ସମ୍ପାଦନା ପଛକୁ ଫେରାଇଦିଆଯାଇଥିବା ଭଳି ଲାଗୁଛି ।",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|ଆଲୋଚନା]]) ଙ୍କ ଦେଇ କରାଯାଇଥିବା $1 ସଙ୍କଳନଟି ପଛକୁ ଫେରାଇନିଆଗଲା",
        "undo-summary-username-hidden": "ଜଣେ ଅଜଣା ସଭ୍ୟଙ୍କ ଦେଇ ହୋଇଥିବା $1 ସଂସ୍କରଣଟି ପଛକୁ ଫେରାନ୍ତୁ",
-       "cantcreateaccounttitle": "ଖାତାଟିଏ ତିଆରି କରାଯାଇପାରିବ ନାହିଁ",
        "cantcreateaccount-text": "[[User:$3|$3]]ଙ୍କ ଦେଇ ('''$1''') IP ଠିକଣାରୁ ଖାତା ଖୋଲିବାକୁ ବାରଣ କରାଯାଇଅଛି ।\n\n$3ଙ୍କ ଦେଇ ଦିଆଯାଇଥିବା କାରଣ ହେଲା ''$2''",
        "cantcreateaccount-range-text": "ଆପଣଙ୍କ IP Address (<strong>$4</strong>) ସମେତ <strong>$1</strong> ସୀମା ଭିତରେ ଥିବା IP Address ରୁ [[User:$3|$3]]ଙ୍କ ଦ୍ୱାରା ନୂଆ ଖାତା ତିଆରିକୁ ଅଟକାଯାଇଛି ।\n\n$3ଙ୍କ ଦ୍ୱାରା ଏହାର କାରଣ ଦିଆଯାଇଛି: <em>$2</em>",
        "viewpagelogs": "ଏହି ପୃଷ୍ଠା ପାଇଁ ଲଗଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ ।",
        "thumbnail_image-missing": "ଫାଇଲଟି ନଥିଲା ଭଳି ଲାଗୁଛି : $1",
        "thumbnail_image-failure-limit": "ଏହି ଥମ୍ବନେଲ ରେଣ୍ଡର କରିବା ପାଇଁ ନିକଟରେ ଅନେକ ($1 କିମ୍ବା ଅଧିକ) ବିଫଳ ଚେଷ୍ଟା କରାଯାଇଛି । ଆଉଥରେ ଚେଷ୍ଟା କରନ୍ତୁ ।",
        "import": "ପୃଷ୍ଠା ଆମଦାନି କରିବେ",
-       "importinterwiki": "à¬\9fà­\8dରାନà­\8dସà¬\89à¬\87à¬\95ି à¬\88ମà­\8dପà­\8bରà­\8dà¬\9f",
+       "importinterwiki": "à¬\86à¬\89 à¬\8fà¬\95 à¬\89à¬\87à¬\95ିରà­\81 à¬\86ମଦାନà­\80 à¬\95ରନà­\8dତà­\81",
        "import-interwiki-text": "ଏକ ଉଇକି ଓ ପୃଷ୍ଠା ନାମ ଆମଦାନି କରିବା ନିମନ୍ତେ ଦିଅନ୍ତୁ ।\nସଂସ୍କରଣ ତାରିଖ ଓ ସମ୍ପାଦକଙ୍କ ନାମ ସାଇତା ହୋଇ ରହିବ ।\nଅନ୍ତଉଇକି ଆମଦାନି କାମସବୁ [[Special:Log/import|ଆମଦାନି ଇତିହାସ]]ରେ ସାଇଟ ହୋଇ ରହିଛି ।",
        "import-interwiki-sourcewiki": "ମୂଳ ଉଇକି:",
        "import-interwiki-sourcepage": "ମୂଳ ପୃଷ୍ଠା:",
        "special-characters-title-endash": "en ଡ୍ୟାସ",
        "special-characters-title-emdash": "em dash",
        "special-characters-title-minus": "ମେନୁଗୁଡିକର ଚିହ୍ନ",
-       "mw-widgets-titleinput-description-redirect": "$1କୁ ପୁନଃପ୍ରେରଣ କରିବେ",
-       "api-error-blacklisted": "ଦୟାକରି ଏକ ଅଲଗା, ବିବରଣୀ ଶିରୋନାମାରେ ରଖିବେ ।"
+       "mw-widgets-titleinput-description-redirect": "$1କୁ ପୁନଃପ୍ରେରଣ କରିବେ"
 }
index cb9ca86..829bb0e 100644 (file)
        "october-date": "$1 ਅਕਤੂਬਰ",
        "november-date": "$1 ਨਵੰਬਰ",
        "december-date": "$1 ਦਸੰਬਰ",
+       "period-am": "ਏਐਮ",
+       "period-pm": "ਪੀਐਮ",
        "pagecategories": "{{PLURAL:$1|ਸ਼੍ਰੇਣੀ|ਸ਼੍ਰੇਣੀਆਂ}}",
        "category_header": "ਸ਼੍ਰੇਣੀ \"$1\" ਵਿੱਚ ਲੇਖ",
        "subcategories": "ਉਪਸ਼੍ਰੇਣੀਆਂ",
        "mypage": "ਸਫ਼ਾ",
        "mytalk": "ਗੱਲ-ਬਾਤ",
        "anontalk": "ਗੱਲ-ਬਾਤ",
-       "navigation": "ਫà©\87ਰà©\80 à¨ªà¨¾à¨\93",
+       "navigation": "ਨà©\87ਵà©\80à¨\97à©\87ਸ਼ਨ",
        "and": "&#32;ਅਤੇ",
        "qbfind": "ਖੋਜ",
        "qbbrowse": "ਝਲਕ",
        "yourdomainname": "ਤੁਹਾਡਾ ਡੋਮੇਨ:",
        "password-change-forbidden": "ਇਸ ਵਿਕੀ ਤੇ ਤੁਸੀਂ ਪਾਸਵਰਡ ਨਹੀਂ ਬਦਲ ਸਕਦੇ।",
        "externaldberror": "ਜਾਂ ਤਾਂ ਪ੍ਰਮਾਣਕੀ ਡਾਟਾਬੇਸ ਦੋਸ਼ ਆਇਆ ਹੈ ਜਾਂ ਤੁਹਾਨੂੰ ਆਪਣੇ ਬਾਹਰੀ ਖਾਤੇ ਨੂੰ ਅੱਪਡੇਟ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।",
-       "login": "ਲਾà¨\97à¨\87ਨ",
+       "login": "ਦਾà¨\96ਲ",
        "login-security": "ਆਪਣੀ ਪਛਾਣ ਦੀ ਪੜਤਾਲ ਕਰਵਾਉ",
        "nav-login-createaccount": "ਲਾਗਇਨ/ਖਾਤਾ ਬਣਾਓ",
        "userlogin": "ਲਾਗਇਨ/ਖਾਤਾ ਬਣਾਓ",
        "createaccount-title": "{{SITENAME}} ਲਈ ਅਕਾਊਂਟ ਬਣਾਉਣਾ",
        "createaccount-text": "ਕਿਸੇ ਨੇ \"$2\" ਮੈਂਬਰ-ਨਾਮ ਅਤੇ \"$3\" ਪਾਸਵਰਡ ਨਾਲ਼ {{SITENAME}} ($4) ਤੇ, ਤੁਹਾਡਾ ਈ-ਮੇਲ ਪਤਾ ਵਰਤਦੇ ਹੋਏ, ਖਾਤਾ ਬਣਾਇਆ ਹੈ।\nਤੁਹਾਨੂੰ ਹੁਣੇ ਲਾਗਇਨ ਕਰਕੇ ਆਪਣਾ ਪਾਸਵਰਡ ਬਦਲਣਾ ਚਾਹੀਦਾ ਹੈ।\n\nਜੇ ਇਹ ਖਾਤਾ ਗ਼ਲਤੀ ਨਾਲ਼ ਬਣ ਗਿਆ ਹੈ ਤਾਂ ਤੁਸੀਂ ਇਸ ਸੁਨੇਹੇ ਨੂੰ ਨਜ਼ਰਅੰਦਾਜ਼ ਕਰ ਸਕਦੇ ਹੋ।",
        "login-throttled": "ਤੁਸੀਂ ਬਹੁਤ ਸਾਰੀਆਂ ਤਾਜ਼ਾ ਲਾਗਇਨ ਕੋਸ਼ਿਸ਼ਾਂ ਕੀਤੀਆਂ ਹਨ।\nਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ $1 ਉਡੀਕ ਕਰੋ ਜੀ।",
-       "login-abort-generic": "ਤà©\81ਹਾਡਾ à¨¦à¨¾à¨\96਼ਲਾ à¨¨à¨¾à¨\95ਾਮ ਸੀ - ਅਧੂਰਾ ਛੱਡਿਆ",
+       "login-abort-generic": "ਤà©\81ਹਾਡਾ à¨¦à¨¾à¨\96਼ਲਾ à¨\85ਸਫਲ ਸੀ - ਅਧੂਰਾ ਛੱਡਿਆ",
        "login-migrated-generic": "ਤੁਹਾਡੇ ਖਾਤੇ ਦੀ ਥਾਂ ਬਦਲ ਦਿੱਤੀ ਗਈ ਹੈ ਅਤੇ ਤੁਹਾਡਾ ਵਰਤੋਂਕਾਰ-ਨਾਂ ਹੁਣ ਇਸ ਵਿਕੀ 'ਤੇ ਮੌਜੂਦ ਨਹੀਂ ਹੈ।",
        "loginlanguagelabel": "ਭਾਸ਼ਾ: $1",
        "suspicious-userlogout": "ਤੁਹਾਡੀ ਵਿਦਾਇਗੀ ਦੀ ਬੇਨਤੀ ਨਕਾਰ ਦਿੱਤੀ ਗਈ ਕਿਉਂਕਿ ਲੱਗਦਾ ਹੈ ਕਿ ਇਹ ਕਿਸੇ ਟੁੱਟੇ ਹੋਏ ਬਰਾਊਜ਼ਰ ਜਾਂ ਕੈਸ਼ ਹੋਈ ਪ੍ਰਾਕਸੀ ਤੋਂ ਭੇਜੀ ਗਈ ਸੀ।",
        "createacct-another-realname-tip": "ਅਸਲੀ ਨਾਂ ਚੋਣਵਾਂ ਹੈ।\nਜੇਕਰ ਤੁਸੀਂ ਇਹ ਦਿੱਤਾ ਹੈ ਤਾਂ ਤੁਹਾਡੇ ਕੰਮ ਵਾਸਤੇ ਗੁਣ ਦੇ ਤੌਰ ਉੱਤੇ ਵਰਤਿਆ ਜਾਵੇਗਾ।",
-       "pt-login": "ਲਾà¨\97 à¨\87ਨ",
-       "pt-login-button": "ਲਾà¨\97 à¨\87ਨ",
+       "pt-login": "ਦਾà¨\96ਲ",
+       "pt-login-button": "ਦਾà¨\96ਲ",
        "pt-createaccount": "ਖਾਤਾ ਬਣਾਓ",
        "pt-userlogout": "ਬਾਹਰ ਆਉ",
        "php-mail-error-unknown": "PHP ਦੇ ਮੇਲ() ਕਰਜ ਵਿੱਚ ਅਣਜਾਣ ਦੋਸ਼",
        "resetpass_submit": "ਪਾਸਵਰਡ ਸੈੱਟ ਕਰੋ ਅਤੇ ਲਾਗਇਨ ਕਰੋ",
        "changepassword-success": "ਤੁਹਾਡਾ ਪਾਸਵਰਡ ਠੀਕ ਤਰ੍ਹਾਂ ਬਦਲਿਆ ਜਾ ਚੁੱਕਾ ਹੈ!",
        "changepassword-throttled": "ਤੁਸੀਂ ਦਾਖ਼ਲ ਹੋਣ ਦੀਆਂ ਬਹੁਤ ਸਾਰੀਆਂ ਤਾਜ਼ਾ ਕੋਸ਼ਿਸ਼ਾਂ ਕੀਤੀਆਂ ਹਨ।\nਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ $1 ਉਡੀਕ ਕਰੋ ਜੀ।",
+       "botpasswords": "ਬੌਟ ਪਾਸਵਰਡ",
+       "botpasswords-label-appid": "ਬੌਟਾ ਨਾੰ:",
+       "botpasswords-label-create": "ਬਣਾਓ",
+       "botpasswords-label-update": "ਨਵਿਆਉ",
+       "botpasswords-label-cancel": "ਰੱਦ ਕਰੋ",
+       "botpasswords-label-delete": "ਮਿਟਾਓ",
+       "botpasswords-label-resetpassword": "ਪਾਸਵਰਡ ਮੁੜ-ਸੈੱਟ ਕਰੋ",
        "resetpass_forbidden": "ਪਾਸਵਰਡ ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ",
        "resetpass-no-info": "ਇਸ ਸਫ਼ੇ ਨੂੰ ਸਿੱਧੇ ਹੀ ਵੇਖਣ ਲਈ ਤੁਹਾਨੂੰ ਲਾਗਇਨ ਕਰਨਾ ਪਵੇਗਾ।",
        "resetpass-submit-loggedin": "ਪਛਾਣ-ਸ਼ਬਦ ਬਦਲੋ",
        "passwordreset-emailtext-user": "{{SITENAME}} 'ਤੇ User $1 ਨੇ ਤੁਹਾਡੇ {{SITENAME}} ($4) ਉਤਲੇ ਪਛਾਣ-ਸ਼ਬਦ ਨੂੰ ਮੁੜ-ਬਣਾਉਣ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਹੈ। ਇਸ ਈਮੇਲ ਪਤੇ ਨਾਲ਼ ਹੇਠ ਲਿਖੇ {{PLURAL:$3|ਖਾਤੇ|ਖਾਤਿਆਂ}} ਦਾ ਵਾਸਤਾ ਹੈ:\n\n$2\n\n{{PLURAL:$3|ਇਸ ਆਰਜ਼ੀ ਪਛਾਣ-ਸ਼ਬਦ|ਇਹਨਾਂ ਆਰਜ਼ੀ ਪਛਾਣ-ਸ਼ਬਦਾਂ}} ਦੀ ਮਿਆਦ {{PLURAL:$5|ਇੱਕ ਦਿਨ|$5 ਦਿਨਾਂ}} 'ਚ ਮੁੱਕ ਜਾਵੇਗੀ।\nਤੁਹਾਨੂੰ ਹੁਣੇ ਦਾਖ਼ਲ ਹੋ ਕੇ ਕੋਈ ਨਵਾਂ ਪਛਾਣ-ਸ਼ਬਦ ਬਣਾ ਲੈਣਾ ਚਾਹੀਦਾ ਹੈ। ਜੇਕਰ ਕਿਸੇ ਹੋਰ ਨੇ ਇਹ ਬੇਨਤੀ ਕੀਤੀ ਹੈ ਜਾਂ ਤੁਹਾਨੂੰ ਆਪਣਾ ਪਹਿਲਾ ਪਛਾਣ-ਸ਼ਬਦ ਯਾਦ ਆ ਗਿਆ ਹੈ ਅਤੇ ਹੁਣ ਤੁਸੀਂ ਉਹਨੂੰ ਬਦਲ਼ਨਾ ਨਹੀਂ ਲੋਚਦੇ ਤਾਂ ਤੁਸੀਂ ਇਸ ਸੁਨੇਹੇ ਨੂੰ ਅਣਡਿੱਠਾ ਕਰ ਕੇ ਆਪਣਾ ਪੁਰਾਣਾ ਪਛਾਣ-ਸ਼ਬਦ ਵਰਤਦੇ ਰਹਿ ਸਕਦੇ ਹੋ।",
        "passwordreset-emailelement": "ਯੂਜ਼ਰ-ਨਾਂ: \n$1\n\nਆਰਜ਼ੀ ਪਾਸਵਰਡ: \n$2",
        "passwordreset-emailsentemail": "ਇੱਕ ਪਾਸਵਰਡ ਮੁੜ-ਸੈੱਟ ਈ-ਮੇਲ ਭੇਜੀ ਜਾ ਚੁੱਕੀ ਹੈ।",
-       "passwordreset-emailsent-capture": "ਇੱਕ ਯਾਦ-ਦਹਾਨੀ ਈ-ਮੇਲ, ਜਿਹੜੀ ਕਿ ਹੇਠਾਂ ਦਿੱਸ ਰਹੀ ਹੈ, ਭੇਜੀ ਜਾ ਚੁੱਕੀ ਹੈ।",
-       "passwordreset-emailerror-capture": "ਪਛਾਣ-ਸ਼ਬਦ ਮੁੜ ਬਣਾਉਣ ਵਾਸਤੇ ਈਮੇਲ ਤਿਆਰ ਹੋ ਗਈ ਸੀ, ਜੋ ਹੇਠਾਂ ਵਿਖਾਈ ਗਈ ਹੈ, ਪਰ ਇਹਨੂੰ {{GENDER:$2|ਵਰਤੋਂਕਾਰ}} ਵੱਲ ਨਹੀਂ ਘੱਲਿਆ ਜਾ ਸਕਿਆ: $1",
-       "changeemail": "ਈ-ਮੇਲ ਸਿਰਨਾਵਾਂ ਬਦਲੋ",
+       "passwordreset-invalideamil": "ਗ਼ਲਤ ਈਮੇਲ ਪਤਾ",
+       "changeemail": "ਈ-ਮੇਲ ਸਿਰਨਾਵਾਂ ਬਦਲੋ ਜਾੰ ਹਟਾਓ",
        "changeemail-header": "ਖਾਤੇ ਵਾਲਾ ਈ-ਮੇਲ ਸਿਰਨਾਵਾਂ ਬਦਲੋ",
        "changeemail-no-info": "ਇਸ ਸਫ਼ੇ ਨੂੰ ਸਿੱਧੇ ਹੀ ਵੇਖਣ ਲਈ ਤੁਹਾਨੂੰ ਲਾਗਇਨ ਕਰਨਾ ਪਵੇਗਾ।",
        "changeemail-oldemail": "ਮੌਜੂਦਾ ਈਮੇਲ ਸਿਰਨਾਵਾਂ:",
        "minoredit": "ਇਹ ਇੱਕ ਛੋਟੀ ਸੋਧ ਹੈ",
        "watchthis": "ਇਸ ਸਫ਼ੇ ’ਤੇ ਨਜ਼ਰ ਰੱਖੋ",
        "savearticle": "ਸਫ਼ਾ ਸਾਂਭੋ",
+       "savechanges": "ਤਬਦੀਲੀਆਂ ਸਾਂਭੋ",
        "preview": "ਝਲਕ",
        "showpreview": "ਝਲਕ ਵਿਖਾਓ",
        "showdiff": "ਤਬਦੀਲੀਆਂ ਵਿਖਾਉ",
        "missingsummary": "'''ਯਾਦ-ਦਹਾਨੀ:''' ਤੁਸੀਂ ਸੋਧ ਸਾਰ ਮੁਹੱਈਆ ਨਹੀਂ ਕਰਵਾਇਆ। ਜੇ ਤੁਸੀਂ \"{{int:savearticle}}\" ਤੇ ਦੁਬਾਰਾ ਕਲਿੱਕ ਕੀਤਾ ਤਾਂ ਤੁਹਾਡਾ ਸਫ਼ਾ ਇਸਦੇ ਬਿਨਾਂ ਹੀ ਸਾਂਭਿਆ ਜਾਵੇਗਾ।",
        "missingcommenttext": "ਹੇਠਾਂ ਇੱਕ ਟਿੱਪਣੀ ਦਿਓ।",
        "summary-preview": "ਸੋਧ ਸਾਰ ਦੀ ਝਲਕ:",
-       "subject-preview": "ਵਿਸ਼ੇ/ਸਿਰਨਾਵੇਂ ਦੀ ਝਲਕ:",
+       "subject-preview": "ਵਿਸ਼ੇ ਦੀ ਝਲਕ:",
        "previewerrortext": "ਤੁਹਾਡੀਆਂ ਤਬਦੀਲੀਆਂ ਦੀ ਝਲਕ ਵਿਖਾਉਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਵਿੱਚ ਗ਼ਲਤੀ ਆ ਗਈ।",
        "blockedtitle": "ਵਰਤੋਂਕਾਰ ਉੱਤੇ ਪਾਬੰਦੀ ਲੱਗੀ ਹੋਈ ਹੈ",
        "blockedtext": "'''ਤੁਹਾਡੇ ਮੌਮਬਰ ਨਾਮ ਜਾਂ IP ਪਤੇ ’ਤੇ ਪਾਬੰਦੀ ਲੱਗ ਚੁੱਕੀ ਹੈ।'''\n\nਪਾਬੰਦੀ $1 ਨੇ ਲਾਈ ਹੈ।\nਦਿੱਤਾ ਗਿਆ ਕਾਰਨ ਇਹ ਹੈ, ''$2''।\n\n* ਪਾਬੰਦੀ ਸ਼ੁਰੂ: $8\n* ਪਾਬੰਦੀ ਖਤਮ: $6\n* ਪਾਬੰਦੀ ਲਾਉਣ ਵਾਲੇ ਦਾ ਇਰਾਦਾ: $7\n\nਪਾਬੰਦੀ ਬਾਰੇ ਚਰਚਾ ਕਰਨ ਲਈ ਤੁਸੀਂ $1 ਜਾਂ ਕਿਸੇ ਹੋਰ\n[[{{MediaWiki:Grouppage-\nsysop}}|administrator]] ਨਾਲ ਰਾਬਤਾ ਕਰ ਸਕਦੇ ਹੋ।\nਤੁਸੀਂ 'ਇਸ ਮੈਂਬਰ ਨੂੰ ਈ-ਮੇਲ ਭੇਜੋ' ਸਹੂਲਤ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕਰ ਸਕਦੇ ਜੇ ਤੁਹਾਡੀਆਂ [[Special:Preferences|ਖਾਤਾ ਪਸੰਦਾਂ]] ਵਿੱਚ ਇੱਕ ਸਹੀ ਈ-ਮੇਲ ਪਤਾ ਨਹੀਂ ਦਿੱਤਾ ਗਿਆ ਜਾਂ ਜੇ ਤੁਹਾਡੇ ਇਸਨੂੰ ਵਰਤਣ ਤੇ ਪਾਬੰਦੀ ਹੈ।\nਤੁਹਾਡਾ ਚਾਲੂ IP ਪਤਾ $3 ਹੈ,\nਅਤੇ ਪਾਬੰਦੀ ਪਤਾ #$5 ਹੈ।\nਮਿਹਰਬਾਨੀ ਕਰਕੇ ਆਪਣੇ ਕਿਸੇ ਵੀ ਸਵਾਲ ਜਾਂ ਪੁੱਛ-ਗਿੱਛ ਵਿਚ ਇਹ ਉੱਪਰਲੀ ਤਫ਼ਸੀਲ ਜ਼ਰੂਰ ਸ਼ਾਮਲ ਕਰੋ।",
        "nosuchsectiontitle": "ਭਾਗ ਲੱਭ ਨਹੀਂ ਰਿਹਾ",
        "nosuchsectiontext": "ਤੁਸੀਂ ਨਾ-ਮੌਜੂਦ ਭਾਗ ਨੂੰ ਸੋਧਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੀ ਹੈ।\nਸ਼ਾਇਦ ਤੁਹਾਡੇ ਸਫ਼ੇ ਨੂੰ ਵੇਖਣ ਦੇ ਦੌਰਾਨ ਇਹ ਮਿਟਾਇਆ ਜਾਂ ਇਸਦਾ ਸਿਰਲੇਖ ਬਦਲਿਆ ਜਾ ਚੁੱਕਾ ਹੈ।",
        "loginreqtitle": "ਲਾਗਇਨ ਚਾਹੀਦਾ ਹੈ",
-       "loginreqlink": "ਲਾà¨\97à¨\87ਨ",
+       "loginreqlink": "ਦਾà¨\96ਲ",
        "loginreqpagetext": "ਹੋਰ ਸਫ਼ੇ ਵੇਖਣ ਲਈ ਤੁਹਾਨੂੰ $1 ਕਰਨਾ ਪਵੇਗਾ।",
        "accmailtitle": "ਪਾਸਵਰਡ ਭੇਜਿਆ।",
        "accmailtext": "[[User talk:$1|$1]] ਲਈ ਰਲ਼ਵੇਂ ਤੌਰ ’ਤੇ ਬਣਿਆ ਪਾਸਵਰਡ $2 ਨੂੰ ਭੇਜਿਆ ਜਾ ਚੁੱਕਾ ਹੈ।\nਇਸ ਨਵੇਂ ਖਾਤੇ ਲਈ ਲਾਗਇਨ ਕਰਨ ਤੋਂ ਬਾਅਦ ''[[Special:ChangePassword|ਪਾਸਵਰਡ ਬਦਲੋ]]'' ’ਤੇ ਜਾ ਕੇ ਪਾਸਵਰਡ ਬਦਲਿਆ ਜਾ ਸਕਦਾ ਹੈ।",
        "undo-nochange": "ਲਗਦਾ ਹੈ ਕਿ ਿਹ ਸੋਧ ਪਹਿਲਾਂ ਹੀ ਮੋੜ ਦਿੱਤੀ ਗਈ ਹੈ।",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|ਗੱਲ-ਬਾਤ]]) ਦੀ ਸੋਧ $1 ਨਕਾਰੀ",
        "undo-summary-username-hidden": "ਗੁਪਤ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਕੀਤੀ $1 ਸੋਧ ਰੱਦ ਕਰੋ",
-       "cantcreateaccounttitle": "ਖਾਤਾ ਬਣਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ",
        "cantcreateaccount-text": "[[User:$3|$3]] ਨੇ ਇਸ IP ਪਤੇ ('''$1''') ਤੋਂ ਖਾਤਾ ਬਣਾਉਣ ਤੇ ਪਾਬੰਦੀ ਲਾਈ ਹੈ।\n\n$3 ਨੇ ਕਾਰਨ ਇਹ ਦੱਸਿਆ ਹੈ, ''$2''",
        "viewpagelogs": "ਇਹ ਸਫ਼ੇ ਲਈ ਇੰਦਰਾਜ ਵੇਖੋ",
        "nohistory": "ਇਸ ਸਫ਼ੇ ਲਈ ਕੋਈ ਸੋਧ ਅਤੀਤ ਨਹੀਂ ਹੈ।",
        "history-feed-empty": "ਦਰਖ਼ਾਸਤਸ਼ੁਦਾ ਸਫ਼ਾ ਮੌਜੂਦ ਨਹੀਂ ਹੈ।\nਸ਼ਾਇਦ ਇਸਨੂੰ ਵਿਕੀ ਤੋਂ ਮਿਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ ਜਾਂ ਨਾਮ ਬਦਲ ਦਿੱਤਾ ਗਿਆ ਹੈ।\nਵਿਕੀ ਦੇ ਨਵੇਂ ਮੁਨਾਸਿਬ ਸਫ਼ਿਆਂ ਵਿਚ [[Special:Search|ਲੱਭਣ]] ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ।",
        "rev-deleted-comment": "(ਸੋਧ ਸਾਰ ਹਟਾਇਆ)",
        "rev-deleted-user": "(ਵਰਤੋਂਕਾਰ ਨਾਂ ਹਟਾਇਆ)",
-       "rev-deleted-event": "(ਲਾà¨\97 à¨\95ਾਰਵਾà¨\88 à¨¹à¨\9fਾà¨\88 à¨\97à¨\88)",
+       "rev-deleted-event": "(ਲਾà¨\97 à¨µà©\87ਰਵਾ à¨¹à¨\9fਾà¨\87à¨\86 à¨\97ਿà¨\86)",
        "rev-deleted-user-contribs": "[ਮੈਂਬਰ-ਨਾਂ ਜਾਂ IP ਪਤਾ ਹਟਾਇਆ - ਸੋਧ ਯੋਗਦਾਨਾਂ ਵਿਚੋਂ ਓਹਲੇ ਕੀਤੀ]",
        "rev-deleted-text-permission": "ਸਫ਼ੇ ਦੀ ਇਹ ਰੀਵਿਜ਼ਨ '''ਮਿਟਾਈ''' ਜਾ ਚੁੱਕੀ ਹੈ।\nਤਫ਼ਸੀਲ [{{fullurl:{{#Special:Log}}/delete|\npage={{FULLPAGENAMEE}}}} ਮਿਟਾਉਣ ਦੇ ਚਿੱਠੇ] ਵਿਚ ਵੇਖੀ ਜਾ ਸਕਦੀ ਹੈ।",
        "rev-deleted-text-unhide": "ਸਫ਼ੇ ਦੀ ਇਹ ਰੀਵਿਜ਼ਨ '''ਮਿਟਾਈ''' ਜਾ ਚੁੱਕੀ ਹੈ।\nਤਫ਼ਸੀਲ [{{fullurl:{{#Special:Log}}/delete|\npage={{FULLPAGENAMEE}}}} ਮਿਟਾਉਣ ਦੇ ਚਿੱਠੇ] ਵਿਚ ਵੇਖੀ ਜਾ ਸਕਦੀ ਹੈ।\nਜੇ ਤੁਸੀਂ ਅੱਗੇ ਵਧਣਾ ਚਾਹੋ ਤਾਂ ਹਾਲੇ ਵੀ [$1 ਇਹ ਰੀਵਿਜ਼ਨ ਵੇਖ] ਸਕਦੇ ਹੋ।",
        "mergehistory-submit": "ਰੀਵਿਜ਼ਨਾਂ ਰਲ਼ਾਓ",
        "mergehistory-empty": "ਕੋਈ ਰੀਵਿਜ਼ਨ ਰਲ਼ਾਈ ਨਹੀ ਜਾ ਸਕਦੀ।",
        "mergehistory-done": "$1 {{PLURAL:|ਦੀ|ਦੀਆਂ}} $3 {{PLURAL:$3|ਰੀਵਿਜ਼ਨ|ਰੀਵਿਜ਼ਨਾਂ}} ਕਾਮਯਾਬੀ ਨਾਲ਼ [[:$2]] ਵਿਚ {{PLURAL:$3|ਰਲ਼ਾਈ|ਰਲ਼ਾਈਆਂ}}।",
+       "mergehistory-fail-bad-timestamp": "ਸਮਾੰਮੋਹਰ ਗਲਤ ਹੈ।",
+       "mergehistory-fail-invalid-source": "ਸਰੋਤ ਸਫ਼ਾ ਗਲਤ ਹੈ।",
        "mergehistory-no-source": "ਸਰੋਤ ਸਫ਼ਾ $1 ਮੌਜੂਦ ਨਹੀਂ ਹੈ।",
        "mergehistory-no-destination": "ਨੀਯਤ ਸਫ਼ਾ $1 ਮੌਜੂਦ ਨਹੀਂ ਹੈ।",
        "mergehistory-invalid-source": "ਸਰੋਤ ਸਫ਼ਾ ਇੱਕ ਸਹੀ ਸਿਰਲੇਖ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ।",
        "prefs-rc": "ਹਾਲੀਆ ਤਬਦੀਲੀਆਂ",
        "prefs-watchlist": "ਨਿਗਰਾਨ-ਸੂਚੀ",
        "prefs-editwatchlist": "ਨਿਗਰਾਨੀ-ਲਿਸਟ ਸੋਧੋ",
+       "prefs-editwatchlist-clear": "ਆਪਣੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਸਾਫ ਕਰੋ",
        "prefs-watchlist-days": "ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿਚ ਦਿਖਾਉਣ ਲਈ ਦਿਨ:",
        "prefs-watchlist-days-max": "ਵੱਧ ਤੋਂ ਵੱਧ $1 {{PLURAL:$1|ਦਿਨ|ਦਿਨ}}",
        "prefs-watchlist-edits": "ਵਧਾਈ ਹੋਈ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਵਿਚ ਦਿਖਾਉਣ ਲਈ ਵੱਧ ਤੋਂ ਵੱਧ ਤਬਦੀਲੀਆਂ:",
        "prefs-watchlist-token": "ਨਿਗਰਾਨੀ-ਲਿਸਟ ਟੋਕਨ:",
        "prefs-misc": "ਫੁਟਕਲ",
        "prefs-resetpass": "ਪਛਾਣ-ਸ਼ਬਦ ਬਦਲੋ",
-       "prefs-changeemail": "ਈ-ਮੇਲ ਪਤਾ ਬਦਲੋ",
+       "prefs-changeemail": "ਈ-ਮੇਲ ਪਤਾ ਬਦਲੋ ਜਾੰ ਹਟਾਓ",
        "prefs-setemail": "ਈ-ਮੇਲ ਪਤਾ ਸੈੱਟ ਕਰੋ",
        "prefs-email": "ਈਮੇਲ ਚੋਣਾਂ",
        "prefs-rendering": "ਦਿੱਖ",
        "right-upload_by_url": "URL ਤੋਂ ਫਾਇਲਾਂ ਅੱਪਲੋਡ ਕਰੋ",
        "right-autoconfirmed": "ਨੀਮ-ਸੁਰੱਖਿਅਤ ਸਫ਼ਿਆਂ ਨੂੰ ਸੋਧਣਾ",
        "right-bot": "ਇੱਕ ਸਵੈ-ਚਾਲਤ ਅਮਲ ਵਜੋਂ ਗਿਣਿਆ ਜਾਣਾ",
-       "right-writeapi": "à¨\8fਪà©\80à¨\86à¨\88(API) à¨¦à©\80 à¨µà¨°à¨¤à©\8bà¨\82",
+       "right-writeapi": "ਲਿà¨\96ਾà¨\88 à¨\8fਪà©\80à¨\86à¨\88 à¨¦à©\80 à¨µà¨°à¨¤à©\8bà©°",
        "right-delete": "ਸਫ਼ੇ ਹਟਾਓ",
        "right-bigdelete": "ਵੱਡੇ ਅਤੀਤ ਵਾਲੇ ਪੰਨੇ ਮਿਟਾਉਣੇ",
        "right-browsearchive": "ਮਿਟਾਏ ਹੋਏ ਸਫ਼ੇ ਖੋਜੋ",
        "right-siteadmin": "ਡੈਟਾਬੇਸ ਨੂੰ ਤਾਲਾ ਲਾਉਣਾ ਤੇ ਖੋਲ੍ਹਣਾ",
        "right-sendemail": "ਦੂਜੇ ਮੈਂਬਰਾਂ ਨੂੰ ਈ-ਮੇਲ ਭੇਜਣਾ",
        "right-passwordreset": "ਪਾਸਵਰਡ ਮੁੜ-ਸੈੱਟ ਈਮੇਲ ਵੇਖੋ",
+       "grant-group-email": "ਈ-ਮੇਲ ਭੇਜੋ",
+       "grant-group-customization": "ਅਨੁਕੂਲਨ ਅਤੇ ਪਸੰਦਾੰ",
+       "grant-createaccount": "ਖਾਤੇ ਬਣਾਓ",
+       "grant-createeditmovepage": "ਸਫ਼ੇ ਬਣਾਓ, ਸੋਧ, ਅਤੇ ਹਿੱਲਾਓ",
+       "grant-editmywatchlist": "ਆਪਣੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਸੋਧੋ",
+       "grant-editprotected": "ਸੁਰੱਖਿਅਤ ਸਫ਼ੇ ਸੋਧ",
+       "grant-uploadfile": "ਨਵੀਆੰ ਫਾਈਲਾੰ ਅੱਪਲੋਡ ਕਰੋ",
+       "grant-basic": "ਬੁਨਿਆਦੀ ਹੱਕ",
+       "grant-viewmywatchlist": "ਆਪਣੀ ਨਿਗਰਾਨੀ-ਸੂਚੀ ਵੇਖੋ",
        "newuserlogpage": "ਬਣਾਏ ਖਾਤਿਆਂ ਦਾ ਚਿੱਠਾ",
        "newuserlogpagetext": "ਇਹ ਬਣੇ ਮੈਂਬਰਾਂ ਦਾ ਚਿੱਠਾ ਹੈ।",
        "rightslog": "ਵਰਤੋਂਕਾਰ ਹੱਕਾਂ ਦਾ ਚਿੱਠਾ",
        "rightslogtext": "ਇਹ ਮੈਂਬਰ ਹੱਕਾਂ ਵਿਚ ਹੋਈਆਂ ਤਬਦੀਲੀਆਂ ਦਾ ਚਿੱਠਾ ਹੈ।",
        "action-read": "ਇਹ ਸਫ਼ਾ ਪੜ੍ਹੋ",
        "action-edit": "ਇਹ ਸਫ਼ਾ ਸੋਧੋ",
-       "action-createpage": "ਸਫ਼à©\87 ਬਣਾਓ",
-       "action-createtalk": "à¨\9aਰà¨\9aਾ à¨¸à¨«à¨¼à©\87 ਬਣਾਉਣ",
+       "action-createpage": "à¨\87ਹ à¨¸à¨«à¨¼à¨¾ ਬਣਾਓ",
+       "action-createtalk": "à¨\87ਹ à¨\9aਰà¨\9aਾ à¨¸à¨«à¨¼à¨¾ ਬਣਾਉਣ",
        "action-createaccount": "ਇਹ ਵਰਤੋਂਕਾਰ ਖਾਤਾ ਬਣਾਓ",
        "action-history": "ਇਸ ਸਫ਼ੇ ਦਾ ਅਤੀਤ ਵੇਖੋ",
        "action-minoredit": "ਇਹ ਸੋਧ ਨੂੰ ਛੋਟੀ ਤੌਰ ਉੱਤੇ ਮੰਨੋ",
        "action-block": "ਇਸ ਮੈਂਬਰ ਦੇ ਸੋਧ ਕਰਨ ਤੇ ਪਾਬੰਦੀ ਲਾਓ",
        "action-protect": "ਇਸ ਸਫ਼ੇ ਦੀ ਸੁਰੱਖਿਆ ਬਦਲੋ",
        "action-import": "ਹੋਰ ਵਿਕੀ ਤੋਂ ਇਹ ਸਫ਼ਾ ਲਵੋ",
-       "action-importupload": "ਫ਼ਾà¨\88ਲ à¨\85ੱਪਲà©\8bਡ à¨¤à©\8bà¨\82 à¨\87ਹ à¨¸à¨«à¨¼à¨¾ ਮੰਗਾਓ",
+       "action-importupload": "ਫ਼ਾà¨\88ਲ à¨\85ੱਪਲà©\8bਡ à¨¤à©\8bà¨\82 à¨¸à¨«à©\87 ਮੰਗਾਓ",
        "action-unwatchedpages": "ਨਜ਼ਰ ਨਾ ਰੱਖੇ ਜਾ ਰਹੇ ਸਫ਼ਿਆਂ ਦੀ ਸੂਚੀ ਵੇਖੋ",
        "action-mergehistory": "ਇਸ ਸਫ਼ੇ ਦੇ ਅਤੀਤ ਨੂੰ ਰਲ਼ਾਉਣ",
        "action-userrights": "ਵਰਤੋਂਕਾਰ ਦੇ ਸਾਰੇ ਹੱਕ ਸੋਧਣ",
        "recentchanges-feed-description": "ਇਸ ਵਿਕੀ ’ਤੇ ਹਾਲ ‘ਚ ਹੋਈਆਂ ਤਬਦੀਲੀਆਂ ਇਸ ਫ਼ੀਡ ’ਚ ਵੇਖੀਆਂ ਜਾ ਸਕਦੀਆਂ ਹਨ।",
        "recentchanges-label-newpage": "ਇਸ ਸੋਧ ਨਾਲ਼ ਨਵਾਂ ਸਫ਼ਾ ਬਣਿਆ ਹੈ",
        "recentchanges-label-minor": "ਇਹ ਇੱਕ ਛੋਟੀ ਸੋਧ ਹੈ",
-       "recentchanges-label-bot": "à¨\87ਹ à¨¸à©\8bਧ à¨\87ੱà¨\95 à¨¬à©\8bਟ ਦੁਆਰਾ ਕੀਤੀ ਗਈ ਸੀ",
+       "recentchanges-label-bot": "à¨\87ਹ à¨¸à©\8bਧ à¨\87ੱà¨\95 à¨¬à©\8cਟ ਦੁਆਰਾ ਕੀਤੀ ਗਈ ਸੀ",
        "recentchanges-label-unpatrolled": "ਇਹ ਫੇਰ-ਬਦਲ ਹਾਲੇ ਵੇਖਿਆ ਨਹੀਂ ਗਿਆ",
        "recentchanges-label-plusminus": "ਸਫ਼ੇ ਵਿੱਚ ਇੰਨੀਆਂ ਬਾਈਟਾਂ ਦੀ ਤਬਦੀਲੀ ਹੋਈ",
        "recentchanges-legend-heading": "<strong>ਟੀਕਾ:</strong>",
        "rcshowhidemine": "ਮੇਰੀਆਂ ਤਬਦੀਲੀਆਂ  $1",
        "rcshowhidemine-show": "ਵੇਖੋ",
        "rcshowhidemine-hide": "ਓਹਲੇ",
+       "rcshowhidecategorization-show": "ਦਿਖਾਓ",
+       "rcshowhidecategorization-hide": "ਲੁਕਾਉ",
        "rclinks": "ਪਿਛਲੇ $2 ਦਿਨਾਂ ਵਿੱਚ ਹੋਈਆਂ $1 ਤਬਦੀਲੀਆਂ ਵਖਾਓ<br /> $3",
        "diff": "ਫ਼ਰਕ",
        "hist": "ਅਤੀਤ",
        "upload-file-error": "ਅੰਦਰੂਨੀ ਗਲਤੀ",
        "upload-misc-error": "ਅਣਪਛਾਤੀ ਅੱਪਲੋਡ ਗਲਤੀ",
        "upload-http-error": "ਇੱਕ HTTP ਗ਼ਲਤੀ ਹੋਈ: $1",
+       "upload-dialog-title": "ਫ਼ਾਈਲ ਅਪਲੋਡ ਕਰੋ",
+       "upload-dialog-button-cancel": "ਰੱਦ ਕਰੋ",
+       "upload-dialog-button-done": "ਮੁਕੰਮਲ",
+       "upload-dialog-button-save": "ਸਾਂਭੋ",
+       "upload-dialog-button-upload": "ਅੱਪਲੋਡ",
+       "upload-form-label-infoform-title": "ਵੇਰਵਾ",
+       "upload-form-label-infoform-name": "ਨਾਂ",
+       "upload-form-label-infoform-description": "ਵਰਨਣ",
+       "upload-form-label-usage-title": "ਉਪਯੋਗਤਾ",
+       "upload-form-label-usage-filename": "ਫ਼ਾਈਲ ਦਾ ਨਾਂ",
+       "upload-form-label-own-work": "ਇਹ ਮੇਰਾ ਆਪਣਾ ਕੰਮ ਹੈ",
+       "upload-form-label-infoform-categories": "ਸ਼੍ਰੇਣੀਆਂ",
        "upload-form-label-infoform-date": "ਤਾਰੀਖ਼",
        "backend-fail-notexists": "ਫ਼ਾਈਲ $1 ਮੌਜੂਦ ਨਹੀਂ ਹੈ।",
        "backend-fail-delete": "ਫ਼ਾਈਲ \"$1\" ਮਿਟਾਈ ਨਹੀਂ ਜਾ ਸਕੀ।",
        "backend-fail-create": "\"$1\" ਫ਼ਾਈਲ ਲਿਖੀ ਨਾ ਜਾ ਸਕੀ।",
        "zip-wrong-format": "ਦੱਸੀ ਗਈ ਫ਼ਾਈਲ ਜ਼ਿੱਪ ਫ਼ਾਈਲ ਨਹੀਂ ਸੀ।",
        "uploadstash-refresh": "ਫ਼ਾਈਲਾਂ ਦੀ ਲਿਸਟ ਨੂੰ ਤਾਜ਼ਾ ਕਰੋ",
+       "uploadstash-thumbnail": "ਥੰਮਨੇਲ ਵੇਖੋ",
        "img-auth-accessdenied": "ਪਹੁੰਚ ਨਕਾਰੀ ਗਈ",
        "img-auth-nofile": "ਫ਼ਾਈਲ \"$1\" ਮੌਜੂਦ ਨਹੀਂ ਹੈ।",
+       "http-invalid-url": "ਗਲਤ URL: $1",
        "upload-curl-error6": "URL ’ਤੇ ਪਹੁੰਚਿਆ ਨਹੀਂ ਜਾ ਸਕਿਆ।",
        "upload-curl-error6-text": "ਦਿੱਤੇ ਹੋਏ URL ’ਤੇ ਪਹੁੰਚਿਆ ਨਹੀਂ ਜਾ ਸਕਿਆ।\nਮਿਹਰਬਾਨੀ ਕਰਕੇ ਦੁਬਾਰਾ ਜਾਂਚ ਕਰੋ ਕਿ URL ਸਹੀ ਹੈ ਅਤੇ ਸਾਈਟ ਉਪਲੱਬਧ ਹੈ।",
        "upload-curl-error28": "ਅੱਪਲੋਡ ਟਾਈਮ-ਆਉਟ",
        "uploadnewversion-linktext": "ਇਸ ਫਾਇਲ ਦਾ ਇੱਕ ਨਵਾਂ ਵਰਜਨ ਅੱਪਲੋਡ ਕਰੋ",
        "shared-repo-from": "$1 ਤੋਂ",
        "shared-repo": "ਕੋਈ ਸਾਂਝਾ ਕੀਤਾ ਭੰਡਾਰ",
-       "upload-disallowed-here": "ਤà©\81ਸà©\80à¨\82 à¨\87ਸ à¨«à¨¼à¨¾à¨\88ਲ à¨\89ੱਤà©\87 à¨¨à¨¹à©\80à¨\82 à¨¨à¨¹à©\80à¨\82 à¨²à¨¿à¨\96 à¨¸à¨\95ਦà©\87।",
+       "upload-disallowed-here": "ਤੁਸੀਂ ਇਸ ਫ਼ਾਈਲ ਉੱਤੇ ਨਹੀਂ ਲਿਖ ਸਕਦੇ।",
        "filerevert": "$1 ਰੀਵਰਟ",
        "filerevert-legend": "ਫਾਇਲ ਰੀਵਰਟ",
        "filerevert-comment": "ਕਾਰਨ:",
        "randomincategory-nopages": "[[:Category:$1|$1]] ਸ਼੍ਰੇਣੀ ਵਿਚ ਕੋਈ ਸਫ਼ਾ ਨਹੀਂ ਹੈ।",
        "randomincategory-category": "ਵਰਗ:",
        "randomincategory-legend": "ਵਰਗ ਵਿਚਲਾ ਰਲ਼ਵਾਂ ਸਫ਼ਾ",
+       "randomincategory-submit": "ਜਾਓ",
        "randomredirect": "ਰਲ਼ਵਾਂ ਰੀਡਿਰੈਕਟ",
        "statistics": "ਅੰਕੜੇ",
        "statistics-header-pages": "ਸਫ਼ਾ ਅੰਕੜੇ",
        "pager-newer-n": "{{PLURAL:$1|1 ਨਵਾਂ|$1 ਨਵੇਂ}}",
        "pager-older-n": "{{PLURAL:$1|1 ਪੁਰਾਣਾ|$1 ਪੁਰਾਣੇ}}",
        "suppress": "ਨਿਗਰਾਨ",
+       "apihelp": "API ਮਦਦ",
+       "apihelp-no-such-module": "ਮੌਡੂਲ \"$1\" ਨਹੀਂ ਲੱਭਾ।",
+       "apisandbox-unfullscreen": "ਸਫ਼ਾ ਵੇਖਾਓ",
+       "apisandbox-reset": "ਸਾਫ਼ ਕਰੋ",
+       "apisandbox-retry": "ਮੁੜ-ਕੋਸ਼ਿਸ਼ ਕਰੋ",
+       "apisandbox-helpurls": "ਮਦਦ ਜੋੜ",
+       "apisandbox-examples": "ਮਿਸਾਲਾਂ",
+       "apisandbox-results": "ਨਤੀਜੇ",
        "booksources": "ਪੁਸਤਕਾਂ ਦੇ ਸਰੋਤ",
        "booksources-search-legend": "ਕਿਤਾਬਾਂ ਦੇ ਸਰੋਤ ਖੋਜੋ",
        "booksources-search": "ਭਾਲ਼",
        "listgrouprights-removegroup-self-all": "ਆਪਣੇ ਖਾਤੇ ਤੋਂ ਸਾਰੇ ਸਮੂਹ ਹਟਾਓ",
        "listgrouprights-namespaceprotection-header": "ਨਾਂ-ਥਾਂ ਦੀਆਂ ਬੰਧੇਜਾਂ",
        "listgrouprights-namespaceprotection-namespace": "ਨਾਂ-ਥਾਂ",
+       "listgrants-rights": "ਹੱਕ",
        "trackingcategories": "ਵਰਗਾਂ ਦੀ ਪੈੜ",
        "trackingcategories-msg": "ਵਰਗ ਦੀ ਪੈੜ",
        "trackingcategories-name": "ਸੁਨੇਹੇ ਦਾ ਨਾਂ",
        "watchlist-details": "ਗੱਲ-ਬਾਤ ਸਫ਼ੇ ਨਾ ਗਿਣਦੇ ਹੋਏ, ਤੁਹਾਡੀ ਨਿਗਰਾਨੀ-ਸੂਚੀ ਵਿਚ{{PLURAL:$1|$1 ਸਫ਼ਾ ਹੈ|$1 ਸਫ਼ੇ ਹਨ}}।",
        "wlheader-enotif": "ਈਮੇਲ ਸੂਚਨਾ ਚਾਲੂ ਹੈ।",
        "wlnote": "$3, $4 ਮੁਤਾਬਕ ਆਖ਼ਰੀ {{PLURAL:$2|ਘੰਟੇ|'''$2''' ਘੰਟਿਆਂ}} ਵਿਚ {{PLURAL:\n$1|ਤਬਦੀਲੀ ਹੋਈ|'''$1''' ਤਬਦੀਲੀਆਂ ਹੋਈਆਂ}}, ਹੇਠਾਂ ਵੇਖੋ।",
-       "wlshowlast": "ਪਿਛਲੇ $1 ਘੰਟੇ $2 ਦਿਨ  ਵਖਾਓ",
+       "wlshowlast": "ਪਿਛਲੇ $1 ਘੰਟੇ $2 ਦਿਨ ਵਖਾਓ",
+       "watchlist-hide": "ਲੁਕਾਓ",
        "watchlist-submit": "ਦਿਖਾਓ",
+       "wlshowhideminor": "ਨਿੱਕੀਆਂ ਸੋਧਾਂ",
+       "wlshowhidebots": "ਬੌਟ",
+       "wlshowhideanons": "ਗੁਮਨਾਮ ਵਰਤੋਂਕਾਰ",
+       "wlshowhidepatr": "ਜਾਂਚੀਆਂ ਸੋਧਾੰ",
+       "wlshowhidemine": "ਮੇਰੀਆਂ ਸੋਧਾਂ",
        "watchlist-options": "ਨਿਗਰਾਨੀ-ਲਿਸਟ ਦੀਆਂ ਚੋਣਾਂ",
        "watching": "ਨਿਗ੍ਹਾ (ਵਾਚ) ਰੱਖੀ ਜਾ ਰਹੀ ਹੈ...",
        "unwatching": "ਨਿਗ੍ਹਾ ਰੱਖਣੀ (ਵਾਚ) ਬੰਦ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ..",
        "sessionfailure-title": "ਇਜਲਾਸ ਫੇਲ੍ਹ",
        "changecontentmodel-title-label": "ਸਫ਼ੇ ਦਾ ਸਿਰਲੇਖ",
        "changecontentmodel-reason-label": "ਕਾਰਨ:",
+       "changecontentmodel-submit": "ਬਦਲੋ",
        "protectlogpage": "ਸੁਰੱਖਿਆ ਚਿੱਠਾ",
        "protectedarticle": "\"[[$1]]\" ਸੁਰੱਖਿਅਤ ਕੀਤਾ",
        "modifiedarticleprotection": "\"[[$1]]\" ਦੀ ਸੁਰੱਖਿਆ ਬਦਲੀ",
        "sp-contributions-username": "IP ਪਤਾ ਜਾਂ ਵਰਤੋਂਕਾਰਨਾਮ:",
        "sp-contributions-toponly": "ਸਿਰਫ ਉਹ ਸੋਧਾਂ ਵਿਖਾਓ ਜੋ ਸਭ ਤੋਂ ਤਾਜ਼ੀਆਂ ਰਿਵੀਜਨਾਂ ਹਨ",
        "sp-contributions-newonly": "ਸਿਰਫ਼ ਉਹ ਸੋਧਾਂ ਵਿਖਾਉ ਜਿਹਨਾਂ ਨਾਲ਼ ਨਵੇਂ ਸਫ਼ੇ ਬਣੇ ਹਨ",
+       "sp-contributions-hideminor": "ਛੋਟੀਆਂ ਤਬਦੀਲੀਆਂ ਲੁਕਾਓ",
        "sp-contributions-submit": "ਖੋਜ",
        "whatlinkshere": "ਇੱਥੇ ਕੀ ਆ ਕੇ ਜੁੜਦਾ ਹੈ",
        "whatlinkshere-title": "$1 ਨਾਲ ਜੋੜਨ ਵਾਲੇ ਸਫ਼ੇ",
        "tooltip-pt-mycontris": "ਤੁਹਾਡੇ ਯੋਗਦਾਨਾਂ ਦੀ ਸੂਚੀ",
        "tooltip-pt-login": "ਤੁਹਾਨੂੰ ਦਾਖ਼ਲ ਹੋਣ ਲਈ ਪ੍ਰੇਰਿਆ ਜਾਂਦਾ ਹੈ; ਪਰ ਇਹ ਕੋਈ ਲਾਜ਼ਮੀ ਨਹੀਂ",
        "tooltip-pt-logout": "ਸਾਈਟ ਤੋਂ ਬਾਹਰ ਆਉ",
-       "tooltip-pt-createaccount": "ਤà©\81ਹਾਨà©\82à©° à¨\96ਾਤਾ à¨¬à¨£à¨¾à¨\89ਣ à¨¤à©\8bà¨\82 à¨¬à¨¾à¨\85ਦ à¨²à¨¾à¨\97 à¨\87ਨ ਕਰਨ ਦੀ ਸਲਾਹ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ; ਹਾਲਾਂਕਿ ਇਹ ਲਾਜ਼ਮੀ ਨਹੀਂ",
+       "tooltip-pt-createaccount": "ਤà©\81ਹਾਨà©\82à©° à¨\96ਾਤਾ à¨¬à¨£à¨¾à¨\89ਣ à¨¤à©\8bà¨\82 à¨¬à¨¾à¨\85ਦ à¨¦à¨¾à¨\96ਲ ਕਰਨ ਦੀ ਸਲਾਹ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ; ਹਾਲਾਂਕਿ ਇਹ ਲਾਜ਼ਮੀ ਨਹੀਂ",
        "tooltip-ca-talk": "ਸਮਗੱਰੀ ਸਫ਼ੇ ਬਾਰੇ ਚਰਚਾ",
        "tooltip-ca-edit": "ਇਹ ਸਫ਼ਾ ਸੋਧੋ",
        "tooltip-ca-addsection": "ਨਵਾਂ ਭਾਗ ਸ਼ੁਰੂ ਕਰੋ",
        "tooltip-ca-unprotect": "ਇਸ ਸਫ਼ੇ ਦੀ ਸੁਰੱਖਿਆ ਬਦਲੋ",
        "tooltip-ca-delete": "ਇਸ ਸਫ਼ੇ ਨੂੰ ਹਟਾਓ",
        "tooltip-ca-move": "ਇਹ ਸਫ਼ਾ ਭੇਜੋ",
-       "tooltip-ca-watch": "à¨\87ਸ à¨¸à¨«à¨¼à©\87 à¨¨à©\82à©° à¨\86ਪਣà©\80 à¨¨à¨¿à¨\97ਰਾਨà©\80-ਲਿਸà¨\9f ਵਿਚ ਜੋੜੋ",
+       "tooltip-ca-watch": "à¨\87ਸ à¨¸à¨«à¨¼à©\87 à¨¨à©\82à©° à¨\86ਪਣà©\80 à¨¨à¨¿à¨\97ਰਾਨà©\80-ਸà©\82à¨\9aà©\80 ਵਿਚ ਜੋੜੋ",
        "tooltip-ca-unwatch": "ਇਹ ਸਫ਼ਾ ਆਪਣੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ’ਚੋਂ ਹਟਾਓ",
        "tooltip-search": "{{SITENAME}} ’ਤੇ ਖੋਜੋ",
        "tooltip-search-go": "ਠੀਕ ਇਸ ਨਾਮ ਵਾਲੇ ਪੰਨੇ ’ਤੇ ਜਾਉ, ਜੇ ਮੌਜੂਦ ਹੈ ਤਾਂ",
        "tooltip-n-mainpage-description": "ਮੁੱਖ ਸਫ਼ੇ ਉੱਤੇ ਜਾਓ",
        "tooltip-n-portal": "ਪ੍ਰੋਜੈਕਟ ਬਾਰੇ, ਤੁਸੀਂ ਕੀ ਕਰ ਸਕਦੇ ਹੋ, ਕਿੱਥੇ ਕੁਝ ਲੱਭਣਾ ਹੈ",
        "tooltip-n-currentevents": "ਹਾਲ ਦੀਆਂ ਘਟਨਾਵਾਂ ਬਾਰੇ ਪਿਛੋਕੜੀ ਜਾਣਕਾਰੀ ਲੱਭੋ",
-       "tooltip-n-recentchanges": "ਵਿਕੀ ਵਿਚ ਹਾਲ ਵਿੱਚ ਹੋਈਆਂ ਤਬਦੀਲੀਆਂ ਦੀ ਸੂਚੀ",
+       "tooltip-n-recentchanges": "ਵਿà¨\95à©\80 à¨µà¨¿à©±à¨\9a à¨¹à¨¾à¨² à¨µà¨¿à©±à¨\9a à¨¹à©\8bà¨\88à¨\86à¨\82 à¨¤à¨¬à¨¦à©\80ਲà©\80à¨\86à¨\82 à¨¦à©\80 à¨¸à©\82à¨\9aà©\80",
        "tooltip-n-randompage": "ਇੱਕ ਰਲ਼ਵਾਂ ਸਫ਼ਾ ਲੋਡ ਕਰੋ",
        "tooltip-n-help": "ਖੋਜਣ ਲਈ ਥਾਂ",
        "tooltip-t-whatlinkshere": "ਵਿਕੀ ਦੇ ਸਾਰੇ ਸਫ਼ਿਆ ਦੀ ਸੂਚੀ, ਜੋ ਇੱਥੇ ਜੋੜਦੇ ਹਨ",
        "pageinfo-length": "ਸਫ਼ੇ ਦੀ ਲੰਬਾਈ (ਬਾਈਟਾਂ ਵਿਚ)",
        "pageinfo-article-id": "ਸਫ਼ੇ ਦੀ ਸ਼ਨਾਖ਼ਤ",
        "pageinfo-language": "ਸਫ਼ੇ ਦੀ ਸਮੱਗਰੀ ਦੀ ਭਾਸ਼ਾ",
-       "pageinfo-robot-policy": "à¨\87à©°à¨\9cਨ à¨¦à©\80 à¨¹à¨¾à¨²à¨¤ à¨\96à©\8bà¨\9cà©\8b",
+       "pageinfo-robot-policy": "ਰà©\8bਬà©\8bà¨\9f à¨¦à©\81à¨\86ਰਾ à¨\87ੰਡà©\88à¨\95ਸਿੰà¨\97",
        "pageinfo-robot-index": "ਮਨਜ਼ੂਰ ਹੈ",
        "pageinfo-robot-noindex": "ਨਾ-ਮਨਜ਼ੂਰ",
        "pageinfo-watchers": "ਸਫ਼ੇ ’ਤੇ ਨਜ਼ਰ ਰੱਖਣ ਵਾਲਿਆਂ ਦੀ ਗਿਣਤੀ",
        "watchlistedit-raw-done": "ਤੁਹਾਡੀ ਨਿਗਰਾਨੀ-ਲਿਸਟ ਅੱਪਡੇਟ ਹੋ ਗਈ ਹੈ।",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 ਸਿਰਲੇਖ ਸ਼ਾਮਲ ਕੀਤਾ|$1 ਸਿਰਲੇਖ ਸ਼ਾਮਲ ਕੀਤੇ}}:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 ਸਿਰਲੇਖ ਹਟਾਇਆ|$1 ਸਿਰਲੇਖ ਹਟਾਏ}}:",
+       "watchlistedit-clear-title": "ਨਿਗਰਾਨ-ਸੂਚੀ ਸਾਫ਼ ਕਰੋ",
+       "watchlistedit-clear-legend": "ਨਿਗਰਾਨ-ਸੂਚੀ ਸਾਫ਼ ਕਰੋ",
        "watchlistedit-clear-titles": "ਸਿਰਲੇਖ:",
        "watchlistedit-clear-submit": "ਨਿਗਰਾਨ-ਸੂਚੀ ਸਾਫ਼ ਕਰੋ (ਇਹ ਪੱਕੇ ਤੌਰ 'ਤੇ ਹੈ!)",
        "watchlistedit-clear-done": "ਤੁਹਾਡੀ ਨਿਗਰਾਨ-ਸੂਚੀ ਸਾਫ਼ ਕਰ ਦਿੱਤੀ ਗਈ ਹੈ।",
        "watchlisttools-edit": "ਨਿਗਰਾਨੀ-ਸੂਚੀ ਵੇਖੋ ’ਤੇ ਸੋਧੋ",
        "watchlisttools-raw": "ਕੱਚੀ ਨਿਗਰਾਨ-ਸੂਚੀ ਸੋਧੋ",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|ਗੱਲ-ਬਾਤ]])",
+       "timezone-local": "ਸਥਾਨਕ",
        "duplicate-defaultsort": "ਪੁਰਾਣੀ ਮੂਲ ਕਰਮਾਂਕਨ ਕੁੰਜੀ $1 ਦੇ ਬਜਾਏ ਹੁਣ ਮੂਲ ਕਰਮਾਂਕਨ ਕੁੰਜੀ $2 ਹੋਵੇਗੀ।",
        "version": "ਵਰਜਨ",
        "version-skins": "ਥਾਪੀਆਂ ਖੱਲਾਂ",
        "version-software-version": "ਵਰਜਨ",
        "version-entrypoints-header-entrypoint": "ਦਾਖ਼ਲਾ ਬਿੰਦੂ",
        "version-entrypoints-header-url": "ਯੂ.ਆਰ.ਐੱਲ",
+       "version-libraries-library": "ਪੁਸਤਕਾਲਾ",
+       "version-libraries-version": "ਵਰਜਨ",
+       "version-libraries-license": "ਲਸੰਸ",
+       "version-libraries-description": "ਵੇਰਵਾ",
+       "version-libraries-authors": "ਲੇਖਕ",
        "redirect-submit": "ਜਾਓ",
        "redirect-lookup": "ਲੱਭੋ:",
        "redirect-value": "ਮੁੱਲ:",
        "specialpages-group-wiki": "ਸਮੱਗਰੀ ਅਤੇ ਸੰਦ",
        "specialpages-group-redirects": "ਖ਼ਾਸ ਸਫ਼ੇ ਰੀਡਿਰੈਕਟ ਕੀਤੇ ਜਾ ਰਹੇ ਹਨ",
        "specialpages-group-spam": "ਸਪੈਮ ਦੇ ਸੰਦ",
+       "specialpages-group-developer": "ਡਿਵੈਲਪਰ ਸੰਦ",
        "blankpage": "ਖ਼ਾਲੀ ਸਫ਼ਾ",
        "intentionallyblankpage": "ਇਹ ਸਫ਼ਾ ਜਾਣ-ਬੁੱਝ ਕੇ ਖ਼ਾਲੀ ਛੱਡਿਆ ਗਿਆ ਹੈ।",
        "external_image_whitelist": " #ਇਸ ਲਾਈਨ ਨੂੰ ਇੰਝ ਹੀ ਰਹਿਣ ਦਿਓ <pre>\n#ਹੇਠਾਂ ਓਹੀ ਐਕਸਪ੍ਰੈਸ਼ਨ ਪਾਓ (ਜਿਹੜਾ ਹਿੱਸਾ // ਦੇ ਵਿਚਾਲੇ ਹੈ)\n#ਇਹ ਬਾਹਰੀ ਤਸਵੀਰਾਂ ਦੇ URLs (ਹੌਟਲਿੰਕਡ) ਨਾਲ ਮਿਲਣਗੀਆਂ\n#ਜਿਹੜੀਆਂ ਮਿਲਣਗੀਆਂ ਓਹ ਬਤੌਰ ਤਸਵੀਰਾਂ ਦਿੱਸਣਗੀਆਂ ਨਹੀਂ ਤਾਂ ਤਸਵੀਰ ਦਾ ਸਿਰਫ਼ ਲਿੰਕ ਨਜ਼ਰ ਆਵੇਗਾ\n#'#' ਨਾਲ਼ ਸ਼ੁਰੂ ਹੋਣ ਵਾਲ਼ੀਆਂ ਲਾਈਨਾਂ ਟਿੱਪਣੀਆਂ ਵਾਂਗ ਲਈਆਂ ਜਾਂਦੀਆਂ ਹਨ\n#ਇਹ ਕੇਸ-ਇਨਸੈਂਸਟਿਵ ਹੈ\n\n#ਸਾਰੇ ਰੈਜੈਕਸ ਫ਼ਰੈਗਮੈਂਟ ਇਸ ਲਾਈਨ ਤੋਂ ਉੱਪਰ ਪਾਓ। ਇਸ ਲਾਈਨ ਨੂੰ ਇੰਝ ਹੀ ਰਹਿਣ ਦਿਓ </pre>",
        "tags-title": "ਟੈਗ",
        "tags-tag": "ਟੈਗ ਦਾ ਨਾਮ",
        "tags-description-header": "ਅਰਥ ਦਾ ਪੂਰਾ ਬਿਓਰਾ",
+       "tags-source-header": "ਸਰੋਤ",
        "tags-active-header": "ਸਰਗਰਮ?",
        "tags-hitcount-header": "ਨਿਸ਼ਾਨਦੇਹ ਤਬਦੀਲੀਆਂ",
+       "tags-actions-header": "ਕਾਰਵਾਈਆਂ",
        "tags-active-yes": "ਹਾਂ",
        "tags-active-no": "ਨਹੀਂ",
        "tags-edit": "ਸੋਧੋ",
+       "tags-delete": "ਮਿਟਾਉ",
+       "tags-activate": "ਕਿਰਿਆਸ਼ੀਲ ਕਰੋ",
        "tags-hitcount": "$1 {{PLURAL:$1|ਤਬਦੀਲੀ|ਤਬਦੀਲੀਆਂ}}",
+       "tags-create-reason": "ਕਾਰਨ:",
+       "tags-create-submit": "ਬਣਾਓ",
+       "tags-delete-reason": "ਕਾਰਨ:",
+       "tags-activate-reason": "ਕਾਰਨ:",
+       "tags-activate-submit": "ਕਿਰਿਆਸ਼ੀਲ ਕਰੋ",
+       "tags-deactivate-reason": "ਕਾਰਨ:",
+       "tags-edit-title": "ਟੈਗ ਸੋਧੋ",
+       "tags-edit-remove-all-tags": "(ਸਾਰੇ ਟੈਗ ਹਟਾਓ)",
+       "tags-edit-reason": "ਕਾਰਨ:",
        "comparepages": "ਸਫ਼ੇ ਮਿਲਾਓ",
        "compare-page1": "ਸਫ਼ਾ ੧",
        "compare-page2": "ਸਫ਼ਾ ੨",
        "rightsnone": "(ਕੋਈ ਨਹੀਂ)",
        "revdelete-summary": "ਸੰਖੇਪ ਸੋਧ",
        "feedback-adding": "ਸਫ਼ੇ ਨਾਲ਼ ਵਿਚਾਰ ਜੁੜ ਰਹੇ ਹਨ...",
+       "feedback-back": "ਪਿੱਛੇ",
        "feedback-bugnew": "ਮੈਂ ਚੈੱਕ ਕੀਤਾ। ਇੱਕ ਨਵੇਂ ਦੋਸ਼ ਦੀ ਰਿਪੋਰਟ ਕਰੋ",
        "feedback-cancel": "ਰੱਦ ਕਰੋ",
        "feedback-close": "ਹੋ ਗਿਆ",
+       "feedback-error-title": "ਗ਼ਲਤੀ",
        "feedback-error2": "ਦੋਸ਼:ਸੋਧ ਫੇਲ੍ਹ ਹੋਈ",
        "feedback-error3": "ਦੋਸ਼:API ਵੱਲੋਂ ਕੋਈ ਜੁਆਬ ਨਹੀਂ",
        "feedback-message": "ਸੁਨੇਹਾ:",
        "feedback-subject": "ਵਿਸ਼ਾ:",
        "feedback-submit": "ਹਵਾਲੇ ਕਰੋ",
+       "feedback-thanks-title": "ਧੰਨਵਾਦ!",
+       "feedback-useragent": "ਉਪਭੋਗੀ ਏਜੰਟ:",
        "searchsuggest-search": "ਖੋਜ",
        "api-error-badaccess-groups": "ਤੁਹਾਨੂੰ ਇਸ ਵਿਕੀ ਉੱਤੇ ਫ਼ਾਈਲਾਂ ਅੱਪਲੋਡ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।",
        "api-error-badtoken": "ਅੰਦਰੂਨੀ ਦੋਸ਼: ਗ਼ਲਤ ਟੋਕਨ",
        "limitreport-walltime": "ਹਕੀਕੀ ਸਮੇਂ ਦੀ ਵਰਤੋਂ",
        "limitreport-walltime-value": "$1 {{PLURAL:$1|ਸਕਿੰਟ|ਸਕਿੰਟ}}",
        "limitreport-postexpandincludesize-value": "$1/$2 {{PLURAL:$2|ਬਾਈਟ|ਬਾਈਟ}}",
+       "expand_templates_input": "ਇੰਪੁੱਟ ਪਾਠ:",
        "expand_templates_output": "ਨਤੀਜਾ",
        "expand_templates_ok": "ਠੀਕ ਹੈ",
        "expand_templates_remove_comments": "ਟਿੱਪਣੀਆਂ ਹਟਾਓ",
        "pagelang-language": "ਭਾਸ਼ਾ",
        "pagelang-use-default": "ਮੂਲ ਭਾਸ਼ਾ ਵਰਤੋਂ",
        "pagelang-select-lang": "ਭਾਸ਼ਾ ਚੁਣੋ",
+       "pagelang-submit": "ਭੇਜੋ",
        "right-pagelang": "ਸਫ਼ੇ ਦੀ ਭਾਸ਼ਾ ਬਦਲੋ",
        "action-pagelang": "ਸਫ਼ੇ ਦੀ ਭਾਸ਼ਾ ਬਦਲੋ",
+       "mediastatistics": "ਮੀਡੀਆ ਅੰਕੜੇ",
+       "mediastatistics-table-mimetype": "MIME ਕਿਸਮ:",
        "mediastatistics-table-count": "ਫ਼ਾਈਲਾਂ ਦੀ ਗਿਣਤੀ",
        "mediastatistics-table-totalbytes": "ਮਿਲ਼ਵਾਂ ਅਕਾਰ",
        "mediastatistics-header-unknown": "ਅਣਪਛਾਤਾ",
        "mediastatistics-header-video": "ਵੀਡੀਓਆਂ",
        "mediastatistics-header-office": "ਦਫ਼ਤਰ",
        "mediastatistics-header-text": "ਲਿਖਤੀ",
+       "mediastatistics-header-total": "ਸਾਰੀਆੰ ਫਾਈਲਾੰ",
        "json-error-syntax": "ਵਾਕ-ਵਿਉਂਤ 'ਚ ਦੋਸ਼",
        "special-characters-group-latin": "ਲਾਤੀਨੀ",
        "special-characters-group-latinextended": "ਲਾਤੀਨੀ ਤੋਂ ਛੁੱਟ",
        "special-characters-title-endash": "ਅੰਗਰੇਜ਼ੀ ਡੈਸ਼",
        "special-characters-title-emdash": "em ਡੈਸ਼",
        "special-characters-title-minus": "ਘਟਾਓ ਦਾ ਨਿਸ਼ਾਨ",
+       "mw-widgets-dateinput-no-date": "ਕੋਈ ਮਿਤੀ ਨਹੀਂ ਚੁਣੀ",
+       "mw-widgets-titleinput-description-new-page": "ਸਫ਼ਾ ਹਜੇ ਮੌਜੂਦ ਨਹੀਂ ਹੈ",
+       "log-action-filter-all": "ਸਾਰੇ",
+       "log-action-filter-block-block": "ਪਾਬੰਦੀ",
+       "log-action-filter-block-unblock": "ਪਾਬੰਦੀ ਹਟਾਈ",
+       "authmanager-authplugin-setpass-bad-domain": "ਗਲਤ ਡੋਮੇਨ",
        "authmanager-password-help": "ਪ੍ਰਮਾਣਿਕਤਾ ਲਈ ਪਛਾਣ-ਸ਼ਬਦ।",
        "authmanager-email-label": "ਈਮੇਲ",
        "authmanager-email-help": "ਈਮੇਲ ਪਤਾ",
        "authmanager-realname-label": "ਅਸਲੀ ਨਾਂ",
+       "authmanager-realname-help": "ਇਸ ਉਪਭੋਗੀ ਦਾ ਅਸਲ ਨਾਮ",
+       "authmanager-provider-temporarypassword": "ਆਰਜ਼ੀ ਪਾਸਵਰਡ",
        "authprovider-resetpass-skip-label": "ਛੱਡ ਦਿਉ",
+       "authform-wrongtoken": "ਗਲਤ ਟੋਕਨ",
        "specialpage-securitylevel-not-allowed-title": "ਇਜਾਜ਼ਤ ਨਹੀਂ",
        "cannotauth-not-allowed-title": "ਪ੍ਰਵਾਨਗੀ ਨਹੀਂ ਮਿਲੀ",
        "credentialsform-account": "ਖਾਤੇ ਦਾ ਨਾਂ:"
index 05cd4f1..6cef6a0 100644 (file)
        "tagline": "Z {{GRAMMAR:D.lp|{{SITENAME}}}}",
        "help": "Pomoc",
        "search": "Szukaj",
+       "search-ignored-headings": " #<!-- ten wiersz zostaw bez zmian --> <pre>\n# Umieszczone tutaj nagłówki będą ignorowane podczas wyszukiwania.\n# Zmiany wprowadzone tutaj zostaną zastosowane, gdy strona wykorzystująca odpowiedni nagłówek zostanie zindeksowana.\n# Można wymusić ponowne zindeksowanie strony wykonując na niej null edit.\n# Składnia jest następująca:\n#   * Wszystko począwszy od znaku „#” aż do końca wiersza jest komentarzem.\n#   * Każdy niepusty wiersz wprowadza dokładną treść nagłówka, który należy zignorować, wielkość liter ma znaczenie.\nLinki zewnętrzne\nZobacz też\n #</pre> <!-- ten wiersz zostaw bez zmian -->",
        "searchbutton": "Szukaj",
        "go": "Przejdź",
        "searcharticle": "Przejdź",
        "title-invalid-too-long": "Podany tytuł strony jest zbyt długi. Nie może mieć więcej niż $1 {{PLURAL:$1|bajt|bajty|bajtów}} w kodowaniu UTF-8.",
        "title-invalid-leading-colon": "Podany tytuł strony zawiera na początku nieprawidłowy dwukropek.",
        "perfcached": "Poniższe dane są kopią z pamięci podręcznej i mogą być nieaktualne. W pamięci podręcznej {{PLURAL:$1|znajduje|znajdują|znajduje}} się maksymalnie {{PLURAL:$1|jeden wynik|$1 wyniki|$1 wyników}}.",
-       "perfcachedts": "Poniższe dane są kopią z pamięci podręcznej. Ostatnia aktualizacja odbyła się $1. W pamięci podręcznej {{PLURAL:$4|znajduje|znajdują|znajduje}} się maksymalnie {{PLURAL:$4|jeden wynik|$4 wyniki|$4 wyników}}.",
+       "perfcachedts": "Poniższe dane są kopią z pamięci podręcznej. Ostatnia aktualizacja odbyła się $1. W pamięci podręcznej {{PLURAL:$4|znajduje|znajdują|znajduje}} się maksymalnie {{PLURAL:$4|jeden wynik|$4 wyniki|$4 wyników}}.",
        "querypage-no-updates": "Uaktualnienia dla tej strony są obecnie wyłączone. Znajdujące się tutaj dane nie zostaną odświeżone.",
        "viewsource": "Tekst źródłowy",
        "viewsource-title": "Tekst źródłowy strony $1",
        "yourpasswordagain": "Powtórz hasło:",
        "createacct-yourpasswordagain": "Potwierdź hasło",
        "createacct-yourpasswordagain-ph": "Wprowadź hasło jeszcze raz",
-       "remembermypassword": "Zapamiętaj moje logowanie na tym komputerze (maksymalnie przez $1 {{PLURAL:$1|dzień|dni}})",
        "userlogin-remembermypassword": "Nie wylogowuj mnie",
        "userlogin-signwithsecure": "Użyj bezpiecznego połączenia",
+       "cannotlogin-title": "Nie można się zalogować",
+       "cannotlogin-text": "Logowanie nie jest możliwe.",
        "cannotloginnow-title": "W tej chwili nie można się teraz zalogować",
        "cannotloginnow-text": "Podczas korzystania z $1 nie można się zalogować.",
+       "cannotcreateaccount-title": "Nie można utworzyć kont",
        "yourdomainname": "Twoja domena:",
        "password-change-forbidden": "Nie można zmieniać haseł na tej wiki.",
        "externaldberror": "Wystąpił błąd autentyfikacyjnej bazy danych lub nie posiadasz uprawnień koniecznych do aktualizacji zewnętrznego konta.",
        "botpasswords-label-restrictions": "Ograniczenia użytkowania:",
        "botpasswords-label-grants-column": "Przyznane",
        "botpasswords-bad-appid": "Nazwa bota \"$1\" nie jest prawidłowa.",
+       "botpasswords-insert-failed": "Nie udało się dodać robota o nazwie \"$1\". Czy był już wcześniej dodany?",
+       "botpasswords-update-failed": "Nie udało się zmienić robota o nazwie \"$1\". Czy został usunięty?",
        "botpasswords-created-title": "Hasło bota stworzone",
        "botpasswords-created-body": "Hasło bota \"$1\" użytkownika \"$2\" zostało utworzone.",
        "botpasswords-updated-title": "Hasło bota zaktualizowane",
        "botpasswords-updated-body": "Hasło bota \"$1\" użytkownika \"$2\" zostało zaktualizowane.",
        "botpasswords-deleted-title": "Hasło bota usunięte",
        "botpasswords-deleted-body": "Hasło bota \"$1\" użytkownika \"$2\" zostało usunięte.",
+       "botpasswords-newpassword": "Nowe hasło do zalogowania przez <strong>$1</strong> to <strong>$2</strong>. <em>Proszę je zapisać w celu wykorzystania w przyszłości.</em>",
        "botpasswords-no-provider": "BotPasswordsSessionProvider nie jest dostępne.",
        "botpasswords-restriction-failed": "Logowanie nie powiodło się z powodu ograniczeń na hasło bota.",
+       "botpasswords-invalid-name": "Określona nazwa użytkownika nie zawiera separatora hasła bota (\"$1\").",
        "botpasswords-not-exist": "Użytkownik \"$1\" nie ma hasła dla bota o nazwie \"$2\".",
        "resetpass_forbidden": "Hasła nie mogą zostać zmienione",
        "resetpass_forbidden-reason": "Hasła nie mogą zostać zmienione: $1",
        "passwordreset-emailelement": "Nazwa użytkownika: \n$1\n\nTymczasowe hasło: \n$2",
        "passwordreset-emailsentemail": "Jeśli ten adres e‐mail jest przypisany do Twojego konta, zostanie na niego wysłany e-mail do odzyskiwania hasła.",
        "passwordreset-emailsentusername": "Jeśli z tym kontem powiązany jest adres e‐mail, zostanie na niego wysłany e-mail do odzyskiwania hasła.",
-       "passwordreset-emailsent-capture": "Wyświetlony poniżej e‐mail pozwalający na zresetowanie hasła został wysłany.",
-       "passwordreset-emailerror-capture": "Poniżej wyświetlony e‐mail pozwalający na zresetowanie hasła został wygenerowany, ale nie udało się wysłać go do {{GENDER:$2|użytkownika|użytkowniczki}}: $1",
+       "passwordreset-emailsent-capture2": "{{PLURAL:$1|Został wysłany e-mail|Zostały wysłane e-mail}} z informacjami o resetowaniu hasła. {{PLURAL:$1|Użytkownik i hasło jest pokazany|Lista użytkowników i haseł jest pokazana}} poniżej.",
+       "passwordreset-emailerror-capture2": "Wysyłanie e-maila do {{GENDER:$2|użytkownika|użytkowniczki}} nie powiodło się: $1 {{PLURAL:$3|Użytkownik i hasło jest pokazany|Lista użytkowników i haseł jest pokazana}} poniżej.",
+       "passwordreset-nocaller": "Musi być podany wywołujący",
+       "passwordreset-nosuchcaller": "Wywołujący nie istnieje: $1",
        "passwordreset-invalideamil": "Nieprawidłowy adres e-mail",
        "passwordreset-nodata": "Nie podano ani nazwy użytkownika, ani adresu e-mail",
        "changeemail": "Zmiana lub usunięcie adresu e‐mail",
        "changeemail-header": "Wypełnij ten formularz, aby zmienić swój adres e-mail. Jeśli chcesz usunąć swój adres e-mail, to przy wypełnianiu formularza pozostaw puste pole nowego adresu e-mail.",
-       "changeemail-passwordrequired": "Musisz podać swoje hasło, aby potwierdzić tę zmianę.",
        "changeemail-no-info": "Musisz być zalogowany, by uzyskać bezpośredni dostęp do tej strony.",
        "changeemail-oldemail": "Obecny adres e‐mail:",
        "changeemail-newemail": "Nowy adres e-mail:",
        "content-model-css": "CSS",
        "content-json-empty-object": "Pusty obiekt",
        "content-json-empty-array": "Pusta tablica",
+       "deprecated-self-close-category": "Strony zawierające nieprawidłowe samozamykające się znaczniki HTML",
+       "deprecated-self-close-category-desc": "Strona zawiera samozamykające się znaczniki HTML, takie jak <code>&lt;b/></code> lub <code>&lt;span/></code>. Ich zachowanie zmieni się na dostosowane do specyfikacji HTML5, więc ich użycie w wikikodzie jest zdeprecjonowane.",
        "duplicate-args-warning": "<strong>Ostrzeżenie:</strong> [[:$1]] wywołuje [[:$2]] z więcej niż jedną wartością dla parametru \"$3\". Tylko ostatnia podana wartość zostanie użyta.",
        "duplicate-args-category": "Strony zawierające wywołania szablonów z parametrami o takich samych nazwach",
        "duplicate-args-category-desc": "Strona zawiera szablony, które używają duplikatów argumentów, jak <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> lub <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Wygląda na to, że edycja została już anulowana.",
        "undo-summary": "Anulowanie wersji $1 autora [[Special:Contributions/$2|$2]] ([[User talk:$2|dyskusja]])",
        "undo-summary-username-hidden": "Anulowanie wersji $1 autorstwa ukrytego użytkownika",
-       "cantcreateaccounttitle": "Nie można utworzyć konta",
        "cantcreateaccount-text": "Tworzenie konta z tego adresu IP ('''$1''') zostało zablokowane przez [[User:$3|$3]].\n\nPodany przez $3 powód to ''$2''",
        "cantcreateaccount-range-text": "Tworzenie konta z adresów IP w zakresie <strong>$1</strong>, zawierającego twój adres IP (<strong>$4</strong>), zostało zablokowane przez [[User:$3|$3]].\n\nPodany przez $3 powód to <em>$2</em>",
        "viewpagelogs": "Zobacz rejestry operacji dla tej strony",
        "listgrants": "Uprawnienia",
        "listgrants-grant": "Uprawnienie",
        "listgrants-rights": "Uprawnienie",
-       "trackingcategories": "Śledzenie kategorii",
+       "trackingcategories": "Kategorie śledzące",
        "trackingcategories-summary": "Ta strona zawiera listę kategorii monitorujących wypełnianych automatycznie przez oprogramowanie MediaWiki. Nazwy kategorii można zmienić edytując odpowiednie komunikaty systemowe znajdujące się w przestrzeni nazw {{ns:8}}.",
        "trackingcategories-msg": "Śledzenie kategorii",
        "trackingcategories-name": "Nazwa komunikatu",
        "watchnologin": "Nie jesteś zalogowany",
        "addwatch": "Dodaj do listy obserwowanych",
        "addedwatchtext": "Strona „[[:$1|$1]]” wraz ze swoją stroną dyskusji została dodana do Twojej [[Special:Watchlist|listy obserwowanych]].",
+       "addedwatchtext-talk": "Strona „[[:$1]]” i strony z nią związane zostały dodane do Twojej [[Special:Watchlist|listy obserwowanych]].",
        "addedwatchtext-short": "Strona „$1” została dodana do twojej listy obserwowanych.",
        "removewatch": "Usuń z listy obserwowanych",
        "removedwatchtext": "Strona „[[:$1|$1]]” wraz ze swoją stroną dyskusji została usunięta z Twojej [[Special:Watchlist|listy obserwowanych]].",
+       "removedwatchtext-talk": "Strona „[[:$1]]” i strony z nią związane zostały usunięte z Twojej [[Special:Watchlist|listy obserwowanych]].",
        "removedwatchtext-short": "Strona „$1” została usunięta z twojej listy obserwowanych.",
        "watch": "Obserwuj",
        "watchthispage": "Obserwuj",
        "sp-contributions-newbies-sub": "Dla nowych użytkowników",
        "sp-contributions-newbies-title": "Wkład nowych użytkowników",
        "sp-contributions-blocklog": "blokady",
-       "sp-contributions-suppresslog": "utajniony wkład użytkownika",
-       "sp-contributions-deleted": "usunięty wkład użytkownika",
+       "sp-contributions-suppresslog": "utajniony wkład {{GENDER:$1|użytkownika|użytkowniczki}}",
+       "sp-contributions-deleted": "usunięty wkład {{GENDER:$1|użytkownika|użytkowniczki}}",
        "sp-contributions-uploads": "przesłane pliki",
        "sp-contributions-logs": "rejestry",
        "sp-contributions-talk": "dyskusja",
        "confirm-unwatch-button": "OK",
        "confirm-unwatch-top": "Usunąć tę stronę z listy obserwowanych?",
        "confirm-rollback-button": "OK",
+       "confirm-rollback-top": "Wycofać edycje tej strony?",
        "percent": "$1%",
        "quotation-marks": "„$1”",
        "imgmultipageprev": "← poprzednia strona",
        "size-megabytes": "$1&nbsp;MB",
        "size-gigabytes": "$1&nbsp;GB",
        "lag-warn-normal": "Zmiany nowsze niż $1 {{PLURAL:$1|sekunda|sekundy|sekund}} mogą nie być widoczne na tej liście.",
-       "lag-warn-high": "Z powodu dużego obciążenia serwerów bazy danych, zmiany nowsze niż $1 {{PLURAL:$1|sekunda|sekundy|sekund}} mogą nie być widoczne na tej liście.",
+       "lag-warn-high": "Z powodu dużego obciążenia serwerów bazy danych zmiany nowsze niż $1 {{PLURAL:$1|sekunda|sekundy|sekund}} mogą nie być widoczne na tej liście.",
        "watchlistedit-normal-title": "Edytuj listę obserwowanych stron",
        "watchlistedit-normal-legend": "Usuń strony z listy obserwowanych",
        "watchlistedit-normal-explain": "Poniżej znajduje się lista obserwowanych przez Ciebie stron.\nAby usunąć stronę z listy zaznacz znajdujące się obok niej pole i naciśnij „{{int:Watchlistedit-normal-submit}}”.\nMożesz także skorzystać z [[Special:EditWatchlist/raw|tekstowego edytora listy obserwowanych]].",
        "timezone-local": "Czas lokalny",
        "duplicate-defaultsort": "Uwaga: Domyślnym kluczem sortowania będzie „$2” i zastąpi on wcześniej wykorzystywany klucz „$1”.",
        "duplicate-displaytitle": "<strong>Uwaga:</strong> Wyświetlenie tytułu „$2” powoduje nadpisanie wcześniej wyświetlanego tytułu „$1”.",
+       "restricted-displaytitle": "<strong>Ostrzeżenie:</strong> Tytuł do wyświetlania \"$1\" został zignorowany, ponieważ nie jest odpowiednikiem aktualnego tytułu tej strony.",
        "invalid-indicator-name": "<strong>Błąd:</strong> Atrybut stanu strony <code>name</code> nie może być pusty.",
        "version": "Wersja oprogramowania",
        "version-extensions": "Zainstalowane rozszerzenia",
        "tags-deactivate": "dezaktywuj",
        "tags-hitcount": "$1 {{PLURAL:$1|zmiana|zmiany|zmian}}",
        "tags-manage-no-permission": "Nie masz uprawnień do zarządzania znacznikami.",
+       "tags-manage-blocked": "Nie możesz zarządzać znacznikami zmian, kiedy jesteś zablokowany.",
        "tags-create-heading": "Utwórz nowy znacznik",
        "tags-create-explanation": "Nowe znaczniki będą dostępne domyślnie dla użytkowników i botów.",
        "tags-create-tag-name": "Nazwa znacznika:",
        "tags-delete-not-found": "Znacznik „$1” nie istnieje.",
        "tags-delete-too-many-uses": "Znacznik „$1” jest stosowany w więcej niż {{PLURAL:$2|jednej wersji|$2 wersjach}}, co oznacza, że nie może być usunięty.",
        "tags-delete-warnings-after-delete": "Znacznik „$1” został usunięty, ale {{PLURAL:$2|otrzymano następujące ostrzeżenie|otrzymano następujące ostrzeżenia}}:",
+       "tags-delete-no-permission": "Nie masz uprawnień do usuwania znaczników zmian.",
        "tags-activate-title": "Aktywacja znacznika",
        "tags-activate-question": "Zamierzasz aktywować znacznik „$1”.",
        "tags-activate-reason": "Powód:",
        "tags-deactivate-not-allowed": "Nie można dezaktywować znacznika „$1”.",
        "tags-deactivate-submit": "Dezaktywuj",
        "tags-apply-no-permission": "Nie masz uprawnień do wprowadzania znaczników wraz z własnymi zmianami.",
+       "tags-apply-blocked": "Nie możesz stosować znaczników do swoich zmian, gdy jesteś zablokowany.",
        "tags-apply-not-allowed-one": "Znacznik „$1” nie może zostać wprowadzony ręcznie.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|Następujący znacznik nie może zostać wprowadzony ręcznie|Następujące znaczniki nie mogą zostać wprowadzone ręcznie}}: $1",
        "tags-update-no-permission": "Nie masz uprawnień do dodawania lub usuwania znaczników z poszczególnych wersji lub wpisów w rejestrze.",
+       "tags-update-blocked": "Nie możesz dodawać i usuwać znaczników zmian, kiedy jesteś zablokowany.",
        "tags-update-add-not-allowed-one": "Znacznik „$1” nie może zostać dodany ręcznie.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|Następujący znacznik nie może zostać dodany ręcznie|Następujące znaczniki nie mogą zostać dodane ręcznie}}: $1",
        "tags-update-remove-not-allowed-one": "Znacznika „$1” nie można usunąć.",
        "api-error-nomodule": "Błąd wewnętrzny – nie określono modułu przesyłania plików.",
        "api-error-ok-but-empty": "Błąd wewnętrzny – brak odpowiedzi od serwera.",
        "api-error-overwrite": "Nadpisanie istniejącego pliku nie jest dopuszczalne.",
+       "api-error-ratelimited": "Próbujesz przesłać więcej plików w krótszym odstępie czasowym, niż ta wiki na to pozwala.\nSpróbuj jeszcze raz za kilka minut.",
        "api-error-stashfailed": "Błąd wewnętrzny – serwer nie mógł zapisać pliku tymczasowego.",
        "api-error-publishfailed": "Błąd wewnętrzny: serwer nie mógł zapisać pliku tymczasowego.",
        "api-error-stasherror": "Wystąpił błąd podczas przesyłania pliku.",
        "mw-widgets-dateinput-placeholder-month": "RRRR-MM",
        "mw-widgets-titleinput-description-new-page": "strona jeszcze nie istnieje",
        "mw-widgets-titleinput-description-redirect": "przekierowanie do $1",
-       "api-error-blacklisted": "Wybierz inny, opisowy tytuł.",
        "sessionmanager-tie": "Nie można łączyć kilku rodzajów uwierzytelniania dla zapytania: $1.",
+       "sessionprovider-generic": "sesje $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sesje na podstawie cookie",
        "sessionprovider-nocookies": "Być może cookies są wyłączone. Upewnij się, że masz włączone cookies i zacznij od nowa.",
        "randomrootpage": "Losowa strona (bez podstron)",
        "log-action-filter-newusers": "Typ utworzenia konta:",
        "log-action-filter-patrol": "Rodzaj patrolu:",
        "log-action-filter-protect": "Rodzaj zabezpieczenia:",
-       "log-action-filter-rights": "Typ zmiany uprawień",
-       "log-action-filter-suppress": "Typ ukrycia",
+       "log-action-filter-rights": "Typ zmiany uprawień:",
+       "log-action-filter-suppress": "Typ ukrycia:",
        "log-action-filter-upload": "Rodzaj przesłanych:",
        "log-action-filter-all": "Wszystkie",
        "log-action-filter-block-block": "Zablokowanie",
        "authpage-cannot-create-continue": "Nie można kontynuować tworzenia konta. Twoja sesja najprawdopodobniej wygasła.",
        "cannotauth-not-allowed-title": "Brak dostępu",
        "cannotauth-not-allowed": "Nie masz uprawnień, aby skorzystać z tej strony",
-       "changecredentials-submit": "Zmień poświadczenia",
-       "removecredentials-submit": "Usuń poświadczenia",
+       "changecredentials": "Zmiana poświadczeń",
+       "changecredentials-submit": "Zmień poświadczenie",
+       "removecredentials": "Usuwanie poświadczeń",
+       "removecredentials-submit": "Usuń poświadczenie",
+       "credentialsform-provider": "Rodzaj poświadczeń:",
        "credentialsform-account": "Nazwa konta:",
        "linkaccounts": "Połącz konta",
        "linkaccounts-success-text": "Konto zostało połączone.",
index c0018be..e0b4667 100644 (file)
        "tagline": "Da {{SITENAME}}.",
        "help": "Agiut",
        "search": "Sërché",
+       "search-ignored-headings": " #<!-- lassé costa linia 'me ch'a l'é --> <pre>\n# Antestassion ch'a saran ignorà da l'arserca.\n# Le modìfiche a sòn a l'han efet pen-a che la pàgina con l'antestassion a l'é indicisà.\n# A peul forsé la riandesassion ëd la pàgina an fasend na modìfica veuida.\n# La sintassi a l'é costa-sì:\n#   * Tut a parte da 'n caràter «#» a l'é un coment.\n#   * Tuta linia nen veuida a l'é ël tìtol precis da ignoré, caràter majùscoj o minùscoj comprèis.\nArferiment\nLiure esterne\nVëdde ëdcò\n #</pre> <!-- lassé costa linia 'me ch'a l'é -->",
        "searchbutton": "Sërché",
        "go": "Andé",
        "searcharticle": "Andé",
        "passwordreset-emailtext-user": "L'utent $1 ansima a {{SITENAME}} a l'ha ciamà na riampostassion ëd soa ciav për {{SITENAME}} ($4). {{PLURAL:$3|Ël cont utent sì-sota a l'é|Ij cont utent sì-sota a son}} associà a st'adrëssa ëd pòsta eletrònica:\n\n$2\n\n{{PLURAL:$3|Costa ciav provisòria|Coste ciav provisòrie}} a scadran da-sì {{PLURAL:$5|un di|$5 di}}.\nA dovrìa intré ant ël sistema e serne na ciav neuva adess. Se quaidun d'àutr a l'ha fàit costa arcesta, o s'a l'é arcordasse soa ciav original, e a veul pa pi cangela, a peul ignoré ës mëssagi e continué a dovré soa veja ciav.",
        "passwordreset-emailelement": "Stranòm: \n$1\n\nCiav provisòria: \n$2",
        "passwordreset-emailsentemail": "Un mëssagi ëd riampostassion ëd la ciav a l'é stàit spedì.",
-       "passwordreset-emailsent-capture": "Un mëssagi ëd riampostassion ëd la ciav a l'é stàit mandà, e a l'é mostrà sì-sota.",
-       "passwordreset-emailerror-capture": "Un mëssagi ëd riampostassion ëd la ciav a l'é stàit generà, e a l'é smonù sì-sota, ma la spedission a {{GENDER:$2|l'utent}} a l'é falìa: $1",
        "changeemail": "Cangé l'adrëssa ëd pòsta eletrònica",
        "changeemail-header": "Cangé l'adrëssa ëd pòsta eletrònica dël cont",
        "changeemail-no-info": "A dev esse intrà ant ël sistema për andé diretament a costa pàgina.",
        "undo-nochange": "A smija che la modìfica a sia già stàita anulà.",
        "undo-summary": "Gavà la revision $1 fàita da [[Special:Contributions/$2|$2]] ([[User talk:$2|Ciaciarade]])",
        "undo-summary-username-hidden": "Anulé la revision $1 ëd n'utent ëstërmà",
-       "cantcreateaccounttitle": "As peul pa registresse d'utent",
        "cantcreateaccount-text": "La cression ëd cont neuv a parte da st'adrëssa IP-sì ('''$1''') a l'é stàita blocà da [[User:$3|$3]].\n\nLa rason butà da $3 për ël blocagi a l'é stàita: ''$2''",
        "cantcreateaccount-range-text": "La creassion ëd cont da l'adrëssa IP ant l'antërval <strong>$1</strong>, ch'a comprend soa adrëssa IP (<strong>$4</strong>), a l'é stàita blocà da [[User:$3|$3]].\n\nLa rason dàita da $3 a l'é <em>$2</em>",
        "viewpagelogs": "Smon ij registr dë sta pàgina-sì",
        "special-characters-title-minus": "segn meno",
        "mw-widgets-dateinput-placeholder-day": "AAAA-MM-DD",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
-       "api-error-blacklisted": "Për piasì sern un tìtol diferent, descritiv.",
        "randomrootpage": "Pàgina root a cas"
 }
index f40ed82..bff6d79 100644 (file)
        "yourpasswordagain": "پټنوم بيا وليکه",
        "createacct-yourpasswordagain": "پټنوم مو تاييد کړۍ",
        "createacct-yourpasswordagain-ph": "پټنوم مو بيا وټاپئ",
-       "remembermypassword": "زما پټنوم په دې کمپيوټر (تر $1 {{PLURAL:$1|ورځې|ورځو}}) په ياد وساته!",
        "userlogin-remembermypassword": "غونډال کې مې ننوتلی وساته",
        "userlogin-signwithsecure": "خوندي اړيکتيا کارول",
        "yourdomainname": "ستاسې شپول:",
        "passwordreset-emailtitle": "د {{SITENAME}} د گڼون څرگندنې",
        "passwordreset-emailelement": "کارن-نوم: \n$1\n\nلنډمهاله پټنوم: \n$2",
        "passwordreset-emailsentemail": "د پټنوم بيا پرځای کېدنې لپاره برېښليک درولېږل شو.",
-       "passwordreset-emailsent-capture": "د پټنوم بياپرځای کېدنې لپار مو يو برېښليک درولېږه، برېښليک په لاندې توگه ښودل شوی.",
        "passwordreset-invalideamil": "ناسمه برېښليک پته",
        "changeemail": "برېښليک پته بدلول يا ليرې کول",
        "changeemail-header": "د گڼون برېښليک پته بدلول",
        "post-expand-template-argument-warning": "'''گواښنه:''' دا مخ لږ تر لږه د يوې کينډۍ عاملين لري چې بې حده لوی دی.\nدا عاملين ړنگ شول.",
        "post-expand-template-argument-category": "هغه مخونه چې د کينډۍ ړنگ شوي عاملين لري.",
        "undo-norev": "دا سمون ناکړل کېدای نه شي دا ځکه چې دا سمون نشته او يا هم ړنگ شوی.",
-       "cantcreateaccounttitle": "گڼون نه شي جوړېدای",
        "viewpagelogs": "د دې مخ يادښتونه کتل",
        "nohistory": "ددې مخ د سمون کوم پېښليک نه شته.",
        "currentrev": "اوسنۍ بڼه",
        "pageinfo-article-id": "د مخ پېژند",
        "pageinfo-language": "د مخ د مېنځپانگې ژبه",
        "pageinfo-content-model": "د مخ مېنځپانگې جوړښت",
+       "pageinfo-content-model-change": "بدلول",
        "pageinfo-robot-policy": "ليکلړ اوډنه د روباټونو لخوا",
        "pageinfo-robot-index": "پرېښل",
        "pageinfo-robot-noindex": "ناپرېښل",
index 034efb7..ae189ec 100644 (file)
@@ -98,7 +98,8 @@
                        "Luan",
                        "Anderson Costa",
                        "LucyDiniz",
-                       "Tusca"
+                       "Tusca",
+                       "Cristofer Alves"
                ]
        },
        "tog-underline": "Sublinhar links:",
        "tagline": "De {{SITENAME}}",
        "help": "Ajuda",
        "search": "Pesquisa",
+       "search-ignored-headings": " #<!-- deixe esta linha exatamente como está --> <pre>\n# Subtítulos que serão ignorados pela busca.\n# Mudanças feitas aqui têm efeito quando a página com o subtítulo é indexada.\n# Você pode forçar a reindexação realizando uma edição nula.\n# A sintaxe é a seguinte:\n#   * Tudo a partir do caractere \"#\" até o final da linha é um comentário\n#   * Cada linha não vazia é o título exato a ser ignorado, inclusive no uso de maiúsculas\nReferências\nLigações externas\nVer também\n #</pre> <!-- deixe esta linha exatamente como está -->",
        "searchbutton": "Pesquisar",
        "go": "Ir",
        "searcharticle": "Ir",
        "databaseerror-query": "Consulta: $1",
        "databaseerror-function": "Função: $1",
        "databaseerror-error": "Erro: $1",
-       "transaction-duration-limit-exceeded": "Para evitar a criação de lag replicação alta, esta operação foi abortada porque a duração de gravação ($1) excedeu o $2 {{PLURAL:$2|second|seconds}} limite.Se você está mudando muitos itens de uma vez, tente fazer várias operações menores em vez.",
+       "transaction-duration-limit-exceeded": "Para evitar a criação de lag replicação alta, esta operação foi abortada porque a duração de gravação ($1) excedeu o limite de $2 {{PLURAL:$2|segundo|segundos}}. Se você está mudando muitos itens de uma vez, tente fazer várias operações menores em vez disso.",
        "laggedslavemode": "Aviso: a página poderá não conter atualizações recentes.",
        "readonly": "Banco de dados disponível no modo \"somente leitura\"",
        "enterlockreason": "Entre com um motivo para trancá-lo, incluindo uma estimativa de quando poderá novamente ser destrancado",
        "yourpasswordagain": "Redigite sua senha",
        "createacct-yourpasswordagain": "Confirmar senha",
        "createacct-yourpasswordagain-ph": "Digite a senha novamente",
-       "remembermypassword": "Lembrar meu login neste navegador (por no máximo $1 {{PLURAL:$1|dia|dias}})",
        "userlogin-remembermypassword": "Mantenha-me conectado",
        "userlogin-signwithsecure": "Use a conexão segura",
        "cannotloginnow-title": "Não é possível iniciar a sessão agora",
        "password-change-forbidden": "Você não pode alterar senhas nessa wiki.",
        "externaldberror": "Ocorreu ou um erro no banco de dados durante a autenticação ou não lhe é permitido atualizar a sua conta externa.",
        "login": "Autenticar-se",
+       "login-security": "Verificar sua identidade",
        "nav-login-createaccount": "Entrar / criar conta",
        "userlogin": "Entrar / criar conta",
        "userloginnocreate": "Entrar",
        "userlogin-resetpassword-link": "Esqueceu sua senha?",
        "userlogin-helplink2": "Ajuda com o login",
        "userlogin-loggedin": "Você já está conectado como {{GENDER:$1|$1}}.\nUse o formulário abaixo para iniciar sessão como outro usuário.",
+       "userlogin-reauth": "Deve iniciar novamente a sessão para verificar se é {{GENDER:$1|$1}}.",
        "userlogin-createanother": "Crie uma outra conta",
        "createacct-emailrequired": "Endereço de e-mail",
        "createacct-emailoptional": "Endereço de e-mail (opcional)",
        "createacct-email-ph": "Confirme seu endereço de e-mail",
        "createacct-another-email-ph": "Forneça o endereço de e-mail",
        "createaccountmail": "Usar uma senha aleatória e temporária que será enviada ao endereço de e-mail especificado a seguir",
+       "createaccountmail-help": "Pode ser utilizado para criar uma conta para outra pessoa sem saber a senha.",
        "createacct-realname": "Nome real (opcional)",
        "createaccountreason": "Motivo:",
        "createacct-reason": "Motivo",
        "createacct-reason-ph": "Por que você está criando outra conta",
+       "createacct-reason-help": "Mensagem mostrada no registro de criação de conta",
        "createacct-submit": "Crie sua conta",
        "createacct-another-submit": "Criar conta",
+       "createacct-continue-submit": "Continuar criação de conta",
+       "createacct-another-continue-submit": "Continuar criação de conta",
        "createacct-benefit-heading": "{{SITENAME}} é feita por pessoas como você.",
        "createacct-benefit-body1": "{{PLURAL:$1|edição|edições}}",
        "createacct-benefit-body2": "{{PLURAL:$1|página|páginas}}",
        "nocookiesnew": "A conta do usuário foi criada, mas você não foi autenticado.\n{{SITENAME}} utiliza ''cookies'' para autenticar os usuários.\nVocê tem os ''cookies'' desativados no seu navegador.\nPor favor ative-os, depois autentique-se com o seu novo nome de usuário e a sua senha.",
        "nocookieslogin": "Você tem os <i>cookies</i> desativados no seu navegador, e a {{SITENAME}} utiliza <i>cookies</i> para ligar os usuários às suas contas. Por favor os ative e tente novamente.",
        "nocookiesfornew": "A conta de usuário não foi criada porque não foi possível confirmar a sua origem.\nCertifique-se de que tem os cookies ativados, recarregue esta página e tente novamente.",
+       "createacct-loginerror": "A conta foi criada com êxito, mas não pôde ser autenticado automaticamente. Por favor, faça o [[Special:UserLogin|início de sessão manualmente]].",
        "noname": "Você não colocou um nome de usuário válido.",
        "loginsuccesstitle": "Autenticado",
        "loginsuccess": "'''Agora você está {{GENDER:autenticado|autenticada}} ao wiki {{SITENAME}} como \"$1\"'''.",
        "botpasswords-label-delete": "Apagar",
        "botpasswords-label-resetpassword": "Redefinir a sua senha",
        "botpasswords-label-grants": "Permissões aplicáveis",
+       "botpasswords-help-grants": "Cada permissão da acesso à lista permissões de usuários que um usuário já tenha. Veja o [[Special:ListGrants|Lista de Permissões]] para mais informações.",
        "botpasswords-label-restrictions": "Restrições de uso:",
        "botpasswords-label-grants-column": "Concedido",
        "botpasswords-bad-appid": "O nome de robô \"$1\" não é válido.",
        "passwordreset-emailelement": "Usuário: \n$1\n\nSenha temporária: \n$2",
        "passwordreset-emailsentemail": "Se este é um endereço de e-mail registrado para a sua conta, em seguida, um e-mail de redefinição de senha será enviada.",
        "passwordreset-emailsentusername": "Se houver um endereço de email associado a esta conta, ser-lhe-á enviada uma mensagem para redefinir a sua senha.",
-       "passwordreset-emailsent-capture": "Foi enviado um e-mail de lembrete, que é mostrado abaixo.",
-       "passwordreset-emailerror-capture": "Foi gerado um e-mail de recuperação da senha, conforme mostrado abaixo, mas o envio {{GENDER:$2|ao usuário|à usuária}} falhou. $1",
        "passwordreset-emailsent-capture2": "A redefinição da senha {{PLURAL:$1|do e-mail|dos e-mails}} foi enviada. {{PLURAL:$1|O nome de usuário e senha|A lista de nomes de usuário e senhas}} encontram-se a seguir.",
+       "passwordreset-emailerror-capture2": "O envio do e-mail {{GENDER:$2|usuário}} falhou: $1 Os {{PLURAL:$3|nome de usuário e senha|lista de nomes de usuários e senhas}} são mostrados abaixo.",
+       "passwordreset-nocaller": "Um interlocutor deve ser fornecido",
+       "passwordreset-nosuchcaller": "O interlocutor não existe: $1",
+       "passwordreset-ignored": "A redefinição da senha não foi realizada. Talvez o provedor não tenha sido configurado?",
+       "passwordreset-invalideamil": "Endereço de e-mail inválido",
+       "passwordreset-nodata": "Não foram fornecidos nome de usuário nem endereço de e-mail",
        "changeemail": "Alterar ou remover endereço de email",
        "changeemail-header": "Preencha este formulário para alterar seu endereço de e-mail. Se você gostaria de remover a associação de qualquer endereço de e-mail da sua conta, deixe o novo endereço de email em branco quando enviar o formulário.",
-       "changeemail-passwordrequired": "Você terá que digitar sua senha para confirmar esta mudança.",
        "changeemail-no-info": "Para acessar diretamente esta página você tem de estar autenticado.",
        "changeemail-oldemail": "Endereço de e-mail atual:",
        "changeemail-newemail": "Novo endereço de e-mail:",
        "minoredit": "Marcar como edição menor",
        "watchthis": "Vigiar esta página",
        "savearticle": "Salvar página",
+       "savechanges": "Salvar alterações",
+       "publishpage": "Publicar página",
+       "publishchanges": "Publicar alterações",
        "preview": "Pré-visualização",
        "showpreview": "Mostrar previsão",
        "showdiff": "Mostrar alterações",
        "accmailtext": "Uma senha gerada aleatoriamente para [[User talk:$1|$1]] foi enviada para $2.\n\nEla pode ser alterada na página ''[[Special:ChangePassword|de troca de senha]]'', após o início de sessão.",
        "newarticle": "(Nova)",
        "newarticletext": "Você seguiu um link para uma página que ainda não existe.\nPara criá-la, comece escrevendo na caixa abaixo (veja [$1 a página de ajuda] para mais informações).\nSe você chegou aqui por engano, clique no botão '''voltar''' do seu navegador.",
-       "anontalkpagetext": "---- ''Esta é a página de discussão para um usuário anônimo que ainda não criou uma conta ou que não a usa, de forma que temos de utilizar o endereço de IP para identificá-lo(a). Tal endereço de IP pode ser compartilhado por vários usuários. Se você é um usuário anônimo e acha que comentários irrelevantes foram direcionados a você, por gentileza, [[Special:CreateAccount|crie uma conta]] ou [[Special:UserLogin|autentique-se]], a fim de evitar futuras confusões com outros usuários anônimos.''",
+       "anontalkpagetext": "---\n<em> Esta é a pagina de discussão para usuários anônimos que ainda não ciaram uma conta, ou para aqueles que a usa. </em>\nNós entretanto temos que usar o endereço numérico de IP para identifica-lo/a.\nEste endereço de IP pode ser compartilhado por vários usuários.\nse você é um usuário anônimo e sente que aquele comentário irrelevante foi direcionado à você, por favor [[Special:CreateAccount|Criar Conta]] ou [[Special:UserLogin|Logar]] para evitar futuras confusões com outros usuários anônimos.",
        "noarticletext": "Não há conteúdo nesta página no momento.\nVocê pode [[Special:Search/{{PAGENAME}}|pesquisar pelo título desta página]] em outras páginas, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} buscar por registros relacionados],\nou [{{fullurl:{{FULLPAGENAME}}|action=edit}} criar esta página]</span>.",
        "noarticletext-nopermission": "No momento, não há conteúdo nesta página\nVocê pode [[Special:Search/{{PAGENAME}}|pesquisar pelo título desta página]] em outras páginas,\nou <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} buscar por registros relacionados] </span>. Note que, no entanto, você não tem permissão para criar esta página.",
        "missing-revision": "A revisão #$1 da página denominada \"{{FULLPAGENAME}}\" não existe.\n\nIsto é geralmente causado por seguir um link de histórico desatualizado para uma página que foi eliminada.\nOs detalhes podem ser encontrados no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de eliminação].",
        "userpage-userdoesnotexist": "A conta \"<nowiki>$1</nowiki>\" não se encontra registrada.\nVerifique se deseja mesmo criar/editar esta página.",
        "userpage-userdoesnotexist-view": "A conta de usuário \"$1\" não está registrada.",
        "blocked-notice-logextract": "Este usuário está atualmente bloqueado.\nO registro de bloqueio mais recente é fornecido abaixo, para referência:",
-       "clearyourcache": "Nota:''' Depois de salvar, você terá de limpar o ''cache'' do seu navegador para ver as alterações.\n* '''Firefox / Safari:''' pressione ''Shift'' enquanto clica em ''Recarregar'', ou pressione ''Ctrl-F5'' ou ''Ctrl-R'' (''Command-R'' para Mac);\n* '''Google Chrome:''' pressione ''Ctrl-Shift-R'' (''Command-Shift-R'' em um Mac)\n* '''Internet Explorer:''' pressione ''Ctrl'' enquanto clica em ''Recarregar'' ou pressione ''Ctrl-F5'';\n* '''Opera:''' limpe o ''cache'' em ''Ferramentas → Preferências'' (''Tools → Preferences'')",
+       "clearyourcache": "<strong>Nota:</strong> Após salvar, você pode ter que limpar o \"cache\" do seu navegador para ver as alterações.\n*<strong>Firefox / Safari:</strong> Pressione <em>Shift</em> enquanto clica <em>Recarregar</em>, ou pressione <em>Ctrl-F5</em> ou <em>Ctrl-R</em> (<em>⌘-R</em> no Mac)\n*<strong>Google Chorme:</strong> Pressione <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> no Mac)\n* <strong>Internet Explorer:</strong> Pressione<em>Ctrl</em> enquanto clica <em>Recarregar</em>, ou Pressione <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Vá para <em>Menu → Configurações</em> (<em>Opera → Preferencias</em> no Mac) e depois para <em>Privacidade e Segurança → Limpar dados de navegação → Imagens e arquivos em cache</em>.",
        "usercssyoucanpreview": "'''Dica:''' Utilize o botão \"{{int:showpreview}}\" para testar seu novo CSS antes de salvar.",
        "userjsyoucanpreview": "'''Dica:''' Utilize o botão \"{{int:showpreview}}\" para testar seu novo JavaScript antes de salvar.",
        "usercsspreview": "'''Lembre-se de que você está apenas previsualizando o seu CSS particular.'''\n'''Ele ainda não foi salvo!'''",
        "content-model-css": "CSS",
        "content-json-empty-object": "Objeto vazio",
        "content-json-empty-array": "Array vazia",
+       "deprecated-self-close-category": "Páginas com etiquetas HTML de autofechamento não válidas",
+       "deprecated-self-close-category-desc": "A página contém tags HTML auto-fechadas inválidas, como <code>&lt;b/></code> ou <code>&lt;span/></code>. O comportamento destas mudará em breve para coincidam com as especificações do HTML5, pelo que seu uso no wikitext está obsoleto.",
        "duplicate-args-warning": "<strong> Aviso: </strong> [[:$1]] está chamando [[:$2]] com mais de um valor para o parâmetro \"$3\". Será utilizado apenas o último valor fornecido.",
        "duplicate-args-category": "Páginas com argumentos de predefinições duplicados",
        "duplicate-args-category-desc": "A pagina contem modelos que usam argumentos duplicados, como <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> ou <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Parece que a edição já foi desfeita.",
        "undo-summary": "Desfeita a edição $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|Discussão]])",
        "undo-summary-username-hidden": "Desfazer a revisão $1 de um usuário oculto",
-       "cantcreateaccounttitle": "Não é possível criar uma conta",
        "cantcreateaccount-text": "Este IP ('''$1''') foi bloqueado de criar novas contas por [[User:$3|$3]].\n\nA justificativa apresentada por $3 foi ''$2''",
        "cantcreateaccount-range-text": "A criação de conta a partir dos endereços IP no intervalo <strong>$1</strong>, que inclui o seu endereço IP (<strong>$4</strong>), foi bloqueada por [[User:$3|$3]].\n\nA razão dada por $3 é <em>$2</em>",
        "viewpagelogs": "Ver registros para esta página",
        "grant-group-high-volume": "Realizar grande volume de atividades",
        "grant-group-customization": "Personalização e preferências",
        "grant-group-administration": "Realizar ações administrativas",
+       "grant-group-private-information": "acessar os dados privados sobre você",
        "grant-group-other": "Atividade diversa",
        "grant-blockusers": "Bloquear e desbloquear usuários",
        "grant-createaccount": "Criar contas",
        "grant-highvolume": "Edição de grandes volumes",
        "grant-oversight": "Ocultar usuários e revisões suprimidas",
        "grant-patrol": "Patrulhar as alterações nas páginas",
+       "grant-privateinfo": "acessar informações privadas",
        "grant-protect": "Proteger e desproteger páginas",
        "grant-rollback": "Reverter alterações nas páginas",
        "grant-sendemail": "Enviar e-mail a outros usuários",
        "rightslogtext": "Este é um registro de mudanças nos privilégios de usuários.",
        "action-read": "ler esta página",
        "action-edit": "editar esta página",
-       "action-createpage": "criar páginas",
-       "action-createtalk": "criar páginas de discussão",
+       "action-createpage": "criar esta páginas",
+       "action-createtalk": "criar esta páginas de discussão",
        "action-createaccount": "criar esta conta de usuário",
        "action-autocreateaccount": "Criar uma conta de usuário externa automaticamente",
        "action-history": "Ver o histórico desta página",
        "action-applychangetags": "aplicar etiquetas juntamente com suas alterações",
        "action-changetags": "adicionar e remover etiquetas arbitrárias em revisões e ''logs'' individuais",
        "action-deletechangetags": "deletar marcações da base de dados",
+       "action-purge": "purgar esta página",
        "nchanges": "$1 {{PLURAL:$1|alteração|alterações}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|desde a última visita}}",
        "enhancedrc-history": "histórico",
        "uploaded-event-handler-on-svg": "Não é permitido configurar atributos que manipulem eventos  <code>$1=\"$2\"</code> em arquivos SVG.",
        "uploaded-href-attribute-svg": "os atributos href nos ficheiros SVG só están autorizados a ligar a direccións http:// ou https://, atopado <code>&lt;$1 $2=\"$3\"&gt;</code>.",
        "uploaded-href-unsafe-target-svg": "Encontrado href para dados não seguros: alvo URI <code>&lt;$1 $2=\"$3\"&gt;</code> no arquivo SVG carregado.",
+       "uploaded-animate-svg": "Encontrado a tag \"animate\" que pode estar mudando \"href\", usando o atributo \"from\" <code>&lt;$1 $2=\"$3\"&gt;</code> no arquivo SVG carregado.",
        "uploadscriptednamespace": "Este aruivo SVG contém um espaço nominal probido \"$1\"",
        "uploadinvalidxml": "O XML no arquivo enviado não pôde ser analisado.",
        "uploadvirus": "O arquivo contém vírus!\nDetalhes: $1",
        "trackingcategories-msg": "Categoria de monitoramento",
        "trackingcategories-name": "Nome da mensagem",
        "trackingcategories-desc": "Critérios de inclusão de categoria",
+       "restricted-displaytitle-ignored": "Páginas com títulos de exibição ignorados",
+       "restricted-displaytitle-ignored-desc": "Esta página tem um <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> ignorado porque não é equivalente ao título verdadeiro da página.",
        "noindex-category-desc": "A página não é indexada por robôs, porque possui a palavra mágica <code><nowiki>__NOINDEX__</nowiki></code> e está em um namespace onde a flag é permitida.",
        "index-category-desc": "A página contém a palavra mágica <code><nowiki>__INDEX__</nowiki></code> (e está num domínio em que essa marca é permitida) e, portanto, será indexada pelos robôs mesmo quando normalmente não o seria.",
        "post-expand-template-inclusion-category-desc": "O tamanho da página é superior a <code>$wgMaxArticleSize</code>, após a expansão de todas as predefinições, pelo que algumas predefinições não foram expandidas.",
        "changecontentmodel-reason-label": "Motivo:",
        "changecontentmodel-success-title": "O modelo de conteúdo foi alterado",
        "changecontentmodel-success-text": "O tipo de conteúdo de [[:$1]] foi alterado.",
-       "log-name-contentmodel": "Log de alterações modelo de conteúdo",
+       "log-name-contentmodel": "Log de alterações do modelo de conteúdo",
        "logentry-contentmodel-change-revertlink": "reverter",
        "logentry-contentmodel-change-revert": "reverter",
        "protectlogpage": "Registro de proteção",
        "feedback-thanks-title": "Obrigado!",
        "feedback-useragent": "Agente de usuário:",
        "searchsuggest-search": "Pesquisa",
-       "searchsuggest-containing": "contendo...",
+       "searchsuggest-containing": "páginas contendo…",
        "api-error-badaccess-groups": "Você não tem permissão para enviar arquivos para este wiki.",
        "api-error-badtoken": "Erro interno: token inválido.",
        "api-error-copyuploaddisabled": "O upload por URL está desativado neste servidor.",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
        "mw-widgets-titleinput-description-new-page": "a página ainda não existe",
        "mw-widgets-titleinput-description-redirect": "redirecionar para $1",
-       "api-error-blacklisted": "Por favor, escolha  um título descritivo diferente.",
        "randomrootpage": "Página raiz aleatória"
 }
index 90829e5..21334a4 100644 (file)
        "tagline": "De {{SITENAME}}",
        "help": "Ajuda",
        "search": "Pesquisa",
+       "search-ignored-headings": " #<!-- deixe esta linha exatamente como ela está --> <pre>\n# Títulos de página que serão ignorados pela pesquisa.\n# Mudanças a esta lista terão efeito quando a página com o título referido for indexada.\n# Pode forçar a indexação de uma página realizando uma edição nula nessa página.\n# A sintaxe é a seguinte:\n#  * Tudo desde um símbolo de cardinal (#) até ao fim da linha é um comentário\n#  * Cada linha não vazia é o título exato a ignorar, respeitando o uso de maiúsculas\nReferências\nLinks externos\nVer também\n #</pre> <!-- deixe esta linha exatamente como ela está -->",
        "searchbutton": "Pesquisar",
        "go": "Ir",
        "searcharticle": "Ir",
        "databaseerror-query": "Consulta: $1",
        "databaseerror-function": "Função: $1",
        "databaseerror-error": "Erro: $1",
+       "transaction-duration-limit-exceeded": "Para evitar a criação de lag replicação alta, esta operação foi abortada porque a duração de gravação ($1) excedeu o limite de $2 {{PLURAL:$2|segundo|segundos}}. Se está a mudar muitos itens de uma vez, tente fazer várias operações menores em vez disso.",
        "laggedslavemode": "'''Aviso:''' A página pode não conter as atualizações mais recentes.",
        "readonly": "Base de dados bloqueada (limitada a leituras)",
        "enterlockreason": "Introduza um motivo para bloquear, incluindo uma estimativa de quando será desbloqueada",
        "title-invalid-interwiki": "O título da página solicitada contém uma ligação interlíngua que não pode ser utilizada em títulos.",
        "title-invalid-talk-namespace": "O título da página solicitada refere-se a uma página de discussão que não existe.",
        "title-invalid-characters": "O título da página solicitada contém carateres inválidos: \"$1\".",
+       "title-invalid-relative": "O título contém um caminho relativo. Os títulos relativos (./, ../) são inválidos porque estarão inacessíveis muitas vezes ao serem carregados pelo navegador do utilizador.",
        "title-invalid-magic-tilde": "O título da página solicitada possui uma sequência de tis inválida (<nowiki>~~~</nowiki>).",
        "title-invalid-too-long": "O título da página solicitada é demasiado longo. Não deverá ser maior que $1 {{PLURAL:$1|byte|bytes}} na codificação UTF-8.",
        "title-invalid-leading-colon": "O título da página solicitada contém um erro de pontuação (:) no início.",
        "yourpasswordagain": "Repita a palavra-passe:",
        "createacct-yourpasswordagain": "Confirme a palavra-passe",
        "createacct-yourpasswordagain-ph": "Digite a palavra-passe novamente",
-       "remembermypassword": "Recordar os meus dados neste computador (no máximo, por $1 {{PLURAL:$1|dia|dias}})",
        "userlogin-remembermypassword": "Manter-me autenticado",
        "userlogin-signwithsecure": "Usar uma ligação segura",
        "cannotloginnow-title": "Não é possível iniciar sessão agora",
        "createaccountreason": "Motivo:",
        "createacct-reason": "Motivo",
        "createacct-reason-ph": "Porque está a criar outra conta",
+       "createacct-reason-help": "Mensagem mostrada no registo de criação de contas",
        "createacct-submit": "Crie a sua conta",
        "createacct-another-submit": "Criar conta",
        "createacct-continue-submit": "Continuar criação de conta",
        "nocookiesnew": "A conta de utilizador foi criada, mas neste momento não tem sessão iniciada.\nA {{SITENAME}} utiliza ''cookies'' para autenticar os utilizadores.\nOs ''cookies'' estão desativados no seu navegador.\nAtive-os e inicie sessão com o seu nome de utilizador e a sua palavra-passe, por favor.",
        "nocookieslogin": "A {{SITENAME}} utiliza ''cookies'' para autenticar os utilizadores.\nOs ''cookies'' estão desativados no seu navegador.\nAtive-os e tente novamente, por favor.",
        "nocookiesfornew": "A conta de utilizador não foi criada, porque não foi possível confirmar a sua origem.\nCertifique-se de que tem os ''cookies'' ativados, recarregue esta página e tente novamente.",
+       "createacct-loginerror": "A conta foi criada com êxito, mas não pôde ser autenticado automaticamente. Por favor, faça o [[Special:UserLogin|início de sessão manualmente]].",
        "noname": "Não especificou um nome de utilizador válido.",
        "loginsuccesstitle": "Autenticação bem sucedida",
        "loginsuccess": "'''Encontra-se agora ligado à {{SITENAME}} como \"$1\"'''.",
-       "nosuchuser": "Não existe nenhum utilizador com o nome \"$1\".\nOs nomes de utilizador são sensíveis à capitalização.\nVerifique a ortografia, ou [[Special:CreateAccount|crie uma nova conta]].",
+       "nosuchuser": "Não existe nenhum utilizador com o nome \"$1\".\nOs nomes de utilizador são sensíveis às letras maiúsculas e minúsculas.\nVerifique a ortografia, ou [[Special:CreateAccount|crie uma nova conta]].",
        "nosuchusershort": "Não existe um utilizador com o nome \"$1\". Verifique o nome que introduziu.",
        "nouserspecified": "Precisa de especificar um nome de utilizador.",
        "login-userblocked": "Este utilizador está bloqueado. Não é permitido o acesso.",
        "botpasswords-insert-failed": "Falhou ao adicionar o nome do robô \"$1\". Já foi adicionado?",
        "botpasswords-update-failed": "Falha ao atualizar o nome do robô \"$1\". Será que foi eliminado?",
        "botpasswords-created-title": "Criada palavra-passe para o robô",
-       "botpasswords-created-body": "O robô palavra-passe para o nome do robô \"$1\" do utilizador \"$2\" foi criado.",
+       "botpasswords-created-body": "A palavra-passe de robô para o robô \"$1\" do utilizador \"$2\" foi criada.",
        "botpasswords-updated-title": "A palavra-passe de robô foi actualizada.",
        "botpasswords-updated-body": "O robô palavra-passe para o nome do robô \"$1\" do utilizador \"$2\" foi atualizado.",
        "botpasswords-deleted-title": "Palavra-passe de robô eliminada",
        "botpasswords-deleted-body": "O robô palavra-passe para o nome do robô \"$1\"do utilizador \"$2\" foi eliminado.",
        "botpasswords-newpassword": "A nova palavra-passe para iniciar sessão com <strong>$1</strong> é <strong>$2</strong>. Por favor, recorde-se dela para futura referência.</em>",
        "botpasswords-no-provider": "BotPasswordsSessionProvider não está disponível.",
+       "botpasswords-restriction-failed": "Restrições de senha de robô evitam esta autenticação.",
+       "botpasswords-invalid-name": "O nome de usuário especificado não contém o separador de senha de robô (\"$1\").",
+       "botpasswords-not-exist": "O usuário \"$1\" não possui uma senha de robô \"$2\".",
        "resetpass_forbidden": "Não é possível alterar palavras-passe",
        "resetpass_forbidden-reason": "As palavras-passe não podem ser alteradas: $1",
        "resetpass-no-info": "Precisa de iniciar sessão para aceder diretamente a esta página.",
        "passwordreset-emailelement": "{{GENDER:$1|Utilizador|Utilizadora}}: \n$1\n\nPalavra-passe temporária: \n$2",
        "passwordreset-emailsentemail": "Se este é o endereço de correio eletrónico associado a esta conta, ser-lhe-á enviada uma palavra-passe de reposição.",
        "passwordreset-emailsentusername": "Se houver um endereço de correio eletrónico associado a esta conta, ser-lhe-á enviada uma mensagem para redefinir a sua palavra-passe.",
-       "passwordreset-emailsent-capture": "Foi enviado um correio eletrónico para recuperação da palavra-passe, que é mostrado abaixo.",
-       "passwordreset-emailerror-capture": "Foi gerado um correio eletrónico para redefinição da palavra-passe, mostrado abaixo, mas o seu envio para {{GENDER:$2|o utilizador|a utilizadora}} falhou: $1",
+       "passwordreset-emailsent-capture2": "A redefinição da senha {{PLURAL:$1|do e-mail|dos e-mails}} foi enviada. {{PLURAL:$1|O nome de usuário e senha|A lista de nomes de usuário e senhas}} encontram-se a seguir.",
+       "passwordreset-emailerror-capture2": "O envio do correio {{GENDER:$2|ao usuário|à usuária}} falhou: $1 {{PLURAL:$3|O nome de usuário e senha são mostradas abaixo|A lista de nomes de usuários e senhas é mostrada abaixo}}.",
+       "passwordreset-nocaller": "Um interlocutor deve ser fornecido",
+       "passwordreset-nosuchcaller": "A pessoa que chama não existe: $1",
+       "passwordreset-ignored": "A redefinição de senha não foi realizada. Talvez o provedor não tenha sido configurado, sim?",
        "passwordreset-invalideamil": "Correio eletrónico inválido",
        "passwordreset-nodata": "Não foram fornecidos nome de utilizador(a) nem endereço de correio eletrónico",
        "changeemail": "Alterar ou remover o endereço de correio eletrónico",
        "changeemail-header": "Complete este formulário para alterar o seu endereço de correio eletrónico. Se quer eliminar a associação de qualquer endereço de correio eletrónico com a sua conta, deixe em branco o novo endereço de correio eletrónico ao submeter o formulário.",
-       "changeemail-passwordrequired": "Necessita de introduzir a sua palavra-passe para confirmar esta alteração.",
        "changeemail-no-info": "Precisa de iniciar sessão para aceder diretamente a esta página.",
        "changeemail-oldemail": "Correio eletrónico atual:",
        "changeemail-newemail": "Novo endereço de correio eletrónico:",
        "userpage-userdoesnotexist": "A conta \"<nowiki>$1</nowiki>\" não se encontra registada.\nVerifique se deseja realmente criar ou editar esta página, por favor.",
        "userpage-userdoesnotexist-view": "A conta de utilizador \"$1\" não está registada.",
        "blocked-notice-logextract": "Este utilizador está bloqueado.\nPara referência, o último registo de bloqueio é apresentado abaixo:",
-       "clearyourcache": "'''Nota:''' Após gravar, terá de limpar a ''cache'' do seu navegador para ver as alterações.\n*'''Firefox / Safari:''' Pressione ''Shift'' enquanto clica ''Recarregar'', ou pressione ''Ctrl-F5'' ou ''Ctrl-R'' (''⌘-R'' no Mac)\n*'''Google Chrome:''' Pressione ''Ctrl-Shift-R'' (''⌘-Shift-R'' no Mac)\n*'''Internet Explorer:''' Pressione ''Ctrl'' enquanto clica ''Recarregar'', ou pressione ''Ctrl-F5''\n*'''Opera:''' Limpe a ''cache'' em ''Ferramentas → Preferências'' (''Tools → Preferences'')",
+       "clearyourcache": "'''Nota:''' Após gravar, terá de limpar a ''cache'' do seu navegador para ver as alterações.\n*'''Firefox / Safari:''' Pressione ''Shift'' enquanto clica ''Recarregar'', ou pressione ''Ctrl-F5'' ou ''Ctrl-R'' (''⌘-R'' no Mac)\n*'''Google Chrome:''' Pressione ''Ctrl-Shift-R'' (''⌘-Shift-R'' no Mac)\n*'''Internet Explorer:''' Pressione ''Ctrl'' enquanto clica ''Recarregar'', ou pressione ''Ctrl-F5''\n*'''Opera:''' Ir para <em>Menu → Configurações</em> (<em>Opera → Preferências</em> no Mac) e, em seguida, <em>Privacidade e segurança → Limpar dados de navegação → Imagens e ficheiros em cache</em>.",
        "usercssyoucanpreview": "'''Dica:''' Use o botão \"{{int:showpreview}}\" para testar o seu novo CSS antes de gravar.",
        "userjsyoucanpreview": "'''Dica:''' Use o botão \"{{int:showpreview}}\" para testar o seu novo JavaScript antes de gravar.",
        "usercsspreview": "'''Lembre-se de que está apenas a antever o seu CSS particular.\nEste ainda não foi gravado!'''",
        "content-model-css": "CSS",
        "content-json-empty-object": "Objeto vazio",
        "content-json-empty-array": "Matriz vazia",
+       "deprecated-self-close-category": "Páginas com etiquetas HTML de autofechamento não válidas",
        "duplicate-args-warning": "<strong>Aviso:</strong> [[:$1]] chama [[:$2]] com mais de um valor para o parâmetro \"$3\". Somente o último valor fornecido será utilizado.",
        "duplicate-args-category": "Páginas com argumentos de predefinições duplicados",
        "duplicate-args-category-desc": "A página contém campos de predefinições que utilizam duplicatas de argumentos, tais como <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> ou <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "A edição parece já ter sido desfeita.",
        "undo-summary": "Desfez a edição $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|Discussão]])",
        "undo-summary-username-hidden": "Desfez a edição $1 de um utilizador oculto",
-       "cantcreateaccounttitle": "Não é possível criar uma conta",
        "cantcreateaccount-text": "A criação de contas a partir deste endereço IP ('''$1''') foi bloqueada por [[User:$3|$3]].\n\nO motivo apresentado por $3 foi ''$2''",
        "cantcreateaccount-range-text": "A criação de conta a partir dos endereços IP no intervalo <strong>$1</strong>, que inclui o seu endereço IP (<strong>$4</strong>), foi bloqueada por [[User:$3|$3]].\n\nA razão dada por $3 é <em>$2</em>",
        "viewpagelogs": "Ver registos para esta página",
        "showhideselectedversions": "Mostrar/ocultar versões selecionadas",
        "editundo": "desfazer",
        "diff-empty": "(Sem diferenças)",
-       "diff-multi-sameuser": "(Há {{PLURAL:$1|uma edição intermédia|$1 edições intermédias}} do mesmo utilizador que não estão a ser apresentadas)",
+       "diff-multi-sameuser": "(Há {{PLURAL:$1|uma edição intermédia do mesmo utilizador que não está a ser apresentada|$1 edições intermédias do mesmo utilizador que não estão a ser apresentadas}})",
        "diff-multi-otherusers": "(Há {{PLURAL:$1|uma revisão intermédia|$1 revisões intermédias}} de {{PLURAL:$2|outro utilizador|$2 utilizadores}} que não {{PLURAL:$1|está a ser apresentada|estão a ser apresentadas}})",
        "diff-multi-manyusers": "({{PLURAL:$1|Uma edição intermédia|$1 edições intermédias}} de mais de {{PLURAL:$2|um utilizador|$2 utilizadores}} não {{PLURAL:$1|apresentada|apresentadas}})",
        "difference-missing-revision": "{{PLURAL:$2|Uma revisão|$2 revisões}} desta diferença ($1) não {{PLURAL:$2|foi encontrada|foram encontradas}}.\n\nIsto é geralmente causado por seguir uma ligação de histórico desatualizada para uma página que foi eliminada.\nOs detalhes podem ser encontrados no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registo de eliminação].",
        "right-override-export-depth": "Exportar páginas incluindo páginas ligadas até uma profundidade de 5",
        "right-sendemail": "Enviar correio eletrónico a outros utilizadores",
        "right-passwordreset": "Ver mensagens de redefinição da palavra-passe",
-       "right-managechangetags": "Criar e eliminar [[Special:Tags|etiquetas]] da base de dados",
+       "right-managechangetags": "Criar e (des)ativar [[Special:Tags|etiquetas]]",
        "right-applychangetags": "Aplicar [[Special:Tags|etiquetas]] juntamente com as alterações",
        "right-changetags": "Adicionar ou remover [[Special:Tags|etiquetas]] arbitrárias em revisões e entradas de registo individuais",
+       "right-deletechangetags": "Eliminar [[Special:Tags|etiquetas]] da base de dados",
        "grant-generic": "Conjunto de direitos \"$1\"",
        "grant-group-page-interaction": "Interagir com páginas",
        "grant-group-file-interaction": "Interagir com conteúdo multimédia",
        "grant-group-high-volume": "Realizar actividades em grande quantidade",
        "grant-group-customization": "Personalização e preferências",
        "grant-group-administration": "Executar acções administrativas",
+       "grant-group-private-information": "Aceder aos seus dados privados",
        "grant-group-other": "Actividade diversa",
        "grant-blockusers": "Bloquear e desbloquear utilizadores",
        "grant-createaccount": "Criar contas",
        "action-viewmyprivateinfo": "ver a sua informação privada",
        "action-editmyprivateinfo": "editar a sua informação privada",
        "action-editcontentmodel": "editar o modelo de conteúdo de uma página",
-       "action-managechangetags": "criar e eliminar etiquetas da base de dados",
+       "action-managechangetags": "criar e (des)ativar etiquetas",
        "action-applychangetags": "aplicar etiquetas juntamente com as suas alterações",
        "action-changetags": "adicionar e remover etiquetas arbitrárias em revisões e entradas de registo individuais",
+       "action-purge": "recarregar esta página",
        "nchanges": "$1 {{PLURAL:$1|alteração|alterações}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|desde a última visita}}",
        "enhancedrc-history": "histórico",
        "uploaded-script-svg": "Encontrou um elemento scriptable no ficheiro \"$1\" SVG carregado.",
        "uploaded-hostile-svg": "Encontrou-se um código CSS não seguro no elemento de estilo do arquivo SVG carregado.",
        "uploaded-event-handler-on-svg": "Não está permitido configurar atributos controladores de eventos <code>$1=\"$2\"</code> nos arquivos SVG.",
+       "uploaded-image-filter-svg": "Foi encontrado um filtro de imagem com a URL: <code>&lt;$1 $2=\"$3\"&gt;</code> no ficheiro SVG carregado.",
        "uploadscriptednamespace": "Este ficheiro SVG contém um domínio que não é permitido \"$1\".",
        "uploadinvalidxml": "Erro detectado na análise do XML do ficheiro carregado.",
        "uploadvirus": "O ficheiro contém um vírus! \nDetalhes: $1",
        "upload-options": "Opções de carregamento",
        "watchthisupload": "Vigiar este ficheiro",
        "filewasdeleted": "Um ficheiro com este nome foi carregado anteriormente e subsequentemente eliminado.\nDeverá verificar o $1 antes de voltar a enviá-lo.",
+       "filename-thumb-name": "Isto parece ser o título de uma miniatura. Por favor, não volte a carregar miniaturas na mesma wiki. Se este não é o caso, corrija o nome do ficheiro por um mais significativo, e que não possua o prefixo de miniatura.",
        "filename-bad-prefix": "O nome do ficheiro que está a enviar começa por '''\"$1\"''', um nome pouco explicativo, normalmente originado de forma automática por câmaras digitais. Escolha um nome de ficheiro mais explicativo, por favor.",
        "filename-prefix-blacklist": " #<!-- deixe esta linha exactamente como está --> <pre>\n# A sintaxe é a seguinte:\n#   * Tudo a partir do carácter \"#\" até ao fim da linha é um comentário\n#   * Todas as linhas não vazias são um prefixo para nomes de ficheiros típicos atribuídos automaticamente por câmaras digitais\nCIMG # Casio\nDSC_ # Nikon\nDSCF # Fuji\nDSCN # Nikon\nDUW # alguns telefones móveis\nIMG # genérico\nJD # Jenoptik\nMGP # Pentax\nPICT # misc.\n #</pre> <!-- deixe esta linha exactamente como está -->",
        "upload-proto-error": "Protocolo incorreto",
        "upload-too-many-redirects": "A URL continha demasiados redirecionamentos",
        "upload-http-error": "Ocorreu um erro HTTP: $1",
        "upload-copy-upload-invalid-domain": "Não é possível realizar carregamentos remotos neste domínio.",
+       "upload-foreign-cant-upload": "Esta wiki não está configurada para carregar ficheiros para o repositório externo solicitado.",
        "upload-dialog-title": "Carregar ficheiro",
        "upload-dialog-button-cancel": "Cancelar",
        "upload-dialog-button-done": "Feito",
        "apisandbox-submit-invalid-fields-title": "Alguns campos são inválidos",
        "apisandbox-submit-invalid-fields-message": "Por favor, corrija os campos marcados e tente novamente.",
        "apisandbox-results": "Resultados",
+       "apisandbox-sending-request": "A enviar solicitação de API...",
+       "apisandbox-loading-results": "A receber resultados da API...",
        "apisandbox-request-url-label": "URL do pedido:",
        "apisandbox-request-time": "Tempo de processamento: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Corrija o identificador e envie-o novamente",
+       "apisandbox-results-fixtoken-fail": "Não foi possível recuperar o identificador \"$1\".",
+       "apisandbox-alert-page": "Os campos nesta página não são válidos.",
        "apisandbox-alert-field": "O valor deste campo não é válido.",
        "booksources": "Fontes bibliográficas",
        "booksources-search-legend": "Pesquisar referências bibliográficas",
        "trackingcategories-msg": "Categoria monitorada",
        "trackingcategories-name": "Nome da mensagem",
        "trackingcategories-desc": "Critérios de inclusão",
+       "restricted-displaytitle-ignored": "Páginas com títulos de exibição ignorados",
+       "restricted-displaytitle-ignored-desc": "Esta página tem um <code><nowiki>{{DISPLAYTITLE}}</nowiki></code> ignorado porque não é equivalente ao título verdadeiro da página.",
        "noindex-category-desc": "A página não é indexada por robôs porque contém a palavra mágica <code><nowiki>__NOINDEX__</nowiki></code> e está num domínio onde o estatuto é permitido.",
        "index-category-desc": "A página contém a palavra mágica <code><nowiki>__INDEX__</nowiki></code> (e está num domínio em que essa marca é permitida) e, portanto, será indexada pelos robôs mesmo quando normalmente não o seria.",
        "post-expand-template-inclusion-category-desc": "O tamanho da página é superior a <code>$wgMaxArticleSize</code>, após a expansão de todas as predefinições, pelo que algumas predefinições não foram expandidas.",
        "watchnologin": "Não está autenticado(a)",
        "addwatch": "Adicionar às páginas vigiadas",
        "addedwatchtext": "\"[[:$1]]\" e a sua página de discussão foram adicionadas à sua [[Special:Watchlist|lista de páginas vigiadas]].",
+       "addedwatchtext-talk": "\"[[:$1]]\" e a sua página associada foram adicionadas à sua lista de [[Special:Watchlist|páginas vigiadas]].",
        "addedwatchtext-short": "A página \"$1\" foi adicionada à sua lista de vigiadas.",
        "removewatch": "Remover das páginas vigiadas",
        "removedwatchtext": "\"[[:$1]]\" e a sua página de discussão foram removidas da sua lista de [[Special:Watchlist|páginas vigiadas]].",
+       "removedwatchtext-talk": "\"[[:$1]]\" e a sua página associada foram removidas da sua lista de [[Special:Watchlist|páginas vigiadas]].",
        "removedwatchtext-short": "A página \"$1\" foi removida da sua lista de vigiadas.",
        "watch": "Vigiar",
        "watchthispage": "Vigiar esta página",
        "rollbacklinkcount-morethan": "reverter mais do que $1 {{PLURAL:$1|edição|edições}}",
        "rollbackfailed": "A reversão falhou",
        "rollback-missingparam": "Faltam parâmetros obrigatórios no pedido.",
+       "rollback-missingrevision": "Não é possível carregar os dados de revisão.",
        "cantrollback": "Não foi possível reverter a edição; o último contribuidor é o único autor desta página",
        "alreadyrolled": "Não foi possível reverter as edições de [[:$1]] por [[User:$2|$2]] ([[User talk:$2|discussão]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nalguém editou ou já reverteu a página.\n\nA última edição foi de [[User:$3|$3]] ([[User talk:$3|discussão]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "O resumo da edição era: <em$1</em>.",
        "changecontentmodel-success-text": "O tipo de conteúdo de [[:$1]] foi alterado.",
        "changecontentmodel-cannot-convert": "O conteúdo em [[:$1]] não pode ser convertido para um tipo de $2.",
        "changecontentmodel-nodirectediting": "O modelo de conteúdo $1 não suporta edição direta",
+       "changecontentmodel-emptymodels-title": "Não há modelos de conteúdo disponíveis",
+       "changecontentmodel-emptymodels-text": "O conteúdo em [[:$1]] não pode ser convertido para qualquer tipo.",
        "log-name-contentmodel": "Registo de alteração de modelo de conteúdo",
        "log-description-contentmodel": "Eventos relacionados com os modelos de conteúdo de uma página",
        "logentry-contentmodel-new": "$1 {{GENDER:$2|criou}} a página $3 com o modelo de conteúdo desconhecido \"$5\"",
        "undeletedrevisions": "$1 {{PLURAL:$1|edição restaurada|edições restauradas}}",
        "undeletedrevisions-files": "$1 {{PLURAL:$1|edição restaurada|edições restauradas}} e $2 {{PLURAL:$2|ficheiro restaurado|ficheiros restaurados}}",
        "undeletedfiles": "{{PLURAL:$1|ficheiro restaurado|$1 ficheiros restaurados}}",
-       "cannotundelete": "Restauração falhada:\n$1",
+       "cannotundelete": "Algumas ou todas as restaurações falharam:\n$1",
        "undeletedpage": "'''$1 foi restaurada'''\n\nConsulte o [[Special:Log/delete|registo de eliminações]] para um registo das eliminações e restaurações mais recentes.",
        "undelete-header": "Consulte o [[Special:Log/delete|registo de eliminações]] para ver as páginas eliminadas recentemente.",
        "undelete-search-title": "Pesquisar páginas eliminadas",
        "sp-contributions-newbies-sub": "Para contas novas",
        "sp-contributions-newbies-title": "Contribuições de contas novas",
        "sp-contributions-blocklog": "registo de bloqueios",
-       "sp-contributions-suppresslog": "contribuições suprimidas",
-       "sp-contributions-deleted": "contribuições eliminadas",
+       "sp-contributions-suppresslog": "contribuições de {{GENDER:$1|utilizador|utilizadora|utilizador(a)}} suprimidas",
+       "sp-contributions-deleted": "contribuições de {{GENDER:$1|utilizador|utilizadora|utilizador(a)}} eliminadas",
        "sp-contributions-uploads": "carregamentos",
        "sp-contributions-logs": "registos",
        "sp-contributions-talk": "discussão",
        "lockdbsuccesstext": "A base de dados da {{SITENAME}} foi bloqueada.<br />\nLembre-se de [[Special:UnlockDB|remover o bloqueio]] após a manutenção.",
        "unlockdbsuccesstext": "A base de dados foi desbloqueada.",
        "lockfilenotwritable": "O ficheiro de bloqueio da base de dados não pode ser escrito.\nPara bloquear ou desbloquear a base de dados, este precisa de poder ser escrito pelo servidor de internet.",
+       "databaselocked": "A base de dados já está bloqueada.",
        "databasenotlocked": "A base de dados não está bloqueada.",
        "lockedbyandtime": "(por {{GENDER:$1|$1}} em $2 às $3)",
        "move-page": "Mover $1",
        "tooltip-ca-nstab-category": "Ver a página de categoria",
        "tooltip-minoredit": "Marcar como edição menor",
        "tooltip-save": "Gravar as alterações",
+       "tooltip-publish": "Publicar as suas alterações",
        "tooltip-preview": "Antever as suas alterações. Use antes de gravar, por favor!",
        "tooltip-diff": "Mostrar alterações que fez a este texto.",
        "tooltip-compareselectedversions": "Ver as diferenças entre as duas versões selecionadas desta página.",
        "confirmemail_body_set": "Alguém, provavelmente você a partir do endereço IP $1,\ndefiniu o seu endereço de correio eletrónico como correio da conta \"$2\" em {{SITENAME}}.\n\nPara confirmar que esta conta é realmente sua e reativar\nas funcionalidades de correio eletrónico em {{SITENAME}},\nabra a seguinte ligação no seu navegador:\n\n$3\n\nCaso a conta *não* lhe pertença, abra a seguinte ligação\npara cancelar a confirmação do endereço de correio eletrónico:\n\n$5\n\nEste código de confirmação expira a $4.",
        "confirmemail_invalidated": "Confirmação de endereço de correio eletrónico cancelada",
        "invalidateemail": "Cancelar confirmação do correio eletrónico",
+       "notificationemail_subject_changed": "O endereço de correio eletrónico registado em {{SITENAME}} foi alterado",
+       "notificationemail_subject_removed": "O endereço de correio eletrónico registado em {{SITENAME}} foi removido",
+       "notificationemail_body_changed": "Alguém, provavelmente você, a partir do endereço IP $1, alterou o endereço de correio eletrónico da conta \"$2\" para \"$3\" em {{SITENAME}}.\n\nCaso não tenha alterado, contacte imediatamente um administrador do sítio.",
+       "notificationemail_body_removed": "Alguém, provavelmente você, a partir do endereço IP $1, eliminou o endereço de correio eletrónico da conta \"$2\" em {{SITENAME}}.\n\nCaso não tenha alterado, contacte imediatamente um administrador do sítio.",
        "scarytranscludedisabled": "[Transclusão interwikis foi impossibilitada]",
        "scarytranscludefailed": "[Não foi possível obter a predefinição a partir de $1]",
        "scarytranscludefailed-httpstatus": "[Não foi possível obter a predefinição a partir de $1: HTTP $2]",
        "timezone-local": "Local",
        "duplicate-defaultsort": "<strong>Aviso:</strong> A chave de ordenação padrão \"$2\" sobrepõe-se à anterior \"$1\".",
        "duplicate-displaytitle": "<strong>Aviso:</strong> O título em exibição \"$2\" anula o título anteriormente em exibição \"$1\".",
+       "restricted-displaytitle": "<strong>Aviso</strong>: A apresentação do título \"$1\" foi ignorada porque não é equivalente ao título atual da página.",
        "invalid-indicator-name": "<strong>Erro:</strong> O atributo <code>name</code>, da página de estados, não deve estar em branco.",
        "version": "Versão",
        "version-extensions": "Extensões instaladas",
        "version-license-not-found": "Não foi encontrada informação detalhada da licença para esta extensão.",
        "version-credits-title": "Créditos de autoria da extensão $1",
        "version-credits-not-found": "Não foi encontrada informação detalhada dos créditos para esta extensão.",
-       "version-poweredby-credits": "Esta wiki é potenciada por <strong>[https://www.mediawiki.org/ MediaWiki]</strong>, copyright © 2001-$1 $2.",
+       "version-poweredby-credits": "Este é um wiki <strong>[https://www.mediawiki.org/ MediaWiki]</strong>, copyright © 2001-$1 $2.",
        "version-poweredby-others": "outros",
        "version-poweredby-translators": "os tradutores da translatewiki.net",
        "version-credits-summary": "Gostaríamos de reconhecer as seguintes pessoas pela sua contribuição para o [[Special:Version|MediaWiki]].",
        "tags-deactivate": "desativar",
        "tags-hitcount": "$1 {{PLURAL:$1|modificação|modificações}}",
        "tags-manage-no-permission": "Não possui permissão para gerir alterações de etiquetas.",
+       "tags-manage-blocked": "Não pode gerir alterações de etiquetas enquanto estiver bloqueado.",
        "tags-create-heading": "Criar nova etiqueta",
        "tags-create-explanation": "Por definição, etiquetas recém-criadas estarão disponíveis para utilização por utilizadores e robôs.",
        "tags-create-tag-name": "Nome da etiqueta:",
        "feedback-useragent": "Agente de utilizador:",
        "searchsuggest-search": "Pesquisa",
        "searchsuggest-containing": "contendo...",
+       "api-error-autoblocked": "O seu endereço de IP foi bloqueado automaticamente, pois foi utilizado por um utilizador bloqueado.",
        "api-error-badaccess-groups": "Não tem permissão para enviar ficheiros para esta wiki.",
        "api-error-badtoken": "Erro interno: Chave incorrecta.",
+       "api-error-blocked": "Foi bloqueado de editar.",
        "api-error-copyuploaddisabled": "O carregamento de ficheiros por URL não foi possibilitado neste servidor.",
        "api-error-duplicate": "Já {{PLURAL:$1|existia outro ficheiro|existiam outros ficheiro}} na wiki com o mesmo conteúdo.",
        "api-error-duplicate-archive": "Já {{PLURAL:$1|estava outro ficheiro|estavam outros ficheiros}} no  site com o mesmo conteúdo, mas {{PLURAL:$1|foi|foram}} eliminados.",
        "special-characters-group-ipa": "AFI (IPA)",
        "special-characters-group-symbols": "Símbolos",
        "special-characters-group-greek": "Grego",
+       "special-characters-group-greekextended": "Grego estendido",
        "special-characters-group-cyrillic": "Cirílico",
        "special-characters-group-arabic": "Árabe",
        "special-characters-group-arabicextended": "Arábico estendido",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
        "mw-widgets-titleinput-description-new-page": "a página ainda não existe.",
        "mw-widgets-titleinput-description-redirect": "redirecionar para $1",
-       "api-error-blacklisted": "Escolha um título diferente e descritivo, por favor.",
+       "sessionmanager-tie": "Não se pode combinar múltiplas solicitações de tipos de autenticação: $1.",
        "sessionprovider-generic": "Sessões $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sessões baseadas em cookie",
        "sessionprovider-nocookies": "Os cookies podem estar desativados. Certifique-se de que os cookies estão ativados e inicie novamente.",
        "log-action-filter-newusers": "Tipo de criação de conta:",
        "log-action-filter-patrol": "Tipo de patrulha:",
        "log-action-filter-protect": "Tipo de proteção:",
-       "log-action-filter-rights": "Tipo de alteração de privilégio",
-       "log-action-filter-suppress": "Tipo de supressão",
+       "log-action-filter-rights": "Tipo de alteração de privilégio:",
+       "log-action-filter-suppress": "Tipo de supressão:",
        "log-action-filter-upload": "Tipo de carregamento:",
        "log-action-filter-all": "Todas",
        "log-action-filter-block-block": "Bloquear",
        "log-action-filter-managetags-delete": "Eliminação de etiqueta",
        "log-action-filter-managetags-activate": "Ativação de etiqueta",
        "log-action-filter-managetags-deactivate": "Desativação de etiqueta",
+       "log-action-filter-move-move": "Mover sem a substituição de páginas de redirecionamento",
+       "log-action-filter-move-move_redir": "Mover com a substituição de páginas de redirecionamento",
        "log-action-filter-newusers-create": "Criação por utilizador anónimo",
        "log-action-filter-newusers-create2": "Criação por utilizador registado",
        "log-action-filter-newusers-autocreate": "Criação automática",
        "log-action-filter-rights-rights": "Alteração manual",
        "log-action-filter-rights-autopromote": "Alteração automática",
        "log-action-filter-suppress-event": "Supressão de registo",
+       "log-action-filter-suppress-revision": "Supressão de revisões",
        "log-action-filter-suppress-delete": "Supressão de página",
+       "log-action-filter-suppress-block": "Supressão de utilizadores por bloqueio",
        "log-action-filter-upload-upload": "Novo carregamento",
-       "log-action-filter-upload-overwrite": "Recarregar"
+       "log-action-filter-upload-overwrite": "Recarregar",
+       "authmanager-authn-no-primary": "As informações de identificação fornecidas não podem ser autenticadas.",
+       "authmanager-authn-autocreate-failed": "A criação automática de uma conta local falhou: $1",
+       "authmanager-create-disabled": "A criação de contas está desativada.",
+       "authmanager-create-from-login": "Para criar a sua conta, por favor, preencha os campos abaixo.",
+       "authmanager-authplugin-setpass-failed-title": "A alteração de palavra-passe falhou",
+       "authmanager-authplugin-setpass-failed-message": "O plugin de autenticação negou a alteração de palavra-passe.",
+       "authmanager-authplugin-create-fail": "O plugin de autenticação negou a criação de conta.",
+       "authmanager-authplugin-setpass-denied": "O plugin de autenticação não permite a alteração de palavras-passe.",
+       "authmanager-authplugin-setpass-bad-domain": "Domínio inválido.",
+       "authmanager-autocreate-noperm": "A criação automática de contas não é permitida.",
+       "authmanager-autocreate-exception": "A criação automática de contas foi temporariamente desativada devido a erros prévios.",
+       "authmanager-userdoesnotexist": "A conta de utilizador(a) \"$1\" não está registada.",
+       "authmanager-username-help": "Nome de utilizador(a) para autenticação.",
+       "authmanager-password-help": "Palavra-passe para autenticação.",
+       "authmanager-domain-help": "Domínio para a autenticação externa.",
+       "authmanager-retype-help": "A palavra-passe novamente para confirmação.",
+       "authmanager-email-label": "Correio eletrónico",
+       "authmanager-email-help": "Endereço de correio eletrónico",
+       "authmanager-realname-label": "Nome verdadeiro",
+       "authmanager-realname-help": "Nome verdadeiro do(a) utilizador(a)",
+       "authmanager-provider-password": "Autenticação baseada em palavra-passe",
+       "authmanager-provider-password-domain": "Autenticação baseada em palavra-passe e domínio",
+       "authmanager-provider-temporarypassword": "Palavra-passe temporária",
+       "authprovider-confirmlink-message": "Com base nas tuas últimas tentativas para iniciar sessão, as seguintes contas podem ser ligadas à tua conta wiki. Vinculá-las permite que inicie sessão através das mesmas. Selecione quais pretende vincular.",
+       "authprovider-confirmlink-success-line": "$1: Ligado com êxito.",
+       "authprovider-resetpass-skip-label": "Ignorar",
+       "authprovider-resetpass-skip-help": "Ignorar redefinição de palavra-passe",
+       "authform-newtoken": "Chave em falta. $1",
+       "authform-notoken": "Chave em falta",
+       "authform-wrongtoken": "Chave errada",
+       "specialpage-securitylevel-not-allowed-title": "Não permitido",
+       "specialpage-securitylevel-not-allowed": "Desculpe, não tem permissão para utilizar esta página porque a sua identidade não pôde ser verificada.",
+       "authpage-cannot-login": "Não é possível iniciar sessão.",
+       "authpage-cannot-login-continue": "Não é possível continuar a iniciar sessão. A sua sessão pode ter expirado.",
+       "authpage-cannot-create": "Não é possível iniciar a criação da conta.",
+       "authpage-cannot-create-continue": "Não é possível continuar a criação da conta. A sua sessão pode ter expirado.",
+       "authpage-cannot-link": "Não se pode iniciar a vinculação da conta.",
+       "authpage-cannot-link-continue": "Não é possível continuar a criação da conta. A sua sessão pode ter expirado.",
+       "cannotauth-not-allowed-title": "Permissão negada",
+       "cannotauth-not-allowed": "Não possui permissão para utilizar esta página",
+       "changecredentials": "Alterar credenciais",
+       "changecredentials-submit": "Alterar credenciais",
+       "changecredentials-invalidsubpage": "$1 não é um tipo de credencial válido.",
+       "changecredentials-success": "As suas credenciais foram alteradas.",
+       "removecredentials": "Remover credenciais",
+       "removecredentials-submit": "Remover credenciais",
+       "removecredentials-invalidsubpage": "$1 não é um tipo de credencial válido.",
+       "removecredentials-success": "As suas credenciais foram removidas.",
+       "credentialsform-provider": "Tipo de credenciais:",
+       "credentialsform-account": "Nome da conta:",
+       "cannotlink-no-provider-title": "Não existem contas vinculáveis",
+       "cannotlink-no-provider": "Não existem contas vinculáveis",
+       "linkaccounts": "Associar contas",
+       "linkaccounts-success-text": "A conta foi associada.",
+       "linkaccounts-submit": "Associar contas",
+       "unlinkaccounts": "Desassociar contas",
+       "unlinkaccounts-success": "A conta foi desassociada."
 }
index 5ba5061..924d25f 100644 (file)
                        "Psychoslave",
                        "Guycn2",
                        "2axterix2",
-                       "Ата"
+                       "Ата",
+                       "Matěj Suchánek",
+                       "Chaduvari",
+                       "MarcoAurelio"
                ]
        },
        "sidebar": "{{notranslate}}",
        "noindex-category": "Name of the [[mw:Special:MyLanguage/Help:Tracking categories|tracking category]] where pages with the <nowiki>__NOINDEX__</nowiki> behavior switch are listed.\n\nFor description of this behavior switch see [[mw:Special:MyLanguage/Help:Magic_words#Behavior_switches|MediaWiki]].\n\nSee also:\n* {{msg-mw|Noindex-category-desc}}",
        "broken-file-category": "Name of [[mw:Special:MyLanguage/Help:Tracking categories|tracking category]] where pages that embed files that do not exist (\"broken images\") are listed.\n\nSee also:\n* {{msg-mw|Broken-file-category-desc}}",
        "categoryviewer-pagedlinks": "{{Optional}}\nThe pagination links in category viewer. Parameters:\n* $1 - the previous link, uses {{msg-mw|Prevn}}\n* $2 - the next link, uses {{msg-mw|Nextn}}",
+       "category-header-numerals": "{{Optional}}\nA header for all pages whose titles start with a number. This is used on category pages. This should only be translated if your language uses a different method to indicate a range of numbers (other than a dash).\n* $1 – 0 (or localized equivalent)\n* $2 – 9 (or localized equivalent)",
        "about": "{{Identical|About}}",
        "article": "A 'content page' is a page that forms part of the purpose of the wiki. It includes the main page and pages in the main namespace and any other namespaces that are included when the wiki is customised. For example on Wikimedia Commons 'content pages' include pages in the file and category namespaces. On Wikinews 'content pages' include pages in the Portal namespace. For technical definition of 'content namespaces' see [[mw:Manual:Using_custom_namespaces#Content_namespaces|MediaWiki]].\n\nPossible alternatives to the word 'content' are 'subject matter' or 'wiki subject' or 'wiki purpose'.\n\n{{Identical|Content page}}",
        "newwindow": "Below the edit form, next to \"{{msg-mw|Edithelp}}\".",
        "tagline": "{{doc-important|Do not change <code><nowiki>{{SITENAME}}</nowiki></code>.}}\nUsed to identify the source of copied information.",
        "help": "General text (noun) used in the sidebar (by default).\n\nSee also {{msg-mw|Helppage}} and {{msg-mw|Edithelp}}.\n\nSee also:\n* {{msg-mw|Help}}\n* {{msg-mw|Accesskey-n-help}}\n* {{msg-mw|Tooltip-n-help}}\n{{Identical|Help}}",
        "search": "{{doc-special|Search}}\nNoun. Text of menu section shown on every page of the wiki above the search form.\n\nSee also:\n* {{msg-mw|Search}}\n* {{msg-mw|Accesskey-search}}\n* {{msg-mw|Tooltip-search}}\n{{Identical|Search}}",
+       "search-ignored-headings": "Headings that will be ignored by search. You can translate the text, including \"Leave this line exactly as it is\". Some lines of this messages have one (1) leading space.",
        "searchbutton": "The button you can see in the sidebar, below the search input box. The \"Go\" button is {{msg-mw|Searcharticle}}.\n{{Identical|Search}}",
        "go": "See also:\n* {{msg-mw|Go}}\n* {{msg-mw|Accesskey-search-go}}\n* {{msg-mw|Tooltip-search-go}}\n{{Identical|Go}}",
        "searcharticle": "Button description in the search menu displayed on every page. The \"Search\" button is {{msg-mw|Searchbutton}}.\n{{Identical|Go}}",
        "yourpasswordagain": "Since 1.22 no longer used in core, but may be used by some extensions. DEPRECATED",
        "createacct-yourpasswordagain": "In create account form, label for field to re-enter password\n\nSee example: [{{canonicalurl:Special:UserLogin|type=signup}} Special:UserLogin?type=signup]\n{{Identical|Confirm password}}",
        "createacct-yourpasswordagain-ph": "Placeholder text in create account form for re-enter password field.\n\nSee example: [{{canonicalurl:Special:UserLogin|type=signup}} Special:UserLogin?type=signup]",
-       "remembermypassword": "Used as checkbox label on [[Special:ChangePassword]]. Parameters:\n* $1 - number of days\n{{Identical|Remember my login on this computer}}",
        "userlogin-remembermypassword": "The text for a check box in [[Special:UserLogin]].",
        "userlogin-signwithsecure": "Text of link to HTTPS login form.\n\nSee example: [[Special:UserLogin]]",
-       "cannotloginnow-title": "Error page title shown when logging in is not possible.",
-       "cannotloginnow-text": "Error page text shown when logging in is not possible. Parameters:\n* $1 - Session type in use that makes it not possible to log in, from a message like {{msg-mw|sessionprovider-mediawiki-session-cookiesessionprovider}}.",
+       "cannotlogin-title": "Error page title shown when logging in is not possible. This is a catch-all when a more specific reason is not available.",
+       "cannotlogin-text": "Error page text shown when logging in is not possible. This is a catch-all when a more specific reason is not available.",
+       "cannotloginnow-title": "Error page title shown when logging in is not possible becuse the session provider in use does the user authentication itself.",
+       "cannotloginnow-text": "Error page text shown when logging in is not possible becuse the session provider in use does the user authentication itself. Parameters:\n* $1 - Session type in use that makes it not possible to log in, from a message like {{msg-mw|sessionprovider-mediawiki-session-cookiesessionprovider}}.",
+       "cannotcreateaccount-title": "Error page title shown when manual account creation is not possible. That probably means the wiki supports some other account creation method, e.g. autocreation by the session provider. (When account creation is possible but the current user does not have permission to do it, a more specific error message is displayed.)",
+       "cannotcreateaccount-text": "Error page text shown when manual account creation is not possible. That probably means the wiki supports some other account creation method, e.g. autocreation by the session provider. (When account creation is possible but the current user does not have permission to do it, a more specific error message is displayed.)",
        "yourdomainname": "Used as label for listbox.",
        "password-change-forbidden": "Error message shown when an external authentication source does not allow the password to be changed.",
        "externaldberror": "This message is thrown when a valid attempt to change the wiki password for a user fails because of a database error or an error from an external system.",
        "resetpass-abort-generic": "Generic error message shown on [[Special:ChangePassword]] when an extension aborts a password change from a hook.",
        "resetpass-expired": "Generic error message shown on [[Special:ChangePassword]] when a user's password is expired",
        "resetpass-expired-soft": "Generic warning message shown on [[Special:ChangePassword]] when a user needs to reset their password, but they are not prevented from logging in at this time",
-       "resetpass-validity-soft": "Warning message shown on [[Special:ChangePassword]] when a user needs to reset their password, because their password is not valid.\n\nRefers to {{msg-mw|Resetpass-submit-cancel}}.\n\nParameters:\n* $1 - error message",
+       "resetpass-validity-soft": "Warning message shown on [[Special:ChangePassword]] when a user needs to reset their password, because their password is not valid.\n\nRefers to {{msg-mw|authprovider-resetpass-skip-label}}.\n\nParameters:\n* $1 - error message",
        "passwordreset": "Title of [[Special:PasswordReset]].\n{{Identical|Reset password}}",
        "passwordreset-text-one": "Text on [[Special:PasswordReset]] that appears when there is only one way of resetting the password.\n\n{{msg-mw|Passwordreset-text-many}} will be used, when there are multiple ways of resetting the password.",
        "passwordreset-text-many": "Text on [[Special:PasswordReset]] that appears when there are multiple ways of resetting the password.\n\nParameters:\n* $1 - the number of password reset routes\n\n{{msg-mw|Passwordreset-text-one}} will be used, when there is only one way of resetting the password.",
        "passwordreset-emailelement": "This is a body of a password reset email to allow them into the system with a new password. Parameters:\n* $1 - the user's login name. This parameter can be used for GENDER.\n* $2 - the temporary password given by the system",
        "passwordreset-emailsentemail": "Used in [[Special:PasswordReset]].\n\nSee also:\n* {{msg-mw|Passwordreset-emailsent-capture}}\n* {{msg-mw|Passwordreset-emailerror-capture}}",
        "passwordreset-emailsentusername": "Used in [[Special:PasswordReset]].\n\nSee also:\n* {{msg-mw|Passwordreset-emailsent-capture}}\n* {{msg-mw|Passwordreset-emailerror-capture}}",
-       "passwordreset-emailsent-capture": "Used in [[Special:PasswordReset]].\n\nSee also:\n* {{msg-mw|Passwordreset-emailsentemail}}\n* {msg-mw|Passwordreset-emailsentusername}}\n* {{msg-mw|Passwordreset-emailerror-capture}}",
-       "passwordreset-emailerror-capture": "Error message displayed in [[Special:PasswordReset]] when sending an email fails. Parameters:\n* $1 - error message\n* $2 - username, used for GENDER\nSee also:\n* {{msg-mw|Passwordreset-emailsentemail}}\n* {msg-mw|Passwordreset-emailsentusername}}\n* {{msg-mw|Passwordreset-emailsent-capture}}",
-       "passwordreset-emailsent-capture2": "Used in [[Special:PasswordReset]].\n\nParameters:\n* $1 - number of accounts notified\n\nSee also:\n* {{msg-mw|Passwordreset-emailsentemail}}\n* {msg-mw|Passwordreset-emailsentusername}}\n* {{msg-mw|Passwordreset-emailerror-capture}}",
+       "passwordreset-emailsent-capture2": "Used in [[Special:PasswordReset]].\n\nParameters:\n* $1 - number of accounts notified\n\nSee also:\n* {{msg-mw|Passwordreset-emailsentemail}}\n* {{msg-mw|Passwordreset-emailsentusername}}\n* {{msg-mw|Passwordreset-emailerror-capture}}",
        "passwordreset-emailerror-capture2": "Error message displayed in [[Special:PasswordReset]] when sending an email fails. Parameters:\n* $1 - error message\n* $2 - username, used for GENDER\n* $3 - number of accounts notified\n\nSee also:\n* {{msg-mw|Passwordreset-emailsentemail}}\n* {{msg-mw|Passwordreset-emailsentusername}}\n* {{msg-mw|Passwordreset-emailsent-capture}}\n* {{msg-mw|Passwordreset-emailerror-capture}}",
        "passwordreset-nocaller": "Shown when a password reset was requested but the caller was not provided. This is an internal error.",
        "passwordreset-nosuchcaller": "Shown when a password reset was requested but the username of the caller could not be resolved to a user. This is an internal error.\n\nParameters:\n* $1 - username of the caller",
        "changeemail": "Title of [[Special:ChangeEmail|special page]]. This page also allows removing the user's email address.",
        "changeemail-summary": "{{ignored}}",
        "changeemail-header": "Text of [[Special:ChangeEmail]].",
-       "changeemail-passwordrequired": "Shown on [[Special:ChangeEmail]] if users are required to enter their password to change their email address..",
        "changeemail-no-info": "Error message for [[Special:ChangeEmail]].\n\nParameters:\n* $1 (unused) - a link to [[Special:UserLogin]] with {{msg-mw|loginreqlink}} as link description",
        "changeemail-oldemail": "Label for e-mail address field in [[Special:ChangeEmail]].",
        "changeemail-newemail": "Label for e-mail address field in [[Special:ChangeEmail]]. See also {{msg-mw|changeemail-newemail-help}}",
        "content-model-json": "Name for the JSON content model, used when decribing what type of content a page contains.\n\nThis message is substituted in:\n*{{msg-mw|Bad-target-model}}\n*{{msg-mw|Content-not-allowed-here}}",
        "content-json-empty-object": "Used to represent an object with no properties on a JSON content model page.",
        "content-json-empty-array": "Used to represent an array with no values on a JSON content model page.",
+       "deprecated-self-close-category": "This message is used as a category name for a [[mw:Special:MyLanguage/Help:Tracking categories|tracking category]] where pages are placed automatically if they contain invalid self-closed HTML tags, such as <code>&lt;b/></code> or <code>&lt;span/></code>.  The behavior of these will change soon to be consistent with the HTML5 specification, so their use in wikitext is deprecated.",
+       "deprecated-self-close-category-desc": "Invalid self-closed HTML tag category description. Shown on [[Special:TrackingCategories]].\n\nSee also:\n* {{msg-mw|deprecated-self-close-category}}",
        "duplicate-args-warning": "If a page calls a template and specifies the same argument more than once, such as <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> or <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>, this warning is displayed when previewing.\n\nParameters:\n* $1 - The calling page\n* $2 - The called template\n* $3 - The name of the duplicated argument",
        "duplicate-args-category": "This message is used as a category name for a [[mw:Special:MyLanguage/Help:Tracking categories|tracking category]] where pages are placed automatically if they contain template calls that use duplicates of arguments, such as <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> or <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "duplicate-args-category-desc": "Duplicate arguments category description. Shown on [[Special:TrackingCategories]].\n\nSee also:\n* {{msg-mw|Duplicate-args-category}}",
        "undo-nochange": "Message appears if an attempt to revert an edit by clicking the \"undo\" link results in an edit making no change to the current version of the page.\n\nSee also:\n* {{msg-mw|Undo-failure}}\n* {{msg-mw|Undo-norev}}",
        "undo-summary": "Edit summary for an undo action. Parameters:\n* $1 - revision ID\n* $2 - username\n{{Identical|Undo}}",
        "undo-summary-username-hidden": "Edit summary for an undo action where the username of the old revision is hidden.\n\nParameters:\n* $1 - the revision ID being undone\nSee also:\n* {{msg-mw|Undo-summary}}",
-       "cantcreateaccounttitle": "Used as title of the error message {{msg-mw|Cantcreateaccount-text}}.",
-       "cantcreateaccount-text": "Used as error message, with the title {{msg-mw|Cantcreateaccounttitle}}.\n* $1 - target IP address\n* $2 - reason or {{msg-mw|Blockednoreason}}\n* $3 - username\nSee also:\n* {{msg-mw|Cantcreateaccount-range-text}}",
+       "cantcreateaccount-text": "Used as error message when account creation is prevented by an IP block.\n* $1 - target IP address\n* $2 - reason or {{msg-mw|Blockednoreason}}\n* $3 - username\nSee also:\n* {{msg-mw|Cantcreateaccount-range-text}}",
        "cantcreateaccount-range-text": "Used instead of the {{msg-mw|Cantcreateaccount-text}} when the block is a range block.\n* $1 - target IP address range\n* $2 - reason or {{msg-mw|Blockednoreason}}\n* $3 - username\n* $4 - current user's IP address",
        "createaccount-hook-aborted": "Placeholder message to return with API errors on account create; passes through the message from a hook {{notranslate}}",
        "viewpagelogs": "Link displayed in history of pages",
        "right-applychangetags": "{{doc-right|applychangetags}}",
        "right-changetags": "{{doc-right|changetags}}",
        "right-deletechangetags": "{{doc-right|deletechangetags}}",
-       "grant-generic": "Used if the grant name is not defined. Parameters:\n* $1 - grant name\n\nDefined grants (grant name refers: blockusers, createeditmovepage, ...):\n* {{msg-mw|grant-checkuser}}\n* {{msg-mw|grant-blockusers}}\n* {{msg-mw|grant-createaccount}}\n* {{msg-mw|grant-createeditmovepage}}\n* {{msg-mw|grant-delete}}\n* {{msg-mw|grant-editinterface}}\n* {{msg-mw|grant-editmycssjs}}\n* {{msg-mw|grant-editmyoptions}}\n* {{msg-mw|grant-editmywatchlist}}\n* {{msg-mw|grant-editpage}}\n* {{msg-mw|grant-editprotected}}\n* {{msg-mw|grant-highvolume}}\n* {{msg-mw|grant-oversight}}\n* {{msg-mw|grant-patrol}}\n* {{msg-mw|grant-protect}}\n* {{msg-mw|grant-rollback}}\n* {{msg-mw|grant-sendemail}}\n* {{msg-mw|grant-uploadeditmovefile}}\n* {{msg-mw|grant-uploadfile}}\n* {{msg-mw|grant-basic}}\n* {{msg-mw|grant-viewdeleted}}\n* {{msg-mw|grant-viewmywatchlist}}",
+       "grant-generic": "Used if the grant name is not defined. Parameters:\n* $1 - grant name\n\nDefined grants (grant name refers: blockusers, createeditmovepage, ...):\n* {{msg-mw|grant-checkuser}}\n* {{msg-mw|grant-blockusers}}\n* {{msg-mw|grant-createaccount}}\n* {{msg-mw|grant-createeditmovepage}}\n* {{msg-mw|grant-delete}}\n* {{msg-mw|grant-editinterface}}\n* {{msg-mw|grant-editmycssjs}}\n* {{msg-mw|grant-editmyoptions}}\n* {{msg-mw|grant-editmywatchlist}}\n* {{msg-mw|grant-editpage}}\n* {{msg-mw|grant-editprotected}}\n* {{msg-mw|grant-highvolume}}\n* {{msg-mw|grant-oversight}}\n* {{msg-mw|grant-patrol}}\n* {{msg-mw|grant-privateinfo}}\n* {{msg-mw|grant-protect}}\n* {{msg-mw|grant-rollback}}\n* {{msg-mw|grant-sendemail}}\n* {{msg-mw|grant-uploadeditmovefile}}\n* {{msg-mw|grant-uploadfile}}\n* {{msg-mw|grant-basic}}\n* {{msg-mw|grant-viewdeleted}}\n* {{msg-mw|grant-viewmywatchlist}}",
        "grant-group-page-interaction": "{{Related|grant-group}}",
        "grant-group-file-interaction": "{{Related|grant-group}}",
        "grant-group-watchlist-interaction": "{{Related|grant-group}}",
        "grant-group-high-volume": "{{Related|Grant-group}}",
        "grant-group-customization": "{{Related|Grant-group}}",
        "grant-group-administration": "{{Related|Grant-group}}",
+       "grant-group-private-information": "{{Related|Grant-group}}",
        "grant-group-other": "{{Related|Grant-group}}",
        "grant-blockusers": "Name for grant \"blockusers\".\n{{Related|Grant}}",
-       "grant-createaccount": "Name for grant \"createaccount\".\n{{Related|grant}}",
-       "grant-createeditmovepage": "Name for grant \"createeditmovepage\".\n{{Related|grant}}",
-       "grant-delete": "Name for grant \"delete\".\n{{Related|grant}}",
-       "grant-editinterface": "Name for grant \"editinterface\".\n\n\"JS\" stands for \"JavaScript\".\n{{Related|grant}}",
-       "grant-editmycssjs": "Name for grant \"editmycssjs\".\n\n\"JS\" stands for \"JavaScript\".\n{{Related|grant}}",
-       "grant-editmyoptions": "Name for grant \"editmyoptions\".\n{{Related|grant}}",
-       "grant-editmywatchlist": "Name for grant \"editmywatchlist\".\n{{Related|grant}}\n{{Identical|Edit your watchlist}}",
-       "grant-editpage": "Name for grant \"editpage\".\n{{Related|grant}}",
-       "grant-editprotected": "Name for grant \"editprotected\".\n{{Related|grant}}",
-       "grant-highvolume": "Name for grant \"highvolume\".\n{{Related|grant}}",
-       "grant-oversight": "Name for grant \"oversight\".\n{{Related|grant}}",
-       "grant-patrol": "Name for grant \"patrol\".\n{{Related|grant}}",
-       "grant-protect": "Name for grant \"protect\".\n{{Related|grant}}",
-       "grant-rollback": "Name for grant \"rollback\".\n{{Related|grant}}",
-       "grant-sendemail": "Name for grant \"sendemail\".\n{{Related|grant}}",
-       "grant-uploadeditmovefile": "Name for grant \"uploadeditmovefile\".\n{{Related|grant}}",
-       "grant-uploadfile": "Name for grant \"uploadfile\".\n{{Related|grant}}\n{{Identical|Upload new file}}",
-       "grant-basic": "Name for grant \"basic\".\n{{Related|grant}}",
-       "grant-viewdeleted": "Name for grant \"viewdeleted\".\n{{Related|grant}}",
-       "grant-viewmywatchlist": "Name for grant \"viewmywatchlist\".\n{{Related|grant}}\n{{Identical|View your watchlist}}",
+       "grant-createaccount": "Name for grant \"createaccount\".\n{{Related|Grant}}",
+       "grant-createeditmovepage": "Name for grant \"createeditmovepage\".\n{{Related|Grant}}",
+       "grant-delete": "Name for grant \"delete\".\n{{Related|Grant}}",
+       "grant-editinterface": "Name for grant \"editinterface\".\n\n\"JS\" stands for \"JavaScript\".\n{{Related|Grant}}",
+       "grant-editmycssjs": "Name for grant \"editmycssjs\".\n\n\"JS\" stands for \"JavaScript\".\n{{Related|Grant}}",
+       "grant-editmyoptions": "Name for grant \"editmyoptions\".\n{{Related|Grant}}",
+       "grant-editmywatchlist": "Name for grant \"editmywatchlist\".\n{{Related|Grant}}\n{{Identical|Edit your watchlist}}",
+       "grant-editpage": "Name for grant \"editpage\".\n{{Related|Grant}}",
+       "grant-editprotected": "Name for grant \"editprotected\".\n{{Related|Grant}}",
+       "grant-highvolume": "Name for grant \"highvolume\".\n{{Related|Grant}}",
+       "grant-oversight": "Name for grant \"oversight\".\n{{Related|Grant}}",
+       "grant-patrol": "Name for grant \"patrol\".\n{{Related|Grant}}",
+       "grant-privateinfo": "Name for grant \"privateinfo\".\n{{Related|Grant}}",
+       "grant-protect": "Name for grant \"protect\".\n{{Related|Grant}}",
+       "grant-rollback": "Name for grant \"rollback\".\n{{Related|Grant}}",
+       "grant-sendemail": "Name for grant \"sendemail\".\n{{Related|Grant}}",
+       "grant-uploadeditmovefile": "Name for grant \"uploadeditmovefile\".\n{{Related|Grant}}",
+       "grant-uploadfile": "Name for grant \"uploadfile\".\n{{Related|Grant}}\n{{Identical|Upload new file}}",
+       "grant-basic": "Name for grant \"basic\".\n{{Related|Grant}}",
+       "grant-viewdeleted": "Name for grant \"viewdeleted\".\n{{Related|Grant}}",
+       "grant-viewmywatchlist": "Name for grant \"viewmywatchlist\".\n{{Related|Grant}}\n{{Identical|View your watchlist}}",
        "newuserlogpage": "{{doc-logpage}}\n\nPart of the \"Newuserlog\" extension. It is both the title of [[Special:Log/newusers]] and the link you can see in [[Special:RecentChanges]].",
        "newuserlogpagetext": "Part of the \"Newuserlog\" extension. It is the description you can see on [[Special:Log/newusers]].",
        "rightslog": "{{doc-logpage}}\n\nIn [[Special:Log]]",
        "action-applychangetags": "{{doc-action|applychangetags}}",
        "action-changetags": "{{doc-action|changetags}}",
        "action-deletechangetags": "{{doc-action|deletechangetags}}",
+       "action-purge": "{{doc-action|purge}}",
        "nchanges": "Appears on enhanced watchlist and recent changes when page has more than one change on given date, linking to a diff of the changes.\n\nParameters:\n* $1 - the number of changes on that day (2 or more)\nThree messages are shown side-by-side: ({{msg-mw|Nchanges}} | {{msg-mw|Enhancedrc-since-last-visit}} | {{msg-mw|Enhancedrc-history}}).",
        "enhancedrc-since-last-visit": "Appears on enhanced watchlist and recent changes when page has more than one change on given date and at least one that the user hasn't seen yet, linking to a diff of the unviewed changes.\n\nParameters:\n* $1 - the number of unviewed changes (1 or more)\nThree messages are shown side-by-side: ({{msg-mw|nchanges}} | {{msg-mw|enhancedrc-since-last-visit}} | {{msg-mw|enhancedrc-history}}).",
        "enhancedrc-history": "Appears on enhanced watchlist and recent changes when page has more than one change on given date, linking to its history.\n\nThis is the same as {{msg-mw|hist}}, but not abbreviated.\n\nThree messages are shown side-by-side: ({{msg-mw|nchanges}} | {{msg-mw|enhancedrc-since-last-visit}} | {{msg-mw|enhancedrc-history}}).\n{{Identical|History}}",
        "file-thumbnail-no": "Error message at [[Special:Upload]]. Parameters:\n* $1 - String (e.g. \"180px-\")",
        "fileexists-forbidden": "{{doc-important|''thumb'' and ''center'' are magic words. Leave it untranslated!}}\nParameters:\n* $1 - name of the existing file",
        "fileexists-shared-forbidden": "{{doc-important|''thumb'' and ''center'' are magic words. Leave it untranslated!}}\nError message at [[Special:Upload]].\nParameters:\n* $1 - name of the existing file",
+       "fileexists-no-change": "Used in [[Special:Upload]] to warn when the upload is an exact duplicate of the current version of the file.\nParameters:\n* $1 - name of the existing file",
+       "fileexists-duplicate-version": "Used in [[Special:Upload]] to warn when the upload is an exact duplicate of (an) older version(s) of the file\nParameters:\n* $1 - name of the existing file\n* $2 - Amount of matching older versions (PLURAL support)",
        "file-exists-duplicate": "Used as warning in [[Special:Upload]].\nThis message is followed by the gallery of the duplicate files.\n\nParameters:\n* $1 - number of duplicate files",
        "file-deleted-duplicate": "Used in [[Special:Upload]. Parameters:\n* $1 - page title of the file\n\nSee also:\n* {{msg-mw|file-deleted-duplicate-notitle}}",
        "file-deleted-duplicate-notitle": "Used in [[Special:Upload]] when the title of the deleted duplicate is not available.\n\nSee also:\n* {{msg-mw|file-deleted-duplicate}}",
        "uploadstash-errclear": "Used as error message in [[Special:UploadStash]].",
        "uploadstash-refresh": "Used as link text in [[Special:UploadStash]].",
        "uploadstash-thumbnail": "Used as link text in [[Special:UploadStash]].",
+       "uploadstash-exception": "Error message shown when an action related to the upload stash fails unexpectedly.\n\nParameters:\n* $1 - exception name, e.g. 'UploadStashFileNotFoundException'\n* $2 - exceptions details (always in English), e.g. 'cannot find path, or not a plain file'",
        "invalid-chunk-offset": "Error that can happen if chunks get uploaded out of order.\nAs a result of this error, clients can continue from an offset provided or restart the upload.\nUsed on [[Special:UploadWizard]].",
        "img-auth-accessdenied": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Access Denied\n{{Identical|Access denied}}",
        "img-auth-nopathinfo": "[[mw:Manual:Image Authorization|Manual:Image Authorization]]: Missing PATH_INFO - see english description\n{{Doc-important|This is plain text. Do not use any wiki syntax.}}",
        "filerevert-submit": "{{Identical|Revert}}",
        "filerevert-success": "Message displayed when you succeed in reverting a version of a file.\n* $1 is the name of the media\n* $2 is a date\n* $3 is a time\n* $4 is an URL and must follow square bracket: [$4\n{{Identical|Revert}}",
        "filerevert-badversion": "Used as error message.",
+       "filerevert-identical": "Used as error message.",
        "filedelete": "Used as page title. Parameters:\n* $1 - file title\nSee also:\n* {{msg-mw|Filedelete-intro}}",
        "filedelete-legend": "Used as fieldset label in the \"Delete file\" form.\n{{Identical|Delete file}}",
        "filedelete-intro": "Used as introduction for FileDelete form. Parameters:\n* $1 - page title for file\nSee also:\n* {{msg-mw|Filedelete|page title}}",
        "apisandbox-jsonly": "Displayed as an error message if the browser does not have JavaScript enabled.",
        "apisandbox-api-disabled": "Displayed as an error message if the API is disabled on this site.",
        "apisandbox-intro": "Displayed (from JavaScript) as a header on [[Special:ApiSandbox]].",
-       "apisandbox-fullscreen": "JavaScript button label for enabling full-page mode.",
+       "apisandbox-fullscreen": "JavaScript button label for enabling full-page mode.\n\nSee https://phabricator.wikimedia.org/T129632#2465838 for details.",
        "apisandbox-fullscreen-tooltip": "Tooltip for the {{msg-mw|apisandbox-fullscreen}} button.",
-       "apisandbox-unfullscreen": "JavaScript button label for disabling full-page mode.",
+       "apisandbox-unfullscreen": "JavaScript button label for disabling full-page mode.\n\nSee https://phabricator.wikimedia.org/T129632#2465838 for details.",
        "apisandbox-unfullscreen-tooltip": "Tooltip for the {{msg-mw|apisandbox-unfullscreen}} button.",
        "apisandbox-submit": "JavaScript button label for submitting the request.",
        "apisandbox-reset": "JavaScript button label for clearing the form.\n{{Identical|Clear}}",
        "watchlistanontext": "Shown on Special:Userlogin when user tries to access their watchlist before logging in",
        "watchnologin": "Used as error page title.\n\nThe error message for this title is:\n* {{msg-mw|Watchnologintext}}\n{{Identical|Not logged in}}",
        "addwatch": "Link to a dialog box, displayed at the end of the list of categories at the foot of each page.\n\nSee also:\n* {{msg-mw|Removewatch}}",
-       "addedwatchtext": "Explanation shown when clicking on the {{msg-mw|Watch}} tab. Parameters:\n* $1 - page title\nSee also:\n* {{msg-mw|Addedwatch}}",
+       "addedwatchtext": "Message shown after clicking on the {{msg-mw|Watch}} tab in a content namespace page. Parameters:\n* $1 - page title\nSee also:\n* {{msg-mw|Removedwatchtext}}\n* {{msg-mw|Addedwatchtext-talk}}",
+       "addedwatchtext-talk": "Message shown after clicking on the {{msg-mw|Watch}} tab in a talk namespace page. Parameters:\n* $1 - page title\nSee also:\n* {{msg-mw|Removedwatchtext-talk}}\n* {{msg-mw|Addedwatchtext}}",
        "addedwatchtext-short": "Explanation shown when watching item from [[Special:UnwatchedPages]].\n\nSee also:\n* {{msg-mw|Removedwatchtext-short}}\n* {{msg-mw|Addedwatchtext}}",
        "removewatch": "Link to a dialog box, displayed at the end of the list of categories at the foot of each page.\n\nSee also:\n* {{msg-mw|Addwatch}}",
-       "removedwatchtext": "After a page has been removed from a user's watchlist by clicking the {{msg-mw|Unwatch}} tab at the top of an article, this message appears just below the title of the article.\n\nParameters:\n* $1 - the title of the article\nSee also:\n* {{msg-mw|Removedwatch}}\n* {{msg-mw|Addedwatchtext}}",
+       "removedwatchtext": "Message shown after clicking on the {{msg-mw|Unwatch}} tab in a content namespace page. Parameters:\n* $1 - page title\nSee also:\n* {{msg-mw|Addedwatchtext}}\n* {{msg-mw|Removedwatchtext-talk}}",
+       "removedwatchtext-talk": "Message shown after clicking on the {{msg-mw|Unwatch}} tab in a talk namespace page. Parameters:\n* $1 - page title\nSee also:\n* {{msg-mw|Addedwatchtext-talk}}\n* {{msg-mw|Removedwatchtext}}",
        "removedwatchtext-short": "Explanation shown when unwatching item from [[Special:UnwatchedPages]].\n\nSee also:\n* {{msg-mw|Addedwatchtext-short}}.\n* {{msg-mw|Removedwatchtext}}.",
        "watch": "{{doc-actionlink}}\nName of the Watch tab. Should be in the imperative mood.\n\nSee also:\n* {{msg-mw|Watch}}\n* {{msg-mw|Accesskey-ca-watch}}\n* {{msg-mw|Tooltip-ca-watch}}",
        "watchthispage": "Used as link text.\n\nSee also:\n* {{msg-mw|Unwatchthispage|link text}}\n* {{msg-mw|Notanarticle|error message}}\n{{Identical|Watch this page}}",
        "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}}",
        "rollbacklinkcount-morethan": "{{doc-actionlink}}\nText of the rollback link when a greater number of edits is to be rolled back. See also {{msg-mw|rollbacklink}}.\n\nWhen the number of edits rolled back is smaller than [[mw:Special:MyLanguage/Manual:$wgShowRollbackEditCount|$wgShowRollbackEditCount]], {{msg-mw|rollbacklinkcount}} is used instead.\n\nParameters:\n* $1 - number of edits",
        "rollbackfailed": "{{Identical|Rollback}}",
-       "rollback-missingparam": "Used as error message rollback is accessed without the required parameters\n\nSee also:\n* {{msg-mw|Rollbackfailed}}",
+       "rollback-missingparam": "Used as error message that rollback is accessed without the required parameters\n\nSee also:\n* {{msg-mw|Rollbackfailed}}",
+       "rollback-missingrevision": "Used as error message that rollback failed to load revision data\n\nSee also:\n* {{msg-mw|Rollbackfailed}}",
        "cantrollback": "Used as error message when rollback fails due to there not being a valid revision to revert back to.\n\nSee also:\n* {{msg-mw|Notvisiblerev}}\n{{Identical|Revert}}\n{{Identical|Rollback}}",
        "alreadyrolled": "Appear when there's rollback and/or edit collision.\n\nRefers to:\n* {{msg-mw|Pipe-separator}}\n* {{msg-mw|Contribslink}}\nParameters:\n* $1 - the page to be rolled back\n* $2 - the editor to be rolled-back of that page\n* $3 - the editor that cause collision\n{{Identical|Rollback}}",
        "editcomment": "Only shown if there is an edit {{msg-mw|Summary}}. Parameters:\n* $1 - the edit summary",
        "revertpage": "Parameters:\n* $1 - username 1\n* $2 - username 2\n* $3 - (Optional) revision ID of the revision reverted to\n* $4 - (Optional) timestamp of the revision reverted to\n* $5 - (Optional) revision ID of the revision reverted from\n* $6 - (Optional) timestamp of the revision reverted from\nSee also:\n* {{msg-mw|Revertpage-nouser}}\n{{Identical|Revert}}",
        "revertpage-nouser": "This is a confirmation message a user sees after reverting, when the username of the version is hidden with RevisionDelete.\n\nIn other cases the message {{msg-mw|Revertpage}} is used.\n\nParameters:\n* $1 - username 1, can be used for GENDER\n* $2 - (Optional) username 2\n* $3 - (Optional) revision ID of the revision reverted to\n* $4 - (Optional) timestamp of the revision reverted to\n* $5 - (Optional) revision ID of the revision reverted from\n* $6 - (Optional) timestamp of the revision reverted from",
        "rollback-success": "This message shows up on screen after successful revert (generally visible only to admins). $1 describes user whose changes have been reverted, $2 describes user which produced version, which replaces reverted version.\n{{Identical|Revert}}\n{{Identical|Rollback}}",
-       "rollback-success-notify": "Notification shown after a successful revert.\n* $1 - User whose changes have been reverted\n* $2 - User that made the edit that was restored\n* $3 - Url to the diff of the rollback\nSee also:\n* {{msg-mw|showdiff}}\n{{Identical|rollback-success}}\n{{Format|jquerymsg}}",
+       "rollback-success-notify": "Notification shown after a successful revert.\n* $1 - User whose changes have been reverted\n* $2 - User that made the edit that was restored\n* $3 - Url to the diff of the rollback\nSee also:\n* {{msg-mw|showdiff}}\n{{related|rollback-success}}\n{{Format|jquerymsg}}",
        "sessionfailure-title": "Used as title of the error message {{msg-mw|Sessionfailure}}.",
        "sessionfailure": "Used as error message.\n\nThe title for this error message is {{msg-mw|Sessionfailure-title}}.",
        "changecontentmodel": "Title of the change content model special page",
        "undeletehistorynoadmin": "Used in [[Special:Undelete]].\n\nSee also:\n* {{msg-mw|Undeletehistory}}\n* {{msg-mw|Undeleterevdel}}",
        "undelete-revision": "Shown in \"View and restore deleted pages\" ([[Special:Undelete/$1]]).\nParameters:\n* $1 - deleted page name\n* $2 - (unused)\n* $3 - username (author of revision, not who deleted it)\n* $4 - date of the revision (localized)\n* $5 - time of the revision (localized)\nExample (in English):\n* Deleted revision of [[Main Page]] (as of 14 September 2013, at 08:17) by [[User:Username|Username]]:",
        "undeleterevision-missing": "Used as warning when undeleting the revision.",
+       "undeleterevision-duplicate-revid": "Used as warning when some revisions could not be undeleted due to <code>rev_id</code> collisions.  Parameters:\n* - Number of revisions that could not be restored for this reason.",
        "undelete-nodiff": "Used in [[Special:Undelete]].",
        "undeletebtn": "Shown on [[Special:Undelete]] as button caption and on [[Special:Log/delete|deletion log]] after each entry (for sysops).\n\n{{Identical|Restore}}",
        "undeletelink": "Display name of link to undelete a page used on [[Special:Log/delete]]\n\n{{Identical|View}}\n{{Identical|Restore}}",
        "sp-contributions-newbies-sub": "Note at the top of the page of results for a search on [[Special:Contributions]] where 'Show contributions for new accounts only' has been selected.",
        "sp-contributions-newbies-title": "The page title in your browser bar, but not the page title.\n\nSee also:\n* {{msg-mw|Sp-contributions-newbies-sub}}",
        "sp-contributions-blocklog": "Used as a display name for a link to the block log on for example [[Special:Contributions/Mediawiki default]]\n\nUsed as link title in [[Special:Contributions]] and in [[Special:DeletedContributions]].\n\nSee also:\n* {{msg-mw|Sp-contributions-talk}}\n* {{msg-mw|Change-blocklink}}\n* {{msg-mw|Unblocklink}}\n* {{msg-mw|Blocklink}}\n* {{msg-mw|Sp-contributions-uploads}}\n* {{msg-mw|Sp-contributions-logs}}\n* {{msg-mw|Sp-contributions-deleted}}\n* {{msg-mw|Sp-contributions-userrights}}\n{{Identical|Block log}}",
-       "sp-contributions-suppresslog": "Used as a display name for a link to log entries of suppressed edits made by that user.\n\nUsed as link title in [[Special:Contributions]] and in [[Special:DeletedContributions]].\n\nSee also {{msg-mw|sp-contributions-deleted}}, {{msg-mw|sp-deletedcontributions-contribs}}, {{msg-mw|contributions}}, {{msg-mw|deletedcontributions-title}}.",
-       "sp-contributions-deleted": "This is a link anchor used in [[Special:Contributions]]/''name'', when user viewing the page has the right to delete pages, or to restore deleted pages.\n\nUsed as link title in [[Special:Contributions]].\n\nSee also:\n* {{msg-mw|Sp-contributions-talk}}\n* {{msg-mw|Change-blocklink}}\n* {{msg-mw|Unblocklink}}\n* {{msg-mw|Blocklink}}\n* {{msg-mw|Sp-contributions-blocklog}}\n* {{msg-mw|Sp-contributions-uploads}}\n* {{msg-mw|Sp-contributions-logs}}\n* {{msg-mw|Sp-contributions-userrights}}",
+       "sp-contributions-suppresslog": "Used as a display name for a link to log entries of suppressed edits made by that user.\n\nUsed as link title in [[Special:Contributions]] and in [[Special:DeletedContributions]]. Parameters:\n* $1 is a plain text username used for GENDER.\nSee also {{msg-mw|sp-contributions-deleted}}, {{msg-mw|sp-deletedcontributions-contribs}}, {{msg-mw|contributions}}, {{msg-mw|deletedcontributions-title}}.",
+       "sp-contributions-deleted": "This is a link anchor used in [[Special:Contributions]]/''name'', when user viewing the page has the right to delete pages, or to restore deleted pages.\n\nUsed as link title in [[Special:Contributions]]. Parameters:\n* $1 is a plain text username used for GENDER.\nSee also:\n* {{msg-mw|Sp-contributions-talk}}\n* {{msg-mw|Change-blocklink}}\n* {{msg-mw|Unblocklink}}\n* {{msg-mw|Blocklink}}\n* {{msg-mw|Sp-contributions-blocklog}}\n* {{msg-mw|Sp-contributions-uploads}}\n* {{msg-mw|Sp-contributions-logs}}\n* {{msg-mw|Sp-contributions-userrights}}",
        "sp-contributions-uploads": "Used as link title in [[Special:Contributions]] and in [[Special:DeletedContributions]].\n\nSee also:\n* {{msg-mw|Sp-contributions-talk}}\n* {{msg-mw|Change-blocklink}}\n* {{msg-mw|Unblocklink}}\n* {{msg-mw|Blocklink}}\n* {{msg-mw|Sp-contributions-blocklog}}\n* {{msg-mw|Sp-contributions-logs}}\n* {{msg-mw|Sp-contributions-deleted}}\n* {{msg-mw|Sp-contributions-userrights}}\n{{Identical|Upload}}",
        "sp-contributions-logs": "Appears as an action link in the header of the Special:Contributions/''Username'' pages (e.g. \"For Somebody (talk | block log | logs)\").\n\nUsed as link title in [[Special:Contributions]] and in [[Special:DeletedContributions]].\n\nSee also:\n* {{msg-mw|Sp-contributions-talk}}\n* {{msg-mw|Change-blocklink}}\n* {{msg-mw|Unblocklink}}\n* {{msg-mw|Blocklink}}\n* {{msg-mw|Sp-contributions-blocklog}}\n* {{msg-mw|Sp-contributions-uploads}}\n* {{msg-mw|Sp-contributions-deleted}}\n* {{msg-mw|Sp-contributions-userrights}}\n{{Identical|Log}}",
        "sp-contributions-talk": "This is a link anchor used in the [[Special:Contributions]]/''usernamename'' pages.\nThe link appears in a list of similar ones separated by {{msg-mw|pipe-separator}}, e.g. like this:<br />\n( talk | block log | logs | deleted contributions | rights management )\n\nUsed as link title in [[Special:Contributions]] and in [[Special:DeletedContributions]].\n\nSee also:\n* {{msg-mw|change-blocklink}}\n* {{msg-mw|unblocklink}}\n* {{msg-mw|blocklink}}\n* {{msg-mw|sp-contributions-blocklog}}\n* {{msg-mw|sp-contributions-uploads}}\n* {{msg-mw|sp-contributions-logs}}\n* {{msg-mw|sp-contributions-deleted}}\n* {{msg-mw|sp-contributions-userrights}}\n{{Identical|Talk}}",
        "pageinfo-article-id": "The numeric identifier of the page.\n{{Identical|Page ID}}",
        "pageinfo-language": "Language in which the page content is written.",
        "pageinfo-content-model": "The model in which the page content is written.\n\nUsed as label at [{{fullurl:Main Page|action=info}} action=info]. Followed by one of the following messages:\n* {{msg-mw|Content-model-wikitext}}\n* {{msg-mw|Content-model-javascript}}\n* {{msg-mw|Content-model-css}}\n* {{msg-mw|Content-model-text}}",
+       "pageinfo-content-model-change": "Link text for a link to Special:ChangeContentModel. The link will be wrapped in parenthesis.\n{{Identical|Change}}",
        "pageinfo-robot-policy": "The search engine status of the page.\n\nUsed as label. Followed by any one of the following messages:\n*{{msg-mw|Pageinfo-robot-index}}\n*{{msg-mw|Pageinfo-robot-noindex}}",
        "pageinfo-robot-index": "An indication that the page is indexable by search engines, that is listed in their search results.\n\nPreceded by the label {{msg-mw|Pageinfo-robot-policy}}.\n{{Identical|Allowed}}",
        "pageinfo-robot-noindex": "An indication that the page is not indexable (that is, is not listed on the results page of a search engine).\n\nPreceded by the label {{msg-mw|Pageinfo-robot-policy}}.",
        "metadata-fields": "{{doc-important|Do not translate list items, only translate the text! So leave \"<code>* make</code>\" and the other items exactly as they are.}}\nThe sentences are for explanation only and are not shown to the user.",
        "metadata-langitem": "{{optional}}\nThis is used for constructing the list of translations when a metadata property is translated into multiple languages.\n\nParameters:\n* $1 - the value of the property (in one language)\n* $2 - the language name that this translation is for (or language code if language name cannot be determined)\n* $3 - (Unused) the language code",
        "metadata-langitem-default": "{{optional}}\nSimilar to \"metadata-langitem\" but for the case where a multilingual property has a default specified that does not specify what language the default is in. $1 is the value of the property.",
-       "exif-imagewidth": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n{{Identical|Width}}",
-       "exif-imagelength": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n{{Identical|Height}}",
+       "exif-imagewidth": "{{exif-qqq}}\n{{Identical|Width}}",
+       "exif-imagelength": "{{exif-qqq}}\n{{Identical|Height}}",
        "exif-bitspersample": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
-       "exif-compression": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThis field labels what the compression of the image is. It is commonly seen in Tiff images. It uses messages like {{msg-mw|exif-compression-1}} for the value. [[w:TIFF#TIFF_Compression_Tag]] has information about this field.\n{{Related|Exif-compression}}",
+       "exif-compression": "{{exif-qqq}}\n\nThis field labels what the compression of the image is. It is commonly seen in Tiff images. It uses messages like {{msg-mw|exif-compression-1}} for the value. [[w:TIFF#TIFF Compression Tag]] has information about this field.\n\n{{Related|Exif-compression}}",
        "exif-photometricinterpretation": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
-       "exif-orientation": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nFor specific information on the orientation tag, see http://sylvana.net/jpegcrop/exif_orientation.html\n{{Related|Exif-orientation}}",
+       "exif-orientation": "{{exif-qqq}}\n\nFor specific information on the orientation tag, see http://sylvana.net/jpegcrop/exif_orientation.html\n\n{{Related|Exif-orientation}}",
        "exif-samplesperpixel": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
-       "exif-planarconfiguration": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nSee also:\n* {{msg-mw|Exif-planarconfiguration}}\n* {{msg-mw|Exif-planarconfiguration-1}}\n* {{msg-mw|Exif-planarconfiguration-2}}",
+       "exif-planarconfiguration": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-planarconfiguration}}\n* {{msg-mw|Exif-planarconfiguration-1}}\n* {{msg-mw|Exif-planarconfiguration-2}}",
        "exif-ycbcrsubsampling": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
-       "exif-ycbcrpositioning": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n{{Related|Exif-ycbcrpositioning}}",
-       "exif-xresolution": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThis is the horizontal resolution in either dots/inch or dots/cm.",
-       "exif-yresolution": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThis is the vertical resolution in either dots/inch or dots/cm.",
+       "exif-ycbcrpositioning": "{{exif-qqq}}\n{{Related|Exif-ycbcrpositioning}}",
+       "exif-xresolution": "{{exif-qqq}}\n\nThis is the horizontal resolution in either dots/inch or dots/cm.",
+       "exif-yresolution": "{{exif-qqq}}\n\nThis is the vertical resolution in either dots/inch or dots/cm.",
        "exif-stripoffsets": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
        "exif-rowsperstrip": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
        "exif-stripbytecounts": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
        "exif-primarychromaticities": "The chromaticity of the three primary colours of the image. Normally this tag is not necessary, since colour space is specified in the colour space information tag. This should probably be translated it as \"Chromaticity of primary colours\".\n\nExif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
        "exif-ycbcrcoefficients": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
        "exif-referenceblackwhite": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
-       "exif-datetime": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nNote, this message is also used for the XMP:ModifyDate property in XMP metadata. See page 35 of http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart1.pdf\n\nDatetime is the time that the digital file was last changed.",
-       "exif-imagedescription": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThis property is the description or caption of the image. It is used for the exif ImageDescription property, the dc:description property in XMP (see http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart1.pdf ), and the iptc-iim 2:120 caption/abstract property ( http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf ).\n\nWhen an image has multiple differing descriptions, mediawiki follows the MWG guidelines when deciding which to show (Which typically means Exif takes precedence).",
-       "exif-make": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThe Manufacturer of the digital camera (or scanner) that took the photo.",
-       "exif-model": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThe model of camera (or scanner) used to take the picture.",
-       "exif-software": "Short for \"The software which was used to create or modify this image\".\n\nThe property can come from the Exif Software tag, PNG software chunk, iptc-iim 2:65 Software field, or XMP's xmp:CreatorTool field.\n\nExif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
-       "exif-artist": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThis message labels the author or artist of the work. Usually this means who took the photograph, or who drew the picture. The corresponding value field most commonly contains a single author, however it can contain an ordered (or unordered depending on which metadata standard is used to store the information) list of authors. Sometimes the persons position is prefixed before their name such as \"Photographer, John Smith\". The exif standard recommends multiple authors be specified by \"position, Author 1; position for author 2, Author 2's name\" however this doesn't seem to happen in practice very often. If multiple authors are specified using a non-exif standard, then a billeted (or numbered) list is used.\n\nThis property can be specified by exif Artist tag, XMP's tiff:Artist, XMP's dc:creator, iptc-iim's 2:80 byline, PNG's author textual chunk, PNG's (unofficial) artist textual chunk. XMP's photoshop:AuthorsPosition and iptc 2:85 byline-title can also affect display of this property.\n{{Identical|Author}}",
+       "exif-datetime": "{{exif-qqq}}\n\nNote: this message is also used for the XMP:ModifyDate property in XMP metadata. See page 35 of http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart1.pdf\n\nDatetime is the time that the digital file was last changed.",
+       "exif-imagedescription": "{{exif-qqq}}\n\nThis property is the description or caption of the image. It is used for the exif ImageDescription property, the dc:description property in XMP (see http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart1.pdf ), and the iptc-iim 2:120 caption/abstract property ( http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf ).\n\nWhen an image has multiple differing descriptions, mediawiki follows the MWG guidelines when deciding which to show (Which typically means Exif takes precedence).",
+       "exif-make": "{{exif-qqq}}\n\nThe Manufacturer of the digital camera (or scanner) that took the photo.",
+       "exif-model": "{{exif-qqq}}\n\nThis tag specifies the model of camera (or scanner) used to take the picture.",
+       "exif-software": "{{exif-qqq}}\n\nThis tag is short for \"The software which was used to create or modify this image\".\n\nThe property can come from the Exif Software tag, PNG software chunk, iptc-iim 2:65 Software field, or XMP's xmp:CreatorTool field.",
+       "exif-artist": "{{exif-qqq}}\n\nThis message labels the author or artist of the work. Usually this means who took the photograph, or who drew the picture. The corresponding value field most commonly contains a single author, however it can contain an ordered (or unordered depending on which metadata standard is used to store the information) list of authors. Sometimes the persons position is prefixed before their name such as \"Photographer, John Smith\". The exif standard recommends multiple authors be specified by \"position, Author 1; position for author 2, Author 2's name\" however this doesn't seem to happen in practice very often. If multiple authors are specified using a non-exif standard, then a billeted (or numbered) list is used.\n\nThis property can be specified by exif Artist tag, XMP's tiff:Artist, XMP's dc:creator, iptc-iim's 2:80 byline, PNG's author textual chunk, PNG's (unofficial) artist textual chunk. XMP's photoshop:AuthorsPosition and iptc 2:85 byline-title can also affect display of this property.\n\n{{Identical|Author}}",
        "exif-copyright": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nLabel for information contained in exif Copyright tag, XMP dc:rights, IPTC-iim 2:116, or PNG copyright textual chunk.\n\nTypically the copyright statement for the photograph/drawing/video (such as ''(c) 2010 John Smith. Released under GFDL''). Sometimes contains license information. See also {{msg-mw|exif-copyrightowner}}",
-       "exif-exifversion": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nVersion of exif standard photo uses. Typically this is 2.22",
+       "exif-exifversion": "{{exif-qqq}}\n\nVersion of exif standard photo uses. Typically this is 2.22",
        "exif-flashpixversion": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nVersion of flashpix used. Flashpix is a format used for storing some types of metadata in image. It is not as commonly used as EXIF, and mediawiki currently cannot read Flashpix data.",
-       "exif-colorspace": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThe colorspace of the photo. This tells the computer how to make the colours in the photo be more true to the original photo. Typical values for this are sRGB or uncalibrated. This only gives information on colour information given in the exif-colorspace property. However, colour information is often stored elsewhere in the photo.\n\nSee also:\n* {{msg-mw|Exif-colorspace}}\n* {{msg-mw|Exif-colorspace-1|optional}}\n* {{msg-mw|Exif-colorspace-65535}}",
-       "exif-componentsconfiguration": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThis contains how the information in the picture is stored. This is most commonly Y, Cr, Cb to specify luma, red, blue. RGB is also possible to specify Red, Green, Blue.\n{{Related|Exif-componentsconfiguration}}",
+       "exif-colorspace": "{{exif-qqq}}\n\nThe colorspace of the photo. This tells the computer how to make the colours in the photo be more true to the original photo. Typical values for this are sRGB or uncalibrated. This only gives information on colour information given in the exif-colorspace property. However, colour information is often stored elsewhere in the photo.\n\nSee also:\n* {{msg-mw|Exif-colorspace}}\n* {{msg-mw|Exif-colorspace-1|optional}}\n* {{msg-mw|Exif-colorspace-65535}}",
+       "exif-componentsconfiguration": "{{exif-qqq}}\n\nThis contains how the information in the picture is stored. This is most commonly Y, Cr, Cb to specify luma, red, blue. RGB is also possible to specify Red, Green, Blue.\n\n{{Related|Exif-componentsconfiguration}}",
        "exif-compressedbitsperpixel": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
-       "exif-pixelxdimension": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n{{Identical|Image width}}",
+       "exif-pixelxdimension": "{{exif-qqq}}\n{{Identical|Image width}}",
        "exif-pixelydimension": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n{{Identical|Image height}}",
-       "exif-usercomment": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nComments by user. Sometimes used like ImageDescription when the ImageDescription contained non-ascii characters. (Technically ImageDescription is supposed to contain ascii characters. In practice utf-8 is used in ImageDescription, so this field isn't used too much.)",
+       "exif-usercomment": "{{exif-qqq}}\n\nComments by user. Sometimes used like ImageDescription when the ImageDescription contained non-ascii characters. (Technically ImageDescription is supposed to contain ascii characters. In practice utf-8 is used in ImageDescription, so this field isn't used too much.)",
        "exif-relatedsoundfile": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nSome cameras offer the option to record an audio \"memo\" for the photo they just took. If the user did that, the name of the file is labelled with this message.",
        "exif-datetimeoriginal": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThe date and time when the original image data was generated. For example if it was a painting from 1773, scanned in to a computer in 2007, the datetimeoriginal would be 1773 and {{msg-mw|exif-datetimedigitized}} would have the 2007 date.",
        "exif-datetimedigitized": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThe date and time when the image was stored as digital data.",
-       "exif-subsectime": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\n'DateTime subseconds' shows the detail of the fraction of a second (1/100s) at which the file was changed, when the tag {{msg-mw|Exif-datetime}} is recorded to the whole second.",
-       "exif-subsectimeoriginal": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThis tag shows the detail of the fraction of a second (1/100s) at which the file data was originally generated, when the tag {{msg-mw|Exif-datetimeoriginal}} is recorded to the whole second.",
-       "exif-subsectimedigitized": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThis tag shows the detail of the fraction of a second (1/100s) at which the file was stored as digital data, when the tag {{msg-mw|Exif-datetimedigitized}} is recorded to the whole second.",
-       "exif-exposuretime": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThe exposure time. Number of (or fraction of) seconds the film was exposed to light. The value for this property is formatted using {{msg-mw|exif-exposuretime-format}}",
-       "exif-exposuretime-format": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nParameters:\n* $1 - the exposure time written as a fraction of a second, for example 1/640 of a second\n* $2 - the exposure time written as a decimal, for example 0.0015625\n\n'sec' is the abbreviation used in English for the unit of time 'second'.",
-       "exif-fnumber": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThe [[w:F_number|F number]] is the relative aperture of the camera.",
+       "exif-subsectime": "{{exif-qqq}}\n\n'DateTime subseconds' shows the detail of the fraction of a second (1/100s) at which the file was changed, when the tag {{msg-mw|Exif-datetime}} is recorded to the whole second.",
+       "exif-subsectimeoriginal": "{{exif-qqq}}\n\nThis tag shows the detail of the fraction of a second (1/100s) at which the file data was originally generated, when the tag {{msg-mw|Exif-datetimeoriginal}} is recorded to the whole second.",
+       "exif-subsectimedigitized": "{{exif-qqq}}\n\nThis tag shows the detail of the fraction of a second (1/100s) at which the file was stored as digital data, when the tag {{msg-mw|Exif-datetimedigitized}} is recorded to the whole second.",
+       "exif-exposuretime": "{{exif-qqq}}\n\nThe exposure time. Number of (or fraction of) seconds the film was exposed to light. The value for this property is formatted using {{msg-mw|exif-exposuretime-format}}",
+       "exif-exposuretime-format": "{{exif-qqq}}\n\nParameters:\n* $1 - the exposure time written as a fraction of a second, for example 1/640 of a second\n* $2 - the exposure time written as a decimal, for example 0.0015625\n\n'sec' is the abbreviation used in English for the unit of time 'second'.",
+       "exif-fnumber": "{{exif-qqq}}\n\nThe [[w:F number|F number]] is the relative aperture of the camera.",
        "exif-fnumber-format": "{{optional}}\nExif is a format for storing metadata in image files. See this [http://en.wikipedia.org/wiki/Exchangeable_image_file_format Wikipedia article] and the example at the bottom of [http://commons.wikimedia.org/wiki/File:Phalacrocorax-auritus-020.jpg this page on Commons]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nParameters:\n* $1 - a number\nNote:\n* f is the abbreviation used in English for \"f-number\".",
-       "exif-exposureprogram": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nHow the camera figured out what exposure to use. (If it was manually set, if its optimizing for fast shutter speed, etc).\n{{Related|Exif-exposureprogram}}",
+       "exif-exposureprogram": "{{exif-qqq}}\n\nHow the camera figured out what exposure to use. (If it was manually set, if its optimizing for fast shutter speed, etc).\n\n{{Related|Exif-exposureprogram}}",
        "exif-spectralsensitivity": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nHow sensitive each channel (colour) of the photo is to light. This tag is almost never used.",
        "exif-isospeedratings": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThe iso speed of the film used in the camera. This is basically a measure of how sensitive the film in the camera is to light.",
        "exif-shutterspeedvalue": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\n[[w:Shutter_speed|Shutter speed]] is the time that the camera shutter is open.\n\nThis is the shutter speed measured in APEX units (negative base 2 log of shutter speed in seconds). See {{msg-mw|exif-exposuretime}} for this property in more traditional units of seconds.",
-       "exif-aperturevalue": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThe [[w:Aperture|aperture]] of a camera is the hole through which light shines. This message can be translated 'Aperture width'. Note, this is measured in APEX units which is 2*log<sub>2</sub>(f-number) . See {{msg-mw|exif-fnumber}} for this value in more traditional units.",
+       "exif-aperturevalue": "{{exif-qqq}}\n\nThe [[w:Aperture|aperture]] of a camera is the hole through which light shines. This message can be translated 'Aperture width'. Note, this is measured in APEX units which is 2*log<sub>2</sub>(f-number) . See {{msg-mw|exif-fnumber}} for this value in more traditional units.",
        "exif-brightnessvalue": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nHow intense the illumination of the scene photographed is. Measured in APEX brightness units. See Annex C of Exif standard for details on the measurement system in use.",
        "exif-exposurebiasvalue": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nAnother term for [[w:Exposure_bias|'exposure bias']] is 'exposure compensation'.",
        "exif-maxaperturevalue": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThe 'land' in a camera refers possibly to the inner surface of the barrel of the lens. An alternative phrasing for this message could perhaps be 'maximum width of the land aperture'.",
        "exif-subjectdistance": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThe subject of a photograph is the person or thing on which the camera focuses. 'Subject distance' is the distance to the subject given in meters.",
-       "exif-meteringmode": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nSee [[w:Metering_mode|Wikipedia article]] on metering mode.\n{{Related|Exif-meteringmode}}",
-       "exif-lightsource": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n{{Related|Exif-lightsource}}",
-       "exif-flash": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nSee this [[w:en:Flash_(photography)|Wikipedia article]] for an explanation of the term.\n{{Related|Exif-flash}}\n{{Identical|Flash}}",
+       "exif-meteringmode": "{{exif-qqq}}\n\nSee [[w:Metering mode|Wikipedia article]] on metering mode.\n\n{{Related|Exif-meteringmode}}",
+       "exif-lightsource": "{{exif-qqq}}\n\n{{Related|Exif-lightsource}}",
+       "exif-flash": "{{exif-qqq}}\n\nSee this [[w:en:Flash (photography)|Wikipedia article]] for an explanation of the term.\n\n{{Related|Exif-flash}}\n{{Identical|Flash}}",
        "exif-focallength": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nSee this [[w:en:Focal_length_(photography)|Wikipedia article]] for an explanation of the term.",
        "exif-focallength-format": "{{optional}}\nExif is a format for storing metadata in image files. See this [http://en.wikipedia.org/wiki/Exchangeable_image_file_format Wikipedia article] and the example at the bottom of [http://commons.wikimedia.org/wiki/File:Phalacrocorax-auritus-020.jpg this page on Commons]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nParameters:\n* $1 - a number\nNote:\n* mm is the abbreviation used in English for the unit of measurement of length \"millimeter\".",
-       "exif-subjectarea": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nThis exif property contains the position of the main subject. The first two numbers is the position of the subject in the picture in pixels from the upper left corner. If a third number is specified, it is a circle centred at the first two numbers. If four numbers are specified, the first two are coordinates of the centre of the subject as before, the third is the width of the rectangle, and the fourth is the height of the rectangle. It is rare for a photo to use this tag.",
+       "exif-subjectarea": "{{exif-qqq}}\n\nThis exif property contains the position of the main subject. The first two numbers is the position of the subject in the picture in pixels from the upper left corner. If a third number is specified, it is a circle centred at the first two numbers. If four numbers are specified, the first two are coordinates of the centre of the subject as before, the third is the width of the rectangle, and the fourth is the height of the rectangle. It is rare for a photo to use this tag.",
        "exif-flashenergy": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nHow bright the flash is in beam candle power seconds.",
        "exif-focalplanexresolution": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nIndicates the number of pixels in the image width (X) direction per FocalPlaneResolutionUnit on the camera focal plane.",
        "exif-focalplaneyresolution": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
-       "exif-focalplaneresolutionunit": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nSee also:\n* {{msg-mw|Exif-focalplaneresolutionunit-2}}",
+       "exif-focalplaneresolutionunit": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-focalplaneresolutionunit-2}}",
        "exif-subjectlocation": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nSame as {{msg-mw|exif-subjectarea}} but only ever has two numbers as a value.",
        "exif-exposureindex": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].",
-       "exif-sensingmethod": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n{{Related|Exif-sensingmethod}}",
+       "exif-sensingmethod": "{{exif-qqq}}\n{{Related|Exif-sensingmethod}}",
        "exif-filesource": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nDetermines if the image was recorded by a digital camera adhering to DSC standard (which is almost all digital cameras).",
-       "exif-scenetype": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nIf the image is directly photographed (taken by a digital camera).\n\nSee also:\n* {{msg-mw|Exif-scenetype}}\n* {{msg-mw|Exif-scenetype-1}}",
-       "exif-customrendered": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nSee also Wikipedia on [[w:Image_processing|image processing]].\n\nSee also:\n* {{msg-mw|Exif-customrendered}}\n* {{msg-mw|Exif-customrendered-0}}\n* {{msg-mw|Exif-customrendered-1}}",
-       "exif-exposuremode": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nSee also Wikipedia on [[w:en:Exposure_(photography)|exposure in photography]]. This tag shows if the photo's exposure was manually set or automatically determined.\n{{Related|Exif-exposuremode}}",
-       "exif-whitebalance": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nSee also Wikipedia on [[w:Color_balance|color balance]].\n\nSee also:\n* {{msg-mw|Exif-whitebalance}}\n* {{msg-mw|Exif-whitebalance-0}}\n* {{msg-mw|Exif-whitebalance-1}}",
+       "exif-scenetype": "{{exif-qqq}}\n\nThis tag indicates whether the image is directly photographed (taken by a digital camera).\n\nSee also:\n* {{msg-mw|Exif-scenetype}}\n* {{msg-mw|Exif-scenetype-1}}",
+       "exif-customrendered": "{{exif-qqq}}\n\nSee also the Wikipedia article on [[w:Image processing|image processing]].\n\nSee also:\n* {{msg-mw|Exif-customrendered}}\n* {{msg-mw|Exif-customrendered-0}}\n* {{msg-mw|Exif-customrendered-1}}",
+       "exif-exposuremode": "{{exif-qqq}}\n\nThis tag shows if the photo's exposure was manually set or automatically determined. See the Wikipedia article on [[w:en:Exposure (photography)|exposure in photography]] for more details.\n\n{{Related|Exif-exposuremode}}",
+       "exif-whitebalance": "{{exif-qqq}}\n\nSee also the Wikipedia article on [[w:Color balance|color balance]].\n\nSee also:\n* {{msg-mw|Exif-whitebalance}}\n* {{msg-mw|Exif-whitebalance-0}}\n* {{msg-mw|Exif-whitebalance-1}}",
        "exif-digitalzoomratio": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nSee also Wikipedia on [[w:Digital_zoom|digital zoom]].",
        "exif-focallengthin35mmfilm": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\nSee also Wikipedia on [[w:Focal_length#In_photography|focal length]].",
-       "exif-scenecapturetype": "{{Related|Exif-scenecapturetype}}",
-       "exif-gaincontrol": "Gain amplifies the signal off of the image sensor. Gain turns the brightness level up or down.\n{{Related|Exif-gaincontrol}}",
-       "exif-contrast": "{{Related|Exif-contrast}}",
-       "exif-saturation": "{{Related|Exif-saturation}}",
-       "exif-sharpness": "{{Related|Exif-sharpness}}",
+       "exif-scenecapturetype": "{{exif-qqq}}\n{{Related|Exif-scenecapturetype}}",
+       "exif-gaincontrol": "{{exif-qqq}}\n\nGain amplifies the signal off of the image sensor. Gain turns the brightness level up or down.\n\n{{Related|Exif-gaincontrol}}",
+       "exif-contrast": "{{exif-qqq}}\n{{Related|Exif-contrast}}",
+       "exif-saturation": "{{exif-qqq}}\n{{Related|Exif-saturation}}",
+       "exif-sharpness": "{{exif-qqq}}\n{{Related|Exif-sharpness}}",
        "exif-devicesettingdescription": "The section header shown above device settings extracted from a file's EXIF data on the file's page.",
-       "exif-subjectdistancerange": "{{Related|Exif-subjectdistancerange}}",
+       "exif-subjectdistancerange": "{{exif-qqq}}\n{{Related|Exif-subjectdistancerange}}",
        "exif-imageuniqueid": "A unique identifier for the image in the form of a 128-bit hexadecimal string. See http://www.exif.org/Exif2-2.PDF for details on exif properties.",
        "exif-gpsversionid": "Version of the GPS IFD used to store location information. This is usually 2.2.0.0",
-       "exif-gpslatituderef": "In older versions of mediawiki this referred to if the latitude was North or South. This is no longer used in modern versions of mediawiki except for when using a foreign image repository that is using an older version of mediawiki since the information is now contained in {{msg-mw|exif-gpslatitude}}.\n{{Related|Exif-gpslatitude}}",
-       "exif-gpslatitude": "The latitude of the location from where the picture was taken from.\n{{Related|Exif-gpslatitude}}\n{{Identical|Latitude}}",
-       "exif-gpslongituderef": "Same as {{msg-mw|exif-gpslatituderef}} but for longitude.\n\n{{Related|Exif-gpslatitude}}",
-       "exif-gpslongitude": "The longitude of the location from where the picture was taken from.\n{{Related|Exif-gpslatitude}}\n{{Identical|Longitude}}",
+       "exif-gpslatituderef": "{{exif-qqq}}\n\nIn older versions of mediawiki this referred to if the latitude was North or South. This is no longer used in modern versions of mediawiki except for when using a foreign image repository that is using an older version of mediawiki since the information is now contained in {{msg-mw|exif-gpslatitude}}.\n\n{{Related|Exif-gpslatitude}}",
+       "exif-gpslatitude": "{{exif-qqq}}\n\nThe latitude of the location from where the picture was taken from.\n\n{{Related|Exif-gpslatitude}}\n{{Identical|Latitude}}",
+       "exif-gpslongituderef": "{{exif-qqq}}\n\nSame as {{msg-mw|exif-gpslatituderef}} but for longitude.\n\n{{Related|Exif-gpslatitude}}",
+       "exif-gpslongitude": "{{exif-qqq}}\n\nThe longitude of the location from where the picture was taken from.\n\n{{Related|Exif-gpslatitude}}\n{{Identical|Longitude}}",
        "exif-gpsaltituderef": "No longer used except for when using foreign image repository with old version of mediawiki. 0 for above sea level, 1 for below sea level.",
        "exif-gpsaltitude": "Altitude in meters that the image was taken at.",
        "exif-gpstimestamp": "Time (does not include date) that GPS measurement was taken, in UTC. Since often this is at the same time as photo was taken, this is sometimes more reliable than {{msg-mw|exif-datetimeoriginal}}.",
        "exif-gpssatellites": "Label for EXIF information. Indicates the GPS satellites used for measurements.",
-       "exif-gpsstatus": "See also:\n* {{msg-mw|Exif-gpsstatus-a}}\n* {{msg-mw|Exif-gpsstatus-v}}",
-       "exif-gpsmeasuremode": "Is the measurement 2D (latitude and longitude) or 3D (latitude, longitude, and altitude).\n\nSee also:\n* {{msg-mw|Exif-gpsmeasuremode-2}}\n* {{msg-mw|Exif-gpsmeasuremode-3}}",
-       "exif-gpsdop": "How accurate the GPS information is. See http://en.wikipedia.org/wiki/Dilution_of_precision_(GPS)\n{{Related|Exif-gpsdop}}",
-       "exif-gpsspeedref": "{{Related|Exif-gpsspeed}}",
-       "exif-gpsspeed": "{{Related|Exif-gpsspeed}}",
-       "exif-gpstrackref": "See also:\n* {{msg-mw|Exif-gpstrack}}\n* {{msg-mw|Exif-gpsimgdirection}}\n* {{msg-mw|Exif-gpsimgdirectionref}}\n* {{msg-mw|Exif-gpsdestbearing}}\n* {{msg-mw|Exif-gpsdestbearingref}}",
-       "exif-gpstrack": "See also:\n* {{msg-mw|Exif-gpstrackref}}\n* {{msg-mw|Exif-gpsimgdirection}}\n* {{msg-mw|Exif-gpsimgdirectionref}}\n* {{msg-mw|Exif-gpsdestbearing}}\n* {{msg-mw|Exif-gpsdestbearingref}}",
-       "exif-gpsimgdirectionref": "See also:\n* {{msg-mw|Exif-gpstrack}}\n* {{msg-mw|Exif-gpstrackref}}\n* {{msg-mw|Exif-gpsimgdirection}}\n* {{msg-mw|Exif-gpsdestbearing}}\n* {{msg-mw|Exif-gpsdestbearingref}}",
-       "exif-gpsimgdirection": "See also:\n* {{msg-mw|Exif-gpstrack}}\n* {{msg-mw|Exif-gpstrackref}}\n* {{msg-mw|Exif-gpsimgdirectionref}}\n* {{msg-mw|Exif-gpsdestbearing}}\n* {{msg-mw|Exif-gpsdestbearingref}}",
+       "exif-gpsstatus": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-gpsstatus-a}}\n* {{msg-mw|Exif-gpsstatus-v}}",
+       "exif-gpsmeasuremode": "{{exif-qqq}}\n\nThis tag indicates whether the measurement 2D (latitude and longitude) or 3D (latitude, longitude, and altitude).\n\nSee also:\n* {{msg-mw|Exif-gpsmeasuremode-2}}\n* {{msg-mw|Exif-gpsmeasuremode-3}}",
+       "exif-gpsdop": "{{exif-qqq}}\n\nHow accurate the GPS information is. See [[wikipedia:Dilution of precision (GPS)]].\n\n{{Related|Exif-gpsdop}}",
+       "exif-gpsspeedref": "{{exif-qqq}}\n{{Related|Exif-gpsspeed}}",
+       "exif-gpsspeed": "{{exif-qqq}}\n{{Related|Exif-gpsspeed}}",
+       "exif-gpstrackref": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-gpstrack}}\n* {{msg-mw|Exif-gpsimgdirection}}\n* {{msg-mw|Exif-gpsimgdirectionref}}\n* {{msg-mw|Exif-gpsdestbearing}}\n* {{msg-mw|Exif-gpsdestbearingref}}",
+       "exif-gpstrack": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-gpstrackref}}\n* {{msg-mw|Exif-gpsimgdirection}}\n* {{msg-mw|Exif-gpsimgdirectionref}}\n* {{msg-mw|Exif-gpsdestbearing}}\n* {{msg-mw|Exif-gpsdestbearingref}}",
+       "exif-gpsimgdirectionref": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-gpstrack}}\n* {{msg-mw|Exif-gpstrackref}}\n* {{msg-mw|Exif-gpsimgdirection}}\n* {{msg-mw|Exif-gpsdestbearing}}\n* {{msg-mw|Exif-gpsdestbearingref}}",
+       "exif-gpsimgdirection": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-gpstrack}}\n* {{msg-mw|Exif-gpstrackref}}\n* {{msg-mw|Exif-gpsimgdirectionref}}\n* {{msg-mw|Exif-gpsdestbearing}}\n* {{msg-mw|Exif-gpsdestbearingref}}",
        "exif-gpsmapdatum": "Label for EXIF information. Indicates the geodetic survey data used by the GPS receiver.",
-       "exif-gpsdestlatituderef": "{{Related|Exif-gpslatitude}}",
-       "exif-gpsdestlatitude": "The latitude of the location shown in the picture, if it is different from latitude of the camera location. See {{msg-mw|exif-gpslatitude}}.\n{{Related|Exif-gpslatitude}}\n{{Identical|Latitude}}",
-       "exif-gpsdestlongituderef": "{{Related|Exif-gpslatitude}}",
-       "exif-gpsdestlongitude": "The longitude of the location shown in the picture, if it is different from longitude of the camera location. See {{msg-mw|exif-gpslongitude}}.\n{{Related|Exif-gpslatitude}}\n{{Identical|Longitude}}",
-       "exif-gpsdestbearingref": "See also:\n* {{msg-mw|Exif-gpstrack}}\n* {{msg-mw|Exif-gpstrackref}}\n* {{msg-mw|Exif-gpsimgdirection}}\n* {{msg-mw|Exif-gpsimgdirectionref}}\n* {{msg-mw|Exif-gpsdestbearing}}",
-       "exif-gpsdestbearing": "See also:\n* {{msg-mw|Exif-gpstrack}}\n* {{msg-mw|Exif-gpstrackref}}\n* {{msg-mw|Exif-gpsimgdirection}}\n* {{msg-mw|Exif-gpsimgdirectionref}}\n* {{msg-mw|Exif-gpsdestbearingref}}",
-       "exif-gpsdestdistanceref": "I think \"reference\" stands for \"unit\". See {{msg-mw|Exif-gpsspeedref}}\n{{Related|Exif-gpsdestdistance}}",
-       "exif-gpsdestdistance": "{{Related|Exif-gpsdestdistance}}",
+       "exif-gpsdestlatituderef": "{{exif-qqq}}\n{{Related|Exif-gpslatitude}}",
+       "exif-gpsdestlatitude": "{{exif-qqq}}\n\nThe latitude of the location shown in the picture, if it is different from latitude of the camera location. See {{msg-mw|exif-gpslatitude}}.\n\n{{Related|Exif-gpslatitude}}\n{{Identical|Latitude}}",
+       "exif-gpsdestlongituderef": "{{exif-qqq}}\n{{Related|Exif-gpslatitude}}",
+       "exif-gpsdestlongitude": "{{exif-qqq}}\n\nThe longitude of the location shown in the picture, if it is different from longitude of the camera location. See {{msg-mw|exif-gpslongitude}}.\n\n{{Related|Exif-gpslatitude}}\n{{Identical|Longitude}}",
+       "exif-gpsdestbearingref": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-gpstrack}}\n* {{msg-mw|Exif-gpstrackref}}\n* {{msg-mw|Exif-gpsimgdirection}}\n* {{msg-mw|Exif-gpsimgdirectionref}}\n* {{msg-mw|Exif-gpsdestbearing}}",
+       "exif-gpsdestbearing": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-gpstrack}}\n* {{msg-mw|Exif-gpstrackref}}\n* {{msg-mw|Exif-gpsimgdirection}}\n* {{msg-mw|Exif-gpsimgdirectionref}}\n* {{msg-mw|Exif-gpsdestbearingref}}",
+       "exif-gpsdestdistanceref": "{{exif-qqq}}\n\nIndicates the unit used to express the distance to the destination point. Values are typically \"K\", \"M\" or \"N\", standing for kilometers, miles and nautical miles, respectively. See also {{msg-mw|Exif-gpsspeedref}}.\n\n{{Related|Exif-gpsdestdistance}}",
+       "exif-gpsdestdistance": "{{exif-qqq}}\n{{Related|Exif-gpsdestdistance}}",
        "exif-gpsprocessingmethod": "Label for EXIF information. A character string recording the name of the method used for location finding.",
        "exif-gpsareainformation": "Label for EXIF information. Contains a character string recording the name of the GPS area.",
-       "exif-gpsdatestamp": "Date (does not generally include time unless recorded in XMP) that GPS measurement was taken, in UTC. Since often this is at the same date as photo was taken, this is sometimes more reliable than {{msg-mw|exif-datetimeoriginal}}.",
+       "exif-gpsdatestamp": "{{exif-qqq}}\n\nDate (does not generally include time unless recorded in XMP) that GPS measurement was taken, in UTC. Since often this is at the same date as photo was taken, this is sometimes more reliable than {{msg-mw|exif-datetimeoriginal}}.",
        "exif-gpsdifferential": "Label for EXIF information. Indicates whether differential correction is applied to the GPS receiver.",
        "exif-coordinate-format": "{{optional}}\nFor formatting GPS latitude coordinates. Parameters:\n* $1 - degrees\n* $2 - minutes\n* $3 - seconds (up to two decimal places)\n* $4 - direction (N, S, W, or E)\n* $5 - (Unused) coordinate as a single positive or negative real number",
        "exif-jpegfilecomment": "This is not a true exif tag, but the contents of the JPEG COM segment. This often contains a file source, but can potentially contain any comment about the file. This is similar to {{msg-mw|exif-usercomment}}, {{msg-mw|exif-pngfilecomment}}, and {{msg-mw|exif-giffilecomment}}.",
-       "exif-keywords": "List of keywords for the photograph (or other media).\n\nThis can come from IPTC-iim 2:25 keyword field, or XMP's dc:subject field.\n{{Identical|Keyword}}",
+       "exif-keywords": "{{exif-qqq}}\n\nList of keywords for the photograph (or other media). This can come from IPTC-iim 2:25 keyword field, or XMP's dc:subject field.\n\n{{Identical|Keyword}}",
        "exif-worldregioncreated": "The world region (generally that means continent, but could also include 'World' as a whole) where the media was created.",
-       "exif-countrycreated": "Country that the picture was taken in. Note this is where it was taken, not what country is depicted in the picture.",
+       "exif-countrycreated": "{{exif-qqq}}\n\nCountry that the picture was taken in. Note this is where it was taken, not what country is depicted in the picture.",
        "exif-countrycodecreated": "ISO Code for the country that the picture was taken in. Note this is where it was taken, not what country is depicted in the picture.",
        "exif-provinceorstatecreated": "Province, state, territory, or other secondary political division (bigger than a city, smaller then a country) where that the picture was taken in.  Note this is where it was taken, not what province/state is depicted in the picture.",
        "exif-citycreated": "City that the picture was taken in.  Note this is where it was taken, not what city is depicted in the picture. This is generally only used if different from the city depicted in photo.",
        "exif-provinceorstatedest": "Province, state, territory, or other secondary political division shown.",
        "exif-citydest": "City shown",
        "exif-sublocationdest": "Sub-location of city shown. This could be an address, a street, an area of town, etc.",
-       "exif-objectname": "This is a short name for the image or other media. (As compared to {{msg-mw|exif-imagedescription}} which is a long description of the image).\n\nThis is sometimes an ID number used to identify the photo, or a (short) title of the photo.\n\nThis property is extracted based on XMP's dc:title property ( http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart1.pdf ), PNG's title keyword ( http://www.w3.org/TR/PNG/#11keywords ), or IPTC-iim 2:05 Object name property ( http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf ).",
+       "exif-objectname": "{{exif-qqq}}\n\nThis is a short name for the image or other media. (As compared to {{msg-mw|exif-imagedescription}} which is a long description of the image).\n\nThis is sometimes an ID number used to identify the photo, or a (short) title of the photo.\n\nThis property is extracted based on XMP's [http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart1.pdf dc:title property], PNG's [http://www.w3.org/TR/PNG/#11keywords title keyword], or IPTC-iim 2:05 [http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf Object name property].",
        "exif-specialinstructions": "Special instructions for how to use the image/media. This might include embargo notices, or other warnings.\n\nThis is IPTC-iim property 2:40. See http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf for details.",
-       "exif-headline": "A short version of the image caption. The IPTC4XMP standard is clear that \"this is not the same thing as title [ {{msg-mw|exif-objectname}} ]\".\n\nThis is extracted from XMP's photoshop:headline ( http://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata-201007_1.pdf ) and IPTC-iim: 2:105 Headline tag ( http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf ).\n{{Identical|Headline}}",
+       "exif-headline": "{{exif-qqq}}\n\nA short version of the image caption. The IPTC4XMP standard is clear that \"this is not the same thing as title [ {{msg-mw|exif-objectname}} ]\".\n\nThis is extracted from [http://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata-201007_1.pdf XMP's photoshop:headline] and [http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf IPTC-iim: 2:105 Headline tag].\n\n{{Identical|Headline}}",
        "exif-credit": "Provider/credit.\n\nWho gave us the image. This might be different from the creator of the image. This is IPTC-iim property 2:110",
-       "exif-source": "See IPTC-iim standard 2:115 - http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf.\n\nThis is who originally owned the image (a person, stock photo agency, etc). This does not refer to the image this image is based on.\n{{Identical|Source}}",
+       "exif-source": "{{exif-qqq}}\n\nSee [http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf IPTC-iim standard 2:115].\n\nThis is who originally owned the image (a person, stock photo agency, etc). This does not refer to the image this image is based on.\n\n{{Identical|Source}}",
        "exif-editstatus": "Editorial status of image. This is more intended for use with people making news papers. This denotes whether the image is on the main page, is part of a correction, etc. See 2:07 of http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf",
-       "exif-urgency": "Urgency. How urgent this image is. 1 is very urgent, 5 is normal, 8 is  very low priority.\n{{Related|Exif-urgency}}",
+       "exif-urgency": "{{exif-qqq}}\n\nUrgency. How urgent this image is. 1 is very urgent, 5 is normal, 8 is  very low priority.\n\n{{Related|Exif-urgency}}",
        "exif-fixtureidentifier": "Fixture name. Identifies frequently occurring object data, for example a regular column in a news paper.",
        "exif-locationdest": "Full printable name of location.",
        "exif-locationdestcode": "Code of location depicted. Typically this is an ISO country code, but the IPTC-iim standard also defines other codes like XSP for outer space. See appendix D (and tag 2:100) of http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf",
        "exif-objectcycle": "Time of day that media is intended for. Either morning only, evening only, or all day. Typically only used for news related things that might only be broadcast at a specific time of day.\n\nSee also:\n* {{msg-mw|Exif-objectcycle-a}}\n* {{msg-mw|Exif-objectcycle-p}}\n* {{msg-mw|Exif-objectcycle-b}}",
        "exif-contact": "Contact information of the person responsible for the image.",
        "exif-writer": "The person who wrote the caption of the image. See Description Writer on page 18 of http://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata-201007_1.pdf\n{{Identical|Writer}}",
-       "exif-languagecode": "Language of image/media.\n\nThis is taken from IPTC-iim 2:135 and XMP's dc:language.\n{{Identical|Language}}",
+       "exif-languagecode": "{{exif-qqq}}\n\nLanguage of image/media. This is taken from IPTC-iim 2:135 and XMP's dc:language.\n\n{{Identical|Language}}",
        "exif-iimversion": "IIM version number. Version of information interchange 2:xx records. 4 is current version. 2 is often seen as well. This is the value stored 2:00 field (Note, iptc-iim also stores a model version in 1:00. This version field displays the 2:00 record only)",
-       "exif-iimcategory": "Primary Category of image (or other media). Technically supposed to be limited to 3 characters, however that is not always followed. Some common 3 letter category abbreviations are expanded by mediawiki. Similar to {{msg-mw|exif-keywords}}.\n{{Identical|Category}}",
+       "exif-iimcategory": "{{exif-qqq}}\n\nPrimary Category of image (or other media). Technically supposed to be limited to 3 characters, however that is not always followed. Some common 3 letter category abbreviations are expanded by mediawiki. Similar to {{msg-mw|exif-keywords}}.\n\n{{Identical|Category}}",
        "exif-iimsupplementalcategory": "Supplemental categories. Like {{msg-mw|exif-iimcategory}} but for categories beyond the main one.",
-       "exif-datetimeexpires": "Date after which not to use the image (media). This is often used in news situations were certain things (like forecasts) should not be used after a specified date.",
-       "exif-datetimereleased": "Earliest date the image (media) can be used.\n\nSee 2:30 of http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf",
+       "exif-datetimeexpires": "{{exif-qqq}}\n\nDate after which not to use the image (media). This is often used in news situations were certain things (like forecasts) should not be used after a specified date.",
+       "exif-datetimereleased": "{{exif-qqq}}\n\nEarliest date the image (media) can be used.\n\nSee 2:30 of http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf",
        "exif-originaltransmissionref": "This is basically a job ID. This could help an individual keep track of for what reason the image was created.\n\nSee Job Id on page 19 of http://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata-201007_1.pdf",
-       "exif-identifier": "A formal identifier for the image. Often this is a URL.\n{{Identical|Identifier}}",
+       "exif-identifier": "{{exif-qqq}}\nA formal identifier for the image. Often this is a URL.\n{{Identical|Identifier}}",
        "exif-lens": "Description of lens used. This is taken from aux:Lens XMP property. See http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart2.pdf",
        "exif-serialnumber": "Serial number of camera. See aux:SerialNumber in http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart2.pdf",
        "exif-cameraownername": "Who owns the camera.",
-       "exif-label": "Label given to the image for organizational purposes. This is very similar to {{msg-mw|exif-keywords}}. Label is more used by a person to organize their media, where keywords are used to describe the photo contents itself.\n\nThis property can come from xmp:Label in XMP ( http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart1.pdf ) or the label textual chunk in PNG.\n{{Identical|Label}}",
-       "exif-datetimemetadata": "Date metadata was last modified. Typically this refers to XMP metadata.",
+       "exif-label": "{{exif-qqq}}\n\nLabel given to the image for organizational purposes. This is very similar to {{msg-mw|exif-keywords}}. Label is more used by a person to organize their media, where keywords are used to describe the photo contents itself.\n\nThis property can come from xmp:Label in XMP ( http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart1.pdf ) or the label textual chunk in PNG.\n\n{{Identical|Label}}",
+       "exif-datetimemetadata": "{{exif-qqq}}\n\nDate metadata was last modified. Typically this refers to XMP metadata.",
        "exif-nickname": "Short informal name of image. See http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart2.pdf",
        "exif-rating": "This is a rating for how good the image is. The range is between 1 to 5 (5 highest), with an additional option of \"reject\".",
        "exif-rightscertificate": "URL of Rights management certificate. This comes from XMPRights:Certificate property. See http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart1.pdf",
        "exif-copyrighted": "Copyright status. This is a true or false field showing either Copyrighted or Public Domain. It should be noted that Copyrighted includes freely-licensed works.",
-       "exif-copyrightowner": "Copyright owner. Can have more than one person or entity.",
+       "exif-copyrightowner": "{{exif-qqq}}\n\nCopyright owner. Can have more than one person or entity.",
        "exif-usageterms": "Terms under which you're allowed to use the image/media.",
-       "exif-webstatement": "URL detailing the copyright status of the image, and how you're allowed to use the image. Often this is a link to a creative commons license, however the creative commons people recommend using a page that generally contains specific information about the image, and recommend using {{msg-mw|exif-licenseurl}} for linking to the license. See http://wiki.creativecommons.org/XMP",
+       "exif-webstatement": "{{exif-qqq}}\n\nURL detailing the copyright status of the image, and how you're allowed to use the image. Often this is a link to a creative commons license, however the creative commons people recommend using a page that generally contains specific information about the image, and recommend using {{msg-mw|exif-licenseurl}} for linking to the license. See http://wiki.creativecommons.org/XMP",
        "exif-originaldocumentid": "A unique ID of the original document (image) that this document (image) is based on.",
-       "exif-licenseurl": "URL for copyright license. This is almost always a creative commons license since this information comes from the creative commons namespace of XMP (but could be a link to any type of license). See also {{msg-mw|exif-webstatement}}",
+       "exif-licenseurl": "{{exif-qqq}}\n\nURL for copyright license. This is almost always a creative commons license since this information comes from the creative commons namespace of XMP (but could be a link to any type of license). See also {{msg-mw|exif-webstatement}}",
        "exif-morepermissionsurl": "A URL where you can \"buy\" (or otherwise negotiate) to get more rights for the image.",
        "exif-attributionurl": "A URL that you're supposed to use when re-using the image.",
        "exif-preferredattributionname": "The preferred name to give credit to when re-using this image.",
-       "exif-pngfilecomment": "See also:\n* {{msg-mw|Exif-pngfilecomment}}\n* {{msg-mw|Exif-giffilecomment}}",
-       "exif-disclaimer": "Disclaimer for the image.\n{{Identical|Disclaimer}}",
+       "exif-pngfilecomment": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-pngfilecomment}}\n* {{msg-mw|Exif-giffilecomment}}",
+       "exif-disclaimer": "{{exif-qqq}}\n\nDisclaimer for the image.\n\n{{Identical|Disclaimer}}",
        "exif-contentwarning": "Content warning for the image. For example if the image/media contains violent, sexual or otherwise offensive content.\n\nThis comes from the png warning textual chunk. See http://www.w3.org/TR/PNG/#11keywords",
-       "exif-giffilecomment": "See also:\n* {{msg-mw|Exif-pngfilecomment}}\n* {{msg-mw|Exif-giffilecomment}}",
+       "exif-giffilecomment": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-pngfilecomment}}\n* {{msg-mw|Exif-giffilecomment}}",
        "exif-intellectualgenre": "The \"intellectual genre\" of the image/media item. This typically means the type of item it is, ignoring the actual content of the item. See http://cv.iptc.org/newscodes/genre/ for some examples of the types of values this field might have.",
        "exif-subjectnewscode": "A (or multiple) codes describing the content of the image/media. The code is an 8 digit number representing some sort of category. The code is hierarchical , with the first two digits being a broad category (this broad category is shown to the user. See {{msg-mw|exif-subjectnewscode-value}} for how the value this field labels is shown to user). See http://cv.iptc.org/newscodes/subjectcode for the full list of codes.",
        "exif-scenecode": "IPTC (numeric) scene code. Contains information on what type of scene it is (like panoramic scene, close-up, etc). See http://cv.iptc.org/newscodes/scene/",
        "exif-software-value": "{{notranslate}}\nParameters:\n* $1 - software name",
        "exif-software-version-value": "{{notranslate}}\nParameters:\n* $1 - software name\n* $2 - version number",
        "exif-contact-value": "{{optional}}\nParameters:\n* $1 - email\n* $2 - URL of website\n* $3 - street address\n* $4 - city\n* $5 - region\n* $6 - postal code\n* $7 - country\n* $8 - telephone number\nNote, not all fields are guaranteed to be present, some may be empty strings.",
-       "exif-subjectnewscode-value": "{{Optional}}\nParameters:\n* $1 - numeric IPTC subject news code (one of http://cv.iptc.org/newscodes/subjectcode )\n* $2 - one of 17 broad categories that the code falls into. For example any code starting with 15 has the contents of {{msg-mw|Exif-iimcategory-spo}} for $2.",
-       "exif-compression-1": "{{Related|Exif-compression}}",
+       "exif-subjectnewscode-value": "{{Optional}}\n\n{{exif-qqq}}\n\nParameters:\n* $1 - numeric IPTC subject news code (one of http://cv.iptc.org/newscodes/subjectcode )\n* $2 - one of 17 broad categories that the code falls into. For example any code starting with 15 has the contents of {{msg-mw|Exif-iimcategory-spo}} for $2.",
+       "exif-compression-1": "{{exif-qqq}}\n{{Related|Exif-compression}}",
        "exif-compression-2": "{{Related|Exif-compression}}",
        "exif-compression-3": "{{Related|Exif-compression}}",
        "exif-compression-4": "{{Related|Exif-compression}}",
        "exif-photometricinterpretation-10": "See http://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html",
        "exif-photometricinterpretation-32803": "Used mostly by DNG images. See http://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html",
        "exif-photometricinterpretation-34892": "Used mostly by DNG images. See http://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html",
-       "exif-unknowndate": "Used if the Exif date and time is \"<code>0000:00:00 00:00:00</code>\".\n\nRelated Exif attributes:\n* {{msg-mw|Exif-datetime}}\n* {{msg-mw|Exif-datetimeoriginal}}\n* {{msg-mw|Exif-datetimedigitized}}\n* {{msg-mw|Exif-datetimereleased}}\n* {{msg-mw|Exif-datetimeexpires}}\n* {{msg-mw|Exif-gpsdatestamp}}\n* {{msg-mw|Exif-dc-date}}\n* {{msg-mw|Exif-datetimemetadata}}",
+       "exif-unknowndate": "{{exif-qqq}}\n\nUsed if the Exif date and time is \"<code>0000:00:00 00:00:00</code>\".\n\nRelated Exif attributes:\n* {{msg-mw|Exif-datetime}}\n* {{msg-mw|Exif-datetimeoriginal}}\n* {{msg-mw|Exif-datetimedigitized}}\n* {{msg-mw|Exif-datetimereleased}}\n* {{msg-mw|Exif-datetimeexpires}}\n* {{msg-mw|Exif-gpsdatestamp}}\n* {{msg-mw|Exif-dc-date}}\n* {{msg-mw|Exif-datetimemetadata}}",
        "exif-orientation-1": "0th row: top; 0th column: left\n{{Related|Exif-orientation}}\n{{Identical|Normal}}",
-       "exif-orientation-2": "0th row: top; 0th column: right\n{{Related|Exif-orientation}}",
-       "exif-orientation-3": "0th row: bottom; 0th column: right\n{{Related|Exif-orientation}}",
-       "exif-orientation-4": "0th row: bottom; 0th column: left\n{{Related|Exif-orientation}}",
+       "exif-orientation-2": "{{exif-qqq}}\n\n0th row: top; 0th column: right\n\n{{Related|Exif-orientation}}",
+       "exif-orientation-3": "{{exif-qqq}}\n\n0th row: bottom; 0th column: right\n\n{{Related|Exif-orientation}}",
+       "exif-orientation-4": "{{exif-qqq}}\n\n0th row: bottom; 0th column: left\n\n{{Related|Exif-orientation}}",
        "exif-orientation-5": "0th row: left; 0th column: top\n\nCCW is an abbreviation for counter-clockwise\n{{Related|Exif-orientation}}",
-       "exif-orientation-6": "0th row: right; 0th column: top\n\nCCW is an abbreviation for counter-clockwise.\n{{Related|Exif-orientation}}",
+       "exif-orientation-6": "{{exif-qqq}}\n\n0th row: right; 0th column: top\n\nCCW is an abbreviation for counter-clockwise.\n\n{{Related|Exif-orientation}}",
        "exif-orientation-7": "0th row: right; 0th column: bottom\n\nCW is an abbreviation for clockwise\n{{Related|Exif-orientation}}",
-       "exif-orientation-8": "0th row: left; 0th column: bottom\n\nCW is an abbreviation for clockwise.\n{{Related|Exif-orientation}}",
-       "exif-planarconfiguration-1": "See also:\n* {{msg-mw|Exif-planarconfiguration}}\n* {{msg-mw|Exif-planarconfiguration-1}}\n* {{msg-mw|Exif-planarconfiguration-2}}",
-       "exif-planarconfiguration-2": "See also:\n* {{msg-mw|Exif-planarconfiguration}}\n* {{msg-mw|Exif-planarconfiguration-1}}\n* {{msg-mw|Exif-planarconfiguration-2}}",
+       "exif-orientation-8": "{{exif-qqq}}\n\n0th row: left; 0th column: bottom\n\nCW is an abbreviation for clockwise.\n\n{{Related|Exif-orientation}}",
+       "exif-planarconfiguration-1": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-planarconfiguration}}\n* {{msg-mw|Exif-planarconfiguration-1}}\n* {{msg-mw|Exif-planarconfiguration-2}}",
+       "exif-planarconfiguration-2": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-planarconfiguration}}\n* {{msg-mw|Exif-planarconfiguration-1}}\n* {{msg-mw|Exif-planarconfiguration-2}}",
        "exif-xyresolution-i": "{{Optional}}\nUsed to format {{msg-mw|Exif-xresolution}} and {{msg-mw|Exif-yresolution}} if the unit is dots per inch.\n\nParameters:\n* $1 - the number of dots/in",
        "exif-xyresolution-c": "{{Optional}}\nUsed to format {{msg-mw|Exif-xresolution}} and {{msg-mw|Exif-yresolution}} if the unit is dots per centimeter.\n\nParameters:\n* $1 - the number of dots/cm",
-       "exif-colorspace-1": "{{Optional}}\nIf it uses the standard sRGB colour space.\n\nSee also:\n* {{msg-mw|Exif-colorspace}}\n* {{msg-mw|Exif-colorspace-1|optional}}\n* {{msg-mw|Exif-colorspace-65535}}",
-       "exif-colorspace-65535": "The photograph is not colour calibrated.\n\nSee also:\n* {{msg-mw|Exif-colorspace}}\n* {{msg-mw|Exif-colorspace-1|optional}}\n* {{msg-mw|Exif-colorspace-65535}}",
-       "exif-componentsconfiguration-0": "{{Related|Exif-componentsconfiguration}}",
-       "exif-componentsconfiguration-1": "{{optional}}\n{{Related|Exif-componentsconfiguration}}",
-       "exif-componentsconfiguration-2": "{{optional}}\n{{Related|Exif-componentsconfiguration}}",
-       "exif-componentsconfiguration-3": "{{optional}}\n{{Related|Exif-componentsconfiguration}}",
-       "exif-componentsconfiguration-4": "{{optional}}\n{{Related|Exif-componentsconfiguration}}",
-       "exif-componentsconfiguration-5": "{{optional}}\n{{Related|Exif-componentsconfiguration}}",
-       "exif-componentsconfiguration-6": "{{optional}}\n{{Related|Exif-componentsconfiguration}}",
-       "exif-exposureprogram-0": "{{Related|Exif-exposureprogram}}",
+       "exif-colorspace-1": "{{Optional}}\n{{exif-qqq}}\n\nThis value indicates that the image uses the standard sRGB colour space.\n\nSee also:\n* {{msg-mw|Exif-colorspace}}\n* {{msg-mw|Exif-colorspace-1|optional}}\n* {{msg-mw|Exif-colorspace-65535}}",
+       "exif-colorspace-65535": "{{exif-qqq}}\n\nThis value indicates that the photograph is not colour-calibrated.\n\nSee also:\n* {{msg-mw|Exif-colorspace}}\n* {{msg-mw|Exif-colorspace-1|optional}}\n* {{msg-mw|Exif-colorspace-65535}}",
+       "exif-componentsconfiguration-0": "{{exif-qqq}}\n{{Related|Exif-componentsconfiguration}}",
+       "exif-componentsconfiguration-1": "{{optional}}\n{{exif-qqq}}\n{{Related|Exif-componentsconfiguration}}",
+       "exif-componentsconfiguration-2": "{{optional}}\n{{exif-qqq}}\n{{Related|Exif-componentsconfiguration}}",
+       "exif-componentsconfiguration-3": "{{optional}}\n{{exif-qqq}}\n{{Related|Exif-componentsconfiguration}}",
+       "exif-componentsconfiguration-4": "{{optional}}\n{{exif-qqq}}\n{{Related|Exif-componentsconfiguration}}",
+       "exif-componentsconfiguration-5": "{{optional}}\n{{exif-qqq}}\n{{Related|Exif-componentsconfiguration}}",
+       "exif-componentsconfiguration-6": "{{optional}}\n{{exif-qqq}}\n{{Related|Exif-componentsconfiguration}}",
+       "exif-exposureprogram-0": "{{exif-qqq}}\n{{Related|Exif-exposureprogram}}",
        "exif-exposureprogram-1": "One of the exposure program types in the table of metadata on image description pages. See the Wikipedia article '[[w:Mode_dial|Mode dial]]' for an explanation.\n{{Related|Exif-exposureprogram}}\n{{Identical|Manual}}",
-       "exif-exposureprogram-2": "One of the exposure program types in the table of metadata on image description pages.\n{{Related|Exif-exposureprogram}}",
-       "exif-exposureprogram-3": "One of the exposure program types in the table of metadata on image description pages. See the Wikipedia article for a definition of the term [[w:Aperture_priority|aperture priority]].\n{{Related|Exif-exposureprogram}}",
-       "exif-exposureprogram-4": "One of the exposure program types in the table of metadata on image description pages. See the Wikipedia article for a definition of the term [[w:Shutter_priority|shutter priority]].\n{{Related|Exif-exposureprogram}}",
+       "exif-exposureprogram-2": "{{exif-qqq}}\n\nOne of the exposure program types in the table of metadata on image description pages.\n\n{{Related|Exif-exposureprogram}}",
+       "exif-exposureprogram-3": "{{exif-qqq}}\n\nOne of the exposure program types in the table of metadata on image description pages. See the Wikipedia article for a definition of the term [[w:Aperture_priority|aperture priority]].\n\n{{Related|Exif-exposureprogram}}",
+       "exif-exposureprogram-4": "{{exif-qqq}}\n\nOne of the exposure program types in the table of metadata on image description pages. See the Wikipedia article for a definition of the term [[w:Shutter_priority|shutter priority]].\n\n{{Related|Exif-exposureprogram}}",
        "exif-exposureprogram-5": "One of the exposure program types in the table of metadata on image description pages. See the Wikipedia article '[[w:Mode_dial|Mode dial]]' for an explanation.\n{{Related|Exif-exposureprogram}}",
        "exif-exposureprogram-6": "One of the exposure program types in the table of metadata on image description pages. See the Wikipedia article '[[w:Mode_dial|Mode dial]]' for an explanation.\n{{Related|Exif-exposureprogram}}",
        "exif-exposureprogram-7": "One of the exposure program types in the table of metadata on image description pages. See the Wikipedia article '[[w:Mode_dial|Mode dial]]' for an explanation.\n{{Related|Exif-exposureprogram}}",
        "exif-subjectdistance-value": "Parameters:\n* $1 - a distance measured in meters. The value can, and usually does, include decimal places.",
        "exif-meteringmode-0": "{{Related|Exif-meteringmode}}\n{{Identical|Unknown}}",
        "exif-meteringmode-1": "{{Related|Exif-meteringmode}}\n{{Identical|Average}}",
-       "exif-meteringmode-2": "{{Related|Exif-meteringmode}}",
-       "exif-meteringmode-3": "{{Related|Exif-meteringmode}}",
-       "exif-meteringmode-4": "{{Related|Exif-meteringmode}}",
-       "exif-meteringmode-5": "{{Related|Exif-meteringmode}}\n{{Identical|Pattern}}",
-       "exif-meteringmode-6": "{{Related|Exif-meteringmode}}",
+       "exif-meteringmode-2": "{{exif-qqq}}\n{{Related|Exif-meteringmode}}",
+       "exif-meteringmode-3": "{{exif-qqq}}\n{{Related|Exif-meteringmode}}",
+       "exif-meteringmode-4": "{{exif-qqq}}\n{{Related|Exif-meteringmode}}",
+       "exif-meteringmode-5": "{{exif-qqq}}\n{{Related|Exif-meteringmode}}\n{{Identical|Pattern}}",
+       "exif-meteringmode-6": "{{exif-qqq}}\n{{Related|Exif-meteringmode}}",
        "exif-meteringmode-255": "{{Identical|Other}}",
        "exif-lightsource-0": "{{Identical|Unknown}}\n{{Related|Exif-lightsource}}",
-       "exif-lightsource-1": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-2": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-3": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-4": "{{Identical|Flash}}\n{{Related|Exif-lightsource}}",
-       "exif-lightsource-9": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-10": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-11": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-12": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-13": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-14": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-15": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-17": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-18": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-19": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-20": "{{optional}}\n{{Related|Exif-lightsource}}",
-       "exif-lightsource-21": "{{optional}}\n{{Related|Exif-lightsource}}",
-       "exif-lightsource-22": "{{optional}}\n{{Related|Exif-lightsource}}",
-       "exif-lightsource-23": "{{optional}}\n{{Related|Exif-lightsource}}",
-       "exif-lightsource-24": "{{Related|Exif-lightsource}}",
-       "exif-lightsource-255": "{{Related|Exif-lightsource}}",
+       "exif-lightsource-1": "{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-2": "{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-3": "{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-4": "{{exif-qqq}}\n{{Identical|Flash}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-9": "{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-10": "{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-11": "{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-12": "{{exif-qqq}}\n\nThe \"D\" stands for \"daylight\", as defined in the JIS Z 9112:2012 standard.\n\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-13": "{{exif-qqq}}\n\nThe \"N\" symbol is defined in the JIS Z 9112:2012 standard.\n\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-14": "{{exif-qqq}}\n\nThe \"W\" stands for \"white\", as defined in the JIS Z 9112:2012 standard.\n\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-15": "{{exif-qqq}}\n\nThe \"WW\" stands for \"warm white\", as defined in the JIS Z 9112:2012 standard.\n\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-17": "{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-18": "{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-19": "{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-20": "{{optional}}\n{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-21": "{{optional}}\n{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-22": "{{optional}}\n{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-23": "{{optional}}\n{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-24": "{{exif-qqq}}\n{{Related|Exif-lightsource}}",
+       "exif-lightsource-255": "{{exif-qqq}}\n{{Related|Exif-lightsource}}",
        "exif-flash-fired-0": "{{Related|Exif-flash}}",
        "exif-flash-fired-1": "{{Related|Exif-flash}}",
        "exif-flash-return-0": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n\n\"Strobe\" and \"flash\" mean the same here.\n{{Related|Exif-flash}}",
        "exif-flash-mode-3": "{{Related|Exif-flash}}",
        "exif-flash-function-1": "Exif is a format for storing metadata in image files. See this [[w:Exchangeable_image_file_format|Wikipedia article]] and the example at the bottom of [[commons:File:Phalacrocorax-auritus-020.jpg|this page on Commons]]. The tags are explained [http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html briefly] and [http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf in further detail].\n{{Related|Exif-flash}}",
        "exif-flash-redeye-1": "{{Related|Exif-flash}}",
-       "exif-focalplaneresolutionunit-2": "See also:\n* {{msg-mw|Exif-focalplaneresolutionunit}}",
+       "exif-focalplaneresolutionunit-2": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-focalplaneresolutionunit}}",
        "exif-sensingmethod-1": "{{Related|Exif-sensingmethod}}\n{{Identical|Undefined}}",
        "exif-sensingmethod-2": "{{Related|Exif-sensingmethod}}",
        "exif-sensingmethod-3": "{{Related|Exif-sensingmethod}}",
        "exif-sensingmethod-4": "{{Related|Exif-sensingmethod}}",
        "exif-sensingmethod-5": "''Color sequential'' means, that the three base colors are measured one after another (i.e. the sensor is first measuring red, than green, than blue).\n{{Related|Exif-sensingmethod}}",
-       "exif-sensingmethod-7": "{{Related|Exif-sensingmethod}}",
+       "exif-sensingmethod-7": "{{exif-qqq}}\n{{Related|Exif-sensingmethod}}",
        "exif-sensingmethod-8": "''Color sequential'' means, that the three base colors are measured one after another (i.e. the sensor is first measuring red, than green, than blue).\n{{Related|Exif-sensingmethod}}",
        "exif-filesource-3": "{{optional}}",
        "exif-scenetype-1": "See also:\n* {{msg-mw|Exif-scenetype}}\n* {{msg-mw|Exif-scenetype-1}}",
-       "exif-customrendered-0": "See also:\n* {{msg-mw|Exif-customrendered}}\n* {{msg-mw|Exif-customrendered-0}}\n* {{msg-mw|Exif-customrendered-1}}",
-       "exif-customrendered-1": "See also:\n* {{msg-mw|Exif-customrendered}}\n* {{msg-mw|Exif-customrendered-0}}\n* {{msg-mw|Exif-customrendered-1}}",
-       "exif-exposuremode-0": "{{Related|Exif-exposuremode}}",
-       "exif-exposuremode-1": "{{Related|Exif-exposuremode}}",
-       "exif-exposuremode-2": "A type of exposure mode shown as part of the metadata on image description pages. The Wikipedia article on [[w:Bracketing#Exposure_bracketing|bracketing]] says that 'auto bracket' is a camera exposure setting which automatically takes a series of pictures at slightly different light exposures.\n{{Related|Exif-exposuremode}}",
-       "exif-whitebalance-0": "See also:\n* {{msg-mw|Exif-whitebalance}}\n* {{msg-mw|Exif-whitebalance-0}}\n* {{msg-mw|Exif-whitebalance-1}}",
-       "exif-whitebalance-1": "See also:\n* {{msg-mw|Exif-whitebalance}}\n* {{msg-mw|Exif-whitebalance-0}}\n* {{msg-mw|Exif-whitebalance-1}}",
+       "exif-customrendered-0": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-customrendered}}\n* {{msg-mw|Exif-customrendered-0}}\n* {{msg-mw|Exif-customrendered-1}}",
+       "exif-customrendered-1": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-customrendered}}\n* {{msg-mw|Exif-customrendered-0}}\n* {{msg-mw|Exif-customrendered-1}}",
+       "exif-exposuremode-0": "{{exif-qqq}}\n{{Related|Exif-exposuremode}}",
+       "exif-exposuremode-1": "{{exif-qqq}}\n{{Related|Exif-exposuremode}}",
+       "exif-exposuremode-2": "{{exif-qqq}}\n\nA type of exposure mode shown as part of the metadata on image description pages. The Wikipedia article on [[w:Bracketing#Exposure_bracketing|bracketing]] says that 'auto bracket' is a camera exposure setting which automatically takes a series of pictures at slightly different light exposures.\n\n{{Related|Exif-exposuremode}}",
+       "exif-whitebalance-0": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-whitebalance}}\n* {{msg-mw|Exif-whitebalance-0}}\n* {{msg-mw|Exif-whitebalance-1}}",
+       "exif-whitebalance-1": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-whitebalance}}\n* {{msg-mw|Exif-whitebalance-0}}\n* {{msg-mw|Exif-whitebalance-1}}",
        "exif-scenecapturetype-0": "{{Related|Exif-scenecapturetype}}\n{{Identical|Standard}}",
-       "exif-scenecapturetype-1": "{{Related|Exif-scenecapturetype}}",
-       "exif-scenecapturetype-2": "{{Related|Exif-scenecapturetype}}",
-       "exif-scenecapturetype-3": "{{Related|Exif-scenecapturetype}}",
+       "exif-scenecapturetype-1": "{{exif-qqq}}\n{{Related|Exif-scenecapturetype}}",
+       "exif-scenecapturetype-2": "{{exif-qqq}}\n{{Related|Exif-scenecapturetype}}",
+       "exif-scenecapturetype-3": "{{exif-qqq}}\n{{Related|Exif-scenecapturetype}}",
        "exif-gaincontrol-0": "Gain amplifies the signal off of the image sensor. Gain turns the brightness level up or down.\n{{Related|Exif-gaincontrol}}\n{{Identical|None}}",
-       "exif-gaincontrol-1": "Gain amplifies the signal off of the image sensor. Gain turns the brightness level up or down.\n{{Related|Exif-gaincontrol}}",
-       "exif-gaincontrol-2": "Gain amplifies the signal off of the image sensor. Gain turns the brightness level up or down.\n{{Related|Exif-gaincontrol}}",
-       "exif-gaincontrol-3": "Gain amplifies the signal off of the image sensor. Gain turns the brightness level up or down.\n{{Related|Exif-gaincontrol}}",
-       "exif-gaincontrol-4": "Gain amplifies the signal off of the image sensor. Gain turns the brightness level up or down.\n{{Related|Exif-gaincontrol}}",
+       "exif-gaincontrol-1": "{{exif-qqq}}\n\nGain amplifies the signal off of the image sensor. Gain turns the brightness level up or down.\n\n{{Related|Exif-gaincontrol}}",
+       "exif-gaincontrol-2": "{{exif-qqq}}\n\nGain amplifies the signal off of the image sensor. Gain turns the brightness level up or down.\n\n{{Related|Exif-gaincontrol}}",
+       "exif-gaincontrol-3": "{{exif-qqq}}\n\nGain amplifies the signal off of the image sensor. Gain turns the brightness level up or down.\n\n{{Related|Exif-gaincontrol}}",
+       "exif-gaincontrol-4": "{{exif-qqq}}\n\nGain amplifies the signal off of the image sensor. Gain turns the brightness level up or down.\n\n{{Related|Exif-gaincontrol}}",
        "exif-contrast-0": "{{Related|Exif-contrast}}\n{{Identical|Normal}}",
-       "exif-contrast-1": "{{Related|Exif-contrast}}\n{{Identical|Soft}}",
-       "exif-contrast-2": "{{Related|Exif-contrast}}\n{{Identical|Hard}}",
+       "exif-contrast-1": "{{exif-qqq}}\n{{Related|Exif-contrast}}\n{{Identical|Soft}}",
+       "exif-contrast-2": "{{exif-qqq}}\n{{Related|Exif-contrast}}\n{{Identical|Hard}}",
        "exif-saturation-0": "{{Related|Exif-saturation}}\n{{Identical|Normal}}",
-       "exif-saturation-1": "{{Related|Exif-saturation}}",
-       "exif-saturation-2": "Color saturation in picture EXIF data\n{{Related|Exif-saturation}}",
+       "exif-saturation-1": "{{exif-qqq}}\n{{Related|Exif-saturation}}",
+       "exif-saturation-2": "{{exif-qqq}}\n\nColor saturation in picture EXIF data\n\n{{Related|Exif-saturation}}",
        "exif-sharpness-0": "{{Related|Exif-sharpness}}\n{{Identical|Normal}}",
-       "exif-sharpness-1": "{{Related|Exif-sharpness}}\n{{Identical|Soft}}",
-       "exif-sharpness-2": "{{Related|Exif-sharpness}}\n{{Identical|Hard}}",
+       "exif-sharpness-1": "{{exif-qqq}}\n{{Related|Exif-sharpness}}\n{{Identical|Soft}}",
+       "exif-sharpness-2": "{{exif-qqq}}\n{{Related|Exif-sharpness}}\n{{Identical|Hard}}",
        "exif-subjectdistancerange-0": "{{Related|Exif-subjectdistancerange}}\n{{Identical|Unknown}}",
-       "exif-subjectdistancerange-1": "Macro view is close-up photography. See [[w:Macro_photography|Wikipedia]].\n{{Identical|Macro}}\n{{Related|Exif-subjectdistancerange}}",
-       "exif-subjectdistancerange-2": "{{Related|Exif-subjectdistancerange}}",
-       "exif-subjectdistancerange-3": "{{Related|Exif-subjectdistancerange}}",
-       "exif-gpslatitude-n": "Very rarely used. Only used when using an old version of Mediawiki as a foreign image repo.\n{{Related|Exif-gpslatitude}}",
-       "exif-gpslatitude-s": "Very rarely used. Only used when using an old version of Mediawiki as a foreign image repo.\n{{Related|Exif-gpslatitude}}",
-       "exif-gpslongitude-e": "Very rarely used. Only used when using an old version of Mediawiki as a foreign image repo.\n{{Related|Exif-gpslatitude}}",
-       "exif-gpslongitude-w": "Very rarely used. Only used when using an old version of Mediawiki as a foreign image repo.\n{{Related|Exif-gpslatitude}}",
+       "exif-subjectdistancerange-1": "{{exif-qqq}}\n\nMacro view is close-up photography. See [[w:Macro photography|Wikipedia]].\n\n{{Identical|Macro}}\n{{Related|Exif-subjectdistancerange}}",
+       "exif-subjectdistancerange-2": "{{exif-qqq}}\n{{Related|Exif-subjectdistancerange}}",
+       "exif-subjectdistancerange-3": "{{exif-qqq}}\n{{Related|Exif-subjectdistancerange}}",
+       "exif-gpslatitude-n": "{{exif-qqq}}\n\nVery rarely used. Only used when using an old version of Mediawiki as a foreign image repo.\n\n{{Related|Exif-gpslatitude}}",
+       "exif-gpslatitude-s": "{{exif-qqq}}\n\nVery rarely used. Only used when using an old version of Mediawiki as a foreign image repo.\n\n{{Related|Exif-gpslatitude}}",
+       "exif-gpslongitude-e": "{{exif-qqq}}\n\nVery rarely used. Only used when using an old version of Mediawiki as a foreign image repo.\n\n{{Related|Exif-gpslatitude}}",
+       "exif-gpslongitude-w": "{{exif-qqq}}\n\nVery rarely used. Only used when using an old version of Mediawiki as a foreign image repo.\n\n{{Related|Exif-gpslatitude}}",
        "exif-gpsaltitude-above-sealevel": "Used as GPS Altitude in Exif data. Parameters:\n* $1 - altitude above sea level (in meters)\nSee also:\n* {{msg-mw|Exif-gpsaltitude-below-sealevel}}",
        "exif-gpsaltitude-below-sealevel": "Used as GPS Altitude in Exif data. Parameters:\n* $1 - altitude below sea level (in meters)\nSee also:\n* {{msg-mw|Exif-gpsaltitude-above-sealevel}}",
-       "exif-gpsstatus-a": "See also:\n* {{msg-mw|Exif-gpsstatus}}\n* {{msg-mw|Exif-gpsstatus-v}}",
-       "exif-gpsstatus-v": "See also:\n* {{msg-mw|Exif-gpsstatus}}\n* {{msg-mw|Exif-gpsstatus-a}}",
-       "exif-gpsmeasuremode-2": "Only latitude and longitude recorded, no altitude.\n\nSee also:\n* {{msg-mw|Exif-gpsmeasuremode}}\n* {{msg-mw|Exif-gpsmeasuremode-3}}",
-       "exif-gpsmeasuremode-3": "Latitude, longitude, and altitude recorded.\n\nSee also:\n* {{msg-mw|Exif-gpsmeasuremode}}\n* {{msg-mw|Exif-gpsmeasuremode-2}}",
-       "exif-gpsspeed-k": "{{Related|Exif-gpsspeed}}",
-       "exif-gpsspeed-m": "{{Related|Exif-gpsspeed}}",
-       "exif-gpsspeed-n": "Knots: ''Knot'' is a unit of speed on water used for ships, etc., equal to one nautical mile per hour.\n{{Related|Exif-gpsspeed}}",
-       "exif-gpsdestdistance-k": "{{Related|Exif-gpsdestdistance}}",
-       "exif-gpsdestdistance-m": "{{Related|Exif-gpsdestdistance}}",
-       "exif-gpsdestdistance-n": "{{Related|Exif-gpsdestdistance}}",
+       "exif-gpsstatus-a": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-gpsstatus}}\n* {{msg-mw|Exif-gpsstatus-v}}",
+       "exif-gpsstatus-v": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-gpsstatus}}\n* {{msg-mw|Exif-gpsstatus-a}}",
+       "exif-gpsmeasuremode-2": "{{exif-qqq}}\n\nOnly latitude and longitude recorded, no altitude.\n\nSee also:\n* {{msg-mw|Exif-gpsmeasuremode}}\n* {{msg-mw|Exif-gpsmeasuremode-3}}",
+       "exif-gpsmeasuremode-3": "{{exif-qqq}}\n\nLatitude, longitude, and altitude recorded.\n\nSee also:\n* {{msg-mw|Exif-gpsmeasuremode}}\n* {{msg-mw|Exif-gpsmeasuremode-2}}",
+       "exif-gpsspeed-k": "{{exif-qqq}}\n{{Related|Exif-gpsspeed}}",
+       "exif-gpsspeed-m": "{{exif-qqq}}\n{{Related|Exif-gpsspeed}}",
+       "exif-gpsspeed-n": "{{exif-qqq}}\n\n''Knot'' is a unit of speed on water used for ships, etc., equal to one nautical mile per hour.\n\n{{Related|Exif-gpsspeed}}",
+       "exif-gpsdestdistance-k": "{{exif-qqq}}\n{{Related|Exif-gpsdestdistance}}",
+       "exif-gpsdestdistance-m": "{{exif-qqq}}\n{{Related|Exif-gpsdestdistance}}",
+       "exif-gpsdestdistance-n": "{{exif-qqq}}\n{{Related|Exif-gpsdestdistance}}",
        "exif-gpsdop-excellent": "Parameters:\n* $1 - the actual HDOP/PDOP value (less than or equal to 2 for excellent). See http://en.wikipedia.org/wiki/Dilution_of_precision_(GPS)\n{{Related|Exif-gpsdop}}",
        "exif-gpsdop-good": "Parameters:\n* $1 - the actual HDOP/PDOP value (2-5 for good). See http://en.wikipedia.org/wiki/Dilution_of_precision_(GPS)\n{{Related|Exif-gpsdop}}",
        "exif-gpsdop-moderate": "Parameters:\n* $1 - the actual HDOP/PDOP value (5-10 for moderate). See http://en.wikipedia.org/wiki/Dilution_of_precision_(GPS)\n{{Related|Exif-gpsdop}}\n{{Identical|Moderate}}",
-       "exif-gpsdop-fair": "Parameters:\n* $1 - the actual HDOP/PDOP value (10-20 for fair). See http://en.wikipedia.org/wiki/Dilution_of_precision_(GPS)\n{{Related|Exif-gpsdop}}",
+       "exif-gpsdop-fair": "{{exif-qqq}}\n\nParameters:\n* $1 - the actual HDOP/PDOP value (10-20 for fair). See http://en.wikipedia.org/wiki/Dilution_of_precision_(GPS)\n\n{{Related|Exif-gpsdop}}",
        "exif-gpsdop-poor": "Parameters:\n* $1 - the actual HDOP/PDOP value (greater than 20 for poor). See http://en.wikipedia.org/wiki/Dilution_of_precision_(GPS)\n{{Related|Exif-gpsdop}}",
        "exif-objectcycle-a": "Morning only (a is for AM).\n\nSee also:\n* {{msg-mw|Exif-objectcycle}}\n* {{msg-mw|Exif-objectcycle-p}}\n* {{msg-mw|Exif-objectcycle-b}}",
-       "exif-objectcycle-p": "Evening only (p is for PM).\n\nSee also:\n* {{msg-mw|Exif-objectcycle}}\n* {{msg-mw|Exif-objectcycle-a}}\n* {{msg-mw|Exif-objectcycle-b}}",
+       "exif-objectcycle-p": "{{exif-qqq}}\n\nEvening only (p is for PM).\n\nSee also:\n* {{msg-mw|Exif-objectcycle}}\n* {{msg-mw|Exif-objectcycle-a}}\n* {{msg-mw|Exif-objectcycle-b}}",
        "exif-objectcycle-b": "Both morning and evening (b is for both).\n\nSee also:\n* {{msg-mw|Exif-objectcycle}}\n* {{msg-mw|Exif-objectcycle-a}}\n* {{msg-mw|Exif-objectcycle-p}}",
-       "exif-gpsdirection-t": "See also:\n* {{msg-mw|Exif-gpsdirection-m}}",
-       "exif-gpsdirection-m": "See also:\n* {{msg-mw|Exif-gpsdirection-t}}",
-       "exif-ycbcrpositioning-1": "If the Chrominance samples are centered with respect to the Luminance samples.\n{{Related|Exif-ycbcrpositioning}}",
+       "exif-gpsdirection-t": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-gpsdirection-m}}",
+       "exif-gpsdirection-m": "{{exif-qqq}}\n\nSee also:\n* {{msg-mw|Exif-gpsdirection-t}}",
+       "exif-ycbcrpositioning-1": "{{exif-qqq}}\n\nThis tag indicates that the Chrominance samples are centered with respect to the Luminance samples.\n\n{{Related|Exif-ycbcrpositioning}}",
        "exif-ycbcrpositioning-2": "If the Chrominance samples are on top of to the Luminance samples.\nSee: http://www.awaresystems.be/imaging/tiff/tifftags/ycbcrpositioning.html\n{{Related|Exif-ycbcrpositioning}}",
        "exif-dc-contributor": "People who helped make the resource, but are secondary in contribution to the author.\n{{Identical|Contributor}}",
        "exif-dc-coverage": "\"The extent or scope of the resource\" see dc:coverage in http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart2.pdf",
        "exif-iimcategory-evn": "Displayed as part of the iimcategory field if the 3 letter code is recognized, or as part {{msg-mw|exif-subjectnewscode-value}}",
        "exif-iimcategory-hth": "Displayed as part of the iimcategory field if the 3 letter code is recognized, or as part {{msg-mw|exif-subjectnewscode-value}}",
        "exif-iimcategory-hum": "Displayed as part of the iimcategory field if the 3 letter code is recognized, or as part {{msg-mw|exif-subjectnewscode-value}}",
-       "exif-iimcategory-lab": "Displayed as part of the iimcategory field if the 3 letter code is recognized, or as part {{msg-mw|exif-subjectnewscode-value}}\n\n\"Labor\" here refers to all news on labor issues; employment; unemployment; work relations; labor disputes; strikes; legislation; unions; job related issues; government policy. (at least, according to Reuters.)",
+       "exif-iimcategory-lab": "{{exif-qqq}}\n\nDisplayed as part of the iimcategory field if the 3 letter code is recognized, or as part {{msg-mw|exif-subjectnewscode-value}}\n\n\"Labor\" here refers to all news on labor issues; employment; unemployment; work relations; labor disputes; strikes; legislation; unions; job related issues; government policy. (at least, according to Reuters.)",
        "exif-iimcategory-lif": "Displayed as part of the iimcategory field if the 3 letter code is recognized, or as part {{msg-mw|exif-subjectnewscode-value}}",
        "exif-iimcategory-pol": "Displayed as part of the iimcategory field if the 3 letter code is recognized, or as part {{msg-mw|exif-subjectnewscode-value}}",
        "exif-iimcategory-rel": "Displayed as part of the iimcategory field if the 3 letter code is recognized, or as part {{msg-mw|exif-subjectnewscode-value}}",
        "exif-iimcategory-spo": "Displayed as part of the iimcategory field if the 3 letter code is recognized, or as part {{msg-mw|exif-subjectnewscode-value}}.\n{{Identical|Sport}}",
        "exif-iimcategory-war": "Displayed as part of the iimcategory field if the 3 letter code is recognized, or as part {{msg-mw|exif-subjectnewscode-value}}",
        "exif-iimcategory-wea": "Displayed as part of the iimcategory field if the 3 letter code is recognized, or as part {{msg-mw|exif-subjectnewscode-value}}",
-       "exif-urgency-normal": "Parameters:\n* $1 - numeric priority (aka 5 for normal)\n{{Related|Exif-urgency}}",
-       "exif-urgency-low": "Parameters:\n* $1 - numeric priority (6-8 for low)\n{{Related|Exif-urgency}}",
+       "exif-urgency-normal": "{{exif-qqq}}\n\nParameters:\n* $1 - numeric priority (aka 5 for normal)\n\n{{Related|Exif-urgency}}",
+       "exif-urgency-low": "{{exif-qqq}}\n\nParameters:\n* $1 - numeric priority (6-8 for low)\n\n{{Related|Exif-urgency}}",
        "exif-urgency-high": "Parameters:\n* $1 - numeric priority (1-4 for high)\n{{Related|Exif-urgency}}",
        "exif-urgency-other": "Parameters:\n* $1 - numeric priority. Most specs define 0 and 9 to either be reserved or not allowed. However the exiftool documentation defines 0 to be reserved and 9 to be user-defined priority.\n{{Related|Exif-urgency}}",
        "namespacesall": "In special page [[Special:WhatLinksHere]]. Drop-down box option for namespace.\n\n{{Identical|All}}",
        "confirmemail_body_set": "This is used in a confirmation email sent when a contact email address is set.\n\nSee also [[MediaWiki:Confirmemail body changed]].\n\nParameters:\n* $1 - the IP address of the user that set the email address\n* $2 - the name of the user\n* $3 - a URL to [[Special:ConfirmEmail]]\n* $4 - a time and date (duplicated by $6 and $7)\n* $5 - a URL to [[Special:InvalidateEmail]]\n* $6 - (Optional) a date\n* $7 - (Optional) a time\n{{Related|Confirmemail body}}",
        "confirmemail_invalidated": "This is the text of the special page [[Special:InvalidateEmail|InvalidateEmail]] (with the title in {{msg-mw|Invalidateemail}}) where user goes if he chooses the cancel e-mail confirmation link from the confirmation e-mail.",
        "invalidateemail": "This is the '''name of the special page''' where user goes if he chooses the cancel e-mail confirmation link from the confirmation e-mail.",
-       "notificationemail_subject_changed": "Subject of the email sent on the previously registered email address notifying them about the change in the registered email address.",
-       "notificationemail_subject_removed": "Subject of the email sent on the previously registered email address notifying them about the removal of the registered email address.",
+       "notificationemail_subject_changed": "Subject of the email sent to the previously registered email address notifying them about the change in the registered email address.",
+       "notificationemail_subject_removed": "Subject of the email sent to the previously registered email address notifying them about the removal of the registered email address.",
        "notificationemail_body_changed": "Body of the email sent on the previously registered email address notifying them about the change in the registered email address.",
        "notificationemail_body_removed": "Body of the email sent on the previously registered email address notifying them about the removal of the registered email address.",
        "scarytranscludedisabled": "Shown when scary transclusion is disabled.",
        "tags-active-yes": "Table cell contents if given tag is \"active\".\n\nSee also:\n* {{msg-mw|Tags-active-no}}\n{{Identical|Yes}}",
        "tags-active-no": "Table cell contents if given tag is not \"active\".\n\nSee also:\n* {{msg-mw|Tags-active-yes}}\n{{Identical|No}}",
        "tags-source-extension": "Table cell contents if given tag can be applied automatically by a software [[mw:Manual:Extensions|extension]].\n\nSee also:\n* {{msg-mw|Tags-source-manual}}\n* {{msg-mw|Tags-source-none}}",
-       "tags-source-manual": "Table cell contents if given tag can be applied by users or bots.\n\nSee also:\n* {{msg-mw|Tags-source-extension}}\n* {{msg-mw|Tags-source-none}}",
+       "tags-source-manual": "\"Applied\" is not past tense, but an adjective that describes an action that sometimes happens, as in the sentence: \"(this tag is usually) applied by users and bots\".\n\nTable cell contents if given tag can be applied by users or bots.\n\nSee also:\n* {{msg-mw|Tags-source-extension}}\n* {{msg-mw|Tags-source-none}}",
        "tags-source-none": "Table cell contents if given tag is no longer in use. (It was applied in the past, but it is currently not applied.)\n\nSee also:\n* {{msg-mw|Tags-source-extension}}\n* {{msg-mw|Tags-source-manual}}",
        "tags-edit": "Used on [[Special:Tags]]. Verb. Used as display text on a link to create/edit a description.\n{{Identical|Edit}}",
        "tags-delete": "Used on [[Special:Tags]]. Verb. Used as display text on a link to delete a tag.\n{{Identical|Delete}}",
        "logentry-delete-revision": "{{Logentry|[[Special:Log/delete]]}}\n{{Logentryparam}}\n* $5 - the number of affected revisions of the page $3",
        "logentry-delete-event-legacy": "{{Logentry|[[Special:Log/delete]]}}",
        "logentry-delete-revision-legacy": "{{Logentry|[[Special:Log/delete]]}}",
-       "logentry-suppress-delete": "{{Logentry}}\n\n'Hid' is a possible alternative to 'suppressed' in this message.",
+       "logentry-suppress-delete": "{{Logentry}}\n\n'Hid' is a possible alternative to 'suppressed' in this message.\n\n'''This is not a normal deletion log entry''' and is used only when the page is removed even from administrators view (on WMF wikis this is called 'oversight' or 'suppression'.",
        "logentry-suppress-event": "{{Logentry}}\n{{Logentryparam}}\n* $5 - count of affected log events",
        "logentry-suppress-revision": "{{Logentry}}\n{{Logentryparam}}\n* $5 - the number of affected revisions of the page $3.",
        "logentry-suppress-event-legacy": "{{Logentry}}",
        "mw-widgets-dateinput-placeholder-month": "Placeholder displayed in a date input field when it's empty, representing a date format with 4 digits for year and 2 digits for month, separated with hyphens (without a day). This should be uppercase, if possible, and must not include any additional explanations. If there is no good way to translate it, make this message blank.",
        "mw-widgets-titleinput-description-new-page": "Description label for a new page in the title input widget.",
        "mw-widgets-titleinput-description-redirect": "Description label for a redirect in the title input widget.",
-       "api-error-blacklisted": "Used as error message.\n\nFollowed by the link {{msg-mw|Mwe-upwiz-feedback-blacklist-info-prompt}}.",
        "sessionmanager-tie": "Used as an error message when multiple session sources are tied in priority.\n\nParameters:\n* $1 - List of dession type descriptions, from messages like {{msg-mw|sessionprovider-mediawiki-session-cookiesessionprovider}}.",
        "sessionprovider-generic": "Used to create a generic session type description when one isn't provided via the proper message. Should be phrased to make sense when added to a message such as {{msg-mw|cannotloginnow-text}}.\n\nParameters:\n* $1 - PHP classname.",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "Description of the sessions provided by the CookieSessionProvider class, which use HTTP cookies. Should be phrased to make sense when added to a message such as {{msg-mw|cannotloginnow-text}}.",
        "linkaccounts-submit": "Text of the main submit button on [[Special:LinkAccounts]] (when there is one)",
        "unlinkaccounts": "Title of the special page [[Special:UnlinkAccounts]] which allows the user to remove linked remote accounts.",
        "unlinkaccounts-success": "Account unlinking form success message",
-       "authenticationdatachange-ignored": "Shown when authentication data change was unsuccessful due to configuration problems.\n\nCf. e.g. {{msg-mw|Passwordreset-ignored}}."
+       "authenticationdatachange-ignored": "Shown when authentication data change was unsuccessful due to configuration problems.\n\nCf. e.g. {{msg-mw|Passwordreset-ignored}}.",
+       "userjsispublic": "A reminder to users that Javascript subpages are not preferences but normal pages, and thus can be viewed by other users and the general public. This message is shown to a user whenever they are editing a subpage in their own user-space that ends in .js. See also {{msg-mw|usercssispublic}}.",
+       "usercssispublic": "A reminder to users that CSS subpages are not preferences but normal pages, and thus can be viewed by other users and the general public. This message is shown to a user whenever they are editing a subpage in their own user-space that ends in .css. See also {{msg-mw|userjsispublic}}"
 }
index de84e8e..bb89a09 100644 (file)
        "yourpasswordagain": "Yaykuna rimaykita kutipayay",
        "createacct-yourpasswordagain": "Yaykuna rimata takyachiy",
        "createacct-yourpasswordagain-ph": "Yaykuna rimata musuqmanta yaykuchiy",
-       "remembermypassword": "Ruraqpa sutiyta yaykuna rimaytapas yuyaykuy llamk'ay tiyayniypura ({{PLURAL:$1|huk p'unchawkama|$1 p'unchawkama}})",
        "userlogin-remembermypassword": "Yaykusqa kakunaytam munani",
        "userlogin-signwithsecure": "Amachasqa t'inkinakusqata llamk'achiy",
        "yourdomainname": "Duminyuykip sutin",
        "passwordreset-emailtext-user": "{{SITENAME}}-pi kaq $1 sutiyuq ruraqqa {{SITENAME}}-paq ($4)\nrakiqunaykipaq yaykuna rimata kutichinatam mañakurqan. Kay qatiq ruraqpa {{PLURAL:$3|rakiqunanmi|rakiqunankunam}}\nkay e-chaski imamaytayuq kachkan:\n\n$2\n\nKay mit'alla yaykuna {{PLURAL:$3|rimaqa|rimakunaqa}} kunanmanta {{PLURAL:$5|huk p'unchawpi|$5 p'unchawpi}} mawk'ayanqam.\nYaykuspayki musuq yaykuna rimaykitam akllankiman. Pi wakiykipas kayta mañakurqaptinqa,\nicha qam ñawpaq yaykuna rimaykita yuyaspayki manaña wakinchayta munaspaykiqa,\nkay willayta mana qhawaspa mana imatapas ruraspa ñawpaq yaykuna rimaykiwanmi llamk'ayta atinki.",
        "passwordreset-emailelement": "Ruraqpa sutin: \n$1\n\nMit'alla yaykuna rima: \n$2",
        "passwordreset-emailsentemail": "Yaykuna rimata kutichina e-chaskiqa kachasqañam.",
-       "passwordreset-emailsent-capture": "Yaykuna rimata kutichina e-chaskiqa kachasqañam, kay qatiqpi rikunki.",
-       "passwordreset-emailerror-capture": "{{GENDER:$2|}}Yaykuna rimata kutichina e-chaskiqa rurasqa karqan, imatachus kay qatiqpi rikunki, ichataq kachasqa kaptin pantasqam tukurqan: $1",
        "changeemail": "E-chaski imamaytata wakinchay",
        "changeemail-header": "Rakiqunap e-chaski imamaytanta wakinchay",
        "changeemail-no-info": "Yaykunaykim tiyan kay p'anqata chiqalla aypanaykipaq.",
        "minoredit": "Kayqa uchuylla hukchaymi",
        "watchthis": "Kay qillqata watiqay",
        "savearticle": "P'anqata waqaychay",
+       "savechanges": "Hukchasqata waqaychay",
        "preview": "Manaraq waqaychaspa qhawariy",
        "showpreview": "Ñawpaqta qhawallay",
        "showdiff": "Hukchasqakunata rikuchiy",
        "undo-norev": "Manam atinichu llamk'apusqata kutichiyta, mana kaptinmi icha qullusqa kaptinmi.",
        "undo-summary": "[[Special:Contributions/$2|$2]]-pa $1 hukchasqanta kutichisqa ([[User talk:$2|rimay]])",
        "undo-summary-username-hidden": "Pakasqa ruraqpa $1 nisqa musuqchasqata kutichiy",
-       "cantcreateaccounttitle": "Manam atinichu rakiqunata kichayta",
        "cantcreateaccount-text": "Kay IP tiyaymanta ('''$1''') rakiquna kichariyqa [[User:$3|$3]]-pa hark'asqanmi.\n\n$3-qa nirqan kayraykum: ''$2''",
        "viewpagelogs": "Kay p'anqamanta hallch'akunata qhaway",
        "nohistory": "Kay p'anqamantaqa manam llamk'apuy wiñay kawsay kanchu.",
index 6171017..cc77f46 100644 (file)
@@ -29,7 +29,8 @@
                        "ImGelu",
                        "Wintereu",
                        "Rsocol",
-                       "Nemo bis"
+                       "Nemo bis",
+                       "Turbojet"
                ]
        },
        "tog-underline": "Sublinierea legăturilor:",
        "tagline": "De la {{SITENAME}}",
        "help": "Ajutor",
        "search": "Căutare",
+       "search-ignored-headings": " #<!-- nu modificați acest rând --> <pre>\n# Titluri de secțiuni care vor fi ignorate de motorul de căutare.\n# Modificările efectuate aici își vor face efectul de îndată ce pagina care conține titlul de secțiune va fi indexată.\n# Puteți forța reindexarea paginii efectuând o modificare vidă.\n# Sintaxa este următoarea:\n#   * Orice rând precedat de caracterul „#” reprezintă un comentariu.\n#   * Orice rând nevid reprezintă exact titlu de secțiune de ignorat.\nReferințe\nLegături externe\nVezi și\n #</pre> <!-- nu modificați acest rând -->",
        "searchbutton": "Căutare",
        "go": "Salt",
        "searcharticle": "Du-te",
        "yourpasswordagain": "Repetați parola:",
        "createacct-yourpasswordagain": "Confirmare parolă",
        "createacct-yourpasswordagain-ph": "Introduceți parola din nou",
-       "remembermypassword": "Autentificare automată de la acest calculator (expiră după {{PLURAL:$1|24 de ore|$1 zile|$1 de zile}})",
        "userlogin-remembermypassword": "Păstrează-mă autentificat",
        "userlogin-signwithsecure": "Utilizează conexiunea securizată",
+       "cannotlogin-title": "Imposibil de autentificat",
+       "cannotlogin-text": "Autentificarea nu este posibilă.",
        "cannotloginnow-title": "Nu se poate conecta acum",
        "cannotloginnow-text": "Conectarea nu este posibilă când se utilizează $1.",
+       "cannotcreateaccount-title": "Imposibil de creat conturi",
+       "cannotcreateaccount-text": "Crearea directă de conturi nu este activată pe acest wiki.",
        "yourdomainname": "Domeniul dumneavoastră:",
        "password-change-forbidden": "Nu puteți schimba parole pe acest wiki.",
        "externaldberror": "A fost fie o eroare de bază de date pentru o autentificare extenă sau nu aveți permisiunea să actualizați contul extern.",
        "passwordreset-emailelement": "Nume de utilizator: \n$1\n\nParolă temporară: \n$2",
        "passwordreset-emailsentemail": "Dacă această adresă de e-mail este asociată contului dumneavoastră, atunci se va trimite un e-mail de resetare a parolei.",
        "passwordreset-emailsentusername": "Dacă există o adresă de e-mail asociată acestui nume de utilizator, atunci se va trimite un e-mail de resetare a parolei.",
-       "passwordreset-emailsent-capture": "Un mesaj de resetare a parolei a fost trimis, fiind afișat mai jos.",
-       "passwordreset-emailerror-capture": "Un mesaj de resetare a parolei a fost generat (fiind afișat mai jos), dar trimiterea sa către {{GENDER:$2|utilizator}} a eșuat: $1",
        "passwordreset-invalideamil": "Adresă de e-mail nevalidă",
        "passwordreset-nodata": "Nu au fost furnizate un nume de utilizator sau o adresă de e-mail",
        "changeemail": "Modificare sau înlăturare adresă de e-mail",
        "changeemail-header": "Completați acest formular pentru a vă schimba adresa de e-mail. Dacă doriți să înlăturați orice asociere a unei adrese de e-mail cu contul dumneavoastră, lăsați necompletat câmpul pentru introducerea unei noi adrese de e-mail atunci când trimiteți formularul.",
-       "changeemail-passwordrequired": "Va trebui să introduceți parola pentru a confirma această schimbare.",
        "changeemail-no-info": "Trebuie să fiți autentificat pentru a accesa această pagină direct.",
        "changeemail-oldemail": "Adresa de e-mail actuală:",
        "changeemail-newemail": "Noua adresă de e-mail:",
        "minoredit": "Aceasta este o modificare minoră",
        "watchthis": "Urmărește această pagină",
        "savearticle": "Salvare pagină",
+       "savechanges": "Salvează modificările",
        "publishpage": "Publică pagina",
+       "publishchanges": "Publică modificările",
        "preview": "Previzualizare",
        "showpreview": "Previzualizare",
        "showdiff": "Afișare diferențe",
        "undo-nochange": "Se pare că această modificare a fost deja anulată.",
        "undo-summary": "Anularea modificării $1 făcute de [[Special:Contributions/$2|$2]] ([[User talk:$2|Discuție]])",
        "undo-summary-username-hidden": "Anularea versiunii $1 a unui utilizator ascuns",
-       "cantcreateaccounttitle": "Crearea contului nu poate fi realizată",
        "cantcreateaccount-text": "Crearea de conturi de la această adresă IP ('''$1''') a fost blocată de [[User:$3|$3]].\n\nMotivul invocat de $3 este ''$2''",
        "cantcreateaccount-range-text": "Crearea de conturi de la adresele IP din gama <strong>$1</strong>, care o include și pe a dumneavoastră (<strong>$4</strong>), a fost blocată de [[User:$3|$3]].\n\nMotivul invocat de $3 este <em>$2</em>",
        "viewpagelogs": "Afișează jurnalele paginii",
        "rightslogtext": "Acest jurnal cuprinde modificările permisiunilor utilizatorilor.",
        "action-read": "citiți această pagină",
        "action-edit": "modificați această pagină",
-       "action-createpage": "creați pagini",
-       "action-createtalk": "creați pagini de discuție",
+       "action-createpage": "creați această pagină",
+       "action-createtalk": "creați această pagină de discuție",
        "action-createaccount": "creați acest cont de utilizator",
        "action-history": "vizualizați istoricul acestei pagini",
        "action-minoredit": "marcați această modificare ca minoră",
        "watchlistedit-raw-done": "Lista paginilor urmărite a fost actualizată.",
        "watchlistedit-raw-added": "{{PLURAL:$1|1 titlu a fost adăugat|$1 titluri au fost adăugate}}:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|1 titlu a fost șters|$1 titluri au fost șterse}}:",
-       "watchlistedit-clear-title": "Listă de pagini urmărite golită",
+       "watchlistedit-clear-title": "Golire listă de pagini urmărite",
        "watchlistedit-clear-legend": "Golire listă de pagini urmărite",
        "watchlistedit-clear-explain": "Toate titlurile vor fi înlăturate din lista dumnevoastră de pagini urmărite",
        "watchlistedit-clear-titles": "Titluri:",
        "mw-widgets-dateinput-placeholder-month": "AAAA-LL",
        "mw-widgets-titleinput-description-new-page": "pagina nu există încă",
        "mw-widgets-titleinput-description-redirect": "redirecționare către $1",
-       "api-error-blacklisted": "Vă rugăm să alegeți un alt titlu, mai descriptiv.",
        "sessionmanager-tie": "Nu se pot combina multiple tipuri de cereri de autentificare: $1.",
        "sessionprovider-generic": "sesiuni $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sesiuni pe bază de module cookie.",
index 27535c3..a9db650 100644 (file)
        "tagline": "Da {{SITENAME}}",
        "help": "Ajute",
        "search": "Cirche",
+       "search-ignored-headings": " #<!-- lasse sta linèe accume ste --> <pre>\n# Testate ca avène scettate jndr'à le ricerche.\n# Le cangiaminde devendane effettive quanne 'a pàgene avène indicizzate.\n# Tu puè forzà 'a reindicizzazzione d'a pàgene facenne 'nu cangiamende vecande.\n# 'A sindasse jè 'a seguende:\n#   * Ogneccose da 'u carattere \"#\" 'nzigne a fine d'a linèe jè 'nu commende\n#   * Ogne linèa chiene jè 'u titole esatte da ignorà, case e ogneccose\nRefereminde\nCollegaminde de fore\n'Ndruche pure\n #</pre> <!-- lasse sta linèe accume ste -->",
        "searchbutton": "Cirche",
        "go": "Véje",
        "searcharticle": "Véje",
        "passwordreset-emailtext-user": "L'utende $1 sus a {{SITENAME}} ave richieste 'na mail pe arrecurdarse le dettaglie d'u cunde sue pe {{SITENAME}}\n($4). {{PLURAL:$3|'U cunde utende seguende jè|le cunde utinde seguende sonde}} associate cu st'indirizze e-mail:\n\n$2\n\n{{PLURAL:$3|Sta passuord temboranèe scade|Ste passuord temboranèe scadene}}  'mbrà {{PLURAL:$5|'nu sciurne|$5 sciurne}}.\nTu avissa trasè e scacchià 'na passuord nova. Ce quacchedun'otre ha fatte sta richieste, o ce tu t'è arrecurdate 'a passuord origgenale toje, e non g'a vuè ccu cange cchiù, tu puè ignorà stu messagge e condinuà ausanne 'a passuord vecchie.",
        "passwordreset-emailelement": "Nome utende: \n$1\n\nPassuord temboranèe: \n$2",
        "passwordreset-emailsentemail": "Ce quiste jè 'n'e-mail pu cunde tune, allore 'na password azzerate ha state mannate addà.",
-       "passwordreset-emailsent-capture": "'Na e-mail pe azzeramende d'a passuord ha state mannate, ca jè fatte vedè aqquà sotte.",
-       "passwordreset-emailerror-capture": "'Na e-mail de azzeramende d'a passuord ha state generate, ca jè fatte vedè aqquà sotte, ma 'u 'nvie a {{GENDER:$2|l'utende}} ha fallite: $1",
        "changeemail": "Cange o live 'u 'ndirizze e-mail",
        "changeemail-header": "Comblete stu module pe cangià 'u 'ndirizze email. Ce tu vuè ccu live l'associazione cu ogne indirizze email da 'u cunde tune, lasse 'u 'ndirizze email vacande quanne conferme 'u module.",
        "changeemail-no-info": "Tu a essere collegate pe accedere a sta pàgene direttamende.",
        "undo-nochange": "'U cangiamende pare ca ha state già annullate.",
        "undo-summary": "Repristine 'a revisione $1 da [[Special:Contributions/$2|$2]] ([[User talk:$2|'Ngazzaminde]])",
        "undo-summary-username-hidden": "Annulle 'a revisione $1 da 'n'utende scunnute",
-       "cantcreateaccounttitle": "Non ge puè ccrejà 'nu cunde utende",
        "cantcreateaccount-text": "'A creazione d'u cunde utende da stu 'ndirizze IP ('''$1''') ha state blocchete da [[User:$3|$3]].\n\n'U mutive dete da $3 jè ''$2''",
        "cantcreateaccount-range-text": "'A crejazzione d'u cunde da l'indirizze IP jndr'à l'indervalle \"$1\", 'u quale 'nglude 'u 'ndirizze IP tune (<strong>$4</strong>), ha state bloccate da [[User:$3|$2]].",
        "viewpagelogs": "Vide l'archivie pe sta pàgene",
        "mw-widgets-dateinput-no-date": "Nisciune date scacchiate",
        "mw-widgets-titleinput-description-new-page": "'a pàgene non g'esiste angore",
        "mw-widgets-titleinput-description-redirect": "redirezionate sus a $1",
-       "api-error-blacklisted": "Pe piacere scacchie 'nu titole diverse, descrittive.",
        "randomrootpage": "Pàgene prengepàle a uecchije"
 }
index a20fd8c..0dade8b 100644 (file)
@@ -92,7 +92,9 @@
                        "Lemondoge",
                        "SamGold",
                        "Jdforrester",
-                       "Jack who built the house"
+                       "Jack who built the house",
+                       "Cat1987",
+                       "SergeyButkov"
                ]
        },
        "tog-underline": "Подчёркивание ссылок:",
        "tagline": "Материал из {{grammar:genitive|{{SITENAME}}}}",
        "help": "Справка",
        "search": "Поиск",
+       "search-ignored-headings": " #<!-- оставьте эту строку как есть --> <pre>\n# Заголовки, которые будут игнорироваться поиском.\n# Изменения вступают в силу, как только страница с заголовком индексируется.\n# Вы можете принудить переиндексировать страницу с помощью нулевой правки.\n# Синтаксис выглядит следующим образом:\n#   * Всё, начинающееся на символ «#» и до конца строки представляет собой комментарий.\n#   * Каждая непустая строка — точное название того, что будет игнорироваться, включая регистр и пр.\nПримечания\nСсылки\nСм. также\n #</pre> <!-- оставьте эту строку как есть -->",
        "searchbutton": "Найти",
        "go": "Перейти",
        "searcharticle": "Перейти",
        "poolcounter-usage-error": "Ошибка использования: $1",
        "aboutsite": "Описание {{grammar:genitive|{{SITENAME}}}}",
        "aboutpage": "Project:Описание",
-       "copyright": "СодеÑ\80жимое доступно по лицензии $1 (если не указано иное).",
+       "copyright": "СодеÑ\80жание доступно по лицензии $1 (если не указано иное).",
        "copyrightpage": "{{ns:project}}:Авторские права",
        "currentevents": "Текущие события",
        "currentevents-url": "Project:Текущие события",
        "site-atom-feed": "$1 — Atom-лента",
        "page-rss-feed": "«$1» — RSS-лента",
        "page-atom-feed": "«$1» — Atom-лента",
-       "feed-atom": "Атом",
+       "feed-atom": "Atom",
        "red-link-title": "$1 (страница не существует)",
        "sort-descending": "Упорядочить по убыванию",
        "sort-ascending": "Упорядочить по возрастанию",
        "title-invalid-characters": "Запрашиваемое название страницы содержит недопустимые символы: «$1».",
        "title-invalid-relative": "Заголовок имеет относительный путь. Заголовки страниц с относительным путем (/,../) являются недействительными, так как они часто недоступны, когда обрабатываются браузером пользователя.",
        "title-invalid-magic-tilde": "Запрашиваемый заголовок страницы содержит недопустимую последовательность тильды (<nowiki>~~~</nowiki>).",
-       "title-invalid-too-long": "Запрашиваемый заголовок страницы слишком длинен. Он должен быть не более $1 {{PLURAL:$1|байта|байтов}} в кодировке UTF-8.",
+       "title-invalid-too-long": "Запрашиваемый заголовок страницы слишком длинен. Он должен быть не более $1 {{PLURAL:$1|байта|байт}} в кодировке UTF-8.",
        "title-invalid-leading-colon": "Запрашиваемое название страницы содержит недопустимое двоеточие в начале.",
        "perfcached": "Следующие данные взяты из кэша и могут не учитывать последних изменений. В кэше хранится не более $1 {{PLURAL:$1|записи|записей}}.",
        "perfcachedts": "Следующие данные взяты из кэша, последний раз он обновлялся в $1. В кэше хранится не более $4 {{PLURAL:$4|записи|записей}}.",
        "querypage-no-updates": "Обновление этой страницы сейчас отключено.\nПредставленные здесь данные не будут обновляться.",
-       "viewsource": "Ð\9fÑ\80оÑ\81моÑ\82Ñ\80 ÐºÐ¾Ð´а",
+       "viewsource": "Ð\9fÑ\80оÑ\81моÑ\82Ñ\80 Ð²Ð¸ÐºÐ¸-Ñ\82екÑ\81Ñ\82а",
        "viewsource-title": "Просмотр исходного текста страницы $1",
        "actionthrottled": "Ограничение по скорости",
        "actionthrottledtext": "Для борьбы со спамом было установлено ограничение на максимальное число попыток выполнения этого действия в короткий промежуток времени — и вы исчерпали этот лимит. Пожалуйста, повторите попытку через несколько минут.",
        "yourpasswordagain": "Повторный набор пароля:",
        "createacct-yourpasswordagain": "Подтвердите пароль",
        "createacct-yourpasswordagain-ph": "Введите пароль еще раз",
-       "remembermypassword": "Помнить мою учётную запись на этом компьютере (не более $1 {{PLURAL:$1|дня|дней}})",
        "userlogin-remembermypassword": "Оставаться в системе",
        "userlogin-signwithsecure": "Защищённое соединение",
+       "cannotlogin-title": "Невозможно войти",
+       "cannotlogin-text": "Вход в систему невозможен.",
        "cannotloginnow-title": "Невозможно войти прямо сейчас",
        "cannotloginnow-text": "Нельзя войти во время использования $1.",
+       "cannotcreateaccount-title": "Невозможно создать учётные записи",
+       "cannotcreateaccount-text": "Прямое создание учетных записей не включено в этой вики.",
        "yourdomainname": "Ваш домен:",
        "password-change-forbidden": "Вы не можете изменить пароль в этой вики.",
        "externaldberror": "Произошла ошибка при аутентификации с помощью внешней базы данных или у вас недостаточно прав для внесения изменений в свою внешнюю учётную запись.",
        "passwordreset-emailelement": "Имя участника: \n$1\n\nВременный пароль: \n$2",
        "passwordreset-emailsentemail": "Если это адрес электронной почты связан с вашей учётной записью, вам будет отправлено письмо для сброса пароля.",
        "passwordreset-emailsentusername": "Если есть адрес электронной почты, связанный с этим именем участника, то будет отправлено письмо для восстановления пароля.",
-       "passwordreset-emailsent-capture": "Отправлено электронное письмо с информацией о сбросе пароля, текст которого можно увидеть ниже.",
-       "passwordreset-emailerror-capture": "Было создано электронное письмо с информацией о сбросе пароля, текст которого можно увидеть ниже, однако его не удалось отправить {{GENDER:$2|участнику|участнице}} по следующей причине: $1",
        "passwordreset-emailsent-capture2": "{{PLURAL:$1|Письмо|Письма}} для сброса пароля {{PLURAL:$1|было отправлено|были отправлены}}. {{PLURAL:$1|Логин и пароль показаны|Список логинов и паролей показан}} ниже.",
        "passwordreset-emailerror-capture2": "Отправка {{GENDER:$2|участнику}} письма по электронной почте не удалась: $1 В {{PLURAL:$3|логин и пароль показаны|список логинов и паролей показан}} ниже.",
+       "passwordreset-nocaller": "Должен быть предоставлен источник вызова",
+       "passwordreset-nosuchcaller": "Источник вызова не существует: $1",
+       "passwordreset-ignored": "Сброс пароля не был обработан. Может быть, не был настроен ни один провайдер?",
        "passwordreset-invalideamil": "Недопустимый адрес электронной почты",
+       "passwordreset-nodata": "Ни имя участника, ни адрес электронной почты не были предоставлены",
        "changeemail": "Изменить или удалить адрес электронной почты",
        "changeemail-header": "Заполните эту форму, чтобы изменить свой адрес электронной почты. Если вы хотите отвязать свой адрес электронной почты от учётной записи, то при заполнении формы оставьте пустым поле нового адреса электронной почты.",
-       "changeemail-passwordrequired": "Чтобы подтвердить это изменение, вам нужно будет ввести свой пароль.",
        "changeemail-no-info": "Чтобы обращаться непосредственно к этой странице, вам следует представиться системе.",
        "changeemail-oldemail": "Текущий адрес электронной почты:",
        "changeemail-newemail": "Новый адрес электронной почты:",
        "summary": "Описание изменений:",
        "subject": "Тема/заголовок:",
        "minoredit": "Малое изменение",
-       "watchthis": "Ð\92клÑ\8eÑ\87иÑ\82Ñ\8c Ñ\8dÑ\82Ñ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 Ð² Ñ\81пиÑ\81ок Ð½Ð°Ð±Ð»Ñ\8eдениÑ\8f",
+       "watchthis": "СледиÑ\82Ñ\8c Ð·Ð° Ñ\8dÑ\82ой Ñ\81Ñ\82Ñ\80аниÑ\86ей",
        "savearticle": "Записать страницу",
        "savechanges": "Сохранить изменения",
-       "publishpage": "издавать страницу",
-       "publishchanges": "Ð\9eпÑ\83бликоваÑ\82Ñ\8c Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f",
+       "publishpage": "Создать страницу",
+       "publishchanges": "Ð\97апиÑ\81аÑ\82Ñ\8c Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83",
        "preview": "Предпросмотр",
        "showpreview": "Предварительный просмотр",
        "showdiff": "Внесённые изменения",
        "content-model-css": "CSS",
        "content-json-empty-object": "Пустой объект",
        "content-json-empty-array": "Пустой массив",
+       "deprecated-self-close-category": "Страницы, использующие недопустимые самозакрывающеся HTML-теги",
+       "deprecated-self-close-category-desc": "Страница содержит недопустимые самозакрывающиеся HTML-теги, такие как <code>&lt;b/></code> или <code>&lt;span/></code>. В скором времени их действие изменится, чтобы соответствовать спецификации HTML5, так что использование этих устаревших тегов в вики-тексте нежелательно.",
        "duplicate-args-warning": "<strong>Внимание:</strong> [[:$1]] вызывает [[:$2]] с более чем одним значением параметра «$3». Будет использовано только последнее указанное значение.",
        "duplicate-args-category": "Страницы, использующие повторяющиеся аргументы в вызовах шаблонов",
        "duplicate-args-category-desc": "Страницы, содержащие вызовы шаблонов, использующие повторяющиеся аргументы, такие как <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> или <code><nowiki>{{foo|bar|1=bar}}</nowiki></code>.",
        "undo-nochange": "Правка, похоже, уже была отменена.",
        "undo-summary": "Отмена правки $1, сделанной {{GENDER:$2|участником|участницей}} [[Special:Contribs/$2|$2]] ([[User talk:$2|обс.]])",
        "undo-summary-username-hidden": "Отмена правки $1, сделанной участником, чьё имя скрыто",
-       "cantcreateaccounttitle": "Невозможно создать учётную запись",
        "cantcreateaccount-text": "Создание учётных записей с этого IP-адреса ('''$1''') было заблокировано {{GENDER:$3|участником|участницей|}} [[User:$3|$3]].\n\n$3 {{GENDER:$3|указал|указала}} следующую причину: ''$2''.",
        "cantcreateaccount-range-text": "{{GENDER:$3|Участник|Участница}} [[User:$3|$3]] {{GENDER:$3|установил|установила}} запрет на создание учётных записей из диапазона IP-адресов <strong>$1</strong>, включающего ваш IP-адрес (<strong>$4</strong>). \n\nБыла указана следующая причина: $2.",
        "viewpagelogs": "Показать журналы для этой страницы",
        "mergehistory-from": "Исходная страница:",
        "mergehistory-into": "Целевая страница:",
        "mergehistory-list": "Объединяемая история правок",
-       "mergehistory-merge": "СледÑ\83Ñ\8eÑ\89ие Ð²ÐµÑ\80Ñ\81ии [[:$1]] Ð¼Ð¾Ð³Ñ\83Ñ\82 Ð±Ñ\8bÑ\82Ñ\8c Ð¾Ð±Ñ\8aединенÑ\8b Ð² [[:$2]]. Ð\98Ñ\81полÑ\8cзÑ\83йÑ\82е Ð¿ÐµÑ\80еклÑ\8eÑ\87аÑ\82ели Ð´Ð»Ñ\8f Ñ\82ого, чтобы объединить только выбранный диапазон правок. Учтите, что использование навигационных ссылок сбросит эту колонку.",
+       "mergehistory-merge": "СледÑ\83Ñ\8eÑ\89ие Ð²ÐµÑ\80Ñ\81ии [[:$1]] Ð¼Ð¾Ð³Ñ\83Ñ\82 Ð±Ñ\8bÑ\82Ñ\8c Ð¿ÐµÑ\80енеÑ\81енÑ\8b Ð² [[:$2]]. Ð\98Ñ\81полÑ\8cзÑ\83йÑ\82е Ð¿ÐµÑ\80еклÑ\8eÑ\87аÑ\82ели, чтобы объединить только выбранный диапазон правок. Учтите, что использование навигационных ссылок сбросит эту колонку.",
        "mergehistory-go": "Показать объединяемые правки",
        "mergehistory-submit": "Объединить правки",
        "mergehistory-empty": "Не найдены правки для объединения.",
        "searchall": "все",
        "showingresults": "Ниже {{PLURAL:$1|1=показан <strong>1</strong> результат|показан  <strong>$1</strong> результат|показано <strong>$1</strong> результата|показаны <strong>$1</strong> результатов}}, начиная с № <strong>$2</strong>.",
        "showingresultsinrange": "Ниже показано до {{PLURAL:$1|<strong>1</strong> результата|<strong>$1</strong> результата|<strong>$1</strong> результатов}} в диапазоне от <strong>$2</strong> до <strong>$3</strong>.",
-       "search-showingresults": "{{PLURAL:$4|Результат <strong>$1</strong> из <strong>$3</strong>|Результаты <strong>$1 — $2</strong> из <strong>$3</strong>}}",
+       "search-showingresults": "{{PLURAL:$4|1=Результат <strong>$1</strong> из <strong>$3</strong>|Результаты <strong>$1—$2</strong> из <strong>$3</strong>}}",
        "search-nonefound": "Соответствий запросу не найдено.",
        "search-nonefound-thiswiki": "Нет результатов, соответствующих запросу на этом сайте.",
        "powersearch-legend": "Расширенный поиск",
        "prefs-watchlist-token": "Токен списка наблюдения:",
        "prefs-misc": "Другие настройки",
        "prefs-resetpass": "Изменить пароль",
-       "prefs-changeemail": "Ð\98зменить или удалить адрес электронной почты",
+       "prefs-changeemail": "изменить или удалить адрес электронной почты",
        "prefs-setemail": "Установка адреса эл. почты",
        "prefs-email": "Параметры электронной почты",
        "prefs-rendering": "Внешний вид",
        "grant-group-high-volume": "Выполнение действий с высокой интенсивностью",
        "grant-group-customization": "Настройки и предпочтения",
        "grant-group-administration": "Выполнение административных действий",
+       "grant-group-private-information": "Доступ к личной информации о вас",
        "grant-group-other": "Разная активность",
        "grant-blockusers": "Блокировка и разблокировка учётных записей",
        "grant-createaccount": "Создание учётных записей",
        "grant-highvolume": "Редактирование с высокой интенсивностью",
        "grant-oversight": "Сокрытие правок участников и версий страниц",
        "grant-patrol": "Патрулирование изменений страниц",
+       "grant-privateinfo": "Доступ к личной информации",
        "grant-protect": "Защита страниц и снятие защиты",
        "grant-rollback": "Откат изменений страниц",
        "grant-sendemail": "Отправка электронной почты другим участникам",
        "action-applychangetags": " применять теги наряду с Вашими изменениями",
        "action-changetags": "Добавлять и удалять произвольные теги на отдельных изменениях и записях в журнале",
        "action-deletechangetags": "удаление меток из базы данных",
+       "action-purge": "очистку кэша этой страницы",
        "nchanges": "$1 {{PLURAL:$1|изменение|изменения|изменений}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|с последнего посещения}}",
        "enhancedrc-history": "история",
        "file-thumbnail-no": "Название файла начинается с <strong>$1</strong>.\nВероятно, это уменьшенная копия изображения ''(миниатюра)''.\nЕсли у вас есть данное изображение в полном размере, пожалуйста, загрузите его или измените имя файла.",
        "fileexists-forbidden": "Файл с этим именем уже существует и не может быть перезаписан.\nЕсли всё равно хотите загрузить данный файл, пожалуйста, вернитесь назад и загрузите его под другим именем. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Файл с этим именем уже существует в общем хранилище файлов.\nЕсли вы всё-таки хотите загрузить этот файл, пожалуйста, вернитесь назад и измените имя файла. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Эта загрузка является точной копией текущей версии файла <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Эта загрузка является точной копией {{PLURAL:$2|более старой версии|более старых версий}} файла <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Этот файл — дубликат {{PLURAL:$1|1=следующего файла|следующих файлов}}:",
        "file-deleted-duplicate": "Подобный файл ([[:$1]]) уже удалялся. Пожалуйста, ознакомьтесь с историей удаления файла, прежде чем загружать его снова.",
        "file-deleted-duplicate-notitle": "Файл, идентичный этому файлу, был ранее удалён, а имя файла было запрещено.\nВам следует попросить кого-нибудь с правами просмотра данных по запрещённым файлам, чтобы он проанализировал ситуацию перед тем, как загружать файл снова.",
        "zip-wrong-format": "Указанный файл — не ZIP-архив.",
        "zip-bad": "ZIP-файл повреждён или не может быть прочитан.\nОн не может быть должным образом проверен.",
        "zip-unsupported": "Этот ZIP-файл использует возможности, не поддерживаемые MediaWiki.\nОн не может быть должным образом проверен.",
-       "uploadstash": "СкÑ\80Ñ\8bÑ\82наÑ\8f Ð·Ð°Ð³Ñ\80Ñ\83зка",
+       "uploadstash": "Ð\97агÑ\80Ñ\83зка Ð²Ð¾ Ð²Ñ\80еменное Ñ\85Ñ\80анилиÑ\89е",
        "uploadstash-summary": "Данная страница предоставляет доступ к файлам, которые были загружены (или находятся в процессе загрузки), но ещё не были опубликованы в вики. Эти файлы никому не видны, кроме загрузившего их участника.",
-       "uploadstash-clear": "Очистить скрытые файлы",
-       "uploadstash-nofiles": "У вас нет скрытых файлов.",
+       "uploadstash-clear": "Очистить временные файлы",
+       "uploadstash-nofiles": "У вас нет временных файлов.",
        "uploadstash-badtoken": "Не удалось выполнить указанные действия. Возможно, истёк срок действия ваших учётных данных. Пожалуйста, пробуйте ещё раз.",
        "uploadstash-errclear": "Очистка файлов не удалась.",
        "uploadstash-refresh": "Обновить список файлов",
        "uploadstash-thumbnail": "показать миниатюру",
+       "uploadstash-exception": "Не удалось сохранить загрузку во временное хранилище ($1): «$2».",
        "invalid-chunk-offset": "Недопустимое смещение фрагмента",
        "img-auth-accessdenied": "Доступ запрещён",
        "img-auth-nopathinfo": "Отсутствует <code>PATH_INFO</code>.\nВаш сервер не настроен для передачи этих сведений.\nВозможно, он работает на основе CGI и не поддерживает <code>img_auth</code>.\nСм. https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "filerevert-submit": "Возвратить",
        "filerevert-success": "'''[[Media:$1|$1]]''' был возвращён к [$4 версии от $3, $2].",
        "filerevert-badversion": "Не существует предыдущей локальной версии этого файла с указанной меткой времени.",
+       "filerevert-identical": "Текущая версия файла уже идентична выбранной.",
        "filedelete": "$1 — удаление",
        "filedelete-legend": "Удалить файл",
        "filedelete-intro": "Вы собираетесь удалить файл '''[[Media:$1|$1]]''' со всей его историей.",
        "withoutinterwiki-legend": "Префикс",
        "withoutinterwiki-submit": "Показать",
        "fewestrevisions": "Страницы с наименьшим количеством версий",
-       "nbytes": "$1 {{PLURAL:$1|байт|байта|байтов}}",
+       "nbytes": "$1 {{PLURAL:$1|байт|байта|байт}}",
        "ncategories": "$1 {{PLURAL:$1|категория|категории|категорий}}",
        "ninterwikis": "$1 {{PLURAL:$1|интервики-ссылка|интервики-ссылки|интервики-ссылок}}",
        "nlinks": "$1 {{PLURAL:$1|ссылка|ссылки|ссылок}}",
        "trackingcategories-name": "Имя сообщения",
        "trackingcategories-desc": "Критерий включения в категорию",
        "restricted-displaytitle-ignored": "Страницы с игнорируемыми отображаемыми названиями",
+       "restricted-displaytitle-ignored-desc": "На странице есть игнорируемый <code><nowiki>{{DISPLAYTITLE}}</nowiki></code>, поскольку он не соответствует реальному названию страницы.",
        "noindex-category-desc": "Страница не индексируются поисковыми роботами, потому что на ней имеется «волшебное слово» <code><nowiki>__NOINDEX__</nowiki></code>, и она находится в пространстве имён, где разрешён этот флаг).",
        "index-category-desc": "На странице имеется «волшебное слово» <nowiki>__INDEX__</nowiki> (и страница находится в пространстве имён, где разрешён этот флаг), поэтому она индексируются поисковыми роботами в тех случаях, когда этого обычно не происходит.",
        "post-expand-template-inclusion-category-desc": "Размер страницы станет больше <code>$wgMaxArticleSize</code> после показа всех шаблонов, поэтому некоторые из них не были показаны полностью.",
        "watchnologin": "Нужно представиться системе",
        "addwatch": "Добавить в список наблюдения",
        "addedwatchtext": "Страница «[[:$1]]» вместе с её обсуждением были добавлены в ваш [[Special:Watchlist|список наблюдения]].",
+       "addedwatchtext-talk": "«[[:$1]]» вместе со связанной с ней страницей были добавлены в ваш [[Special:Watchlist|список наблюдения]].",
        "addedwatchtext-short": "Страница «$1» была добавлена в ваш список наблюдения.",
        "removewatch": "Удалить из списка наблюдения",
-       "removedwatchtext": "Статья «[[:$1]]» и её страница обсуждения были удалены из вашего [[Special:Watchlist|списка наблюдения]].",
+       "removedwatchtext": "Страница «[[:$1]]» вместе с её обсуждением были удалены из вашего [[Special:Watchlist|списка наблюдения]].",
+       "removedwatchtext-talk": "«[[:$1]]» вместе со связанной с ней страницей были удалены из вашего [[Special:Watchlist|списка наблюдения]].",
        "removedwatchtext-short": "Страница «$1» была удалена из вашего списка наблюдения.",
        "watch": "Следить",
        "watchthispage": "Наблюдать за этой страницей",
        "notvisiblerev": "Версия была удалена",
        "watchlist-details": "В вашем списке наблюдения $1 {{PLURAL:$1|страница|страницы|страниц}}, не считая страниц обсуждений.",
        "wlheader-enotif": "Уведомления по эл. почте включены.",
-       "wlheader-showupdated": "Страницы, изменившиеся с вашего последнего их посещения, выделены '''жирным''' шрифтом.",
+       "wlheader-showupdated": "СÑ\82Ñ\80аниÑ\86Ñ\8b, Ð¸Ð·Ð¼ÐµÐ½Ð¸Ð²Ñ\88иеÑ\81Ñ\8f Ñ\81 Ð²Ð°Ñ\88его Ð¿Ð¾Ñ\81леднего Ð¸Ñ\85 Ð¿Ð¾Ñ\81еÑ\89ениÑ\8f, Ð²Ñ\8bделенÑ\8b '''полÑ\83жиÑ\80нÑ\8bм''' Ñ\88Ñ\80иÑ\84Ñ\82ом.",
        "wlnote": "Ниже {{PLURAL:$1|показано последнее изменение|показаны <strong>$1</strong> последние изменения|показаны <strong>$1</strong> последних изменений}} за {{PLURAL:$2|последний час|последние <strong>$2</strong> часа|последние <strong>$2</strong> часов}}, по состоянию на $3 $4.",
        "wlshowlast": "Показать за последние $1 часов $2 дней",
        "watchlist-hide": "Скрыть",
        "rollbacklinkcount-morethan": "откатить больше, чем $1 {{PLURAL:$1|правку|правки|правок}}",
        "rollbackfailed": "Ошибка при совершении отката",
        "rollback-missingparam": "Отсутствуют обязательные параметры по запросу.",
+       "rollback-missingrevision": "Не удалось загрузить данные версии.",
        "cantrollback": "Невозможно откатить изменения. Последним, кто вносил изменения, был единственный автор этой страницы.",
        "alreadyrolled": "Невозможно откатить последние изменения страницы «[[:$1]]», совершённые [[User:$2|$2]] ([[User talk:$2|обсуждение]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]),\nпоскольку кто-то другой уже успел откатить эти правки или отредактировать страницу.\n\nПоследние изменения {{GENDER:$3|внёс|внесла}} [[User:$3|$3]] ([[User talk:$3|обсуждение]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Было дано описание изменения: <em>$1</em>.",
        "revertpage": "Откат правок [[Special:Contributions/$2|$2]] ([[User talk:$2|обсуждение]]) к версии [[User:$1|$1]]",
        "revertpage-nouser": "Правки (имя участника скрыто) откачены к версии {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Откачены правки $1; возврат к версии $2.",
+       "rollback-success-notify": "Откачены правки $1; возврат к последней версии $2. [$3 Показать изменения]",
        "sessionfailure-title": "Ошибка сеанса",
        "sessionfailure": "Похоже, возникли проблемы с текущим сеансом работы;\nэто действие было отменено в целях предотвращения «захвата сеанса».\nПожалуйста, нажмите кнопку «Назад» и перезагрузите страницу, с которой вы пришли.",
        "changecontentmodel": "Редактирование контентной модели страницы",
        "undeletehistorynoadmin": "Статья была удалена. Причина удаления и список участников, редактировавших статью до её удаления, показаны ниже. Текст удалённой статьи могут просмотреть только администраторы.",
        "undelete-revision": "Удалённая версия $1 (от $4 $5) участника $3:",
        "undeleterevision-missing": "Неверная или отсутствующая версия. Возможно, вы перешли по неправильной ссылке, либо версия могла быть удалена из архива.",
+       "undeleterevision-duplicate-revid": "$1 {{PLURAL:$1|версия|версий|версии}} не могут быть восстановлены, поскольку {{PLURAL:$1|её|их}} <code>rev_id</code> уже используется.",
        "undelete-nodiff": "Не найдено предыдущей версии.",
        "undeletebtn": "Восстановить",
        "undeletelink": "просмотреть/восстановить",
        "undeletedrevisions": "{{PLURAL:$1|восстановлено|восстановлены}} $1 {{PLURAL:$1|изменение|изменения|изменений}}",
        "undeletedrevisions-files": "восстановлены $1 {{PLURAL:$1|версия|версии|версий}} и $2 {{PLURAL:$2|файл|файла|файлов}}",
        "undeletedfiles": "{{PLURAL:$1|восстановлен|восстановлены}} $1 {{PLURAL:$1|файл|файла|файлов}}",
-       "cannotundelete": "Ð\9eÑ\88ибка Ð²Ð¾Ñ\81Ñ\81Ñ\82ановлениÑ\8f:\n$1",
+       "cannotundelete": "Ð\9dекоÑ\82оÑ\80Ñ\8bе Ð¸Ð»Ð¸ Ð²Ñ\81е Ð²Ð°Ñ\88и Ð²Ð¾Ñ\81Ñ\81Ñ\82ановлениÑ\8f Ð½Ðµ Ñ\83далиÑ\81Ñ\8c:\n$1",
        "undeletedpage": "'''Страница «$1» была восстановлена.'''\n\nДля просмотра списка последних удалений и восстановлений см. [[Special:Log/delete|журнал удалений]].",
        "undelete-header": "Список недавно удалённых страниц можно посмотреть в [[Special:Log/delete|журнале удалений]].",
        "undelete-search-title": "Поиск удалённых страниц",
        "sp-contributions-newbies-sub": "С новых учётных записей",
        "sp-contributions-newbies-title": "Вклад с недавно созданных учётных записей",
        "sp-contributions-blocklog": "блокировки",
-       "sp-contributions-suppresslog": "удалённый вклад участника",
-       "sp-contributions-deleted": "удалённые правки",
+       "sp-contributions-suppresslog": "удалённый вклад {{GENDER:$1|участника|участницы}}",
+       "sp-contributions-deleted": "удалённые правки {{GENDER:$1|участника|участницы}}",
        "sp-contributions-uploads": "загрузки",
        "sp-contributions-logs": "журналы",
        "sp-contributions-talk": "обсуждение",
        "tooltip-ca-undelete": "Восстановить исправления страницы, сделанные до того, как она была удалена",
        "tooltip-ca-move": "Переименовать страницу",
        "tooltip-ca-watch": "Добавить эту страницу в ваш список наблюдения",
-       "tooltip-ca-unwatch": "Удалить эту страницу из вашего списка наблюдения",
+       "tooltip-ca-unwatch": "Удалить эту страницу из своего списка наблюдения",
        "tooltip-search": "Искать в {{grammar:prepositional|{{SITENAME}}}}",
        "tooltip-search-go": "Перейти к странице, имеющей в точности такое название",
        "tooltip-search-fulltext": "Найти страницы, содержащие указанный текст",
        "pageinfo-article-id": "Идентификатор страницы",
        "pageinfo-language": "Язык страницы",
        "pageinfo-content-model": "Модель содержимого страницы",
+       "pageinfo-content-model-change": "изменить",
        "pageinfo-robot-policy": "Индексация поисковыми роботами",
        "pageinfo-robot-index": "Разрешено",
        "pageinfo-robot-noindex": "Не разрешено",
        "api-error-ratelimited": "Вы пытаетесь загрузить несколько файлов за более короткий промежуток времени, чем это позволено.\nПожалуйста, попробуйте ещё раз через несколько минут.",
        "api-error-stashfailed": "Внутренняя ошибка: сервер не смог сохранить временный файл.",
        "api-error-publishfailed": "Внутренняя ошибка: сервер не смог сохранить временный файл.",
-       "api-error-stasherror": "При загрузке файла в хранилище произошла ошибка.",
+       "api-error-stasherror": "При загрузке файла во временное хранилище произошла ошибка.",
        "api-error-stashedfilenotfound": "При попытке загрузить файл из временного хранилища исходный файл не найден.",
        "api-error-stashpathinvalid": "Путь, по которому должен располагаться файл, загруженный во временное хранилище, некорректен.",
        "api-error-stashfilestorage": "При загрузке файла во временное хранилище произошла ошибка.",
        "limitreport-ppvisitednodes": "Количество узлов, посещённых препроцессором",
        "limitreport-ppgeneratednodes": "Количество сгенерированных препроцессором узлов",
        "limitreport-postexpandincludesize": "Размер раскрытых включений",
-       "limitreport-postexpandincludesize-value": "$1/$2 {{PLURAL:$2|байт|байта|байтов}}",
+       "limitreport-postexpandincludesize-value": "$1/$2 {{PLURAL:$2|байт|байта|байт}}",
        "limitreport-templateargumentsize": "Размер аргумента шаблона",
-       "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|байт|байта|байтов}}",
+       "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|байт|байта|байт}}",
        "limitreport-expansiondepth": "Наибольшая глубина расширения",
        "limitreport-expensivefunctioncount": "Количество «дорогих» функций анализатора",
        "expandtemplates": "Развёртка шаблонов",
        "mw-widgets-dateinput-placeholder-month": "ГГГГ-ММ",
        "mw-widgets-titleinput-description-new-page": "страница ещё не существует",
        "mw-widgets-titleinput-description-redirect": "перенаправление на $1",
-       "api-error-blacklisted": "Пожалуйста, выберите другое, более понятное название.",
        "sessionmanager-tie": "Невозможно использовать одновременно несколько типов проверки подлинности запроса: $1.",
        "sessionprovider-generic": "$1 сессий",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "сессий на основе куки",
        "log-action-filter-newusers": "Тип создания учётной записи:",
        "log-action-filter-patrol": "Тип патрулирования:",
        "log-action-filter-protect": "Тип защиты:",
-       "log-action-filter-rights": "Тип изменения прав",
-       "log-action-filter-suppress": "Тип сокрытия",
+       "log-action-filter-rights": "Тип изменения прав:",
+       "log-action-filter-suppress": "Тип сокрытия:",
        "log-action-filter-upload": "Тип загрузки:",
        "log-action-filter-all": "Все",
        "log-action-filter-block-block": "Блокировка",
        "log-action-filter-suppress-reblock": "Сокрытие пользователя через повторное блокирование",
        "log-action-filter-upload-upload": "Новая загрузка",
        "log-action-filter-upload-overwrite": "Повторно загрузить",
+       "authmanager-authn-not-in-progress": "Проверка подлинности не выполняется или данные сессии были утеряны. Пожалуйста, начните снова с самого начала.",
+       "authmanager-authn-no-primary": "Предоставленные учётные данные не могут быть проверены на подлинность.",
+       "authmanager-authn-no-local-user": "Предоставленные учётные данные не связаны ни с одним участником этой вики.",
+       "authmanager-authn-no-local-user-link": "Предоставленные учётные данные корректны, но не связаны ни с одни участником этой вики. Войдите с помощью какого-то другого способа или создайте новую учётную запись, и у вас появится возможность привязать свои предыдущие учётные данные к этой учетной записи.",
        "authmanager-authn-autocreate-failed": "Автоматическое создание локальной учётной записи не удалось: $1",
+       "authmanager-change-not-supported": "Предоставленные учётные данные не могут быть изменены, так как они не будут использованы.",
        "authmanager-create-disabled": "Создание учётных записей отключено.",
        "authmanager-create-from-login": "Чтобы создать учётную запись, пожалуйста, заполните приведённые ниже поля.",
+       "authmanager-create-not-in-progress": "Создание учётной записи не выполняется или данные сессии были утеряны. Пожалуйста, начните снова с самого начала.",
+       "authmanager-create-no-primary": "Предоставленные учётные данные не могут быть использованы для создания учётной записи.",
+       "authmanager-link-no-primary": "Предоставленные учётные данные не могут быть использованы для связывания учётных записей.",
+       "authmanager-link-not-in-progress": "Связывание учётной записи не выполняется или данные сессии были утеряны. Пожалуйста, начните снова с самого начала.",
        "authmanager-authplugin-setpass-failed-title": "Ошибка изменения пароля",
        "authmanager-authplugin-setpass-failed-message": "Плагин аутентификации запрещает смену пароля.",
        "authmanager-authplugin-create-fail": "Плагин аутентификации запрещает создание учётных записей.",
+       "authmanager-authplugin-setpass-denied": "Плагин аутентификации не позволяет изменять пароли.",
        "authmanager-authplugin-setpass-bad-domain": "Неверный домен.",
        "authmanager-autocreate-noperm": "Автоматическое создание учётных записей не разрешено.",
        "authmanager-autocreate-exception": "Автоматическое создание учётной записи временно отключено из-за предыдущих ошибок.",
        "authmanager-userdoesnotexist": "Не зарегистрировано учётной записи «$1».",
+       "authmanager-userlogin-remembermypassword-help": "Будет ли пароль запоминаться на время большее, чем продолжительность сессии.",
        "authmanager-username-help": "Имя участника для проверки подлинности.",
        "authmanager-password-help": "Пароль для проверки подлинности.",
        "authmanager-domain-help": "Домен для внешней аутентификации.",
        "authmanager-realname-label": "Настоящее имя",
        "authmanager-realname-help": "Настоящее имя участника",
        "authmanager-provider-password": "Аутентификация на основе пароля",
+       "authmanager-provider-password-domain": "Проверка подлинности на основе пароля и домена",
        "authmanager-provider-temporarypassword": "Временный пароль",
+       "authprovider-confirmlink-message": "На основании ваших последних попыткок входа, учётные записи могут быть связаны с вашей учётной записью в вики. Их связывание даёт возможность входа через эти учётные записи. Пожалуйста, выберите, какие из них должны быть связаны между собой.",
        "authprovider-confirmlink-option": "$1 ($2)",
        "authprovider-confirmlink-request-label": "Учётные записи, которые должны быть связаны",
        "authprovider-confirmlink-success-line": "$1: успешно связан.",
        "authprovider-confirmlink-failed-line": "$1: $2",
        "authprovider-confirmlink-failed": "Удалось связать не все учётные записи: $1",
+       "authprovider-confirmlink-ok-help": "Продолжать после вывода сообщений об ошибках связывания.",
        "authprovider-resetpass-skip-label": "Пропустить",
        "authprovider-resetpass-skip-help": "Пропустить сброс пароля.",
+       "authform-nosession-login": "Проверка подлинности прошла успешно, но ваш браузер не сможет «запомнить», что вы вошли.\n\n$1",
+       "authform-nosession-signup": "Учётная запись была создана, но ваш браузер не сможет «запомнить», что вы вошли.\n\n$1",
        "authform-newtoken": "Отсутствует токен. $1",
        "authform-notoken": "Отсутствует токен",
        "authform-wrongtoken": "Неверный токен",
        "linkaccounts-success-text": "Учетная запись была связана.",
        "linkaccounts-submit": "Связать учётные записи",
        "unlinkaccounts": "Отвязать учётные записи",
-       "unlinkaccounts-success": "Учетная запись была отвязан."
+       "unlinkaccounts-success": "Учетная запись была отвязан.",
+       "authenticationdatachange-ignored": "Изменение данных для проверки подлинности не было обработано. Может быть, не был настроен ни один провайдер?",
+       "userjsispublic": "Обратите внимание: подстраницы JavaScript не должны содержать конфиденциальные сведения, поскольку они доступны для просмотра другим участникам.",
+       "usercssispublic": "Обратите внимание: подстраницы CSS не должны содержать конфиденциальные сведения, поскольку они доступны для просмотра другим участникам."
 }
index 39b1992..8f664fc 100644 (file)
        "yourpasswordagain": "कूटशब्दः पुनः लिख्यताम् :",
        "createacct-yourpasswordagain": "कूटशब्दस्य पुष्टिं करोतु ।",
        "createacct-yourpasswordagain-ph": "कूटशब्दः पुनः लिख्यताम्",
-       "remembermypassword": "अस्मिन् सङ्गणके मम प्रवेशः स्मर्यताम् (अधिकतमम् $1 {{PLURAL:$1|दिनम्|दिनानि}})",
        "userlogin-remembermypassword": "अहं प्रविष्ट एव स्याम्",
        "userlogin-signwithsecure": "संरक्षितः सम्पर्कः (https) उपयुज्यताम्",
        "yourdomainname": "भवतः प्रदेशः (domain) :",
        "passwordreset-capture-help": "अस्यां मञ्जूषायां यदि भवता अङ्क्यते तर्हि वि-पत्रम् (अस्थायिकूटशब्देन सह) दर्श्यते प्रेष्यते च ।",
        "passwordreset-email": "वि-पत्रसङ्केतः",
        "passwordreset-emailtitle": "{{SITENAME}} इत्यत्र योजकविषये",
-       "passwordreset-emailtext-ip": "कोऽपि (कदाचित् भवान्/भवती, $1 अन्तर्जालसंविदः (from IP)) {{SITENAME}}($4) जालस्थानस्य  कृते कूटशब्दपरिवर्तनस्य विनतिम् अकरोत् । निम्न{{PLURAL:$3|योजकः|योजकाः}} अनेन वि-पत्रेण सह सल्लग्नः अस्ति/सल्लग्नाः सन्ति ।\n\n$2\n\n{{PLURAL:$3|एषः अल्पकालीनकूटशब्दः|एते अल्पकालीनकूटशब्दाः}} {{PLURAL:$5|चतुर्विंशतिघण्टासु|$5 दिनेषु}} निरस्तः भविष्यति/निरस्ताः भविष्यन्ति ।\nअधुना प्रवेशं सम्प्राप्य कूटशब्दः परिवर्तनीयः एव । \n\nनिम्नकारणानि यदि सन्ति, तर्हि एनं सन्देशम् अवगण्यताम् ।\n\n१ कोऽपि अन्यः अत्र विनतिम् अकरोत् । \n२ पूरातनः कूटशब्दः भवतः/भवत्याः स्मरणे अस्ति ।\n३ भवान्/भवती कूटशब्दं परिवर्तयितुं नेच्छिति ।",
-       "passwordreset-emailtext-user": "$1 सदस्यः {{SITENAME}}($4) जालस्थानस्य  कृते कूटशब्दपरिवर्तनस्य विनतिम् अकरोत् । निम्न{{PLURAL:$3|सदस्यः|सदस्याः}} अनेन वि-पत्रेण सह सल्लग्नः अस्ति/सल्लग्नाः सन्ति ।\n\n$2\n\n{{PLURAL:$3|एषः अल्पकालीनकूटशब्दः|एते अल्पकालीनकूटशब्दाः}} {{PLURAL:$5|चतुर्विंशतिघण्टासु|$5 दिनेषु}} निरस्तः भविष्यति/निरस्ताः भविष्यन्ति ।\nअधुना प्रवेशं सम्प्राप्य कूटशब्दः परिवर्तनीयः एव । \n\nनिम्नकारणानि यदि सन्ति, तर्हि एनं सन्देशम् अवगण्यताम् ।\n\n१ कोऽपि अन्यः अत्र विज्ञप्तिम् अकरोत् । \n२ पूरातनः कूटशब्दः भवतः/भवत्याः स्मरणे अस्ति ।\n३ भवान्/भवती कूटशब्दं परिवर्तयितुं नेच्छिति ।",
+       "passwordreset-emailtext-ip": "कोऽपि (कदाचित् भवान्/भवती, $1 अन्तर्जालसंविदः (from IP)) {{SITENAME}}($4) जालस्थानस्य कृते कूटशब्दपरिवर्तनस्य विनतिम् अकरोत् । अनेन वि-पत्रेण सह निम्न{{PLURAL:$3|योजकः सल्लग्नः अस्ति|योजकाः सल्लग्नाः सन्ति}} ।\n\n$2\n\n{{PLURAL:$5|चतुर्विंशतिघण्टासु|$5 दिनेषु}} {{PLURAL:$3|एषः अल्पकालीनकूटशब्दः निरस्तः भविष्यति|एते अल्पकालीनकूटशब्दाः निरस्ताः भविष्यन्ति}} ।\n\nअधुना प्रवेशं सम्प्राप्य कूटशब्दः परिवर्तनीयः एव । \n\nनिम्नकारणानि यदि सन्ति, तर्हि एनं सन्देशम् अवगण्यताम् ।\n\n१ कोऽपि अन्यः अत्र विनतिम् अकरोत् । \n२ पुरातनः कूटशब्दः भवतः/भवत्याः स्मरणे अस्ति ।\n३ भवान्/भवती कूटशब्दं परिवर्तयितुं नेच्छति ।",
+       "passwordreset-emailtext-user": "कोऽपि (कदाचित् भवान्/भवती, $1 अन्तर्जालसंविदः (from IP)) {{SITENAME}}($4) जालस्थानस्य कृते कूटशब्दपरिवर्तनस्य विनतिम् अकरोत् । अनेन वि-पत्रेण सह निम्न{{PLURAL:$3|योजकः सल्लग्नः अस्ति|योजकाः सल्लग्नाः सन्ति}} ।\n\n$2\n\n{{PLURAL:$5|चतुर्विंशतिघण्टासु|$5 दिनेषु}} {{PLURAL:$3|एषः अल्पकालीनकूटशब्दः निरस्तः भविष्यति|एते अल्पकालीनकूटशब्दाः निरस्ताः भविष्यन्ति}} ।\n\nअधुना प्रवेशं सम्प्राप्य कूटशब्दः परिवर्तनीयः एव । \n\nनिम्नकारणानि यदि सन्ति, तर्हि एनं सन्देशम् अवगण्यताम् ।\n\n१ कोऽपि अन्यः अत्र विनतिम् अकरोत् । \n२ पुरातनः कूटशब्दः भवतः/भवत्याः स्मरणे अस्ति ।\n३ भवान्/भवती कूटशब्दं परिवर्तयितुं नेच्छति ।",
        "passwordreset-emailelement": "सदस्यनाम : \n$1\n\nअल्पकालीनकूटशब्दः : \n$2",
        "passwordreset-emailsentemail": "परिवर्तितकूटशब्दस्य वि-पत्रं प्रेषितम् अस्ति ।",
-       "passwordreset-emailsent-capture": "परिवर्तितकूटशब्दस्य वि-पत्रं प्रेषितम् अस्ति । तत् अधः द्रष्टुं शक्यते ।",
-       "passwordreset-emailerror-capture": "परिवर्तितकूटशब्दस्य वि-पत्रं निर्मितम् अस्ति । तत् अधः द्रष्टुं शक्यते । परन्तु {{GENDER:$2|योजकाय}} प्रेषणकाले तत् निरस्तम् अभवत् : $1",
        "changeemail": "वि-पत्रसङ्केतः परिवर्त्यताम्",
        "changeemail-header": "प्रयोक्तृनाम्नः ई-पत्रसङ्केतः परिवर्त्यताम्",
        "changeemail-no-info": "एतत् पृष्ठं सम्पादयितुं प्रवेशः अनिवार्यः ।",
        "minoredit": "इदं लघु सम्पादनम्",
        "watchthis": "इदं पृष्ठं निरीक्षताम्",
        "savearticle": "पृष्ठं रक्ष्यताम्",
+       "publishpage": "पृष्ठं प्रकाश्यताम्",
+       "publishchanges": "परिवर्तनानि प्रकाश्यन्ताम्",
        "preview": "प्राग्दृश्यम्",
        "showpreview": "प्राग्दृश्यं दृश्यताम्",
        "showdiff": "परिवर्तनानि दृश्यन्ताम्",
        "undo-nochange": "पूर्वमेव एतत् सम्पादनं पूर्ववत् कृतं स्यात् ।",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|talk]]) द्वारा कृता  $1 पूर्ववत्-प्रक्रिया निरस्तीक्रियताम् ।",
        "undo-summary-username-hidden": "निगूहितयोजकद्वारा कृता  $1 पूर्ववत्-प्रक्रिया निरस्तीक्रियताम् ।",
-       "cantcreateaccounttitle": "सदस्यता प्राप्तुं न शक्यते",
        "cantcreateaccount-text": "(<strong>$1</strong>) इत्यस्य संविदः (IP) कृते सदस्यता प्राप्तुं न शक्यते । कारणं [[User:$3|$3]] द्वारा सा संवित्सङ्ख्या प्रतिबन्धिता अस्ति । \n\n$3 इत्यनेन कारणं दत्तं यत्, <em>$2</em>",
        "cantcreateaccount-range-text": "सदस्यताप्राप्तेः सीमा-आक्रान्तृषु <strong>$1</strong> अन्तर्जालसंवित्सु (IP) भवतः/भवत्याः अन्तर्जालसंवद् (<strong>$4</strong>) अन्तर्भवति । अतः [[User:$3|$3]] द्वारा भवतः/भवत्याः अन्तर्जालसंविद् प्रतिबन्धिता ।\n\n$3 इत्यनेन कारणं दत्तं यत्, <em>$2</em>",
        "viewpagelogs": "अस्य पृष्ठस्य संरक्षिताऽऽवलिः (logs) दृश्यताम्",
        "whatlinkshere-prev": "{{PLURAL:$1|पुरस्तात् (previous) $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|अग्रिमम् $1}}",
        "whatlinkshere-links": "← परिसन्धयः",
-       "whatlinkshere-hideredirs": "$1 पुनर्निर्दिष्टानि पृष्ठानि",
-       "whatlinkshere-hidetrans": "$1 à¤\85नà¥\8dयलà¥\87à¤\96भाà¤\97ाः (transclusions)",
+       "whatlinkshere-hideredirs": "$1 पुनर्निर्दिष्टानि",
+       "whatlinkshere-hidetrans": "$1 à¤\85नà¥\81वादाः (transclusions)",
        "whatlinkshere-hidelinks": "$1 परिसन्धयः",
        "whatlinkshere-hideimages": "$1 चित्रपरिसन्धिः",
        "whatlinkshere-filters": "शोधनी",
        "tooltip-ca-nstab-category": "वर्गाणां पृष्ठं दृश्यताम्",
        "tooltip-minoredit": "लघुसम्पादनत्वेन इदं सम्पादनम् अङ्क्यताम्",
        "tooltip-save": "परिवर्तनानि रक्ष्यन्ताम्",
+       "tooltip-publish": "स्वपरिवर्तनानि प्रकश्यन्ताम्",
        "tooltip-preview": "भवता/भवत्या कृतानां परिवर्तनानां प्राग्दृश्यं दृश्यताम्, रक्षणात्पूर्वं कृपया इदम् उपयुज्यताम्।",
        "tooltip-diff": "भवता/भवत्या कृतानि परिवर्तनानि अत्र द्रष्टुं शक्यते",
        "tooltip-compareselectedversions": "पृष्ठस्य द्वयोः चितयोः आवृत्त्योः भेदः दृश्यताम्",
        "special-characters-title-endash": "en dash",
        "special-characters-title-emdash": "em dash",
        "special-characters-title-minus": "minus sign",
-       "mw-widgets-titleinput-description-new-page": "पृष्ठं न विद्यते",
-       "api-error-blacklisted": "कृपया भिन्नं विवरणपूर्वकं शीर्षकं चीयताम् ।"
+       "mw-widgets-titleinput-description-new-page": "पृष्ठं न विद्यते"
 }
index 643d165..4dca72d 100644 (file)
        "category-subcat-count-limited": "Бу категория {{PLURAL:$1|субкатегориялаах|$1 субкатегориялардаах}}.",
        "category-article-count": "{{PLURAL:$2|Бу категория манна эрэ көстүбүт субкатегориялаах.|$2 категорияттан {{PLURAL:$1|субкатегорията|$1 субкатегориялара}} көрдөрүлүннүлэр.}}",
        "category-article-count-limited": "Бу категорияҕа {{PLURAL:$1|1 эрэ сирэй|$1 сирэй}} баар.",
-       "category-file-count": "{{PLURAL:$2|Бу категория манна эрэ көстүбүт билэлээх.|$2 категорияттан {{PLURAL:$1|билэтэ|$1 билэлэрэ}} көрдөрүлүннүлэр.}}",
+       "category-file-count": "{{PLURAL:$2|Бу категория манна эрэ көстүбүт билэлээх.|Бу категорияҕа баар $2 билэттэн {{PLURAL:$1|билэтэ|$1 билэтэ}} көрдөрүлүннэ.}}",
        "category-file-count-limited": "Бу категорияҕа  {{PLURAL:$1|соҕотох билэ|$1 билэ}} баар.",
        "listingcontinuesabbrev": "(салгыыта)",
        "index-category": "Индекстэммит сирэйдэр",
        "userlogin-yourname-ph": "Бэлиэ-ааккын киллэр",
        "createacct-another-username-ph": "Ааккын суруй",
        "yourpassword": "Киирии тыла:",
-       "userlogin-yourpassword": "Ð\9aииÑ\80ии Ñ\82Ñ\8bл",
+       "userlogin-yourpassword": "Ð\90һаÑ\80Ñ\8bк",
        "userlogin-yourpassword-ph": "Киирии тылгын суруй",
        "createacct-yourpassword-ph": "Киирии тылгын суруй",
        "yourpasswordagain": "Киирии тылгын хатылаа:",
        "passwordtooshort": "Киирии тылыҥ наһаа кылгас.\nКырата {{PLURAL:$1|1 бэлиэлээх|$1 бэлиэлээх}} буолуохтаах.",
        "passwordtoolong": "Аһарык {{PLURAL:$1|1 бэлиэттэн|$1 бэлиэттэн}} уһун буолуо суохтаах.",
        "passwordtoopopular": "Элбэхтэ туттуллар аһарыктары туттар сатаммат. Бука диэн атын аһарыкта тал.",
-       "password-name-match": "Ð\9aииÑ\80ии Ñ\82Ñ\8bл ааккыттан атын буолуохтаах.",
+       "password-name-match": "Ð\90һаÑ\80Ñ\8bгÑ\8bÒ¥ ааккыттан атын буолуохтаах.",
        "password-login-forbidden": "Маннык ааты уонна киирии тылы туһаныы бобуллар.",
        "mailmypassword": "Киирии тылы саҥардыы",
        "passwordremindertitle": "{{SITENAME}} киирии тылын санатыы",
-       "passwordremindertext": "Ð\9aим Ñ\8dÑ\80Ñ\8d (бадаÒ\95а Ñ\8dн Ð±Ñ\83 IP-аадÑ\8bÑ\80Ñ\8bÑ\81Ñ\82ан: $1), {{SITENAME}} ($4) ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bлÑ\8bн Ñ\81аҥаÑ\82Ñ\82ан Ñ\8bÑ\8bÑ\82Ñ\8bÒ¥ Ð´Ð¸Ñ\8dбиÑ\82.\n\"$2\" ÐºÑ\8bÑ\82Ñ\82ааÑ\87Ñ\87Ñ\8b Ð±Ñ\8bÑ\81Ñ\82аÑ\85 ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bла Ð±Ð¸Ð»Ð¸Ð³Ð¸Ð½ Ð¼Ð°Ð½Ð½Ñ\8bк: \"$3\".\nÓ¨Ñ\81кө Ð¼Ð°Ð½Ñ\8b Ñ\8dн Ñ\87аÑ\85Ñ\87Ñ\8b ÐºÓ©Ñ\80дөөбүÑ\82 Ð±Ñ\83оллаÑ\85Ñ\85Ñ\8bна, Ñ\81иÑ\81Ñ\82иÑ\8dмÑ\8dÒ\95Ñ\8d Ñ\81аҥаÑ\82Ñ\82ан ÐºÐ¸Ð¸Ñ\80Ñ\8dҥҥин ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bлгÑ\8bн Ñ\83лаÑ\80Ñ\8bÑ\82Ñ\8bаÑ\85Ñ\85Ñ\8bн Ñ\81өп.\nÐ\91Ñ\8bÑ\81Ñ\82аÑ\85 ÐºÐ¸Ð¸Ñ\80и Ñ\82Ñ\8bл {{PLURAL:$5|бииÑ\80 Ñ\85онÑ\83к|$5 Ñ\85онÑ\83к Ñ\83Ñ\81Ñ\82аÑ\82а}} Ò¯Ð»Ñ\8dлииÑ\80.\n\nÓ¨Ñ\81көÑ\82үн ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bлÑ\8b Ñ\81аҥаÑ\82Ñ\82ан ÐºÓ©Ñ\80дөөбөÑ\82Ó©Ñ\85 Ð±Ñ\83оллаÑ\85Ñ\85Ñ\8bна,\nÑ\8dбÑ\8dÑ\82Ñ\8dÑ\80 Ñ\83Ñ\80Ñ\83ккÑ\83 ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bлгÑ\8bн Ó©Ð¹Ð´Ó©Ó©Ð½ ÐºÑ\8dлбиÑ\82 Ð±Ñ\83оллаÑ\85Ñ\85Ñ\8bна,\nбÑ\83 Ñ\81Ñ\83Ñ\80Ñ\83кка Ð°Ð°Ñ\85Ñ\85айÑ\8bма Ñ\83онна Ñ\83Ñ\80Ñ\83ккÑ\83 ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bлгын салгыы туһан.",
+       "passwordremindertext": "Ð\9aим Ñ\8dÑ\80Ñ\8d (бадаÒ\95а Ñ\8dн Ð±Ñ\83 IP-аадÑ\8bÑ\80Ñ\8bÑ\81Ñ\82ан: $1), {{SITENAME}} ($4) Ð°Ò»Ð°Ñ\80Ñ\8bгÑ\8bн Ñ\81аҥаÑ\82Ñ\82ан Ñ\8bÑ\8bÑ\82Ñ\8bÒ¥ Ð´Ð¸Ñ\8dбиÑ\82.\n\"$2\" ÐºÑ\8bÑ\82Ñ\82ааÑ\87Ñ\87Ñ\8b Ð±Ñ\8bÑ\81Ñ\82аÑ\85 Ð°Ò»Ð°Ñ\80Ñ\8bга Ð±Ð¸Ð»Ð¸Ð³Ð¸Ð½ Ð¼Ð°Ð½Ð½Ñ\8bк: \"$3\".\nÓ¨Ñ\81кө Ð¼Ð°Ð½Ñ\8b Ñ\8dн Ñ\87аÑ\85Ñ\87Ñ\8b ÐºÓ©Ñ\80дөөбүÑ\82 Ð±Ñ\83оллаÑ\85Ñ\85Ñ\8bна, Ñ\81иÑ\81Ñ\82иÑ\8dмÑ\8dÒ\95Ñ\8d Ñ\81аҥаÑ\82Ñ\82ан ÐºÐ¸Ð¸Ñ\80Ñ\8dҥҥин Ð°Ò»Ð°Ñ\80Ñ\8bккÑ\8bн Ñ\83лаÑ\80Ñ\8bÑ\82Ñ\8bаÑ\85Ñ\85Ñ\8bн Ñ\81өп.\nÐ\91Ñ\8bÑ\81Ñ\82аÑ\85 Ð°Ò»Ð°Ñ\80Ñ\8bк {{PLURAL:$5|бииÑ\80 Ñ\85онÑ\83к|$5 Ñ\85онÑ\83к Ñ\83Ñ\81Ñ\82аÑ\82а}} Ò¯Ð»Ñ\8dлииÑ\80.\n\nÓ¨Ñ\81көÑ\82үн Ð°Ò»Ð°Ñ\80Ñ\8bгÑ\8b Ñ\81аҥаÑ\82Ñ\82ан ÐºÓ©Ñ\80дөөбөÑ\82Ó©Ñ\85 Ð±Ñ\83оллаÑ\85Ñ\85Ñ\8bна,\nÑ\8dбÑ\8dÑ\82Ñ\8dÑ\80 Ñ\83Ñ\80Ñ\83ккÑ\83 Ð°Ò»Ð°Ñ\80Ñ\8bккн Ó©Ð¹Ð´Ó©Ó©Ð½ ÐºÑ\8dлбиÑ\82 Ð±Ñ\83оллаÑ\85Ñ\85Ñ\8bна,\nбÑ\83 Ñ\81Ñ\83Ñ\80Ñ\83гÑ\83 Ð°Ð°Ñ\85Ñ\85айÑ\8bма Ñ\83онна Ñ\83Ñ\80Ñ\83ккÑ\83 Ð°Ò»Ð°Ñ\80Ñ\8bккын салгыы туһан.",
        "noemail": "\"$1\" ааттаах киһиэхэ эл. почтата ыйыллыбатах.",
        "noemailcreate": "Электроннай почтаҥ сөптөөх аадырыһын суруйуохтааххын",
-       "passwordsent": "Саҥа ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bл \"$1\" Ñ\8dл. Ð¿Ð¾Ñ\87Ñ\82аÑ\82Ñ\8bгаÑ\80 Ñ\8bÑ\8bÑ\82Ñ\8bлÑ\8bнна.\nСиÑ\81Ñ\82емаÒ\95а Ñ\81аҥа ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bлÑ\8b Ñ\82Ñ\83һанан ÐºÐ¸Ð¸Ñ\80.",
+       "passwordsent": "Саҥа Ð°Ò»Ð°Ñ\80Ñ\8bк Ñ\82Ñ\8bл \"$1\" Ñ\8dл. Ð¿Ð¾Ñ\87Ñ\82аÑ\82Ñ\8bгаÑ\80 Ñ\8bÑ\8bÑ\82Ñ\8bлÑ\8bнна.\nТиһиккÑ\8d ÐºÐ¸Ð¸Ñ\80Ñ\8dÑ\80гÑ\8d Ñ\81аҥа Ð°Ò»Ð°Ñ\80Ñ\8bгÑ\8b Ñ\82Ñ\83һан.",
        "blocked-mailpassword": "Эн IP аадырыскыттан манна тугу эмэ уларытар бобуллубут,\nонон киирии тылы өйдөтөр кыах эмиэ суох.",
        "eauthentsent": "Эл. почтаҕар сурук ыытылынна.\nБу аадырыс эйиэнэ буоларын бигэргэтэргэ өссө тугу гыныахтааҕыҥ туһунан сурукка кэпсэниллэр.",
        "throttled-mailpassword": "Киирии тылы өйдөтөр тэрил бүтэһик {{PLURAL:$1|чаас|$1 чаас}} иһигэр туттулла сылдьыбыт.\nКөмүскэнэр соруктан сылтаан киирии тылы {{PLURAL:$1|чааска|$1 чааска}} биирдэ эрэ ыйытыахха сөп.",
        "resetpass_announce": "Түмүктүүргэ саҥа киирии тылла суруй.",
        "resetpass_text": "<!-- Тиэкиһи манна эбэн суруйуҥ -->",
        "resetpass_header": "Аат киирии тылын уларытыы",
-       "oldpassword": "ЭÑ\80гÑ\8d ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bл:",
-       "newpassword": "Саҥа ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bл:",
+       "oldpassword": "ЭÑ\80гÑ\8d Ð°Ò»Ð°Ñ\80Ñ\8bк:",
+       "newpassword": "Саҥа Ð°Ò»Ð°Ñ\80Ñ\8bк:",
        "retypenew": "Саҥа киирии тылы хатылаа:",
        "resetpass_submit": "Киирии тылы уларыт уонна киир",
        "changepassword-success": "Киирии тылыҥ этэҥҥэ уларыйда!",
        "resetpass-no-info": "Ааккын билиһиннэрдэххинэ эрэ бу сирэйгэ быһа тиийиэххин сөп.",
        "resetpass-submit-loggedin": "Киирии тылы уларытыы",
        "resetpass-submit-cancel": "Салҕаама",
-       "resetpass-wrong-oldpass": "Ð\9aииÑ\80ии Ñ\82Ñ\8bл Ñ\81өп Ñ\82үбÑ\8dÑ\81пÑ\8dÑ\82Ñ\8d.\nÐ\91аÒ\95аÑ\80 Ñ\83лаÑ\80Ñ\8bппÑ\8bÑ\82Ñ\8bÒ¥ Ð±Ñ\83олÑ\83о Ñ\8dбÑ\8dÑ\82Ñ\8dÑ\80 Ð±Ñ\8bÑ\81Ñ\82аÑ\85 ÐºÑ\8dмҥÑ\8d Ñ\82Ñ\83Ñ\82Ñ\82Ñ\83ллаÑ\80 ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bлы оҥотторбутуҥ буолуо.",
+       "resetpass-wrong-oldpass": "Ð\90һаÑ\80Ñ\8bк Ñ\81өп Ñ\82үбÑ\8dÑ\81пÑ\8dÑ\82Ñ\8d.\nÐ\91аÒ\95аÑ\80 Ñ\83лаÑ\80Ñ\8bппÑ\8bÑ\82Ñ\8bÒ¥ Ñ\8dбÑ\8dÑ\82Ñ\8dÑ\80 Ð±Ñ\8bÑ\81Ñ\82аÑ\85 ÐºÑ\8dмҥÑ\8d Ñ\82Ñ\83Ñ\82Ñ\82Ñ\83ллаÑ\80 Ð°Ò»Ð°Ñ\80Ñ\8bгы оҥотторбутуҥ буолуо.",
        "resetpass-recycled": "Бука диэн, билиҥҥи киирии тылтан атыны суруй.",
        "resetpass-temp-emailed": "Быстах кэмҥэ туттуллар киирии тылынан киирдиҥ.\nТүмүктүүргэ саҥа киирии тылы суруйуохтааххын:",
-       "resetpass-temp-password": "Ð\91Ñ\8bÑ\81Ñ\82аÑ\85 ÐºÑ\8dмҥÑ\8d Ñ\82Ñ\83Ñ\82Ñ\82Ñ\83ллаÑ\80 ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bл:",
+       "resetpass-temp-password": "Ð\91Ñ\8bÑ\81Ñ\82аÑ\85 ÐºÑ\8dмҥÑ\8d Ñ\82Ñ\83Ñ\82Ñ\82Ñ\83ллаÑ\80 Ð°Ò»Ð°Ñ\80Ñ\8bк:",
        "resetpass-abort-generic": "Киирии тылы уларытыыны кэҥэтии тохтотто.",
        "resetpass-expired": "Киирии тылыҥ болдьоҕо ааспыт эбит. Бука диэн, саҥа киирии тылла туруорун.",
        "resetpass-expired-soft": "Киирии тылыҥ болдьоҕо бүппүт, онон уларытыллыахтаах эбит. Бука диэн атын киирии тылы суруй эбэтэр маня баттаан кэлин киллэрээр \"{{int:authprovider-resetpass-skip-label}}\".",
        "passwordreset-emailtitle": "{{SITENAME}} бырайыакка аатын туһунан",
        "passwordreset-emailtext-ip": "Ким эрэ (баҕар эн буолуо, бу IP-ттан $1)  {{SITENAME}} ($4) бырайыакка киирии тылы уларытар туһунан ыйытык биэрбит.\nБу электрон аадырыһы кытта бу {{PLURAL:$3|аат ситимнээх|ааттар ситимнээхтэр}}:\n\n$2\n\nБу быстах кэмҥэ аналлаах {{PLURAL:$3|киирии тыл|кирии тыллар}} {{PLURAL:$5|биир күн үлэлиэҕэ|$5 күн үлэлиэхтэрэ}}.\nЭн тиһиликкэ ааккын этэн саҥа киирии тылы киллэриэхтээххин.\nӨскө бу ыйытыгы ыыппатах буоллаххына, эбэтэр урукку киирии тылгын өйдөөн кэлбит буоллаххына \nбу биллэриини ааххайыа суоххун сөп.\nОччоҕо урукку киирии тылыҥ оннунан хаалыа.",
        "passwordreset-emailtext-user": "$1 диэн кыттааччы  {{SITENAME}} ($4) бырайыакка киирии тылгын уларытар туһунан ыйытык ыыппыт.\nБу электрон аадырыһы кытта бу {{PLURAL:$3|аат ситимнээх|ааттар ситимнээхтэр}}\n\n$2\n\nБу быстах кэмҥэ аналлаах {{PLURAL:$3|киирии тыл|кирии тыллар}} {{PLURAL:$5|биир күн үлэлиэҕэ|$5 күн үлэлиэхтэрэ}}.\nЭн тиһиликкэ ааккын этэн саҥа киирии тылы киллэриэхтээххин.\nӨскө бу ыйытыгы ыыппатах буоллаххына, эбэтэр урукку киирии тылгын өйдөөн кэлбит буоллаххына \nбу биллэриини ааххайыа суоххун сөп.\nОччоҕо урукку киирии тылыҥ оннунан хаалыа.",
-       "passwordreset-emailelement": "Ð\9aÑ\8bÑ\82Ñ\82ааÑ\87Ñ\87Ñ\8b: \n$1\n\nÐ\91Ñ\8bÑ\81Ñ\82аÑ\85 ÐºÐ¸Ð¸Ñ\80ии тыл: \n$2",
+       "passwordreset-emailelement": "Ð\9aÑ\8bÑ\82Ñ\82ааÑ\87Ñ\87Ñ\8b: \n$1\n\nÐ\91Ñ\8bÑ\81Ñ\82аÑ\85 Ð°Ò»Ð°Ñ\80Ñ\8bк тыл: \n$2",
        "passwordreset-emailsentemail": "Өскө бу Эн ааккар баайыллыбыт аадырыс буоллаҕына, аһарык тылы уларытар туһунан сурук барыа.",
        "passwordreset-emailsentusername": "Өскө бу аакка баайыллыбыт аадырыс баар буоллаҕына, аһарык тылы уларытар туһунан сурук онно барыа.",
-       "passwordreset-emailsent-capture": "Киирии тылы уларытар туһунан сурук аллара эмиэ көрдөрүлүннэ.",
-       "passwordreset-emailerror-capture": "Манна киирии тылы уларытар туһунан сурук көрдөрүлүннэ. Ол эрэн сурук бу төрүөттэн $2 кыттааччыга сатаан барбата: $1",
        "changeemail": "Аадырыһы уларытыы уонна сотуу",
        "changeemail-header": "Бу форманы толорон аадырыскын уларыт. Уларытыыны бигэргэтэргэ аһарыккын киллэриэхтээххин. Почтаҥ аадырыһыттан бэлиэ-ааккыттан араарыаххын баҕарар буоллаххына аадырыһы сотон баран бигэргэтэн кэбиһээр.",
-       "changeemail-passwordrequired": "Бу дьайыыны бигэргэтэргэ аһарык тылгын киллэриэхтээххин.",
        "changeemail-no-info": "Бу сирэйгэ чопчу тиийэргэ, тиһиликкэ бэлиэтэммит ааккын этиэхтиэххин.",
        "changeemail-oldemail": "Билиҥҥи аадырыс:",
        "changeemail-newemail": "Саҥа аадырыс:",
        "loginreqtitle": "Бэйэҕин билиһиннэр",
        "loginreqlink": "Ааккын эт",
        "loginreqpagetext": "Атын сирэйдэри көрөргө маны оҥоруохтааххын: $1.",
-       "accmailtitle": "Ð\9aииÑ\80ии Ñ\82Ñ\8bл ыытылынна.",
-       "accmailtext": "[[User talk:$1|$1]] ÐºÑ\8bÑ\82Ñ\82ааÑ\87Ñ\87Ñ\8bга Ñ\82үбÑ\8dÑ\81пиÑ\87Ñ\87Ñ\8d Ð±Ñ\8dлиÑ\8dлÑ\8dÑ\80Ñ\82Ñ\8dн Ð¾Ò¥Ð¾Ò»Ñ\83ллÑ\83бÑ\83Ñ\82 ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bл Ð±Ñ\83 Ð°Ð°Ð´Ñ\8bÑ\80Ñ\8bÑ\81ка $2 Ñ\8bÑ\8bÑ\82Ñ\8bлÑ\8bнна.\nТиһиккÑ\8d Ð±Ñ\8dлиÑ\8dÑ\82Ñ\8dнÑ\8dн Ð±Ð°Ñ\80ан ÐºÐ¸Ð¸Ñ\80ии Ñ\82Ñ\8bлгын ''[[Special:ChangePassword|уларытыаххын]]'' сөп.",
+       "accmailtitle": "Ð\90һаÑ\80Ñ\8bк ыытылынна.",
+       "accmailtext": "[[User talk:$1|$1]] ÐºÑ\8bÑ\82Ñ\82ааÑ\87Ñ\87Ñ\8bга Ñ\82үбÑ\8dÑ\81пиÑ\87Ñ\87Ñ\8d Ð±Ñ\8dлиÑ\8dлÑ\8dÑ\80Ñ\82Ñ\8dн Ð¾Ò¥Ð¾Ò»Ñ\83ллÑ\83бÑ\83Ñ\82 Ð°Ò»Ð°Ñ\80Ñ\8bк Ñ\82Ñ\8bл Ð±Ñ\83 Ð°Ð°Ð´Ñ\8bÑ\80Ñ\8bÑ\81ка $2 Ñ\8bÑ\8bÑ\82Ñ\8bлÑ\8bнна.\nТиһиккÑ\8d Ð±Ñ\8dлиÑ\8dÑ\82Ñ\8dнÑ\8dн Ð±Ð°Ñ\80ан Ð°Ò»Ð°Ñ\80Ñ\8bккын ''[[Special:ChangePassword|уларытыаххын]]'' сөп.",
        "newarticle": "(Саҥа ыстатыйа)",
        "newarticletext": "Эн суох сирэйгэ киирэ сатаатыҥ.\nМаннык ааттаах саҥа ыстатыйаны оҥорор буоллаххына, аллара баар түннүккэ суруй\n(сиһ. [$1 көмөнү] көрүөххүн сөп).\nӨскө манна сыыһа киирбит буоллаххына интэриниэтиҥ бырагыраамматын \"төнүн\" диэххин сөп.",
        "anontalkpagetext": "----''Бу аатын эппэтэх кыттааччы ырытар сирэйэ.\nIP-аадырыһа эрэ көстөр.\nБиир IP-аадырыс хас да киһиэхэ бэриллиэн сөп. Өскө атын киһиэхэ суруллубут суругу алҕас туппут буоллаххына, бэйэҥ [[Special:CreateAccount|ааккын билиһиннэр]] эбэтэр [[Special:UserLogin|киир]], оччоҕо кэлин да булкуур тахсыа суоҕа.''",
        "undo-nochange": "Бу уларытыы хайыы-үйэ сотуллубут курдук.",
        "undo-summary": "[[Special:Contributions/$2|$2]] кыттааччы ([[User talk:$2|ырытыы]] | [[Special:Contributions/$2|{{MediaWiki:Contribslink}}]]) $1 нүөмэрдээх уларытыытын сотон оннугар түһэрэргэ.",
        "undo-summary-username-hidden": "Кистэммит кыттааччы $1 уларытыытын төннөр",
-       "cantcreateaccounttitle": "Саҥа ааты киллэрэр сатаммат",
        "cantcreateaccount-text": "[[User:$3|$3]] кыттааччы бу IP-ттан ('''$1''') саҥа бэлиэтэниини бопто.\n\nБыһаарыыта: $3 - ''$2''",
        "cantcreateaccount-range-text": "Бу IP-диапазонтан <strong>$1</strong> ааты бэлиэтиири [[User:$3|$3]] боппут. Эн IP-аадырыһыҥ (<strong>$4</strong>) онно киирсэр эбит. \n\nЫйыллыбыт төрүөтэ: $2.",
        "viewpagelogs": "Бу сирэй сурунаалларын көрүү",
        "mw-widgets-dateinput-no-date": "Күнэ-дьыла ыйыллыбатах",
        "mw-widgets-titleinput-description-new-page": "сирэй суох эбит",
        "mw-widgets-titleinput-description-redirect": "манна $1 утаарыы",
-       "api-error-blacklisted": "Бука диэн өйдөнөр аатта тал дуу.",
        "randomrootpage": "Түбэһиэх төрүт сирэй."
 }
index 75e3238..7b57f55 100644 (file)
        "otherlanguages": "Eṭagak pạrsi",
        "redirectedfrom": "$1 khon ạcur heć akana",
        "redirectpagesub": "Bań sojhe sakam",
+       "redirectto": "Ar hõ udugoḱakana:",
        "lastmodifiedat": "Noa sakam do sạjao hoena $1, $2 te",
        "viewcount": "Noa sakamdo {{PLURAL:$1 dhom $1 dhom}} udug hoena.",
        "protectedpage": "Rukhíạ sakamko",
        "mainpage": "Mukhiạ Sakam",
        "mainpage-description": "Mukhiạ sakam",
        "policy-url": "Project:Ritiniti",
-       "portal": "Hoṛko boloḱ hor",
-       "portal-url": "Projeṭ:Hoṛko bolon hor",
+       "portal": "Gusṭi bolon hor",
+       "portal-url": "Projeṭ: Gusṭ bolon hor",
        "privacy": "Oku eḱtear",
        "privacypage": "Project: Nijaḱ eḱteạr",
        "badaccess": "Ektiạr vul",
        "minoreditletter": "m",
        "newpageletter": "N",
        "boteditletter": "b",
-       "rc-change-size-new": "Bodol tayomte",
+       "rc-change-size-new": "$1 {{PLURAL:$1|bayiṭ|bayiṭ}} Bodol tayomte",
        "rc-enhanced-expand": "Purạote uduḱ",
        "rc-enhanced-hide": "Purạo cuku",
        "recentchangeslinked": "Sãotenaḱ bodolko",
        "exif-imagewidth": "Ganḍe",
        "exif-imagelength": "Usul",
        "exif-datetime": "Rẽt bodol reaḱ tạrikh ar okte",
+       "exif-make": "Kemera tearić",
+       "exif-model": "Kemera model",
+       "exif-software": "Beoharen Software",
        "exif-artist": "Onoliạ",
+       "exif-exifversion": "Exif bharson",
+       "exif-colorspace": "Roṅcoṅ dhạrti",
        "namespacesall": "sanam",
        "monthsall": "Sanamak",
        "watchlisttools-view": "Jońgṛao bodolaḱko ńel",
        "watchlisttools-edit": "Ńelok tạlika ńel ar joṛao",
        "watchlisttools-raw": "Baṇ purạo akan ńelok tạlika purạomẽ",
-       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_galmarao}}:$1|talk]])",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|galmarao]])",
        "duplicate-defaultsort": "'''Sontoroḱmẽ:''' ḍifolṭ sajao reaḱ cạbi: $2 lahare ḍifolṭ sajao reaḱ sakam: ''$1'' e bae luturaḱ kana.",
        "specialpages": "Osokayteaḱ sakamko",
        "external_image_whitelist": "#Noa sakam do cet leka menaḱa oṅkage dohoemẽ\n#Sanam okte re jạhiren kuṭrạ latar re (khạli hạtiń //talare) bạisạomẽ\n#Noako do bahre reaḱ (hotlinked) chubi reaḱ URL saõte milạo hoyoḱa\n#Okako milạḱa, onako do chubi lekate udugoḱa, baṅkhan do khali chubi joṛao udugoḱa\n#Noa layen reaḱ ehoṕre # menaḱa ona layenko menko hisapte beohar hoyoḱka\n#Noa do kas-baṅ rimjhạoaḱge\n#Noa dag cetanre regex kuṭrạ bạsạomẽ. Noa layen cetleka menaḱa oṅkage dohoemẽ</pre>",
index b85a4d5..152a08a 100644 (file)
        "tagline": "Fae {{SITENAME}}",
        "help": "Help",
        "search": "Rake",
+       "search-ignored-headings": " #<!-- lea this line exactly aes it is --> <pre>\n# Heidins that will be ignored bi rake.\n# Chynges til this tak effect aes suin aes the page wi the heiding is index't.\n# Ye can force page reindexin bi daeing ae null edit.\n# Syntax is aes follaes:\n#   * Awthin fae ae \"#\" chairacter til the end o the line is ae comment\n#   * Ilka no-blank line is the exact title tae ignore, case an awthin\nReferences\nExternal links\nSee ava\n #</pre> <!-- leave this line exactly as it is -->",
        "searchbutton": "Rake",
        "go": "Gang",
        "searcharticle": "Gang",
        "passwordreset-emailtext-user": "Uiser $1 oan {{SITENAME}} requested ae reset o yer passwaird fer {{SITENAME}}\n($4). The follaein uiser {{PLURAL:$3|accoont is|accoonts ar}} associated wi this wab-mail address:\n\n$2\n\n{{PLURAL:$3|This temperie passwaird|Thir temperie passwairds}} will expire in {{PLURAL:$5|yin day|$5 days}}.\nYe shid log in n chuise ae new password nou. Gif some ither bodie haes makit this request, or gif ye'v mynded yer oreeginal passwaird, n ye nae langer wish tae chynge it, ye can ignore this message n continue uisin yer auld passwaird.",
        "passwordreset-emailelement": "Uisername: \n$1\n\nTemperie passwaird: \n$2",
        "passwordreset-emailsentemail": "Ae passwaird reset wab-mail haes been sent.",
-       "passwordreset-emailsent-capture": "Ae passwaird reset wab-mail haas been sent, this is shawn ablow.",
-       "passwordreset-emailerror-capture": "Ae passwaird reset wab-mail wis generated, (this is shawn ablow), but sendin it til the {{GENDER:$2|uiser}} failed: $1",
        "changeemail": "Chynge wab-mail address",
        "changeemail-header": "Chynge accoont wab-mail address",
        "changeemail-no-info": "Ye maun be loggit in tae access this page directly.",
        "undo-nochange": "The edit appears tae hae awready been ondone.",
        "undo-summary": "Ondae reveesion $1 bi [[Special:Contributions/$2|$2]] ([[User talk:$2|Tauk]])",
        "undo-summary-username-hidden": "Ondae reveesion $1 bi ae skauk't uiser",
-       "cantcreateaccounttitle": "Canna creaut accoont",
        "cantcreateaccount-text": "Accoont cræftin fae this IP address ('''$1''') haes been blockit bi [[User:$3|$3]].\n\nThe raison fer this, gien bi $3 is ''$2''",
        "cantcreateaccount-range-text": "Accoont cræftin fae IP addresses in the range <strong>$1</strong>, that inclædes yer IP address (<strong>$4</strong>), haes been blockit bi [[User:$3|$3]].\n\nThe raison gien bi $3 is <em>$2</em>",
        "viewpagelogs": "Leuk at logs fer this page",
index 279b68f..15d45cf 100644 (file)
        "nocookiesnew": "يُوزر کاتو کلي چڪو، پر توهان لاگ اِن نہ ٿيا آهيو. يُوزرس کي لاگ اِن ڪرڻ لاءِ {{SITENAME}} ڪوڪيز استعمال ڪندي آهي. توهان ڪوڪيز کي ناڪاره بڻائي رکيو آهي. لاگ اِن ٿيڻ لاءِ ڪوڪيز کي ڪارائتو بڻايو.",
        "nocookieslogin": "يُوزرس کي لاگ اِن ڪرڻ لاءِ {{SITENAME}} ڪوڪيز استعمال ڪندي آهي. توهان ڪوڪيز کي ناڪاره بڻائي رکيو آهي. لاگ اِن ٿيڻ لاءِ ڪوڪيز کي ڪارائتو بڻايو.",
        "noname": "توهان جو ڄاڻايل يُوزرنانءُ ناقابل ڪار آهي.",
-       "loginsuccesstitle": "لاگ اِن ڪامياب",
+       "loginsuccesstitle": "لاگ اِن ٿيل",
        "loginsuccess": "'''هاڻي توهان {{SITENAME}} تي بطور \"$1\" لاگ اِن ٿيل آهيو.'''",
-       "nosuchuser": "\"$1\" نالي سان ڪو بہ يوزر نہ آهي.  \"$1\".\n ننڍن وڏن اکرن ۾ امتياز ڪرڻ لازمي آهي. \nهِجي چڪاسيو،يا [[Special:CreateAccount|نئون کاتو تخليق ڪريو]]",
+       "nosuchuser": "\"$1\" نالي سان ڪو بہ يوزر نہ آهي.\nننڍن وڏن اکرن ۾ امتياز ڪرڻ لازمي آهي. \nهِجي چڪاسيو، يا [[Special:CreateAccount|نئون کاتو تخليق ڪريو]]",
        "nosuchusershort": "\"$1\" نالي ڪو بہ يُوزر ناهي.\nهِجي جي پڪ ڪندا.",
        "nouserspecified": "توهان کي ڪو يوزرنانءُ ڄاڻائڻو پوندو.",
        "login-userblocked": "هيءُ يُوزر بندشيل آهي. لاگ اِن جي اجازت نہ ٿي ڏجي.",
        "newpassword": "نئون ڳجھو لفظ:",
        "retypenew": "نئون ڳجھو لفظ ٻيهر ٽائيپ ڪندا:",
        "resetpass_submit": "ڳجھو لفظ طَي ڪريو ۽ لاگ اِن ٿيو",
-       "changepassword-success": "توهان جو ڳجھو لفظ ڪاميابيءَ سان بدلايو ويو!",
+       "changepassword-success": "توهان جو ڳجھولفظ بدلايو ويو آھي!",
        "changepassword-throttled": "توهان تازو ئي لاگ اِن ٿيڻ جون هيڪانديون گھڻيون ڪوششون ڪيون آهن. مهرباني ڪري $1 لاءِ ترسي پوءِ وري ڪوشش ڪريو.",
        "botpasswords-label-create": "سرجيو",
        "botpasswords-label-update": "تجديد",
        "resetpass-no-info": "هيءُ صفحو پڙهڻ لاءِ لاگ اِن ٿيڻ ضروري آهي.",
        "resetpass-submit-loggedin": "ڳجھو لفظ بدلايو",
        "resetpass-submit-cancel": "رد",
-       "resetpass-wrong-oldpass": "ناقابل ڪار هاڻوڪو يا عارضي ڳجھو لفظ. \nتوهان پنهنجو ڳجھو لفظ اڳ ۾ ئي بدلائي چڪا آهيو يا نئين ڳجھي لفظ لاءِ درخواست ڏئي چڪا آهيو.",
+       "resetpass-wrong-oldpass": "ناقابل ڪار هاڻوڪو يا عارضي ڳجھولفظ. \nتوهان پنهنجو ڳجھو لفظ اڳ ۾ ئي بدلائي چڪا آهيو يا نئين ڳجھي لفظ لاءِ درخواست ڏئي چڪا آهيو.",
        "resetpass-recycled": "مهرباني ڪري پنهنجي هاڻوڪي ڳجھي لفظ کان ڪو مختلف ڳجھو لفظ چونڊيو.",
        "resetpass-temp-emailed": "توهان برق ٽپال ذريعي اماڻيل عارضي ڳجھي لفظ سان لاگ اِن ٿيا آهيو. لاگ اِن کي مڪمل ڪرڻ لاءِ توهان کي هتي نئون ڳجھو لفظ طَي ڪرڻو ئي پوندو:",
        "resetpass-temp-password": "عارضي ڳجھو لفظ:",
        "userrights": "يُوزر حقن جو بندوبست",
        "userrights-lookup-user": "يوزر گروپَ سنڀاليو",
        "userrights-user-editname": "يُوزرنانءُ ڄاڻايو:",
-       "editusergroup": "يوزر گروپَ سنواريو",
+       "editusergroup": "{{GENDER:$1|يوزر}} گروھ ترميميو",
        "userrights-editusergroup": "يوزر گروپَ سنواريو",
-       "saveusergroups": "يوزر گروپَ سنڀاليو",
+       "saveusergroups": "{{GENDER:$1|يوزر}} گروھ سانڍيو",
        "userrights-groupsmember": "برڪن:",
        "userrights-groupsmember-auto": "رڪن واجبي:",
        "userrights-reason": "سبب:",
        "rightslog": "يُوزر حق لاگ",
        "action-read": "هي صفحو پڙهو",
        "action-edit": "هن صفحي کي سسنواريو",
-       "action-createpage": "صفحا تخليق ڪريو",
-       "action-createtalk": "مباحثي صفحا تخليق ڪريو",
+       "action-createpage": "ھي صفحو تخليق ڪريو",
+       "action-createtalk": "ھي مباحثي صفحو تخليق ڪريو",
        "action-createaccount": "هي يوزر کاتو تخليق ڪريو",
        "action-history": "هن صفحي جي سوانح ڏسو",
        "action-minoredit": "هن ترميم کي معمولي طور نشان لڳايو",
        "tooltip-t-recentchangeslinked": "ويجھڙائيءَ ۾ صفحن ۾ ٿيل تبديليون هن صفحي سان ڳنڍيل آهن",
        "tooltip-feed-atom": "هن صفحي لاءِ ايٽم فيڊ",
        "tooltip-t-contributions": "{{GENDER:$1|ھن يوزر}} جي ڀاڱيدارين جي فھرست",
-       "tooltip-t-emailuser": "هن يُوزر کي برق ٽپال اماڻيو",
+       "tooltip-t-emailuser": "{{GENDER:$1|ھن يوزر}} ڏانھن ايميل موڪليو",
        "tooltip-t-upload": "فائيل چاڙهيو",
        "tooltip-t-specialpages": "سڀني خاص صفحن جي فهرست",
        "tooltip-t-print": "هن صفحي جو ڇاپائتو پرت",
        "tags-activate-submit": "فعاليو",
        "tags-deactivate-title": "ٽيگ کي غير فعال ڪريو",
        "tags-deactivate-reason": "سبب:",
-       "tags-edit-existing-tags-none": "\"ڪو بہ نہ\"",
+       "tags-edit-existing-tags-none": "<em>ڪو بہ نہ</em>",
        "tags-edit-new-tags": "نوان ٽيگس:",
        "tags-edit-reason": "سبب:",
        "compare-page1": "صفحو 1",
        "special-characters-group-thai": "ٿائي",
        "mw-widgets-dateinput-no-date": "ڪا بہ تاريخ نہ چونڊيل",
        "mw-widgets-titleinput-description-new-page": "اڃا اهو صفحو وجود نہ ٿو رکي",
-       "mw-widgets-titleinput-description-redirect": "$1 ڏانهن چوريل",
-       "api-error-blacklisted": "براءِ مهرباني ڪو مختلف، تشريحي عنوان چونڊيو."
+       "mw-widgets-titleinput-description-redirect": "$1 ڏانهن چوريل"
 }
index 7b36ba3..273c8a6 100644 (file)
        "newwindow": "(īr atverams naujam longė)",
        "cancel": "Pabengtė",
        "moredotdotdot": "Daugiau...",
-       "morenotlisted": "Tas sārošos ožbengts nie.",
+       "morenotlisted": "Tas sārašos ožbėngts nie.",
        "mypage": "Poslapis",
        "mytalk": "Aptarėms",
        "anontalk": "Aptarėms",
        "media_tip": "Nūruoda abruozdielin",
        "sig_tip": "Tamstas parašos ė čiesos",
        "hr_tip": "Golos briežis (nenauduokat ba rēkala)",
-       "summary": "Pāiškėnėms:",
+       "summary": "Keitėma paāškėnėms:",
        "subject": "Tema/ontraštė:",
        "minoredit": "Mažos pakeitėms",
        "watchthis": "Keravuotė ton poslapė",
-       "savearticle": "Ėšsauguotė poslapė",
+       "savearticle": "Ėšsauguotė poslapi",
+       "publishpage": "Padėrbtė ton poslapi",
+       "publishchanges": "Ožrašītė poslapė pamainīmus",
        "preview": "Parveiza",
        "showpreview": "Ruodītė parveiza",
        "showdiff": "Ruodītė skėrtomus",
        "protectedpagewarning": "'''DIEMESĖ: Šėts poslapis īr ožrakints ėr anū redagoutė gal tėk admėnėstratuorė teises torėntīs prietelē.'''",
        "semiprotectedpagewarning": "'''Pastebiejėms:''' Šėts poslapis bova ožrakėnts ėr anuo gal redagoutė tėk regėstroutė nauduotojā.",
        "titleprotectedwarning": "'''DIEMESĖ: Tas poslapis bova ožrakėnts tēp, ka tėktās kāpkatrė nauduotuojē galietu ana sokortė.'''",
-       "templatesused": "{{PLURAL:$1|Šabluons, katros|Šabluonā, katrėi}}, īr nauduojamė tamė poslapi:",
+       "templatesused": "{{PLURAL:$1|Šabluons, katros|Šabluonā, katrėi}}, īr nauduojamė tamė poslapie:",
        "templatesusedpreview": "{{PLURAL:$1|Šabluons|Šabluonā}}, nauduotė šėtuo parvaizuo:",
        "templatesusedsection": "Šabluonā, nauduotė šėtom skėrsnelī:",
        "template-protected": "(apsergiets)",
        "permissionserrorstext": "Tamsta netorėt teisiu šėta darītė diel {{PLURAL:$1|tuos prīžastėis|tū prīžastiū}}:",
        "permissionserrorstext-withaction": "Tamsta natorėt leidėma $2 dielē {{PLURAL:$1|tuokės dingstėis|tuokiū dingstiū}}:",
        "recreate-moveddeleted-warn": "'''Parspiejėms: Tamsta ikeliat faila, katros onkstiau bova ėštrėnts.'''\n\nTamsta torietomiet nusprēstė, a īr naudėnga tuoliau ikeldinietė ta faila.\nTuo faila pašalinėma istuorėjė īr pateikta dielē patuogoma:",
-       "moveddeleted-notice": "Tas poslapis bova ėštrints.\nĖštrinta poslapė atmainū sārošos īr douts paveiziejėmō apatiuo.",
+       "moveddeleted-notice": "Tas poslapis bova ėštrints.\nĖštrinta poslapė atmainū sārašos īr douts paveiziejėmō apatiuo.",
        "log-fulllog": "Veizietė vėskon",
        "edit-conflict": "Do keitėmo nesotink.",
        "postedit-confirmation-created": "Poslapis padėrbts.",
        "undo-success": "Keitėms gal būtė atšaukts. Prašuom patėkrėntė palīgėnėma, asonti žemiau, kū patvėrtėntomiet, kū Tamsta šėta ė nuorėt padarītė, ė tumet ėšsauguokit pakeitėmos, asontios žemiau, kū ožbėngtomiet keitėma atšaukėma.",
        "undo-failure": "Keitėms nagal būt atšaukts diel konflėktounantiu tarpėniu pakeitėmu.",
        "undo-summary": "Pargrōžėnams pakeitėms $1, padėrbts nauduotuojė [[Special:Contributions/$2|$2]] ([[User talk:$2|aptarėms]])",
-       "cantcreateaccounttitle": "Nie galam padėrbtė nauduotuojė",
        "cantcreateaccount-text": "Paskīrū kūrėma ėš šėta IP adresa ('''$1''') ožbluokava [[User:$3|$3]].\n\n$3 nuruodīta prīžastis īr ''$2''",
        "cantcreateaccount-range-text": "Nauduotuoju dėrbėms nug IP adresū <strong>$1</strong>, terp katrū prėgol ė Tamstas IP adresos (<strong>$4</strong>), bova ožgints nauduotuojė [[User:$3|$3]].\n\n$3 ožrašė tuokė dingstiː <em>$2</em>",
        "viewpagelogs": "Ruodītė ton poslapė īpatingus notėkėmus",
        "difference-title-multipage": "Skėrtoms terp poslapiu „$1“ ė „$2“",
        "difference-multipage": "(Skėtroms terp poslapiu)",
        "lineno": "Eilotė $1:",
-       "compareselectedversions": "Pamieruotė pasėrinktus atmainus",
+       "compareselectedversions": "Pamieruotė pasirinktus atmainus",
+       "showhideselectedversions": "Ruodītė/kavuotė atmainus, katrūs pasirinkuot",
        "editundo": "atgrōžintė",
        "diff-empty": "(Nie skėrtoma)",
        "diff-multi-sameuser": "({{PLURAL:$1|Vėins tarpėnis pakeitėms|$1 tarpėnē pakeitėmā|$1 tarpėniu pakeitėmu}}, padėrbtū tuo patė nauduotuojė, nie ruoduoma)",
        "prevn-title": "{{PLURAL:$1|Onkstesnis $1 rezoltats|Onkstesnio $1 rezoltato|Onkstesni $1 rezoltatā}}",
        "nextn-title": "{{PLURAL:$1|Kėts $1 gavėms|Kėtė $1 gavėmā|Kėtū $1 gavėmu}}",
        "shown-title": "Ruodītė $1 {{PLURAL:$1|gavėni|gavėnius|gavėniū}} ont poslapė",
-       "viewprevnext": "Veizėtė ($1 {{int:pipe-separator}} $2) ($3).",
+       "viewprevnext": "Veizietė ($1 {{int:pipe-separator}} $2) ($3).",
        "searchmenu-exists": "'''Poslapis pavadėnts „[[$1]]“ šėtuo wiki'''",
        "searchmenu-new": "<strong>Padėrbtė poslapi, katros vadėntos „[[:$1]]“</strong> {{PLURAL:$2|0=|Dā veiziekat paėiškuo rasta straipsni|Dā veiziekat paėiškuos gavėnius.}}",
        "searchprofile-articles": "Torėnė poslapē",
        "right-delete": "Trintė poslapius",
        "right-browsearchive": "Ėiškuotė ėštrintū poslapiu",
        "right-undelete": "Tou poslapi padėrbtė apent",
-       "newuserlogpage": "Nauduotuojė kūrėma sārošos",
+       "newuserlogpage": "Nauduotuojė kūrėma sārašos",
        "rightslog": "Nauduotuoju teisiu istuorėjė",
        "rightslogtext": "Pateikiams nauduotuoju teisiu pakeitėmu sārašos.",
        "action-read": "skaitītė ton poslapi",
        "enhancedrc-history": "istuorėjė",
        "recentchanges": "Vielībė̄jė pakeitėmā",
        "recentchanges-legend": "Vielībūju pakeitėmu pasėrinkėmā",
-       "recentchanges-summary": "Keravuokat patius vielībuosius wiki pakeitėmus tamė poslapi.",
+       "recentchanges-summary": "Keravuokat patius vielībuosius wiki pakeitėmus tamė poslapie.",
        "recentchanges-feed-description": "Keravuokėt patius vielībiausius pakeitėmus pruojektō tamė šaltėnī.",
        "recentchanges-label-newpage": "Šėtuo keitėmuo padėrbts naus poslapis",
        "recentchanges-label-minor": "Tas īr mažos pataisīms",
        "rcshowhidemine": "$1 mona pakeitėmus",
        "rcshowhidemine-show": "Ruodītė",
        "rcshowhidemine-hide": "Kavuotė",
+       "rcshowhidecategorization": "$1, kap poslapiam doud kateguorėjės",
        "rclinks": "Ruodītė vielībus $1 pakeitėmu par paskuojės $2 dėinas<br />$3",
        "diff": "skėrt",
        "hist": "ist",
        "uploadlogpage": "Ožkruovėmu istuorėjė",
        "uploadlogpagetext": "Žemiau pateikiam paskotėniu failu ikielima istuorėjė.",
        "filename": "Abruozdėlė vards",
-       "filedesc": "Pāiškėnėms",
-       "fileuploadsummary": "Pāiškėnėms:",
+       "filedesc": "Pškėnėms",
+       "fileuploadsummary": "Tromps aprašīms:",
        "filereuploadsummary": "Abruozdielė pakeitėmāː",
        "filestatus": "Derbieju teisėsː",
        "filesource": "Šaltėnis:",
        "filehist-user": "Nauduotuos",
        "filehist-dimensions": "Mierā",
        "filehist-filesize": "Abruozdielė dėdloms",
-       "filehist-comment": "Pāiškėnėms",
+       "filehist-comment": "Pškėnėms",
        "imagelinks": "Abruozdieliu nauduojėms",
        "linkstoimage": "{{PLURAL:$1|Ons poslapis|Anėi poslapē}} ruod ton abruozdielin:",
        "nolinkstoimage": "Abruozdielėp neruod anėjuoks poslapis.",
        "filerevert": "Sogrōžėntė $1",
        "filerevert-legend": "Faila sogrōžinėms",
        "filerevert-intro": "<span class=\"plainlinks\">Tamsta grōžėnat '''[[Media:$1|$1]]''' i versėje $4 ($2, $3).</span>",
-       "filerevert-comment": "Pāiškėnėms:",
+       "filerevert-comment": "Dingstės:",
        "filerevert-submit": "Grōžėntė",
        "filedelete": "Trintė $1",
        "filedelete-legend": "Trintė faila",
        "statistics-users": "Ožsėregėstravosiu [[Special:ListUsers|nauduotuoju]]",
        "statistics-users-active": "Aktīviu nauduotuoju",
        "statistics-users-active-desc": "Nauduotuojē, katrėi par {{PLURAL:$1|paskiausė dėina|paskiausė 2 dėinė|paskiausės $1 dėinas|paskiausiu $1 dėinū}} padėrba keitėmu",
+       "pageswithprop": "Poslapē so apībriežtuom savībėm",
+       "pageswithprop-legend": "Poslapē so apībriežtuom savībėm",
        "pageswithprop-submit": "Ēk",
        "doubleredirects": "Dvėgobė nusokėmā",
        "doubleredirectstext": "Tėi paradresavėmā ruod i kėtus paradresavėma poslapius. Kuožnuo eilotē pamėnavuots pėrmasā ėr ontrasā paradresavėmā, tēpuogi ontrojė paradresavėma paskėrtis, katra paprastā ė paruod i tėkraji poslapi, i katra pėrmasā paradresavėms ė torietu ruodītė.",
        "protectedpages-unknown-performer": "Nežėnuoms nauduotuos",
        "protectedtitles": "Apsauguotė pavadinėmā",
        "protectedtitlesempty": "Šėtou čieso nier anėjuokė pavadinėma, katros apsauguots tās parametrās.",
-       "listusers": "Sārošos nauduotuoju",
+       "listusers": "Sārašos nauduotuoju",
        "listusers-editsonly": "Ruodītė tėktās nauduotuojus katrėi īr atlėkė pakeitėmus",
        "usereditcount": "{{PLURAL:$1|pataisīms|pataisīmā|pataisīmu}}",
        "usercreated": "{{GENDER:$3|Padėrba paskīra}} $1 $2",
        "logempty": "Istuorėjuo nier anėjuokiū atitinkontiu atsėtėkimu.",
        "log-title-wildcard": "Ėiškuotė pavadinėmu, katrė prasėded šėtuo teksto",
        "showhideselectedlogentries": "Ruodītė/kavuotė sāraša ponktus, katrūs pasėrėnkot",
+       "checkbox-select": "Rinktėis: $1",
+       "checkbox-all": "Viskos",
        "allpages": "Vėsė straipsnē",
        "nextpage": "Kėts poslapis ($1)",
        "prevpage": "Onkstesnis poslapis ($1)",
        "listgrouprights": "Nauduotuoju gropiu teisės",
        "listgrouprights-group": "Gropė",
        "listgrouprights-rights": "Teisės",
-       "listgrouprights-members": "(nariū sārošos)",
+       "listgrouprights-members": "(nariū sārašos)",
+       "trackingcategories": "Pruogramėnės keravuojėma kateguorėjės",
        "mailnologin": "Nier adresa",
        "mailnologintext": "Tamstā reik būtė [[Special:UserLogin|prisėjongosiam]]\nė tor būtė ivests teisings el. pašta adresos Tamstas [[Special:Preferences|nustatīmuos]],\nkū siōstomiet el. gruomatas kėtėm nauduotuojam.",
        "emailuser": "Rašītė gruomata šėtam nauduotuojō",
        "actioncomplete": "Vēksmos padėrbts īr",
        "actionfailed": "Vēksmos atšaukts īr",
        "deletedtext": "„$1“ ėštrints īr.\nVielībūju trīnėmu istuorėjė - $2.",
-       "dellogpage": "Ėštrīnėmu sārošos",
+       "dellogpage": "Ėštrīnėmu sārašos",
        "dellogpagetext": "Apatiuo gol patīs vielībė̄jė ėštrīnėmā.",
-       "deletionlog": "ėštrīnėmu sārošos",
+       "deletionlog": "ėštrīnėmu sārašos",
        "reverted": "Grōžinta tāp, kāp ėšruodė pėrmiou",
        "deletecomment": "Dingstės:",
        "deleteotherreason": "Kėta/papėlduoma dingstės:",
        "rollbackfailed": "Atmetėms nasėgava",
        "cantrollback": "Negalėma atmestė redagavėma; paskotinis keitės nauduotuos īr tuo poslapė autorius.",
        "alreadyrolled": "Nė̄šēn otgrōžintė pakeitėma, [[:$1]] katra padėrba [[User:$2|$2]] ([[User talk:$2|aptarėms]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\nkažkas ton jau padėrba pėrmiou.\n\nVielībiausā ton poslapė pakeitėms padėrbts [[User:$3|$3]] ([[User talk:$3|aptarėms]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) īr.",
-       "editcomment": "Padėrbėma pāiškėnėms bova: <em>$1</em>.",
+       "editcomment": "Padėrbėma pškėnėms bova: <em>$1</em>.",
        "revertpage": "Atmests [[Special:Contributions/$2|$2]] ([[User talk:$2|aptarėms]]) pakeitėms; sogrōžints atmains, katron padėrba nauduotuos [[User:$1|$1]]",
        "rollback-success": "Atmestė $1 padėrbtė keitėmā; grōžints $2 padėrbts atmains.",
        "sessionfailure": "Atruod ka īr biedū so Tamstas prėsėjongėmo; tas vēksmos bova grōžints kāp atsargoma prėimonė nu sesėjės vuogėma.\nPrašoum mīgtė „atgal“ ėr parkrautė poslapi ėš katruo atiejėt, ė pamieginkėt apent.",
        "undeleteextrahelp": "Nuoriedamė atkortė vėsa poslapi, palikit vėsas varnales napažīmietas ėr\nspauskėt '''''Atkortė'''''. Nuoriedamiė atlėktė pasirėnktini atstatīma, pažīmiekit varnales šėtū versėju, katras nuorietomiet atstatītė, ėr spauskėt '''''Atkortė'''''. Paspaudus\n'''''Ėš naujė''''' bos ėšvalītuos vėsos varnalės ėr kuomentara lauks.",
        "undeleterevisions": "$1 {{PLURAL:$1|versėjė|versėjės|versėju}} soarkīvouta",
        "undeletehistory": "Jē atstatīsėt straipsni, istuorėjuo bos atstatītuos vėsos versėjės.\nJē puo ėštrīnima bova sokuots straipsnis tuokiuo patio pavadėnėmo,\natstatītuos versėjės atsiras onkstesnie istuorėjuo, o dabartėnė\nversėjė lėks napakeista. Atkoriant īr prarondamė apribuojimā failu versėjuom.",
-       "undeleterevdel": "Atkorėms nebus ivīkdīts, jē šėtā nulems paskotėnės poslapė versėjės dalini ėštrīnima.\nTuokēs atvejās, Tamstā rēk atžīmietė a atkavuotė naujausēs ėštrintas versėjės.\nFailu versėjės, katrū netorėt teisiu veizėtė, nebus atkortas.",
+       "undeleterevdel": "Poslapė atstatīms nabūs padarīts, jēb dielē ton ėšsitrintom vielībė̄jė poslapė pakeitėmā.\nTūdie Tamstā rēk atžīmietė aba atkavuotė vielībuosius ėštrintus atmainus.",
        "undeletehistorynoadmin": "Šėts straipsnis bova ėštrints. Trīnima prižastis\nruodoma žemiau, teipuogi kas redagava poslapi\nlėgė trīnima. Ėštrintū poslapiu tekstos īr galėmas tėk admėnėstratuoriam.",
        "undelete-revision": "Ėštrėnta $1 versėjė, katra $4 d. $5 padėrba $3:",
        "undeleterevision-missing": "Neteisėnga a dėngosė versėjė. Tamsta mažo torėt bluoga nūruoda, a versėjė bova atkorta a pašalėnta ėš arkīva.",
        "undeletebtn": "Grōžintė",
        "undeletelink": "veizietė/grōžintė",
        "undeleteviewlink": "veizietė",
-       "undeleteinvert": "Žīmietė prīšėngā",
+       "undeleteinvert": "Žīmietė atvėrkštē",
        "undeletecomment": "Dingstės:",
        "undeletedrevisions": "$1 {{PLURAL:$1|pakeitėms sogrōžints|pakeitėmā sogrōžintė|pakeitėmu sogrōžintu}} īr",
        "undeletedrevisions-files": "sogrōžintaː $1 {{PLURAL:$1|pakeitėms|pakeitėmā|pakeitėmu}} ėr $2 {{PLURAL:$2|abruozdielis|abruozdielē|abruozdieliu}}",
        "undelete-show-file-confirm": "A ėš tėkrā nuorėt parveizietė ėštrėnta faila „<nowiki>$1</nowiki>“ $2 $3 versėjė?",
        "undelete-show-file-submit": "Tēp",
        "namespace": "Vardū srėtės:",
-       "invert": "Žīmietė prīšėngā",
+       "invert": "Žīmietė atvėrkštē",
        "tooltip-invert": "Pažīmiekat ton varnalė, ka pakavuotomiet pakeitėmus pasėrinktūs poslapiūs (ė prėgolontės vardū srėtis)",
        "namespace_association": "Prėgolontė vardū srėtės",
        "tooltip-namespace_association": "Pažīmiekat ton varnalė, ka prėgoldītomat aptarėmus, katrėi ī sosėjė so parinkta vardū srėtim",
        "uctop": " (vielībs)",
        "month": "Nug mienėsė (ėr onkstiau):",
        "year": "Nug metu (ėr onkstiau):",
-       "sp-contributions-newbies": "Ruodītė tėk naujū prieteliu duovios",
+       "sp-contributions-newbies": "Ruodītė tėktās naujū prieteliu duovius",
        "sp-contributions-newbies-sub": "Naujuoms paskīruoms",
        "sp-contributions-newbies-title": "Nauduotuoju keitėmā naujuoms paskīruoms",
        "sp-contributions-blocklog": "Bluokavėmu istuorėjė",
        "contribslink": "duovis",
        "emaillink": "siōstė pašta",
        "autoblocker": "Autuomatėnis ožbluokavėms, nes dalėnaties IP adreso so nauduotuojo \"$1\". Prīžastės - \"$2\".",
-       "blocklogpage": "Ožgīnėmu sārošos",
+       "blocklogpage": "Ožgīnėmu sārašos",
        "blocklog-showlog": "Nauduotuos jau bova ožgints pėrmiou.\nApatiuo veiziekat kas ė kāpː",
        "blocklogentry": "ožgīnė [[$1]] nug dėrbėma, tas vēk ton čiesa - $2 $3",
        "reblock-logentry": "pakeistė [[$1]] bluokavėma nustatīmā, naus bluokavėma čiesos īr $2 $3",
        "tooltip-pt-anonuserpage": "Nauduotuojė poslapis Tamstas IP adresō",
        "tooltip-pt-mytalk": "Tamstas aptarėma poslapis",
        "tooltip-pt-preferences": "Mona nustatīmā",
-       "tooltip-pt-watchlist": "Poslapiu, katrūs Tamsta pasėrėnkuot keravuotė, sārošos.",
+       "tooltip-pt-watchlist": "Poslapiu, katrūs Tamsta pasirinkuot keravuotė, sārašos.",
        "tooltip-pt-mycontris": "Tamstas dėrbtū keitėmu sārašos",
        "tooltip-pt-login": "Kvėitam prėsėjongtė, nuors tas ė nie būtėna.",
        "tooltip-pt-logout": "Atsėjongtė",
        "tooltip-save": "Ėšsauguotė pakeitėmus",
        "tooltip-preview": "Ta īr pakeitėmu parveiza. Prašuom parveizat prīš ėšsaugont!",
        "tooltip-diff": "Ruod, kū parkeitiet straipsni.",
-       "tooltip-compareselectedversions": "Veizėtė abodvėju pasėrėnktū poslapė versėju skėrtomos.",
+       "tooltip-compareselectedversions": "Veizietė abodvėju pasirinktū poslapė atmainu skėrtomus.",
        "tooltip-watch": "Pridietė šėta poslapi i keravuojamu sāraša",
        "tooltip-recreate": "Atkortė poslapi napaisant šėto, kū ans bova ėštrints",
        "tooltip-rollback": "Sogrōžintė poslapi tāp, kāp ons bova prīš vielībūs ton nauduotuojė pardėrbėmus",
        "watchlistedit-raw-explain": "Žemiau ruodomė poslapē Tamstas keravuojamu sārašė, ė gal būtė pridietė i a pašalėntė ėš sāraša; vėins poslapis eilotie. Bėngė paspauskėt „Atnaujėntė keravuojamu sāraša“. Tamsta tēpuogi galėt [[Special:EditWatchlist|nauduotė standartėni radaktuoriu]].",
        "watchlistedit-raw-titles": "Poslapē:",
        "watchlistedit-raw-submit": "Atnaujėntė keravuojamu sāraša",
-       "watchlistedit-raw-done": "Tamstas keravuojamu sārošos bova atnaujėnts.",
+       "watchlistedit-raw-done": "Tamstas keravuojamu poslapiu sārašos bova atnaujints.",
        "watchlistedit-raw-added": "$1 {{PLURAL:$1|poslapis bova pridiets|poslapē bova pridietė|poslapiu bova pridieta}}:",
        "watchlistedit-raw-removed": "$1 {{PLURAL:$1|poslapis bova pašalėnts|poslapē bova pašalėntė|poslapiu bova pašalėnta}}:",
        "watchlisttools-view": "Veizietė sosėjosius pakeitėmus",
        "logentry-upload-overwrite": "$1 {{GENDER:$2|ožkruovė}} naujė $3 atmaina",
        "logentry-upload-revert": "$1 {{GENDER:$2|ožkruovė}} $3",
        "rightsnone": "(juokiū)",
-       "revdelete-summary": "keitėma pāiškėnėms",
+       "revdelete-summary": "keitėma pškėnėms",
        "feedback-close": "Padėrbt",
        "searchsuggest-search": "Ėiškuotė",
        "searchsuggest-containing": "katrėi tor...",
index 54c4e65..09392ad 100644 (file)
        "passwordreset-emailtext-user": "Korisnik $1 na {{SITENAME}} je zatražio resetiranje vaše lozinke/zaporke za {{SITENAME}}\n($4). Sljedeći {{PLURAL:$3|korisnički račun je|korisnički računi su}} povezani s ovom e-mail adresom:\n\n$2\n\n{{PLURAL:$3|Ova privremena lozinka|Ove privremene lozinke}} će isteći za {{PLURAL:$5|jedan dan|$5 dana}}.\nTrebate se prijaviti i odabrati novu lozinku. Ako je neko drugi napravio ovaj\nzahtjev, ili ako ste se sjetili Vaše originalne lozinke, a ne želite je više promijeniti, \nmožete zanemariti ovu poruku i nastaviti koristiti staru lozinku.",
        "passwordreset-emailelement": "Korisničko ime: \n$1\n\nPrivremena šifra: \n$2",
        "passwordreset-emailsentemail": "Ako je ovo adresa e-pošte s kojom ste registrirali ovaj račun, podsjetnik šifre/lozinke/zaporke će vam biti poslan na vašu adresu e-pošte.",
-       "passwordreset-emailsent-capture": "E-mail za resetiranje lozinke/zaporke je poslan (prikazan dolje).",
-       "passwordreset-emailerror-capture": "E-mail za resetiranje lozinke/zaporke, prikazan dolje, je poslan, ali slanje {{GENDER:$2|korisniku|korisnici|korisniku}} nije uspjelo: $1",
        "changeemail": "Promeni ili ukloni e-adresu",
        "changeemail-header": "Ispunite sljedeći formular da biste promijenili adresu e-pošte. Ako želite ukloniti postojeću adresu e-pošte s vašeg korisničkog računa, pri ispunjavanju formulara, polje nove adrese e-pošte ostavite prazno.",
-       "changeemail-passwordrequired": "Morat ćete unijeti vašu lozinku/zaporku da potvrdite ovu promjenu.",
        "changeemail-no-info": "Morate biti prijavljeni da biste izravno pristupili ovoj stranici.",
        "changeemail-oldemail": "Trenutna e-mail adresa:",
        "changeemail-newemail": "Nova e-mail adresa:",
        "minoredit": "Ovo je manje uređenje",
        "watchthis": "Prati ovu stranicu",
        "savearticle": "Spremi stranicu",
+       "publishpage": "Objavi stranicu",
+       "publishchanges": "Objavi izmjene",
        "preview": "Pregled",
        "showpreview": "Prikaži pregled",
        "showdiff": "Prikaži izmjene",
        "undo-nochange": "Ovo je uređivanje izgleda već bilo poništeno.",
        "undo-summary": "Poništena izmjena $1 [[Special:Contribs/$2|korisnika $2]] ([[User talk:$2|razgovor]])",
        "undo-summary-username-hidden": "Poništi izmjenu $1 od skrivenog korisnika",
-       "cantcreateaccounttitle": "Nije moguće napraviti korisnički račun",
        "cantcreateaccount-text": "Pravljenje korisničkog računa sa ove IP adrese ('''$1''') je blokirano od strane [[User:$3|$3]].\n\nRazlog koji je naveo $3 je ''$2''",
        "cantcreateaccount-range-text": "Stvaranje računa od IP adresa iz pojasa<strong>$1</strong>, koji uključuje vašu IP adresu (<strong>$4</strong>), je blokirao/la [[User:$3|$3]].\n\nRazlog koji je dao/la $3 je <em>$2</em>",
        "viewpagelogs": "Pogledaj protokole ove stranice",
        "tooltip-ca-nstab-category": "Pogledajte stranicu kategorije",
        "tooltip-minoredit": "Označi ovo kao manje uređenje",
        "tooltip-save": "Spremite svoje izmjene",
+       "tooltip-publish": "Objavi svoje izmjene",
        "tooltip-preview": "Pregledajte svoje izmjene. Molimo vas da ovo koristite prije spremanja.",
        "tooltip-diff": "Prikaz izmjena koje ste napravili u tekstu",
        "tooltip-compareselectedversions": "Pogledajte pazlike između dvije selektovane verzije ove stranice.",
index 87c4234..cb5735d 100644 (file)
@@ -32,7 +32,8 @@
                        "Roonyh",
                        "Matma Rex",
                        "SusithCM",
-                       "Sandaru"
+                       "Sandaru",
+                       1100100
                ]
        },
        "tog-underline": "සබැඳි යටීර කිරීම:",
        "october-date": "ඔක්තොම්බර් $1",
        "november-date": "නොවැම්බර් $1",
        "december-date": "දෙසැම්බර් $1",
+       "period-am": "පෙ.ව.",
+       "period-pm": "ප.ව.",
        "pagecategories": "{{PLURAL:$1|ප්‍රවර්ගය|ප්‍රවර්ග}}",
        "category_header": "\"$1\" ප්‍රවර්ගයට අයත් පිටු",
        "subcategories": "උපප්‍රවර්ග",
        "virus-scanfailed": "පරිලෝකනය අසාර්ථක විය (කේතය $1)",
        "virus-unknownscanner": "නොහඳුනන ප්‍රතිවයිරසයක්:",
        "logouttext": "<strong>ඔබ දැන් ගිණුමෙන් නික්මී ඇත.</strong>\n\nඔබගේ බ්‍රවුසරයෙහි පූර්වාපේක්‍ෂී සංචිතය (කෑෂය) පිරිසිදුකරන තෙක්, සමහරක් පිටු විසින් ඔබ තවදුරටත් පිවිසී ඇති බවක් දිගටම පෙන්නුම් කිරීමට ඉඩ ඇත.",
+       "cannotlogoutnow-title": "දැන් නික්මීමට නොහැකිය",
+       "cannotlogoutnow-text": "$1 භාවිතා කරන විට නික්මීමට නොහැකිය.",
        "welcomeuser": "ආයුබෝවන්, $1!",
        "welcomecreation-msg": "ඔබගේ ගිණුම තනා ඇත.\nඔබගේ [[Special:Preferences|{{SITENAME}} අභිරුචීන්]] නෙස් කිරීමට අමතක නොකරන්න.",
        "yourname": "පරිශීලක නාමය:",
        "remembermypassword": "මාගේ පිවිසීම මෙම ගවේෂක මතකයෙහි (උපරිම ලෙස {{PLURAL:$1|දින|දින}}) $1 ක් මතක තබාගන්න",
        "userlogin-remembermypassword": "මා ප්‍රවිසීම් තත්වයේම තබන්න",
        "userlogin-signwithsecure": "ආරක්‍ෂිත සබඳතාව භාවිතා කරන්න",
+       "cannotloginnow-title": "දැන් පිවිසීමට නොහැකිය",
+       "cannotloginnow-text": "$1 භාවිතා කරන විට පිවිසීමට නොහැකිය.",
        "yourdomainname": "ඔබගේ වසම:",
        "password-change-forbidden": "ඔබට මෙම විකියෙහි මුරපද වෙනස් කල නොහැක.",
        "externaldberror": "එක්කෝ සත්‍යාවත් දත්ත-ගබඩා දෝෂයක් පැවතුනි නැතිනම් ඔබගේ බාහිර ගිණුම යාවත්කාලීන කිරීමට ඔබ හට අවසර දී නොමැත.",
        "login": "පිවිසෙන්න",
+       "login-security": "ඔබේ අනන්‍යතාවය තහවුරු කරන්න",
        "nav-login-createaccount": "පිවිසෙන්න / නව ගිණුමක් තනන්න",
        "userlogin": "පිවිසෙන්න / නව ගිණුමක් තනන්න",
        "userloginnocreate": "ප්‍රවිෂ්ට වන්න",
        "userlogout": "නික්මීම",
        "notloggedin": "ප්‍රවිසී නැත",
        "userlogin-noaccount": "ගිණුමක් නොමැතිද?",
-       "userlogin-joinproject": "{{SITENAME}}හා එක්වන්න",
+       "userlogin-joinproject": "{{SITENAME}} හා එක්වන්න",
        "nologin": "ඔබ හට ගිණුමක් නොමැතිද? '''$1'''.",
        "nologinlink": "ගිණුමක් තනන්න",
        "createaccount": "අලුත් ගිණුමක් තනන්න",
        "createacct-reason-ph": "ඔබ තවත් ගිණුමක් තනන්නේ කුමක් නිසාද",
        "createacct-submit": "ඔබේ ගිණුම තනන්න",
        "createacct-another-submit": "ගිණුමක් තනන්න",
+       "createacct-continue-submit": "ගිණුම තැනීම ඉදිරියට කරගෙන යන්න",
+       "createacct-another-continue-submit": "ගිණුම තැනීම ඉදිරියට කරගෙන යන්න",
        "createacct-benefit-heading": "{{SITENAME}} ඔබ වැනි අයෙක් විසින් නිමවා ඇත",
        "createacct-benefit-body1": "{{PLURAL:$1|සංස්කරණය|සංස්කරණ}}",
        "createacct-benefit-body2": "{{PLURAL:$1|පිටුව|පිටු}}",
        "nocookieslogin": "පරිශීලකයන් ප්‍රවිෂ්ට කර ගැනීම සඳහා, {{SITENAME}} විසින් කුකී භාවිතා කරනු ලැබේ.\nඔබ විසින් කුකී අක්‍රීය නොට ඇත.\nකරුණාකර, ඒවා සක්‍රීය කොට, නැවත උත්සාහ ‍කරන්න.",
        "nocookiesfornew": "මූලාශ්‍රය තහවුරු කරගත නොහැකි වුනු බැවින් පරිශීලක ගිණුම නොතැනිනි.\nකුකීස් සක්‍රීය බව තහවුරු කරගෙන, මෙම පිටුව ප්‍රතිපූරණය කර නැවත උත්සාහ කරන්න.",
        "noname": "වලංගු පරිශීලක-නාමයක් සඳහන් කිරීමට ඔබ අසමත් වී ඇත.",
-       "loginsuccesstitle": "පà·\92à·\80à·\92à·\83à·\94ම à·\83à·\8fරà·\8aථà¶\9aයà·\92!",
+       "loginsuccesstitle": "පà·\8aâ\80\8dරà·\80à·\92à·\82à·\8aට à·\80à·\93 à¶\87ත",
        "loginsuccess": "'''දැන් ඔබ , \"$1\" ලෙස, {{SITENAME}} වෙත පිවිස සිටී.'''",
        "nosuchuser": "\"$1\" යන නමැති පරිශීලකයෙකු නොමැත.\nපරිශීලක නාමයන්හි මහාප්‍රාණ ආදිය සැලකේ (case sensitive).\nඔබගේ අක්ෂර-වින්‍යාසය පිරික්සා බැලීම හෝ, [[Special:CreateAccount|නව ගිණුමක් තැනීම]] හෝ සිදුකරන්න.",
        "nosuchusershort": "\"$1\" නමින් පරිශීලකයෙකු නොමැත.\nඅක්‍ෂර-වින්‍යාසය පිරික්සා බලන්න.",
        "newpassword": "නව මුර-පදය:",
        "retypenew": "නව මුර-පදය නැවත ඇතුළු කරන්න:",
        "resetpass_submit": "මුර-පදය පූරණය කොට ඉන් පසු ප්‍රවිෂ්ට වන්න",
-       "changepassword-success": "ඔබගේ මුර-පදය සාර්ථක ලෙස වෙනස් කරන ලදී!",
+       "changepassword-success": "ඔබගේ මුරපදය වෙනස් කරන ලදී!",
        "changepassword-throttled": "ඔබ විසින් මෑතදී  පමණට වඩා වාර ගණනක් පිවිසීමෙහි උත්සාහයන් දරා ඇත.\nයළි උත්සාහ කිරීමට පෙර $1 වේලාවක් රැඳී සිටින්න.",
+       "botpasswords-label-appid": "රොබෝ නාමය:",
+       "botpasswords-label-create": "තනන්න",
+       "botpasswords-label-update": "යාවත්කාලීන කරන්න",
+       "botpasswords-label-cancel": "අවලංගු කරන්න",
+       "botpasswords-label-delete": "මකන්න",
+       "botpasswords-label-resetpassword": "මුරපදය යළි පිහිටුවන්න",
+       "botpasswords-label-grants-column": "අවසර දෙන ලදී",
        "resetpass_forbidden": "මුර-පදයන් වෙනස් කිරීම  සිදු කල නොහැක",
        "resetpass-no-info": "මෙම පිටුව සෘජු ලෙස පරිශීලනය කෙරුමට ඔබ පළමු ප්‍රවිෂ්ට විය යුතුය.",
        "resetpass-submit-loggedin": "මුර-පදය වෙනස්කරන්න",
        "passwordreset-emailtext-user": "{{SITENAME}} හි පරිශීලක $1,{{SITENAME}}($4)සඳහා මුරපදය යලි පිහිටුවීමට ඉල්ලා ඇත.\n\n$2\n\n{{PLURAL:$3|මෙම මුරපදය|මෙම මුරපද}}{{PLURAL:$5|එක් දිනකින්|දවස්$5කින්}}කල් ඉකුත් වනු ඇත.\nඔබ දැන් ඇතුළු වී නව මුරපදයක් තේරිය යුතුය.මෙම ඉල්ලීම වෙන කෙනෙකු විසින් හෝ ඔබට ඔබගේ මුල් මුරපදය මතක නම් හෝ ඔබ තව දුරටත් එය වෙනස් කිරීමට අදහස් නොකරයි නම් හෝ ඔබ මෙම පනිවිඩය නොසලකාහැර ඔබගේ පැරණි මුරපදය භාවිතා කරන්න.",
        "passwordreset-emailelement": "පරිශීලක නාමය: \n$1\n\nතාවකාලික මුරපදය: \n$2",
        "passwordreset-emailsentemail": "මුර-පදය නැවත සකස් කිරීම පිළිබඳව විද්‍යුත් තැපෑලක් යවන ලදී.",
-       "passwordreset-emailsent-capture": "මුර-පදය වෙනස් කිරීම පිළිබඳව විද්‍යුත් තැපෑලක් යවන ලදී, එය පහත දැක්වේ.",
-       "passwordreset-emailerror-capture": "සිහිකැඳවුම් ඊ-තැපෑල ජනිත කරනු ලැබූ අතර, එය පහත දැක්වේ, නමුත් එය {{GENDER:$2|}}පරිශීලකයාට යැවීම අසාර්ථක වුනි: $1",
+       "passwordreset-invalideamil": "වලංගු නැති ඊමේල් ලිපිනය",
        "changeemail": "විද්‍යුත් තැපෑල වෙනස් කරන්න හෝ ඉවත් කරන්න",
        "changeemail-header": "ගිණුම් විද්‍යුත් තැපැල් ලිපිනය වෙනස් කරන්න",
        "changeemail-no-info": "මෙම පිටුව සෘජු ලෙස සම්ප්‍රවේශය කෙරුමට පළමුව ඔබ ප්‍රවිෂ්ටව සිටිය යුතුය.",
        "minoredit": "මෙය සුළු සංස්කරණයකි",
        "watchthis": "මෙම පිටුව මුර කරන්න",
        "savearticle": "පිටුව සුරකින්න",
+       "savechanges": "වෙනස්කම් සුරකින්න",
+       "publishpage": "පිටුව පළ කරන්න",
+       "publishchanges": "වෙනස්කම් පළ කරන්න",
        "preview": "පෙරදසුන",
        "showpreview": "පෙරදසුන පෙන්වන්න",
        "showdiff": "වෙනස්කිරීම් පෙන්වන්න",
        "missingsummary": "'''සිහිගැන්වීමයි:''' ඔබ විසින් සංස්කරණ සාරාංශයක් සපයා නොමැත.\nඔබ නැවතත් සුරැකීම ක්ලික් කලහොත්, ඔබගේ සංස්කරණය එවැන්නක් විරහිතවම සුරැකෙනු ඇත.",
        "selfredirect": "<Strong>අවවාදයයි:</strong> ඔබ තමන් වෙත මෙම පිටුව හරවා යවයි ඇත. \nඔබ යළි-යොමුවීම් සඳහා වැරදි ඉලක්කය නිශ්චිතව දක්වා ඇති විය හැක, හෝ ඔබ වැරදි පිටුව සංස්කරණය කල හැක. \nඔබ ක්ලික් නම් \"{{int:savearticle}}\" නැවතත්, යළි-යොමුවීම් කෙසේ හෝ නිර්මාණය කරනු ඇත.",
        "missingcommenttext": "කරුණාකර පහතින් පරිකථනයක් ඇතුළු කරන්න.",
-       "missingcommentheader": "'''සිහිගැන්වීමයි:''' මෙම පරිකථනය සඳහා ඔබ විසින් විෂයයක්/සිරස්තලයක් සපයා නොමැත.\nඔබ නැවතත් \"{{int:savearticle}}\" ක්ලික් කලහොත්, ඔබගේ සංස්කරණය එවැන්නක් විරහිතවම සුරැකෙනු ඇත.",
+       "missingcommentheader": "<strong>සිහිගැන්වීමයි:</strong>  මෙම පරිකථනය සඳහා ඔබ විසින් විෂයයක්/සිරස්තලයක් සපයා නොමැත.\nඔබ නැවතත් \"{{int:savearticle}}\" ක්ලික් කලහොත්, ඔබගේ සංස්කරණය එවැන්නක් විරහිතවම සුරැකෙනු ඇත.",
        "summary-preview": "සාරාංශ පෙර-දසුන:",
        "subject-preview": "විෂයය හි පෙර දසුන:",
        "previewerrortext": "ඔබේ වෙනස්කම් පෙරදසුන් කිරීමට උත්සාහ දරන අතර දෝෂයක් ඇතිවිය.",
        "undo-nochange": "මෙම සංස්කරණය දැනටමත් අතහැර දමන ලද කර ඇති බව පෙනී යයි.",
        "undo-summary": " [[Special:Contributions/$2|$2]] මගින් සිදුකල  $1 සංශෝධනය අහෝසි කරන්න ([[User talk:$2|සාකච්ඡා]])",
        "undo-summary-username-hidden": "සැඟවුණු පරිශීලකයෙක් විසින් සංශෝධනය $1 අස් කරන්න",
-       "cantcreateaccounttitle": "ගිණුම තැනිය නොහැක",
        "cantcreateaccount-text": "මෙම IP ලිපිනය ('''$1''') මගින් ගිණුම් තැනීම [[User:$3|$3]] විසින් වාරණය කොට ඇත.\n\n$3 විසින් සපයා ඇති හේතුව ''$2'' වේ",
        "cantcreateaccount-range-text": "ඔබේ IP ලිපිනය ('' '$4' '') ද ඇතුළත් වන පරාසය තුළ IP ලිපිනයන් '' '$1', '' සිට ගිණුම් තැනීම  [[User:$3|$3]] විසින් වාරණය කොට ඇත. $3 විසින් දී ඇති හේතුව '' '$2' වේ",
        "viewpagelogs": "මෙම පිටුව පිලිබඳ සටහන් නරඹන්න",
        "special-characters-title-endash": "en තේජස",
        "special-characters-title-emdash": "em තේජස",
        "special-characters-title-minus": "ඍණ ලකුණ",
-       "api-error-blacklisted": "කරුණාකර වෙනස්, විස්තරාත්මක මාතෘකාවක් තෝරන්න.",
        "randomrootpage": "අහඹු මූල පිටුව"
 }
index 39cfc47..3480e4f 100644 (file)
        "yourpasswordagain": "Zopakujte heslo:",
        "createacct-yourpasswordagain": "Potvrdiť heslo",
        "createacct-yourpasswordagain-ph": "Zadajte heslo znova",
-       "remembermypassword": "Pamätať si prihlásenie na tomto počítači (naviac $1 {{PLURAL:$1|deň|dni|dní}})",
        "userlogin-remembermypassword": "Zapamätať si ma",
        "userlogin-signwithsecure": "Použiť zabezpečené pripojenie",
        "yourdomainname": "Vaša doména:",
        "password-change-forbidden": "Na tejto wiki si nemôžete zmeniť heslo.",
        "externaldberror": "Buď nastala chyba externej autentifikačnej databázy alebo vám nie je povolené aktualizovať váš externý účet.",
        "login": "Prihlásiť",
+       "login-security": "Overte svoju identitu",
        "nav-login-createaccount": "Prihlásenie / vytvorenie účtu",
        "userlogin": "Prihlásenie / vytvorenie účtu",
        "userloginnocreate": "Prihlásiť",
        "userlogin-resetpassword-link": "Zabudli ste heslo?",
        "userlogin-helplink2": "Pomoc s prihlásením",
        "userlogin-loggedin": "Ste už {{GENDER:$1|prihĺasený|prihlásená}} ako $1.\nPomocou formulára nižšie sa môžete prihlásiť ako iný redaktor.",
+       "userlogin-reauth": "Aby ste preukázali, že ste $1, musíte sa znovu prihlásiť.",
        "userlogin-createanother": "Vytvoriť ďalší účet",
        "createacct-emailrequired": "E-mailová adresa",
        "createacct-emailoptional": "E-mailová adresa (nepovinné)",
        "createacct-email-ph": "Zadajte vašu e-mailovú adresu",
        "createacct-another-email-ph": "Zadajte vašu e-mailovú adresu",
        "createaccountmail": "Použiť dočasné náhodné heslo a poslať ho na uvedenú e-mailovú adresu",
+       "createaccountmail-help": "Môže byť použité na vytvorenie účtu pre inú osobu bez prezradenia hesla.",
        "createacct-realname": "Skutočné meno (nepovinné)",
        "createaccountreason": "Dôvod:",
        "createacct-reason": "Dôvod",
        "createacct-reason-ph": "Prečo si vytvárate ďalší účet",
+       "createacct-reason-help": "Správa zobrazená v knihe nových používateľov",
        "createacct-submit": "Vytvoriť účet",
        "createacct-another-submit": "Vytvoriť účet",
+       "createacct-continue-submit": "Pokračovať v zakladaní účtu",
+       "createacct-another-continue-submit": "Pokračovať v zakladaní účtu",
        "createacct-benefit-heading": "{{GRAMMAR:akuzatív|{{SITENAME}}}} tvoria ľudia ako vy.",
        "createacct-benefit-body1": "{{PLURAL:$1|úprava|úpravy|úprav}}",
        "createacct-benefit-body2": "{{PLURAL:$1|stránka|stránky|stránok}}",
        "nocookiesnew": "Používateľské konto bolo vytvorené, ale nie ste prihlásený. {{SITENAME}} používa cookies na prihlásenie. Máte cookies vypnuté. Zapnite ich a potom sa prihláste pomocou vášho nového používateľského mena a hesla.",
        "nocookieslogin": "{{SITENAME}} používa cookies na prihlásenie. Vy máte cookies vypnuté. Prosíme, zapnite ich a skúste znovu.",
        "nocookiesfornew": "Používateľský účet nebol vytvorený, pretože sme nemohli potvrdiť jeho zdroj. \nUbezpečte sa, že máte povolené cookies, obnovte túto stránku a skúste to znova.",
+       "createacct-loginerror": "Účet bol úspešne vytvorený, ale neboli ste automaticky prihlásený. Prosím, [[Special:UserLogin|prihláste sa]].",
        "noname": "Nezadali ste platné používateľské meno.",
        "loginsuccesstitle": "Prihlásenie úspešné",
        "loginsuccess": "'''Teraz ste prihlásený do {{GRAMMAR:genitív|{{SITENAME}}}} ako „$1“.'''",
        "createaccount-title": "Vytvorenie účtu na {{GRAMMAR:lokál|{{SITENAME}}}}",
        "createaccount-text": "Niekto vytvoril účet pre vašu emailovú adresu na {{GRAMMAR:lokál|{{SITENAME}}}}\n($4) s názvom „$2“, s heslom „$3“. Mali by ste sa prihlásiť a svoje heslo teraz zmeniť.\n\nAk bol účet vytvorený omylom, túto správu môžete ignorovať.",
        "login-throttled": "Uskutočnili ste príliš mnoho neúspešných pokusov o prihlásenie.\nProsím, počkajte $1 predtým, než to skúsite znova.",
-       "login-abort-generic": "Vaše prihlásenie nebolo úspešné - zrušené",
+       "login-abort-generic": "Vaše prihlásenie bolo neúspešné – zrušené",
        "login-migrated-generic": "Váš účet bol presťahovaný a vaše používateľské meno už viac na tejto wiki neexistuje.",
        "loginlanguagelabel": "Jazyk: $1",
        "suspicious-userlogout": "Vaša požiadavka odhlásiť sa bola zamietnutá, pretože to vyzerá, že ju poslal pokazený prehliadač alebo proxy server.",
        "createacct-another-realname-tip": "Skutočné meno je nepovinné.\nAk sa rozhodnete ho poskytnúť, použije sa na označenie vašej práce.",
        "pt-login": "Prihlásiť sa",
        "pt-login-button": "Prihlásiť sa",
+       "pt-login-continue-button": "Pokračovať v prihlasovaní",
        "pt-createaccount": "Vytvoriť účet",
        "pt-userlogout": "Odhlásiť sa",
        "php-mail-error-unknown": "Neznáma chyba vo funkcii PHP mail()",
        "resetpass_submit": "Nastaviť heslo a prihlásiť sa",
        "changepassword-success": "Vaše heslo bolo úspešne zmenené!",
        "changepassword-throttled": "Uskutočnili ste príliš mnoho neúspešných pokusov o prihlásenie. Prosím, počkajte $1 predtým, než to skúsite znova.",
+       "botpasswords": "Heslá pre botov",
+       "botpasswords-summary": "<em>Heslá pre botov</em> umožňujú pristupovať k redaktorskému účtu prostredníctvom API bez použitia hlavných prihlasovacích údajov účtu. Redaktorské oprávnenia dostupné po prihlásení pomocou hesla pre botov môžu byť obmedzené.\n\nAk neviete, k čomu by ste to mohli použiť, pravdepodobne by ste to použiť nemali. Nikto by vám nikdy nemal žiadať, aby ste si tu vygenerovali heslo a dali mu ho.",
+       "botpasswords-disabled": "Heslá pre botov sú zakázané.",
+       "botpasswords-no-central-id": "Aby ste mohli použiť heslá pre botov musíte byť prihlásený k centrálnemu účtu.",
+       "botpasswords-existing": "Jestvujúce heslá pre botov",
+       "botpasswords-createnew": "Vytvoriť nové heslo pre botov",
+       "botpasswords-label-appid": "Názov bota:",
+       "botpasswords-label-create": "Vytvoriť",
+       "botpasswords-label-update": "Aktualizovať",
+       "botpasswords-label-cancel": "Zrušiť",
+       "botpasswords-label-delete": "Vymazať",
+       "botpasswords-label-resetpassword": "Obnoviť heslo",
        "resetpass_forbidden": "Heslá nie je možné zmeniť",
        "resetpass-no-info": "Aby ste mohli priamo pristupovať k tejto stránke, musíte sa prihlásiť.",
        "resetpass-submit-loggedin": "Zmeniť heslo",
        "resetpass-submit-cancel": "Zrušiť",
-       "resetpass-wrong-oldpass": "Neplatné dočasné alebo aktuálne heslo.\nJe možné, že sa vám už podarilo úspešne zmeniť svoje heslo alebo ste si vyžiadali nové dočasné heslo.",
+       "resetpass-wrong-oldpass": "Neplatné, dočasné alebo aktuálne heslo.\nJe možné, že sa vám už podarilo úspešne zmeniť svoje heslo alebo ste si vyžiadali nové dočasné heslo.",
        "resetpass-recycled": "Ako nové heslo si prosím nastavte niečo iné než súčasné heslo.",
        "resetpass-temp-emailed": "Prihlasujete sa dočasným heslom, zaslaným e-mailom. Aby ste dokončili prihlásenie, nastavte si tu nové heslo:",
        "resetpass-temp-password": "Dočasné heslo:",
        "passwordreset-emailtext-ip": "Niekto (pravdepodobne vy z IP adresy $1) požiadal o obnovenie vášho hesla na {{GRAMMAR:genitív|{{SITENAME}}}} ($4). {{PLURAL:$3|Nasledujúci používateľský účet je spojený|Nasledujúce používateľské účty sú spojené}}\ns touto emailovou adresou:\n\n$2\n\n{{PLURAL:$3|Platnosť tohto dočasného hesla vyprší|Platnosť týchto dočasných hesiel vyprší}} o {{PLURAL:$5|jeden deň|$5 dni|$5 dní}}.\nMali by ste sa prihlásiť teraz a zvoliť nové heslo. Ak túto žiadosť podal niekto iný alebo\nak ste si spomenuli svoje pôvodné heslo a už ho chcete zmeniť, môžete túto správu\nignorovať a ďalej používať vaše staré heslo.",
        "passwordreset-emailtext-user": "Používateľ $1 na {{GRAMMAR:genitív|{{SITENAME}}}} požiadal o obnovenie vášho hesla na na {{GRAMMAR:genitív|{{SITENAME}}}} ($4). {{PLURAL:$3|Nasledujúci používateľský účet je spojený|Nasledujúce používateľské účty sú spojené}}\ns touto emailovou adresou:\n\n$2\n\n{{PLURAL:$3|Platnosť tohto dočasného hesla vyprší|Platnosť týchto dočasných hesiel vyprší}} o {{PLURAL:$5|jeden deň|$5 dni|$5 dní}}.\nMali by ste sa prihlásiť teraz a zvoliť nové heslo. Ak túto žiadosť podal niekto iný alebo\nak ste si spomenuli svoje pôvodné heslo a už ho chcete zmeniť, môžete túto správu\nignorovať a ďalej používať vaše staré heslo.",
        "passwordreset-emailelement": "Používateľské meno: \n$1\n\nDočasné heslo:\n$2",
-       "passwordreset-emailsentemail": "Pokiaľ je toto e-mailová adresa, zaregistrovaná k vášmu účtu, bude na ňu zaslaný e-mail pre získanie nového hesla.",
+       "passwordreset-emailsentemail": "Pokiaľ je toto e-mailová adresa zaregistrovaná k vášmu účtu, bude na ňu zaslaný e-mail pre získanie nového hesla.",
        "passwordreset-emailsentusername": "Pokiaľ je príslušná mailová adresa zaregistrovaná, bude na ňu zaslaný e-mail s novým heslom.",
-       "passwordreset-emailsent-capture": "Bol odoslaný email s novým heslom, ktorý je zobrazený nižšie.",
-       "passwordreset-emailerror-capture": "Bol odoslaný email s novým heslom, ktorý je zobrazený nižšie, ale nepodarilo sa ho odoslať {{GENDER:$2|používateľovi}}: $1",
        "changeemail": "Zmeniť alebo odstrániť e-mailovú adresu",
        "changeemail-header": "Zmena e-mailovej adresy pre účet",
-       "changeemail-passwordrequired": "Pre potvrdenie tejto zmeny budete musieť zadať svoje heslo.",
        "changeemail-no-info": "Na prístup k tejto stránke musíte byť prihlásený.",
        "changeemail-oldemail": "Súčasná e-mailová adresa:",
        "changeemail-newemail": "Nová e-mailová adresa:",
        "sig_tip": "Váš podpis s dátumom a časom",
        "hr_tip": "Vodorovná čiara (radšej ju nepoužívajte)",
        "summary": "Zhrnutie úprav:",
-       "subject": "Téma/nadpis:",
+       "subject": "Predmet:",
        "minoredit": "Toto je drobná úprava",
        "watchthis": "Sledovať úpravy tejto stránky",
        "savearticle": "Uložiť stránku",
+       "savechanges": "Uložiť zmeny",
+       "publishpage": "Publikovať stránku",
+       "publishchanges": "Publikovať zmeny",
        "preview": "Náhľad",
        "showpreview": "Zobraziť náhľad",
        "showdiff": "Zobraziť rozdiely",
        "undo-nochange": "Zdá sa, že úprava už bola zrušená.",
        "undo-summary": "Revízia $1 používateľa [[Special:Contributions/$2|$2]] ([[User talk:$2|diskusia]]) bola vrátená",
        "undo-summary-username-hidden": "Vrátiť revíziu $1, ktorú vykonal skrytý používateľ",
-       "cantcreateaccounttitle": "Nie je možné vytvoriť účet",
        "cantcreateaccount-text": "Zakladanie nových účtov z tejto IP adresy ('''$1''') bolo zablokované {{GENDER:$3|používateľom|používateľkou}} [[User:$3|$3]].\n\nDôvod, ktorý $3 {{GENDER:$3|uviedol|uviedla}}, je: ''$2''",
        "cantcreateaccount-range-text": "Zakladanie nových účtov z IP adries v rozsahu <strong>$1</strong>, ktorý zahŕňa aj vašu IP adresu (<strong>$4</strong>), bolo zablokované {{GENDER:$3|používateľom|používateľkou}} [[User:$3|$3]].\n\nDôvod, ktorý $3 {{GENDER:$3|uviedol|uviedla}}, je: <em>$2</em>",
        "viewpagelogs": "Zobraziť záznamy pre túto stránku",
        "mw-widgets-dateinput-placeholder-month": "RRRR-MM",
        "mw-widgets-titleinput-description-new-page": "stránka zatiaľ neexistuje",
        "mw-widgets-titleinput-description-redirect": "presmerovanie na $1",
-       "api-error-blacklisted": "Prosím, zvoľte iný, opisný názov.",
        "randomrootpage": "Náhodná koreňová stránka"
 }
index 84af377..88c6886 100644 (file)
        "tagline": "Iz {{GRAMMAR:rodilnik|{{SITENAME}}}}",
        "help": "Pomoč",
        "search": "Iskanje",
+       "search-ignored-headings": " #<!-- te vrstice ne spreminjajte --> <pre>\n# Poglavja, ki bodo prezrta pri iskanju.\n# Spremembe bodo začele veljati takoj, ko bo stran s poglavjem indeksirana.\n# Reindeksacijo strani lahko vsilite z ničelnim urejanjem.\n# Skladnja je sledeča:\n#   * Vse od znaka »#« do konca vrstice je pripomba.\n#   * Vsaka neprazna vrstica je natančen naslov, ki ga bomo prezrli, upoštevajoč velikost črk in ostalo.\nOpombe\nOpombe in sklici\nViri\nZunanje povezave\nGlej tudi\n #</pre> <!-- te vrstice ne spreminjajte -->",
        "searchbutton": "Iskanje",
        "go": "Pojdi na",
        "searcharticle": "Pojdi na",
        "yourpasswordagain": "Ponovno vpišite geslo",
        "createacct-yourpasswordagain": "Potrdite geslo",
        "createacct-yourpasswordagain-ph": "Ponovno vnesite geslo",
-       "remembermypassword": "Zapomni si me na tem računalniku (za največ $1 {{PLURAL:$1|dan|dneva|dni}})",
        "userlogin-remembermypassword": "Zapomni si me",
        "userlogin-signwithsecure": "Uporabi varno povezavo",
+       "cannotlogin-title": "Ne moremo vas prijaviti",
+       "cannotlogin-text": "Prijava ni mogoča.",
        "cannotloginnow-title": "Trenutno se ne morete prijaviti",
        "cannotloginnow-text": "Prijava ni možna pri uporabi $1.",
+       "cannotcreateaccount-title": "Ne moremo ustvariti računov",
+       "cannotcreateaccount-text": "Neposredno ustvarjanje računov na tem wikiju ni omogočeno.",
        "yourdomainname": "Domena",
        "password-change-forbidden": "Na tem wikiju ne morete spreminjati gesel.",
        "externaldberror": "Pri potrjevanju istovetnosti je prišlo do notranje napake ali pa za osveževanje zunanjega računa nimate dovoljenja.",
        "passwordreset-emailelement": "Uporabniško ime: \n$1\n\nZačasno geslo: \n$2",
        "passwordreset-emailsentemail": "Če je e-poštni naslov povezan z vašim računom, vam bomo poslali e-pošto za postavitev gesla.",
        "passwordreset-emailsentusername": "Če obstaja e-poštni naslov, povezan s tem uporabniškim imenom, vam bomo poslali e-pošto za postavitev gesla.",
-       "passwordreset-emailsent-capture": "Poslali smo e-pošto za ponastavitev gesla, ki je prikazana spodaj.",
-       "passwordreset-emailerror-capture": "Ustvarili smo e-pošto za ponastavitev gesla, ki je prikazana spodaj, vendar pa pošiljanje {{GENDER:$2|uporabniku|uporabnici}} ni uspelo: $1",
        "passwordreset-emailsent-capture2": "Poslali smo {{PLURAL:$1|e-pošto|e-pošti|e-pošte}} za ponastavitev gesla. {{PLURAL:$1|Uporabniško ime in geslo sta navedena spodaj.|Seznam uporabniških imen in gesel je naveden spodaj.}}",
        "passwordreset-emailerror-capture2": "Pošiljanje e-pošte {{GENDER:$2|uporabniku|uporabnici}} je spodletelo: $1 {{PLURAL:$3|Uporabniško ime in geslo sta navedena spodaj.|Seznam uporabniških imen in gesel je naveden spodaj.}}",
        "passwordreset-nocaller": "Podati morate klicatelja",
        "passwordreset-nodata": "Navedli niste ne uporabniškega imena ne e-poštnega naslova",
        "changeemail": "Sprememba ali odstranitev e-poštnega naslova",
        "changeemail-header": "Izpolnite obrazec za spremembo vašega e-poštnega naslova. Če želite s svojega računa odstraniti povezavo s katerim koli e-poštnim naslovom, pustite polje za nov e-poštni naslov med potrjevanje obrazca prazno.",
-       "changeemail-passwordrequired": "Za potrditev spremembe boste morali vnesti svoje geslo.",
        "changeemail-no-info": "Za neposredni dostop do strani morate biti prijavljeni.",
        "changeemail-oldemail": "Trenutni e-poštni naslov:",
        "changeemail-newemail": "Novi e-poštni naslov:",
        "content-model-css": "CSS",
        "content-json-empty-object": "Prazen objekt",
        "content-json-empty-array": "Prazno polje",
+       "deprecated-self-close-category": "Strani, ki uporabljajo neveljavne samozaključljive oznake HTML",
+       "deprecated-self-close-category-desc": "Stran uporablja neveljavne samozaključljive oznake HTML, kot sta <code>&lt;b/></code> ali <code>&lt;span/></code>. Njihovo vedenje se bo kmalu spremenilo, da bo v skladu s specifikacijo HTML5, zato njihova uporaba v wikibesedilu ni zaželena.",
        "duplicate-args-warning": "<strong>Opozorilo:</strong> [[:$1]] kliče [[:$2]] z več kot eno vrednostjo za parameter »$3«. Uporabili bomo samo zadnjo navedeno vrednost.",
        "duplicate-args-category": "Strani s podvojenimi argumenti v klicih predlog",
        "duplicate-args-category-desc": "Stran vsebuje klice predlog, ki vsebujejo dvojnike argumentov, kot sta <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> ali <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Zdi se, da je urejanje nekdo že razveljavil.",
        "undo-summary": "Redakcija $1 uporabnika [[Special:Contributions/$2|$2]] ([[User talk:$2|pogovor]]) razveljavljena",
        "undo-summary-username-hidden": "Razveljavi redakcijo $1 skritega uporabnika",
-       "cantcreateaccounttitle": "Računa ni moč ustvariti",
        "cantcreateaccount-text": "Registracije z IP-naslova ('''$1''') je administrator(ka) [[User:$3|$3]] blokiral(a).\n\nRazlog, ki ga je $3 podal(a), je ''$2''.",
        "cantcreateaccount-range-text": "Ustvarjanje računov z IP-naslovov v območju <strong>$1</strong>, ki vključuje vaš IP-naslov (<strong>$4</strong>), je blokiral(-a) [[User:$3|$3]].\n\nRazlog, ki ga je podal(-a) $3, je <em>$2</em>.",
        "viewpagelogs": "Poglej dnevniške zapise o strani",
        "grant-group-high-volume": "Izvajanje visokoobsežnih dejavnosti",
        "grant-group-customization": "Prilagoditve in nastavitve",
        "grant-group-administration": "Izvajanje administrativnih dejanj",
+       "grant-group-private-information": "Dostop do zasebnih podatkov o vas",
        "grant-group-other": "Druga dejavnost",
        "grant-blockusers": "Blokiranje in odblokiranje uporabnikov",
        "grant-createaccount": "Ustvarjanje računov",
        "grant-highvolume": "Visokoobsežno urejanje",
        "grant-oversight": "Skrivanje uporabnikov in zatiranje redakcij",
        "grant-patrol": "Nadzor sprememb strani",
+       "grant-privateinfo": "Dostop do zasebnih podatkov",
        "grant-protect": "Zaščita in odstranitev zaščite strani",
        "grant-rollback": "Razveljavitev sprememb strani",
        "grant-sendemail": "Pošiljanje e-pošte drugim uporabnikom",
        "action-applychangetags": "uveljavitev oznak skupaj z vašimi spremembami",
        "action-changetags": "dodajanje in odstranjevanje poljubnih oznak na posameznih redakcijah in dnevniških vnosih",
        "action-deletechangetags": "izbris oznak iz zbirke podatkov",
+       "action-purge": "počiščenje strani",
        "nchanges": "$1 {{PLURAL:$1|sprememba|spremembi|spremembe|sprememb|sprememb}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|od zadnjega obiska}}",
        "enhancedrc-history": "zgodovina",
        "file-thumbnail-no": "Ime datoteke se začne z <strong>$1</strong>.\nIzgleda, da je to pomanjšana slika ''(thumbnail)''.\nČe imate sliko polne resolucije, jo naložite, drugače spremenite ime datoteke.",
        "fileexists-forbidden": "Datoteka s tem imenom že obstaja in je ni mogoče prepisati.\nČe še vedno želite naložiti vašo datoteko, se prosimo vrnite nazaj in uporabite novo ime.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Datoteka s tem imenom že obstaja v skupnem skladišču datotek.\nProsimo, vrnite se in naložite svojo datoteko pod drugim imenom.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Naložena datoteka je točen dvojnik trenutne različice <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Naložena datoteka je točen dvojnik {{PLURAL:$2|starejše različice|starejših različic}} <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Ta datoteka je dvojnik {{PLURAL:$1|naslednje datoteke|naslednjih datotek}}:",
        "file-deleted-duplicate": "Datoteka je identična tej ([[:$1]]), ki je bila predhodno izbrisana.\nPreverite zgodovino brisanja datoteke, preden jo ponovno naložite.",
        "file-deleted-duplicate-notitle": "Datoteka, identična tej datoteki, je bila v preteklosti izbrisana in naslov je bil zatrt.\nPoprosite koga, ki ima možnost ogleda podatkov zatrtih datotek, da preveri položaj, preden nadaljujete s ponovnim nalaganjem.",
        "uploadstash-errclear": "Čiščenje datotek je spodletelo.",
        "uploadstash-refresh": "Osveži seznam datotek",
        "uploadstash-thumbnail": "ogled sličice",
+       "uploadstash-exception": "Nalaganja nismo uspeli shraniti v zalogo ($1): »$2«.",
        "invalid-chunk-offset": "Neveljaven odmik delčka",
        "img-auth-accessdenied": "Dostop zavrnjen",
        "img-auth-nopathinfo": "Manjka PATH_INFO.\nVaš strežnik ne poda te informacije.\nMorda temelji na CGI in ne more podpirati img_auth.\nOglejte si  https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
        "filerevert-submit": "Vrni",
        "filerevert-success": "Datoteka '''[[Media:$1|$1]]''' je bila vrnjena na [$4 različico $3, $2].",
        "filerevert-badversion": "Ne najdem preteklih lokalnih verzij datoteke s podanim časovnim žigom.",
+       "filerevert-identical": "Trenutna različica datoteke je že enaka izbrani.",
        "filedelete": "Izbriši $1",
        "filedelete-legend": "Brisanje datoteke",
        "filedelete-intro": "Brišete datoteko '''[[Media:$1|$1]]''' skupaj z njeno celotno zgodovino.",
        "watchnologin": "Niste prijavljeni",
        "addwatch": "Dodaj na spisek nadzorov",
        "addedwatchtext": "»[[:$1]]« in pripadajočo pogovorno stran smo dodali na vaš [[Special:Watchlist|spisek nadzorov]].",
+       "addedwatchtext-talk": "»[[:$1]]« in pripadajočo stran smo dodali na vaš [[Special:Watchlist|spisek nadzorov]].",
        "addedwatchtext-short": "Stran »$1« smo dodali na vaš spisek nadzorov.",
        "removewatch": "Odstrani s spiska nadzorov",
        "removedwatchtext": "»[[:$1]]« in pripadajočo pogovorno stran smo odstranili z vašega [[Special:Watchlist|spiska nadzorov]].",
+       "removedwatchtext-talk": "»[[:$1]]« in pripadajočo stran smo odstranili z vašega [[Special:Watchlist|spiska nadzorov]].",
        "removedwatchtext-short": "Stran »$1« smo odstranili z vašega spiska nadzorov.",
        "watch": "Opazuj",
        "watchthispage": "Opazuj stran",
        "rollbacklinkcount-morethan": "vrni več kot $1 {{PLURAL:$1|urejanje|urejanji|urejanja|urejanj}}",
        "rollbackfailed": "Vrnitev ni uspela",
        "rollback-missingparam": "Pri zahtevi manjkajo zahtevani parametri.",
+       "rollback-missingrevision": "Ne moremo naložiti podatkov o redakciji.",
        "cantrollback": "Urejanja ne morem vrniti; zadnji urejevalec je hkrati edini.",
        "alreadyrolled": "Zadnje spremembe [[:$1]] uporabnika [[User:$2|$2]] ([[User talk:$2|pogovor]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) ne morem vrniti;\nstran je spremenil ali vrnil že nekdo drug.\n\nZadnji je stran urejal uporabnik [[User:$3|$3]] ([[User talk:$3|pogovor]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Povzetek urejanja je bil: <em>$1</em>.",
        "undeletehistorynoadmin": "Stran je bila izbrisana.\nRazlog za izbris je skupaj s podrobnostmi o uporabnikih, ki so jo urejali pred izbrisom, naveden v prikazanem povzetku.\nDejansko besedilo izbrisanih redakcij je dostopno le administratorjem.",
        "undelete-revision": "Izbrisana redakcija $1 (dne $4 ob $5) uporabnika $3:",
        "undeleterevision-missing": "Napačna ali manjkajoča redakcija.\nMorda imate napačno povezavo ali pa je bila redakcija obnovljena ali odstranjena iz arhiva.",
+       "undeleterevision-duplicate-revid": "$1 {{PLURAL:$1|redakcije|redakcij}} nismo mogli obnoviti, saj je bil {{PLURAL:$1|1=njen|njihov}} <code>rev_id</code> že v uporabi.",
        "undelete-nodiff": "Predhodnih različic ne najdem.",
        "undeletebtn": "Obnovi",
        "undeletelink": "poglej/obnovi",
        "undeletedrevisions": "{{PLURAL:$1|obnovljena $1 redakcija|obnovljeni $1 redakciji|obnovljene $1 redakcije|obnovljenih $1 redakcij}}",
        "undeletedrevisions-files": "$1 {{PLURAL:$1|redakcija|redakciji|redakcije|redakcij}} in $2 {{PLURAL:$2|datoteka|datoteki|datoteke|datotek}} {{PLURAL:$1+$2|obnovljena|obnovljeni|obnovljene|obnovljenih}}",
        "undeletedfiles": "{{PLURAL:$1|obnovljena je $1 datoteka|obnovljeni sta $1 datoteki|obnovljene so $1 datoteke|obnovljenih je $1 datotek}}",
-       "cannotundelete": "Obnova je spodletela:\n$1",
+       "cannotundelete": "Nekatere ali vse obnove so spodletele:\n$1",
        "undeletedpage": "'''Obnovili ste stran $1.'''\n\nNedavna brisanja in obnove so zapisani v [[Special:Log/delete|dnevniku brisanja]].",
        "undelete-header": "Glej [[Special:Log/delete|dnevnik brisanja]] za nedavno izbrisane strani.",
        "undelete-search-title": "Iskanje izbrisanih strani",
        "sp-contributions-newbies-sub": "Prispevki novincev",
        "sp-contributions-newbies-title": "Uporabniški prispevki novih računov",
        "sp-contributions-blocklog": "dnevnik blokiranja",
-       "sp-contributions-suppresslog": "zatrti uporabnikovi prispevki",
-       "sp-contributions-deleted": "izbrisani uporabnikovi prispevki",
+       "sp-contributions-suppresslog": "zatrti {{GENDER:$1|uporabnikovi|uporabničini}} prispevki",
+       "sp-contributions-deleted": "izbrisani zatrti {{GENDER:$1|uporabnikovi|uporabničini}} prispevki",
        "sp-contributions-uploads": "naložene datoteke",
        "sp-contributions-logs": "dnevniki",
        "sp-contributions-talk": "pogovor",
        "pageinfo-article-id": "ID strani",
        "pageinfo-language": "Jezik vsebine strani",
        "pageinfo-content-model": "Model vsebine strani",
+       "pageinfo-content-model-change": "spremeni",
        "pageinfo-robot-policy": "Robotsko indeksiranje",
        "pageinfo-robot-index": "Dovoljeno",
        "pageinfo-robot-noindex": "Nedovoljeno",
        "mw-widgets-dateinput-placeholder-month": "LLLL-MM",
        "mw-widgets-titleinput-description-new-page": "stran še ne obstaja",
        "mw-widgets-titleinput-description-redirect": "preusmeritev na $1",
-       "api-error-blacklisted": "Prosimo, izberite drugačen, opisen naslov.",
        "sessionmanager-tie": "Ne morem združiti več vrst overitvenih zahtev: $1.",
        "sessionprovider-generic": "sej $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "sej, ki temeljijo na piškotkih",
        "log-action-filter-newusers": "Vrsta ustvarjanja računa:",
        "log-action-filter-patrol": "Vrsta nadzora:",
        "log-action-filter-protect": "Vrsta zaščite:",
-       "log-action-filter-rights": "Vrsta spremembe pravic",
-       "log-action-filter-suppress": "Vrsta zatrtja",
+       "log-action-filter-rights": "Vrsta spremembe pravic:",
+       "log-action-filter-suppress": "Vrsta zatrtja:",
        "log-action-filter-upload": "Vrsta nalaganja:",
        "log-action-filter-all": "Vse",
        "log-action-filter-block-block": "Blokada",
        "linkaccounts-submit": "Poveži račune",
        "unlinkaccounts": "Razveži račune",
        "unlinkaccounts-success": "Račun smo razvezali.",
-       "authenticationdatachange-ignored": "Sprememba overitvenih podatkov ni bila obdelana. Morda ni bil konfiguriran noben ponudnik?"
+       "authenticationdatachange-ignored": "Sprememba overitvenih podatkov ni bila obdelana. Morda ni bil konfiguriran noben ponudnik?",
+       "userjsispublic": "Pomnite: Podstrani JavaScript naj ne vsebujejo zaupnih podatkov, saj so vidne tudi drugim uporabnikom.",
+       "usercssispublic": "Pomnite: Podstrani CSS naj ne vsebujejo zaupnih podatkov, saj so vidne tudi drugim uporabnikom."
 }
index 6295f84..fce5916 100644 (file)
        "tagline": "Ka {{SITENAME}}",
        "help": "Caawinaad",
        "search": "Raadi",
+       "search-ignored-headings": " #<!-- Udaa sadarkaan sida uu yahay --> <pre>\n# Waxaa dhacaysa in la iska iloowo madaxqorkiisa inta lagu gudajiro raadinta.\n# Isbedeleda waxay aadi doonaan halkii loogu talo galay ee hordhaca bogga uu ku jiro madaxqorkiisa.\n# Waxaa kuu suurtagal ah inaad howl hordhac ah usameeyso bogga adigoo bedela bogga banaan.\n# Weeraynta waa sidaan soo socota:\n#   * wax kasta oo gadaasheeda lagu qoro \"#\" sadarka dhammaadkiis waxaa loo qaadanaa faallo\n#   * Sadarkasta ee banaan wuxuu qaadanayaa ciwaankii laga tagay, isagii iyo wax kabedelan\nReferences\nLinkiyada dibadda\nSidoo kale eeg\n #</pre> <!-- leave this line exactly as it is -->",
        "searchbutton": "Raadi",
        "go": "Soco",
        "searcharticle": "Soco",
        "passwordreset-emailtext-ip": "(Qof laga yaabo in ee tahay adi, oo ka socdo ciwaanka IP'ka $1)  ayaa  codsaday xasuus faah faahin akoonkaada {{SITENAME}}\n($4). Isticmaaladaan soo socota {{PLURAL:$3|akoonkooda waxee|akoonkooda waxee}} la xiriiraan ciwaankaan e-mailka ah:\n\n$2\n\n{{PLURAL:$3|eraysirkaan kumeel gaarka ah|Eraysiryadaan kumeel gaarka ah}} waxay dhacaayaan {{PLURAL:$5|hal maalin|$5 maalmood}}.\nWaa in aad gudaha gashaa oo e-mail cusub doorataa hadda. Hadii ee qof kale codsigaan sameysay, ama hadii aad soo xasuusatay eraysirkaagii hore, oo aadna u baahneen in aad badashid, iska ilaaw fariintaan siina isticmaal eraysirkaagii duqi ahaa.",
        "passwordreset-emailtext-user": "Isticmaale $1 ee {{SITENAME}} wuxuu codsaday xasuus faah faahin akoonkaada {{SITENAME}}\n($4). Isticmaaladaan soo socota {{PLURAL:$3|akoonkooda waxee|akoonkooda waxee}} la xiriiraan ciwaankaan e-mailka ah:\n\n$2\n\n{{PLURAL:$3|eraysirkaan kumeel gaarka ah|Eraysiryadaan kumeel gaarka ah}} waxay dhacaayaan {{PLURAL:$5|hal maalin|$5 maalmood}}.\nWaa in aad gudaha gashaa oo e-mail cusub doorataa hadda. Hadii ee qof kale codsigaan sameysay, ama hadii aad soo xasuusatay eraysirkaagii hore, oo aadna u baahneen in aad badashid, iska ilaaw fariintaan siina isticmaal eraysirkaagii duqi ahaa.",
        "passwordreset-emailelement": "Magaca gudagalka: \n$1\n\nEreysirka kumeelgaarka ah: \n$2",
-       "passwordreset-emailerror-capture": "E-mail xasuus ah ayaa la sameeyay, oo ka arki kartid hoosta,laakiin wuxuu ku guul dareestay in isticmaalaha loo diro: $1",
        "changeemail": "Bedel ciwaanka E-mailka",
        "changeemail-header": "Bedel ciwaanka e-mailka akoonka",
        "changeemail-oldemail": "Ciwaanka e-mailka hadda jiro:",
        "post-expand-template-inclusion-category": "Boggaga ku xad gudbay weyninka tusmo'da",
        "post-expand-template-argument-warning": "'''Digniin:'''Boggaan waxaa ku jira ugu yaraan hal iyo wixii ka badan oo template ah, waxaana ku xiran kuwa kale.",
        "undo-summary": "Noqay bedelaadka $1 ee sameeyay  [[Special:Contributions/$2|$2]] ([[User talk:$2|hadal]])",
-       "cantcreateaccounttitle": "Ma' sameynkaro gudagal (akoon)",
        "cantcreateaccount-text": "Akoon'ka sameeyaha leh ciwaankaan  IP:ka ('''$1''') waxaa mamnuucay [[User:$3|$3]].\n\nSababta uu qoray $3 waa ''$2''",
        "viewpagelogs": "Fiiri guadagalayaasha boggaan",
        "nohistory": "Boggaan malahan taariikhda bedelaadka",
index a49e46a..c82f7ff 100644 (file)
        },
        "tog-underline": "Nënvizimi i lidhjes:",
        "tog-hideminor": "Fshih redaktimet e vogla nga ndryshimet e fundit",
-       "tog-hidepatrolled": "Fshih redaktimet e paturlluara nga ndryshimet e fundit",
-       "tog-newpageshidepatrolled": "Fshih faqet e patrulluara nga lista e faqeve të reja",
+       "tog-hidepatrolled": "Fshih redaktimet e kontrolluara nga ndryshimet e fundit",
+       "tog-newpageshidepatrolled": "Fshih faqet e kontrolluara nga lista e faqeve të reja",
        "tog-hidecategorization": "Fshih kategorizimin e faqeve",
-       "tog-extendwatchlist": "Zgjero listën e faqeve të vëzhguara që t'i tregojë të gjitha ndryshimet, jo vetëm më të fundit.",
-       "tog-usenewrc": "Grupo ndryshimet sipas faqeve në ndryshime së fundmi dhe listën mbikqyrëse (kërkon JavaScript)",
-       "tog-numberheadings": "Numëro automatikish titujt",
+       "tog-extendwatchlist": "Zgjero listën e të gjitha faqeve të vëzhguara në mënyrë që t'i tregojë të gjitha ndryshimet, jo vetëm më të fundit.",
+       "tog-usenewrc": "Grupo ndryshimet sipas faqes në ndryshimet më të fundit dhe listën e vëzhgimit (kërkon JavaScript)",
+       "tog-numberheadings": "Numërim automatik i titujve",
        "tog-showtoolbar": "Shfaq shiritin e veglave të redaktorit",
        "tog-editondblclick": "Redakto faqe në klikim të dyfishtë",
        "tog-editsectiononrightclick": "Aktivizo redaktimin e seksioneve duke klikuar me të djathtën mbi titullin e seksionit",
        "tog-watchdeletion": "Shto faqet dhe skedat e grisura prej meje tek lista e faqeve  nën mbikqyrje",
        "tog-watchrollback": "Shto faqet ku unë kam kryer një rikthim tek lista ime mbikqyrëse",
        "tog-minordefault": "Shëno të gjitha redaktimet si të vogla automatikisht",
-       "tog-previewontop": "Vendose kutinë e bocetit sipër kutisë së redaktimeve",
-       "tog-previewonfirst": "Tregoje bocetin në redaktimin e parë",
-       "tog-enotifwatchlistpages": "Më njofto me e-mail kur ndryshohet një faqe apo skedarë nga lista ime e faqeve nën mbikqyrje",
-       "tog-enotifusertalkpages": "Kur faqja ime e diskutimeve e përdoruesit ndryshohet, më dërgo email",
-       "tog-enotifminoredits": "Më njofto me e-mail edhe kur ka redaktime të vogla në faqe dhe skedave",
-       "tog-enotifrevealaddr": "Tregoje adresën time të e-mail-it në e-mail-et njoftuese",
+       "tog-previewontop": "Trego se si do të duket faqja mbi kutinë redaktimit",
+       "tog-previewonfirst": "Trego se si do të duket faqja posa ta filloj redaktimin",
+       "tog-enotifwatchlistpages": "Më njofto me email kur ndryshohet një faqe apo skedë nga lista ime e faqeve nën vëzhgim",
+       "tog-enotifusertalkpages": "Më dërgo email kur ndryshohet faqja ime e diskutimeve",
+       "tog-enotifminoredits": "Më njofto me email edhe kur ka redaktime të vogla të faqeve dhe skedave",
+       "tog-enotifrevealaddr": "Trego adresën time të emailit në emailet njoftuese",
        "tog-shownumberswatching": "Trego numrin e përdoruesve që vëzhgojnë këtë faqe",
        "tog-oldsig": "Nënshkrimi ekzistues:",
        "tog-fancysig": "Mbaje nënshkrimin si wikitekst (pa lidhje automatike)",
-       "tog-uselivepreview": "Trego pamjen në mënyrë të drejtëpërdrejtë",
-       "tog-forceeditsummary": "Më njofto kur e lë përmbledhjen e redaktimit bosh",
+       "tog-uselivepreview": "Trego parapamjen drejtpërdrejt",
+       "tog-forceeditsummary": "Më njofto kur përmbledhjen e redaktimit e lë bosh",
        "tog-watchlisthideown": "Fshih redaktimet e mia nga lista e faqeve të vëzhguara",
        "tog-watchlisthidebots": "Fshih redaktimet e robotëve nga lista e faqeve të vëzhguara",
        "tog-watchlisthideminor": "Fshih redaktimet e vogla nga lista e faqeve të vëzhguara",
@@ -72,7 +72,7 @@
        "tog-ccmeonemails": "Më dërgo kopje të mesazheve që u dërgoj të tjerëve",
        "tog-diffonly": "Mos trego përmbajtjen e faqes nën diff-e",
        "tog-showhiddencats": "Trego kategoritë e fshehura",
-       "tog-norollbackdiff": "Ndryshimi pas rikthimit do të fshihet",
+       "tog-norollbackdiff": "Mos trego ndrysh pas kryerjes së një rikthkimi",
        "tog-useeditwarning": "Më paralajmëro kur unë lë një redaktim faqeje me ndryshime të paruajtura",
        "tog-prefershttps": "Gjithmonë përdorni një lidhje të sigurt kur të kyçur",
        "underline-always": "Gjithmonë",
@@ -80,7 +80,7 @@
        "underline-default": "Parapërcaktuar nga shfletuesi",
        "editfont-style": "Zgjidh stilin e gërmave të hapsirës:",
        "editfont-default": "Sipas rregullit në shfletues",
-       "editfont-monospace": "Gërma monospace",
+       "editfont-monospace": "Germa me hapësirë të barabartë",
        "editfont-sansserif": "Germa Sans-serif",
        "editfont-serif": "Gërma serif",
        "sunday": "E diel",
        "listingcontinuesabbrev": "vazh.",
        "index-category": "Faqe të indeksuara",
        "noindex-category": "Faqe jo të indeksuara",
-       "broken-file-category": "Faqet me lidhjet file thyer",
-       "about": "Rreth",
+       "broken-file-category": "Faqet me lidhje të parregullta",
+       "about": "Për",
        "article": "Faqja e përmbajtjes",
        "newwindow": "(hapet në një dritare të re)",
        "cancel": "Anulo",
        "laggedslavemode": "'''Kujdes:''' Kjo faqe nuk mund të ketë përditësime të kohëve të fundit.",
        "readonly": "Databaza e kyçur",
        "enterlockreason": "Shëno arsyen e kyçjes, gjithashtu shëno se kur mund të hapet.",
-       "readonlytext": "Databaza është për momentin e kyçur, nuk lejohen ndryshime ose modifikime, ndoshta për shkak të rutinës së mirëmbajtjes. Pas përfundimit të mirëmbajtjes databaza do të hapet përsëri.\n\nAdministratori që e kyçi dha këtë arsye: $1",
+       "readonlytext": "Databaza për momentin është mbyllur për redaktime dhe ndryshime të reja, ndoshta për shkak të rutinës së mirëmbajtjes. Pas përfundimit të mirëmbajtjes databaza do të hapet përsëri.\n\nAdministratori që e mbylli dha këtë arsye: $1",
        "missing-article": "Databaza nuk gjeti dot tekstin e faqes që duhet të kishte gjetur me emër \"$1\" $2.\n\nKjo zakonisht vjen si rezultat i një diff-i të vjetëruar ose të ndonjë lidhjeje në historikun e një faqeje që është fshirë.\n\nNëse nuk është kjo arsyeja, ateherë ju mund të keni gjetur një gabim në program. Ju lutemi, njoftoni një [[Special:ListUsers/sysop|administrator]], tregojini URL-në problematike.",
        "missingarticle-rev": "(rishikim#: $1)",
        "missingarticle-diff": "(Diff: $1, $2)",
        "createacct-another-realname-tip": "* Emri i vërtetë nuk është i domosdoshëm: Nëse e jepni do të përmendeni si kontribues për punën që ke bërë.",
        "pt-login": "Hyni",
        "pt-login-button": "Hyni",
+       "pt-login-continue-button": "Vazhdoidntifikohu",
        "pt-createaccount": "Krijo llogari",
        "pt-userlogout": "Dil",
        "php-mail-error-unknown": "Gabim i panjohur në funksionin e postës PHP ()",
        "newpassword": "I riu",
        "retypenew": "I riu përsëri",
        "resetpass_submit": "Ndrysho fjalëkalimin dhe hyni brenda",
-       "changepassword-success": "Fjalëkalimin juaj ka ndryshuar me sukses!",
+       "changepassword-success": "Fjalëkalimi yt është ndryshuar.",
        "changepassword-throttled": "Keni bërë shumë tentime të njëpasnjëshme në fjalëkalimin e kësaj llogarie. Ju lutemi prisni para se te tentoni përsëri.",
+       "botpasswords": "Bot fjalëkalimet",
+       "botpasswords-label-create": "Krijo",
+       "botpasswords-label-update": "Përditëso",
+       "botpasswords-label-cancel": "Anulo",
+       "botpasswords-label-delete": "Fshi",
+       "botpasswords-label-resetpassword": "Rivendos fjalëkalimin",
+       "botpasswords-update-failed": "Dështoi për të përditësuar emrin e bot \"$1\". Ishte fshirë?",
        "resetpass_forbidden": "Fjalëkalimet nuk mund të ndryshohen",
        "resetpass-no-info": "Duhet të jeni i kyçur që të keni qasje direkte në këtë faqe.",
        "resetpass-submit-loggedin": "Ndrysho fjalëkalimin",
        "passwordreset-emailtext-user": "Përdoruesi  $1 në {{SITENAME }} ka kërkuar një kujtesë për të dhënat e llogarisë suaj për {{SITENAME }} ($4). Përdoruesi në vijim {{PLURAL:$3 | llogaria është | llogaritë janë}} të lidhur me këtë postë elektronike: \n\n$2\n\n{{PLURAL:$3 | Ky fjalëkalim i përkohshëm | Këto fjalëkalime të përkohshme}} do të përfundojë në {{PLURAL:$5 | një ditë | $5 ditë}}.\nJu duhet të kyçeni dhe të zgjidhni një fjalëkalim të ri tani. Nëse dikush tjetër e ka bërë këtë kërkesës, ose në qoftë se ju mbani mend fjalëkalimin tuaj origjinal, dhe ju nuk dëshirojni të ndryshoni atë, ju mund të injoroni këtë mesazh dhe do të vazhdoni përdorimin e fjalëkalimit tuaj të vjetër.",
        "passwordreset-emailelement": "Emri i përdoruesit: \n$1\n\nFjalëkalimi i përkohshëm: \n$2",
        "passwordreset-emailsentemail": "Nëse kjo është një adresë emaili e regjistruar për llogarinë tuaj, atëherë një email për rivendosjen e fjalëkalimit do të dërgohet.",
-       "passwordreset-emailsent-capture": "Një email për rivendosjen e fjalëkalimit është dërguar, i cili tregohet më poshtë.",
-       "passwordreset-emailerror-capture": "U dërgua një e-mail kujtesë, i cili tregohet më poshtë, por dërgesa për tek përdoruesi qe e pamundur: $1",
        "changeemail": "Ndrysho ose hiq postën elektronike",
        "changeemail-header": "Ndrysho llogarinë e adresës së postës elektronike",
        "changeemail-no-info": "Ju duhet të identifikoheni në mënyrë që të keni të drejtë hyrjeje në këtë faqe.",
        "watchthis": "Vëzhgoje këtë faqe",
        "savearticle": "Kryej ndryshimet",
        "publishpage": "Publiko faqen",
+       "publishchanges": "Publiko ndryshimet",
        "preview": "Shqyrto",
        "showpreview": "Shfaq për shqyrtim",
        "showdiff": "Trego ndryshimet",
        "undo-failure": "Redaktimi nuk mund të zhbëhet për shkak të redaktimeve konfliktuese të ndërmjetshme.",
        "undo-norev": "Redaktimi nuk mund të zhbëhet sepse nuk ekziston ose është fshirë.",
        "undo-summary": "Zhbëje versionin $1 i bërë nga [[Special:Contributions/$2|$2]] ([[User talk:$2|ligjëratë]])",
-       "cantcreateaccounttitle": "Nuk mundet të krijohet llogaria",
        "cantcreateaccount-text": "Hapja e llogarive nga kjo adresë IP ('''$1''') është bllokuar nga [[User:$3|$3]].\n\nArsyeja e dhënë nga $3 është ''$2''.",
        "viewpagelogs": "Shiko regjistrat për këtë faqe",
        "nohistory": "Nuk ka histori redaktimesh për këtë faqe.",
        "special-characters-group-lao": "Lao",
        "special-characters-group-khmer": "Khmer",
        "mw-widgets-dateinput-placeholder-day": "VVVV-MM-DD",
-       "mw-widgets-dateinput-placeholder-month": "VVVV-MM",
-       "api-error-blacklisted": "Ju lutemi zgjidhni një titull të ndryshëm, përshkrues."
+       "mw-widgets-dateinput-placeholder-month": "VVVV-MM"
 }
index 0601177..b04c775 100644 (file)
        "yourpasswordagain": "Потврда лозинке:",
        "createacct-yourpasswordagain": "Потврдите лозинку",
        "createacct-yourpasswordagain-ph": "Унесите лозинку још једном",
-       "remembermypassword": "Запамти ме на овом прегледачу (најдуже $1 {{PLURAL:$1|дан|дана}})",
        "userlogin-remembermypassword": "Остави ме пријављеног/у",
        "userlogin-signwithsecure": "Користите сигурну конекцију",
        "yourdomainname": "Домен:",
        "passwordreset-emailtext-ip": "Неко (вероватно Ви, са ИП адресе $1) је затражио нову лозинку на викију {{SITENAME}} ($4).\nСледећи {{PLURAL:$3|кориснички налог је повезан|кориснички налози су повезани}} с овом имејл адресом:\n\n$2\n\n{{PLURAL:$3|Привремена лозинка истиче|Привремене лозинке истичу}} за {{PLURAL:$5|један дан|$5 дана}}.\nПријавите се и изаберите нову лозинку. Ако је неко други захтевао ову радњу или сте се сетили лозинке и не желите да је мењате, занемарите ову поруку и наставите користити стару лозинку.",
        "passwordreset-emailtext-user": "{{GENDER:$1|Корисник је затражио|Корисница је затражила}} подсетник о подацима за пријаву на викију {{SITENAME}} ($4).\nСледећи {{PLURAL:$3|кориснички налог је повезан|кориснички налози су повезани}} с овом имејл адресом:\n\n$2\n\n{{PLURAL:$3|Привремена лозинка истиче|Привремене лозинке истичу}} за {{PLURAL:$5|један дан|$5 дана}}.\nПријавите се и изаберите нову лозинку. Ако је неко други захтевао ову радњу или сте се сетили лозинке и не желите да је мењате, занемарите ову поруку.",
        "passwordreset-emailelement": "Корисничко име: \n$1\n\nПривремена лозинка: \n$2",
-       "passwordreset-emailsentemail": "Ако је ово имејл адреса регистована на Вашем налогу, подсетник о лозинци ће бити послат на имејл.",
+       "passwordreset-emailsentemail": "Ако је ово имејл адреса повезана са Вашим налогом, подсетник о лозинци ће бити послат на имејл.",
        "passwordreset-emailsentusername": "Ако сте навели имејл адресу приликом регистрације, биће послат имејл за ресетовање лозинке.",
-       "passwordreset-emailsent-capture": "Послат је подсетник преко имејла, који је приказан доле.",
-       "passwordreset-emailerror-capture": "Имејл за ресетовање лозинке, приказан испод је послат, али слање {{GENDER:$2|кориснику|корисници}} није успело: $1",
        "changeemail": "Промени или уклони имејл адресу",
        "changeemail-header": "Попуните овај образац да би сте променили Вашу имејл адресу. Ако жели да ускратите приступ било којој имејл адреси Вашем налогу, оставите празно поље за нову имејл адресу приликом попуњавање обрасца.",
-       "changeemail-passwordrequired": "Морате унети лозинку да би потврдили ову измену.",
        "changeemail-no-info": "Морате бити пријављени да бисте приступили овој страници.",
        "changeemail-oldemail": "Тренутна имејл адреса:",
        "changeemail-newemail": "Нова имејл адреса:",
        "minoredit": "мања измена",
        "watchthis": "надгледај ову страницу",
        "savearticle": "Сачувај страницу",
+       "savechanges": "Сачувај измене",
+       "publishpage": "Објави страницу",
+       "publishchanges": "Објави измене",
        "preview": "Претпреглед",
        "showpreview": "Прикажи претпреглед",
        "showdiff": "Прикажи измене",
        "userpage-userdoesnotexist": "Кориснички налог „<nowiki>$1</nowiki>“ није отворен.\nРазмислите да ли заиста желите да направите/уредите ову страницу.",
        "userpage-userdoesnotexist-view": "Кориснички налог „$1“ није отворен.",
        "blocked-notice-logextract": "Овај корисник је тренутно блокиран.\nИзвештај о последњем блокирању можете погледати испод:",
-       "clearyourcache": "'''Напомена:''' након чувања, можда ћете морати да очистите кеш прегледача.\n*'''Фајерфокс и Сафари:''' држите ''Shift'' и кликните на ''Освежи'', или притисните ''Ctrl-F5'' или Ctrl-R (''⌘-R'' на Макинтошу)\n*'''Гугл кроум:''' притисните ''Ctrl-Shift-R'' (''⌘-Shift-R'' на Макинтошу)\n*'''Интернет експлорер: '''држите ''Ctrl'' и кликните на ''Освежи'', или притисните ''Ctrl-F5''\n*'''Опера:''' очистите привремену меморију преко менија ''Алатке → Поставке''.",
+       "clearyourcache": "<strong>Напомена:</strong> након чувања, можда ћете морати да очистите кеш прегледача како бисте видели промене.\n* <strong>Фајерфокс / Сафари:</strong> држите <em>Shift</em> и кликните на <em>Освежи</em>, или притисните <em>Ctrl-F5</em> или <em>Ctrl-R</em> (<em>⌘-R</em> на Меку)\n* <strong>Гугл кроум:</strong> притисните <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Меку)\n* <strong>Интернет експлорер:</strong> држите <em>Ctrl</em> и кликните на <em>Освежи</em> или притисните <em>Ctrl-F5</em>\n* <strong>Опера:</strong> идите на <em>Алатке → Подешавања</em> (<em>Опера → Поставке</em> на Меку) и затим <em>Приватност и безбедност → Очистите податке о прегледима → Кеширане слике и датотеке</em>.",
        "usercssyoucanpreview": "'''Савет:''' кориситите дугме „{{int:showpreview}}“ да испробате свој нови CSS пре него што га сачувате.",
        "userjsyoucanpreview": "'''Савет:''' кориситите дугме „{{int:showpreview}}“ да испробате свој нови јаваскрипт пре него што га сачувате.",
        "usercsspreview": "'''Ово је само преглед CSS-а.'''\n'''Страница још није сачувана!'''",
        "previewnote": "<strong>Ово је претпреглед.</strong>\nВаше измене још нису сачуване!",
        "continue-editing": "Иди на уређивачки оквир",
        "previewconflict": "Овај преглед осликава како ће текст у текстуалном оквиру изгледати.",
-       "session_fail_preview": "'''Нисмо могли да обрадимо вашу измену због губитка података сесије.'''\nПокушајте поново.\nАко и даље не ради, покушајте да се [[Special:UserLogout|одјавите]] и поново пријавите.",
-       "session_fail_preview_html": "'''Нисмо могли да обрадимо вашу измену због губитка података сесије.'''\n\n''Будући да је на овом викију омогућен унос HTML ознака, преглед је сакривен као мера предострожности против напада преко јаваскрипта.''\n\n'''Ако сте покушали да направите праву измену, покушајте поново.\nАко и даље не ради, покушајте да се [[Special:UserLogout|одјавите]] и поново пријавите.'''",
+       "session_fail_preview": "Нисмо могли да обрадимо вашу измену због губитка података сесије.\n\nМожда сте одјављени. <strong>Проверите да ли сте пријављен и покушајте поново</strong>.\n\nАко и даље не ради, покушајте да се [[Special:UserLogout|одјавите]] и поново пријавите и проверите да ли су на Вашем претраживачу дозвољени колачићи са овог сајта.",
+       "session_fail_preview_html": "Нисмо могли да обрадимо вашу измену због губитка података сесије.\n\n<em>Будући да је на овом викију омогућен унос HTML ознака, преглед је сакривен као мера предострожности против напада преко јаваскрипта.</em>\n\n<strong>Ако сте покушали да направите праву измену, покушајте поново.<strong>\nАко и даље не ради, покушајте да се [[Special:UserLogout|одјавите]] и поново пријавите и проверите да ли Ваш претраживач дозвољава колачиће са овог сајта.",
        "token_suffix_mismatch": "'''Ваша измена је одбачена јер је ваш прегледач убацио знакове интерпункције у новчић уређивања.\nТо се понекад догађа када се користи неисправан посредник.'''",
        "edit_form_incomplete": "<strong>Неки делови обрасца за уређивање нису стигли до сервера. Проверите да ли су ваше измене непромењене и покушајте поново.</strong>",
        "editing": "Уређујете $1",
        "undo-nochange": "Изгледа да је измена већ поништена.",
        "undo-summary": "Поништена измена $1 {{GENDER:$2|корисника|кориснице}} [[Special:Contribs/$2|$2]] ([[User talk:$2|разговор]])",
        "undo-summary-username-hidden": "Поништи измену $1 скривеног корисника",
-       "cantcreateaccounttitle": "Не могу да отворим налог",
        "cantcreateaccount-text": "Отварање налога с ове ИП адресе (<strong>$1</strong>) је блокирао/ла [[User:$3|$3]].\n\nРазлог који је навео/ла $3 је <em>$2</em>",
        "cantcreateaccount-range-text": "Отварање налога са ИП адреса у распону <strong>$1</strong>, који укључује и вашу ИП адресу (<strong>$4</strong>) је блокирао/ла [[User:$3|$3]].\n\nРазлог који је навео/ла $3 је <em>$2</em>",
        "viewpagelogs": "Погледај дневнике ове странице",
        "userrights-unchangeable-col": "Групе које не можете да промените",
        "userrights-irreversible-marker": "$1*",
        "userrights-conflict": "Сукоб промена корисничких права! Молимо проверите ваше измене.",
-       "userrights-removed-self": "УÑ\81пеÑ\88но Ñ\81Ñ\82е Ñ\81еби Ñ\81кинÑ\83ли Ð¿Ñ\80ава. Ð\97бог Ñ\82ога Ð½Ð¸Ñ\98е Ð²Ð°Ð¼ дозвољен приступ овој страници.",
+       "userrights-removed-self": "СкинÑ\83ли Ñ\81Ñ\82е Ñ\81еби Ð¿Ñ\80ава. Ð\97бог Ñ\82ога Ð\92ам Ð½Ð¸Ñ\98е дозвољен приступ овој страници.",
        "group": "Група:",
        "group-user": "Корисници",
        "group-autoconfirmed": "Аутоматски потврђени корисници",
        "group-bot-member": "{{GENDER:$1|бот}}",
        "group-sysop-member": "{{GENDER:$1|администратор|администраторка|администратор}}",
        "group-bureaucrat-member": "{{GENDER:$1|бирократа}}",
-       "group-suppress-member": "{{GENDER:$1|ревизор|ревизорка}}",
+       "group-suppress-member": "{{GENDER:$1|брисач измена}}",
        "grouppage-user": "{{ns:project}}:Корисници",
        "grouppage-autoconfirmed": "{{ns:project}}:Аутоматски потврђени корисници",
        "grouppage-bot": "{{ns:project}}:Ботови",
        "grant-createeditmovepage": "Прављење, уређивање и премештање страница",
        "grant-delete": "Брисање страница, измена и уноса у дневницима",
        "grant-editinterface": "Уређивање Медијавики именског простора и корисничких CSS/JavaScript страница",
+       "grant-editmycssjs": "Уређивање вашег CSS/JavaScript-а",
        "grant-editmyoptions": "Уређивање ваших подешавања",
        "grant-editmywatchlist": "Уређивање вашег списка надгледања",
        "grant-editpage": "Уређивање постојећих страница",
        "grant-editprotected": "Уређивање заштићених страница",
+       "grant-patrol": "Патролирање измена",
        "grant-protect": "Закључавање и откључавање страница",
        "grant-rollback": "Враћање измена",
+       "grant-sendemail": "Слање имејлова другим корисницима",
        "grant-uploadeditmovefile": "Отпремање, замена и премештање датотека",
        "grant-uploadfile": "Слање нових датотека",
        "grant-basic": "Основна права",
        "file-thumbnail-no": "Датотека почиње са <strong>$1</strong>.\nИзгледа да се ради о умањеној слици ''(thumbnail)''.\nУколико имате ову слику у пуној величини, пошаљите је, а ако немате, промените назив датотеке.",
        "fileexists-forbidden": "Датотека с овим називом већ постоји и не може се заменити.\nАко и даље желите да пошаљете датотеку, вратите се и изаберите други назив.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Датотека с овим називом већ постоји у заједничкој остави.\nВратите се и пошаљите датотеку с другим називом.\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Датотека је дупликат тренутне верзије <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Датотека је дупликат {{PLURAL:$2|старе верзије|старих верзија}} <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Ово је дупликат {{PLURAL:$1|следеће датотеке|следећих датотека}}:",
        "file-deleted-duplicate": "Датотека истоветна овој ([[:$1]]) је претходно обрисана.\nПогледајте историју брисања пре поновног слања.",
        "file-deleted-duplicate-notitle": "Датотека идентична овој претходно је обрисана и име јој је сакривено.\nТребали бисте питати некога ко може видети податке скривених датотека да прегледа ситуацију пре него што поново отпремите датотеку.",
        "uploadstash-summary": "Ова страница пружа приступ датотекама које су послате (или се шаљу), али још нису објављене. Ове датотеке су видљиве само кориснику који га је послао.",
        "uploadstash-clear": "Очисти сакривене датотеке",
        "uploadstash-nofiles": "Немате сакривене датотеке.",
-       "uploadstash-badtoken": "Извршавање дате радње није успело. Разлог томе може бити истек времена за уређивање. Покушајте поново.",
+       "uploadstash-badtoken": "Извршавање дате радње није успело, разлог томе може бити истек времена за уређивање. Покушајте поново.",
        "uploadstash-errclear": "Чишћење датотека није успело.",
        "uploadstash-refresh": "Освежи списак датотека",
        "invalid-chunk-offset": "Неисправна полазна тачка",
        "filerevert-submit": "Врати",
        "filerevert-success": "Датотека '''[[Media:$1|$1]]''' је враћена на [$4 издање од $2; $3].",
        "filerevert-badversion": "Не постоји раније локално издање датотеке с наведеним временским подацима.",
+       "filerevert-identical": "Тренутна верзија датотеке индентична је изабраној.",
        "filedelete": "Обриши $1",
        "filedelete-legend": "Обриши датотеку",
        "filedelete-intro": "Бришете датотеку '''[[Media:$1|$1]]''' заједно с њеном историјом.",
        "listgrouprights-namespaceprotection-restrictedto": "Права потребна за уређивање",
        "listgrants-rights": "Права",
        "trackingcategories": "Медијавики категорије",
-       "trackingcategories-summary": "Ð\9eва Ð¿Ð¾Ñ\81ебна Ñ\81Ñ\82Ñ\80аниÑ\86а Ñ\98е Ñ\81пиÑ\81ак ÐºÐ°Ñ\82егоÑ\80иÑ\98а ÐºÐ¾Ñ\98е Ñ\81Ñ\83 Ð´ÐµÐ¾ Ð\9cедиÑ\98авикиÑ\98а, Ð¾Ð½Ðµ Ñ\81е Ð°Ñ\83Ñ\82омаÑ\82Ñ\81ки Ð°Ð¶Ñ\83Ñ\80иÑ\80аÑ\98Ñ\83 Ð¸ Ñ\9aиÑ\85ови Ð½Ð°Ð·Ð¸Ð²Ð¸ Ñ\81е Ð¼Ð¾Ð³Ñ\83 Ð¼ÐµÑ\9aаÑ\9aаÑ\82и Ñ\83Ñ\80еÑ\92иваÑ\9aем Ñ\81иÑ\81Ñ\82емÑ\81киÑ\85 Ð¿Ð¾Ñ\80Ñ\83ка Ñ\83 Ð¸Ð¼ÐµÐ½Ñ\81ком Ð¿Ñ\80оÑ\81Ñ\82оÑ\80Ñ\83 {{ns:8}}.",
+       "trackingcategories-summary": "Ова посебна страница је списак категорија које су део Медијавикија, оне се аутоматски ажурирају и њихови називи се могу мењати уређивањем системских порука у именском простору {{ns:8}}.",
        "trackingcategories-name": "Име поруке",
        "trackingcategories-desc": "Које странице се налазе у категорији",
        "noindex-category-desc": "Странице које у себи имају магичну реч <code><nowiki>__NOINDEX__</nowiki></code>.",
        "protectlogpage": "Дневник закључавања",
        "protectlogtext": "Испод је списак заштићених страница.\nПогледајте [[Special:ProtectedPages|списак заштићених страница]] за више детаља.",
        "protectedarticle": "је заштитио „[[$1]]“",
-       "modifiedarticleprotection": "је променио степен заштите за „[[$1]]“",
+       "modifiedarticleprotection": "промењен степен заштите за „[[$1]]“",
        "unprotectedarticle": "је скинуо заштиту са странице „[[$1]]“",
        "movedarticleprotection": "је преместио поставке заштите са „[[$2]]“ на „[[$1]]“",
        "protect-title": "Степен заштите за „$1“",
        "tooltip-ca-nstab-category": "Погледајте страницу категорија",
        "tooltip-minoredit": "Означите измену као мању",
        "tooltip-save": "Сачувајте измене које сте направили",
+       "tooltip-publish": "Објави своје измене",
        "tooltip-preview": "Прегледајте своје измене. Пожељно је да користите ово дугме пре чувања",
        "tooltip-diff": "Погледајте све измене које сте направили на тексту",
        "tooltip-compareselectedversions": "Погледаjте разлике између две изабране измене ове странице.",
        "previousdiff": "← Старија измена",
        "nextdiff": "Новија измена →",
        "mediawarning": "<strong>Упозорење:</strong> ова врста датотеке може садржати штетан код.\nАко га покренете, Ваш рачунар може бити угрожен.",
-       "imagemaxsize": "Ограничење величине слике:<br />''(на страницама за опис датотека)''",
+       "imagemaxsize": "Ограничење величине слике:<br /><em>(на страницама за опис датотека)</em>",
        "thumbsize": "Величина минијатуре:",
        "widthheight": "$1 × $2",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|страница|странице|страница}}",
        "version-libraries-license": "Лиценца",
        "version-libraries-description": "Опис",
        "version-libraries-authors": "Аутори",
-       "redirect": "Преусмерење на датотеку, корисника, страницу или измену",
+       "redirect": "Преусмерење на датотеку, корисника, страницу, измену или дневник",
        "redirect-submit": "Иди",
        "redirect-lookup": "Тип вредности:",
        "redirect-value": "Вредност:",
        "mw-widgets-dateinput-placeholder-month": "ГГГГ-ММ",
        "mw-widgets-titleinput-description-new-page": "страница још увек не постоји",
        "mw-widgets-titleinput-description-redirect": "преусмерава на $1",
-       "api-error-blacklisted": "Изаберите другачији, описан назив.",
        "randomrootpage": "Случајна коренска страница",
        "log-action-filter-block": "Тип блокирања:",
        "log-action-filter-contentmodel": "Тип промене модела садржаја:",
index 996d191..155e8c6 100644 (file)
        "passwordreset-emailelement": "Korisničko ime: \n$1\n\nPrivremena lozinka: \n$2",
        "passwordreset-emailsentemail": "Podsetnik o lozinci je poslat na vašu adresu.",
        "passwordreset-emailsentusername": "Ako ste naveli imejl adresu prilikom registracije, biće poslat imejl za resetovanje lozinke.",
-       "passwordreset-emailsent-capture": "Poslat je podsetnik preko e-pošte (prikazan dole).",
-       "passwordreset-emailerror-capture": "E-poruka za resetovanje lozinke, prikazana ispod je poslata, ali slanje {{GENDER:$2|korisniku|korisnici}} nije uspelo: $1",
        "changeemail": "Promeni ili ukloni e-adresu",
        "changeemail-header": "Promenite e-adresu naloga",
-       "changeemail-passwordrequired": "Morate uneti lozinku da bi potvrdili ovu izmenu.",
        "changeemail-no-info": "Morate biti prijavljeni da biste pristupili ovoj stranici.",
        "changeemail-oldemail": "Trenutna e-adresa:",
        "changeemail-newemail": "Nova e-adresa:",
        "minoredit": "manja izmena",
        "watchthis": "nadgledaj ovu stranicu",
        "savearticle": "Sačuvaj stranicu",
-       "preview": "Pregled",
+       "savechanges": "Sačuvaj izmene",
+       "publishpage": "Objavi stranicu",
+       "publishchanges": "Objavi izmene",
+       "preview": "Pretpregled",
        "showpreview": "Prikaži pretpregled",
        "showdiff": "Prikaži izmene",
        "blankarticle": "<strong>Upozorenje:</strong> stranica koju pravite nema nikakav sadržaj.\nAko još jednom pritisnete „{{int:savearticle}}“ napravićete praznu stranicu.",
        "userpage-userdoesnotexist": "Korisnički nalog „<nowiki>$1</nowiki>“ nije otvoren.\nRazmislite da li zaista želite da napravite/uredite ovu stranicu.",
        "userpage-userdoesnotexist-view": "Korisnički nalog „$1“ nije otvoren.",
        "blocked-notice-logextract": "Ovaj korisnik je trenutno blokiran.\nIzveštaj o poslednjem blokiranju možete pogledati ispod:",
-       "clearyourcache": "'''Napomena:''' nakon čuvanja, možda ćete morati da očistite keš pregledača.\n*'''Fajerfoks i Safari:''' držite ''Shift'' i kliknite na ''Osveži'', ili pritisnite ''Ctrl-F5'' ili Ctrl-R (''⌘-R'' na Makintošu)\n*'''Gugl kroum:''' pritisnite ''Ctrl-Shift-R'' (''⌘-Shift-R'' na Makintošu)\n*'''Internet eksplorer: '''držite ''Ctrl'' i kliknite na ''Osveži'', ili pritisnite ''Ctrl-F5''\n*'''Opera:''' očistite privremenu memoriju preko menija ''Alatke → Postavke''.",
+       "clearyourcache": "<strong>Napomena:</strong> nakon čuvanja, možda ćete morati da očistite keš pregledača kako biste videli promene.\n* <strong>Fajerfoks / Safari:</strong> držite <em>Shift</em> i kliknite na <em>Osveži</em>, ili pritisnite <em>Ctrl-F5</em> ili <em>Ctrl-R</em> (<em>⌘-R</em> na Meku)\n* <strong>Gugl kroum:</strong> pritisnite <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> na Meku)\n* <strong>Internet eksplorer:</strong> držite <em>Ctrl</em> i kliknite na <em>Osveži</em> ili pritisnite <em>Ctrl-F5</em>\n* <strong>Opera:</strong> idite na <em>Alatke → Podešavanja</em> (<em>Opera → Postavke</em> na Meku) i zatim <em>Privatnost i bezbednost → Očistite podatke o pregledima → Keširane slike i datoteke</em>.",
        "usercssyoucanpreview": "'''Savet:''' korisitite dugme „{{int:showpreview}}“ da isprobate svoj novi CSS pre nego što ga sačuvate.",
        "userjsyoucanpreview": "'''Savet:''' korisitite dugme „{{int:showpreview}}“ da isprobate svoj novi javaskript pre nego što ga sačuvate.",
        "usercsspreview": "'''Ovo je samo pregled CSS-a.'''\n'''Stranica još nije sačuvana!'''",
        "undo-nochange": "Izgleda da je izmena već poništena.",
        "undo-summary": "Poništena izmena $1 {{GENDER:$2|korisnika|korisnice}} [[Special:Contribs/$2|$2]] ([[User talk:$2|razgovor]])",
        "undo-summary-username-hidden": "Poništi izmenu $1 skrivenog korisnika",
-       "cantcreateaccounttitle": "Ne mogu da otvorim nalog",
        "cantcreateaccount-text": "Otvaranje naloga s ove IP adrese (<strong>$1</strong>) je blokirao/la [[User:$3|$3]].\n\nRazlog koji je naveo/la $3 je <em>$2</em>",
        "cantcreateaccount-range-text": "Otvaranje naloga sa IP adresa u rasponu <strong>$1</strong>, koji uključuje i vašu IP adresu (<strong>$4</strong>) je blokirao/la [[User:$3|$3]].\n\nRazlog koji je naveo/la $3 je <em>$2</em>",
        "viewpagelogs": "Pogledaj dnevnike ove stranice",
        "rev-showdeleted": "prikaži",
        "revisiondelete": "Obriši/vrati izmene",
        "revdelete-nooldid-title": "Nema tražene izmene",
-       "revdelete-nooldid-text": "Niste naveli željenu izmenu, ona ne postoji ili pokušavate da je sakrijete.",
+       "revdelete-nooldid-text": "Niste izabrali odredišnu izmenu na kojoj treba da se izvrši ova funkcija, ta izmena ne postoji, ili pokušavate sakriti trenutnu izmenu.",
        "revdelete-no-file": "Tražena datoteka ne postoji.",
        "revdelete-show-file-confirm": "Želite li da vidite obrisanu izmenu datoteke „<nowiki>$1</nowiki>“ od $2; $3?",
        "revdelete-show-file-submit": "Da",
        "group-bot-member": "{{GENDER:$1|bot}}",
        "group-sysop-member": "{{GENDER:$1|administrator|administratorka}}",
        "group-bureaucrat-member": "{{GENDER:$1|birokrata}}",
-       "group-suppress-member": "{{GENDER:$1|revizor|revizorka}}",
+       "group-suppress-member": "{{GENDER:$1|brisač izmena}}",
        "grouppage-user": "{{ns:project}}:Korisnici",
        "grouppage-autoconfirmed": "{{ns:project}}:Automatski potvrđeni korisnici",
        "grouppage-bot": "{{ns:project}}:Botovi",
        "listgrouprights-namespaceprotection-restrictedto": "Prava potrebna za uređivanje",
        "listgrants-rights": "Prava",
        "trackingcategories": "Medijaviki kategorije",
-       "trackingcategories-summary": "Ova posebna stranica je spisak kategorija koje su deo Medijavikija, one se automatski ažuriraju i njihovi nazivi se mogu menjanjati uređivanjem sistemskih poruka u imenskom prostoru {{ns:8}}.",
+       "trackingcategories-summary": "Ova posebna stranica je spisak kategorija koje su deo Medijavikija, one se automatski ažuriraju i njihovi nazivi se mogu menjati uređivanjem sistemskih poruka u imenskom prostoru {{ns:8}}.",
        "trackingcategories-name": "Ime poruke",
        "trackingcategories-desc": "Koje stranice se nalaze u kategoriji",
        "noindex-category-desc": "Stranice koje u sebi imaju magičnu reč <code><nowiki>__NOINDEX__</nowiki></code>.",
        "protectlogpage": "Dnevnik zaključavanja",
        "protectlogtext": "Ispod je spisak zaštićenih stranica.\nPogledajte [[Special:ProtectedPages|spisak zaštićenih stranica]] za više detalja.",
        "protectedarticle": "je zaštitio „[[$1]]“",
-       "modifiedarticleprotection": "je promenio stepen zaštite za „[[$1]]“",
+       "modifiedarticleprotection": "promenjen stepen zaštite za „[[$1]]“",
        "unprotectedarticle": "je skinuo zaštitu sa stranice „[[$1]]“",
        "movedarticleprotection": "je premestio postavke zaštite sa „[[$2]]“ na „[[$1]]“",
        "protect-title": "Stepen zaštite za „$1“",
        "tooltip-ca-nstab-category": "Pogledajte stranicu kategorija",
        "tooltip-minoredit": "Označite izmenu kao manju",
        "tooltip-save": "Sačuvajte izmene koje ste napravili",
+       "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-compareselectedversions": "Pogledajte razlike između dve izabrane izmene ove stranice.",
        "previousdiff": "← Starija izmena",
        "nextdiff": "Novija izmena →",
        "mediawarning": "<strong>Upozorenje:</strong> ova vrsta datoteke može sadržati štetan kod.\nAko ga pokrenete, Vaš računar može biti ugrožen.",
-       "imagemaxsize": "Ograničenje veličine slike:<br />''(na stranicama za opis datoteka)''",
+       "imagemaxsize": "Ograničenje veličine slike:<br /><em>(na stranicama za opis datoteka)</em>",
        "thumbsize": "Veličina minijature:",
        "widthheight": "$1 × $2",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|stranica|stranice|stranica}}",
        "mw-widgets-dateinput-placeholder-month": "GGGG-MM",
        "mw-widgets-titleinput-description-new-page": "stranica još uvek ne postoji",
        "mw-widgets-titleinput-description-redirect": "preusmerava na $1",
-       "api-error-blacklisted": "Izaberite drugačiji, opisan naziv.",
        "randomrootpage": "Slučajna korenska stranica"
 }
index 4f61cf7..5331c26 100644 (file)
                        "Dammråtta",
                        "Mgr",
                        "Matma Rex",
-                       "McDutchie"
+                       "McDutchie",
+                       "Larske",
+                       "Rockyfelle",
+                       "Johan"
                ]
        },
        "tog-underline": "Stryk under länkar:",
        "tagline": "Från {{SITENAME}}",
        "help": "Hjälp",
        "search": "Sök",
+       "search-ignored-headings": "#<!-- lämna denna rad precis som den är --> <pre>\n# Rubriker som kommer att ignoreras av sökningen.\n# Förändringar av detta kommer att gälla så fort sidan med rubriken är indexerad.\n# Du kan tvinga sidan att indexeras om genom att göra en null-redigering.\n# Syntaxen är som följer:\n#  * Allt från ett \"#\" tecken till slutet av raden är en kommentar.\n#  * Varje icke-tom rad är den exakta titeln som ska ignoreras, shiftläge och allt.\nReferenser\nExterna länkar\nSe även\n #</pre> <!-- lämna denna rad precis som den är -->",
        "searchbutton": "Sök",
        "go": "Gå till",
        "searcharticle": "Gå till",
        "actionthrottledtext": "Som skydd mot missbruk finns det en begränsning av hur många gånger du kan utföra den här åtgärden under en viss tid. Du har överskridit den gränsen.\nFörsök igen om några minuter.",
        "protectedpagetext": "Den här sidan har skrivskyddats för att förhindra redigering eller andra åtgärder.",
        "viewsourcetext": "Du kan se och kopiera denna sidas källtext.",
-       "viewyourtext": "Du kan se och kopiera källan för <strong>dina redigeringar</strong> av denna sida.",
+       "viewyourtext": "Du kan se och kopiera källtexten för <strong>dina redigeringar</strong> av denna sida.",
        "protectedinterface": "Denna sida innehåller text för mjukvarans gränssnitt på denna wiki, och är skrivskyddad för att förebygga missbruk.\nFör att lägga till eller ändra översättningar för alla wikis, var god använd [https://translatewiki.net/ translatewiki.net], lokaliseringsprojektet för MediaWiki.",
        "editinginterface": "<strong>Varning:</strong> Du redigerar en sida som används för texten i gränssnittet.\nÄndringar på denna sida kommer att påverka användargränssnittets utseende för andra användare på denna wiki.",
        "translateinterface": "För att lägga till eller ändra översättningar för alla wikis, använd [https://translatewiki.net/ translatewiki.net], lokaliseringsprojektet för MediaWiki.",
        "yourpasswordagain": "Upprepa lösenord",
        "createacct-yourpasswordagain": "Bekräfta lösenordet",
        "createacct-yourpasswordagain-ph": "Ange lösenordet igen",
-       "remembermypassword": "Spara min inloggning på den här datorn (i max $1 {{PLURAL:$1|dygn}})",
        "userlogin-remembermypassword": "Håll mig inloggad",
        "userlogin-signwithsecure": "Använd säker anslutning",
        "cannotloginnow-title": "Kan inte logga in nu",
        "passwordreset-emailelement": "Användarnamn: \n$1\n\nTillfälligt lösenord: \n$2",
        "passwordreset-emailsentemail": "Om denna e-postadress är associerad med ditt konto kommer en lösenordsåterställning skickas via e-post.",
        "passwordreset-emailsentusername": "Om det finns en e-postadress som associeras med detta användarnamn kommer en lösenordsåterställning skickas via e-post.",
-       "passwordreset-emailsent-capture": "En lösenordsåterställning via e-post har skickats, som visas nedan.",
-       "passwordreset-emailerror-capture": "En lösenordsåterställning via e-post har skapats, som visas nedan, men det gick inte att skicka den till {{GENDER:$2|användaren}}: $1",
        "passwordreset-emailsent-capture2": "{{PLURAL:$1|E-postmeddelande|E-postmeddelanden}} för återställning av lösenord har skickats. {{PLURAL:$1|Användarnamnet och lösenordet|Listan över användarnamn och lösenord}} visas nedan.",
        "passwordreset-emailerror-capture2": "Kunde inte skicka e-post till {{GENDER:$2|användaren}}: $1 {{PLURAL:$3|Användarnamnet och lösenordet|Listan över användarnamn och lösenord}} listas nedan.",
        "passwordreset-nocaller": "En användare måste anges",
        "passwordreset-invalideamil": "Ogiltig e-postadress",
        "passwordreset-nodata": "Varken ett användarnamn eller en e-postadress angavs",
        "changeemail": "Ändra eller ta bort e-postadress",
-       "changeemail-header": "Fyll i detta formulär för att ändra din e-postadress. Lämna fältet för ny e-postadress tomt när du skickar in formuläret om du vill ta bort en associerad e-postadress från ditt konto.",
-       "changeemail-passwordrequired": "Du måste ange ditt lösenord för att bekräfta denna ändring.",
+       "changeemail-header": "Fyll i detta formulär för att ändra din e-postadress. Om du vill ta bort en associerad e-postadress från ditt konto lämnar du fältet för ny e-postadress tomt när formuläret skickas in.",
        "changeemail-no-info": "Du måste vara inloggad för att komma åt den här sidan direkt.",
        "changeemail-oldemail": "Nuvarande e-postadress:",
        "changeemail-newemail": "Ny e-postadress:",
        "anontalkpagetext": "----<em>Detta är diskussionssidan för en anonym användare som inte ännu skapat ett konto, eller som inte använder det.</em>\nDärför måste vi använda den numeriska IP-adressen för att identifiera honom/henne.\nEn sådan IP-adress kan delas av flera användare.\nOm du är en anonym användare och känner att irrelevanta kommentarer har riktats mot dig, vänligen [[Special:CreateAccount|skapa ett konto]] eller [[Special:UserLogin|logga in]] för att undvika framtida förväxlingar med andra anonyma användare.",
        "noarticletext": "Det finns just nu ingen text på denna sida.\nDu kan [[Special:Search/{{PAGENAME}}|söka efter denna sidtitel]] på andra sidor, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} söka i relaterade loggar], eller [{{fullurl:{{FULLPAGENAME}}|action=edit}} skapa denna sida]</span>.",
        "noarticletext-nopermission": "Det finns för tillfället ingen text på denna sida.\nDu kan [[Special:Search/{{PAGENAME}}|söka efter denna sidas titel]] på andra sidor,\neller <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} söka i relaterade loggar]</span> men du har inte behörighet att skapa sidan.",
-       "missing-revision": "Revisionen #$1 av sidan med namnet \"{{FULLPAGENAME}}\" finns inte.\n\nDetta orsakas vanligen av efter en gammal historiklänk till en sida som har raderats.\nDetaljer kan hittas i [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} raderingsloggen].",
+       "missing-revision": "Version #$1 av sidan med namnet \"{{FULLPAGENAME}}\" finns inte.\n\nDetta orsakas vanligen genom att en gammal historiklänk följts till en sida som har raderats.\nDetaljer kan hittas i [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} raderingsloggen].",
        "userpage-userdoesnotexist": "\"<nowiki>$1</nowiki>\" är inte ett registrerat användarkonto. Tänk efter om du vill skapa/redigera den här sidan.",
        "userpage-userdoesnotexist-view": "Kontot \"$1\" är inte registrerat.",
        "blocked-notice-logextract": "Användaren är blockerad.\nOrsaken till senaste blockeringen kan ses nedan:",
        "content-model-css": "CSS",
        "content-json-empty-object": "Tomt objekt",
        "content-json-empty-array": "Tomt fält",
+       "deprecated-self-close-category": "Sidor som använder ogiltiga självstängda HTML-taggar",
+       "deprecated-self-close-category-desc": "Sidan använder ogiltiga självstängda HTML-taggar, som <code>&lt;b/></code> eller <code>&lt;span/></code>.  Beteendet för dessa kommer snart att ändras för att bli konsistent med HTML5-specifikationen, så dessa anses vara för föråldrade för att använda i wikitext.",
        "duplicate-args-warning": "<strong>Varning:</strong> [[:$1]] anropar [[:$2]] med mer än ett värde för parametern \"$3\". Endast det sista värdet kommer att användas.",
        "duplicate-args-category": "Sidor som använder upprepade argument i mallanrop",
        "duplicate-args-category-desc": "Sidan innehåller mallanrop som använder repeterade argument, så som <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> eller <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Det verkar som att redigeringen redan har blivit ogjord.",
        "undo-summary": "Gör version $1 av [[Special:Contributions/$2|$2]] ([[User talk:$2|diskussion]]) ogjord",
        "undo-summary-username-hidden": "Gör version $1 av en dold användare ogjord",
-       "cantcreateaccounttitle": "Kan inte skapa konto",
        "cantcreateaccount-text": "[[User:$3|$3]] har blockerat den här IP-adressen ('''$1''') från att registrera konton.\n\nAnledningen till blockeringen var \"$2\".",
        "cantcreateaccount-range-text": "IP-adresserna i intervallet <strong>$1</strong>, som inkluderar din IP-adress (<strong>$4</strong>), har blockerats från att skapa konton av [[User:$3|$3]].\n\nAnledningen enligt $3 var <em>$2</em>",
        "viewpagelogs": "Visa loggar för denna sida",
        "revdelete-selected-text": "{{PLURAL:$1|Vald version|Valda versioner}} av [[:$2]]:",
        "revdelete-selected-file": "{{PLURAL:$1|Vald filversion|Valda filversioner}} av [[:$2]]:",
        "logdelete-selected": "{{PLURAL:$1|Vald loggåtgärd|Valda loggåtgärder}}:",
-       "revdelete-text-text": "Raderade sidversioner kommer fortfarande synas i sidans historik, men delar av innehållet kommer inte att bli tillgängligt offentligt.",
-       "revdelete-text-file": "Raderade filversioner kommer fortfarande synas i filens historik, men delar av innehållet kommer inte att bli tillgängligt offentligt.",
-       "logdelete-text": "Raderade logghändelser kommer fortfarande synas i loggarna, men delar av innehållet kommer inte att bli tillgängligt offentligt.",
+       "revdelete-text-text": "Raderade sidversioner kommer fortfarande synas i sidans historik, men delar av innehållet kommer inte att vara tillgängligt offentligt.",
+       "revdelete-text-file": "Raderade filversioner kommer fortfarande synas i filhistoriken, men delar av innehållet kommer att vara otillgängligt för allmänheten.",
+       "logdelete-text": "Raderade logghändelser kommer fortfarande synas i loggarna, men delar av innehållet kommer att vara otillgängligt för allmänheten.",
        "revdelete-text-others": "Andra administratörer kommer fortfarande att kunna komma åt det dolda innehållet och återställa det igen om inte ytterligare begränsningar används.",
        "revdelete-confirm": "Var god bekräfta att du vill göra detta, och att du förstår konsekvenserna, och att du gör så i enlighet med [[{{MediaWiki:Policy-url}}|policyn]].",
        "revdelete-suppress-text": "Undanhållande ska '''bara''' användas i följande fall:\n* Eventuell förolämpande information\n* Opassande personlig information\n*: ''hemadresser och telefonnummer, personnummer, etc.''",
        "search-relatedarticle": "Relaterad",
        "searchrelated": "relaterad",
        "searchall": "alla",
-       "showingresults": "Nedan visas upp till {{PLURAL:$1|'''1''' post|'''$1''' poster}} från och med nummer '''$2'''.",
+       "showingresults": "Nedan visas upp till {{PLURAL:$1|<strong>1</strong> resultat|<strong>$1</strong> resultat}} från och med nummer <strong>$2</strong>.",
        "showingresultsinrange": "Nedan visas upp till {{PLURAL:$3|<strong>1</strong> resultat|<strong>$1</strong> resultat}} mellan nummer <strong>$2</strong> och nummer <strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Resultat <strong>$1</strong> av <strong>$3</strong>|Resultat <strong>$1 – $2</strong> av <strong>$3</strong>}}",
        "search-nonefound": "Inga resultat matchade frågan.",
        "prefs-email": "Alternativ för e-post",
        "prefs-rendering": "Utseende",
        "saveprefs": "Spara",
-       "restoreprefs": "Återgå till standardinställningar",
+       "restoreprefs": "Återgå till standardinställningar (i alla delar)",
        "prefs-editing": "Redigering",
        "rows": "Rader:",
        "columns": "Kolumner:",
        "email": "E-post",
        "prefs-help-realname": "Riktigt namn behöver inte anges.\nOm angivet, kan det komma att användas för att tillskriva dig ditt arbete.",
        "prefs-help-email": "Att ange e-postadress är valfritt, men gör det möjligt att få ditt lösenord mejlat till dig om du glömmer det.",
-       "prefs-help-email-others": "Du kan också välja att låta andra kontakta dig via e-post genom en länk på din användar- eller diskussionssida. Din e-postadress avslöjas inte när andra användare kontaktar dig.",
+       "prefs-help-email-others": "Du kan också välja att låta andra kontakta dig via e-post genom en länk på din användar- eller diskussionssida. \nDin e-postadress avslöjas inte när andra användare kontaktar dig.",
        "prefs-help-email-required": "E-postadress måste anges.",
        "prefs-info": "Grundläggande information",
        "prefs-i18n": "Internationalisering",
        "right-createpage": "Skapa sidor (som inte är diskussionssidor)",
        "right-createtalk": "Skapa diskussionssidor",
        "right-createaccount": "Skapa nya användarkonton",
-       "right-autocreateaccount": "Logga in automatiskt med en extern användarkonto",
+       "right-autocreateaccount": "Logga in automatiskt med ett externt användarkonto",
        "right-minoredit": "Markera redigeringar som mindre",
        "right-move": "Flytta sidor",
        "right-move-subpages": "Flytta sidor med deras undersidor",
        "grant-group-high-volume": "Utför aktivitet av hög volym",
        "grant-group-customization": "Anpassning och inställningar",
        "grant-group-administration": "Utför administrativa åtgärder",
+       "grant-group-private-information": "Få tillgång till privat data om dig",
        "grant-group-other": "Diverse aktivitet",
        "grant-blockusers": "Blockera och avblockera användare",
        "grant-createaccount": "Skapa konton",
        "grant-highvolume": "Hög volymsredigering",
        "grant-oversight": "Dölj användare och censurera versioner",
        "grant-patrol": "Patrullera ändringar på sidor",
+       "grant-privateinfo": "Få tillgång till privat information",
        "grant-protect": "Skydda och ta bort skydd på sidor",
        "grant-rollback": "Rulla tillbaka ändringar på sidor",
        "grant-sendemail": "Skicka e-post till andra användare",
        "action-applychangetags": "tillämpa märken tillsammans med dina ändringar",
        "action-changetags": "lägg till och ta bort godtyckliga märken på individuella sidversioner och loggposter",
        "action-deletechangetags": "radera märken från databasen",
+       "action-purge": "rensa denna sida",
        "nchanges": "$1 {{PLURAL:$1|ändring|ändringar}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|sedan senaste besöket}}",
        "enhancedrc-history": "historik",
        "recentchanges-legend-plusminus": "(''±123'')",
        "recentchanges-submit": "Visa",
        "rcnotefrom": "Nedan visas {{PLURAL:$5|ändringen|ändringar}} sedan <strong>$3, $4</strong> (upp till <strong>$1</strong> ändringar visas).",
-       "rclistfrom": "Visa ändringar från och med $2 $3",
+       "rclistfrom": "Visa nya ändringar från och med $2 $3",
        "rcshowhideminor": "$1 mindre ändringar",
        "rcshowhideminor-show": "Visa",
        "rcshowhideminor-hide": "Dölj",
        "filetype-mime-mismatch": "Filtillägget \".$1\" matchar inte med den identifierade MIME-typen för filen ($2).",
        "filetype-badmime": "Uppladdning av filer av MIME-typ \"$1\" är inte tillåtet.",
        "filetype-bad-ie-mime": "Kan inte ladda upp denna fil på grund av att Internet Explorer skulle upptäcka att den är \"$1\", vilket är en otillåten och möjligtvis farlig filtyp.",
-       "filetype-unwanted-type": "'''\".$1\"''' är en oönskad filtyp.\n{{PLURAL:$3|Föredragen filtyp|Föredragna filtyper}} är $2.",
-       "filetype-banned-type": "'''\".$1\"''' är inte {{PLURAL:$4|en tillåten filtyp|tillåtna filtyper}}.\n{{PLURAL:$3|Tillåtna filtyper|Tillåten filtyp}} är $2.",
+       "filetype-unwanted-type": "<strong>\".$1\"</strong> är en oönskad filtyp.\n{{PLURAL:$3|Föredragen filtyp|Föredragna filtyper}} är $2.",
+       "filetype-banned-type": "<strong>\".$1\"</strong> är inte {{PLURAL:$4|en tillåten filtyp|tillåtna filtyper}}.\n{{PLURAL:$3|Tillåten filtyp|Tillåtna filtyper}} är $2.",
        "filetype-missing": "Filnamnet saknar ändelse (t ex \".jpg\").",
        "empty-file": "Filen du skickade var tom.",
        "file-too-large": "Filen du skickade var för stor.",
        "filename-tooshort": "Filnamnet är för kort.",
        "filetype-banned": "Denna typ av fil är förbjuden.",
        "verification-error": "Denna fil klarade inte verifieringen.",
-       "hookaborted": "Ändringen du försökte göra avbröts av en extension hook.",
+       "hookaborted": "Ändringen du försökte göra avbröts av ett tillägg.",
        "illegal-filename": "Filnamnet är inte tillåtet.",
        "overwrite": "Det är inte tillåtet att skriva över en befintlig fil.",
        "unknown-error": "Ett okänt fel uppstod.",
        "fileexists": "Det finns redan en fil med detta namn. Titta på <strong>[[:$1]]</strong>, om {{GENDER:|du}} inte är säker på att {{GENDER:|du}} vill ändra den.\n[[$1|thumb]]",
        "filepageexists": "Beskrivningssidan för denna fil har redan skapats på <strong>[[:$1]]</strong>, men just nu finns ingen fil med detta namn.\nDen sammanfattning du skriver här kommer inte visas på beskrivningssidan.\nFör att din sammanfattning ska visas där, så måste du redigera beskrivningssidan manuellt.\n[[$1|thumb]]",
        "fileexists-extension": "En fil med ett liknande namn finns redan: [[$2|thumb]]\n* Namn på den fil du försöker ladda upp: <strong>[[:$1]]</strong>\n* Namn på filen som redan finns: <strong>[[:$2]]</strong>\nVill du möjligen välja ett mer distinkt namn?",
-       "fileexists-thumbnail-yes": "Filen verkar vara en bild med förminskad storlek ''(miniatyrbild)''. [[$1|thumb]]\nVar vänlig kontrollera filen <strong>[[:$1]]</strong>.\nOm det är samma fil i originalstorlek så är det inte nödvändigt att ladda upp en extra miniatyrbild.",
+       "fileexists-thumbnail-yes": "Filen verkar vara en bild med förminskad storlek <em>(miniatyrbild)</em>. [[$1|thumb]]\nVar vänlig kontrollera filen <strong>[[:$1]]</strong>.\nOm det är samma fil i originalstorlek så är det inte nödvändigt att ladda upp en extra miniatyrbild.",
        "file-thumbnail-no": "Filnamnet börjar med <strong>$1</strong>.\nDet verkar vara en bild med förminskad storlek ''(miniatyrbild)''.\nOm du har denna bild i full storlek, ladda då hellre upp den, annars var vänlig och ändra filens namn.",
-       "fileexists-forbidden": "En fil med detta namn existerar redan, och kan inte överskrivas.\nOm du fortfarande vill ladda upp din fil, var god gå tillbaka och välj ett nytt namn. [[File:$1|thumb|center|$1]]",
+       "fileexists-forbidden": "En fil med detta namn existerar redan, och kan inte skrivas över.\nOm du ändå vill ladda upp din fil, gå då tillbaka och använd ett annat namn. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "En fil med detta namn finns redan bland de delade filerna.\nOm du ändå vill ladda upp din fil, gå då tillbaka och använd ett annat namn. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Den uppladdade filen är en exakt kopia av den aktuella versionen av <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Den uppladdade versionen är en exakt kopia av {{PLURAL:$2|en äldre version|äldre versioner}} av <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Denna fil är en dubblett av följande {{PLURAL:$1|fil|filer}}:",
-       "file-deleted-duplicate": "En identisk fil till den här filen ([[:$1]]) har tidigare raderats. Du bör kontrollera den filens raderingshistorik innan du fortsätter att återuppladda den.",
-       "file-deleted-duplicate-notitle": "En identisk fil till den här filen har tidigare raderats och titeln har undanhållits.\nDu borde be någon som kan se undanhållen fildata att granska situationen innan du försöker ladda upp den.",
+       "file-deleted-duplicate": "En identisk fil till den här filen ([[:$1]]) har tidigare raderats. \nDu bör kontrollera den filens raderingshistorik innan du fortsätter att ladda upp den på nytt.",
+       "file-deleted-duplicate-notitle": "En identisk fil till den här filen har tidigare raderats och titeln har undanhållits.\nDu borde be någon som kan se undanhållen fildata att granska situationen innan du försöker ladda upp den på nytt.",
        "uploadwarning": "Uppladdningsvarning",
        "uploadwarning-text": "Var god och ändra filbeskrivningen nedanför och försök igen.",
        "savefile": "Spara fil",
        "filejournal-fail-dbquery": "Kunde inte uppdatera journaldatabasen för lagringssystemet \"$1\".",
        "lockmanager-notlocked": "Kunde inte låsa upp \"$1\"; den är inte låst.",
        "lockmanager-fail-closelock": "Kunde inte att stänga låsfilen för \"$1\".",
-       "lockmanager-fail-deletelock": "Kunde inte att radera låsfilen för \"$1\".",
-       "lockmanager-fail-acquirelock": "Kunde inte skaffa låset för \"$1\".",
-       "lockmanager-fail-openlock": "Kunde inte att öppna låsfilen för \"$1\".",
-       "lockmanager-fail-releaselock": "Kunde inte att frigöra låset för \"$1\".",
+       "lockmanager-fail-deletelock": "Kunde inte radera låsfilen för \"$1\".",
+       "lockmanager-fail-acquirelock": "Kunde inte skaffa lås för \"$1\".",
+       "lockmanager-fail-openlock": "Kunde inte öppna låsfilen för \"$1\".",
+       "lockmanager-fail-releaselock": "Kunde inte att frigöra lås för \"$1\".",
        "lockmanager-fail-db-bucket": "Kunde inte kontakta tillräckligt många låsdatabaser i hinken $1.",
        "lockmanager-fail-db-release": "Kunde inte frigöra låsen på databasen $1 .",
        "lockmanager-fail-svr-acquire": "Kunde inte erhålla lås på servern $1 .",
-       "lockmanager-fail-svr-release": "Kunde inte frigöra låsen på servern $1.",
+       "lockmanager-fail-svr-release": "Kunde inte frigöra lås på servern $1.",
        "zip-file-open-error": "Ett fel inträffade när filen öppnades för en ZIP-kontroll.",
        "zip-wrong-format": "Den angivna filen var inte en ZIP-fil.",
-       "zip-bad": "Filen är en skadad eller annars oläsbar ZIP fil.\nDen kan inte säkerhetskontrolleras ordentligt.",
-       "zip-unsupported": "Filen är en ZIP-fil som använder ZIP funktioner som inte stöds av MediaWiki.\nDen kan inte säkerhetskontrolleras ordentligt.",
+       "zip-bad": "Filen är en skadad eller annars oläsbar ZIP-fil.\nDen kan inte säkerhetskontrolleras ordentligt.",
+       "zip-unsupported": "Filen är en ZIP-fil som använder ZIP-funktioner som inte stöds av MediaWiki.\nDen kan inte säkerhetskontrolleras ordentligt.",
        "uploadstash": "Temporära lagringsytan för uppladdningar",
        "uploadstash-summary": "Denna sida ger tillgång till filer som är uppladdade (eller håller på att laddas upp) men som ännu inte är publicerade till wikin. Dessa filer är inte synliga för någon annan än den användare som laddade upp dem.",
        "uploadstash-clear": "Rensa temporärt lagrade filer",
        "uploadstash-errclear": "Rensning av filerna misslyckades.",
        "uploadstash-refresh": "Uppdatera listan över filer",
        "uploadstash-thumbnail": "visa miniatyr",
+       "uploadstash-exception": "Kunde inte lagra uppladdning i stash ($1): \"$2\".",
        "invalid-chunk-offset": "Ogiltig segmentsförskjutning",
        "img-auth-accessdenied": "Åtkomst nekad",
        "img-auth-nopathinfo": "PATH_INFO saknas.\nDin server är inte inställd för att ge denna information.\nDen kan vara CGI-baserad och stöder inte img_auth.\n[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization Se bildbehörighet.]",
        "filerevert-legend": "Återställ fil",
        "filerevert-intro": "Du återställer '''[[Media:$1|$1]]''' till [$4 versionen från $2 kl. $3].",
        "filerevert-comment": "Anledning:",
-       "filerevert-defaultcomment": "Återställer till versionen från $1 kl. $2 ($3)",
+       "filerevert-defaultcomment": "Återställd till versionen från $1, kl. $2 ($3)",
        "filerevert-submit": "Återställ",
        "filerevert-success": "'''[[Media:$1|$1]]''' har återställts till [$4 versionen från $2 kl. $3].",
        "filerevert-badversion": "Det finns ingen tidigare version av filen från den angivna tidpunkten.",
+       "filerevert-identical": "Den aktuella versionen av filen är redan identisk med den valda.",
        "filedelete": "Radera $1",
        "filedelete-legend": "Radera fil",
        "filedelete-intro": "Du håller på att radera filen '''[[Media:$1|$1]]''' tillsammans med hela dess historik.",
        "apisandbox-api-disabled": "API är inaktiverat på denna webbplats.",
        "apisandbox-intro": "Använd den här sidan för att experimentera med <strong>MediaWikis API för webbtjänster</strong>.\nSe [[mw:API:Main page|API-dokumentationen]] för ytterligare detaljer kring API-användningen. Exempel: [https://www.mediawiki.org/wiki/API#A_simple_example få innehållet från en huvudsida]. Välj en handling för att se fler exempel.\n\nObservera att även om detta är en sandlåda kan handlingar du utför på denna sida påverka wikin.",
        "apisandbox-fullscreen": "Utvidga panel",
-       "apisandbox-fullscreen-tooltip": "Utvidga sandlådspanelen för att fylla webbläsarens fönster.",
+       "apisandbox-fullscreen-tooltip": "Utvidga sandlådspanelen till att fylla webbläsarens fönster.",
        "apisandbox-unfullscreen": "Visa sida",
        "apisandbox-unfullscreen-tooltip": "Förminska sandlådspanelen så MediaWikis navigeringslänkar syns.",
        "apisandbox-submit": "Utför begäran",
        "listgrouprights-namespaceprotection-header": "Namnrymdsbegränsningar",
        "listgrouprights-namespaceprotection-namespace": "Namnrymd",
        "listgrouprights-namespaceprotection-restrictedto": "Rättighet(er) som låter användare redigera",
-       "listgrants": "Beviljanden",
+       "listgrants": "Behörigheter",
        "listgrants-summary": "Följande är en lista över behörigheter med deras associerade tillgång till användarrättigheter. Användare kan tillåta applikationer att använda deras konto, men med begränsad åtkomst baserat på de behörigheter användaren gav applikationen. En applikation som agerar på uppdrag av en användare kan i praktiken inte använda rättigheter som den användaren saknar.\nDet kan finnas [[{{MediaWiki:Listgrouprights-helppage}}|ytterligare information]] om individuella rättigheter.",
        "listgrants-grant": "Behörighet",
        "listgrants-rights": "Rättigheter",
        "trackingcategories-nodesc": "Ingen beskrivning tillgänglig.",
        "trackingcategories-disabled": "Kategorin är inaktiverad",
        "mailnologin": "Ingen adress att skicka till",
-       "mailnologintext": "För att kunna skicka e-post till andra användare, måste du vara [[Special:UserLogin|inloggad]] och ha angivit en korrekt e-postadress i dina [[Special:Preferences|användarinställningar]].",
+       "mailnologintext": "För att kunna skicka e-post till andra användare måste du vara [[Special:UserLogin|inloggad]] och ha angivit en korrekt e-postadress i dina [[Special:Preferences|användarinställningar]].",
        "emailuser": "Skicka e-post till den här användaren",
        "emailuser-title-target": "Skicka e-post till denna {{GENDER:$1|användare}}",
        "emailuser-title-notarget": "E-postanvändare",
        "watchlistanontext": "Du måste logga in för att se eller redigera din bevakningslista.",
        "watchnologin": "Inte inloggad",
        "addwatch": "Lägg till i bevakningslistan",
-       "addedwatchtext": "\"[[:$1]]\" har lagts till i din [[Special:Watchlist|bevakningslista]].",
+       "addedwatchtext": "\"[[:$1]]\" och dess diskussionssida har lagts till i din [[Special:Watchlist|bevakningslista]].",
+       "addedwatchtext-talk": "\"[[:$1]]\" och dess associerade sida har lagts till i din [[Special:Watchlist|bevakningslista]].",
        "addedwatchtext-short": "Sidan \"$1\" har lagts till i din bevakningslista.",
        "removewatch": "Ta bort från bevakningslistan",
        "removedwatchtext": "\"[[:$1]]\" och dess diskussionssida har tagits bort från [[Special:Watchlist|din bevakningslista]].",
+       "removedwatchtext-talk": "\"[[:$1]]\" och dess associerade sida har tagits bort från din [[Special:Watchlist|bevakningslista]].",
        "removedwatchtext-short": "Sidan \"$1\" har tagits bort från din bevakningslista.",
        "watch": "Bevaka",
        "watchthispage": "Bevaka denna sida",
        "watchlist-hide": "Dölj",
        "watchlist-submit": "Visa",
        "wlshowtime": "Tidsperiod att visa:",
-       "wlshowhideminor": "mindre redigering",
+       "wlshowhideminor": "mindre redigeringar",
        "wlshowhidebots": "robotar",
        "wlshowhideliu": "registrerade användare",
        "wlshowhideanons": "anonyma användare",
        "exbeforeblank": "innehåll före tömning var: \"$1\"",
        "delete-confirm": "Radera \"$1\"",
        "delete-legend": "Radera",
-       "historywarning": "<strong>Varning:</strong> Sidan du håller på att radera har en historik med ungefär $1 {{PLURAL:$1|version|versioner}}:",
+       "historywarning": "<strong>Varning:</strong> Sidan du håller på att radera har en historik med $1 {{PLURAL:$1|version|versioner}}:",
        "historyaction-submit": "Visa",
        "confirmdeletetext": "Du håller på att ta bort en sida med hela dess historik.\nBekräfta att du förstår vad du håller på med och vilka konsekvenser detta leder till, och att du följer [[{{MediaWiki:Policy-url}}|riktlinjerna]].",
        "actioncomplete": "Genomfört",
        "rollbacklinkcount-morethan": "rulla tillbaka mer än $1 {{PLURAL:$1|redigering|redigeringar}}",
        "rollbackfailed": "Tillbakarullning misslyckades",
        "rollback-missingparam": "Nödvändiga parametrar i begäran saknas.",
+       "rollback-missingrevision": "Kunde inte läsa in sidversionsdata.",
        "cantrollback": "Det gick inte att rulla tillbaka, då sidan endast redigerats av en användare.",
        "alreadyrolled": "Det gick inte att rulla tillbaka den senaste redigeringen av [[User:$2|$2]] ([[User talk:$2|diskussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) på sidan [[:$1|$1]]. Någon annan har redan rullat tillbaka eller redigerat sidan.\n\nSidan ändrades senast av [[User:$3|$3]] ([[User talk:$3|diskussion]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]).",
        "editcomment": "Redigeringskommentaren var: <em>$1</em>.",
        "revertpage": "Återställde redigeringar av  [[Special:Contributions/$2|$2]] ([[User talk:$2|användardiskussion]]) till senaste versionen av [[User:$1|$1]]",
        "revertpage-nouser": "Återställde redigeringar av en dold användare till den senaste versionen av {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Återställde ändringar av $1;\nändrade tillbaka till senaste version av $2.",
-       "rollback-success-notify": "Återställde ändringar av $1;\nändrade tillbaka till senaste version av $2. [$3 Visa ändringar]",
+       "rollback-success-notify": "Återställde ändringar av $1;\nändrade tillbaka till senaste sidversion av $2. [$3 Visa ändringar]",
        "sessionfailure-title": "Sessionsfel",
        "sessionfailure": "Något med din session som inloggad är på tok. Din begärda åtgärd har avbrutits, för att förhindra att någon kapar din session. Klicka på \"Tillbaka\" i din webbläsare och ladda om den sida du kom ifrån. Försök sedan igen.",
        "changecontentmodel": "Ändra innehållsmodell för en sida",
        "logentry-contentmodel-change-revertlink": "återställ",
        "logentry-contentmodel-change-revert": "återställ",
        "protectlogpage": "Skrivskyddslogg",
-       "protectlogtext": "Detta är en lista över applicerande och borttagande av skrivskydd.\nSe [[Special:ProtectedPages|listan över skyddade sidor]] för listan över aktiva sidskydd.",
+       "protectlogtext": "Nedan är en lista över ändringar av sidskydd.\nSe [[Special:ProtectedPages|listan över skyddade sidor]] för en förteckning över de sidskydd som för närvarande är aktiva.",
        "protectedarticle": "skrivskyddade \"[[$1]]\"",
        "modifiedarticleprotection": "ändrade skyddsnivån för \"[[$1]]\"",
        "unprotectedarticle": "tog bort skrivskydd från \"[[$1]]\"",
        "protect-default": "Tillåt alla användare",
        "protect-fallback": "Kräv \"$1\"-behörighet",
        "protect-level-autoconfirmed": "Blockera nya och oregistrerade användare",
-       "protect-level-sysop": "Enbart administratörer",
+       "protect-level-sysop": "Tillåt endast administratörer",
        "protect-summary-cascade": "kaskaderande",
        "protect-expiring": "upphör den $1 (UTC)",
        "protect-expiring-local": "upphör $1",
        "protect-expiry-indefinite": "på obestämd tid",
        "protect-cascade": "Skydda sidor som är inkluderade i den här sidan (kaskaderande skydd)",
        "protect-cantedit": "Du kan inte ändra skrivskyddsnivån för den här sidan, eftersom du inte har behörighet att redigera den.",
-       "protect-othertime": "Annan tidsperiod:",
-       "protect-othertime-op": "annan tidsperiod",
+       "protect-othertime": "Annan tid:",
+       "protect-othertime-op": "annan tid",
        "protect-existing-expiry": "Gällande varaktighet: $2, kl. $3",
        "protect-existing-expiry-infinity": "Gällande varaktighet: oändlig",
        "protect-otherreason": "Annan/ytterligare anledning:",
        "protect-dropdown": "*Vanliga anledningar för skrivskydd\n** Upprepad vandalisering\n** Upprepad spam\n** Redigeringskrig\n** Sida med många besökare",
        "protect-edit-reasonlist": "Redigera skrivskyddsanledningar",
        "protect-expiry-options": "1 timme:1 hour,1 dygn:1 day,1 vecka:1 week,2 veckor:2 weeks,1 månad:1 month,3 månader:3 months,6 månader:6 months,1 år:1 year,oändlig:infinite",
-       "restriction-type": "Typ av skydd:",
+       "restriction-type": "Behörighet:",
        "restriction-level": "Skyddsnivå:",
        "minimum-size": "Minsta storlek",
        "maximum-size": "Största storlek:",
        "undeletepagetext": "Följande {{PLURAL:$1|sida har blivit raderad|$1 sidor har blivit raderade}} men finns fortfarande i arkivet och kan återställas.\nArkivet kan ibland rensas ut.",
        "undelete-fieldset-title": "Återställ sidversioner",
        "undeleteextrahelp": "För att återställa sidans hela historik, lämna alla rutor oifyllda och klicka på '''''{{int:undeletebtn}}'''''.\nFör att göra en selektiv återställning, kryssa i de rutor som hör till de versioner som ska återställas, och klicka på '''''{{int:undeletebtn}}'''''.",
-       "undeleterevisions": "$1 {{PLURAL:$1|version|versioner}} raderade",
-       "undeletehistory": "Om du återställer sidan kommer alla tidigare versioner att återfinnas i versionshistoriken.\nOm en ny sida med samma namn har skapats sedan sidan raderades, kommer den återskapade historiken automatiskt att återfinnas i den äldre historiken.",
+       "undeleterevisions": "$1 {{PLURAL:$1|sidversion|sidversioner}} raderade",
+       "undeletehistory": "Om du återställer sidan kommer alla tidigare sidversioner att återfinnas i versionshistoriken.\nOm en ny sida med samma namn har skapats sedan sidan raderades, kommer den återskapade historiken automatiskt att återfinnas i den äldre historiken.",
        "undeleterevdel": "Återställningen kan inte utföras om den resulterar i att den senaste versionen är delvis borttagen.\nI sådana fall måste du se till att den senaste raderade versionen inte är ikryssad, eller att den inte är dold.",
        "undeletehistorynoadmin": "Den här sidan har blivit raderad. Anledningen till detta anges i sammanfattningen nedan, tillsammans med uppgifter om de användare som redigerat sidan innan den raderades. Enbart administratörerna har tillgång till den raderade texten.",
        "undelete-revision": "Raderad version av $1 (från den $4 kl. $5) av $3.",
        "undeleterevision-missing": "Versionen finns inte eller är felaktig. Versionen kan ha återställts eller tagits bort från arkivet, du kan också ha följt en felaktig länk.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|En sidversion|$1 sidversioner}} kunde inte återställas, eftersom dess <code>rev_id</code> redan användes.",
        "undelete-nodiff": "Ingen tidigare version hittades.",
        "undeletebtn": "Återställ",
        "undeletelink": "visa/återställ",
        "undeletedrevisions": "{{PLURAL:$1|en version återställd|$1 versioner återställda}}",
        "undeletedrevisions-files": "$1 {{PLURAL:$1|version|versioner}} och $2 {{PLURAL:$2|fil|filer}} återställda",
        "undeletedfiles": "{{PLURAL:$1|en fil återställd|$1 filer återställda}}",
-       "cannotundelete": "Återställning misslyckades:\n$1",
+       "cannotundelete": "En del eller alla återställningar misslyckades:\n$1",
        "undeletedpage": "'''$1 har återställts'''\n\nSe [[Special:Log/delete|raderingsloggen]] för en förteckning över de senaste raderingarna och återställningarna.",
        "undelete-header": "Se [[Special:Log/delete|raderingsloggen]] för nyligen raderade sidor.",
        "undelete-search-title": "Sök efter raderade sidor",
        "undelete-search-prefix": "Sidor som börjar med:",
        "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.",
-       "undelete-bad-store-key": "Filversionen med tidsstämpeln $1 kan inte återställas: filen saknades före radering.",
+       "undelete-filename-mismatch": "Filversionen med tidsstämpeln $1 kan inte återställas: Filnamnet stämmer inte.",
+       "undelete-bad-store-key": "Filversionen med tidsstämpeln $1 kan inte återställas: Filen saknades före radering.",
        "undelete-cleanup-error": "Fel vid radering av den oanvända arkivfilen \"$1\".",
        "undelete-missing-filearchive": "Filen med arkiv-ID $1 kunde inte återställas eftersom den inte finns i databasen. Filen kanske redan har återställts.",
        "undelete-error": "Kunde inte återställa sidan",
        "undelete-error-short": "Fel vid filåterställning: $1",
-       "undelete-error-long": "Fel inträffade när vid återställning av filen:\n\n$1",
-       "undelete-show-file-confirm": "Är du säker på att du vill visa en raderad version av filen \"<nowiki>$1</nowiki>\" från den $2 kl $3?",
+       "undelete-error-long": "Fel inträffade vid återställning av filen:\n\n$1",
+       "undelete-show-file-confirm": "Är du säker på att du vill visa en raderad version av filen \"<nowiki>$1</nowiki>\" från den $2 kl. $3?",
        "undelete-show-file-submit": "Ja",
        "namespace": "Namnrymd:",
        "invert": "Invertera val",
        "tooltip-namespace_association": "Markera denna ruta för att även inkludera diskussions- eller ämnesnamnrymden som är associerad med den valda namnrymden",
        "blanknamespace": "(Huvudnamnrymden)",
        "contributions": "{{GENDER:$1|Användarbidrag}}",
-       "contributions-title": "Bidrag av $1",
+       "contributions-title": "Användarbidrag av $1",
        "mycontris": "Bidrag",
        "anoncontribs": "Bidrag",
        "contribsub2": "För {{GENDER:$3|$1}} ($2)",
        "sp-contributions-newbies-sub": "Från nya konton",
        "sp-contributions-newbies-title": "Bidrag från nya konton",
        "sp-contributions-blocklog": "blockeringslogg",
-       "sp-contributions-suppresslog": "undanhållna användarbidrag",
-       "sp-contributions-deleted": "raderade användarbidrag",
+       "sp-contributions-suppresslog": "censurerade {{GENDER:$1|användarbidrag}}",
+       "sp-contributions-deleted": "raderade {{GENDER:$1|användarbidrag}}",
        "sp-contributions-uploads": "uppladdningar",
        "sp-contributions-logs": "loggar",
        "sp-contributions-talk": "diskussion",
        "sp-contributions-userrights": "hantering av användarrättigheter",
-       "sp-contributions-blocked-notice": "Användaren är blockerad.\nOrsaken till senaste blockeringen kan ses nedan:",
+       "sp-contributions-blocked-notice": "Användaren är blockerad.\nDen senaste posten i blockeringsloggen visas nedan som referens:",
        "sp-contributions-blocked-notice-anon": "Denna IP-adress är för närvarande blockerad.\nDen senaste posten i blockeringsloggen visas nedan som referens:",
        "sp-contributions-search": "Sök efter användarbidrag",
        "sp-contributions-username": "IP-adress eller användarnamn:",
        "sp-contributions-toponly": "Visa endast aktuella sidversioner",
        "sp-contributions-newonly": "Visa endast redigeringar där sidor skapas",
-       "sp-contributions-hideminor": "Dölj mindre ändringar",
+       "sp-contributions-hideminor": "Dölj mindre redigeringar",
        "sp-contributions-submit": "Sök",
        "whatlinkshere": "Vad som länkar hit",
        "whatlinkshere-title": "Sidor som länkar till \"$1\"",
        "whatlinkshere-page": "Sida:",
-       "linkshere": "Följande sidor länkar till '''[[:$1]]''':",
-       "nolinkshere": "Inga sidor länkar till '''[[:$1]]'''.",
-       "nolinkshere-ns": "Inga sidor i den angivna namnrymden länkar till '''[[:$1]]'''.",
+       "linkshere": "Följande sidor länkar till <strong>[[:$1]]</strong>:",
+       "nolinkshere": "Inga sidor länkar till <strong>[[:$1]]</strong>.",
+       "nolinkshere-ns": "Inga sidor i den angivna namnrymden länkar till <strong>[[:$1]]</strong>.",
        "isredirect": "omdirigeringssida",
        "istemplate": "inkluderad som mall",
        "isimage": "fillänk",
        "ipbemailban": "Hindra användaren från att skicka e-post",
        "ipbenableautoblock": "Blockera automatiskt den IP-adress som användaren använde senast, samt alla adresser som användaren försöker redigera ifrån",
        "ipbsubmit": "Blockera användaren",
-       "ipbother": "Annan tidsperiod:",
+       "ipbother": "Annan tid:",
        "ipboptions": "2 timmar:2 hours,1 dygn:1 day,3 dygn:3 days,1 vecka:1 week,2 veckor:2 weeks,1 månad:1 month,3 månader:3 months,6 månader:6 months,1 år:1 year,oändlig:infinite",
        "ipbhidename": "Dölj användarnamnet från redigeringar och listor",
        "ipbwatchuser": "Bevaka användarens användarsida och diskussionssida",
        "createaccountblock": "kontoregistrering blockerad",
        "emailblock": "e-post blockerad",
        "blocklist-nousertalk": "kan inte redigera sin egen diskussionssida",
-       "ipblocklist-empty": "Listan över blockerade IP-adresser är tom.",
+       "ipblocklist-empty": "Listan över blockeringar är tom.",
        "ipblocklist-no-results": "Den angivna IP-adressen eller användaren är inte blockerad.",
        "blocklink": "blockera",
        "unblocklink": "ta bort blockering",
        "blocklogpage": "Blockeringslogg",
        "blocklog-showlog": "Denna användare har blivit blockerad tidigare.\nBlockeringsloggen är tillgänglig nedan som referens:",
        "blocklog-showsuppresslog": "Denna användare har tidigare blivit blockerad och dold.\nUndanhållandeloggen visas nedan för referens:",
-       "blocklogentry": "blockerade [[$1]] med blockeringstid på $2 $3",
+       "blocklogentry": "blockerade [[$1]] med en varaktighet på $2 $3",
        "reblock-logentry": "ändrade blockeringsinställningar för [[$1]] med en varaktighet på $2 $3",
        "blocklogtext": "Detta är en logg över blockeringar och avblockeringar.\nAutomatiskt blockerade IP-adresser listas ej.\nSe [[Special:BlockList|blockeringslistan]] för en översikt av gällande blockeringar.",
-       "unblocklogentry": "tog bort blockering av \"$1\"",
+       "unblocklogentry": "tog bort blockering av $1",
        "block-log-flags-anononly": "bara oinloggade",
        "block-log-flags-nocreate": "hindrar kontoregistrering",
        "block-log-flags-noautoblock": "utan automatblockering",
        "block-log-flags-angry-autoblock": "utökad automatblockering aktiverad",
        "block-log-flags-hiddenname": "användarnamn dolt",
        "range_block_disabled": "Möjligheten för administratörer att blockera intervall av IP-adresser har stängts av.",
-       "ipb_expiry_invalid": "Ogiltig varaktighetstid.",
+       "ipb_expiry_invalid": "Ogiltig utgångstid.",
        "ipb_expiry_old": "Utgångstiden har redan passerat.",
        "ipb_expiry_temp": "För att dölja användarnamnet måste blockeringen vara permanent.",
        "ipb_hide_invalid": "Kan inte undanhålla detta konto; det har fler än {{PLURAL:$1|en redigering|$1 redigeringar}}.",
        "lockdbtext": "En låsning av databasen hindrar alla användare från att redigera sidor, ändra inställningar och andra saker som kräver ändringar i databasen.\nBekräfta att du verkligen vill göra detta, och att du kommer att låsa upp databasen när underhållet är utfört.",
        "unlockdbtext": "Om du låser upp databasen kommer alla användare att åter kunna redigera sidor, ändra sina inställningar och så vidare. Bekräfta att du vill göra detta.",
        "lockconfirm": "Ja, jag vill verkligen låsa databasen.",
-       "unlockconfirm": "Ja, jag vill låsa upp databasen.",
+       "unlockconfirm": "Ja, jag vill verkligen låsa upp databasen.",
        "lockbtn": "Lås databasen",
        "unlockbtn": "Lås upp databasen",
        "locknoconfirm": "Du har inte bekräftat låsningen.",
        "moveuserpage-warning": "'''Varning:''' Du håller på att flytta en användarsida. Observera att endast sidan kommer att flyttas och att användaren ''inte'' kommer att byta namn.",
        "movecategorypage-warning": "<strong>Varning:</strong> Du är på väg att flytta en kategorisida. Observera att endast sidan kommer att flyttas och eventuella sidor i den gamla kategorin kommer <em>inte</em> att kategoriseras om till den nya kategorin.",
        "movenologintext": "För att flytta en sida måste du vara registrerad användare och [[Special:UserLogin|inloggad]].",
-       "movenotallowed": "Du har inte behörighet att flytta sidor på den här wikin.",
+       "movenotallowed": "Du har inte behörighet att flytta sidor.",
        "movenotallowedfile": "Du har inte tillåtelse att flytta filer.",
        "cant-move-user-page": "Du har inte behörighet att flytta användarsidor (bortsett från undersidor).",
        "cant-move-to-user-page": "Du har inte behörighet att flytta en sida till en användarsida (förutom till en användarundersida).",
        "cant-move-category-page": "Du har inte behörighet att flytta kategorisidor.",
-       "cant-move-to-category-page": "Du har inte behörighet att en sida till en kategorisida.",
+       "cant-move-to-category-page": "Du har inte behörighet att flytta en sida till en kategorisida.",
        "newtitle": "Ny titel:",
        "move-watch": "Bevaka denna sida",
        "movepagebtn": "Flytta sidan",
        "immobile-source-page": "Denna sida är inte flyttbar.",
        "immobile-target-page": "Kan inte flytta till det målnamnet.",
        "bad-target-model": "Den önskade destinationen använder en annan innehållsmodell. Kan inte konvertera från $1 till $2.",
-       "imagenocrossnamespace": "Kan inte flytta filer till andra namnrymder än filnamnrymden",
-       "nonfile-cannot-move-to-file": "Kan inte flytta icke-fil till filnamnrymden",
-       "imagetypemismatch": "Den nya filändelsen motsvarar inte filtypen",
-       "imageinvalidfilename": "Önskat filnamn är ogiltigt",
+       "imagenocrossnamespace": "Kan inte flytta filer till andra namnrymder än filnamnrymden.",
+       "nonfile-cannot-move-to-file": "Kan inte flytta icke-fil till filnamnrymden.",
+       "imagetypemismatch": "Den nya filändelsen motsvarar inte filtypen.",
+       "imageinvalidfilename": "Önskat filnamn är ogiltigt.",
        "fix-double-redirects": "Uppdatera omdirigeringar som leder till den gamla titeln",
        "move-leave-redirect": "Lämna kvar en omdirigering",
        "protectedpagemovewarning": "'''Varning:''' Den här sidan har låsts så att endast användare med administratörsrättigheter kan flytta den.\nDen senaste loggposten tillhandahålls nedan som referens:",
        "exporttext": "Du kan exportera text och versionshistorik för en eller flera sidor i XML-format.\nFilen kan sedan importeras till en annan MediaWiki-wiki med hjälp av sidan [[Special:Import|importera]].\n\nExportera sidor genom att skriva in sidtitlarna i rutan här nedan.\nSkriv en titel per rad och välj om du du vill exportera alla versioner av texten med sidhistorik, eller om du enbart vill exportera den nuvarande versionen med information om den senaste redigeringen.\n\nI det senare fallet kan du även använda en länk, exempel [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] för sidan \"[[{{MediaWiki:Mainpage}}]]\".",
        "exportall": "Exportera alla sidor",
        "exportcuronly": "Inkludera endast den nuvarande versionen, inte hela historiken",
-       "exportnohistory": "----\n'''OBS:''' export av fullständig sidhistorik med hjälp av detta formulär har stängts av på grund av prestandaskäl.",
+       "exportnohistory": "----\n<strong>OBS:</strong> Export av fullständig sidhistorik med hjälp av detta formulär har stängts av på grund av prestandaskäl.",
        "exportlistauthors": "Inkludera en fullständig lista över bidragsgivare för varje sida",
        "export-submit": "Exportera",
        "export-addcattext": "Lägg till sidor från kategori:",
        "export-addcat": "Lägg till",
        "export-addnstext": "Lägg till sidor från namnrymd:",
        "export-addns": "Lägg till",
-       "export-download": "Ladda ner som fil",
+       "export-download": "Spara som fil",
        "export-templates": "Inkludera mallar",
        "export-pagelinks": "Inkludera länkade sidor till ett djup på:",
        "export-manual": "Lägg till sidor manuellt:",
        "allmessagesname": "Namn",
        "allmessagesdefault": "Standardtext",
        "allmessagescurrent": "Nuvarande text",
-       "allmessagestext": "Detta är en lista över alla meddelanden i namnrymden MediaWiki.\nBesök [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki Localisation] eller [https://translatewiki.net translatewiki.net] om du vill bidra till översättningen av MediaWiki.",
+       "allmessagestext": "Detta är en lista över alla systemmeddelanden i namnrymden MediaWiki.\nBesök [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki Localisation] eller [https://translatewiki.net translatewiki.net] om du vill bidra till översättningen av MediaWiki.",
        "allmessagesnotsupportedDB": "Den här sidan kan inte användas eftersom '''$wgUseDatabaseMessages''' är avstängd.",
        "allmessages-filter-legend": "Filtrera",
        "allmessages-filter": "Filtrera efter anpassningsgrad:",
        "thumbnail_image-type": "Bildtypen stöds inte",
        "thumbnail_gd-library": "Inkomplett GD library konfigurering: saknar funktionen $1",
        "thumbnail_image-missing": "Fil verkar saknas: $1",
-       "thumbnail_image-failure-limit": "Det har nyligen förekommit alltför många misslyckade ($1 eller fler) försök skapa den här miniatyrbilden. Försök igen senare.",
+       "thumbnail_image-failure-limit": "Det har nyligen förekommit alltför många misslyckade försök ($1 eller fler) att skapa den här miniatyrbilden. Försök igen senare.",
        "import": "Importera sidor",
        "importinterwiki": "Importera från en annan wiki",
        "import-interwiki-text": "Välj en wiki och sidtitel att importera.\nVersionshistorikens datum och redigerare kommer att bevaras.\nAll importering från andra wikis listas i [[Special:Log/import|importloggen]].",
        "import-mapping-subpage": "Importera som undersidor till följande sida:",
        "import-upload-filename": "Filnamn:",
        "import-comment": "Kommentar:",
-       "importtext": "Var god exportera filen från ursprungs-wikin med hjälp av [[Special:Export|exporteringsverktyget]].\nSpara den på din dator och ladda upp den här.",
+       "importtext": "Var god exportera filen frånkällwikin med hjälp av [[Special:Export|exporteringsverktyget]].\nSpara den på din dator och ladda upp den här.",
        "importstart": "Importerar sidor....",
        "import-revision-count": "$1 {{PLURAL:$1|version|versioner}}",
        "importnopages": "Det finns inga sidor att importera.",
        "importuploaderrorpartial": "Uppladdningen av importfilen misslyckades. Bara en del av filen laddades upp.",
        "importuploaderrortemp": "Uppladdningen av importfilen misslyckades. En temporär katalog saknas.",
        "import-parse-failure": "Tolkningsfel vid XML-import",
-       "import-noarticle": "Inga sidor att importera!",
+       "import-noarticle": "Ingen sida att importera!",
        "import-nonewrevisions": "Inga sidversioner importerades (alla var antingen redan där eller hoppades över p.g.a. fel).",
        "xml-error-string": "$1 på rad $2, kolumn $3 (byte $4): $5",
        "import-upload": "Ladda upp XML-data",
        "javascripttest-pagetext-unknownaction": "Okänd handling \"$1\".",
        "javascripttest-qunit-intro": "Se [$1 testningsdokumentationen] på mediawiki.org.",
        "tooltip-pt-userpage": "{{GENDER:|Din användarsida}}",
-       "tooltip-pt-anonuserpage": "Användarsida för ip-numret du redigerar från",
+       "tooltip-pt-anonuserpage": "Användarsida för IP-numret du redigerar från",
        "tooltip-pt-mytalk": "{{GENDER:|Din}} diskussionssida",
-       "tooltip-pt-anontalk": "Diskussion om redigeringar från det här ip-numret",
+       "tooltip-pt-anontalk": "Diskussion om redigeringar från det här IP-numret",
        "tooltip-pt-preferences": "{{GENDER:|Dina}} inställningar",
        "tooltip-pt-watchlist": "Listan över sidor du bevakar för ändringar",
        "tooltip-pt-mycontris": "Lista över {{GENDER:|dina}} bidrag",
        "pageinfo-article-id": "Sid-ID",
        "pageinfo-language": "Språk för sidinnehåll",
        "pageinfo-content-model": "Sidinnehållsmodell",
+       "pageinfo-content-model-change": "ändra",
        "pageinfo-robot-policy": "Indexering av robotar",
        "pageinfo-robot-index": "Tillåten",
        "pageinfo-robot-noindex": "Inte tillåten",
        "pageinfo-few-watchers": "Färre än $1 {{PLURAL:$1|bevakare}}",
        "pageinfo-few-visiting-watchers": "Det kan finnas någon bevakande användare som granskar nyliga redigeringar",
        "pageinfo-redirects-name": "Antal omdirigeringar till denna sida",
-       "pageinfo-subpages-name": "Undersidor till denna sida",
+       "pageinfo-subpages-name": "Antal undersidor till denna sida",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|omdirigering|omdirigeringar}}; $3 {{PLURAL:$3|icke-omdirigering|icke-omdirigeringar}})",
        "pageinfo-firstuser": "Sidskapare",
        "pageinfo-firsttime": "Datum när sidan skapades",
        "markaspatrolledtext": "Märk den här sidan som patrullerad",
        "markaspatrolledtext-file": "Märk denna filversion som patrullerad",
        "markedaspatrolled": "Markerad som patrullerad",
-       "markedaspatrolledtext": "Den valda versionen av [[:$1]] har märkts som patrullerad.",
+       "markedaspatrolledtext": "Den valda versionen av [[:$1]] har markerats som patrullerad.",
        "rcpatroldisabled": "Patrullering av Senaste ändringar är avstängd.",
        "rcpatroldisabledtext": "Funktionen \"patrullering av Senaste ändringar\" är tillfälligt avstängd.",
        "markedaspatrollederror": "Kan inte markera som patrullerad",
        "file-info-size": "$1 × $2 pixlar, filstorlek: $3, MIME-typ: $4",
        "file-info-size-pages": "$1 × $2 pixlar, filstorlek: $3, MIME-typ: $4, $5 {{PLURAL:$5|sida|sidor}}",
        "file-nohires": "Det finns ingen version med högre upplösning.",
-       "svg-long-desc": "SVG-fil, grundstorlek: $1 × $2 pixlar, filstorlek: $3",
+       "svg-long-desc": "SVG-fil, standardstorlek: $1 × $2 pixlar, filstorlek: $3",
        "svg-long-desc-animated": "Animerad SVG-fil, standardstorlek $1 × $2 pixlar, filstorlek: $3",
        "svg-long-error": "Felaktig SVG-fil: $1",
        "show-big-image": "Originalfil",
        "exif-gpsspeed-m": "Miles i timmen",
        "exif-gpsspeed-n": "Knop",
        "exif-gpsdestdistance-k": "Kilometer",
-       "exif-gpsdestdistance-m": "Mil",
+       "exif-gpsdestdistance-m": "Miles",
        "exif-gpsdestdistance-n": "Nautiska mil",
        "exif-gpsdop-excellent": "Utmärkt ($1)",
        "exif-gpsdop-good": "Bra ($1)",
        "confirmemail": "Bekräfta e-postadress",
        "confirmemail_noemail": "Du har inte angivit någon giltig e-postadress i dina [[Special:Preferences|inställningar]].",
        "confirmemail_text": "Innan du kan använda {{SITENAME}}s funktioner för e-post måste du bekräfta din e-postadress. Aktivera knappen nedan för att skicka en bekräftelsekod till din e-postadress. Mailet kommer att innehålla en länk, som innehåller en kod. Genom att klicka på den länken eller kopiera den till din webbläsares fönster för webbadresser, bekräftar du att din e-postadress fungerar.",
-       "confirmemail_pending": "En bekräftelsekod har redan skickats till din epostadress. Om du skapade ditt konto nyligen, så kanske du vill vänta några minuter innan du begär en ny kod.",
+       "confirmemail_pending": "En bekräftelsekod har redan skickats till din e-postadress. Om du skapade ditt konto nyligen, så kanske du vill vänta några minuter innan du begär en ny kod.",
        "confirmemail_send": "Skicka bekräftelsekod",
        "confirmemail_sent": "E-post med bekräftelse skickat.",
        "confirmemail_oncreate": "En bekräftelsekod skickades till din epostadress. Koden behövs inte för att logga in, men du behöver koden för att få tillgång till de epostbaserade funktionerna på wikin.",
        "confirmemail_body": "Någon, troligen du, har från IP-adressen $1 registrerat användarkontot \"$2\" med denna e-postadress på {{SITENAME}}.\n\nFör att bekräfta att detta konto verkligen är ditt, och för att aktivera funktionerna för e-post på {{SITENAME}}, öppna denna länk i din webbläsare:\n\n$3\n\nOm det *inte* är du som registrerat kontot, följ denna länk för att avbryta bekräftelsen av e-postadressen:\n\n$5\n\nDenna bekräftelsekod kommer inte att fungera efter $4.",
        "confirmemail_body_changed": "Någon, troligen du, har från IP-adressen $1\nregistrerat användarkontot \"$2\" med denna e-postadress på {{SITENAME}}.\n\nFör att bekräfta att detta konto verkligen är ditt, och för att aktivera\nfunktionerna för e-post på {{SITENAME}}, öppna denna länk i din webbläsare:\n\n$3\n\nOm det *inte* är du som registrerat kontot, följ denna länk\nför att avbryta bekräftelsen av e-postadressen:\n\n$5\n\nDenna bekräftelsekod kommer inte att fungera efter $4.",
        "confirmemail_body_set": "Någon, förmodligen du, från IP-adressen $1,\nhar angivit e-postadressen till kontot \"$2\" till den här adressen på {{SITENAME}}.\n\nFör att bekräfta att kontot verkligen tillhör dig, bör du aktivera e-postfunktionerna på {{SITENAME}}, öppna denna länk i din webbläsare:\n\n$3\n\nOm kontot *inte* tillhör dig, följ den här länken för att avbryta bekräftelsen av e-postadressen:\n\n$5\n\nDenna bekräftelsekod kommer att sluta fungera efter $4.",
-       "confirmemail_invalidated": "Bekräftelsen av e-postadressen har ogiltigförklarats",
+       "confirmemail_invalidated": "Bekräftelsen av e-postadressen har avbrutits",
        "invalidateemail": "Avbryt bekräftelse av e-postadress",
        "notificationemail_subject_changed": "Registrerad e-postadress på {{SITENAME}} har ändrats",
        "notificationemail_subject_removed": "Registrerad e-postadress på {{SITENAME}} har tagits bort",
        "table_pager_prev": "Föregående sida",
        "table_pager_first": "Första sidan",
        "table_pager_last": "Sista sidan",
-       "table_pager_limit": "Visa $1 poster per sida",
+       "table_pager_limit": "Visa $1 objekt per sida",
        "table_pager_limit_label": "Objekt per sida:",
        "table_pager_limit_submit": "Utför",
        "table_pager_empty": "Inga resultat",
        "lag-warn-high": "På grund av omfattande fördröjning i databasen visas kanske inte ändringar nyare än $1 {{PLURAL:$1|sekund|sekunder}} i den här listan.",
        "watchlistedit-normal-title": "Redigera bevakningslista",
        "watchlistedit-normal-legend": "Ta bort sidor från bevakningslistan",
-       "watchlistedit-normal-explain": "Titlar på din bevakningslista visas nedan.\nFör att ta bort en titel, markera rutan bredvid den och klicka på \"{{int:Watchlistedit-normal-submit}}\".\nDu kan också [[Special:EditWatchlist/raw|redigera listan i råformat]].",
+       "watchlistedit-normal-explain": "Sidor på din bevakningslista visas nedan.\nFör att ta bort en sida, markera rutan bredvid den och klicka på \"{{int:Watchlistedit-normal-submit}}\".\nDu kan också [[Special:EditWatchlist/raw|redigera listan i råformat]].",
        "watchlistedit-normal-submit": "Ta bort sidor",
        "watchlistedit-normal-done": "{{PLURAL:$1|1 sida|$1 sidor}} togs bort från din bevakningslista:",
        "watchlistedit-raw-title": "Redigera bevakningslistan i råformat",
        "watchlistedit-raw-legend": "Redigera bevakningslistan i råformat",
-       "watchlistedit-raw-explain": "Titlar på din bevakningslista visas nedan, och kan redigeras genom att lägga till och ta bort från listan;\nen titel per rad.\nNär du är klar klickar du på \"{{int:Watchlistedit-raw-submit}}\".\nDu kan också [[Special:EditWatchlist|använda standardeditorn]].",
+       "watchlistedit-raw-explain": "Sidor på din bevakningslista visas nedan, och kan redigeras genom att lägga till och ta bort från listan;\nen sida per rad.\nNär du är klar klickar du på \"{{int:Watchlistedit-raw-submit}}\".\nDu kan också [[Special:EditWatchlist|använda standardeditorn]].",
        "watchlistedit-raw-titles": "Sidor:",
        "watchlistedit-raw-submit": "Uppdatera bevakningslistan",
        "watchlistedit-raw-done": "Din bevakningslista har uppdaterats.",
        "watchlistedit-clear-title": "Rensa bevakningslistan",
        "watchlistedit-clear-legend": "Rensa bevakningslistan",
        "watchlistedit-clear-explain": "Alla titlar kommer att tas bort från din bevakningslista",
-       "watchlistedit-clear-titles": "Titlar:",
+       "watchlistedit-clear-titles": "Sidor:",
        "watchlistedit-clear-submit": "Rensa bevakningslistan (Detta är permanent!)",
        "watchlistedit-clear-done": "Din bevakningslista har rensats.",
-       "watchlistedit-clear-removed": "{{PLURAL:$1|1 titel|$1 titlar}} togs bort:",
+       "watchlistedit-clear-removed": "{{PLURAL:$1|1 sida|$1 sidor}} togs bort:",
        "watchlistedit-too-many": "Det finns för många sidor att visa här.",
        "watchlisttools-clear": "Rensa bevakningslistan",
        "watchlisttools-view": "Visa relevanta ändringar",
        "tags-activate": "aktivera",
        "tags-deactivate": "inaktivera",
        "tags-hitcount": "$1 {{PLURAL:$1|ändring|ändringar}}",
-       "tags-manage-no-permission": "Du har inte behörighet att hantera förändringstaggar.",
-       "tags-manage-blocked": "Du kan inte hantera ändringsmärken när du är blockerad.",
+       "tags-manage-no-permission": "Du har inte behörighet att hantera förändringsmärken.",
+       "tags-manage-blocked": "Du kan inte hantera förändringsmärken när du är blockerad.",
        "tags-create-heading": "Skapa ett nytt märke",
-       "tags-create-explanation": "Som standard, kommer nyskapade taggar att bli tillgängliga för användning av användare och botar.",
+       "tags-create-explanation": "Som standard, kommer nyskapade märken att bli tillgängliga för användning av användare och botar.",
        "tags-create-tag-name": "Märkesnamn:",
        "tags-create-reason": "Anledning:",
        "tags-create-submit": "Skapa",
        "tags-deactivate-reason": "Anledning:",
        "tags-deactivate-not-allowed": "Det är inte möjligt att inaktivera märket \"$1\".",
        "tags-deactivate-submit": "Inaktivera",
-       "tags-apply-no-permission": "Du har inte behörighet att tillämpa märken på dina ändringar",
+       "tags-apply-no-permission": "Du har inte behörighet att tillämpa ändringsmärken på dina ändringar.",
        "tags-apply-blocked": "Du kan inte ange ändringsmärken med dina ändringar medans du är blockerad.",
        "tags-apply-not-allowed-one": "Märket \"$1\" kan inte läggas till manuellt.",
        "tags-apply-not-allowed-multi": "Följande {{PLURAL:$2|märke|märken}} kan inte läggas till manuellt: $1",
        "log-name-managetags": "Märkeshanteringslogg",
        "log-description-managetags": "Denna sida innehåller administrativa [[Special:Tags|märke]]srelaterade uppgifter. Loggen innehåller bara åtgärder som utförts manuellt av en administratör; märken kan skapas eller raderas av wikins mjukvara utan att en post registreras i loggen.",
        "logentry-managetags-create": "$1 {{GENDER:$2|skapade}} märket \"$4\"",
-       "logentry-managetags-delete": "$1 {{GENDER:$2|raderade}} märket \"$4\" (borttagen från $5 {{PLURAL:$5|version eller loggpost|versioner och/eller loggposter}})",
+       "logentry-managetags-delete": "$1 {{GENDER:$2|raderade}} märket \"$4\" (borttagen från $5 {{PLURAL:$5|sidversion eller loggpost|sidversioner och/eller loggposter}})",
        "logentry-managetags-activate": "$1 {{GENDER:$2|aktiverade}} märket \"$4\" för användning av användare och botar.",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2|inaktiverade}} märket \"$4\" för användning av användare och botar.",
        "log-name-tag": "Märkeslogg",
        "log-description-tag": "Denna sida visar när användare har lagt till eller tagit bort [[Special:Tags|märken]] från individuella sidversioner eller loggposter. Loggen registrerar inte handlingar där märken hanteras i redigeringar, raderingar eller liknande handlingar.",
        "logentry-tag-update-add-revision": "$1 {{GENDER:$2|lade till}} {{PLURAL:$7|märket|märkena}} $6 för sidversionen $4 av sidan $3",
-       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|lade till}} {{PLURAL:$7|märket|märkena}} $6 till loggposten $5 för siden $3",
+       "logentry-tag-update-add-logentry": "$1 {{GENDER:$2|lade till}} {{PLURAL:$7|märket|märkena}} $6 till loggposten $5 för sidan $3",
        "logentry-tag-update-remove-revision": "$1 {{GENDER:$2|tog bort}} {{PLURAL:$9|märket|märkena}} $8 från sidversionen $4 av sidan $3",
        "logentry-tag-update-remove-logentry": "$1 {{GENDER:$2|tog bort}} {{PLURAL:$9|märket|märkena}} $8 från loggposten $5 för sidan $3",
        "logentry-tag-update-revision": "$1 {{GENDER:$2|uppdaterade}} märken på sidversionen $4 för sidan $3 ({{PLURAL:$7|lade till}} $6; {{PLURAL:$9|tog bort}} $8)",
        "api-error-badaccess-groups": "Du får inte ladda upp filer till denna wiki.",
        "api-error-badtoken": "Internt fel: felaktig nyckel.",
        "api-error-blocked": "Du har blockerats från att redigera.",
-       "api-error-copyuploaddisabled": "Uppladdning via URL är inaktiverad på den här servern.",
+       "api-error-copyuploaddisabled": "Uppladdning via URL är inaktiverat på den här servern.",
        "api-error-duplicate": "Det finns redan {{PLURAL:$1|en annan fil|andra filer}} på webbplatsen med samma innehåll.",
        "api-error-duplicate-archive": "Det fanns redan {{PLURAL:$1|en annan fil|några andra filer}} på webbplatsen med samma innehåll, men {{PLURAL:$1|den har|de har}} raderats.",
        "api-error-empty-file": "Filen du skickade var tom.",
        "api-error-filename-tooshort": "Filnamnet är för kort.",
        "api-error-filetype-banned": "Denna typ av fil är förbjuden.",
        "api-error-filetype-banned-type": "$1 är inte {{PLURAL:$4|en tillåten filtyp|tillåtna filtyper}}. {{PLURAL:$3|Tillåten filtyp|Tillåtna filtyper}} är $2.",
-       "api-error-filetype-missing": "Filen saknar en filändelse.",
+       "api-error-filetype-missing": "Filnamnet saknar en filändelse.",
        "api-error-hookaborted": "Ändringen du försökte göra avbröts av en extension hook.",
        "api-error-http": "Internt fel: Det gick inte att ansluta till servern.",
        "api-error-illegal-filename": "Filnamnet är inte tillåtet.",
-       "api-error-internal-error": "Internt fel: något gick fel med bearbetningen av din uppladdning på wikin.",
+       "api-error-internal-error": "Internt fel: Något gick fel med bearbetningen av din uppladdning på wikin.",
        "api-error-invalid-file-key": "Internt fel: filen hittades inte i tillfällig lagring.",
-       "api-error-missingparam": "Internt fel: det saknas parametrar i begäran.",
-       "api-error-missingresult": "Internt fel: kunde inte avgöra om kopieringen lyckades.",
+       "api-error-missingparam": "Internt fel: Det saknas parametrar i begäran.",
+       "api-error-missingresult": "Internt fel: Kunde inte avgöra om kopieringen lyckades.",
        "api-error-mustbeloggedin": "Du måste vara inloggad för att kunna ladda upp filer.",
        "api-error-mustbeposted": "Det finns en bugg i detta program, det använder inte rätt HTTP-metod.",
        "api-error-noimageinfo": "Uppladdningen lyckades, men servern gav oss inte någon information om filen.",
-       "api-error-nomodule": "Internt fel: ingen uppladdningsmodul uppsatt.",
+       "api-error-nomodule": "Internt fel: Ingen uppladdningsmodul uppsatt.",
        "api-error-ok-but-empty": "Internt fel: Inget svar från servern.",
        "api-error-overwrite": "Det är inte tillåtet att skriva över en befintlig fil.",
-       "api-error-ratelimited": "Du försöker ladda upp fler filer inom en kort tidsrymd än denna wiki tillåter.\nFörsök igen om några minuter.",
+       "api-error-ratelimited": "Du försöker ladda upp fler filer inom en kortare tidsrymd än denna wiki tillåter.\nFörsök igen om några minuter.",
        "api-error-stashfailed": "Internt fel: servern kunde inte lagra temporär fil.",
        "api-error-publishfailed": "Internt fel: Servern kunde inte publicera temporär fil.",
        "api-error-stasherror": "Ett fel uppstod under uppladdningen av filen till mellanlagringsfilen.",
        "api-error-stashwrongowner": "Filen du försöker komma åt i det temporära lagringsutrymmet tillhör inte dig.",
        "api-error-stashnosuchfilekey": "Filnyckeln som du försökte komma åt i den temporära lagringsytan existerar inte.",
        "api-error-timeout": "Servern svarade inte inom förväntad tid.",
-       "api-error-unclassified": "Ett okänt fel uppstod",
-       "api-error-unknown-code": "Okänt fel: \"$1\"",
+       "api-error-unclassified": "Ett okänt fel uppstod.",
+       "api-error-unknown-code": "Okänt fel: \"$1\".",
        "api-error-unknown-error": "Internt fel: något gick fel när vi försökte ladda upp din fil.",
-       "api-error-unknown-warning": "Okänd varning: $1",
+       "api-error-unknown-warning": "Okänd varning: \"$1\".",
        "api-error-unknownerror": "Okänt fel: \"$1\".",
        "api-error-uploaddisabled": "Uppladdning är inaktiverad på denna wiki.",
        "api-error-verification-error": "Denna fil kan vara skadad eller har fel filändelse.",
        "expand_templates_output": "Expanderad kod",
        "expand_templates_xml_output": "XML-kod",
        "expand_templates_html_output": "Rå HTML-utdata",
-       "expand_templates_ok": "Expandera",
+       "expand_templates_ok": "OK",
        "expand_templates_remove_comments": "Ta bort kommentarer",
        "expand_templates_remove_nowiki": "Undertryck <nowiki> taggar i resultatet",
        "expand_templates_generate_xml": "Visa parseträd som XML",
        "pagelang-use-default": "Använd standardspråk",
        "pagelang-select-lang": "Välj språk",
        "pagelang-submit": "Skicka",
-       "right-pagelang": "Ändra sidans språk",
+       "right-pagelang": "Ändra sidspråk",
        "action-pagelang": "ändra sidspråket",
        "log-name-pagelang": "Språkändringslogg",
        "log-description-pagelang": "Detta är en logg över ändringar i sidspråken.",
        "special-characters-group-ipa": "IPA",
        "special-characters-group-symbols": "Symboler",
        "special-characters-group-greek": "Grekiska",
-       "special-characters-group-greekextended": "Grekiska utvidgad",
-       "special-characters-group-cyrillic": "Kyrilliskt",
+       "special-characters-group-greekextended": "Utökad grekiska",
+       "special-characters-group-cyrillic": "Kyrilliska",
        "special-characters-group-arabic": "Arabiska",
-       "special-characters-group-arabicextended": "Arabiska utökade",
+       "special-characters-group-arabicextended": "Utökad arabiska",
        "special-characters-group-persian": "Persiska",
        "special-characters-group-hebrew": "Hebreiska",
        "special-characters-group-bangla": "Bengali",
        "special-characters-group-gujarati": "Gujarati",
        "special-characters-group-devanagari": "Devenagari",
        "special-characters-group-thai": "Thai",
-       "special-characters-group-lao": "Laotisk",
+       "special-characters-group-lao": "Laotiska",
        "special-characters-group-khmer": "Khmer",
        "special-characters-title-endash": "tankstreck",
        "special-characters-title-emdash": "långt tankstreck",
        "mw-widgets-dateinput-placeholder-month": "ÅÅÅÅ-MM",
        "mw-widgets-titleinput-description-new-page": "sidan existerar inte ännu",
        "mw-widgets-titleinput-description-redirect": "omdirigerar till $1",
-       "api-error-blacklisted": "Välj en annan beskrivande titel.",
        "sessionmanager-tie": "Kan inte kombinera flera begäransautentiseringstyper: $1.",
        "sessionprovider-generic": "$1-sessioner",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "cookiebaserade sessioner",
        "log-action-filter-block": "Typ av blockering:",
        "log-action-filter-contentmodel": "Typ av innehållsmodellsändring:",
        "log-action-filter-delete": "Typ av radering:",
-       "log-action-filter-import": "Importeringstyp:",
+       "log-action-filter-import": "Typ av importering:",
        "log-action-filter-managetags": "Typ av märkeshanteringsåtgärd:",
-       "log-action-filter-move": "Flyttningstyp:",
+       "log-action-filter-move": "Typ av flyttning:",
        "log-action-filter-newusers": "Typ av kontoskapande:",
        "log-action-filter-patrol": "Typ av patrullering:",
        "log-action-filter-protect": "Typ av skydd:",
-       "log-action-filter-rights": "Typ av rättighetsändring",
-       "log-action-filter-suppress": "Censurtyp",
+       "log-action-filter-rights": "Typ av rättighetsändring:",
+       "log-action-filter-suppress": "Typ av censur:",
        "log-action-filter-upload": "Typ av uppladdning:",
        "log-action-filter-all": "Alla",
        "log-action-filter-block-block": "Blockering",
        "log-action-filter-contentmodel-change": "Ändring av innehållsmodell",
        "log-action-filter-contentmodel-new": "Skapande av sida med icke-standardiserad innehållsmodell",
        "log-action-filter-delete-delete": "Radering av sida",
-       "log-action-filter-delete-restore": "Återställde sida",
+       "log-action-filter-delete-restore": "Återställning av sida",
        "log-action-filter-delete-event": "Radering av logg",
        "log-action-filter-delete-revision": "Radering av sidversion",
        "log-action-filter-import-interwiki": "Interwikiimport",
        "authmanager-email-label": "E-post",
        "authmanager-email-help": "E-postadress",
        "authmanager-realname-label": "Riktigt namn",
-       "authmanager-realname-help": "Användarens riktiga namnet",
+       "authmanager-realname-help": "Användarens riktiga namn",
        "authmanager-provider-password": "Lösenordsbaserad autentisering",
        "authmanager-provider-password-domain": "Lösenord- och domänbaserad autentisering",
        "authmanager-provider-temporarypassword": "Tillfälligt lösenord",
        "linkaccounts-submit": "Länka konton",
        "unlinkaccounts": "Avlänka konton",
        "unlinkaccounts-success": "Kontot avlänkades.",
-       "authenticationdatachange-ignored": "Ändringen av autentiseringsdata hanterades inte. Kanske ingen tillhandahållare har konfigurerats?"
+       "authenticationdatachange-ignored": "Ändringen av autentiseringsdata hanterades inte. Kanske ingen tillhandahållare har konfigurerats?",
+       "userjsispublic": "Observera: JavaScript-undersidor bör inte innehålla konfidentiella uppgifter eftersom de kan ses av andra användare.",
+       "usercssispublic": "Observera: CSS-undersidor bör inte innehålla konfidentiella uppgifter eftersom de kan ses av andra användare."
 }
index c595457..3685734 100644 (file)
@@ -17,7 +17,8 @@
                        "Tchoř",
                        "Timpul",
                        "아라",
-                       "Macofe"
+                       "Macofe",
+                       "Uostofchuodnego"
                ]
        },
        "tog-underline": "Podsztrychńyńcy linkůw:",
        "listingcontinuesabbrev": "ć.d.",
        "index-category": "Indeksowane zajty",
        "noindex-category": "Ńyindeksowane zajty",
-       "broken-file-category": "Zajty ze linkomi lo ńyistniejących ůobrozkůw",
+       "broken-file-category": "Zajty z linkami do niyôbecnych zbiorōw",
        "about": "Uo serwiśe",
        "article": "zajta",
        "newwindow": "(uodwjyro śe we nowym uokńe)",
        "unprotectthispage": "Uodymkńij ta zajta",
        "newpage": "Nowy artikel",
        "talkpage": "Godej uo tym artiklu",
-       "talkpagelinktext": "Dyskusyjo",
+       "talkpagelinktext": "dyskusyjŏ",
        "specialpage": "Szpecyjolno zajta",
        "personaltools": "Perzōnŏlne",
        "articlepage": "Zajta artikla",
        "disclaimers": "Prawne informacyje",
        "disclaimerpage": "Project:Prawne informacyje",
        "edithelp": "Půmoc we půmjyńańy",
-       "mainpage": "Przodńo zajta",
+       "mainpage": "Przodniŏ zajta",
        "mainpage-description": "Przodńo zajta",
        "policy-url": "Project:Prawidła",
        "portal": "Portal używoczůw",
        "newmessageslinkplural": "{{PLURAL:$1|jedno nowina|999=nowiny}}",
        "newmessagesdifflinkplural": "{{PLURAL:$1|uostatńe sprowjyńe|999=uostatńe sprowjyńa}}",
        "youhavenewmessagesmulti": "Mosz nowe powjadůmjyńa: $1",
-       "editsection": "Sprowjej",
+       "editsection": "edytuj",
        "editold": "sprowjej",
        "viewsourceold": "pokoż zdrzůdło",
        "editlink": "sprowjej",
        "viewsourcelink": "zdrzůdłowy tekst",
-       "editsectionhint": "Sprowjej tajla: $1",
+       "editsectionhint": "Edytuj tajlã: $1",
        "toc": "Treść",
        "showtoc": "uobejrzij",
        "hidetoc": "schrůń",
        "nstab-template": "Muster",
        "nstab-help": "Zajta půmocy",
        "nstab-category": "Kategoryjo",
+       "mainpage-nstab": "Przodniŏ zajta",
        "nosuchaction": "Ńy mo takij uoperacyji",
        "nosuchactiontext": "Uoprogramowańy ńy rozpoznowo uoperacyji takij kej podano w URL.",
        "nosuchspecialpage": "Ńy mo takij szpecyjolnyj zajty",
        "resetpass-abort-generic": "Půmjyńańe hasła uostoła zatrzimane bez rozszyrzyńe.",
        "passwordreset": "Wyczyść hasło",
        "passwordreset-disabled": "No tyj wiki zamkńynto resytowańy hasył.",
-       "passwordreset-username": "Mjano używacza:",
+       "passwordreset-username": "Miano ôd używŏcza:",
        "passwordreset-domain": "Domyna:",
        "passwordreset-capture": "Pokozać treść e-brifa?",
        "passwordreset-capture-help": "Eli zaznaczysz to pole, uobejrzisz wjadůmość e-brifa ze hasłym.",
        "passwordreset-emailtext-ip": "Ftoś (cheba Ty, s IP $1)\npado, aże chce informacyji lo konta do {{GRAMMAR:MS.lp{{SITENAME}}}} ($4).\nZe tym ausdrukym sům powjůnzane kůnta:\n$2\n\n{{PLURAL:$3|Tymczasowygo hasła|Tymczasowych hasył}} możno użyć we {{PLURAL:$5|jedyn dźyń|$5 dńi}}.\n\nJak chćołżeś gynał to zrobjyć, to zaloguj śe terozki a podej swoje hasło.\n\nJak ftoś inkszy chćoł nowe hasło abo jak Ci śe przipůmńoło stare a ńy chcysz nowygo, to zignoruj to a używej starygo hasła.",
        "passwordreset-emailelement": "Mjano sprowjorza: \n$1\n\nTymczasowe hasło: \n$2",
        "passwordreset-emailsentemail": "E-brif posłany.",
-       "passwordreset-emailsent-capture": "E-brif posłony, kerego widać niżej.",
-       "passwordreset-emailerror-capture": "Ńy udoło śe posłać wjadomości lo {{GENDER:$2|używocza|używoczki}}: $1",
        "changeemail": "Pomjyno ausdruka e-mail",
        "changeemail-header": "Pomjyno ausduku e-mail",
        "changeemail-no-info": "Muśisz być zalogowany, coby uzyskać bezpostrzedńi dostymp do tyj zajty.",
        "undo-failure": "Sprowjyńo ńy idźe wycofać skuli kůnflikta ze wersyjůma postrzedńimi.",
        "undo-norev": "Sprowjyńo ńy idźe cofnůńć skuli tego, co ńy istńije abo uostoło wyćepane.",
        "undo-summary": "Wycůfańy wersyji $1 naszkryflanej bez [[Special:Contributions/$2|$2]] ([[User talk:$2|godka]])",
-       "cantcreateaccounttitle": "Ńy do śe utworzić kůnta",
        "cantcreateaccount-text": "Tworzyńy kůnta s tygo adresu IP ('''$1''') uostoło zawarte bez użytkowńika [[User:$3|$3]].\n\nSkuli: ''$2''",
        "viewpagelogs": "Uobocz rejery uoperacyji lo tyj zajty",
        "nohistory": "Ta zajta ńy mo swojij historyje sprowjyń.",
        "skin-preview": "podglůnd",
        "datedefault": "Důmyślny",
        "prefs-labs": "Funkcyje \"labs\"",
-       "prefs-user-pages": "Zajty używaczy",
+       "prefs-user-pages": "Zajty ôd używŏczōw",
        "prefs-personal": "Dane używocza",
        "prefs-rc": "Ńydowno pomjyńane",
        "prefs-watchlist": "Pozůrlista",
        "userrights-changeable-col": "Grupy, kere moges wybrać",
        "userrights-unchangeable-col": "Grupy, kerych ńy moges wybrać",
        "group": "Grupa:",
-       "group-user": "Używacze",
-       "group-autoconfirmed": "AutůmatyczÅ\84y zatwjerdzůne używacze",
+       "group-user": "Używŏcze",
+       "group-autoconfirmed": "AutÅ\8dmatycznie przituplowani używÅ\8fcze",
        "group-bot": "Boty",
        "group-sysop": "Admińi",
        "group-bureaucrat": "Bjurokraty",
        "group-suppress": "Rewizorze",
        "group-all": "(wszyjscy)",
-       "group-user-member": "{{GENDER:$1|używacz}}",
-       "group-autoconfirmed-member": "AutůmatyczÅ\84y zatwjerdzůny używacz",
+       "group-user-member": "{{GENDER:$1|używŏcz|używŏczka}}",
+       "group-autoconfirmed-member": "AutÅ\8dmatycznie {{GENDER:$1|przituplowany używÅ\8fcz|przituplowanÅ\8f używÅ\8fczka}}",
        "group-bot-member": "{{GENDER:$1|bot}}",
        "group-sysop-member": "{{GENDER:$1|admin}}",
        "group-bureaucrat-member": "{{GENDER:$1|bjurokrata}}",
        "group-suppress-member": "{{GENDER:$1|rewizůr}}",
-       "grouppage-user": "{{ns:project}}:Używacze",
-       "grouppage-autoconfirmed": "{{ns:project}}:AutůmatyczÅ\84y zatwjerdzyÅ\84i używacze",
+       "grouppage-user": "{{ns:project}}:Używŏcze",
+       "grouppage-autoconfirmed": "{{ns:project}}:AutÅ\8dmatycznie przituplowani używÅ\8fcze",
        "grouppage-bot": "{{ns:project}}:Boty",
        "grouppage-sysop": "{{ns:project}}:Admińistratory",
        "grouppage-bureaucrat": "{{ns:project}}:Bjurokraty",
        "nopagetitle": "Ńy ma sam zajty docelowyj",
        "nopagetext": "Wybrano zajta docelowo ńy istńeje.",
        "pager-newer-n": "{{PLURAL:$1|1 nowšy|$1 nowše|$1 nowšych}}",
-       "pager-older-n": "{{PLURAL:$1|1 staršy|$1 starše|$1 staršych}}",
+       "pager-older-n": "{{PLURAL:$1|1 starszy|$1 starsze|$1 starszych}}",
        "suppress": "Oversight",
        "booksources": "Kśůnžki",
        "booksources-search-legend": "Sznupej za zdrzůdłůma kśůnżkowymi",
        "listusers-noresult": "Ńy znejdźůno žodnygo užytkowńika.",
        "activeusers-hidebots": "Schrůń boty",
        "activeusers-hidesysops": "Schrůń adminy",
-       "activeusers-noresult": "Ńy sům używacze.",
+       "activeusers-noresult": "Niy szło znŏjść żŏdnych używŏczōw",
        "listgrouprights": "Uprawńyńo grup użytkowńikůw",
        "listgrouprights-summary": "Půńiży znojdowo śe spis grup użytkowńikůw zdefińjowanych na tyj wiki, s wyszczygůlńyńym przidźelůnych im prow dostympu.\nSprowdź zajta [[{{MediaWiki:Listgrouprights-helppage}}|s dodatkowymi informacjami]] uo uprowńyńach użytkowńikůw.",
        "listgrouprights-key": "* <span class=\"listgrouprights-granted\">Dane uprawńyńy</span>\n* <span class=\"listgrouprights-revoked\">Uodebrane uprawńyńy</span>",
        "mailnologintext": "Muśyš śe [[Special:UserLogin|zalůgować]] i mjeć wpisany aktualny adres e-brif w swojich [[Special:Preferences|preferyncyjach]], coby můc wysuać e-brif do inkšygo užytkowńika.",
        "emailuser": "Poślij tymu używoczowi e-brif",
        "emailpagetext": "Możesz użyć půńiższygo formularza, coby wysłać wjadůmość e-brif do tygo użytkowńika.\nAdres e-brifa, kery zostoł bez Ćebje wkludzůny we [[Special:Preferences|Twojich sztalowańach]], pojawi śe we polu „Uod”, bez cůż uodbjorca bydźe můg Ći uodpedźeć.",
-       "defemailsubject": "{{SITENAME}} - e-brif uod używacza \"$1\"",
-       "usermaildisabled": "E-brif używacza ńy załůnczony",
+       "defemailsubject": "{{SITENAME}} - e-mail ôd używŏcza \"$1\"",
+       "usermaildisabled": "E-mail ôd używŏcza je zastŏwiōny",
        "noemailtitle": "Brak adresu e-brif",
-       "noemailtext": "Tyn używacz ńy podoł dobrygo e-brifa, abo zadecydowoł, co ńy chce uotrzimywać wjadůmośći e-brif uod inkszych używaczy.",
-       "nowikiemailtext": "Tyn sprowjorz ńy chcy e-brifůw uod inkszych używaczy.",
+       "noemailtext": "Tyn używŏcz niy podoł nŏleżnyj adresy email.",
+       "nowikiemailtext": "Tyn używŏcz niy chce dostŏwać emaili ôd inkszych.",
        "emailtarget": "Podej adresata",
-       "emailusername": "Mjano używacza:",
+       "emailusername": "Miano ôd używŏcza:",
        "emailusernamesubmit": "Poślij",
        "email-legend": "Wyślij e-brif ku inkszymu użytkowńikowi {{GRAMMAR:MS.lp|{{SITENAME}}}}",
        "emailfrom": "Uod:",
        "delete-confirm": "Wyćep „$1”",
        "delete-legend": "Wyćep",
        "historywarning": "Pozor! Ta zajta kerům chceš wyćepnůńć mo historjo:",
-       "confirmdeletetext": "Chceš wyćepnůńć trwale artikel abo plik s bazy danych s historią. Pokož, aže wjyš co robdza, i to aže to je tak jak godojům [[{{MediaWiki:Policy-url}}|zasady]].",
+       "confirmdeletetext": "Zarŏz wyciepniesz artikel i cołkõ ôd niygo historyjõ. Przitupluj, iże na isto chcesz to zrobić, miarkujesz kōnsekwyncyje, i co robisz to podle [[{{MediaWiki:Policy-url}}|prawideł]].",
        "actioncomplete": "Fertig",
        "actionfailed": "Ńy udało sie.",
        "deletedtext": "Wyćepano \"$1\". Rejer uostatnio zrobiůnych wyćepań možeš uobejžyć tukej: $2.",
        "contribsub2": "Lo {{GENDER:$3|używocza|używoczki}} $1 ($2)",
        "nocontribs": "Brak pomjyńań uodpowjadajůncych tym kryterjům.",
        "uctop": "(teroźńo)",
-       "month": "Uod mjeśůnca (i downiyjše):",
-       "year": "Uod roku (i dowńijše):",
+       "month": "Do miesiōnca:",
+       "year": "Do roku:",
        "sp-contributions-newbies": "Pokoż ajnzac ino uod nowych użytkowńikůw",
        "sp-contributions-newbies-sub": "Dlo nowych užytkowńikůw",
        "sp-contributions-newbies-title": "Wkłod nowych użytkowńików",
        "tooltip-pt-logout": "Uodloguj śe ze wiki",
        "tooltip-pt-createaccount": "Namawjůmy do stworzyńo kůnta a zalogůwańo śa, atoli niy je to uobowjůnzkowe",
        "tooltip-ca-talk": "Dyskusyjo uo tym artiklu",
-       "tooltip-ca-edit": "Mogesz sprowjać ta zajta. Podwjela spamjyntosz půmjyńańo, klikńij we knefel \"Uobźyrej\".",
+       "tooltip-ca-edit": "Edytuj tã zajtã",
        "tooltip-ca-addsection": "Przidej nowy temat",
        "tooltip-ca-viewsource": "Ta zajta je zawrzito. Mogesz uobźyrać zdrzůdłowy tekst.",
        "tooltip-ca-history": "Storsze wersyje tyj zajty",
        "tooltip-ca-nstab-main": "Uobźyrej zajta artikla",
        "tooltip-ca-nstab-user": "Ukoż perzůnalno zajta używocza",
        "tooltip-ca-nstab-media": "Uobejřij zajta artikla",
-       "tooltip-ca-nstab-special": "To je ekstra zajta. Ńy moges jej sprowjać.",
+       "tooltip-ca-nstab-special": "To je ekstra zajta i niy idzie jij edytować",
        "tooltip-ca-nstab-project": "Uobejřij zajta projektu",
        "tooltip-ca-nstab-image": "Ukoż zajta grafiki",
        "tooltip-ca-nstab-mediawiki": "Zoboč komunikat systymowy",
        "tooltip-ca-nstab-template": "Uobźyrej muster",
-       "tooltip-ca-nstab-help": "Pokož zajte s půmocą",
+       "tooltip-ca-nstab-help": "Pokŏż zajtã pōmocy",
        "tooltip-ca-nstab-category": "Ukoż zajta kategoryje",
        "tooltip-minoredit": "Uoznacz ta zmjana za drobno",
        "tooltip-save": "Naszkryflej půmjyńańa",
        "exif-exposureprogram-5": "kreatywny (duža guymbja uostrośći)",
        "exif-exposureprogram-6": "aktywny (dužo gibkość migawki)",
        "exif-exposureprogram-7": "tryb portretowy (do zdjyńć s bliska, s ńyuostrym tuym)",
-       "exif-exposureprogram-8": "tryb krajobrazowy (dla zdjęć wykůnywanych s dužej uodlyguośći s uostrośćům ustavjůnům na tuo)",
+       "exif-exposureprogram-8": "tryb landszaftu (dlŏ bildōw ze ôstrościōm nasztalowanōm na zadek)",
        "exif-subjectdistance-value": "$1 metrůw",
        "exif-meteringmode-0": "ńyuokryślůny",
        "exif-meteringmode-1": "średńo",
        "exif-gpsdirection-t": "kerůnek geůgrafičny",
        "exif-gpsdirection-m": "kerůnek magnetyčny",
        "namespacesall": "wszyjske",
-       "monthsall": "wšyskie",
+       "monthsall": "wszyjske",
        "confirmemail": "Potwjerdź adres e-brif",
        "confirmemail_noemail": "Ńy podoužeś prawiduowygo adresa e-brifa we [[Special:Preferences|preferencyjach]].",
        "confirmemail_text": "Projekt {{SITENAME}} wymago weryfikacyji adresa e-brif před užyćym fůnkcyji kořistajůncych s počty.\nWćiś knefel půńižy coby wysúać na swůj adres list s linkym do zajty WWW.\nList bydźe zawjeroú link do zajty, w kerym zakodowany bydźe idyntyfikator.\nUodymkńij tyn link we přyglůndarce, čym potwjerdźiš, co ježeś užytkowńikym tygo adresa e-brif.",
index 293e001..46df1bb 100644 (file)
        "yourpasswordagain": "கடவுச்சொல்லைத் திரும்ப தட்டச்சிடுக:",
        "createacct-yourpasswordagain": "கடவுச்சொல்லை உறுதிசெய்க",
        "createacct-yourpasswordagain-ph": "கடவுச்சொல்லை மீளவும் இடுக",
-       "remembermypassword": "எனது கடவுச்சொல்லை (கூடியது $1 {{PLURAL:$1|நாள்|நாட்கள்}}) அமர்வுகளிடையே நினைவில் வைத்திருக்கவும்.",
        "userlogin-remembermypassword": "இடுபதிந்தே இருக்கவிடவும்",
        "userlogin-signwithsecure": "பாதுகாப்பான தொடர்பை உபயோகிக்கவும்",
        "cannotloginnow-title": "இப்பொழுது விடுபதிகை செய்ய இயலாது.",
        "passwordreset-emailelement": "பயனர் பெயர்:  \n$1\n\nதற்காலிகக் கடவுச்சொல்: \n$2",
        "passwordreset-emailsentemail": "இது உங்கள் கணக்கிற்கான பதிவு செய்யப்பட்ட மின்னஞ்சலாயின், கடவுச்சொல் மீட்டமைக்கும் மின்னஞ்சல் அனுப்பப்படும்",
        "passwordreset-emailsentusername": "இது உங்கள் கணக்கிற்கான பதிவு செய்யப்பட்ட மின்னஞ்சலாயின், கடவுச்சொல் மீட்டமைக்கும் மின்னஞ்சல் அனுப்பப்படும்",
-       "passwordreset-emailsent-capture": "கீழே காண்பிக்கப்பட்டுள்ளது போல் கடவுச்சொல் மீட்டமைக்கும் மின்னஞ்சல் அனுப்பப்பட்டது.",
-       "passwordreset-emailerror-capture": "கடவுச்சொல் மீட்டமைக்கும் மின்னஞ்சல்  உருவாக்கப்பட்டுவிட்டது, அது கீழே காட்டப்பட்டுள்ளது, ஆனால் {{GENDER:$2|user}} அனுப்புவது தோல்வியடைந்தது:$1",
        "changeemail": "மின்னஞ்சல் முகவரியை மாற்று / நீக்கு",
        "changeemail-header": "இந்த படிவத்தை உங்கள் மின்னஞ்சல் முகவரியை மாற்ற பூர்த்தி செய்யவும். நீங்கள் இந்த மாற்றத்தை உறுதிசெய்ய உங்கள் கடவுச்சொல்லை உள்ளிட வேண்டிவரும்.  உங்கள் கணக்கிலிருந்து ஏதாவது மின்னஞ்சலை நீக்க விரும்பினால், படிவத்தை சமர்ப்பிக்கும்போது மின்னஞ்சல் முகவரியை காலியாக விடவும்.",
-       "changeemail-passwordrequired": "இந்த மாற்றத்தை சேமிக்க உங்கள் கடவுச்சொல் தேவைப்படுகிறது.",
        "changeemail-no-info": "இப்பக்கத்தை நேரடியாக அணுகுவதற்கு நீங்கள் புகுபதிகை செய்திருக்கவேண்டும்.",
        "changeemail-oldemail": "தற்பொழுதுள்ள மின்னஞ்சல் முகவரி:",
        "changeemail-newemail": "புதிய மின்னஞ்சல் முகவரி:",
        "minoredit": "இது ஒரு சிறு தொகுப்பு",
        "watchthis": "இக்கட்டுரையைக் கவனிக்கவும்",
        "savearticle": "பக்கத்தைச் சேமி",
+       "savechanges": "மாற்றங்களைச் சேமி",
+       "publishpage": "பக்கத்தைப் பதிப்பிடுக",
+       "publishchanges": "மாற்றங்களைப் பதிப்பிடுக",
        "preview": "முன்தோற்றம்",
        "showpreview": "முன்தோற்றம் காட்டு",
        "showdiff": "மாற்றங்களைக் காட்டு",
        "undo-nochange": "திருத்தங்கள் ஏற்கனவே நீக்கப்பட்டதாக தோன்றுகிறது.",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|Talk]]) பயனரால் செய்யப்பட்ட திருத்தம் $1 இல்லாது செய்யப்பட்டது",
        "undo-summary-username-hidden": "மறை பயனரால் செய்யப்பட்ட மீள்பார்வை $1 ஐ நீக்கு",
-       "cantcreateaccounttitle": "கணக்கைத் தொடக்க முடியாது",
        "cantcreateaccount-text": "இந்த இணைய விதிமுறை இலக்க முகவரியிலிருந்து (IP address) ('''$1''') பயனர் கணக்குகள் தொடங்குவதை பயனர் [[User:$3|$3]] தடை செய்துள்ளார்.\n\nஇதற்காக $3 கொடுத்துள்ள காரணங்கள்  ''$2''",
        "cantcreateaccount-range-text": "இந்த இணைய விதிமுறை இலக்க முகவரி அளவில் உள்ள(IP address) <strong>$1</strong>,  உங்கள் முகவரி (<strong>$4</strong>) உட்பட, பயனர் கணக்குகள் தொடங்குவதை [[User:$3|$3]] தடை செய்துள்ளார்.\n\nஇதற்காக $3 கொடுத்துள்ள காரணங்கள் <em>$2</em>",
        "viewpagelogs": "இப்பக்கத்துக்கான பதிகைகளைப் பார்",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|புதிய பக்கங்கள் பட்டியலையும்]] காணவும்)",
        "recentchanges-submit": "காட்டு",
        "rcnotefrom": "கீழே காணப்படுவது <strong>$3, $4</strong> இலிருந்து செய்யப்பட்ட (<strong>$1</strong> வரைக் காட்டப்பட்டுள்ளது) {{PLURAL:$5|மாற்றமாகும்.|மாற்றங்களாகும்.}}",
-       "rclistfrom": "$2, $3 à®¤à¯\8aà®\9fà®\95à¯\8dà®\95à®®à¯\8d செய்யப்பட்ட புதிய மாற்றங்களைக் காட்டவும்",
+       "rclistfrom": "$2, $3 à®®à¯\81தலà¯\8d à®\87னà¯\8dà®±à¯\81 à®µà®°à¯\88 செய்யப்பட்ட புதிய மாற்றங்களைக் காட்டவும்",
        "rcshowhideminor": "சிறிய தொகுப்புகளை $1",
        "rcshowhideminor-show": "காட்டு",
        "rcshowhideminor-hide": "மறை",
        "sp-contributions-blocked-notice-anon": "இந்த IP முகவரி தற்போது தடுக்கப்பட்டுள்ளது.\nசமீபத்திய தடுப்பு குறிப்பேடு  கீழே குறிப்பிற்காக வழங்கப்பட்டுள்ளது :",
        "sp-contributions-search": "பங்களிப்புகளைத் தேடு",
        "sp-contributions-username": "ஐ.பி. அல்லது பயனர் பெயர்:",
-       "sp-contributions-toponly": "சமீபத்திய பரிசீலனைகளுக்குட்பட்ட  திருத்தங்களை மட்டும் காண்பி",
-       "sp-contributions-newonly": "பக்க உருவாக்க திருத்தங்களை மட்டும் காட்டு",
+       "sp-contributions-toponly": "சமீபத்திய மாற்றமைவுத் திருத்தங்கள் மட்டும்",
+       "sp-contributions-newonly": "பக்க உருவாக்கங்கள் மட்டும்",
+       "sp-contributions-hideminor": "சிறு தொகுப்புக்களை மறை",
        "sp-contributions-submit": "தேடுக",
        "whatlinkshere": "இப்பக்கத்தை இணைத்தவை",
        "whatlinkshere-title": "\"$1\" பக்கத்துக்கு இணைக்கப்பட்டவை",
        "mw-widgets-dateinput-no-date": "திகதி தெரிவுச் செய்யப்படவில்லை",
        "mw-widgets-titleinput-description-new-page": "இப்பக்கம் இன்னும் உருவாக்கப்படவில்லை",
        "mw-widgets-titleinput-description-redirect": "$1-க்கு வழிமாற்று",
-       "api-error-blacklisted": "தயவுகூர்ந்து வேறு, விளக்கமான தலைப்பைத் தேர்ந்தெடுக்கவும்.",
        "sessionprovider-generic": "$1 பகுதி",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "குக்கீயை அடிபடையாக்கக் கொண்ட பகுதிகள்",
        "sessionprovider-nocookies": "குக்கீசு செயலற்று இருக்கலாம். உங்களது குக்கீசு செயலில் உள்ளது என உறுதிப்படுத்திவிட்டு மீண்டும் முயல்க.",
index 662edaf..3fa93b8 100644 (file)
@@ -9,42 +9,43 @@
                        "Bharathesha Alasandemajalu",
                        "Soundarya shetty s",
                        "రహ్మానుద్దీన్",
-                       "BHARATHESHA ALASANDEMAJALU"
+                       "BHARATHESHA ALASANDEMAJALU",
+                       "Lokesha kunchadka"
                ]
        },
-       "tog-underline": "ಲಿà²\82à²\95à³\8dâ\80\99ಲà³\86ದ à²¤à²¿à²°à³\8dತà³\8d à²\97à³\86ರà³\86(à²\85à²\82ಡರà³\8d à²²à³\88ನà³\8d) à²ªà²¾à²¡à³\8dâ\80\99ಲೆ",
-       "tog-hideminor": "à²\8eಲà³\8dಯà³\86ಲà³\8dಯ à²¬à²¦à²²à²¾à²µà²£ೆಲೆನ್ ದೆಂಗಾಲೆ",
-       "tog-hidepatrolled": "à²\95ಾತà³\8aà²\82ದಿಪà³\8dಪà³\81ನ à²¸à²\82ಪದನà³\86ಲà³\86ನà³\8d à²\87à²\82à²\9aಿಪà³\8aದ à²¬à²¦à²²à²¾à²µà²£à³\86ಡà³\8d à²\85ಡà³\86à²\82à²\97ಲ",
-       "tog-newpageshidepatrolled": "à²\95ಾತà³\8aà²\82ದಿಪà³\8dಪà³\81ನ à²ªà³\81à²\9fà³\8aಲà³\86ನà³\8d à²ªà³\8aಸ à²ªà³\81à²\9fà³\8aà²\95à³\81ಲà³\86 à²ªà²\9fà³\8dà²\9fಿಡà³\8d à²\85ಡà³\86à²\82à²\97ಲ",
-       "tog-hidecategorization": "ವಿà²\82à²\97ಡಿತà³\8dâ\80\8dನ à²ªà³\81à²\9fà³\8aಲà³\86ನà³\8d à²\85ಡà³\86à²\82à²\97ಲ",
-       "tog-extendwatchlist": "à²\95à³\87ವಲà³\8a à²\87à²\82à²\9aಿಪà³\8aದ à²¬à²¦à²²à²¾à²µà²£à³\86ಲತà³\8dತà²\82ದà³\86, à²¸à²\82ಬà²\82ದà³\8a à²\87ಪà³\8dಪà³\81ನ à²®à²¾à²¤ à²¬à²¦à²²à²¾à²µà²£ೆನ್ಲಾ ತೋಜುನಂಚನೆ ಪಟ್ಟಿನ್ ವಿಸ್ತರಿಸಲೆ",
-       "tog-usenewrc": "à²\87à²\82à²\9aಿಪà³\8aದ à²¬à²¦à²²à²¾à²µà²£à³\86 à²¬à³\8aà²\95à³\8dà²\95à³\8a à²µà³\80à²\95à³\8dಷಣಾಪà²\9fà³\8dà²\9fಿಡà³\8d à²\97à³\81à²\82ಪà³\81 à²ªà³\81à²\9fà³\8a à²¬à²¦à²²à²¾à²µà²£ೆ",
-       "tog-numberheadings": "ಹà³\86ಡà³\8dಡಿà²\82à²\97à³\8dâ\80\99ಲà³\86à²\97à³\8d à²¸à²\82à²\96à³\8dಯà³\86ಲà³\86ನà³\8d à²¤à³\8aà²\9cà³\8dಪಾಲà³\86",
-       "tog-showtoolbar": "ಸà²\82ಪಾದನà³\86ದ à²\89ಪà²\95ರಣೊ ಪಟ್ಟಿನ್ ತೋಜಾವು",
+       "tog-underline": "ಲಿà²\82à²\95à³\8dâ\80\8dಲà³\86ದ à²¤à²¿à²°à³\8dತà³\8d à²\97à³\86ರà³\86(à²\85à²\82ಡರà³\8d à²²à³\88ನà³\8d) à²ªà²¾à²¡à³\8dâ\80\8dಲೆ",
+       "tog-hideminor": "à²\8eಲà³\8dಯà³\86ಲà³\8dಯ à²¬à²¦à²²à²¾à²µà²¨ೆಲೆನ್ ದೆಂಗಾಲೆ",
+       "tog-hidepatrolled": "à²\95ಾತà³\8aà²\82ದಿಪà³\8dಪà³\81ನ à²¸à²\82ಪದನà³\86ಲà³\86ನà³\8d à²\87à²\82à²\9aಿಪà³\8aದ à²¬à²¦à²²à²¾à²µà²¨à³\86ಡà³\8d à²¦à³\86à²\82à²\97ಾಲ",
+       "tog-newpageshidepatrolled": "à²\95ಾತà³\8aà²\82ದಿಪà³\8dಪà³\81ನ à²ªà³\81à²\9fà³\8aಲà³\86ನà³\8d à²ªà³\8aಸ à²ªà³\81à²\9fà³\8aà²\95à³\81ಲà³\86 à²ªà²\9fà³\8dà²\9fಿಡà³\8d à²¦à³\86à²\82à²\97ಾಲ",
+       "tog-hidecategorization": "ವಿà²\82à²\97ಡಿತà³\8dâ\80\8dನ à²ªà³\81à²\9fà³\8aಲà³\86ನà³\8d à²¦à³\86à²\82à²\97ಾಲ",
+       "tog-extendwatchlist": "à²\95à³\87ವಲà³\8a à²\87à²\82à²\9aಿಪà³\8aದ à²¬à²¦à²²à²¾à²µà²¨à³\86ಲತà³\8dತà²\82ದà³\86, à²¸à²\82ಬà²\82ದà³\8a à²\87ಪà³\8dಪà³\81ನ à²®à²¾à²¤ à²¬à²¦à²²à²¾à²µà²¨ೆನ್ಲಾ ತೋಜುನಂಚನೆ ಪಟ್ಟಿನ್ ವಿಸ್ತರಿಸಲೆ",
+       "tog-usenewrc": "à²\87à²\82à²\9aಿಪà³\8aದ à²¬à²¦à²²à²¾à²µà²¨à³\86 à²¬à³\8aà²\95à³\8dà²\95à³\8a à²µà³\80à²\95à³\8dಷಣಾಪà²\9fà³\8dà²\9fಿಡà³\8d à²\97à³\81à²\82ಪà³\81 à²ªà³\81à²\9fà³\8a à²¬à²¦à²²à²¾à²µà²¨ೆ",
+       "tog-numberheadings": "ತರà³\86ಬರವà³\81ಲà³\86à²\97à³\8d à²\85à²\82à²\95à³\86ಲà³\86ನà³\8d à²¤à³\8bà²\9cಾವà³\81",
+       "tog-showtoolbar": "ಸà²\82ಪಾದನà³\86ದ à²\89ಪà²\95ರನೊ ಪಟ್ಟಿನ್ ತೋಜಾವು",
        "tog-editondblclick": "ರಡ್ಡ್ ಸರ್ತಿ ಒತ್ತ್‌ನಗ ಪುಟೊನು ಸಂಪೊಲಿಪುನಂಚ ಆವಡ್",
        "tog-editsectiononrightclick": "ಪುಟೊತ ವಿಬಾಗೊಲೆನ್ ಐತ ಸೀರ್ಸಿಕೆನ್ ರಡ್ಡ್ ಸರ್ತಿ ಒತ್ತ್‌ನಗ ಸಂಪೊಲಿಪುನಂಚ ಉಪ್ಪಡ್",
-       "tog-watchcreations": "ಯಾನà³\8d à²ುರು ಮಲ್ತಿನ ಲೇಕನೊಲೆನ್ ಎನ್ನ ವೀಕ್ಷಣಾಪಟ್ಟಿಗ್ ಸೇರ್ಪಾಲೆ",
+       "tog-watchcreations": "ಯಾನà³\8d à²¸ುರು ಮಲ್ತಿನ ಲೇಕನೊಲೆನ್ ಎನ್ನ ವೀಕ್ಷಣಾಪಟ್ಟಿಗ್ ಸೇರ್ಪಾಲೆ",
        "tog-watchdefault": "ಯಾನ್ ಸಂಪೊಲಿಪುನ ಪುಟೊಲೆನ್ ವೀಕ್ಷಣಾಪಟ್ಟಿಗ್ ಸೇರ್ಪಾಲೆ",
-       "tog-watchmoves": "ಯಾನ್ ಸ್ತಲಾಂತರಿಸುನ ಪುಟೊಲೆನ್ ಎನ್ನ ವೀಕ್ಷಣಾಪಟ್ಟಿಗ್ ಸೇರ್ಪಾಲೆ",
+       "tog-watchmoves": "ಯಾನà³\8d à²¸à³\8dತಲಾà²\82ತರಿಸಪà³\81ನ à²ªà³\81à²\9fà³\8aಲà³\86ನà³\8d à²\8eನà³\8dನ à²µà³\80à²\95à³\8dಷಣಾಪà²\9fà³\8dà²\9fಿà²\97à³\8d à²¸à³\87ರà³\8dಪಾಲà³\86",
        "tog-watchdeletion": "ಯಾನ್ ದೆತ್ತ್‌ ಪಾಡುನ ಪುಟೊಲೆನ್ ಎನ್ನ ವೀಕ್ಷಣಾಪಟ್ಟಿಗ್ ಸೇರ್ಪಾಲೆ",
-       "tog-watchuploads": "ಎನ್ನ ಅಪ್ಲೋಡ್ ಪಟ್ಟಿಗ್  ಪೊಸ ಕಡತೊಲೆನ್ ಸೇರಲ",
+       "tog-watchuploads": "ಎನ್ನ ಅಪ್ಲೋಡ್ ಪಟ್ಟಿಗ್ ಪೊಸ ಕಡತೊಲೆನ್ ಸೇರಲ",
        "tog-watchrollback": "ಯಾನ್ ಪಿರ ದೆತೊನುನ ಪುಟೊಲೆನ್ ಎನ್ನ ಗುಮನೊಗು ಸೇರಲೆ",
-       "tog-minordefault": "ಪà³\82ರಾ à²¸à²\82ಪಾದನà³\86ನà³\8dಲಾ à²\8eಲà³\8dಯ à²ªà²\82ಡà³\8dâ\80\99ದ್ ಗುರ್ತ ಮಲ್ಪುಲೆ",
-       "tog-previewontop": "ಮà³\81ನà³\8dನà³\8bà²\9fನà³\8d à²¸à²\82ಪಾದನà³\86 à²\85à²\82à²\95ಣದ ಮಿತ್ತ್ ತೊಜ್ಪಾಲೆ",
-       "tog-previewonfirst": "ಶà³\81ರà³\81ತ à²¬à²¦à²²à²¾à²µà²£ೆದ ಬೊಕ್ಕ ಮನ್ನೋಟನ್ ತೊಜ್ಪಾಲೆ",
+       "tog-minordefault": "ಪà³\82ರಾ à²¸à²\82ಪಾದನà³\86ನà³\8dಲಾ à²\8eಲà³\8dಯ à²ªà²\82ಡà³\8dâ\80\8dದ್ ಗುರ್ತ ಮಲ್ಪುಲೆ",
+       "tog-previewontop": "ಮà³\81ನà³\8dನà³\8bà²\9fನà³\8d à²¸à²\82ಪಾದನà³\86 à²\85à²\82à²\95ನà³\8aದ ಮಿತ್ತ್ ತೊಜ್ಪಾಲೆ",
+       "tog-previewonfirst": "ಸà³\81ತ à²¬à²¦à²²à²¾à²µà²¨ೆದ ಬೊಕ್ಕ ಮನ್ನೋಟನ್ ತೊಜ್ಪಾಲೆ",
        "tog-enotifwatchlistpages": "ಎನ್ನ ವೀಕ್ಷಣಾಪಟ್ಟಿಡ್ ಉಪ್ಪುನಂಚಿನ ಒವಾಂಡಲ ಪುಟೊ ಬದಲಾನಗ ಎಂಕ್ ಇ-ಅಂಚೆ ಕಡಪುಡ್ಲೆ",
        "tog-enotifusertalkpages": "ಎನ್ನ ಚರ್ಚೆ ಪುಟ ಬದಲಾಂಡ ಎಂಕ್ ಇ-ಮೇಲ್ ಕಡಪುಡ್ಲೆ",
-       "tog-enotifminoredits": "à²\8eಲà³\8dಯà³\86ಲà³\8dಯ à²¬à²¦à²²à²¾à²µà²£ೆ ಆಂಡಲ ಎಂಕ್ ಇ-ಅಂಚೆ ಕಡಪುಡ್ಲೆ",
-       "tog-enotifrevealaddr": "ಪà³\8dರà²\95à²\9fಣà³\86 à²\87-ಮà³\87ಲà³\8dâ\80\99ಡ್ ಎನ್ನ ಇ-ಮೇಲ್ ವಿಳಾಸನ್ ತೊಜ್ಪಾಲೆ",
-       "tog-shownumberswatching": "ಪà³\81à²\9fà³\8aನà³\81 à²¤à³\82ವà³\8aà²\82ದà³\81ಪà³\8dಪà³\81ನà²\82à²\9aಿನ à²¸à²¦à²¸à³\8dಯà³\86ರà³\8dâ\80\99ನ ಸಂಖ್ಯೆನ್ ತೊಜ್ಪಾಲೆ",
+       "tog-enotifminoredits": "à²\8eಲà³\8dಯà³\86ಲà³\8dಯ à²¬à²¦à²²à²¾à²µà²¨ೆ ಆಂಡಲ ಎಂಕ್ ಇ-ಅಂಚೆ ಕಡಪುಡ್ಲೆ",
+       "tog-enotifrevealaddr": "ಪà³\8dರà²\95à²\9fಣà³\86 à²\87-ಮà³\87ಲà³\8dâ\80\8dಡ್ ಎನ್ನ ಇ-ಮೇಲ್ ವಿಳಾಸನ್ ತೊಜ್ಪಾಲೆ",
+       "tog-shownumberswatching": "ಪà³\81à²\9fà³\8aನà³\81 à²¤à³\82ವà³\8aà²\82ದà³\81ಪà³\8dಪà³\81ನà²\82à²\9aಿನ à²¸à²¦à²¸à³\8dಯà³\86ರà³\8dâ\80\8dನ ಸಂಖ್ಯೆನ್ ತೊಜ್ಪಾಲೆ",
        "tog-oldsig": "ಇತ್ತೆದ ಸಹಿ",
        "tog-fancysig": "ವಿಕಿಟೆಕ್ಸ್‌ಟ್‍ಗ್ ದಸ್ಕತ್ತ್‌ದ ಉಪಚಾರೊ(ಸ್ವಂತೊ ಚಾಲನೆದ ಕೊಂಡಿ ಇದ್ಯಂದಿಲೆಕ)",
        "tog-uselivepreview": "ನೇರೊ ಮುನ್ನೋಟೊನು ಉಪಯೋಗ ಮಲ್ಪುಲೆ",
-       "tog-forceeditsummary": "ಸà²\82ಪಾದನà³\86 à²¸à²¾à²°à²¾à²\82ಶà³\8aನà³\81 à²\96ಾಲಿ à²¬à³\81ಡà³\8dâ\80\99ನà³\8dಡ್ ಎಂಕ್ ನೆನಪು ಮಲ್ಪುಲೆ",
-       "tog-watchlisthideown": "ವà³\80à²\95à³\8dಷಣಾಪà²\9fà³\8dà²\9fಿಡà³\8d à²\8eನà³\8dನ à²¸à²\82ಪಾದನà³\86ಲà³\86ನà³\8d à²¤à³\8aà²\9cà³\8dâ\80\99ಪಾವà³\8aಚಿ",
+       "tog-forceeditsummary": "ಸà²\82ಪಾದನà³\86 à²¸à²¾à²°à²¾à²\82ಸà³\8aನà³\81 à²\95ಾಲಿ à²¬à³\81ಡà³\8dâ\80\8dà²\82ದ್ ಎಂಕ್ ನೆನಪು ಮಲ್ಪುಲೆ",
+       "tog-watchlisthideown": "ವà³\80à²\95à³\8dಷಣಾಪà²\9fà³\8dà²\9fಿಡà³\8d à²\8eನà³\8dನ à²¸à²\82ಪಾದನà³\86ಲà³\86ನà³\8d à²¤à³\8aà²\9cà³\8dâ\80\8dಪಾವà³\8aಡà³\8dಚಿ",
        "tog-watchlisthidebots": "ವೀಕ್ಷಣಾಪಟ್ಟಿಡ್ ಬಾಟ್ ಸಂಪಾದನೆಲೆನ್ ದೆಂಗಾಲೆ",
        "tog-watchlisthideminor": "ಎಲ್ಯ ಬದಲಾವಣೆಲೆನ್ ವೀಕ್ಷಣಾಪಟ್ಟಿರ್ದ್ ದೆಂಗಾಲೆ",
-       "tog-watchlisthideliu": "ಲಾà²\97ಿನà³\8d à²\86ತಿನà²\82à²\9aಿನ à²¸à²¦à²¸à³\8dಯà³\86ರà³\8dâ\80\99ನ ಸಂಪಾದನೆಲೆನ್ ವೀಕ್ಷಣಾಪಟ್ಟಿರ್ದ್ ದೆಂಗಾಲೆ",
+       "tog-watchlisthideliu": "ಲಾà²\97ಿನà³\8d à²\86ತಿನà²\82à²\9aಿನ à²¸à²¦à²¸à³\8dಯà³\86ರà³\8dâ\80\8dನ ಸಂಪಾದನೆಲೆನ್ ವೀಕ್ಷಣಾಪಟ್ಟಿರ್ದ್ ದೆಂಗಾಲೆ",
        "tog-watchlisthideanons": "ಪುದರಿಜ್ಜಂದಿನ ಬಳಕೆದಾರನ ಸಂಪಾದನೆಲೆನ್ ವೀಕ್ಷಣಾಪಟ್ಟಿರ್ದ್ ದೆಂಗಾಲೆ",
        "tog-watchlisthidepatrolled": "ವೀಕ್ಷಣಾಪಟ್ಟಿಡ್ ಬಾಟ್ ಸಂಪಾದನೆಲೆನ್ ದೆಂಗಾಲೆ",
        "tog-watchlisthidecategorization": "ವಿಂಗಡಿತ್‍ನ ಪುಟೊಲೆನ್ ಅಡೆಂಗಲ",
        "mainpage-description": "ಮುಖ್ಯ ಪುಟ",
        "policy-url": "Project:ನಿಯಮಾವಳಿ",
        "portal": "ಸಮುದಾಯೊ ಪುಟೊ",
-       "portal-url": "Project:ಸಮುದಾಯ ಪುಟ",
+       "portal-url": "Project:ಸಮುದಾಯ ಪುಟ",
        "privacy": "ಕಾಸಗಿ ಕಾರ್ಯೊನೀತಿ",
        "privacypage": "Project:ಕಾಸಗಿ ಕಾರ್ಯೊನೀತಿ",
        "badaccess": "ಅನುಮತಿ ದೋಷ",
        "yourpasswordagain": "ಪಾಸ್ವರ್ಡ್ ಪಿರ ಟೈಪ್ ಮಲ್ಪುಲೆ",
        "createacct-yourpasswordagain": "ಪ್ರವೇಸೊ ಪದೊನು ದೃಡೊ ಮಲ್ಪುಲೆ",
        "createacct-yourpasswordagain-ph": "ಪ್ರವೇಸೊ ಪದೊನು ನನ ಒರ ನಮೂದಿಸಲೆ",
-       "remembermypassword": "ಈ ಗಣಕಯಂತ್ರೊಡು ಎನ್ನ ಲಾಗಿನ್ ನೆಂಪು ದೀಡೊನ್ಲೆ(ಹೆಚ್ಚ್ $1 {{PLURAL:$1|ದಿನೊತ|ದಿನೊಕ್ಕುಲೆ}}ಮುಟ್ಟೊ)",
        "userlogin-remembermypassword": "ಎನನ್ ಲಾಗಿನ್ ಆತೇ ದೀಡ್ಲೆ",
        "userlogin-signwithsecure": "ರಕ್ಷಣೆದ ಕನೆಕ್ಷನ್ ಉಪಯೋಗಿಸಲೆ.",
        "cannotloginnow-title": "ಇತ್ತೆ ಉಲಾಯಿ ಪೋಯರ್ ಸಾದ್ಯೊ ಇದ್ದಿ",
        "createacct-submit": "ಪೊಸ ಕಾತೆ ಸುರು ಮಲ್ಪುಲೆ",
        "createacct-another-submit": "ಪೊಸ ಕಾತೆ ಸುರು ಮಲ್ಪುಲೆ",
        "createacct-benefit-heading": "{{SITENAME}}ನಿಕ್ಲೆನಂಚಿತ್ತಿನ ಜನೊಕ್ಲೆಡ್ದ್ ಉಂಡಾಪುಂಡು.",
-       "createacct-benefit-body1": "{{PLURAL:$1|ಸà²\82ಪದನà³\86|ಸà²\82ಪದನೆಲು}}",
+       "createacct-benefit-body1": "{{PLURAL:$1|ಸà²\82ಪà³\8aದನà³\86|ಸà²\82ಪದà³\8aನೆಲು}}",
        "createacct-benefit-body2": "{{PLURAL:$1|ಪುಟೊ|ಪುಟೊಕ್ಕುಲು}}",
        "createacct-benefit-body3": "{{PLURAL:$1|ಇನಾಮು|ಇನಾಮುಲು}}",
        "badretype": "ಈರ್ ಕೊರ್ನ ಪ್ರವೇಶ ಪದೆ ಬೇತೆ ಬೇತೆ ಅತ್ಂಡ್",
        "noname": "ಈರ್ ಸರಿಯಾಯಿನ ಬಳಕೆದಾರ ಪುದರ್ ಕೊರ್ತಿಜ್ಜರ್.",
        "loginsuccesstitle": "ಲಾಗ್ ಇನ್ ಯಶಸ್ವಿಯಾತ್ಂಡ್",
        "loginsuccess": "ಲಾಗ್ ಇನ್ ಯಶಸ್ವಿಯಾತ್‘ಂಡ್\". {{SITENAME}}  \"$1\".'''",
-       "nosuchuser": "!!\"$1\"ಪà³\81ದರà³\8dâ\80\98ದ à²µà²¾ à²¸à²¦à²¸à³\8dಯà³\86ರà³\8dâ\80\98ಲಾ à²\87à²\9cà³\8dà²\9cà³\86ರà³\8d, à²\85à²\95à³\8dಷರ à²¸à²°à²¿à²¯à²¾à²¦ ತೂಲೆ ಅಥವಾ  [[Special:CreateAccount|ಪೊಸ ಸದಸ್ಯತ್ವ  ಖಾತೆನ್ ಸೃಷ್ಟಿ ಮಲ್ಪುಲೆ]].",
+       "nosuchuser": "!!\"$1\"ಪà³\81ದರà³\8dâ\80\98ದ à²µà²¾ à²¸à²¦à²¸à³\8dಯà³\86ರà³\8dâ\80\98ಲಾ à²\87à²\9cà³\8dà²\9cà³\86ರà³\8d, à²\85à²\95à³\8dಷರ à²¸à²°à²¿à²¯à²¾à²¤à³\8d ತೂಲೆ ಅಥವಾ  [[Special:CreateAccount|ಪೊಸ ಸದಸ್ಯತ್ವ  ಖಾತೆನ್ ಸೃಷ್ಟಿ ಮಲ್ಪುಲೆ]].",
        "nosuchusershort": "!!\"$1\"ಪುದರ್‘ದ ವಾ ಸದಸ್ಯೆರ್‘ಲಾ ಇಜ್ಜೆರ್, ಅಕ್ಷರ ಸರಿಯಾದ ತೂಲೆ.",
        "nouserspecified": "ಈರ್ ಒಂಜಿ ಸದಸ್ಯತ್ವದ ಪುದರ್ ಸೂಚನೆ ಮಲ್ಪೊಡು.",
        "login-userblocked": "ಈ ಸದಸ್ಯರೆನ ಖಾತೆನ್ ತಡೆ ಪತ್ತ್‘ದುಂಡು. ಲಾಗ್ ಇನ್ ಮಲ್ಪರೆ ಆಪುಜ್ಜಿ.",
        "passwordreset-username": "ಸದಸ್ಯೆರ್ನ ಪುದರ್:",
        "passwordreset-domain": "ಕ್ಷೇತ್ರೊ:",
        "passwordreset-email": "ಇ-ಅಂಚೆ ವಿಳಾಸೊ",
+       "passwordreset-invalideamil": "ಇಮೇಲ್ ಸರಿ ಇಜ್ಜಿ",
+       "changeemail-oldemail": "ಇತ್ತೆತಾ ಈಮೇಲ್ ವಿಳಾಸೊ:",
        "changeemail-newemail": "ಪೊಸ ಇ-ಅಂಚೆ ವಿಳಾಸೊ:",
        "changeemail-none": "ಒವ್ವುಲಾ ಇಜ್ಜಿ",
        "changeemail-submit": "ಇ-ಅಂಚೆ ವಿಳಾಸ ಬದಲಾವಣೆ ಮಲ್ಪುಲೆ",
        "minoredit": "ಉಂದು ಎಲ್ಯ ಬದಲಾವಣೆ",
        "watchthis": "ಈ ಪುಟೊನು ತೂಲೆ",
        "savearticle": "ಪುಟೊನು ಒರಿಪಾಲೆ",
+       "savechanges": "ಬದಲಾವನೆನ್ ಒರಿಪಾಲೆ",
        "publishpage": "ಪುಟೋನು ಪ್ರಕಟಿಸಲೇ",
+       "publishchanges": "ಬದಲಾವನೆನ್ ತೋಜಾಲೆ",
        "preview": "ಮುನ್ನೋಟ",
        "showpreview": "ಮುನ್ನೋಟೊ ತೋಜಾವು",
        "showdiff": "ಬದಲಾವಣೆಲೆನ್ ತೋಜಾವ್",
        "template-protected": "(ಸಂರಕ್ಷಿತೊ)",
        "template-semiprotected": "(ಅರೆ-ಸಂರಕ್ಷಿತೊ)",
        "hiddencategories": "ಈ ಪುಟೊ {{PLURAL:$1|೧ ಗುಪ್ತ ವರ್ಗೊಗ್|$1 ಗುಪ್ತ ವರ್ಗೊಲೆಗ್}} ಸೇರ್ದ್‍ನ್ಡ್:",
+       "permissionserrors": "ಅನುಮತಿ ದೋಷ",
        "permissionserrorstext-withaction": "$2 ಗ್ ಇರೆಗ್ ಅನುಮತಿ ಇದ್ದಿ, ಅಯಿಕ್ {{PLURAL:$1|ಕಾರಣೊ|ಕಾರಣೊಲು}}:",
        "moveddeleted-notice": "ಈ ಪುಟೊ ಅಸ್ತಿತ್ವೊಡ್ ಇದ್ದಿ.\nಪುಟೊದ ಡಿಲೀಶನ್ ಅತ್ತ್ಂಡ್ ಕಡಪ್ಪುಡುನೆ ಲಾಗ್‍ನ್ ತೂಯರೆ ತಿರ್ತ್ ಕೊರ್ತ್ಂಡ್.",
+       "postedit-confirmation-created": "ಈ ಪುಟೋನು ಉಂಡು ಮಾನ್ತುಂಡು.",
+       "postedit-confirmation-saved": "ಇರೇನಾ ಸಂಪಾದನೆನ್ ಒರಿಪಾತುಂಡು.",
+       "edit-already-exists": "ಪೊಸ ಪುಟೋನು ಉಂಡು ಮಲ್ಪರೆ ಅಯಿಜಿ. ಅವ್ವು ದುಂಬೇ ಉಂಡು.",
+       "content-model-wikitext": "ವಿಕಿ ಪಠ್ಯ",
        "viewpagelogs": "ಈ ಪುಟೊತ ದಾಕಲೆಲೆನ್ ತೂಲೆ",
        "nohistory": "ಈ ಪುಟಕ್ ಬದಲಾವಣೆದ ಇತಿಹಾಸ ಇಜ್ಜಿ",
        "currentrev": "ಇತ್ತೆದ ಆವೃತ್ತಿ",
        "page_last": "ಕಡೆತ",
        "history-fieldset-title": "ಇತಿಹಾಸಡ್ ನಾಡ್ಲೆ",
        "history-show-deleted": "ದೆತ್ತ್ ಪಾಡಿನ",
-       "histfirst": "ಬಾರಿ à²¦à³\81à²\82ಬà³\81ದ",
+       "histfirst": "ಪರ",
        "histlast": "ಪೊಸ",
        "historysize": "({{PLURAL:$1|೧ ಬೈಟ್|$1 ಬೈಟ್‍ಲು}})",
        "historyempty": "(ಖಾಲಿ)",
        "rev-showdeleted": "ತೊಜಾವು",
        "revisiondelete": "ಮಾಜಾಯಿನ/ಮಾಜಾವಂದಿನ ಬದಲಾವಣೆಲು",
        "revdelete-show-file-submit": "ಅಂದ್",
-       "revdelete-hide-text": "ಬದಲಾವಣà³\86ದ à²ªà² à³\8dಯನà³\8d à²¦à³\86à²\82à²\97ಾಲà³\86",
+       "revdelete-hide-text": "ಪರಿಷà³\8dà²\95ರಣà³\86 à²\86ಯಿನ à²ªà² à³\8dಯ",
        "revdelete-hide-image": "ಪೈಲ್‘ಡ್  ಇಪ್ಪುನ ಮಾಹಿತ್‘ನ್ ದೆಂಗಾಲೆ",
        "revdelete-hide-name": "ಕಾರ್ಯ ಬೊಕ್ಕ ಗುರಿನ್ ದೆಂಗಾಲ",
-       "revdelete-hide-comment": "ಸಂಪಾದನೆದ ವಿವರಣೆ ದೆಂಗಾಲೆ",
-       "revdelete-radio-set": "ಅಂದ್",
-       "revdelete-radio-unset": "ಇಜ್ಜಿ",
+       "revdelete-hide-comment": "ಸಾರಾಂಶ ಸಂಪೊಲಿಪುಲೆ",
+       "revdelete-radio-same": "(ಬದಲಾವಣೆ ಮಾಂಪಾಡ್ಚಿ)",
+       "revdelete-radio-set": "ದೆಂಗಾಲೆ",
+       "revdelete-radio-unset": "ತೋಜುಂಡು",
        "revdelete-log": "ಕಾರಣ",
        "revdel-restore": "ವಿಸಿಬಿಲಿಟಿನ್ ಬದಲ್ ಮಲ್ಪುಲೆ",
        "pagehist": "ಪುಟೊತ ಚರಿತ್ರೆ",
        "mergehistory-reason": "ಕಾರಣ:",
        "revertmerge": "ಅನ್-ಮರ್ಜ್ ಮಲ್ಪುಲೆ",
        "history-title": "\"$1\" ಪುಟೊತ ಆವೃತ್ತಿ ಇತಿಹಾಸೊ",
-       "difference-title": "ಪಿರ à²ªà²°à²¿à²¸à³\80ಲನà³\86ದ à²¨à²¡à³\81ತ à²µà³\8dಯತà³\8dವಾಸೊ \"$1\"",
+       "difference-title": "ಪಿರ à²ªà²°à²¿à²¸à³\80ಲನà³\86ದ à²¨à²¡à³\81ತ à²µà³\8dಯತà³\8dಯಾಸೊ \"$1\"",
        "lineno": "$1ನೇ ಸಾಲ್:",
        "compareselectedversions": "ಆಯ್ಕೆ ಮಲ್ತಿನ ಆವೃತ್ತಿಲೆನ್ ಹೊಂದಾಣಿಕೆ ಮಲ್ತ್ ತೂಲೆ",
        "editundo": "ದುಂಬುದಲೆಕೊ",
        "notextmatches": "ವಾ ಪುಟೊತ ಪಠ್ಯೊಡುಲಾ ಹೋಲಿಕೆ ಇಜ್ಜಿ",
        "prevn": "ದುಂಬು {{PLURAL:$1|$1}}",
        "nextn": "ಬೊಕ್ಕೊ {{PLURAL:$1|$1}}",
+       "prev-page": "ದುಂಬುತ ಪುಟೊ",
+       "next-page": "ನನತಾ ಪುಟ",
        "nextn-title": "ದುಂಬುದ $1 {{PLURAL:$1|result|ಪಲಿತಾಂಸೊಲು}}",
        "shown-title": "ಪ್ರತಿ ಪುಟೊಡುಲಾ $1 {{PLURAL:$1|result|ಪಲಿತಾಂಸೊ}} ತೋಜಿಪಾವು",
        "viewprevnext": "ತೂಲೆ($1 {{int:pipe-separator}} $2) ($3)",
        "prefs-skin": "ಸ್ಕಿನ್",
        "skin-preview": "ಮುನ್ನೋಟೊ",
        "datedefault": "ಒವ್ವೇ ಪ್ರಾಸಸ್ತ್ಯೊ ಇದ್ದಿ",
+       "prefs-user-pages": "ಸದಸ್ಯೆರೆನ ಪುಟೊ",
        "prefs-rc": "ಇಂಚಿಪದ ಬದಲಾವಣೆಲು",
+       "prefs-watchlist": "ವೀಕ್ಷಣಾಪಟ್ಟಿ",
        "prefs-resetpass": "ಪ್ರವೇಶಪದೊನ್ ಬದಲಾವಣೆ ಮಲ್ಪುಲೆ",
        "prefs-changeemail": "ಇ-ಅಂಚೆ ವಿಳಾಸೊನು ಬದಲಾವಣೆ ಮಲ್ಪುಲೆ ಅತ್ತಂಡ ದೆಪ್ಪುಲೆ",
        "prefs-setemail": "ಇ-ಅಂಚೆ ವಿಳಾಸೊನು ಸ್ತಾಪನೆ ಮಲ್ಪು",
        "prefs-tokenwatchlist": "ಟೊಕನ್",
        "userrights-lookup-user": "ಬಳಕೆದಾರೆರೆ ಗುಂಪುಲೆನ್ ನಿರ್ವಹಿಸಲ",
        "userrights-user-editname": "ಒಂಜಿ ಸದಸ್ಯ ಪುದರ್ ಬರೆಲೆ",
+       "userrights-reason": "ಕಾರಣೊ:",
        "group": "ಗುಂಪುಲು:",
        "group-user": "ಬಳಕೆದಾರೆರ್",
        "group-sysop": "ನಿರ್ವಾಹಕೆರ್",
        "right-writeapi": "ಬರೆಯಿನ ಎಪಿಐದ ಬಳಕೆ",
        "right-delete": "ಪುಟೊಕುಲೆನ್ ಮಾಜಾಲೆ",
        "right-undelete": "ಪುಟೊನ್ ಮಾಜಾವಡೆ",
+       "grant-group-email": "ಇ-ಅಂಚೆ ಕಡಪುಡುಲೆ",
+       "grant-createaccount": "ಪೊಸ ಕಾತೆ ಸುರು ಮಲ್ಪುಲೆ",
        "newuserlogpage": "ಸದಸ್ಯೆರೆ ಸ್ರಿಸ್ಟಿದ ದಾಕಲೆ",
        "rightslog": "ಸದಸ್ಯೆರ್ನ ಹಕ್ಕು ದಾಖಲೆ",
        "action-read": "ಈ ಪುಟೊನು ಓದುಲೆ",
        "action-edit": "ಈ ಪುಟೊನು ಎಡಿಟ್ ಮಲ್ಪುಲೆ",
        "action-createpage": "ಈ ಪುಟೊನು ಸೃಷ್ಟಿಸಾಲೆ",
-       "action-createtalk": "ಚರ್ಚಾಪುಟೊಕ್‘ಲೆನ್ ಸೃಷ್ಟಿಸಾಲೆ",
+       "action-createtalk": "ಚರ್ಚಾ ಪುಟೊನ್ ಸೃಷ್ಟಿಸಾಲೆ",
        "action-createaccount": "ಈ ಸದಸ್ಯೆರನ ಖಾತೆನ್ ಸೃಷ್ಟಿ ಮಲ್ಪುಲೆ",
        "action-minoredit": "ಉದೊಂಜಿ ಎಲ್ಯ  ಬದಲಾವಣೆ",
        "action-move": "ಈ ಪೂಟೊನು ಮೂವ್(ಸ್ಥಳಾಂತರ) ಮಲ್ಪುಲೆ",
        "action-upload": "ಈ ಫೈಲ್‘ನ್ ಅಪ್‘ಲೋಡ್ ಮಲ್ಪುಲೆ",
        "action-delete": "ಈ ಪುಟೊನ್ ಮಾಜಾಲೆ",
        "action-deleterevision": "ಈ ಆವೃತ್ತಿನ್ ಮಾಜಾಲೆ",
+       "action-browsearchive": "ಮಜಾಯಿನಾ ಪುಟೋನ್ ನಡ್ಲೆ",
+       "action-undelete": "ಈ ಪುಟೊನ್ ಮಾಜಾಯಿನೆನ್ ರದ್ದ್ ಮಾನ್ಪುಲೇ",
        "action-sendemail": "ಇ-ಅಂಚೆ ಕಡಪುಡುಲೆ",
        "nchanges": "$1 {{PLURAL:$1|ಬದಲಾವಣೆ|ಬದಲಾವಣೆಲು}}",
        "enhancedrc-history": "ಇತಿಹಾಸೊ",
        "recentchanges-label-plusminus": "ಬೈಟ್ಸ್‌ದ ಲೆಕ್ಕೊಡು ಈ ಪಾಲೆದ ಗಾತ್ರೊ ಬದಲಾತ್ಂಡ್",
        "recentchanges-legend-heading": "<strong>ಲೆಜೆಂಡ್:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ಬೊಕ್ಕೊಲಾ ತೂಲೆ [[Special:NewPages|ಪೊಸ ಪುಟೊದ ಪಟ್ಟಿ]])",
+       "recentchanges-submit": "ತೋಜಾಲೆ",
        "rclistfrom": "$3 $2 ರ್ದ್ ಸುರುವಾತಿನ ಪೊಸ ಬದಲಾವಣೆಲೆನ್ ತೊಜ್ಪಾವು",
        "rcshowhideminor": "$1 ಎಲ್ಯೆಲ್ಯ ಬದಲಾವಣೆಲು",
        "rcshowhideminor-show": "ತೋಜಾಲೆ",
        "rcshowhidebots-show": "ತೊಜಾವು",
        "rcshowhidebots-hide": "ಅಡೆಂಗಾವು",
        "rcshowhideliu": "ನೋಂದವಣೆ ಆತಿನಂಚಿನ ಸದಸ್ಯೆರ್ $1",
+       "rcshowhideliu-show": "ತೋಜಾಲೆ",
        "rcshowhideliu-hide": "ಅಡೆಂಗಾವು",
        "rcshowhideanons": "ಪುದರ್ ಇದ್ಯಾಂದಿನ ಸದಸ್ಯೆರ್ $1",
        "rcshowhideanons-show": "ತೋಜಾಲೆ",
        "rcshowhideanons-hide": "ಅಡೆಂಗಾವು",
        "rcshowhidepatr": "$1 ಪರೀಕ್ಷಿಸಾದಿನ ಸಂಪಾದನೆಲು",
+       "rcshowhidepatr-show": "ತೋಜಾಲೆ",
+       "rcshowhidepatr-hide": "ಅಡೆಂಗಾವು",
        "rcshowhidemine": "ಎನ್ನ ಸಂಪಾದನೆಲೆನ್ $1",
        "rcshowhidemine-show": "ತೋಜಾಲೆ",
        "rcshowhidemine-hide": "ಅಡೆಂಗಾವು",
+       "rcshowhidecategorization-show": "ತೋಜಾಲೆ",
+       "rcshowhidecategorization-hide": "ಅಡೆಂಗಾವು",
        "rclinks": "ದುಂಬುದ $2 ದಿನೊಲೆಡ್ ಮಲ್ತಿನ $1 ಪಿರವುದ ಬದಲಾವಣೆಲೆನ್ ತೂಲೆ <br />$3",
        "diff": "ವ್ಯತ್ಯಾಸೊ",
        "hist": "ಇತಿಹಾಸೊ",
        "rc_categories_any": "ಒವ್ವೇ",
        "rc-change-size-new": "$1 {{PLURAL:$1|ಬೈಟ್|ಬೈಟ್‍ಲು}}ಬದಲಾವಣೆಡ್ದ್ ಬುಕ್ಕೊ",
        "newsectionsummary": "\n/* $1 */ಪೊಸ ವಿಭಾಗ",
-       "rc-enhanced-expand": "ವಿವರà³\8aಲà³\86ನà³\8d à²¤à³\8aà²\9cà³\8dಪಾವà³\81 (à²\9cಾವ à²¸à³\8dà²\95à³\8dರಿಪà³\8dà²\9fà³\8d à²¬à³\8bಡಾಪà³\81à²\82ಡà³\81)",
+       "rc-enhanced-expand": "ವಿವರà³\8aಲà³\86ನà³\8d à²¤à³\8aà²\9cಾವà³\8d",
        "rc-enhanced-hide": "ವಿವರೊಲೆನ್ ದೆಂಗಾವು",
        "recentchangeslinked": "ಸಂಬಂದೊ ಉಪ್ಪುನಂಚಿನ ಬದಲಾವಣೆಲು",
        "recentchangeslinked-feed": "ಸಂಬಂಧ ಉಪ್ಪುನಂಚಿನ ಬದಲಾವಣೆಲು",
        "uploadlogpage": "ಅಪ್ಲೋಡ್ ದಾಖಲೆ",
        "filename": "ಕಡತದ ಪುದರ್",
        "filedesc": "ಸಾರಾಂಸೊ",
+       "fileuploadsummary": "ಸಾರಾಂಸೊ:",
        "filesource": "ಮೂಲ",
        "savefile": "ಕಡತನ್ ಒರಿಪಾಲೆ",
        "upload-source": "ಮೂಲ ಕಡತ",
+       "upload-options": "ಅಪ್ಲೋಡ್ ಆಯ್ಕೆಲು",
+       "watchthisupload": "ಈ ಪುಟೊನು ತೂಲೆ",
+       "upload-file-error": "ಆ೦ತರಿಕ ದೋಷ",
+       "upload-dialog-title": "ಫೈಲ್ ಅಪ್ಲೋಡ್",
+       "upload-dialog-button-cancel": "ವಜಾ ಮಲ್ಪುಲೆ",
+       "upload-dialog-button-done": "ಆಂಡ್",
+       "upload-dialog-button-save": "ಒರಿಪಾಲೆ",
+       "upload-dialog-button-upload": "ಅಪ್ಲೊಡ್",
+       "upload-form-label-infoform-title": "ವಿವರೊ",
+       "upload-form-label-infoform-name": "ಪುದರ್",
+       "upload-form-label-infoform-description": "ವಿವರಣೆ",
+       "upload-form-label-usage-filename": "ಕಡತದ ಪುದರ್",
+       "upload-form-label-infoform-categories": "ವರ್ಗೊಲು",
+       "upload-form-label-infoform-date": "ದಿನೊ",
+       "license": "ಪರವಾನಗಿ:",
        "license-header": "ಪರವಾನಿಗೆ",
+       "listfiles-delete": "ಮಾಜಾಲೆ",
        "imgfile": "ಫೈಲ್",
+       "listfiles_thumb": "ಎಲ್ಯಚಿತ್ರೊ",
+       "listfiles_date": "ದಿನೊ",
+       "listfiles_name": "ಪುದರ್",
+       "listfiles_user": "ಬಳಕೆದಾರೆರ್",
+       "listfiles_size": "ಗಾತ್ರೊ",
+       "listfiles_description": "ವಿವರಣೆ",
+       "listfiles_count": "ಆವೃತ್ತಿಲು",
+       "listfiles-latestversion": "ಪ್ರಸಕ್ತ ಆವೃತ್ತಿ",
+       "listfiles-latestversion-yes": "ಅಂದ್",
+       "listfiles-latestversion-no": "ಅತ್ತ್",
        "file-anchor-link": "ಫೈಲ್",
        "filehist": "ಫೈಲ್‍ದ ಇತಿಹಾಸೊ",
        "filehist-help": "ದಿನೊ/ಪೊರ್ತುದ ಮಿತ್ತ್ ಒತ್ತ್‌ಂಡ ಈ ಫೈಲ್‍ದ ನಿಜೊಸ್ತಿತಿ ತೋಜುಂಡು.",
        "filehist-deleteall": "ಮಾತಾ ಮಾಜಾಲೆ",
        "filehist-deleteone": "ಮಾಜಾಲೆ",
+       "filehist-revert": "ದುಂಬುದ ಲೆಕ ಮಲ್ಪುಲೆ",
        "filehist-current": "ಇತ್ತೆದ",
        "filehist-datetime": "ದಿನೊ/ಪೊರ್ತು",
        "filehist-thumb": "ಎಲ್ಯಚಿತ್ರೊ",
        "filehist-thumbtext": "$1ತ ಆವೃತ್ತಿದ ಎಲ್ಯಚಿತ್ರೊ",
+       "filehist-nothumb": "ಎಲ್ಯಚಿತ್ರೊ ಇಜ್ಜಿ",
        "filehist-user": "ಬಳಕೆದಾರೆರ್",
        "filehist-dimensions": "ಆಯಾಮೊಲು",
        "filehist-filesize": "ಫೈಲ್’ದ ಗಾತ್ರ",
        "imagelinks": "ಫೈಲ್‍ದ ಬಳಕೆ",
        "linkstoimage": "ಈ ತಿರ್ತ್‍ದ {{PLURAL:$1|ಪುಟ|$1 ಪುಟೊಲೆ ಕೊಂಡಿ}}ಈ ಫೈಲ್‍ಗ್ ಕೊರ್ಪುಂಡು.",
        "nolinkstoimage": "ಈ ಫೈಲ್‍ಗ್ ಸಂಪರ್ಕೊ ಉಪ್ಪುನ ವಾ ಪುಟೊಲಾ ಇದ್ದಿ.",
-       "sharedupload": "à²\88 à²«à³\88ಲà³\8dâ\80\99ನà³\8d à²®à²¸à³\8dತà³\8d à²\9cನ à²ªà²\9fà³\8dà²\9fà³\8dâ\80\99ದà³\81ಲà³\8dಲà³\86ರà³\8d à²\85à²\82à²\9aà³\86ನà³\86 à²\89à²\82ದà³\81 à²®à²¸à³\8dತà³\8d à²ªà³\8dರà³\8aà²\9cà³\86à²\95à³\8dà²\9fà³\8dâ\80\99ಲà³\86ಡà³\8d à²\89ಪಯà³\8bà²\97ಡà³\81ಪà³\8dಪà³\81.",
+       "sharedupload": "à²\88 à²«à³\88ಲà³\8dâ\80\99ನà³\8d à²®à²¸à³\8dತà³\8d à²\9cನ à²ªà²\9fà³\8dà²\9fà³\8dâ\80\99ದà³\81ಲà³\8dಲà³\86ರà³\8d à²\85à²\82à²\9aà³\86ನà³\86 à²\89à²\82ದà³\81 à²®à²¸à³\8dತà³\8d à²ªà³\8dರà³\8aà²\9cà³\86à²\95à³\8dà²\9fà³\8dâ\80\99ಲà³\86ಡà³\8d à²\89ಪಯà³\8bà²\97ಿಸà³\8aಲಿ",
        "sharedupload-desc-here": "ಈ ಪುಟೊ $1ಡ್ದ್ ಬೊಕ್ಕ ಬೇತೆ ಯೋಜನೆಡ್ದ್ ಗಳಸೊಲಿ.\nಈ ಪುಟೊತ ವಿವರೊ [$2 ಪುಟೊತ ವಿವರೊ] ತಿರ್ತ ಸಾಲ್‍ಡ್ ತೋಜಾದ್ಂಡ್",
        "upload-disallowed-here": "ಈರ್ ಈ ಫೈಲ್‍ನ್ ಕುಡೊರೊ ಬರೆವರೆ ಸಾದ್ಯೊ ಇದ್ದಿ.",
+       "filerevert-comment": "ಕಾರಣ:",
+       "filerevert-submit": "ದುಂಬುದ ಲೆಕ ಮಲ್ಪುಲೆ",
+       "filedelete": "$1 ನ್ ಮಾಜಾಲೆ",
+       "filedelete-legend": "ಕಡತನ್ ಮಾಜಾಲೆ",
        "filedelete-comment": "ಕಾರಣ",
        "filedelete-submit": "ಮಾಜಾಲೆ",
+       "filedelete-reason-otherlist": "ಬೇತೆ ಕಾರಣ",
+       "download": "ಡೌನ್‍ಲೋಡ್",
        "randompage": "ಯಾದೃಚ್ಛಿಕ ಪುಟೊ",
+       "randomincategory-category": "ವರ್ಗೊ:",
+       "randomincategory-submit": "ಪೋಲೆ",
        "statistics": "ಅಂಕಿ ಅಂಶೊಲು",
        "statistics-header-pages": "ಪುಟೊತ ಅಂಕಿ ಅಂಶಲು",
+       "statistics-pages": "ಪುಟಕುಲು",
+       "statistics-users-active": "ಸಕ್ರಿಯ ಬಳಕೆದಾರೆರ್",
+       "pageswithprop-submit": "ಪೋಲೆ",
+       "brokenredirects-edit": "ಸಂಪೊಲಿಪುಲೆ",
+       "brokenredirects-delete": "ಮಾಜಾಲೆ",
+       "withoutinterwiki-submit": "ತೋಜಾಲೆ",
        "nbytes": "$1 {{PLURAL:$1|ಬೈಟ್|ಬೈಟ್‍ಲು}}",
        "nmembers": "$1 {{PLURAL:$1|ಸದಸ್ಯೆ|ಸದಸ್ಯೆರ್}}",
        "wantedfiles": "ಬೋಡಾಯಿನ ಕಡತೊಲು",
        "prefixindex": "ಪೂರ್ವನಾಮೊಲ್ದ ಸೂಚಿಕೆ",
+       "prefixindex-submit": "ತೋಜಾಲೆ",
+       "shortpages": "ಎಲ್ಯ ಪುಟೊಕುಲು",
+       "longpages": "ಉದ್ದ ಪುಟೊಕುಲು",
+       "protectedpages": "ಸಂರಕ್ಷಿತ ಪುಟೊ",
+       "protectedpages-page": "ಪುಟೊ",
+       "protectedpages-reason": "ಕಾರಣೊ",
+       "protectedpages-submit": "ಪ್ರದರ್ಶಿಶಿಸಾಯಿನ ಪುದರ್",
+       "protectedpages-unknown-timestamp": "ಗೊತ್ತಿಜ್ಜಾಂದಿನ",
+       "protectedpages-unknown-performer": "ಅಜ್ಞಾತ ಬಳಕೆದಾರೆ",
+       "protectedtitles": "ಸಂರಕ್ಷಿತ ಶೀರ್ಷಿಕೆಲು",
        "listusers": "ಬಳಕೆದಾರರೆನ ತಖ್ತೆ",
        "newpages": "ಪೊಸ ಪುಟೊಲು",
+       "newpages-submit": "ತೋಜಾಲೆ",
        "newpages-username": "ಸದಸ್ಯೆರ್ನ ಪುದರ್:",
        "move": "ಮೂವ್(ಸ್ಥಳಾಂತರ) ಮಲ್ಪುಲೆ",
        "movethispage": "ಈ ಪುಟೊನು ಮೂವ್ ಮಲ್ಪುಲೆ",
        "pager-newer-n": "{{PLURAL:$1|ಪೊಸ ೧|ಪೊಸ $1}}",
        "pager-older-n": "{{PLURAL:$1|ಪರತ್ತ್ ೧|ಪರತ್ತ್ $1}}",
+       "apisandbox-reset": "ಮಾಜಲೇ",
+       "apisandbox-retry": "ನನೊರ ಪ್ರಯತ್ನ ಮಾನ್ಪುಲೇ",
+       "apisandbox-examples": "ಉದಾಹರಣೆಲು",
+       "apisandbox-results": "ಪಲಿತಾಂಸೊ",
        "booksources": "ಬೂಕುದ ಮೂಲೊ",
        "booksources-search-legend": "ಬೂಕುದ ಮೂಲೊನು ನಾಡ್‍ಲೆ",
        "booksources-search": "ನಾಡ್‍ಲೆ",
        "log": "ದಾಕಲೆಲು",
+       "logeventslist-submit": "ತೋಜಾಲೆ",
+       "checkbox-all": "ಮಾತಾ",
+       "checkbox-none": "ಒವ್ವುಲಾ ಇಜ್ಜಿ",
        "allpages": "ಪೂರಾ ಪೂಟೊಲು",
        "allpagesfrom": "ಇಂದೆರ್ದ್ ಶುರುವಾಪುನ ಪುಟೊಲೆನ್ ತೊಜ್ಪಾವು:",
        "allpagesto": "ಇಂದೆರ್ದ್ ಅಂತ್ಯ ಆಪುನ ಪುಟೊಲೆನ್ ತೊಜ್ಪಾವು:",
        "allarticles": "ಮಾತ ಪುಟೊಲು",
        "allpagessubmit": "ಪೋಲೆ",
        "categories": "ವರ್ಗೊಲು",
+       "categories-submit": "ತೋಜಾಲೆ",
+       "sp-deletedcontributions-contribs": "ಕಾನಿಕೆಲು",
+       "linksearch-ok": "ನಾಡ್‍ಲೆ",
+       "listusers-submit": "ತೋಜಾಲೆ",
+       "listgrouprights-group": "ಗುಂಪು",
        "listgrouprights-members": "(ಸದಸ್ಯೆರ್ನ ಪಟ್ಟಿ)",
+       "listgrants-rights": "ಹಕ್ಕುಗಳು",
        "emailuser": "ಈ ಸದಸ್ಯೆರೆಗ್ ಇ-ಮೈಲ್ ಕಡಪುಡ್ಲೆ",
+       "emailusername": "ಸದಸ್ಯೆರ್ನ ಪುದರ್:",
+       "emailusernamesubmit": "ಒಪ್ಪಿಸಾಲೆ",
+       "emailsubject": "ವಿಷಯ:",
+       "emailmessage": "ಸಂದೇಶಲು:",
+       "emailsend": "ಕಡಪುಡುಲೆ",
        "watchlist": "ವೀಕ್ಷಣಾ ಪಟ್ಟಿ",
        "mywatchlist": "ಎನ್ನ ವೀಕ್ಷಣಾಪಟ್ಟಿ",
+       "watchnologin": "ಲಾಗಿನ್ ಆತ್‍ಜರ್",
        "watch": "ತೂಲೆ",
        "watchthispage": "ಈ ಪುಟೊನು ತೂಲೆ",
        "unwatch": "ವೀಕ್ಷಣಾಪಟ್ಟಿರ್ದ್ ದೆಪ್ಪು",
+       "watchlist-hide": "ಅಡೆಂಗಾವು",
+       "watchlist-submit": "ತೋಜಾವು",
+       "wlshowhideminor": "ಎಲ್ಯೆಲ್ಯ ಬದಲಾವಣೆಲು",
+       "wlshowhideliu": "ನೋಂದವಣೆ ಆತಿನಂಚಿನ ಸದಸ್ಯೆರ್",
+       "wlshowhideanons": "ಪುದರ್ ಇದ್ಯಾಂದಿನ ಸದಸ್ಯೆರ್",
        "watchlist-options": "ವೀಕ್ಷಣಾಪಟ್ಟಿ ಆಯ್ಕೆಲು",
        "watching": "ವೀಕ್ಷಣಾಪಟ್ಟಿಗ್ ಸೇರ್ಪಾವೊಂದುಂಡು...",
        "unwatching": "ವೀಕ್ಷಣಾಪಟ್ಟಿರ್ದ್ ದೆತ್ತೊಂದುಂಡು...",
+       "deletepage": "ಪುಟೊಕುಲೆನ್ ಮಾಜಾಲೆ",
+       "confirm": "ಗಟ್ಟಿಮಲ್ಪುಲೆ",
+       "delete-legend": "ಮಾಜಾಲೆ",
+       "historyaction-submit": "ತೋಜಾಲೆ",
        "actioncomplete": "ಕಾರ್ಯ ಸಂಪೂರ್ಣ",
        "dellogpage": "ಡಿಲೀಟ್ ಮಲ್ತಿನ ಫೈಲ್‍ದ ದಾಕಲೆ",
+       "deletionlog": "ಡಿಲೀಟ್ ಮಲ್ತಿನ ಫೈಲ್‍ದ ದಾಕಲೆ",
+       "deletecomment": "ಕಾರಣ:",
+       "deletereasonotherlist": "ಬೇತೆ ಕಾರಣ",
+       "delete-edit-reasonlist": "ಮಾಜಾಯಿನ ಕಾರಣೊಲೆನ್ ಸಂಪಾದನೆ ಮಲ್ಪುಲೆ",
        "rollbacklink": "ಪುಡತ್ತ್ ಪಾಡ್",
        "rollbacklinkcount": "ಪಿರ ದೆತೊನ್ಲೆ $1 {{PLURAL:$1|edit|ಸಂಪದನೆಲು}}",
+       "changecontentmodel-title-label": "ಪುಟೊದ ಪುದರ್",
+       "changecontentmodel-reason-label": "ಕಾರಣ:",
+       "changecontentmodel-submit": "ಬದಲಾವಣೆ",
+       "logentry-contentmodel-change-revertlink": "ದುಂಬುದ ಲೆಕ ಮಲ್ಪುಲೆ",
+       "logentry-contentmodel-change-revert": "ದುಂಬುದ ಲೆಕ ಮಲ್ಪುಲೆ",
        "protectlogpage": "ಸೇರಾಯಿನ ದಾಕಲೆ",
        "protectedarticle": "\"[[$1]]\" ಸಂರಕ್ಷಿತವಾದುಂಡು.",
        "modifiedarticleprotection": "\"[[$1]]\" ಪುಟೊತ ಸಂರಕ್ಷಣೆ ಮಟ್ಟ ಬದಲಾಂಡ್",
+       "protectcomment": "ಕಾರಣೊ:",
+       "protect-otherreason-op": "ಬೇತೆ ಕಾರಣ",
+       "restriction-type": "ಒಪ್ಪುಗೆ:",
+       "restriction-edit": "ಸಂಪಾದನೆ ಮಲ್ಪುಲೆ",
+       "restriction-move": "ಸ್ಥಳಾಂತರ ಮಲ್ಪುಲೆ",
+       "restriction-create": "ಸೃಷ್ಟಿಸಾಲೆ",
+       "restriction-upload": "ಅಪ್ಲೊಡ್",
        "undeletelink": "ದುಂಬುದ ಆವೃತ್ತಿಗ್ ಪೋಲೆ",
        "undeleteviewlink": "ತೂಲೆ",
+       "undeletecomment": "ಕಾರಣೊ:",
+       "undelete-search-submit": "ನಾಡ್‍ಲೆ",
+       "undelete-show-file-submit": "ಅಂದ್",
        "namespace": "ಪುದರ್‍ದ ಬಗೆ:",
        "invert": "ಆಯ್ಕೆನ್ ತಿರ್ಗಾಲೆ",
        "tooltip-invert": "ಈ ಚೌಕೊದುಲಯಿಡ್ ಅಡೆಂಗಿನ ಪುದರ್‍ನ್ ಈ ಚೌಕೊಡೆ ಪರೀಕ್ಷಿಸಲೆಲೆ(ಬೊಕ್ಕೊ ಒಟ್ಟುಗಿತ್ತಿನ ಪುದರ್‍ನ್ಲಾ ಪರೀಕ್ಷಿಸವೊಲಿ)",
        "whatlinkshere-hidetrans": "$1 ಸೇರಾವುನವು",
        "whatlinkshere-hidelinks": "$1 ಕೊಂಡಿಕುಲು",
        "whatlinkshere-filters": "ಅರಿಪೆಲು",
+       "whatlinkshere-submit": "ಪೋಲೆ",
        "blockip": "ಈ ಸದಸ್ಯೆರೆನ್ ಬ್ಲಾಕ್ ಮಲ್ಪುಲೆ",
+       "ipbreason": "ಕಾರಣೊ:",
        "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",
        "ipblocklist": "ತಡೆಪತ್ತ್’ದಿನ ಐ.ಪಿ ವಿಳಾಸೊಲು ಅಂಚೆನೆ ಬಳಕೆದ ಪುದರ್’ಲು",
+       "blocklist-target": "ಗುರಿ",
+       "blocklist-reason": "ಕಾರಣೊ",
+       "ipblocklist-submit": "ನಾಡ್‍ಲೆ",
        "blocklink": "ಅಡ್ಡ ಪತ್ತ್‌ಲೆ",
        "unblocklink": "ಅಡ್ಡನ್ ದೆಪ್ಪುಲೆ",
        "change-blocklink": "ಬ್ಲಾಕ್’ನ್ ಬದಲಾಲೆ",
        "contribslink": "ಕಾಣಿಕೆಲು",
+       "emaillink": "ಇ-ಅಂಚೆ ಕಡಪುಡುಲೆ",
        "blocklogpage": "ತಡೆಪತ್ತ್’ದ್’ನ ಸದಸ್ಯೆರ್ನ ದಿನಚರಿ",
        "blocklogentry": "[[$1]] ಖಾತೆನ್ $2 $3 ಮುಟ್ಟ ತಡೆಪತ್ತ್’ದ್’ನ್ಡ್",
        "unblocklogentry": "$1 ಖಾತೆನ್ ಅನ್-ಬ್ಲಾಕ್ ಮಲ್ತ್’ನ್ಡ್",
        "block-log-flags-nocreate": "ಖಾತೆ ಸೃಷ್ಟಿನ್ ತಡೆಪತ್ತ್’ದ್’ನ್ಡ್",
        "movelogpage": "ಸ್ತಲಾಂತರೊದ ದಾಕಲೆ",
+       "movereason": "ಕಾರಣೊ:",
        "revertmove": "ದುಂಬುದ ಲೆಕೆ ಮಲ್ಪುಲೆ",
        "export": "ಪುಟೊಲೆನ್ ಕಡಪುಡ್ಲೆ",
+       "export-submit": "ರಫ್ತು ಮಲ್ಪುಲೆ",
+       "export-addcat": "ಸೇರಾಲೆ",
+       "export-addns": "ಸೇರಾಲೆ",
+       "export-download": "ಕಡತನ್ ಒರಿಪಾಲೆ",
        "allmessagesname": "ಪುದರ್",
+       "allmessages-filter-legend": "ಅರಿಪೆ",
+       "allmessages-filter-all": "ಮಾತಾ",
+       "allmessages-filter-modified": "ಬದಲಾಯಿನ",
+       "allmessages-language": "ಬಾಸೆ:",
+       "allmessages-filter-submit": "ಪೋ",
+       "allmessages-filter-translate": "ಭಾಷಾಂತರ ಮಲ್ಪುಲೆ",
        "thumbnail-more": "ಮಲ್ಲೆ ಮಲ್ಪುಲೆ",
        "thumbnail_error": "ಮುನ್ನೋಟ ಚಿತ್ರೊನು ಸೃಷ್ಟಿ ಮನ್ಪುನಗ ದೋಷ: $1",
+       "import": "ಪುಟೊಲೆನ್ ಕಡಪುಡ್ಲೆ",
+       "import-interwiki-sourcepage": "ಮೂಲ ಪುಟ",
+       "import-interwiki-submit": "ಆಮದು",
+       "import-upload-filename": "ಕಡತದ ಪುದರ್:",
+       "import-comment": "ಅಭಿಪ್ರಾಯೊ:",
        "tooltip-pt-userpage": "{{GENDER:|ಎನ್ನ ಸದಸ್ಯ}} ಪುಟೊ",
        "tooltip-pt-mytalk": "{{GENDER:|ಎನ್ನ}} ಚರ್ಚೆ ಪುಟೊ",
        "tooltip-pt-preferences": "{{GENDER:|ಎನ್ನ}} ಇಸ್ಟೊಲು",
        "tooltip-undo": "\"ವಜಾ ಮಲ್ಪುಲೆ\" ಈ ಬದಲಾವಣೆನ್ ದೆತೊನುಜಿ ಬುಕ್ಕೊ ಪ್ರಿವ್ಯೂ ಮೋಡ್‍ಡ್ ಬದಲಾವಣೆ ಮಲ್ಪೆರ್ ಕೊನೊಪು೦ಡು. ಅ೦ಚೆನೆ ಸಾರಾಂಸೊಡು ಬದಲಾವಣೆಗ್ ಕಾರಣ ಸೇರಾಯರ ಆಪು೦ಡು.",
        "tooltip-summary": "ಒಂಜಿ ಎಲ್ಯ ಸಾರಾಂಸೊ ಕೊರ್ಲೆ",
        "simpleantispam-label": "ಯಾಂಟಿ-ಸ್ಪಾಮ್ ಚೆಕ್.\nಮುಲ್ಪ <strong>ದಿಂಜಾವೊಡ್ಚಿ</strong>",
+       "pageinfo-article-id": "ಪುಟೊದ ಐಡಿ",
+       "pageinfo-content-model-change": "ಬದಲಾವಣೆಲು",
        "pageinfo-toolboxlink": "ಪುಟೊದ ಮಾಹಿತಿ",
+       "pageinfo-contentpage-yes": "ಅಂದ್",
+       "pageinfo-protect-cascading-yes": "ಅಂದ್",
+       "pageinfo-category-pages": "ಪುಟೊಕುಲೆ ಸಂಕ್ಯೆ",
        "previousdiff": "← ದುಂಬುದ ಸಂಪದನೆ",
        "nextdiff": "ಬುಕ್ಕೊದ ಸಂಪದನೆ →",
+       "thumbsize": "ಕಿರುನೋಟದ ಗಾತ್ರೊ:",
        "file-info-size": "$1 × $2 ಚಿತ್ರಬಿಂದುಲು, ಫೈಲ್‍ದ ಗಾತ್ರೊ: $3, MIME ಪ್ರಕಾರೊ: $4",
        "file-nohires": "ಇಂದೆರ್ದ್ ಜಾಸ್ತಿ ರೆಸಲ್ಯೂಶನ್ ಇದ್ದಿ,",
        "svg-long-desc": "ಎಸ್.ವಿ.ಜಿ ಫೈಲ್, ಸುಮಾರಾದ್ $1 × $2 ಚಿತ್ರೊಬಿಂದು, ಫೈಲ್‍ದ ಗಾತ್ರ: $3",
        "show-big-image-preview": "ಪಿರವುದ ಪುಟೊದ ಗಾತ್ರೊ: $1.",
        "show-big-image-other": "ಬೇತೆ{{PLURAL:$2|resolution|ನಿರ್ನಯೊಲು}}: $1.",
        "show-big-image-size": "$1 × $2 ಚಿತ್ರೊಬಿಂದುಲು",
+       "newimages-legend": "ಅರಿಪೆ",
+       "ilsubmit": "ನಾಡ್‍ಲೆ",
        "bad_image_list": "ವ್ಯವಸ್ಥೆದ ಆಕಾರ ಈ ರೀತಿ ಉಂಡು:\n\nಪಟ್ಟಿಡುಪ್ಪುನಂಚಿನ ದಾಖಲೆಲೆನ್ (* ರ್ದ್ ಶುರು ಆಪುನ ಸಾಲ್’ಲು) ಮಾತ್ರ ಪರಿಗಣನೆಗ್ ದೆತೊನೆರಾಪುಂಡು.\nಪ್ರತಿ ಸಾಲ್’ದ ಶುರುತ ಲಿಂಕ್ ಒಂಜಿ ದೋಷ ಉಪ್ಪುನಂಚಿನ ಫೈಲ್’ಗ್ ಲಿಂಕಾದುಪ್ಪೊಡು.\nಅವ್ವೇ ಸಾಲ್’ದ ಶುರುತ ಪೂರಾ ಲಿಂಕ್’ಲೆನ್ ಪರಿಗನೆರ್ದ್ ದೆಪ್ಪೆರಾಪುಂಡು, ಪಂಡ ಓವು ಪುಟೊಲೆಡ್ ಫೈಲ್’ದ ಬಗ್ಗೆ ಬರ್ಪುಂಡೋ ಔಲು.",
        "metadata": "ಮೆಟಾಡೇಟಾ",
        "metadata-help": "ಈ ಪೈಲ್‍ಡ್ ಜಾಸ್ತಿ ಮಾಹಿತಿ ಉಂಡು. ಹೆಚ್ಚಿನಂಸೊ ಪೈಲ್‍ನ್ ಉಂಡು ಮಲ್ಪೆರೆ ಉಪಯೋಗ ಮಲ್ತಿನ ಡಿಜಿಟಲ್ ಕ್ಯಾಮೆರರ್ದ್ ಅತ್ತ್ಂಡ ಸ್ಕ್ಯಾನರ್‌ರ್ದ್ ಈ ಮಾಹಿತಿ ಬತ್ತ್ಂಡ್.\nಮೂಲಪ್ರತಿರ್ದ್ ಈ ಪೈಲ್ ಬದಲಾದಿತ್ತ್ಂಡ್, ಈ ಮಾಹಿತಿ ಬದಲಾತಿನ ಪೈಲ್‍ದ ವಿವರೊಲೆಗ್ ಸರಿಯಾದ್ ಹೊಂದಂದೆ ಉಪ್ಪು.",
        "metadata-expand": "ವಿಸ್ತಾರವಾಯಿನ ವಿವರೊಲೆನ್ ತೊಜ್ಪಾವು",
        "metadata-collapse": "ವಿಸ್ತಾರವಾಯಿನ ವಿವರೊಲೆನ್ ದೆಂಗಾವು",
        "metadata-fields": "ಈ ಸಂದೇಸೊಡು ಪಟ್ಟಿ ಮಲ್ತಿನಂಚಿನ EXIF ಮಿತ್ತ ದರ್ಜೆದ ಮಾಹಿತಿನ್ ಚಿತ್ರೊ ಪುಟೊಕು ಸೇರ್ಪಾಯೆರೆ ಆವೊಂದುಂಡು. ಪುಟೊಟು ಮಿತ್ತ ದರ್ಜೆ ಮಾಹಿತಿದ ಪಟ್ಟಿನ್ ದೆಪ್ಪುನಗ ಉಂದು ತೋಜುಂಡು.\nಒರಿದನವು ಮೂಲೊ ಸ್ಥಿತಿಟ್ ಅಡೆಂಗ್‍ದುಂಡು.\n*ಮಲ್ಪುಲೆ\n*ಮಾದರಿ\n*ದಿನೊ ಪೊರ್ತು ಮೂಲೊ\n*ಮಾನಾದಿಗೆದ ಸಮಯೊ\n*ಫ್‍ಸಂಖ್ಯೆ\n*ಐಎಸ್ಒ ವೇಗೊದ ರೇಟಿಂಗ್\n*ತೂಪಿನ ಜಾಗೆದ ದೂರ\n*ಕಲಾವಿದೆ\n*ಕೃತಿಸ್ವಾಮ್ಯೊ\n*ಚಿತ್ರೊ ವಿವರಣೆ\n*ಜಿಪಿಎಸ್ ಅಕ್ಷಾಂಸೊ\n*ಜಿಪಿಎಸ್ ರೇಖಾಂಸೊ\n*ಜಿಪಿಎಸ್ ಎತ್ತರೊ",
+       "exif-imagewidth": "ಅಗೆಲ",
+       "exif-imagelength": "ಎತ್ತರೊ",
        "exif-orientation": "ದಿಕ್ಕ್ ದಿಸೆ",
        "exif-xresolution": "ಅಡ್ಡಗಲೊದ ರೆಜ಼ಲ್ಯೂಶನ್",
        "exif-yresolution": "ಉದ್ದೊದ ರೆಜ಼ಲ್ಯೂಶನ್",
        "exif-make": "ಕ್ಯಾಮರೊದ ತಯಾರೆಕೆರ್",
        "exif-model": "ಕ್ಯಾಮರೊದ ಮಾದರಿ",
        "exif-software": "ಉಪಯೋಗೊ ಮಲ್ತಿನ ತಂತ್ರಾಂಸೊ",
+       "exif-artist": "ಬರೆತಿನಾರ್",
+       "exif-copyright": "ಹಕ್ಕುದಾರೆ",
        "exif-exifversion": "Exif ಆವೃತ್ತಿ",
        "exif-colorspace": "ಬಣ್ಣೊದ ಜಾಗೆ",
        "exif-datetimeoriginal": "ಮಾಹಿತಿ ಸ್ರಿಸ್ಟಿಸಯಿನ ದಿನೊ ಬೊಕ್ಕ ಪೊರ್ತು",
        "exif-datetimedigitized": "ಗಣಕೀಕರಣೊದ ದಿನೊ ಬೊಕ್ಕ ಪೊರ್ತು",
+       "exif-flash": "ಫ್ಲ್ಯಾಶ್",
+       "exif-source": "ಮೂಲೊ",
+       "exif-languagecode": "ಭಾಸೆ",
+       "exif-iimcategory": "ವರ್ಗೊ",
+       "exif-label": "ಗುರುತು ಪಟ್ಟಿ",
        "exif-orientation-1": "ಸಾದಾರನೊ",
+       "exif-meteringmode-1": "ಸರಾಸರಿ",
+       "exif-meteringmode-255": "ಇತರೊ",
+       "exif-lightsource-0": "ಗೊತ್ತಿಜ್ಜಾಂದಿನ",
+       "exif-lightsource-4": "ಫ್ಲ್ಯಾಶ್",
+       "exif-contrast-0": "ಸಾದಾರನೊ",
+       "exif-saturation-0": "ಸಾದಾರನೊ",
+       "exif-subjectdistancerange-0": "ಗೊತ್ತಿಜ್ಜಾಂದಿನ",
+       "exif-iimcategory-hth": "ಆರೋಗ್ಯ",
        "namespacesall": "ಮಾತ",
        "monthsall": "ಮಾತ",
+       "confirm_purge_button": "ಸರಿ",
+       "confirm-watch-button": "ಸರಿ",
+       "confirm-unwatch-button": "ಸರಿ",
+       "confirm-rollback-button": "ಸರಿ",
+       "quotation-marks": "\"$1\"",
+       "imgmultipageprev": "← ದುಂಬುತ ಪುಟೊ",
+       "imgmultipagenext": "ನನತಾ ಪುಟ →",
+       "img-lang-go": "ಪೋಲೆ",
+       "table_pager_next": "ನನತಾ ಪುಟ",
+       "table_pager_prev": "ದುಂಬುತ ಪುಟೊ",
+       "table_pager_first": "ಸುರುತ ಪುಟೊ",
+       "table_pager_last": "ಕಡೆತ ಪುಟೊ",
+       "table_pager_limit_submit": "ಪೋಲೆ",
+       "watchlistedit-raw-titles": "ತರೆಬರವು:",
        "watchlistedit-clear-title": "ತುಯಿನೇನ್ ಮಾಜಾಲೇ",
+       "watchlistedit-clear-legend": "ತುಯಿನೇನ್ ಮಾಜಾಲೇ",
+       "watchlistedit-clear-titles": "ತರೆಬರವು:",
        "watchlisttools-view": "ಪ್ರಸ್ತುತ ಬದಲಾವಣೆಲ್ ತೋಜಾಲೆ",
        "watchlisttools-edit": "ವೀಕ್ಷಣಾಪಟ್ಟಿನ್ ತೂಲೆ ಬೊಕ್ಕ ಎಡಿಟ್ ಮಲ್ಪುಲೆ",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|ಪಾತೆರ್ಲೆ]])",
+       "version": "ಆವೃತ್ತಿ",
+       "version-specialpages": "ವಿಸೇಸೊ ಪುಟೊಲು",
+       "version-other": "ಇತರೊ",
+       "version-ext-license": "ಪರವಾನಗಿ",
+       "version-ext-colheader-name": "ವಿಸ್ತರಣೆ",
+       "version-skin-colheader-name": "ಸ್ಕಿನ್",
+       "version-ext-colheader-version": "ಆವೃತ್ತಿ",
+       "version-ext-colheader-license": "ಪರವಾನಗಿ",
+       "version-ext-colheader-description": "ವಿವರಣೆ",
+       "version-ext-colheader-credits": "ಲೇಖಕೆರ್",
+       "version-poweredby-others": "ಇತರೊ",
+       "version-software-product": "ಉತ್ಪನ್ನ",
+       "version-software-version": "ಆವೃತ್ತಿ",
+       "version-libraries-library": "ಗ್ರಂಥಾಲಯೊ",
+       "version-libraries-version": "ಆವೃತ್ತಿ",
+       "version-libraries-license": "ಪರವಾನಗಿ",
+       "version-libraries-description": "ವಿವರಣೆ",
+       "version-libraries-authors": "ಲೇಖಕೆರ್",
+       "redirect-submit": "ಪೋಲೆ",
+       "redirect-value": "ಮೌಲ್ಯ:",
+       "redirect-user": "ಬಳಕೆದಾರೆರ ID",
+       "redirect-page": "ಪುಟೊದ ಐಡಿ",
+       "redirect-file": "ಕಡತದ ಪುದರ್",
+       "fileduplicatesearch-filename": "ಕಡತದ ಪುದರ್:",
+       "fileduplicatesearch-submit": "ನಾಡ್‍ಲೆ",
        "specialpages": "ವಿಸೇಸೊ ಪುಟೊಲು",
+       "blankpage": "ಖಾಲಿ ಪುಟ",
        "tag-filter": "[[Special:Tags|ಟ್ಯಾಗ್]]ಅರಿಪೆ:",
+       "tag-filter-submit": "ಅರಿಪೆ",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag|ಟ್ಯಾಗುಲು}}]]:$2)",
+       "tags-title": "ತೂಗು ಪಟ್ಟಿಲು",
+       "tags-source-header": "ಮೂಲೊ",
+       "tags-active-header": "ಸಕ್ರಿಯ?",
+       "tags-actions-header": "ಕ್ರಿಯೆಕ್ಕುಲು",
+       "tags-active-yes": "ಅಂದ್",
+       "tags-active-no": "ಅತ್ತ್",
+       "tags-edit": "ಸಂಪೊಲಿಪುಲೆ",
+       "tags-delete": "ಮಾಜಾಲೆ",
+       "tags-create-reason": "ಕಾರಣ:",
+       "tags-create-submit": "ಸೃಷ್ಟಿಸಾಲೆ",
+       "tags-delete-reason": "ಕಾರಣ:",
+       "tags-deactivate-reason": "ಕಾರಣ:",
        "logentry-delete-delete": "$1{{GENDER:$2|ಮಾಜಾದ್‍ಂಡ್}}ಪುಟೊ $3",
        "logentry-move-move": "$1 {{GENDER:$2|ಜಾರಲೆ}} ಪುಟೊ $3 ಡ್ದ್ $4",
        "logentry-newusers-create": "ಬಳಕೆದಾರೆರೆ ಕಾತೆ $1 ನ್ನು {{GENDER:$2|ಸ್ರಿಸ್ಟಿ ಮಲ್ತಾಂಡ್}}",
index 86290ea..b49742e 100644 (file)
                        "వైజాసత్య",
                        "아라",
                        "Macofe",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "WP MANIKHANTA"
                ]
        },
        "tog-underline": "లంకె క్రీగీత:",
        "tog-hideminor": "ఇటీవలి మార్పులలో చిన్న మార్పులను దాచు",
        "tog-hidepatrolled": "ఇటీవలి మార్పులలో నిఘా ఉన్న మార్పులను దాచు",
        "tog-newpageshidepatrolled": "కొత్త పేజీల జాబితా నుంచి నిఘా ఉన్న పేజీలను దాచు",
+       "tog-hidecategorization": "పేజీ వర్గీకరణను దాచు",
        "tog-extendwatchlist": "కేవలం ఇటీవలి మార్పులే కాక, మార్పులన్నీ చూపించటానికి నా వీక్షణా జాబితాను పెద్దది చేయి",
        "tog-usenewrc": "ఇటీవలి మార్పులు మరియు వీక్షణ జాబితాలలో మార్పులను పేజీ వారీగా చూపించు",
        "tog-numberheadings": "శీర్షికలకు అప్రమేయంగా వరుస సంఖ్యలు చేర్చు",
@@ -40,6 +42,7 @@
        "tog-watchdefault": "నేను మార్చే పేజీలను మరియు దస్త్రాలను నా వీక్షణ జాబితాకు చేర్చు",
        "tog-watchmoves": "నేను తరలించిన పేజీలను మరియు దస్త్రాలను నా వీక్షణ జాబితాకు చేర్చు",
        "tog-watchdeletion": "నేను తొలగించిన పేజీలను మరియు దస్త్రాలను నా వీక్షణ జాబితాకు చేర్చు",
+       "tog-watchuploads": "నేను ఎక్కించే కొత్త దస్త్రాలను నా వీక్షణ జాబితాకు చేర్చు",
        "tog-watchrollback": "నేను పునస్స్థాపించిన పేజీలను నా వీక్షణ జాబితాకు జోడించు",
        "tog-minordefault": "అప్రమేయంగా నా మార్పులను చిన్న మార్పులుగా గుర్తించు",
        "tog-previewontop": "వ్యాసం మార్పుల మునుచూపును ఎడిట్ పెట్టె పైన చూపు",
        "tog-watchlisthideliu": "లాగిన్ ఐన వాడుకరులు చేసే మార్పులను వీక్షణా జాబితాలో చూపించకు",
        "tog-watchlisthideanons": "అజ్ఞాత వాడుకరుల మార్పులను వీక్షణా జాబితాలో చూపించకు",
        "tog-watchlisthidepatrolled": "నిఘా ఉన్న మార్పులను వీక్షణజాబితా నుంచి దాచిపెట్టు",
+       "tog-watchlisthidecategorization": "పేజీ వర్గీకరణను దాచు",
        "tog-ccmeonemails": "నేను ఇతర వాడుకరులకు పంపించే ఈ-మెయిళ్ల కాపీలను నాకు కూడా పంపు",
        "tog-diffonly": "తేడాల కింద, పేజీలోని సమాచారాన్ని చూపించొద్దు",
        "tog-showhiddencats": "దాచిన వర్గాలను చూపించు",
-       "tog-norollbackdiff": "à°°à°¦à±\8dà°¦à±\81 చేసాక తేడాలు చూపించవద్దు",
+       "tog-norollbackdiff": "à°°à±\8bà°²à±\8dâ\80\8cà°¬à±\8dయాà°\95à±\8d చేసాక తేడాలు చూపించవద్దు",
        "tog-useeditwarning": "ఏదైనా పేజీని నేను వదిలివెళ్తున్నప్పుడు దానిలో భద్రపరచని మార్పులు ఉంటే నన్ను హెచ్చరించు",
        "tog-prefershttps": "లాగిన్ అయి ఉన్నప్పుడెల్లా భద్ర కనెక్షనునే వాడు",
        "underline-always": "ఎల్లప్పుడూ",
        "october-date": "అక్టోబరు $1",
        "november-date": "నవంబరు $1",
        "december-date": "డిసెంబరు $1",
+       "period-am": "ఉద",
+       "period-pm": "సాయం",
        "pagecategories": "{{PLURAL:$1|వర్గం|వర్గాలు}}",
        "category_header": "\"$1\" వర్గంలోని పుటలు",
        "subcategories": "ఉపవర్గాలు",
        "tagline": "{{SITENAME}} నుండి",
        "help": "సహాయం",
        "search": "వెతుకు",
+       "search-ignored-headings": " #<!-- ఈ లైనును ఉన్నది ఉన్నట్లుగా వదిలేయండి --> <pre>\n# వెతుకులాటలో పరిగణింపబడని శీర్షికలు.\n# శీర్షికతో సహా పేజీ ఇండెక్స్ కాగానే మార్పులు వర్తిస్తాయి.\n# ఉత్తుత్తి సవరణ చేయడం ద్వారా బలవంతంగా రీయిండెక్సింగ్ చేయించవచ్చు.\n# వ్యాకరణం ఇలా ఉంటుంది:\n#   * \"#\" కారెక్టరు నుండి లైను చివరి వరకూ ఉన్నదంతా వ్యాఖ్య అవుతుంది\n#   * ఖాళీగా లేని ప్రతీ లైను వెతుకులాటలో పట్టించుకోనక్కర్లేని శీర్షికగా పరిగణింపబడుతుంది.\nమూలాలు\nబయటి లింకులు\nఇవి కూడా చూడండి\n #</pre> <!-- ఈ లైనును ఉన్నది ఉన్నట్లుగా వదిలేయండి -->",
        "searchbutton": "వెతుకు",
        "go": "వెళ్లు",
        "searcharticle": "వెళ్లు",
        "laggedslavemode": "<strong>హెచ్చరిక:</strong> పేజీలో ఇటీవల జరిగిన మార్పులు ఉండకపోవచ్చు.",
        "readonly": "డేటాబేసు లాక్‌చెయ్యబడింది",
        "enterlockreason": "డేటాబేసుకు వేయబోతున్న లాకుకు కారణం తెలుపండి, దానితోపాటే ఎంతసమయం తరువాత ఆ లాకు తీసేస్తారో కూడా తెలుపండి",
-       "readonlytext": "à°¡à±\87à°\9fాబà±\87à°¸à±\81 à°ªà±\8dà°°à°¸à±\8dà°¤à±\81à°¤à°\82 à°²à°¾à°\95à±\81 à°\9aà±\87యబడిà°\82ది. à°®à°¾à°°à±\8dà°ªà±\81à°²à±\81, à°\9aà±\87à°°à±\8dà°ªà±\81à°²à±\81 à°ªà±\8dà°°à°¸à±\8dà°¤à±\81à°¤à°\82 à°\9aà±\86à°¯à±\8dయలà±\87à°°à±\81. à°®à°¾à°®à±\82à°²à±\81à°\97à°¾ à°\9cà°°à°¿à°\97à±\87 à°¨à°¿à°°à±\8dవహణ à°\95à±\8aà°°à°\95à±\81 à°\87ది à°\9cà°°à°¿à°\97à°¿ à°\89à°\82à°¡à°µà°\9aà±\8dà°\9aà±\81; అది పూర్తి కాగానే తిరిగి మామూలుగా పనిచేస్తుంది.\n\nదీనిని లాకు చేసిన నిర్వాహకుడు ఇలా తెలియజేస్తున్నాడు: $1",
+       "readonlytext": "à°ªà±\8dà°°à°¸à±\8dà°¤à±\81à°¤à°\82 à°\95à±\8aà°¤à±\8dà°¤ à°\8eà°\82à°\9fà±\8dà°°à±\80à°²à±\81, à°®à°¾à°°à±\8dà°ªà±\81à°\9aà±\87à°°à±\8dà°ªà±\81à°²à±\81 à°\9aà±\86à°¯à±\8dà°¯à°\95à±\81à°\82à°¡à°¾ à°¡à±\87à°\9fాబà±\87à°¸à±\81 à°²à°¾à°\95à±\81 à°\9aà±\87యబడిà°\82ది. à°®à°¾à°®à±\82à°²à±\81à°\97à°¾ à°\9cà°°à°¿à°\97à±\87 à°¨à°¿à°°à±\8dవహణ à°\95à±\8aà°°à°\95à±\81 à°\87ది à°\9cà°°à°¿à°\97à°¿ à°\89à°\82à°¡à°µà°\9aà±\8dà°\9aà±\81. అది పూర్తి కాగానే తిరిగి మామూలుగా పనిచేస్తుంది.\n\nదీనిని లాకు చేసిన నిర్వాహకుడు ఇలా తెలియజేస్తున్నాడు: $1",
        "missing-article": "\"$1\" $2 అనే పేజీ యొక్క పాఠ్యం డేటాబేసులో దొరకలేదు.\n\nకాలం చెల్లిన తేడా కోసం చూసినపుడుగానీ, తొలగించిన పేజీ చరితం కోసం చూసినపుడుగానీ ఇది సాధారణంగా జరుగుతుంది.\n\nఒకవేళ అలా కాకపోతే, మీరో బగ్‌ను కనుక్కున్నట్టే.\nఈ URLను సూచిస్తూ, దీన్ని ఓ [[Special:ListUsers/sysop|నిర్వాహకునికి]] తెలియజేయండి.",
        "missingarticle-rev": "(కూర్పు#: $1)",
        "missingarticle-diff": "(తేడా: $1, $2)",
        "badtitletext": "మీరు కోరిన పేజీ యొక్క పేరు చెల్లనిది, ఖాళీగా ఉంది, లేదా తప్పు లింకుతో కూడిన అంతర్వికీ లేదా అంతర-భాషా శీర్షిక అయివుండాలి.\nశీర్షికలలో ఉపయోగించకూడని అక్షరాలు దానిలో ఉండివుండొచ్చు.",
        "title-invalid-empty": "కోరబడిన పేజీ శీర్షిక ఖాళీగా ఉంది లేదా కేవలం పేరుబరి పేరు కలిగి ఉంది.",
        "title-invalid-utf8": "కోరబడిన పేజీ శీర్షికలో చెల్లని UTF-8 అక్షరాలున్నాయి.",
-       "title-invalid-interwiki": "à°¶à±\80à°°à±\8dà°·à°¿à°\95 à°ªà°¾à° à±\8dà°¯à°\82à°²à±\8b à°\85à°\82తరవిà°\95à±\80 à°²à°\82à°\95à±\86 à°\89à°\82ది",
+       "title-invalid-interwiki": "à°®à±\80à°°à°¡à°¿à°\97à°¿à°¨ à°ªà±\87à°\9cà±\80 à°¶à±\80à°°à±\8dà°·à°¿à°\95à°²à±\8b à°\85à°\82తర à°µà°¿à°\95à±\80 à°²à°\82à°\95à±\86 à°\89à°\82ది, à°\95ానà±\80 à°\85ది à°¨à°¿à°·à°¿à°¦à±\8dà°§à°\82.",
        "title-invalid-talk-namespace": "మీరడిగిన పేజీ శీర్షిక అసలు సృష్టించే వీలే లేని చర్చా పేజీకి చెందినది.",
        "title-invalid-characters": "కోరబడిన పేజీ శీర్షికలో చెల్లని అక్షరాలున్నాయి : \"$1\".",
        "title-invalid-relative": "శీర్షికలో లంకె పాఠ్యం సాపేక్షంగా ఉంది - పూర్తిగా లేదు. సాపేక్ష పేజీ చిరునామాలు (./, ../) గల పేజీ శీర్షికలు ఎక్కువశాతం అందుబాటులో ఉండవు కనుక అవి చెల్లవు.",
        "title-invalid-magic-tilde": "కోరబడిన పేజీ శీర్షిక పాఠ్యం లో చెల్లని మ్యాజిక్ టిల్డా పదాలున్నాయి (<nowiki>~~~</nowiki>).",
-       "title-invalid-too-long": "à°\95à±\8bరబడిన à°ªà±\87à°\9cà±\80 à°¶à±\80à°°à±\8dà°·à°¿à°\95 à°ªà°¾à° à±\8dà°¯à°\82 à°®à°°à±\80 à°ªà±\8aà°¡à°µà±\81à°\97à°¾ à°\89à°\82ది. à°\87ది UTF-8 à°ªà°¦à±\8dధతిలà±\8b $1 à°¬à±\88à°\9fà±\8dà°²కు మించి ఉండరాదు.",
+       "title-invalid-too-long": "à°®à±\80à°°à°¡à°¿à°\97à°¿à°¨ à°ªà±\87à°\9cà±\80 à°¶à±\80à°°à±\8dà°·à°¿à°\95 à°®à°°à±\80 à°ªà±\8aà°¡à°µà±\81à°\97à°¾ à°\89à°\82ది. à°\87ది UTF-8 à°ªà°¦à±\8dధతిలà±\8b $1 {{PLURAL:$1|à°¬à±\88à°\9fà±\8dâ\80\8c|à°¬à±\88à°\9fà±\8dà°²}}కు మించి ఉండరాదు.",
        "title-invalid-leading-colon": "కోరబడిన పేజీ శీర్షిక పాఠ్యం మొదట్లో చెల్లని కొలొన్ చిహ్నం (:) ఉంది.",
        "perfcached": "కింది డేటా ముందే సేకరించి పెట్టుకున్నది. కాబట్టి తాజా డేటాతో పోలిస్తే తేడాలుండవచ్చు. ఈ కాషెలో గరిష్టంగా {{PLURAL:$1|ఒక్క ఫలితం ఉంది|$1 ఫలితాలు ఉన్నాయి}}.",
        "perfcachedts": "కింది సమాచారం ముందే సేకరించి పెట్టుకున్నది. దీన్ని $1న చివరిసారిగా తాజాకరించారు. ఈ కాషెలో గరిష్టంగా {{PLURAL:$4|ఒక్క ఫలితం ఉంది|$4 ఫలితాలు ఉన్నాయి}}.",
        "viewsource": "మూలాన్ని చూపించు",
        "viewsource-title": "$1 యొక్క సోర్సు చూడండి",
        "actionthrottled": "కార్యాన్ని ఆపేసారు",
-       "actionthrottledtext": "à°¸à±\8dపామà±\81à°¨à±\81 à°¨à°¿à°°à±\8bధిà°\82à°\9aà±\87à°\82à°¦à±\81à°\95à±\81 à°\97ానà±\81, à°¤à°\95à±\8dà°\95à±\81à°µ à°¸à°®à°¯à°\82à°²à±\8b à°®à°°à±\80 à°\8eà°\95à±\8dà°\95à±\81à°µ à°¸à°¾à°°à±\8dà°²à±\81 à°\88 à°ªà°¨à°¿ à°\9aà±\87à°¯à°\95à±\81à°\82à°¡à°¾ à°ªà°°à°¿à°®à°¿à°¤à°¿ à°µà°¿à°§à°¿à°\82à°\9aà°¾à°\82. à°®à±\80à°°à±\81 à°¦à°¾à°¨à±\8dని à°\85ధిà°\97మిà°\82à°\9aారà±\81. à°\95à±\8aà°¨à±\8dà°¨ి నిమిషాలు ఆగి మరలా ప్రయత్నించండి.",
+       "actionthrottledtext": "à°¦à±\81à°¶à±\8dà°\9aà°°à±\8dయనà±\81 à°¨à°¿à°°à±\8bధిà°\82à°\9aà±\87à°\82à°¦à±\81à°\95à±\81 à°\97ానà±\81, à°¤à°\95à±\8dà°\95à±\81à°µ à°¸à°®à°¯à°\82à°²à±\8b à°®à°°à±\80 à°\8eà°\95à±\8dà°\95à±\81à°µ à°¸à°¾à°°à±\8dà°²à±\81 à°\88 à°ªà°¨à°¿ à°\9aà±\87à°¯à°\95à±\81à°\82à°¡à°¾ à°ªà°°à°¿à°®à°¿à°¤à°¿ à°µà°¿à°§à°¿à°\82à°\9aà°¾à°\82. à°®à±\80à°°à±\81 à°¦à°¾à°¨à±\8dని à°\85ధిà°\97మిà°\82à°\9aారà±\81. à°\95à±\8aà°¦à±\8dà°¦ి నిమిషాలు ఆగి మరలా ప్రయత్నించండి.",
        "protectedpagetext": "ఈ పేజీలో మార్పులు వగైరాలు చెయ్యకుండా ఉండేందుకు గాను, సంరక్షించబడింది.",
-       "viewsourcetext": "మీరీ పేజీ సోర్సును చూడవచ్చు, కాపీ చేసుకోవచ్చు:",
-       "viewyourtext": "ఈ పేజీలోని <strong>మీ మార్పుల</strong> యొక్క మూలాన్ని చూడవచ్చు, కాపీచేసుకోవచ్చు:",
+       "viewsourcetext": "మీరీ పేజీ సోర్సును చూడవచ్చు, కాపీ చేసుకోవచ్చు.",
+       "viewyourtext": "ఈ పేజీలో <strong>మీరు చేసిన మార్పుల</strong> యొక్క మూలాన్ని చూడవచ్చు, కాపీచేసుకోవచ్చు.",
        "protectedinterface": "ఈ పేజీ, ఈ వికీ యొక్క సాఫ్టువేరు ఇంటరుఫేసుకు చెందిన టెక్స్టును అందిస్తుంది. దుశ్చర్యల నివారణ కోసమై దీన్ని సంరక్షించాం. వికీలన్నిటిలోను అనువాదాలను చేర్చాలన్నా, మార్చాలన్నా మీడియావికీ స్థానికీకరణ ప్రాజెక్టైన [https://translatewiki.net/ translatewiki.net] ను వాడండి.",
        "editinginterface": "<strong>హెచ్చరిక:</strong> సాఫ్టువేరుకు ఇంటరుఫేసు టెక్స్టును అందించేందుకు పనికొచ్చే పేజీని మీరు సరిదిద్దుతున్నారు.\nఈ పేజీలో చేసే మార్పుల వల్ల ఇతర వాడుకరులకు ఇంటరుఫేసు కనబడే విధానంలో తేడావస్తుంది.",
        "translateinterface": "అన్ని వికీలలో కనిపించేలా అనువాదాలు చేర్చాలన్నా, మార్చాలన్నా, దయచేసి [https://translatewiki.net/ translatewiki.net] ను వాడండి. ఇది మీడియావికీ స్థానికీకరణ ప్రాజెక్టు.",
-       "cascadeprotected": "à°\95à°¿à°\82ది {{PLURAL:$1|à°ªà±\87à°\9cà±\80ని|à°ªà±\87à°\9cà±\80లనà±\81}} à°\95ాసà±\8dà°\95à±\87à°¡à°¿à°\82à°\97à±\81 à°\86à°ªà±\8dà°·à°¨à±\81à°¤à±\8b à°\9aà±\87సి à°¸à°\82à°°à°\95à±\8dà°·à°¿à°\82à°\9aారà±\81. à°ªà±\8dà°°à°¸à±\8dà°¤à±\81à°¤ à°ªà±\87à°\9cà±\80, à°\88 à°ªà±\87à°\9cà±\80à°²à±\8dà°²à±\8b à°\87à°\82à°\95à±\8dà°²à±\82à°¡à±\81 à°\85యి à°\89à°\82ది à°\95ాబà°\9fà±\8dà°\9fà°¿, à°¦à°¿à°¦à±\8dà°¦à±\81బాà°\9fà±\81 à°\9aà±\87à°¸à±\87 à°µà±\80à°²à±\81 à°²à±\87à°\95à±\81à°\82à°¡à°¾ à°\87ది à°\95à±\82à°¡à°¾ à°°à°\95à±\8dషణలà±\8b à°\89à°\82ది.\n$2",
+       "cascadeprotected": "à°\95à°¿à°\82ది {{PLURAL:$1|à°ªà±\87à°\9cà±\80ని|à°ªà±\87à°\9cà±\80లనà±\81}} à°\95ాసà±\8dà°\95à±\87à°¡à°¿à°\82à°\97à±\81 à°\86à°ªà±\8dà°·à°¨à±\81à°¤à±\8b à°¸à°\82à°°à°\95à±\8dà°·à°¿à°\82à°\9aబడిà°\82ది. à°ªà±\8dà°°à°¸à±\8dà°¤à±\81à°¤ à°ªà±\87à°\9cà±\80, à°\88 à°ªà±\87à°\9cà±\80à°²à±\8dà°²à±\8b à°\9fà±\8dరానà±\8dà°¸à±\8dâ\80\8cà°\95à±\8dà°²à±\82à°¡à±\81 à°\85యి à°\89à°\82ది à°\95ాబà°\9fà±\8dà°\9fà°¿, à°¦à°¿à°¦à±\8dà°¦à±\81బాà°\9fà±\81 à°\9aà±\87à°¸à±\87 à°µà±\80à°²à±\81 à°²à±\87à°\95à±\81à°\82à°¡à°¾ à°\87ది à°\95à±\82à°¡à°¾ à°°à°\95à±\8dషణలà±\8b à°\89à°\82ది:\n$2",
        "namespaceprotected": "'''$1''' నేంస్పేసులో మార్పులు చేయటానికి మీకు అనుమతి లేదు.",
        "customcssprotected": "ఈ CSS పేజీని మార్చేందుకు మీకు అనుమతి లేదు. ఎందుకంటే వేరే వాడుకరి యొక్క వ్యక్తిగత సెట్టింగులు అందులో ఉన్నాయి.",
        "customjsprotected": "ఈ JavaScript పేజీని మార్చేందుకు మీకు అనుమతి లేదు. ఎందుకంటే వేరే వాడుకరి యొక్క వ్యక్తిగత సెట్టింగులు అందులో ఉన్నాయి.",
        "virus-scanfailed": "స్కాన్ విఫలమైంది (సంకేతం $1)",
        "virus-unknownscanner": "అజ్ఞాత యాంటీవైరస్:",
        "logouttext": "<strong>ఇప్పుడు మీరు లాగౌటయ్యారు.</strong>\n\nఅయితే, ఓ గమనిక.. మీ విహారిణిలోని కోశాన్ని ఖాళీ చేసేవరకూ కొన్ని పేజీలు మీరింకా లాగినై ఉన్నట్లుగానే చూపించవచ్చు.",
+       "cannotlogoutnow-title": "ఇప్పుడు లాగౌట్ అవలేరు",
+       "cannotlogoutnow-text": "$1 ను వాడుతూండగా లాగౌట్ అవలేరు.",
        "welcomeuser": "స్వాగతం, $1!",
        "welcomecreation-msg": "మీ ఖాతాని సృష్టించాం.\nమీ [[Special:Preferences|{{SITENAME}} అభిరుచులను]] మార్చుకోవడం మరువకండి.\nతెలుగు వికీపీడియాలో తెలుగులోనే రాయాలి. వికీలో రచనలు చేసే ముందు, కింది సూచనలను గమనించండి.\nతెలుగు {{SITENAME}}లో తెలుగులోనే రాయాలి. వికీలో రచనలు చేసే ముందు, కింది సూచనలను గమనించండి.\n*వికీని త్వరగా అర్థం చేసుకునేందుకు [[వికీపీడియా:5 నిమిషాల్లో వికీ|5 నిమిషాల్లో వికీ]] పేజీని చూడండి.\n*తెలుగులో రాసేందుకు ఇంగ్లీషు అక్షరాల ఉచ్ఛారణతో తెలుగు టైపు చేసే [[వికీపీడియా:టైపింగు సహాయం| టైపింగ్  సహాయం]] వాడవచ్చు. మరిన్ని ఉపకరణాల కొరకు [[కీ బోర్డు]] మరియు   తెరపై తెలుగు సరిగా లేకపోతే[[వికీపీడియా:Setting up your browser for Indic scripts|ఈ పేజీ]]  చూడండి.",
        "yourname": "వాడుకరి పేరు:",
        "yourpasswordagain": "సంకేతపదాన్ని మళ్ళీ ఇవ్వండి:",
        "createacct-yourpasswordagain": "సంకేతపదాన్ని నిర్ధారించండి",
        "createacct-yourpasswordagain-ph": "సంకేతపదాన్ని మళ్ళీ ఇవ్వండి",
-       "remembermypassword": "ఈ కంప్యూటరులో నా ప్రవేశాన్ని గుర్తుంచుకో (గరిష్ఠంగా $1 {{PLURAL:$1|రోజు|రోజుల}}కి)",
        "userlogin-remembermypassword": "నన్ను లాగిన్ చేసే ఉంచు",
        "userlogin-signwithsecure": "సురక్షిత కనెక్షను వాడు",
+       "cannotloginnow-title": "ఇప్పుడు లాగిన్ అవలేరు",
+       "cannotloginnow-text": "$1 ను వాడుతూండగా లాగౌట్ అవలేరు.",
        "yourdomainname": "మీ డోమైను",
        "password-change-forbidden": "ఈ వికీలో మీరు సంకేతపదాలను మార్చలేరు.",
        "externaldberror": "డేటాబేసు అధీకరణలో లోపం జరిగింది లేదా మీ బయటి ఖాతాను తాజాకరించడానికి మీకు అనుమతి లేదు.",
        "login": "లాగినవండి",
+       "login-security": "మీ ఐడీని ధ్రువపరచుకోండి",
        "nav-login-createaccount": "లాగినవండి / ఖాతాని సృష్టించుకోండి",
        "userlogin": "లాగినవండి / ఖాతాను సృష్టించుకోండి",
        "userloginnocreate": "లాగినవండి",
        "userlogin-resetpassword-link": "మీ సంకేతపదాన్ని మర్చిపోయారా?",
        "userlogin-helplink2": "లాగినవడంలో సహాయం",
        "userlogin-loggedin": "మీరు ఈసరికే {{GENDER:$1|$1}} గా లాగిన్ అయి ఉన్నారు.\nవేరే వాడుకరిగా లాగినయేందుకు కింది ఫారమును వాడండి.",
+       "userlogin-reauth": "మీరు {{GENDER:$1|$1}} అని నిర్ధారించుకునేందుకు మళ్ళీ లాగిన్ అవాలి.",
        "userlogin-createanother": "మరొక ఖాతాను సృష్టించండి",
        "createacct-emailrequired": "ఈమెయిలు చిరునామా",
        "createacct-emailoptional": "ఈమెయిలు చిరునామా (ఐచ్చికం)",
        "createacct-reason-ph": "మీరు మరో ఖాతాను ఎందుకు సృష్టించుకుంటున్నారు",
        "createacct-submit": "మీ ఖాతాను సృష్టించుకోండి",
        "createacct-another-submit": "ఖాతాను సృష్టించు",
+       "createacct-continue-submit": "ఖాతా సృష్టిని కొనసాగించండి",
+       "createacct-another-continue-submit": "ఖాతా సృష్టిని కొనసాగించండి",
        "createacct-benefit-heading": "{{SITENAME}}ను తయారుచేస్తున్నది మీలాంటి వారే.",
        "createacct-benefit-body1": "{{PLURAL:$1|మార్పు|మార్పులు}}",
        "createacct-benefit-body2": "{{PLURAL:$1|పేజీ|పేజీలు}}",
        "createacct-benefit-body3": "ఇటీవలి {{PLURAL:$1|సమర్పకుడు|సమర్పకులు}}",
        "badretype": "మీరు ఇచ్చిన రెండు సంకేతపదాలు ఒకదానితో మరొకటి సరిపోలడం లేదు.",
+       "usernameinprogress": "ఈ వాడుకరి పేరుతో ఖాతా సృష్టించుకోవడం పురోగతిలో ఉంది. కాస్త ఆగండి.",
        "userexists": "ఇచ్చిన వాడుకరిపేరు ఇప్పటికే వాడుకలో ఉంది.\nవేరే పేరును ఎంచుకోండి.",
        "loginerror": "లాగిన్ లోపం",
        "createacct-error": "ఖాతా సృష్టించడంలో లోపం",
        "nocookiesnew": "ఖాతాని సృష్టించాం, కానీ మీరు ఇంకా లోనికి ప్రవేశించలేదు.\nవాడుకరుల ప్రవేశానికి {{SITENAME}} కూకీలను వాడుతుంది.\nమీరు కూకీలని అచేతనం చేసివున్నారు.\nదయచేసి వాటిని చేతనంచేసి, మీ కొత్త వాడుకరి పేరు మరియు సంకేతపదాలతో లోనికి ప్రవేశించండి.",
        "nocookieslogin": "వాడుకరుల ప్రవేశానికై {{SITENAME}} కూకీలను వాడుతుంది.\nమీరు కుకీలని అచేతనం చేసివున్నారు.\nవాటిని చేతనంచేసి ప్రయత్నించండి.",
        "nocookiesfornew": "మూలాన్ని కనుక్కోలేకపోయాం కాబట్టి, ఈ వాడుకరి ఖాతాను సృష్టించలేకపోయాం.\nమీ కంప్యూటర్లో కూకీలు చేతనమై ఉన్నాయని నిశ్చయించుకొని, ఈ పేజీని తిరిగి లోడు చేసి, మళ్ళీ ప్రయత్నించండి.",
+       "createacct-loginerror": "ఖాతా విజయవంతంగా సృష్టించబడింది, కానీ ఆటోమాటిగ్గా లాగిన్ అవలేరు.  స్వయంగా మీరే [[Special:UserLogin|లాగినవండి]].",
        "noname": "మీరు సరైన వాడుకరి పేరు ఇవ్వలేదు.",
-       "loginsuccesstitle": "à°ªà±\8dà°°à°µà±\87à°¶à°\82 à°µà°¿à°\9cయవà°\82తమà±\88à°\82ది",
+       "loginsuccesstitle": "లాà°\97ినయà±\8dయారà±\81",
        "loginsuccess": "<strong>మీరు ఇప్పుడు {{SITENAME}}లోనికి \"$1\"గా ప్రవేశించారు.</strong>",
-       "nosuchuser": "\"$1\" à°\85à°¨à±\87 à°ªà±\87à°°à±\81à°¤à±\8b à°µà°¾à°¡à±\81à°\95à°°à±\81à°²à±\81 à°²à±\87à°°à±\81.\nవాడà±\81à°\95à°°à°¿ పేర్లు కేస్ సెన్సిటివ్.\nఅక్షరక్రమం సరిచూసుకోండి, లేదా [[Special:CreateAccount|కొత్త ఖాతా సృష్టించుకోండి]].",
+       "nosuchuser": "\"$1\" à°\85à°¨à±\87 à°ªà±\87à°°à±\81à°¤à±\8b à°µà°¾à°¡à±\81à°\95à°°à°¿ à°\8eవరà±\82 à°²à±\87à°°à±\81.\nవాడà±\81à°\95à°°à°¿పేర్లు కేస్ సెన్సిటివ్.\nఅక్షరక్రమం సరిచూసుకోండి, లేదా [[Special:CreateAccount|కొత్త ఖాతా సృష్టించుకోండి]].",
        "nosuchusershort": "\"$1\" పేరుతో వాడుకరి ఎవరూ లేరు. పేరు సరి చూసుకోండి.",
        "nouserspecified": "వాడుకరి పేరును తప్పనిసరిగా ఇవ్వాలి.",
        "login-userblocked": "ఈ వాడుకరిని నిరోధించారు. ప్రవేశానికి అనుమతి లేదు.",
        "wrongpasswordempty": "ఖాళీ సంకేతపదం ఇచ్చారు. మళ్ళీ ప్రయత్నించండి.",
        "passwordtooshort": "సంకేతపదం కనీసం {{PLURAL:$1|1 అక్షరం|$1 అక్షరాల}} నిడివి ఉండాలి.",
        "passwordtoolong": "సంకేతపదంలో {{PLURAL:$1|1 అక్షరం|$1 అక్షరాల}} కన్నా ఎక్కువ ఉండకూడదు.",
+       "passwordtoopopular": "మామూలుగా వాడే సంకేతపదాలను వాడే వీల్లేదు. మరింత విశిష్టమైన సంకేతపదాన్ని ఎంచుకోండి.",
        "password-name-match": "మీ సంకేతపదం మీ వాడుకరిపేరుకి భిన్నంగా ఉండాలి.",
        "password-login-forbidden": "ఈ వాడుకరిపేరు మరియు సంకేతపదాలను ఉపయోగించడం నిషిద్ధం.",
        "mailmypassword": "సంకేతపదాన్ని మార్చు",
        "noemail": "వాడుకరి \"$1\" కు ఈమెయిలు చిరునామా నమోదయి లేదు.",
        "noemailcreate": "మీరు సరైన ఈమెయిల్ చిరునామాని ఇవ్వాలి",
        "passwordsent": "\"$1\" కొరకు నమోదైన ఈ-మెయిలు చిరునామాకి కొత్త సంకేతపదాన్ని పంపించాం.\nఅది అందిన తర్వాత ప్రవేశించి చూడండి.",
-       "blocked-mailpassword": "దిదà±\8dà°¦à±\81బాà°\9fà±\8dà°²à±\81 à°\9aà±\86à°¯à±\8dà°¯à°\95à±\81à°\82à°¡à°¾ à°®à±\80 à°\90à°ªà±\80à°\85à°¡à±\8dà°°à°¸à±\81à°¨à±\81 à°¨à°¿à°°à±\8bధిà°\82à°\9aà°¾à°\82. à°\85à°\82à°\9aà±\87à°¤, à°¦à±\81à°¶à±\8dà°\9aà°°à±\8dయల à°¨à°¿à°µà°¾à°°à°£ à°\95à±\8bà°¸à°\82 à°\97ానà±\81, à°®à°°à°\9aà°¿à°ªà±\8bయిన à°¸à°\82à°\95à±\87తపదానà±\8dని à°ªà±\8aà°\82à°¦à±\87 à°µà±\80à°²à±\81 à°\88 à°\90à°ªà±\80à°\95à°¿ à°²à±\87à°¦à±\81.",
+       "blocked-mailpassword": "దిద్దుబాట్లు చెయ్యకుండా మీ ఐపీఅడ్రసును నిరోధించాం. దుశ్చర్యల నివారణ కోసం గాను, మరచిపోయిన సంకేతపదాన్ని పొందే వీలు ఈ ఐపీకి లేదు.",
        "eauthentsent": "ఇచ్చిన ఈ-మెయిలు అడ్రసుకు ధృవీకరణ మెయిలు పంపించాం.\nఇకపై మేము ఆ ఖాతాకు మెయిలు పంపాలంటే, ముందుగా మీరు ఆ మెయిల్లో సూచించినట్లుగా చేసి, ఈ చిరునామా మీదేనని ధృవీకరించాలి.",
        "throttled-mailpassword": "గడచిన {{PLURAL:$1|ఒక గంటలో|$1 గంటల్లో}} సంకేతపదం మార్చినట్లుగా ఒక మెయిలు పంపించివున్నాం.\nదుశ్చర్యలను నివారించేందుకు గాను, {{PLURAL:$1|ఒక గంటకు|$1 గంటలకు}} ఒక్కసారి మాత్రమే సంకేతపదం మార్పు మెయిలు పంపిస్తాము.",
        "mailerror": "మెయిలు పంపించడంలో లోపం: $1",
        "createacct-another-realname-tip": "అసలు పేరు ఐచ్ఛికం.\nమీరు దాన్ని ఇస్తే, వాడుకరి పనుల శ్రేయస్సు ఆ పేరుకు ఆపాదించబడుతుంది.",
        "pt-login": "లాగినవండి",
        "pt-login-button": "లాగినవండి",
+       "pt-login-continue-button": "లాగిన్ అవడాన్ని కొనసాగించండి",
        "pt-createaccount": "ఖాతా సృష్టించుకోండి",
        "pt-userlogout": "లాగౌటవండి",
        "php-mail-error-unknown": "PHP యొక్క mail() ఫంక్షన్‍లో ఏదో తెలియని లోపం దొర్లింది",
        "newpassword": "కొత్త సంకేతపదం:",
        "retypenew": "సంకేతపదం, మళ్ళీ",
        "resetpass_submit": "సంకేతపదాన్ని మార్చి లాగినవండి",
-       "changepassword-success": "à°®à±\80 à°¸à°\82à°\95à±\87తపదà°\82 à°µà°¿à°\9cయవà°\82à°¤à°\82à°\97à°¾ à°®à°¾à°°à±\8dà°\9aబడిà°\82ది.",
+       "changepassword-success": "à°®à±\80 à°¸à°\82à°\95à±\87తపదà°\82 à°®à°¾à°°à±\8dà°\9aబడిà°\82ది!",
        "changepassword-throttled": "కొద్దిసేపటిగా మీరు చాలా లాగిన్ ప్రయత్నాలు చేసారు.\nమళ్ళీ ప్రయత్నించే ముందు $1 ఆగండి.",
+       "botpasswords-label-appid": "బాట్ పేరు:",
+       "botpasswords-label-create": "సృష్టించు",
+       "botpasswords-label-update": "తాజాకరించు",
+       "botpasswords-label-cancel": "రద్దుచేయి",
+       "botpasswords-label-delete": "తొలగించు",
+       "botpasswords-label-resetpassword": "సంకేతపదాన్ని మార్చు",
+       "botpasswords-label-grants": "వర్తించే గ్రాంట్లు:",
+       "botpasswords-label-restrictions": "వాడుక పరిమితులు:",
+       "botpasswords-label-grants-column": "గ్రాంటు చేసాం",
+       "botpasswords-bad-appid": "బాట్ పేరు \"$1\" సరైనది కాదు.",
+       "botpasswords-insert-failed": "బాట్ పేరు \"$1\"ను చేర్చలేకపోయాం. దీన్ని ఇంతకు ముందే చేర్చారా ఏంటి?",
+       "botpasswords-update-failed": "బాట్ పేరు \"$1\" ను తాజాకరించలేకపోయాం. దీన్ని తొలగించారా?",
+       "botpasswords-created-title": "బాట్ సంకేతపదాన్ని సృష్టించాం",
+       "botpasswords-created-body": "వాడుకరి \"$2\" కు చెందిన \"$1\" అనే బాట్‌కు బాట్ సంకేతపదాన్ని సృష్టించాం.",
+       "botpasswords-updated-title": "బాట్ సంకేతపదాన్ని తాజాకరించాం",
+       "botpasswords-updated-body": "వాడుకరి \"$2\" కు చెందిన \"$1\" అనే బాట్‌ యొక్క బాట్ సంకేతపదాన్ని తాజాకరించాం.",
+       "botpasswords-deleted-title": "బాట్ సంకేతపదాన్ని తొలగించాం",
+       "botpasswords-deleted-body": "వాడుకరి \"$2\" కు చెందిన \"$1\" అనే బాట్‌ యొక్క బాట్ సంకేతపదాన్ని తొలగించాం.",
+       "botpasswords-newpassword": "<strong>$1</strong> తో లాగినయేందుకు కొత్త సంకేతపదం <strong>$2</strong>. <em>భావి ఉపయోగం కోసం దీన్ని జాగ్రత్త చేసుకోండి.</em>",
+       "botpasswords-not-exist": "వాడుకరి \"$1\" కి \"$2\" అనే బాట్ సంకేతపదం లేదు.",
        "resetpass_forbidden": "సంకేతపదాలను మార్చటం కుదరదు",
+       "resetpass_forbidden-reason": "సంకేతపదాలు మార్చజాలరు: $1",
        "resetpass-no-info": "ఈ పేజీని నేరుగా చూడటానికి మీరు లాగినయి వుండాలి.",
        "resetpass-submit-loggedin": "సంకేతపదాన్ని మార్చు",
        "resetpass-submit-cancel": "రద్దుచేయి",
-       "resetpass-wrong-oldpass": "తపà±\8dà°ªà±\81à°¡à±\81 à°¤à°¾à°¤à±\8dà°\95ాలిà°\95 à°²à±\87దా à°ªà±\8dà°°à°¸à±\8dà°¤à±\81à°¤ à°¸à°\82à°\95à±\87తపదà°\82.\nà°®à±\80à°°à±\81 à°®à±\80 à°¸à°\82à°\95à±\87తపదానà±\8dని à°\87à°ªà±\8dà°ªà°\9fà°¿à°\95à±\87 à°µà°¿à°\9cయవà°\82à°¤à°\82à°\97à°¾ à°®à°¾à°°à±\8dà°\9aà±\81à°\95à±\8aనివà±\81à°\82à°¡à°µà°\9aà±\8dà°\9aà±\81 à°²à±\87దా à°\95à±\8aà°¤à±\8dà°¤ à°¤à°¾à°¤à±\8dà°\95ాలిà°\95 à°¸à°\82à°\95à±\87తపదà°\82 à°\95à±\8bà°¸à°\82 à°\85à°­à±\8dయరà±\8dథిà°\82à°\9aారు.",
+       "resetpass-wrong-oldpass": "తపà±\8dà°ªà±\81à°¡à±\81 à°¤à°¾à°¤à±\8dà°\95ాలిà°\95 à°²à±\87దా à°ªà±\8dà°°à°¸à±\8dà°¤à±\81à°¤ à°¸à°\82à°\95à±\87తపదà°\82.\nà°®à±\80à°°à±\81 à°®à±\80 à°¸à°\82à°\95à±\87తపదానà±\8dని à°\87à°ªà±\8dà°ªà°\9fà°¿à°\95à±\87 à°®à°¾à°°à±\8dà°\9aà±\81à°\95à±\8aని à°\89à°\82à°¡à°µà°\9aà±\8dà°\9aà±\81 à°²à±\87దా à°\95à±\8aà°¤à±\8dà°¤ à°¤à°¾à°¤à±\8dà°\95ాలిà°\95 à°¸à°\82à°\95à±\87తపదà°\82 à°\95à±\8bà°¸à°\82 à°\85à°­à±\8dయరà±\8dథిà°\82à°\9aà°¿ à°\89à°\82à°¡à°µà°\9aà±\8dà°\9aు.",
        "resetpass-recycled": "మీ ప్రస్తుత సంకేతపదాన్ని వేరే సంకేతపదంతో మార్చుకోండి",
        "resetpass-temp-emailed": "మీరు మీ ఈమెయిలుకు పంపించిన తాత్కాలిక కోడుతో లాగినయ్యారు. లాగిన్ను పూర్తి చేసేందుకు, ఇక్కడ మీరు తప్పనిసరిగా సంకేతపదం మార్చుకోవాలి:",
        "resetpass-temp-password": "తాత్కాలిక సంకేతపదం:",
        "passwordreset-emailtext-ip": "ఎవరో (బహుశా మీరే, ఐపీ అడ్రసు $1 నుంచి)  {{SITENAME}} ($4) లో మీ సంకేతపదాన్ని మార్చమంటూ అడిగారు. కింది వాడుకరి {{PLURAL:$3|ఖాతా|ఖాతాలు}}\nఈ ఈమెయిలు చిరునామాతో అనుసంధింపబడి ఉన్నాయి:\n\n$2\n\n{{PLURAL:$3|ఈ సంకేతపదానికి|ఈ సంకేతపదాలకు}} {{PLURAL:$5|ఒక్కరోజులో|$5 రోజుల్లో}} కాలం చెల్లుతుంది.\nఇప్పుడు మీరు లాగినై కొత్త సంకేతపదాన్ని ఎంచుకోవాల్సి ఉంటుంది. ఈ అభ్యర్ధన చేసింది మరెవరైనా అయినా, లేక మీ అసలు సంకేతపదం మీకు గుర్తొచ్చి దాన్ని మార్చాల్సిన అవసరం లేదని అనుకున్నా, మీరీ సందేశాన్ని పట్టించుకోనక్కర్లేదు. పాత సంకేతపదాన్నే వాడుకోవచ్చు.",
        "passwordreset-emailtext-user": "{{SITENAME}} లోని వాడుకరి $1, {{SITENAME}} ($4) లోని మీ సంకేతపదాన్ని మార్చమంటూ అడిగారు. కింది వాడుకరి {{PLURAL:$3|ఖాతా|ఖాతాలు}}\nఈ ఈమెయిలు అడ్రసుతో అనుసంధింపబడి ఉన్నాయి:\n\n$2\n\n{{PLURAL:$3|ఈ తాత్కాలిక సంకేతపదానికి|ఈ తాత్కాలిక సంకేతపదాలకు}} {{PLURAL:$5|ఒక్క రోజులో|$5 రోజుల్లో}} కాలం చెల్లుతుంది.\nఇప్పుడు మీరు లాగినై కొత్త సంకేతపదాన్ని ఎంచుకోవాల్సి ఉంటుంది. ఈ అభ్యర్ధన చేసింది మరెవరైనా అయినా, లేక మీ అసలు సంకేతపదం మీకు గుర్తొచ్చి దాన్ని మార్చాల్సిన అవసరం లేదని అనుకున్నా, మీరీ సందేశాన్ని పట్టించుకోనక్కర్లేదు. పాత సంకేతపదాన్నే వాడుకోవచ్చు.",
        "passwordreset-emailelement": "వాడుకరిపేరు: \n$1\n\nతాత్కాలిక సంకేతపదం: \n$2",
-       "passwordreset-emailsentemail": "సంకేతపదం మార్పు ఈమెయిలును పంపించాం.",
-       "passwordreset-emailsent-capture": "క్రింద చూపిన సంకేతపదం మార్పు ఈమెయిలును పంపించాం.",
-       "passwordreset-emailerror-capture": "కింద చూపిన సంకేతపదం మార్పు ఈమెయిలును తయారుచేసాం. కానీ దాన్ని {{GENDER:$2|వాడుకరికి}} పంపడం విఫలమైంది: $1",
-       "changeemail": "ఈ-మెయిలు చిరునామా మార్పు",
-       "changeemail-header": "ఖాతా ఈ-మెయిల్ చిరునామాని మార్చండి",
+       "passwordreset-emailsentemail": "ఈ ఈమెయిలు చిరునామా మీ ఖాతాకు అనుసంధించి ఉంటే, సంకేతపదం మార్పు ఈమెయిలు పంపించబడుతుంది.",
+       "passwordreset-emailsentusername": "ఈ వాడుకరిపేరుకు ఏదైనా ఈమెయిలు చిరునామా అనుసంధించి ఉంటే, సంకేతపదం మార్పు ఈమెయిలు పంపించబడుతుంది.",
+       "passwordreset-emailsent-capture2": "సంకేతపదం మార్పు {{PLURAL:$1|ఈమెయిలును|ఈమెయిళ్ళను}} పంపించాం. {{PLURAL:$1|వాడుకరిపేరు, సంకేతపదాన్ని|వాడుకరిపేర్లు, సంకేతపదాల జాబితాను}} కింద చూపించాం.",
+       "passwordreset-emailerror-capture2": "{{GENDER:$2|వాడుకరికి}} ఈమెయిలు పంపడం విఫలమైంది: $1 {{PLURAL:$3|వాడుకరిపేరు, సంకేతపదాన్ని|వాడుకరిపేర్లు, సంకేతపదాల జాబితాను}} కింద చూపించాం.",
+       "passwordreset-invalideamil": "తప్పు ఈ-మెయిలు చిరునామా",
+       "passwordreset-nodata": "వాడుకరిపేరుగానీ, ఈ-మెయిలు చిరునామా గానీ ఇవ్వలేదు",
+       "changeemail": "ఈ-మెయిలు చిరునామా మార్పు లేదా తొలగింపు",
+       "changeemail-header": "మీ ఈ-మెయిల్ చిరునామాను మార్చుకునేందుకు ఈ ఫారమును నింపండి. అసలు మీ ఖాతాకు ఈ-మెయిలు చిరునామా దేన్నీ జోడించ వద్దనుకుంటే, కొత్త ఈ-మెయిలు చిరునామాను ఖాళీగా ఉంచి, ఫారాన్ని సమర్పించండి.",
        "changeemail-no-info": "ఈ పేజీని నేరుగా చూడటానికి మీరు లాగినయి వుండాలి.",
        "changeemail-oldemail": "ప్రస్తుత ఈ-మెయిలు చిరునామా:",
        "changeemail-newemail": "కొత్త ఈ-మెయిలు చిరునామా:",
+       "changeemail-newemail-help": "ఈ-మెయిలు చిరునామాను తొలగించాం.",
        "changeemail-none": "(ఏమీలేదు)",
        "changeemail-password": "మీ {{SITENAME}} సంకేతపదం:",
        "changeemail-submit": "ఈ-మెయిల్ మార్చు",
        "changeemail-throttled": "మరీ ఎక్కువగా లాగిన్ ప్రయత్నాలు చేసారు.\nమళ్ళీ ప్రయత్నించే ముందు $1 ఆగండి.",
+       "changeemail-nochange": "వేరే ఈ-మెయిలు చిరునామా ఇవ్వండి.",
        "resettokens": "టోకెన్ ను రీసెట్ చెయ్యి",
        "resettokens-text": "మీ ఖాతాకు అనుబంధంగా ఉన్న గోపనీయ డేటాను చూపించే టోకెన్లను మీరు ఇక్కడ రీసెట్ చెయ్యవచ్చు.\n\nమీరా టోకెన్లను పొరపాటున ఎవరికైనా ఇచ్చి ఉన్నా, లేక మీ ఖాతా వివరాలు మరెవరికైనా తెలిసిపోయినా మీరీ పని చెయ్యాలి.",
        "resettokens-no-tokens": "రీసెట్ చేసేందుకు టోకెన్లేమీ లేవు.",
        "minoredit": "ఇది ఒక చిన్న మార్పు",
        "watchthis": "ఈ పుట మీద కన్నేసి ఉంచు",
        "savearticle": "పేజీని భద్రపరచు",
+       "savechanges": "మార్పులను భద్రపరచు",
+       "publishpage": "పేజీని ప్రచురించు",
+       "publishchanges": "మార్పులను ప్రచురించు",
        "preview": "మునుజూపు",
        "showpreview": "మునుజూపు చూపు",
        "showdiff": "తేడాలను చూపించు",
        "missingsummary": "<strong>గుర్తు చేస్తున్నాం:</strong> మీరు దిద్దుబాటు సారాంశమేమీ ఇవ్వలేదు. పేజీని మళ్ళీ భద్రపరచమని చెబితే సారాంశమేమీ లేకుండానే దిద్దుబాటును భద్రపరుస్తాం.",
        "selfredirect": "<strong>హెచ్చరిక:</strong> మీరు ఈ పేజీని దానికే దారిమార్పు చేస్తున్నారు. బహుశా మీరు తప్పు దారిమార్పును సూచించి ఉండవచ్చు, లేదా మీరు తప్పుడు పేజీని మారుస్తున్నారు. \nమీరు \"{{int:savearticle}}\" ను నొక్కితే దారిమార్పు పేజీ ఖచ్చితంగా సృష్టించబడుతుంది.",
        "missingcommenttext": "కింద ఓ వ్యాఖ్య రాయండి.",
-       "missingcommentheader": "<strong>గుర్తు చేస్తున్నాం:</strong> ఈ వ్యాఖ్యకు మీరు విషయం/శీర్షిక పెట్టలేదు.\n\"{{int:savearticle}}\"ని మళ్ళీ నొక్కితే, అది లేకుండానే మీ మార్పును భద్రపరుస్తాం.",
+       "missingcommentheader": "<strong>గుర్తు చేస్తున్నాం:</strong> ఈ వ్యాఖ్యకు మీరు విషయం పెట్టలేదు.\n\"{{int:savearticle}}\"ని మళ్ళీ నొక్కితే, అది లేకుండానే మీ మార్పును భద్రపరుస్తాం.",
        "summary-preview": "సారాంశం మునుజూపు:",
        "subject-preview": "విషయపు మునుజూపు:",
        "previewerrortext": "మీ మార్పులు మునుజూపు చూడటంలో తప్పిదమయింది.",
        "accmailtext": "[[User talk:$1|$1]] కొరకు ఒక యాదృచ్ఛిక సంకేతపదాన్ని $2కి పంపించాం. లాగినయ్యాక, ''[[Special:ChangePassword|సంకేతపదాన్ని మార్చుకోండి]]'' అనే పేజీలో ఈ సంకేతపదాన్ని మార్చుకోవచ్చు.",
        "newarticle": "(కొత్తది)",
        "newarticletext": "ఈ లింకుకు సంబంధించిన పేజీ లేనే లేదు.\nకింది పెట్టెలో మీ రచనను టైపు చేసి ఆ పేజీని సృష్టించండి (దీనిపై సమాచారం కొరకు [$1 సహాయం పేజీ] చూడండి). మీరిక్కడికి పొరపాటున వచ్చి ఉంటే, మీ బ్రౌజరు <strong>back</strong> మీట నొక్కండి.",
-       "anontalkpagetext": "----\n<em>à°\87ది à°\92à°\95 à°\85à°\9cà±\8dà°\9eాత à°µà°¾à°¡à±\81à°\95à°°à°¿ à°\9aà°°à±\8dà°\9aà°¾ à°ªà±\87à°\9cà±\80. à°\86 à°µà°¾à°¡à±\81à°\95à°°à°¿ à°\87à°\82à°\95à°¾ à°¤à°¨à°\95à±\88 à°\96ాతానà±\81 à°¸à±\83à°·à±\8dà°\9fà°¿à°\82à°\9aà±\81à°\95à±\8bà°²à±\87à°¦à±\81, à°²à±\87దా à°\96ాతా à°\89à°¨à±\8dనా à°¦à°¾à°¨à°¿à°¨à°¿ à°\89పయà±\8bà°\97à°¿à°\82à°\9aà°¡à°\82 à°²à±\87à°¦à±\81.</em>\nà°\85à°\82à°\9aà±\87à°¤, à°\85తణà±\8dణి/à°\86à°®à±\86à°¨à±\81 à°\97à±\81à°°à±\8dతిà°\82à°\9aడానిà°\95à°¿ à°\90.à°ªà±\80. à°\9aà°¿à°°à±\81నామానà±\81 à°µà°¾à°¡à°¾à°²à±\8dసి à°µà°\9aà±\8dà°\9aà°¿à°\82ది. \nà°\92à°\95à±\87 à°\90.à°ªà±\80. à°\9aà°¿à°°à±\81నామాని చాలా మంది వాడుకరులు ఉపయోగించే అవకాశం ఉంది. \nమీరూ అజ్ఞాత వాడుకరి అయితే, మీకు సంబంధంలేని వ్యాఖ్యలు మిమ్మల్ని ఉద్దేశించినట్టుగా అనిపిస్తే, భవిష్యత్తులో ఇతర అజ్ఞాత వాడుకరులతో అయోమయం లేకుండా ఉండటానికి, [[Special:CreateAccount|ఖాతాను సృష్టించుకోండి]] లేదా [[Special:UserLogin|లాగినవండి]].''",
+       "anontalkpagetext": "----\n<em>à°\87ది à°\92à°\95 à°\85à°\9cà±\8dà°\9eాత à°µà°¾à°¡à±\81à°\95à°°à°¿ à°\9aà°°à±\8dà°\9aà°¾ à°ªà±\87à°\9cà±\80. à°\86 à°µà°¾à°¡à±\81à°\95à°°à°¿ à°\87à°\82à°\95à°¾ à°¤à°¨à°\95à±\88 à°\96ాతానà±\81 à°¸à±\83à°·à±\8dà°\9fà°¿à°\82à°\9aà±\81à°\95à±\8bà°²à±\87à°¦à±\81, à°²à±\87దా à°\96ాతా à°\89à°¨à±\8dనా à°¦à°¾à°¨à°¿à°¨à°¿ à°\89పయà±\8bà°\97à°¿à°\82à°\9aà°¡à°\82 à°²à±\87à°¦à±\81.</em>\nà°\85à°\82à°\9aà±\87à°¤, à°\85తణà±\8dణి/à°\86à°®à±\86à°¨à±\81 à°\97à±\81à°°à±\8dతిà°\82à°\9aడానిà°\95à°¿ à°\90.à°ªà±\80. à°\9aà°¿à°°à±\81నామానà±\81 à°µà°¾à°¡à°¾à°²à±\8dసి à°µà°\9aà±\8dà°\9aà°¿à°\82ది. \nà°\86 à°\90.à°ªà±\80. à°\9aà°¿à°°à±\81నామానà±\81 చాలా మంది వాడుకరులు ఉపయోగించే అవకాశం ఉంది. \nమీరూ అజ్ఞాత వాడుకరి అయితే, మీకు సంబంధంలేని వ్యాఖ్యలు మిమ్మల్ని ఉద్దేశించినట్టుగా అనిపిస్తే, భవిష్యత్తులో ఇతర అజ్ఞాత వాడుకరులతో అయోమయం లేకుండా ఉండటానికి, [[Special:CreateAccount|ఖాతాను సృష్టించుకోండి]] లేదా [[Special:UserLogin|లాగినవండి]].''",
        "noarticletext": "ప్రస్తుతం ఈ పేజీలో పాఠ్యమేమీ లేదు.\nవేరే పేజీలలో [[Special:Search/{{PAGENAME}}|ఈ పేజీ శీర్షిక కోసం వెతకవచ్చు]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} సంబంధిత చిట్టాలు చూడవచ్చు],\nలేదా [{{fullurl:{{FULLPAGENAME}}|action=edit}} ఈ పేజీని మార్చవచ్చు]</span>.",
        "noarticletext-nopermission": "ప్రస్తుతం ఈ పేజీలో పాఠ్యమేమీ లేదు.\nమీరు ఇతర పేజీలలో [[Special:Search/{{PAGENAME}}|ఈ పేజీ శీర్షిక కోసం వెతకవచ్చు]], లేదా <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} సంబంధిత చిట్టాలలో వెతకవచ్చు]</span>, కానీ ఈ పేజీని సృష్టించడానికి మీకు అనుమతి లేదు.",
        "missing-revision": "\"{{FULLPAGENAME}}\" అనే పేజీ యొక్క కూర్పు #$1 ఉనికిలో లేదు. సాధారణంగా ఏదైనా తొలగించబడిన పేజీ యొక్క కాలం చెల్లిన చరితం లింకును నొక్కినపుడు ఇది జరుగుతుంది. వివరాలు [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} తొలగింపు లాగ్] లో దొరుకుతాయి.",
        "previewnote": "<strong>ఇది మునుజూపు మాత్రమేనని గుర్తుంచుకోండి.</strong>\nమీ మార్పులు ఇంకా భద్రమవ్వలేదు!",
        "continue-editing": "సరిదిద్దే చోటుకి వెళ్ళండి",
        "previewconflict": "భద్రపరచిన తరువాత పై టెక్స్ట్‌ ఏరియాలోని టెక్స్టు ఇలాగ కనిపిస్తుంది.",
-       "session_fail_preview": "<strong>క్షమించండి! సెషను డేటా పోవడం వలన మీ మార్పులను స్వీకరించలేకపోతున్నాం.</strong>\nమళ్ళీ ప్రయత్నించండి.\nఅయినా పని జరక్కపోతే, ఓసారి [[Special:UserLogout|లాగౌటై]] మళ్ళీ లాగినయి ప్రయత్నించండి.",
-       "session_fail_preview_html": "<strong>సారీ! సెషను డేటా పోవడం వలన మీ దిద్దుబాటును ప్రాసెస్ చెయ్యలేలేక పోతున్నాం.</strong>\n\n''{{SITENAME}}లో ముడి HTML సశక్తమై ఉంది కాబట్టి, జావాస్క్రిప్టు దాడుల నుండి రక్షణగా మునుజూపును దాచేశాం.''\n\n<strong>మీరు చేసినది సరైన దిద్దుబాటే అయితే, మళ్ళీ ప్రయత్నించండి. అయినా పనిచెయ్యకపోతే, ఓ సారి [[Special:UserLogout|లాగౌటై]], మళ్ళీ లాగినయి చూడండి.</strong>",
+       "session_fail_preview": "సారీ! సెషను డేటా పోవడం వలన మీ మార్పులను ప్రాసెస్ చెయ్యలేకపోయాం.\n\nబహుశా మీరు లాగౌటయ్యారేమో. <strong>లాగినయ్యే ఉన్నారని నిర్ధారించుకుని, మళ్ళీ ప్రయత్నించండి.</strong> అయినా పని చెయ్యకపోతే, ఓసారి [[Special:UserLogout|లాగౌటై]], మళ్ళీ లాగినయి ప్రయత్నించండి. మీ బ్రౌజరు ఈ సైటు యొక్క కూకీలను అనుమతిస్తుందని కూడా నిర్ధారించుకోండి.",
+       "session_fail_preview_html": "<strong>సారీ! సెషను డేటా పోవడం వలన మీ దిద్దుబాటును ప్రాసెస్ చెయ్యలేలేక పోతున్నాం.</strong>\n\n<em>{{SITENAME}}లో ముడి HTML సశక్తమై ఉంది కాబట్టి, జావాస్క్రిప్టు దాడుల నుండి రక్షణగా మునుజూపును దాచేశాం.</em>\n\n<strong>మీరు చేసినది సరైన దిద్దుబాటే అయితే, మళ్ళీ ప్రయత్నించండి.</strong> అయినా పనిచెయ్యకపోతే, ఓ సారి [[Special:UserLogout|లాగౌటై]], మళ్ళీ లాగినయి చూడండి. మీ బ్రౌజరు ఈ సైటు యొక్క కూకీలను అనుమతిస్తుందని కూడా నిర్ధారించుకోండి.",
        "token_suffix_mismatch": "'''మీ క్లయంటు, దిద్దుబాటు టోకెన్‌లోని వ్యాకరణ గుర్తులను గజిబిజి చేసింది కాబట్టి మీ దిద్దుబాటును తిరస్కరించాం. పేజీలోని పాఠ్యాన్ని చెడగొట్టకుండా ఉండేందుకు గాను, ఆ దిద్దుబాటును రద్దు చేశాం. వెబ్‌లో ఉండే లోపభూయిష్టమైన అజ్ఞాత ప్రాక్సీ సర్వీసులను వాడినపుడు ఒక్కోసారి ఇలా జరుగుతుంది.'''",
        "edit_form_incomplete": "’’’ఈ ఎడిట్ ఫారంలోని కొన్ని భాగాలు సర్వరును చేరలేదు; మీ మార్పుచేర్పులు భద్రంగా ఉన్నాయని ధృవపరచుకుని, మళ్ళీ ప్రయత్నించండి.’’’",
        "editing": "$1 ని సవరిస్తున్నారు",
        "undo-nochange": "ఆ మార్పును ఈసరికే రద్దు చేసినట్లుగా కనిపిస్తోంది.",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|చర్చ]]) యొక్క కూర్పు $1 ను రద్దుచెయ్యి",
        "undo-summary-username-hidden": "దాచబడిన వాడుకరి చేసిన కూర్పు $1 ని వెనక్కి తిప్పండి",
-       "cantcreateaccounttitle": "ఈ ఖాతా తెరవలేము",
        "cantcreateaccount-text": "ఈ ఐపీ అడ్రసు ('''$1''') నుండి ఖాతా సృష్టించడాన్ని [[User:$3|$3]] నిరోధించారు.\n\n$3 చెప్పిన కారణం: ''$2''",
        "cantcreateaccount-range-text": "<strong>$1</strong> శ్రేణిలోని IP చిరునామాల నుండి ఖాతా సృష్టించడాన్ని [[User:$3|$3]] నిషేధించారు. మీ IP చిరునామా (<strong>$4</strong>) ఈ శ్రేణిలోనే ఉంది.\n\n$3 ఇచ్చిన కారణం: <em>$2</em>",
        "viewpagelogs": "ఈ పేజీకి సంబంధించిన లాగ్‌లను చూడండి",
        "mergehistory-empty": "ఏ కూర్పులనూ విలీనం చెయ్యలేము.",
        "mergehistory-done": "$1 యొక్క $3 {{PLURAL:$3|కూర్పుని|కూర్పులను}} [[:$2]] లోనికి జయప్రదంగా విలీనం చేసాం.",
        "mergehistory-fail": "చరితాన్ని విలీనం చెయ్యలేకపోయాం. పేజీని, సమయాలను సరిచూసుకోండి.",
+       "mergehistory-fail-bad-timestamp": "కాలముద్ర చెల్లదు.",
        "mergehistory-no-source": "మూలం పేజీ, $1 లేదు.",
        "mergehistory-no-destination": "గమ్యం పేజీ, $1 లేదు.",
        "mergehistory-invalid-source": "మూలం పేజీకి సరైన పేరు ఉండాలి.",
        "prefs-watchlist-token": "వీక్షణాజాబితా టోకెను:",
        "prefs-misc": "ఇతరత్రా",
        "prefs-resetpass": "సంకేతపదాన్ని మార్చుకోండి",
-       "prefs-changeemail": "ఈ-మెయిలు చిరునామా మార్పు",
+       "prefs-changeemail": "ఈ-మెయిలు చిరునామా మార్పు లేదా తొలగింపు",
        "prefs-setemail": "ఓ ఈమెయిల్ చిరునామా ఇవ్వండి",
        "prefs-email": "ఈ-మెయిల్ ఎంపికలు",
        "prefs-rendering": "రూపురేఖలు",
        "saveprefs": "భద్రపరచు",
        "restoreprefs": "అప్రమేయ అమరికలను పునఃస్థాపించు (అన్ని విభాగాల్లోనూ)",
-       "prefs-editing": "సవరిసà±\8dà°¤à±\81à°¨à±\8dనారు",
+       "prefs-editing": "దిదà±\8dà°¦à±\81బాà°\9fà±\8dà°²ు",
        "rows": "అడ్డు వరుసలు:",
        "columns": "నిలువు వరుసలు:",
        "searchresultshead": "వెతుకు",
-       "stub-threshold": "<a href=\"#\" class=\"stub\">మొలక లింకు</a> ఫార్మాటింగు కొరకు హద్దు (బైట్లు):",
+       "stub-threshold": "మొలక లింకు ఫార్మాటింగు కొరకు హద్దు ($1):",
        "stub-threshold-sample-link": "నమూనా",
        "stub-threshold-disabled": "అచేతనం",
        "recentchangesdays": "ఇటీవలి మార్పులు లో చూపించవలసిన రోజులు:",
        "prefs-help-recentchangescount": "ఇది ఇటీవలి మార్పులు, పేజీ చరిత్రలు, మరియు చిట్టాలకు వర్తిస్తుంది.",
        "prefs-help-watchlist-token2": "మీ వీక్షణజాబితా యొక్క జాలవడ్డింపుకు చెందిన రహస్య తాళమిది.\nఈ తాళం తెలిసిన ఎవరైనా మీ వీక్షణజాబితాను చదవగలుగుతారు. అందుచేత దీన్ని ఎవరికీ ఇవ్వకండి.\n[[Special:ResetTokens|దాన్ని మార్చాలంటే ఇక్కడ నొక్కండి]].",
        "savedprefs": "మీ అభిరుచులను భద్రపరిచాం.",
+       "savedrights": "{{GENDER:$1|$1}} వాడుకరి హక్కులను భద్రపరచాం.",
        "timezonelegend": "కాల మండలం:",
        "localtime": "స్థానిక సమయం:",
        "timezoneuseserverdefault": "వికీ అప్రమేయాన్ని ఉపయోగించు ($1)",
        "badsig": "సంతకం చెల్లనిది.\nHTML ట్యాగులను ఒకసారి సరిచూసుకోండి.",
        "badsiglength": "మీ సంతకం చాలా పెద్దగా ఉంది.\nఇది తప్పనిసరిగా $1 {{PLURAL:$1|అక్షరం|అక్షరాల}} లోపులోనే ఉండాలి.",
        "yourgender": "మిమ్మల్ని మీరు ఎలా వర్ణించుకుంటారు?",
-       "gender-unknown": "à°µà±\86à°²à±\8dలడిà°\82à°\9aడానిà°\95à°¿ à°¨à±\87à°¨à±\81 à°\87à°·à±\8dà°\9fపడà°\9fà±\8dà°²à±\87à°¦à±\81",
+       "gender-unknown": "సాఫà±\8dà°\9fà±\81à°µà±\87à°°à±\81 à°®à°¿à°®à±\8dమలà±\8dని à°\89à°²à±\8dà°²à±\87à°\96à°¿à°\82à°\9aà±\87 à°¸à°\82దరà±\8dà°­à°\82à°²à±\8b, à°µà±\80à°²à±\88à°¨à°\82తవరà°\95à±\81 à°²à°¿à°\82à°\97 à°¤à°\9fà°¸à±\8dథతనà±\81 à°\85వలà°\82బిసà±\8dà°¤à±\81à°\82ది",
        "gender-male": "అతను వికీ పేజీలను సరిదిద్దుతాడు",
        "gender-female": "ఆమె వికీ పేజీలను సరిదిద్దుతుంది",
        "prefs-help-gender": "ఈ అభిరుచిని అమర్చుకోవడం ఐచ్చికం.\nమిమ్మల్ని సంబోధించేప్పుడూ మిమ్మల్ని పేర్కొనేప్పుడూ వ్యాకరణపరంగా సరైన లింగాన్ని  వాడటానికి ఈ విలువ ఉపయోగపడుతుంది.\nఈ సమాచారం బహిరంగం.",
        "prefs-tokenwatchlist": "టోకెన్",
        "prefs-diffs": "తేడాలు",
        "prefs-help-prefershttps": "ఈ అభిరుచి మీరు పైసారి లాగినైనపుడు అమలౌతుంది.",
+       "prefswarning-warning": "మీ అభిరుచులలో మీరు చేసిన మార్పులను ఇంకా భద్రపరచలేదు. మీరు \"$1\" ను నొక్కకుండా ఈ పేజీని వదలి వెళ్తే, మీ అభిరుచులు భద్రం కావు.",
        "prefs-tabs-navigation-hint": "చిట్కా: ట్యాబుల జాబితాలో ఓ ట్యాబు నుండి మరోదానికి వెళ్ళేందుకు కుడి ఎడమ బాణాల కీలను వాడవచ్చు.",
        "userrights": "వాడుకరి హక్కుల నిర్వహణ",
        "userrights-lookup-user": "వాడుకరి సమూహాలను నిర్వహించండి",
        "userrights-user-editname": "వాడుకరిపేరును ఇవ్వండి:",
-       "editusergroup": "వాడుకరి గుంపులను మార్చు",
-       "editinguser": "వాడుకరి '''[[User:$1|$1]]''' $2 యొక్క వాడుకరి హక్కులను మారుస్తున్నారు",
+       "editusergroup": "{{GENDER:$1|వాడుకరి}} గుంపులను మార్చు",
+       "editinguser": "{{GENDER:$1|వాడుకరి}} <strong>[[వాడుకరి:$1|$1]]</strong> $2 యొక్క వాడుకరి హక్కులను మారుస్తున్నారు",
        "userrights-editusergroup": "వాడుకరి సమూహాలను మార్చండి",
-       "saveusergroups": "వాడుకరి గుంపులను భద్రపరచు",
+       "saveusergroups": "{{GENDER:$1|వాడుకరి}} గుంపులను భద్రపరచు",
        "userrights-groupsmember": "సభ్యులు:",
        "userrights-groupsmember-auto": "సంభావిత సభ్యులు:",
        "userrights-groups-help": "ఈ వాడుకరి ఏయే గుంపులలో ఉండాలో మీరు మార్చవచ్చు.\n* టిక్కు పెట్టివుంటే ఆ గుంపులో ఈ వాడుకరి ఉన్నట్టు.\n* టిక్కు లేకుంటే ఆ గుంపులో ఈ వాడుకరి లేనట్టు.\n* * ఉంటే ఒకసారి ఆ గుంపుని చేర్చాక మీరు తీసివేయలేరు, లేదా తీసివేసాక తిరిగి చేర్చలేరు.",
        "userrights-changeable-col": "మీరు మార్చదగిన గుంపులు",
        "userrights-unchangeable-col": "మీరు మార్చలేని గుంపులు",
        "userrights-conflict": "వాడుకరి హక్కుల మార్పులలో ఘర్షణ! మీ మార్పులను సమీక్షించి, నిర్ధారించండి.",
-       "userrights-removed-self": "à°®à±\80 à°¹à°\95à±\8dà°\95à±\81లనà±\81 à°®à±\80à°°à±\81 à°µà°¿à°\9cయవà°\82à°¤à°\82à°\97à°¾ à°¤à±\8aà°²à°\97à°¿à°\82à°\9aà±\81à°\95à±\81à°¨à±\8dనారà±\81. à°¤à°¦à±\8dవారా, à°\88 à°ªà±\87à°\9cà±\80ని à°\9aà±\82డడానిà°\95à°¿ à°®à±\80à°\95à±\81 à°\87à°\95 à°\85à°¨à±\81మతి à°²à±\87à°¦ు.",
+       "userrights-removed-self": "à°®à±\80 à°¹à°\95à±\8dà°\95à±\81లనà±\81 à°®à±\80à°°à±\81 à°¤à±\8aà°²à°\97à°¿à°\82à°\9aà±\81à°\95à±\81à°¨à±\8dనారà±\81. à°\87à°\95, à°®à±\80à°°à±\80 à°ªà±\87à°\9cà±\80ని à°\9aà±\82à°¡à°²à±\87à°°ు.",
        "group": "గుంపు:",
        "group-user": "వాడుకరులు",
        "group-autoconfirmed": "ఆటోమాటిగ్గా నిర్ధారించబడిన వాడుకరులు",
        "right-managechangetags": "డేటాబేసులో [[Special:Tags|ట్యాగుల]]ను సృష్టించడం, తొలగించడం",
        "right-applychangetags": "తన మార్పులతో [[Special:Tags|ట్యాగుల]]ను ఆపాదించడం",
        "right-changetags": "విడి కూర్పులకు, చిట్టా పద్దులకు ఏవైనా [[Special:Tags|ట్యాగుల]]ను చేర్చడం, తొలగించడం",
+       "right-deletechangetags": "[[ప్రత్యేక:Tags|ట్యాగులను]] డేటాబేసు నుండి తొలగించు",
+       "grant-generic": "\"$1\" హక్కుల కట్ట",
+       "grant-group-email": "ఈమెయిలు పంపించడం",
+       "grant-group-administration": "నిర్వాహక చర్యలు చేపట్టడం",
+       "grant-group-private-information": "మీ గోపనీయ డేటాను చూడడం",
+       "grant-basic": "ప్రాథమిక హక్కులు",
        "newuserlogpage": "కొత్త వాడుకరుల చిట్టా",
        "newuserlogpagetext": "ఇది వాడుకరి నమోదుల చిట్టా.",
        "rightslog": "వాడుకరుల హక్కుల మార్పుల చిట్టా",
        "rightslogtext": "ఇది వాడుకరుల హక్కులకు జరిగిన మార్పుల చిట్టా.",
        "action-read": "ఈ పేజీని చదివే",
        "action-edit": "ఈ పేజీని సవరించే",
-       "action-createpage": "à°ªà±\87à°\9cà±\80లనà±\81 సృష్టించే",
-       "action-createtalk": "à°\9aà°°à±\8dà°\9aాపà±\87à°\9cà±\80లనà±\81 సృష్టించే",
+       "action-createpage": "à°\88 à°ªà±\87à°\9cà±\80ని సృష్టించే",
+       "action-createtalk": "à°\88 à°\9aà°°à±\8dà°\9aాపà±\87à°\9cà±\80ని సృష్టించే",
        "action-createaccount": "ఈ వాడుకరి ఖాతాని సృష్టించే",
        "action-history": "ఈ పేజీ యొక్క చరిత్రని చూడండి",
        "action-minoredit": "ఈ మార్పుని చిన్నదిగా గుర్తించే",
        "action-viewmyprivateinfo": "మీ గోపనీయ సమాచారాన్ని చూడండి",
        "action-editmyprivateinfo": "మీ గోపనీయ సమాచారాన్ని సరిదిద్దండి",
        "action-editcontentmodel": "పేజీ యొక్క కంటెంటు మోడలును సవరించు",
-       "action-managechangetags": "à°¡à±\87à°\9fాబà±\87à°¸à±\81à°²à±\8b à°\9fà±\8dయాà°\97à±\81లనà±\81 à°\9aà±\87à°°à±\8dà°\9aà±\87 à°²à±\87దా à°¤à±\8aà°²à°\97à°¿à°\82à°\9aే",
+       "action-managechangetags": "à°\9fà±\8dయాà°\97à±\81లనà±\81 à°\9aà±\87à°°à±\8dà°\9aà±\87, (à°\85)à°\9aà±\87తనà°\82 à°\9aà±\87à°¸ే",
        "action-applychangetags": "మీ మార్పులతో ట్యాగులను ఆపాదించే",
+       "action-deletechangetags": "డేటాబేసు నుండి ట్యాగులను తొలగించే",
        "nchanges": "{{PLURAL:$1|ఒక మార్పు|$1 మార్పులు}}",
        "enhancedrc-since-last-visit": "{{PLURAL:$1|చివరి సందర్శన తరువాత}}, $1",
        "enhancedrc-history": "చరితం",
        "recentchanges-legend-heading": "<strong>సూచిక :</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|కొత్త పేజీల జాబితా]]ను కూడా చూడండి)",
        "recentchanges-submit": "చూపించు",
-       "rcnotefrom": "<strong>$2</strong> నుండి జరిగిన మార్పులు (<strong>$1</strong> వరకు) కింద చూపబడ్డాయి.",
+       "rcnotefrom": "<strong>$3, $4</strong> తరువాత జరిగిన {{PLURAL:$5|మార్పు|మార్పులు}} కింద ఇచ్చాం (<strong>$1</strong> దాకా చూపించాం).",
        "rclistfrom": "$3, $2 కు ముందు  జరిగిన మార్పులను చూపించు",
        "rcshowhideminor": "చిన్న మార్పులను $1",
        "rcshowhideminor-show": "చూపించు",
        "newpageletter": "కొ",
        "boteditletter": "బా",
        "number_of_watching_users_pageview": "[వీక్షిస్తున్న సభ్యులు: {{PLURAL:$1|ఒక్కరు|$1}}]",
-       "rc_categories": "ఈ వర్గాలకు పరిమితం చెయ్యి (\"|\" తో వేరు చెయ్యండి)",
-       "rc_categories_any": "ఏదయినా",
+       "rc_categories": "ఈ వర్గాలకు పరిమితం చెయ్యి (\"|\" తో వేరు చెయ్యండి):",
+       "rc_categories_any": "à°\8eà°\82à°\9aà±\81à°\95à±\81à°¨à±\8dనది à°\8fదయినా",
        "rc-change-size-new": "మార్పు తర్వాత $1 {{PLURAL:$1|బైటు|బైట్లు}}",
        "newsectionsummary": "/* $1 */ కొత్త విభాగం",
        "rc-enhanced-expand": "వివరాలను చూపించు",
        "recentchangeslinked-summary": "ఏదైనా పేజీకి లింకై ఉన్న పేజీల్లో (లేదా ఏదైనా వర్గంలోని పేజీల్లో) జరిగిన ఇటీవలి మార్పుల జాబితా ఇది.  [[Special:Watchlist|మీ వీక్షణ జాబితా]]లో ఉన్న పేజీలు <strong>బొద్దు</strong>గా ఉంటాయి.",
        "recentchangeslinked-page": "పేజీ పేరు:",
        "recentchangeslinked-to": "లేదంటే, ఇచ్చిన పేజీకి లింకయివున్న పేజీలలో జరిగిన మార్పులను చూపించు",
+       "recentchanges-page-added-to-category": "[[:$1]] ను వర్గానికి చేర్చాం",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] వర్గానికి చేర్చబడింది, [[Special:WhatLinksHere/$1|ఈ పేజీ ఇతర పేజీల్లో చేర్చబడింది]]",
+       "recentchanges-page-removed-from-category": "[[:$1]] వర్గం నుండి తీసివేయబడింది",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] వర్గం నుండి తీసివేయబడింది, [[Special:WhatLinksHere/$1|ఈ పేజీ ఇతర పేజీల్లో చేర్చబడింది]]",
        "upload": "దస్త్రపు ఎక్కింపు",
        "uploadbtn": "దస్త్రాన్ని ఎక్కించు",
        "reuploaddesc": "మళ్ళీ అప్‌లోడు ఫారంకు వెళ్ళు.",
        "uploaderror": "ఎక్కింపు లోపం",
        "upload-recreate-warning": "<strong>హెచ్చరిక: ఆ పేరుతో ఉన్న దస్త్రాన్ని తరలించడం లేదా తొలగించడం జరిగింది.</strong>\n\nమీ సౌకర్యం కోసం ఈ పేజీ యొక్క తొలగింపు మరియు తరలింపు చిట్టాని ఇక్కడ ఇస్తున్నాం:",
        "uploadtext": "దస్త్రాలను ఎక్కించడానికి ఈ కింది ఫారాన్ని ఉపయోగించండి.\nగతంలో ఎక్కించిన దస్త్రాలను చూడడానికి లేదా వెతకడానికి [[Special:FileList|ఎక్కించిన దస్త్రాల యొక్క జాబితా]]కు వెళ్ళండి, (పునః)ఎక్కింపులు [[Special:Log/upload|ఎక్కింపుల చిట్టా]] లోనూ తొలగింపులు [[Special:Log/delete|తొలగింపుల చిట్టా]] లోనూ కూడా నమోదవుతాయి.\n\nఒక దస్త్రాన్ని ఏదైనా పుటలో చేర్చడానికి, కింద చూపిన వాటిలో ఏదేనీ విధంగా లింకుని వాడండి:\n* దస్త్రపు పూర్తి కూర్పుని వాడడానికి '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></code>'''\n* ఎడమ వైపు మార్జినులో 200 పిక్సెళ్ళ వెడల్పుగల బొమ్మ  మరియు 'ప్రత్యామ్నాయ పాఠ్యం' అన్న వివరణతో గల పెట్టె కోసం  '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|ప్రత్యామ్నాయ పాఠ్యం]]</nowiki></code>'''\n* దస్త్రాన్ని చూపించకుండా నేరుగా లింకు ఇవ్వడానికి '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code>'''",
-       "upload-permitted": "à°\85à°¨à±\81మతిà°\82à°\9aà±\87 à°«à±\88à°²à±\81 à°°à°\95ాలà±\81: $1.",
+       "upload-permitted": "à°\85à°¨à±\81మతిà°\82à°\9aబడిన à°«à±\88à°²à±\81 {{PLURAL:$2|à°°à°\95à°\82|à°°à°\95ాలà±\81}}: $1.",
        "upload-preferred": "అనుమతించే ఫైలు రకాలు: $1.",
-       "upload-prohibited": "నిషà±\87ధిà°\82à°\9aà°¿à°¨ à°«à±\88à°²à±\81 à°°à°\95ాలà±\81: $1.",
+       "upload-prohibited": "నిషà±\87ధిà°\82à°\9aబడిన à°«à±\88à°²à±\81 {{PLURAL:$2|à°°à°\95à°\82|à°°à°\95ాలà±\81}}: $1.",
        "uploadlogpage": "ఎక్కింపుల చిట్టా",
        "uploadlogpagetext": "ఇటీవల జరిగిన ఫైలు అప్‌లోడుల జాబితా ఇది.\nమరింత దృశ్యాత్మకంగా చూడటం కోసం [[Special:NewFiles|కొత్త ఫైళ్ళ కొలువు]]కు వెళ్ళండి.",
        "filename": "ఫైలు పేరు",
        "largefileserver": "ఈ ఫైలు సైజు సర్వరులో విధించిన పరిమితి కంటే ఎక్కువగా ఉంది.",
        "emptyfile": "మీరు అప్‌లోడు చేసిన ఫైలు ఖాళీగా ఉన్నట్లుంది. ఫైలు పేరును ఇవ్వడంలో స్పెల్లింగు తప్పు దొర్లి ఉండొచ్చు. మీరు అప్‌లోడు చెయ్యదలచింది ఇదో కాదో నిర్ధారించుకోండి.",
        "windows-nonascii-filename": "దస్త్రాల పేర్లలో ప్రత్యేక అక్షరాలకు ఈ వికీలో అనుకూలత లేదు.",
-       "fileexists": "ఈ పేరుతో ఒక ఫైలు ఇప్పటికే ఉంది. దీనిని మార్చాలో లేదో తెలియకపోతే ఫైలు <strong>[[:$1]]</strong>ను చూడండి.\n[[$1|thumb]]",
+       "fileexists": "ఈ పేరుతో ఒక ఫైలు ఇప్పటికే ఉంది. దీనిని మార్చాలో లేదో {{GENDER:|మీకు}} తెలియకపోతే ఫైలు <strong>[[:$1]]</strong>ను చూడండి.\n[[$1|thumb]]",
        "filepageexists": "ఈ ఫైలు కొరకు వివరణ పేజీని <strong>[[:$1]]</strong> వద్ద ఈసరికే సృష్టించారు, కానీ ఆ పేరుతో ప్రస్తుతం ఫైలేదీ లేదు.\nమీరు ఇస్తున్న సంగ్రహం ఆ వివరణ పేజీలో కనబడదు. \nమీ సంగ్రహం అక్కడ కనబడాలంటే, నేరుగా అక్కడే చేర్చాలి.\n[[$1|thumb]]",
-       "fileexists-extension": "à°\87à°\9fà±\81à°µà°\82à°\9fà°¿ à°ªà±\87à°°à±\81à°¤à±\8bà°\9fà±\87 à°®à°°à±\8b à°«à±\88à°²à±\81 à°\89à°\82ది: [[$2|thumb]]\n* à°\8eà°\95à±\8dà°\95à°¿à°¸à±\8dà°¤à±\81à°¨à±\8dà°¨ à°«à±\88à°²à±\81 à°ªà±\87à°°à±\81: <strong>[[:$1]]</strong>\n* à°ªà±\8dà°°à°¸à±\8dà°¤à±\81à°¤à°\82 à°\89à°¨à±\8dà°¨ à°«à±\88à°²à±\81 à°ªà±\87à°°à±\81: <strong>[[:$2]]</strong>\nమరà±\8b à°ªà±\87à°°à±\81 à°\8eà°\82à°\9aà±\81à°\95à±\8bà°\82à°¡à°¿.",
+       "fileexists-extension": "à°\87à°\9fà±\81à°µà°\82à°\9fà°¿ à°ªà±\87à°°à±\81à°¤à±\8bà°\9fà±\87 à°®à°°à±\8b à°«à±\88à°²à±\81 à°\89à°\82ది: [[$2|thumb]]\n* à°\8eà°\95à±\8dà°\95à°¿à°¸à±\8dà°¤à±\81à°¨à±\8dà°¨ à°«à±\88à°²à±\81 à°ªà±\87à°°à±\81: <strong>[[:$1]]</strong>\n* à°ªà±\8dà°°à°¸à±\8dà°¤à±\81à°¤à°\82 à°\89à°¨à±\8dà°¨ à°«à±\88à°²à±\81 à°ªà±\87à°°à±\81: <strong>[[:$2]]</strong>\nమరిà°\82à°¤ à°µà±\88విధà±\8dయమà±\88à°¨ à°ªà±\87à°°à±\81 à°\8eà°\82à°\9aà±\81à°\95à±\81à°\82à°\9fారా?",
        "fileexists-thumbnail-yes": "ఈ ఫైలు కుదించిన బొమ్మ లాగా ఉంది ''(థంబ్‌నెయిలు)''. [[$1|thumb]]\n<strong>[[:$1]]</strong> ఫైలు చూడండి.\nగుర్తు పెట్టబడిన ఫైలు అసలు సైజే అది అయితే, మరో థంబ్‌నెయిలును అప్‌లోడు చెయ్యాల్సిన అవసరం లేదు.",
        "file-thumbnail-no": "ఫైలు పేరు <strong>$1</strong> తో మొదలవుతోంది.\nఅది పరిమాణం తగ్గించిన ''(నఖచిత్రం)'' లాగా అనిపిస్తోంది.\nఈ బొమ్మ యొక్క పూర్తి స్పష్టత కూర్పు ఉంటే, దాన్ని ఎగుమతి చెయ్యండి. లేదా ఫైలు పేరును మార్చండి.",
        "fileexists-forbidden": "ఈ పేరుతో ఇప్పటికే ఒక ఫైలు ఉంది, దాన్ని తిరగరాయలేరు.\nమీరు ఇప్పటికీ ఈ ఫైలుని ఎగుమతి చేయాలనుకుంటే, వెనక్కి వెళ్ళి మరో పేరుతో ఎగుమతి చేయండి. [[File:$1|thumb|center|$1]]",
        "statistics-pages": "పేజీలు",
        "statistics-pages-desc": "ఈ వికీలోని అన్ని పేజీలు (చర్చా పేజీలు, దారిమార్పులు, మొదలైనవన్నీ కలుపుకొని).",
        "statistics-files": "ఎక్కించిన దస్త్రాలు",
-       "statistics-edits": "{{SITENAME}}ని మొదలుపెట్టినప్పటినుండి జరిగిన మార్పులు",
+       "statistics-edits": "{{SITENAME}} మొదలుపెట్టినప్పటినుండి జరిగిన మార్పులు",
        "statistics-edits-average": "పేజీకి సగటు మార్పులు",
        "statistics-users": "నమోదైన [[Special:ListUsers|వాడుకరులు]]",
        "statistics-users-active": "క్రియాశీల వాడుకరులు",
        "apihelp-no-such-module": "\"$1\" మాడ్యూలు కనబడలేదు.",
        "apisandbox": "API ప్రయోగశాల",
        "apisandbox-api-disabled": "ఈ సైటులో API అచేతనమై ఉంది.",
+       "apisandbox-unfullscreen": "పేజీను చూపించు",
        "apisandbox-submit": "అభ్యర్ధించు",
        "apisandbox-reset": "తుడిచివేయి",
-       "apisandbox-examples": "ఉదాహరణ",
-       "apisandbox-results": "ఫలితం",
+       "apisandbox-retry": "మళ్ళీ ప్రయత్నించు",
+       "apisandbox-helpurls": "సహాయపు లంకెలు",
+       "apisandbox-examples": "ఉదాహరణలు",
+       "apisandbox-dynamic-parameters": "అదనపు పరామితులు",
+       "apisandbox-dynamic-parameters-add-label": "పరామితిని చేర్చు:",
+       "apisandbox-dynamic-parameters-add-placeholder": "పరామితి పేరు",
+       "apisandbox-dynamic-error-exists": "\"$1\" అనే పరామితి ఇప్పటికే ఉంది.",
+       "apisandbox-results": "ఫలితాలు",
        "apisandbox-request-url-label": "అభ్యర్థన URL:",
        "apisandbox-request-time": "అభ్యర్ధన సమయం: $1",
        "booksources": "పుస్తక మూలాలు",
        "log-title-wildcard": "ఈ పాఠ్యంతో మొదలయ్యే పుస్తకాల కొరకు వెతుకు",
        "showhideselectedlogentries": "ఎంచుకున్న చిట్టా పద్దులను చూపించు/దాచు",
        "log-edit-tags": "ఎంచుకున్న చిట్టా ప్రవేశాల ట్యాగులను సవరించు",
+       "checkbox-all": "అన్నీ",
+       "checkbox-none": "దేన్నీ వద్దు",
+       "checkbox-invert": "తిరగవెయ్యి",
        "allpages": "అన్ని పేజీలు",
        "nextpage": "తరువాతి పేజీ ($1)",
        "prevpage": "మునుపటి పేజీ ($1)",
        "activeusers-hidebots": "బాట్లను దాచు",
        "activeusers-hidesysops": "నిర్వాహకులను దాచు",
        "activeusers-noresult": "వాడుకరులెవరూ లేరు.",
+       "activeusers-submit": "చేతనంగా ఉన్న వాడుకరులను చూపించు",
        "listgrouprights": "వాడుకరి గుంపుల హక్కులు",
        "listgrouprights-summary": "కింది జాబితాలో ఈ వికీలో నిర్వచించిన వాడుకరి గుంపులు, వాటికి సంబంధించిన హక్కులు ఉన్నాయి.\nవిడివిడిగా హక్కులకు సంబంధించిన మరింత సమాచారం [[{{MediaWiki:Listgrouprights-helppage}}]] వద్ద లభించవచ్చు.",
        "listgrouprights-key": "సూచిక:\n* <span class=\"listgrouprights-granted\">ప్రసాదించిన హక్కు</span>\n* <span class=\"listgrouprights-revoked\">వెనక్కి తీసుకున్న హక్కు</span>",
        "listgrouprights-namespaceprotection-header": "పేరుబరి నిబంధనలు",
        "listgrouprights-namespaceprotection-namespace": "పేరుబరి",
        "listgrouprights-namespaceprotection-restrictedto": "వాడుకరి మార్పు చేయుటకు హక్కు(లు)",
+       "listgrants": "గ్రాంట్లు",
+       "listgrants-grant": "గ్రాంటు",
+       "listgrants-rights": "హక్కులు",
        "trackingcategories": "పహారా కాయు వర్గాలు",
        "trackingcategories-msg": "పహారా కార్యు వర్గము",
        "trackingcategories-name": "సందేశం పేరు",
        "watchlist-submit": "చూపించు",
        "wlshowtime": "చూపించాల్సిన కాలం:",
        "wlshowhideminor": "చిన్న మార్పులు",
-       "wlshowhidebots": "బాట్లు",
+       "wlshowhidebots": "బాట్లు",
        "wlshowhideliu": "నమోదైన వాడుకరులు",
        "wlshowhideanons": "అజ్ఞాత వాడుకరులు",
        "wlshowhidemine": "నా మార్పులు",
        "sessionfailure-title": "సెషను వైఫల్యం",
        "sessionfailure": "మీ ప్రవేశపు సెషనుతో ఏదో సమస్య ఉన్నట్లుంది;\nసెషను హైజాకు కాకుండా ఈ చర్యను రద్దు చేసాం.\n\"back\" కొట్టి, ఎక్కడి నుండి వచ్చారో ఆ పేజీని మళ్ళీ లోడు చేసి, తిరిగి ప్రయత్నించండి.",
        "changecontentmodel-reason-label": "కారణం:",
+       "changecontentmodel-submit": "మార్చు",
        "protectlogpage": "సంరక్షణల చిట్టా",
        "protectlogtext": "ఈ క్రింద ఉన్నది పేజీల సంరక్షణలకు జరిగిన మార్పుల జాబితా.\nప్రస్తుతం అమలులో ఉన్న సంరక్షణలకై [[Special:ProtectedPages|సంరక్షిత పేజీల జాబితా]]ను చూడండి.",
        "protectedarticle": "\"[[$1]]\" సంరక్షించబడింది.",
        "undeletedrevisions": "{{PLURAL:$1|ఒక సంచిక|$1 సంచికల}} పునఃస్థాపన జరిగింది",
        "undeletedrevisions-files": "{{PLURAL:$1|ఒక కూర్పు|$1 కూర్పులు}} మరియు {{PLURAL:$2|ఒక ఫైలు|$2 ఫైళ్ళ}}ను పునస్థాపించాం",
        "undeletedfiles": "{{PLURAL:$1|ఒక ఫైలును|$1 ఫైళ్లను}} పునఃస్థాపించాం",
-       "cannotundelete": "తొలగింపు రద్దు విఫలమైంది:\n$1",
+       "cannotundelete": "తొలగింపు రద్దులు పూర్తిగానో, కొన్నిగానీ విఫలమయ్యాయి:\n$1",
        "undeletedpage": "'''$1 ను పునస్థాపించాం'''\n\nఇటీవల జరిగిన తొలగింపులు, పునస్థాపనల కొరకు [[Special:Log/delete|తొలగింపు చిట్టా]]ని చూడండి.",
        "undelete-header": "ఇటీవల తొలగించిన పేజీల కొరకు [[Special:Log/delete|తొలగింపు చిట్టా]]ని చూడండి.",
        "undelete-search-title": "తొలగించిన పేజీల అన్వేషణ",
        "blanknamespace": "(మొదటి)",
        "contributions": "{{GENDER:$1|వాడుకరి}} రచనలు",
        "contributions-title": "$1 యొక్క మార్పులు-చేర్పులు",
-       "mycontris": "మార్పుచేర్పులు",
+       "mycontris": "నా మార్పులు",
+       "anoncontribs": "యోగదానములు",
        "contribsub2": "{{GENDER:$3|$1}} ($2) కొరకు",
        "contributions-userdoesnotexist": "వాడుకరి ఖాతా \"$1\" నమోదుకాలేదు.",
        "nocontribs": "ఈ విధమైన మార్పులేమీ దొరకలేదు.",
        "sp-contributions-newbies-sub": "కొత్తవారి కోసం",
        "sp-contributions-newbies-title": "కొత్త ఖాతాల వాడుకరుల మార్పుచేర్పులు",
        "sp-contributions-blocklog": "నిరోధాల చిట్టా",
-       "sp-contributions-suppresslog": "అణచిపెట్టబడిన వాడుకరి రచనలు",
-       "sp-contributions-deleted": "తొలగించబడిన వాడుకరి రచనలు",
+       "sp-contributions-suppresslog": "అణచిపెట్టబడిన {{GENDER:$1|వాడుకరి}} రచనలు",
+       "sp-contributions-deleted": "తొలగించబడిన {{GENDER:$1|వాడుకరి}} రచనలు",
        "sp-contributions-uploads": "ఎక్కింపులు",
        "sp-contributions-logs": "చిట్టాలు",
        "sp-contributions-talk": "చర్చ",
        "whatlinkshere-prev": "{{PLURAL:$1|మునుపటిది|మునుపటి $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|తరువాతది|తరువాతి $1}}",
        "whatlinkshere-links": "← లంకెలు",
-       "whatlinkshere-hideredirs": "దారిమార్పులను $1",
+       "whatlinkshere-hideredirs": "$1 దారిమార్పులు",
        "whatlinkshere-hidetrans": "$1 ట్రాన్స్‌క్లూజన్లు",
-       "whatlinkshere-hidelinks": "లింకులను $1",
+       "whatlinkshere-hidelinks": "$1 లింకులు",
        "whatlinkshere-hideimages": "$1 దస్త్రాల లంకెలు",
        "whatlinkshere-filters": "వడపోతలు",
        "whatlinkshere-submit": "వెళ్ళు",
        "movepagetext-noredirectfixer": "కింది ఫారాన్ని వాడి, ఓ పేజీ పేరు మార్చవచ్చు. దాని చరిత్ర పూర్తిగా కొత్త పేరుకు తరలిపోతుంది. \nపాత శీర్షిక కొత్తదానికి దారిమార్పు పేజీగా మారిపోతుంది.\n[[Special:DoubleRedirects|double]] లేదా [[Special:BrokenRedirects|broken redirects]] లను చూడటం మరువకండి.\nలింకులు వెళ్ళాల్సిన చోటికి వెళ్తున్నాయని నిర్ధారించుకోవాల్సిన బాధ్యత మీదే.\nకొత్త పేరుతో ఈసరికే ఏదైనా పేజీ ఉంటే - అది ఖాళీగా ఉన్నా లేక మార్పుచేర్పుల చరిత్ర ఏమీ లేని దారిమార్పు పేజీ అయినా తప్ప- తరలింపు ’’’జరుగదు’’’ అని గమనించండి.\nఅంటే, ఏదైనా పొరపాటు జరిగితే పేరును తిరిగి పాత పేరుకే మార్చగలరు తప్ప, ఈపాటికే ఉన్న పేజీపై ఓవరరైటు చెయ్యలేరు.\n\n'''హెచ్చరిక!'''\nబహుళ వ్యాప్తి పొందిన ఓ పేజీలో ఈ మార్పు చాలా తీవ్రమైనది, ఊహించనిదీ అవుతుంది.\nదాని పర్యవసానాలు అర్థం చేసుకున్నాకే ముందుకు వెళ్ళండి.",
        "movepagetalktext": "దానితో పాటు సంబంధిత చర్చా పేజీ కూడా ఆటోమాటిక్‌‌గా తరలించబడుతుంది, '''కింది సందర్భాలలో తప్ప:'''\n*ఒక నేంస్పేసు నుండి ఇంకోదానికి తరలించేటపుడు,\n*కొత్త పేరుతో ఇప్పటికే ఒక చర్చా పేజీ ఉంటే,\n*కింది చెక్‌బాక్సులో టిక్కు పెట్టకపోతే.\n\nఆ సందర్భాలలో, మీరు చర్చా పేజీని కూడా పనిగట్టుకుని తరలించవలసి ఉంటుంది, లేదా ఏకీకృత పరచవలసి ఉంటుంది.",
        "moveuserpage-warning": "'''హెచ్చరిక:''' మీరు ఒక వాడుకరి పేజీని తరలించబోతున్నారు. పేజీ మాత్రమే తరలించబడుతుందనీ, వాడుకరి పేరుమార్పు జరగదనీ గమనించండి.",
+       "movecategorypage-warning": "<strong>హెచ్చరిక:</strong> మీరు ఓ వర్గం పేజీని తరలించబోతున్నారు. కేవలం పేజీ మాత్రమే తరలుతుందని, పాత వర్గంలో ఉన్న పేజీలేవీ కొత్త వర్గంలోకి <em>చేరవని</em>  గ్రహించండి.",
        "movenologintext": "పేజీని తరలించడానికి మీరు [[Special:UserLogin|లాగిన్‌]] అయిఉండాలి.",
        "movenotallowed": "పేజీలను తరలించడానికి మీకు అనుమతి లేదు.",
        "movenotallowedfile": "మీకు ఫైళ్ళను తరలించే అనుమతి లేదు.",
        "javascripttest": "జావాస్క్రిప్ట్ పరీక్ష",
        "javascripttest-pagetext-unknownaction": "తెలియని చర్య \"$1\".",
        "javascripttest-qunit-intro": "mediawiki.org లోని [$1 పరీక్షా డాక్యుమెంటేషన్] చూడండి.",
-       "tooltip-pt-userpage": "మీ వాడుకరి పేజీ",
+       "tooltip-pt-userpage": "{{GENDER:|మీ వాడుకరి}} పేజీ",
        "tooltip-pt-anonuserpage": "మీ ఐపీ చిరునామాకి సంబంధించిన వాడుకరి పేజీ",
-       "tooltip-pt-mytalk": "మీ చర్చా పేజీ",
+       "tooltip-pt-mytalk": "{{GENDER:|మీ}} చర్చా పేజీ",
        "tooltip-pt-anontalk": "ఈ ఐపీ చిరునామా నుండి చేసిన మార్పుల గురించి చర్చ",
-       "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": "à°\88 à°µà°¾à°¡à±\81à°\95à°°à°¿ à°¯à±\8aà°\95à±\8dà°\95 à°°à°\9aనల à°\9cాబితా à°\9aà±\82à°¡à°\82à°¡à°¿",
+       "tooltip-t-contributions": "à°¯à±\8bà°\97దానమà±\81à°²à±\81 à°\9cాబితాâ\80\8c {{GENDER:$1|à°\88 à°µà°¾à°¡à±\81à°\95à°°à°¿}}",
        "tooltip-t-emailuser": "ఈ వాడుకరికి ఓ ఈమెయిలు పంపండి",
        "tooltip-t-info": "ఈ పేజీ గురించి మరింత సమాచారం",
        "tooltip-t-upload": "దస్త్రాలను ఎక్కించండి",
        "confirm-watch-top": "ఈ పుటను మీ వీక్షణ జాబితాలో చేర్చాలా?",
        "confirm-unwatch-button": "సరే",
        "confirm-unwatch-top": "ఈ పుటను మీ వీక్షణ జాబితా నుండి తొలగించాలా?",
+       "confirm-rollback-button": "సరే",
        "quotation-marks": "“$1”",
        "imgmultipageprev": "← మునుపటి పేజీ",
        "imgmultipagenext": "తరువాతి పేజీ →",
        "tags-edit-title": "ట్యాగులను సవరించు",
        "tags-edit-manage-link": "ట్యాగులను నిర్వహించండి",
        "tags-edit-existing-tags": "ప్రస్తుత ట్యాగులు:",
-       "tags-edit-existing-tags-none": "''ఏమీలేవు''",
+       "tags-edit-existing-tags-none": "<em>ఏమీలేవు</em>",
        "tags-edit-new-tags": "కొత్త ట్యాగులు:",
        "tags-edit-add": "ఈ ట్యాగులను చేర్చు:",
        "tags-edit-remove": "ఈ ట్యాగులను తొలగించు:",
        "special-characters-title-minus": "మైనస్ గుర్తు",
        "mw-widgets-dateinput-no-date": "ఏ తేదీనీ ఎంచుకోలేదు",
        "mw-widgets-titleinput-description-new-page": "పేజీ ఇంకా లేదు",
-       "api-error-blacklisted": "వేరే వివరమైన శీర్షకను ఎంచుకోండి"
+       "log-action-filter-all": "అన్నీ",
+       "authmanager-realname-label": "అసలు పేరు",
+       "authmanager-realname-help": "వాడుకరి అసలు పేరు",
+       "authmanager-provider-temporarypassword": "తాత్కాలిక సంకేతపదం",
+       "credentialsform-account": "ఖాతా పేరు:"
 }
index 23c10a3..656ea39 100644 (file)
@@ -24,7 +24,9 @@
                        "Pphongpan355",
                        "Macofe",
                        "Pilarbini",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "B20180",
+                       "Pon44695"
                ]
        },
        "tog-underline": "การขีดเส้นใต้ลิงก์:",
        "laggedslavemode": "<strong>คำเตือน:</strong> หน้านี้อาจไม่มีการปรับล่าสุด",
        "readonly": "ฐานข้อมูลถูกล็อก",
        "enterlockreason": "ใส่เหตุแห่งการล็อก ทั้งเวลาที่คาดว่าจะปลดล็อก",
-       "readonlytext": "à¸\82à¸\93ะà¸\99ีà¹\89à¸\90าà¸\99à¸\82à¹\89อมูลà¸\96ูà¸\81ลà¹\87อà¸\81มิà¹\83หà¹\89รัà¸\9aà¸\82à¹\89อมูลใหม่และการแก้ไขเพิ่มเติมอื่น อาจเป็นเพราะการบำรุงรักษาฐานข้อมูลรูทีน หลังแล้วเสร็จจะกลับมาใช้งานได้ตามปกติ\n\nผู้ดูแลระบบที่ล็อกได้ให้คำอธิบายดังนี้: $1",
+       "readonlytext": "à¸\82à¸\93ะà¸\99ีà¹\89à¸\90าà¸\99à¸\82à¹\89อมูลà¸\96ูà¸\81ลà¹\87อà¸\81รายà¸\81ารใหม่และการแก้ไขเพิ่มเติมอื่น อาจเป็นเพราะการบำรุงรักษาฐานข้อมูลรูทีน หลังแล้วเสร็จจะกลับมาใช้งานได้ตามปกติ\n\nผู้ดูแลระบบที่ล็อกได้ให้คำอธิบายดังนี้: $1",
        "missing-article": "ฐานข้อมูลไม่พบข้อความของหน้าที่ควรมี ชื่อ \"$1\" $2\n\nสาเหตุมักเกิดจากผลต่างที่ล้าสมัย หรือการเชื่อมโยงประวัติไปยังหน้านั้นถูกลบแล้ว\n\nหากไม่ใช่กรณีดังกล่าว คุณอาจพบจุดบกพร่องในซอฟต์แวร์ กรุณารายงานต่อ[[Special:ListUsers/sysop|ผู้ดูแลระบบ]] พร้อมระบุยูอาร์แอล",
        "missingarticle-rev": "(รุ่นปรับปรุง#: $1)",
        "missingarticle-diff": "(ต่าง: $1, $2)",
        "protectedinterface": "หน้านี้เป็นข้อความส่วนต่อประสานสำหรับซอฟต์แวร์บนวิกินี้ และถูกล็อกเพื่อป้องกันการกระทำผิด\nในการเพิ่มหรือเปลี่ยนแปลงการแปลสำหรับทุกวิกิ โปรดใช้ [https://translatewiki.net/ translatewiki.net] โครงการแปลมีเดียวิกิเป็นภาษาถิ่น",
        "editinginterface": "<strong>คำเตือน:</strong> คุณกำลังแก้ไขหน้าที่ใช้จัดหาข้อความอินเตอร์เฟซให้ซอฟต์แวร์\nการเปลี่ยนแปลงหน้านี้จะมีผลต่อสภาพปรากฏของส่วนต่อประสานผู้ใช้แก่ผู้ใช้อื่นบนวิกินี้",
        "translateinterface": "ในการเพิ่มหรือเปลี่ยนแปลงคำแปลสำหรับทุกวิกิ โปรดใช้ [https://translatewiki.net/ translatewiki.net] โครงการแปลเป็นภาษาถิ่นของมีเดียวิกิ",
-       "cascadeprotected": "หน้านี้ถูกป้องกันมิให้แก้ไข เพราะถูกรวมอยู่ในหน้าต่อไปนี้ซึ่งถูกล็อกโดยเปิดตัวเลือก \"ต่อเรียง\":\n$2",
+       "cascadeprotected": "หน้านี้ถูกป้องกันมิให้แก้ไข เพราะถูกรวมอยู่ใน{{PLURAL:$1|page, which is|หน้าต่อไปนี้}} ซึ่งถูกล็อกโดยเปิดตัวเลือก \"ต่อเรียง\":\n$2",
        "namespaceprotected": "คุณไม่มีสิทธิแก้ไขหน้าในเนมสเปซ <strong>$1</strong>",
        "customcssprotected": "คุณไม่มีสิทธิแก้ไขหน้า CSS นี้ เพราะมีการตั้งค่าส่วนบุคคลของผู้ใช้อื่น",
        "customjsprotected": "คุณไม่มีสิทธิแก้ไขหน้าจาวาสคริปต์นี้ เพราะหน้านี้มีการตั้งค่าส่วนบุคคลของผู้ใช้อื่น",
        "passwordreset-emailelement": "ชื่อผู้ใช้: \n$1\n\nรหัสผ่านชั่วคราว: \n$2",
        "passwordreset-emailsentemail": "หากที่อยู่อีเมลนี้สัมพันธ์กับบัญชีของคุณ เช่นนั้นจะส่งอีเมลตั้งรหัสผ่านใหม่",
        "passwordreset-emailsentusername": "หากมีที่อยู่อีเมลที่ลงทะเบียนไว้ด้วยกับชื่อผู้ใช้นี้ เช่นนั้นจะส่งอีเมลตั้งรหัสผ่านใหม่",
-       "passwordreset-emailsent-capture": "อีเมลตั้งรหัสผ่านใหม่ถูกส่งไปแล้ว ซึ่งแสดงด้านล่าง",
-       "passwordreset-emailerror-capture": "อีเมลตั้งรหัสผ่านใหม่ถูกสร้างขึ้นแล้ว ซึ่งแสดงด้านล่าง แต่ไม่สามารถส่งไปยัง{{GENDER:$2|ผู้ใช้}}: $1",
        "changeemail": "เปลี่ยนหรือลบที่อยู่อีเมล",
        "changeemail-header": "กรอกแบบนี้เพื่อเปลี่ยนที่อยู่อีเมลของคุณ หากคุณต้องการลบการเชื่อมโยงของที่อยู่อีเมลใด ๆ จากบัญชีของคุณ ให้เว้นที่อยู่อีเมลใหม่ว่างเมื่อส่งแบบ",
-       "changeemail-passwordrequired": "คุณจะต้องกรอกรหัสผ่านใหม่เพื่อยืนยันการเปลี่ยนแปลงนี้",
        "changeemail-no-info": "คุณจำต้องล็อกอินเพื่อเข้าถึงหน้านี้โดยตรง",
        "changeemail-oldemail": "ที่อยู่อีเมลปัจจุบัน:",
        "changeemail-newemail": "ที่อยู่อีเมลใหม่:",
        "undo-nochange": "ดูเหมือนว่าการแก้ไขดังกล่าวถูกย้อนกลับแล้ว",
        "undo-summary": "ทำกลับรุ่นแก้ไข $1 โดย [[Special:Contributions/$2|$2]] ([[User talk:$2|พูดคุย]])",
        "undo-summary-username-hidden": "ทำกลับรุ่นแก้ไข $1 โดยผู้ใช้ไม่ระบุชื่อ",
-       "cantcreateaccounttitle": "ไม่สามารถสร้างบัญชีได้",
        "cantcreateaccount-text": "การสร้างบัญชีใหม่จากที่อยู่ไอพีนี้ ('''$1''') ถูกระงับโดย [[User:$3|$3]]\n\nเหตุผลที่ $3 ให้ไว้ คือ ''$2''",
        "cantcreateaccount-range-text": "การสร้างบัญชีจากเลขที่อยู่ไอพีในช่วง <strong>$1</strong> ซึ่งรวมเลขที่อยู่ไอพีของคุณ (<strong>$4</strong>) ถูกบล็อกโดย [[User:$3|$3]] \n\nเหตุผลที่ $3 ชี้แจง คือ <em>$2</em>",
        "viewpagelogs": "ดูปูมของหน้านี้",
        "whatlinkshere-prev": "{{PLURAL:$1|ก่อนหน้า|ก่อนหน้า $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|ถัดไป|ถัดไป $1}}",
        "whatlinkshere-links": "← ลิงก์",
-       "whatlinkshere-hideredirs": "$1การเปลี่ยนทาง",
+       "whatlinkshere-hideredirs": "$1 การเปลี่ยนทาง",
        "whatlinkshere-hidetrans": "$1 ถูกรวมอยู่",
        "whatlinkshere-hidelinks": "$1 ลิงก์",
        "whatlinkshere-hideimages": "$1ลิงก์ไฟล์",
        "special-characters-group-bangla": "บังคลา",
        "special-characters-group-telugu": "เตลูกู",
        "special-characters-group-sinhala": "สิงหล",
-       "special-characters-group-gujarati": "คุชราต",
-       "api-error-blacklisted": "โปรดใช้ชื่ออื่นที่สื่อความหมาย"
+       "special-characters-group-gujarati": "คุชราต"
 }
index 385deaa..a63aac3 100644 (file)
@@ -85,7 +85,9 @@
                        "Imabadplayer",
                        "İnternion",
                        "Hbseren",
-                       "Kumkumuk"
+                       "Kumkumuk",
+                       "Basak",
+                       "Ece Alpdeniz"
                ]
        },
        "tog-underline": "Bağlantıların altını çiz:",
        "tagline": "{{SITENAME}} sitesinden",
        "help": "Yardım",
        "search": "Ara",
+       "search-ignored-headings": " #<!-- bu satırı olduğu gibi bırakın --> <pre>\n# Başlıklar aramada göz ardı edilecektir.\n# Bu değişiklikler başlık ve sayfa endeksli şekilde en kısa sürede yürürlüğe girecektir.\n# Boş bir düzenleme yaparak, yeniden dizin oluşturulmasını zorlayabilirsiniz.\n# Sözdizimi aşağıdaki gibidir:\n#   * \"#\" karakteri ile başlayan her satır, satır sonuna kadar bir yorumdur.\n#   * Boş olmayan her türlü satırın tam başlığı her durumda görmezden gelinir.\nKaynaklar\nDış bağlantılar\nAyrıca bakınız\n #</pre> <!-- bu satırları olduğu gibi bırakın-->",
        "searchbutton": "Ara",
        "go": "Git",
        "searcharticle": "Git",
        "print": "Yazdır",
        "view": "Görüntüle",
        "view-foreign": "$1 üzerinde gör",
-       "edit": "Değiştir",
+       "edit": "Düzenle",
        "edit-local": "Yerel açıklamayı düzenle",
        "create": "Oluştur",
        "create-local": "Yerel açıklama ekle",
        "yourpasswordagain": "Parolayı yeniden girin:",
        "createacct-yourpasswordagain": "Parolayı onayla",
        "createacct-yourpasswordagain-ph": "Parolayı yeniden girin",
-       "remembermypassword": "Girişimi bu tarayıcıda hatırla (en fazla $1 {{PLURAL:$1|gün|gün}} için)",
        "userlogin-remembermypassword": "Oturumumu sürekli açık tut",
        "userlogin-signwithsecure": "Güvenli bağlantı kullanın",
        "cannotloginnow-title": "Şu an oturum açılamıyor",
        "passwordreset-emailelement": "Kullanıcı adı: \n$1\n\nGeçici şifre: \n$2",
        "passwordreset-emailsentemail": "Eğer bu e-posta adresi hesabınızın bağlı olduğu adres ise, bir parola sıfırlama e-postası gönderilecektir.",
        "passwordreset-emailsentusername": "Eğer bu e-posta adresi hesabınızın bağlı olduğu adres ise, bir parola sıfırlama e-postası gönderilecektir.",
-       "passwordreset-emailsent-capture": "Aşağıda gözüktüğü gibi bir parola sıfırlama e-postası gönderildi.",
-       "passwordreset-emailerror-capture": "Aşağıda gözüktüğü gibi bir parola sıfırlama e-postası oluşturuldu ancak {{GENDER:$2|kullanıcıya}} gönderme işlemi başarısız oldu: $1",
        "passwordreset-invalideamil": "Geçersiz e-posta adresi",
        "passwordreset-nodata": "Ne bir kullanıcı adı ne de bir e-posta adresi verildi.",
        "changeemail": "E-posta adresini değiştir veya çıkar",
        "changeemail-header": "E-posta adresinizi değiştirmek için bu formu doldurun. Eğer e-posta adresini hesabınızdan kaldırmak istiyorsanız formu gönderirken e-posta adresi bölümünü boş bırakın.",
-       "changeemail-passwordrequired": "Bu değişikliği onaylamak için şifrenizi girmeniz gerekir.",
        "changeemail-no-info": "Bu sayfaya doğrudan erişmek için oturum açmanız gereklidir.",
        "changeemail-oldemail": "Mevcut E-posta adresi:",
        "changeemail-newemail": "Yeni E-posta adresi:",
        "savearticle": "Sayfayı kaydet",
        "savechanges": "Değişiklikleri kaydet",
        "publishpage": "Sayfayı yayımla",
+       "publishchanges": "Değişiklikleri yayımla",
        "preview": "Önizleme",
        "showpreview": "Önizlemeyi göster",
        "showdiff": "Değişiklikleri göster",
        "undo-nochange": "Düzeltme zaten geri alınmış.",
        "undo-summary": "$1 değişikliği [[Special:Contributions/$2|$2]] ([[User talk:$2|mesaj]]) tarafından geri alındı.",
        "undo-summary-username-hidden": "Gizli bir kullanıcı tarafından $1 sürümü geri alınıyor",
-       "cantcreateaccounttitle": "Hesap açılamıyor",
        "cantcreateaccount-text": "Bu IP adresinden ('''$1''') kullanıcı hesabı oluşturulması [[User:$3|$3]] tarafından engellenmiştir.\n\n$3 tarafından verilen sebep ''$2''",
        "cantcreateaccount-range-text": "<strong>$1</strong> aralığındaki IP'ler için hesap oluşturma [[User:$3|$3]] tarafından engellendi, bu sizin IP adresinizi de (<strong>$4</strong>) içeriyor.\n\n$3 tarafından verilen gerekçe <em>$2</em>",
        "viewpagelogs": "Bu sayfa ile ilgili kayıtları göster",
        "log-action-filter-delete": "Silme türü:",
        "log-action-filter-patrol": "Devriye türü:",
        "log-action-filter-protect": "Koruma tipi:",
+       "log-action-filter-rights": "Hak türü seçimi:",
+       "log-action-filter-suppress": "Kaldırma türü:",
        "log-action-filter-upload": "Yükleme türü:",
        "log-action-filter-all": "Tümü",
        "log-action-filter-block-block": "Blok",
index bafa6e8..0af6224 100644 (file)
        "passwordreset-emailtext-user": "{{SITENAME}} проектыннан $1 кулланучысы {{SITENAME}} ($4) проектында сезнең серсүзне искә төшерүне сорады.\n{{PLURAL:$3|1=Түбәндәге хисап язмасы|Түбәндәге хисап язмалары}} бу электрон әрҗә адресы белән бәйле:\n\n$2\n\n{{PLURAL:$3|1=Бу вакытлы серсүз|Бу вакытлы серсүзләр}} {{PLURAL:$5|$5 көн}} дәвамында эшлиячәкләр.\nСез системага керергә һәм яңа серсүз сайларга тиешсез.\nӘгәр сез серсүз сорамаган булсагыз яки элеккеге серсүзегезне искә төшерсәгез \nһәм аны үзгәртергә теләмәсәгез, бу хатка җавап бирмәгез\nһәм элеккеге серсүзегезне кулланыгыз.",
        "passwordreset-emailelement": "Кулланучы исеме: \n$1\n\nВакытлыча серсүз: \n$2",
        "passwordreset-emailsentemail": "Әгәрдә бу электрон әрҗәгез сезнең хисап язмагыз белән бәйләнгән булса,сезгә серсүзне яңарту өчен хат җибәреләчәк.",
-       "passwordreset-emailsent-capture": "Серсүзне яңарту турындагы мәгълүмәт электрон хат белән сезгә җибәрелде, аның текстын түбәндә карарга мөмкин.",
-       "passwordreset-emailerror-capture": "Серсүзне яңарту турында хәбәр итече хат ясалды, ләкин аны  {{GENDER:$2|кулланучыга}} түбәндәге сәбәп аркасында җибәре булмады: $1",
        "changeemail": "Электрон әрҗә адресын үзгәртү яисә бетерү",
        "changeemail-header": "Электрон әрҗә адресын үзгәртү өчен әлеге форманы тутырыгыз. Әгәрдә сез электрон әрҗәгезне хисап язмагыздан өзәсегез килмәсә, форманы тутырганда яңа адрес урынын буш калдырыгыз.",
-       "changeemail-passwordrequired": "Әлеге үзгәрешләрне раслау өчен, Сезгә кулланылучы серсүзне язарга кирәк.",
        "changeemail-no-info": "Бу сәхифәгә турыдан-туры мөрәҗәгать итәр өчен, сез системага керергә тиешсез",
        "changeemail-oldemail": "Хәзерге электрон әрҗә адресы:",
        "changeemail-newemail": "Яңа электрон почта адресы:",
        "undo-failure": "Аралыктагы үзгәртүләр туры килмәү сәбәпле, үзгәртүдән баш тартып булмый.",
        "undo-norev": "Үзгәртү юк яисә ул бетерелгән, шуңа аннан баш тартып булмый.",
        "undo-summary": "[[Special:Contributions/$2|$2]] кулланучысының ([[User talk:$2|бәхәс]]) $1 үзгәртүеннән баш тарту",
-       "cantcreateaccounttitle": "Хисап язмасын төзеп булмый",
        "cantcreateaccount-text": "Бу IP адресыннан (<b>$1</b>) хисап язмалары төзү тыела. Тыючы: [[User:$3|$3]].\n\n$3 күрсәткән сәбәп: ''$2''",
        "viewpagelogs": "Бу битнең көндәлекләрен карау",
        "nohistory": "Бу битнең үзгәртүләр тарихы юк.",
        "prefs-namespaces": "Исемнәр мәйданы",
        "default": "килешү буенча",
        "prefs-files": "Файллар",
-       "prefs-custom-css": "Үземнең CSS",
-       "prefs-custom-js": "Үземнең JS",
+       "prefs-custom-css": "Шәхси CSS",
+       "prefs-custom-js": "Шәхси JS",
        "prefs-common-css-js": "Барлык бизәлешләр өчен гомуми CSS/JS:",
        "prefs-reset-intro": "Бу бит сезнең көйләнмәләрегезне бетерү өчен кулланыла. Бу эшне башкару нәтиҗәсендә сез яңадан үз көйләнмәләрне яңадан кайтара алмыйсыз.",
        "prefs-emailconfirm-label": "E-mail раслау",
        "rightslog": "Кулланучының хокуклары көндәлеге",
        "action-read": "бу битне укырга",
        "action-edit": "бу битне үзгәртергә",
-       "action-createpage": "битләрне язырга",
-       "action-createtalk": "бәхәс битен ясарга",
+       "action-createpage": "бу битне төзү",
+       "action-createtalk": "бу бәхәс битен төзү",
        "action-move": "бу битне күчерерге",
        "action-sendemail": "электрон хат җибәрү",
        "nchanges": "$1 {{PLURAL:$1|үзгәртү}}",
index 757e97e..d2a098f 100644 (file)
        "databaseerror": "Медээ шыгжамыры алдаг",
        "laggedslavemode": "'''Оваарымчалыг:''' Бо арында чаартыышкыннар чок болуп болур.",
        "readonly": "шоочалаарынга медээ шыгжамыры",
-       "missing-article": "Данныйлар базазында тывар ужурлуг «$1» $2 деп арынның негеттинип турар сөзүглели тывылбаан.\n\nНургулайында ындыг байдал эрги шөлүг-биле казыттынган арынның өскерилге төөгүзүнче дамчып оралдажырга тыптыр.\n\nА шынында ындыг эвес болза, Силер программа хандырылгазының алдаанга душканыңар хөңнү.\n\nОоң дугайында кайы-бир [[Special:ListUsers/sysop|удуртукчуларга]], мүн URL-ин айытпышаан, дамчыдыңарам.",
+       "missing-article": "«$1» $2 деп арында дилеп турганың сөстер медээ үндезининден тывылбады.\n\nБаладып каапкан арынның төөгүзүн, эрги айтыг таварыштыр киргеш көөр болзуңза ындыг чүвеге таваржып болур сен.\n\nБир эвес хамыктың ужуру ындыг эвес боор болза, сен программаның шын эвес ажылдааныга душканыңар ол болур. \nОоң дугайында бээр [[Special:ListUsers/sysop|удуртукчуларга]] бижипкеш, шын эвес ажылдаан арынның URL адрезин база албан айып каар сен.",
        "missingarticle-rev": "(үндүрериниң саны: $1)",
        "missingarticle-diff": "(Ылгал: $1, $2)",
        "internalerror": "Иштики алдаг",
        "createaccountreason": "Чылдагаан:",
        "badretype": "Силерниң парлаан чажыт сөзүңер таарышпас.",
        "userexists": "Силерниң парлаан адыңар амгы үеде ажыглаттынып турар.\nӨске аттан шилип алыңар.",
-       "loginerror": "Ð\9aиÑ\80им Ð°Ð»Ð´Ð°Ð° (ЧазÑ\8bглÑ\8bг ÐºÐ¸Ñ\80им)",
+       "loginerror": "Ð\9aиÑ\80ип Ñ\88Ñ\8bдавадÑ\8bÒ£, Ñ\81ени Ñ\82анÑ\8bваан Ð±Ð¾Ð¾Ñ\80",
        "loginsuccesstitle": "Чедимчелиг кирери",
        "login-userblocked": "Бо ажыглакчы blocked.  Кирери хоржок.",
        "password-login-forbidden": "Бо ажыглакчының ады болгаш чажыт сөс хоржок.",
        "searchmenu-new": "Бо вики-төлевилелге «[[:$1]]» деп арынны кылыр.{{PLURAL:$2|0=|Ол ышкаш дилээшкиниңерниң түңнелинде тывылган арынны база көрүңер.|Ол ышкаш дилээшкиниңерниң түңнелдерин база көрүңер.}}",
        "searchprofile-articles": "үндезин арыннар",
        "searchprofile-images": "Мультимедиа",
-       "searchprofile-everything": "каяа-даа",
+       "searchprofile-everything": "Ð\9aаяа-даа",
        "searchprofile-advanced": "Делгереңгей",
        "searchprofile-articles-tooltip": "$1 иштинден дилээри",
        "searchprofile-images-tooltip": "Файлдар дилээри",
        "allmessages-language": "Дыл:",
        "allmessages-filter-submit": "Күүcедири",
        "thumbnail-more": "Улгаттыр",
-       "thumbnail_error": "Биче чурумал (миниатюра) чаяарының алдаа: $1",
+       "thumbnail_error": "Бичежек чурук болдунмады: $1",
        "import-comment": "Тайылбыр:",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|үндүрери}}",
        "tooltip-pt-userpage": "Силерниң ажыглакчы арыныңнар",
        "specialpages-group-pages": "Арыннарның даңзызы",
        "specialpages-group-pagetools": "Арын херекселдери",
        "blankpage": "Куруг арын",
-       "external_image_whitelist": " #Ð\91о Ð¾Ð´Ñ\83Ñ\80Ñ\83гнÑ\83 Ð¾Ð»-ла Ñ\85евÑ\8dÑ\8dÑ\80 Ð°Ñ\80Ñ\82Ñ\82Ñ\8bÑ\80Ñ\8bңаÑ\80<pre>\n#ТÑ\83Ñ\80Ñ\83м Ð¸Ð»ÐµÑ\80едиглеÑ\80 (Ñ\80егÑ\83лÑ\8fÑ\80нÑ\8bе Ð²Ñ\8bÑ\80ажениÑ\8f) Ñ\84Ñ\80агменÑ\82илеÑ\80ин Ð¼Ð°Ò£Ð°Ð° Ñ\81алÑ\8bңаÑ\80 (// Ð°Ñ\80азÑ\8bнга Ñ\82Ñ\83Ñ\80аÑ\80 ÐºÐµÐ·Ñ\8dÑ\8dн)\n#Ð\94аÑ\88Ñ\82Ñ\8bкÑ\8b Ñ\87Ñ\83Ñ\80Ñ\83малдаÑ\80 URL-биле Ð¾Ð»Ð°Ñ\80 Ñ\85олбааÑ\88кан Ð±Ð¾Ð»Ñ\83Ñ\80.\n#Ð\94Ñ\83жа Ð±ÐµÑ\80геннеÑ\80и Ñ\87Ñ\83Ñ\80Ñ\83малдаÑ\80 ÐºÑ\8bлдÑ\8bÑ\80 ÐºÓ©Ñ\81Ñ\82үп ÐºÐµÐ»Ð¸Ñ\80, Ð°Ñ\80Ñ\82каннаÑ\80Ñ\8b Ñ\87Ñ\83Ñ\80Ñ\83малдаÑ\80же Ñ\88өлүг ÐºÑ\8bлдÑ\8bÑ\80 ÐºÓ©Ñ\81Ñ\82Ò¯Ñ\80.\n# \"#\" Ð´ÐµÐ¿ Ð´ÐµÐ¼Ð´ÐµÐºÑ\82ен Ñ\8dгелÑ\8dÑ\8dн Ð¾Ð´Ñ\83Ñ\80Ñ\83глаÑ\80нÑ\8b Ñ\81аналдаÑ\80 ÐºÑ\8bлдÑ\8bÑ\80 Ð±Ð¸Ð»Ð´Ð¸Ð½ÐµÑ\80.\n#Ð\9eдÑ\83Ñ\80Ñ\83глаÑ\80 Ñ\80егиÑ\81Ñ\82Ñ\80ге ÐºÑ\83нÑ\83к Ñ\8dвеÑ\81 (билинмеÑ\81)\n\n#ТÑ\83Ñ\80Ñ\83м Ð¸Ð»ÐµÑ\80едиглеÑ\80 Ñ\84Ñ\80агменÑ\82илеÑ\80ин Ð±Ð¾ Ð¾Ð´Ñ\83Ñ\80Ñ\83гнÑ\83Ò£ ÐºÑ\8bÑ\80Ñ\8bнга Ñ\81алÑ\8bңаÑ\80. Ð\90 Ð±Ð¾ Ð¾Ð´Ñ\83Ñ\80Ñ\83гнÑ\83 Ð¾Ð»Ñ\87аан Ñ\85евÑ\8dÑ\8dÑ\80 Ð°Ñ\80Ñ\82тырыңар</pre>",
+       "external_image_whitelist": " #Ð\91о Ð¾Ð´Ñ\83Ñ\80Ñ\83гнÑ\83 Ð¾Ð»-ла Ñ\85евÑ\8dÑ\8dÑ\80 Ð°Ñ\80Ñ\82Ñ\8bÑ\80Ñ\8bңаÑ\80<pre>\n#ТÑ\83Ñ\80Ñ\83м Ð¸Ð»ÐµÑ\80едигниң (Ñ\80егÑ\83лÑ\8fÑ\80нÑ\8bе Ð²Ñ\8bÑ\80ажениÑ\8f) Ò¯Ð·Ò¯ÐºÑ\82еÑ\80ин Ð¼Ð°Ò£Ð°Ð° Ð±Ð¸Ð¶Ð¸Ð¿Ñ\82иңеÑ\80 (// Ð´ÐµÐ¿ Ð´ÐµÐ¼Ð´ÐµÐº Ð°Ñ\80азÑ\8bнда Ñ\82Ñ\83Ñ\80аÑ\80 ÐºÐµÐ·Ñ\8dÑ\8d) Ð´Ð°Ñ\80аазÑ\8bнда Ð±Ð¾Ð»Ð·Ð°\n#олаÑ\80 Ó©Ñ\81ке URL Ð°Ð´Ñ\80еÑ\81Ñ\82е Ñ\82Ñ\83Ñ\80аÑ\80 Ñ\87Ñ\83Ñ\80Ñ\83кÑ\82аÑ\80-биле Ñ\85аÑ\80Ñ\8bлзаа Ñ\82Ñ\83дÑ\83п Ñ\82Ñ\83Ñ\80аÑ\80.\n#ТааÑ\80Ñ\8bÑ\88кан Ñ\87Ñ\83Ñ\80Ñ\83кÑ\82аÑ\80, Ñ\87Ñ\83Ñ\80Ñ\83к Ð±Ð¾Ð¾Ð¿ Ð¾Ð»-ла Ñ\85евÑ\8dÑ\8dÑ\80 ÐºÓ©Ñ\81Ñ\82үп ÐºÐµÐ»Ð¸Ñ\80, Ð°Ñ\80Ñ\82аннаÑ\80Ñ\8b Ñ\87Ñ\83Ñ\80Ñ\83кÑ\82аÑ\80же Ð°Ð¹Ñ\8bÑ\82кан Ð°Ð¹Ñ\82Ñ\8bг Ð±Ð¾Ð¾Ð¿ Ð°Ñ\80Ñ\82Ñ\8bп ÐºÐ°Ð°Ñ\80.\n# \"#\" Ð´ÐµÐ¿ Ð´ÐµÐ¼Ð´ÐµÐºÑ\82ен Ñ\8dгелÑ\8dÑ\8dн Ð¾Ð´Ñ\83Ñ\80Ñ\83глаÑ\80 Ð±Ð¾Ð»Ð·Ð°, Ð±Ð¸Ð¶Ð¸ÐºÑ\82иң Ñ\82Ñ\83Ñ\81кай Ñ\82айÑ\8bлбÑ\8bÑ\80 Ñ\82Ñ\83Ñ\80аÑ\80 ÐºÐµÐ·Ñ\8dÑ\8d-диÑ\80.\n#Ð\9eдÑ\83Ñ\80Ñ\83гга Ñ\83лÑ\83г-даа, Ð±Ð¸Ñ\87ии-даа Ò¯Ð¶Ò¯ÐºÑ\82еÑ\80 Ð±Ð¸Ð¶Ð¸Ð¿ Ð±Ð¾Ð»Ñ\83Ñ\80.\n\n#ТÑ\83Ñ\80Ñ\83м Ð¸Ð»ÐµÑ\80едигниң ÐºÐµÐ·ÐµÐºÑ\82еÑ\80ин Ð±Ð¾ Ð¾Ð´Ñ\83Ñ\80Ñ\83гнÑ\83Ò£ ÐºÑ\8bÑ\80Ñ\8bнга Ð±Ð¸Ð¶Ð¸Ð¿ Ñ\81алÑ\8bÑ\80 Ñ\81илеÑ\80. Ð\91о Ð¾Ð´Ñ\83Ñ\80Ñ\83гнÑ\83 Ð¾Ð»Ñ\87аан, Ñ\85евÑ\8dÑ\8dÑ\80 Ð°Ñ\80тырыңар</pre>",
        "tag-filter": "[[Special:Tags|демдек]] шүүрү:",
        "tag-filter-submit": "Шүүрү",
        "tags-title": "Демдеглелдер",
index 79a6f94..402a034 100644 (file)
        "tagline": "Матеріал з {{grammar:genitive|{{SITENAME}}}}",
        "help": "Довідка",
        "search": "Пошук",
+       "search-ignored-headings": " #<!-- залиште цей рядок точно таким, яким він є --> <pre>\n# Заголовки, які будуть ігноруватися при пошуці.\n# Зміни, які набирають сили при індексуванні сторінки з заголовком.\n# Ви можете примусити переіндексувати сторінку з нульовим редагуванням.\n# Синтаксис наступний:\n#   * Усе, що починається з символу \"#\" до кінця рядка, є коментарем\n#   * Кожний непорожній рядок є точним заголовком для ігнорування\nПосилання\nЗовнішні посилання\nДив. також\n #</pre> <!-- залиште цей рядок точно таким, яким він є -->",
        "searchbutton": "Пошук",
        "go": "Перейти",
        "searcharticle": "Перейти",
        "yourpasswordagain": "Повторний набір пароля:",
        "createacct-yourpasswordagain": "Підтвердіть пароль",
        "createacct-yourpasswordagain-ph": "Введіть пароль знову",
-       "remembermypassword": "Запам'ятати мій обліковий запис на цьому комп'ютері (на строк не більше $1 {{PLURAL:$1|1=дня|днів}})",
        "userlogin-remembermypassword": "Запам'ятати мене",
        "userlogin-signwithsecure": "Захищене з'єднання",
        "cannotloginnow-title": "Неможливо увійти прямо зараз",
        "passwordreset-emailelement": "Ім'я користувача: \n$1\n\nТимчасовий пароль: \n$2",
        "passwordreset-emailsentemail": "Якщо ця електронна адреса асоційована з вашим обліковим записом, то лист для відновлення пароля буде відправлено на неї.",
        "passwordreset-emailsentusername": "Якщо існує електронна адреса, яка асоційована з цим обліковим записом, на неї буде надіслано лист для відновлення пароля.",
-       "passwordreset-emailsent-capture": "Електронний лист скидання пароля було надіслано, як показано нижче.",
-       "passwordreset-emailerror-capture": "Електронний лист для відновлення пароля мав бути надісланий, як показано нижче, але його надсилання {{GENDER:$2|користувачеві|користувачці}} $1 не вдалося.",
        "passwordreset-emailsent-capture2": "{{PLURAL:$1|Електронний лист|Електронні листи}} скидання паролю було надіслано. {{PLURAL:$1|Ім'я користувача і пароль|Список імен користувачів і паролів}} показано нижче.",
        "passwordreset-emailerror-capture2": "Не вдалося надіслати листа {{GENDER:$2|користувачу|користувачці}}: $1 {{PLURAL:$3|Ім'я користувача і пароль|список імен користувачів і паролів}} показано нижче.",
        "passwordreset-nocaller": "Має бути надане джерело виклику",
        "passwordreset-nodata": "Не надано ні імені користувача, ні електронної адреси",
        "changeemail": "Змінити або вилучити адресу електронної пошти",
        "changeemail-header": "Заповніть цю форму, щоб змінити адресу електронної пошти. Якщо Ви хочете взагалі прибрати зв'язок свого облікового запису з адресою електронної пошти, при надсиланні форми залиште поле нової електронної адреси порожнім.",
-       "changeemail-passwordrequired": "Вам потрібно буде ввести пароль, щоб підтвердити цю зміну.",
        "changeemail-no-info": "Ви повинні увійти в систему, щоб отримати безпосередній доступ до цієї сторінки.",
        "changeemail-oldemail": "Поточна адреса електронної пошти:",
        "changeemail-newemail": "Нова адреса електронної пошти:",
        "minoredit": "Незначна зміна",
        "watchthis": "Спостерігати за цією сторінкою",
        "savearticle": "Зберегти сторінку",
-       "publishpage": "Опублікувати сторінку",
+       "savechanges": "Зберегти зміни",
+       "publishpage": "Зберегти сторінку",
+       "publishchanges": "Зберегти зміни",
        "preview": "Попередній перегляд",
        "showpreview": "Попередній перегляд",
        "showdiff": "Показати зміни",
        "userpage-userdoesnotexist": "Користувач під назвою \"<nowiki>$1</nowiki>\" не зареєстрований. Переконайтеся, що ви хочете створити/редагувати цю сторінку.",
        "userpage-userdoesnotexist-view": "Обліковий запис користувача «$1» не зареєстровано.",
        "blocked-notice-logextract": "Цей користувач наразі заблокований.\nОстанній запис у журналі блокувань такий:",
-       "clearyourcache": "<strong>Увага:</strong> Після збереження слід очистити кеш оглядача, щоб побачити зміни.\n* <strong>Firefox / Safari:</strong> тримайте <em>Shift</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em> чи <em>Ctrl-Shift-R</em> (<em>⌘-R</em> на Apple Mac)\n* <strong>Google Chrome:</strong> натисніть <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Apple Mac)\n* <strong>Internet Explorer:</strong> тримайте <em>Ctrl</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em>\n* <strong>Opera:</strong> очистіть кеш за допомогою <em>Інструменти → Налаштування</em> (<em>Opera → Побажання</em> на Apple Mac) та перейдіть на <em>Привітність & безпека → очистити дані браузера → кеш</em>",
+       "clearyourcache": "<strong>Увага:</strong> Після збереження слід очистити кеш оглядача, щоб побачити зміни.\n* <strong>Firefox / Safari:</strong> тримайте <em>Shift</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em> чи <em>Ctrl-Shift-R</em> (<em>⌘-R</em> на Apple Mac)\n* <strong>Google Chrome:</strong> натисніть <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Apple Mac)\n* <strong>Internet Explorer:</strong> тримайте <em>Ctrl</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em>\n* <strong>Opera:</strong> очистіть кеш за допомогою <em>Інструменти → Налаштування</em> (<em>Opera → Побажання</em> на Apple Mac) та перейдіть на <em>Приватність & безпека → очистити дані браузера → кеш</em>",
        "usercssyoucanpreview": "'''Підказка:''' використовуйте кнопку «{{int:showpreview}}», щоб протестувати ваш новий css-файл перед збереженням.",
        "userjsyoucanpreview": "'''Підказка:''' використовуйте кнопку «{{int:showpreview}}», щоб протестувати ваш новий код JavaScript перед збереженням.",
        "usercsspreview": "'''Пам'ятайте, що це лише попередній перегляд вашого css-файлу.'''\n'''Його ще не збережено!'''",
        "content-model-css": "CSS",
        "content-json-empty-object": "Порожній об'єкт",
        "content-json-empty-array": "Порожній масив",
+       "deprecated-self-close-category": "Сторінки, що використовують недійсні самозакривні теги HTML",
+       "deprecated-self-close-category-desc": "Сторінка містить недійсні самозакривні теги HTML, наприклад, <code>&lt;b/></code> чи <code>&lt;span/></code>.  Їхню поведінку невдовзі буде змінено у відповідності зі специфікацією HTML5, тому їх використання у вікітексті є застарілим.",
        "duplicate-args-warning": "<strong>Увага:</strong> [[:$1]] викликає [[:$2]] з більш ніж одним значенням параметра «$3». Буде використано лише останнє вказане значення.",
        "duplicate-args-category": "Сторінки, що містять шаблон із кількома значеннями одного й того ж параметра",
        "duplicate-args-category-desc": "Тут перелічено сторінки, що містять дублікатне визначення аргументу при включенні шаблону, приміром, <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> або <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Схоже, редагування вже було скасовано.",
        "undo-summary": "Скасування редагування № $1 користувача [[Special:Contribs/$2|$2]] ([[User talk:$2|обговорення]])",
        "undo-summary-username-hidden": "Скасувати версію $1, виконану прихованим користувачем",
-       "cantcreateaccounttitle": "Неможливо створити обліковий запис",
        "cantcreateaccount-text": "Створення облікових записів із цієї IP-адреси ('''$1''') було заблоковане [[User:$3|користувачем $3]].\n\n$3 зазначив таку причину: ''$2''",
        "cantcreateaccount-range-text": "Створення облікового запису із IP-адрес у діапазоні  <strong>$1</strong>, який включає вашу IP-адресу (<strong>$4</strong>), було заблоковано користувачем [[User:$3|$3]].\n\nКористувач $3 вказав як причину <em>$2</em>",
        "viewpagelogs": "Показати журнали для цієї сторінки",
        "grant-group-high-volume": "Виконувати великий обсяг діяльності",
        "grant-group-customization": "Налаштування і переваги",
        "grant-group-administration": "Виконувати адміністративні дії",
+       "grant-group-private-information": "Доступ до приватних даних про Вас",
        "grant-group-other": "Різна діяльність",
        "grant-blockusers": "Блокувати і розблокувати користувачів",
        "grant-createaccount": "Створювати облікові записи",
        "grant-highvolume": "Редагування у великих обсягах",
        "grant-oversight": "Приховувати користувачів і версії",
        "grant-patrol": "Патрулювати зміни на сторінках",
+       "grant-privateinfo": "Доступ до приватних даних",
        "grant-protect": "Захищати і знімати захист сторінок",
        "grant-rollback": "Відкочувати зміни на сторінках",
        "grant-sendemail": "Надсилати пошту іншим користувачам",
        "action-applychangetags": "додавання міток разом зі змінами",
        "action-changetags": "додавання або вилучення будь-яких міток для певних версій сторінок або записів журналів",
        "action-deletechangetags": "вилучення міток з бази даних",
+       "action-purge": "очищення кешу цієї сторінки",
        "nchanges": "$1 {{PLURAL:$1|зміна|зміни|змін}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|зміна з останнього візиту|зміни з останнього візиту|змін з останнього візиту}}",
        "enhancedrc-history": "історія",
        "uploadstash-errclear": "Очищення файлів зазнало невдачі.",
        "uploadstash-refresh": "Оновити список файлів",
        "uploadstash-thumbnail": "перегляд мініатюри",
+       "uploadstash-exception": "Не вдалося зберегти завантаження у сховку ($1): «$2».",
        "invalid-chunk-offset": "Неприпустимий зсув фрагмента",
        "img-auth-accessdenied": "Відмовлено в доступі",
        "img-auth-nopathinfo": "Відсутній PATH_INFO.\nВаш сервер не налаштовано для передачі цих даних.\nМожливо, він працює на основі CGI та не підтримує img_auth.\nПерегляньте [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization Відкриття доступу до зображень]",
        "filerevert-submit": "Повернути",
        "filerevert-success": "'''[[Media:$1|$1]]''' був повернутий до [$4 версії від $3, $2].",
        "filerevert-badversion": "Немає локальної версії цього файлу з вказаною поміткою дати і часу.",
+       "filerevert-identical": "Поточна версія файлу вже ідентична обраній.",
        "filedelete": "Вилучення $1",
        "filedelete-legend": "Вилучити файл",
        "filedelete-intro": "Ви збираєтесь вилучити '''[[Media:$1|$1]]''' і всю його історію.",
        "booksources-text": "На цій сторінці наведено список посилань на сайти, де ви, можливо, знайдете додаткову інформацію про книгу. Це інтернет-магазини й системи пошуку в бібліотечних каталогах.",
        "booksources-invalid-isbn": "Вказаний номер ISBN, судячи з усього, містить помилку. Будь ласка, перевірте, що при перенесенні номера з першоджерела не виникло спотворень.",
        "specialloguserlabel": "Виконавець:",
-       "speciallogtitlelabel": "Ціль (назва сторінки або {{ns:user}}:ім'я користувача):",
+       "speciallogtitlelabel": "Ціль (назва сторінки або {{ns:user}}:Ім'я_користувача):",
        "log": "Журнали",
        "logeventslist-submit": "Показати",
        "all-logs-page": "Усі публічні журнали",
        "listgrants-summary": "Нижче наведено список дозволів, з їхнім пов'язаним доступом до прав користувача. Користувачі можуть дозволити програмам використовувати свій обліковий запис, але з обмеженими правами на основі грантів користувача наданих до заяви. Програма діє від імені користувача, однак насправді не може використовувати права, які користувач не має.\nМоже бути [[{{MediaWiki:Listgrouprights-helppage}}|додаткова інформація]] про індивідуальні права.",
        "listgrants-grant": "Грант",
        "listgrants-rights": "Права",
-       "trackingcategories": "Ð\92Ñ\96дÑ\81Ñ\82ежÑ\83ванÑ\96 ÐºÐ°Ñ\82егоÑ\80Ñ\96Ñ\97",
+       "trackingcategories": "Ð\9aаÑ\82егоÑ\80Ñ\96Ñ\97 Ñ\81поÑ\81Ñ\82еÑ\80еженнÑ\8f",
        "trackingcategories-summary": "На цій сторінці перераховані відстежують категорії, які заповнюються автоматично програмним забезпеченням MediaWiki. Їх можна перейменувати, змінивши відповідні системні повідомлення в просторі назв {{ns:8}}.",
        "trackingcategories-msg": "Відстежувана категорія",
        "trackingcategories-name": "Ім'я повідомлення",
        "watchnologin": "Ви не увійшли до системи",
        "addwatch": "Додати до списку спостереження",
        "addedwatchtext": "«[[:$1]]» та її сторінка обговорення додані до вашого [[Special:Watchlist|списку спостереження]].",
+       "addedwatchtext-talk": "«[[:$1]]» та пов'язану з нею сторінку додано до Вашого [[Special:Watchlist|списку спостереження]].",
        "addedwatchtext-short": "Сторінка \" $1 \" була додана до вашого списку спостереження.",
        "removewatch": "Видалити зі списку спостереження",
        "removedwatchtext": "«[[:$1]]» та її сторінка обговорення вилучені з Вашого [[Special:Watchlist|списку спостереження]].",
+       "removedwatchtext-talk": "«[[:$1]]» та пов'язану з нею сторінку вилучено з Вашого [[Special:Watchlist|списку спостереження]].",
        "removedwatchtext-short": "Сторінка \"$1\" була видалена із вашого списку спостереження.",
        "watch": "Спостерігати",
        "watchthispage": "Спостерігати за цією сторінкою",
        "rollbacklinkcount-morethan": "відкинути понад $1 {{PLURAL:$1|редагування|редагування|редагувань}}",
        "rollbackfailed": "Відкинути зміни не вдалося",
        "rollback-missingparam": "Відсутні обов'язкові параметри за запитом.",
+       "rollback-missingrevision": "Не вдалося завантажити дані версії.",
        "cantrollback": "Неможливо відкинути редагування, оскільки останній дописувач сторінки є її автором.",
        "alreadyrolled": "Неможливо відкинути останні редагування [[:$1]], зроблені [[User:$2|$2]] ([[User talk:$2|обговорення]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]), оскільки хтось інший уже змінив чи відкинув редагування цієї статті.\n\nОстанні редагування зроблено [[User:$3|$3]] ([[User talk:$3|обговорення]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Пояснення редагування було: «<em>$1</em>.».",
        "undeletehistorynoadmin": "Стаття вилучена. Причина вилучення та список користувачів, що редагували статтю до вилучення, вказані нижче. Текст вилученої статті можуть переглянути лише адміністратори.",
        "undelete-revision": "Вилучена версія $1 (від $4 $5) користувача $3:",
        "undeleterevision-missing": "Невірна версія. Помилкове посилання, або вказану версію сторінки вилучено з архіву.",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|Одну версію|$1 версії|$1 версій}} не вдалося відновити, оскільки {{PLURAL:$1|її|їхній}} <code>rev_id</code> вже використовується.",
        "undelete-nodiff": "Не знайдена попередня версія.",
        "undeletebtn": "Відновити",
        "undeletelink": "переглянути/відновити",
        "undeletedrevisions": "$1 {{PLURAL:$1|редагування|редагування|редагувань}} відновлено",
        "undeletedrevisions-files": "$1 {{PLURAL:$1|версія|версії|версій}} та $2 {{PLURAL:$2|файл|файли|файлів}} відновлено",
        "undeletedfiles": "$1 {{PLURAL:$1|файл|файли|файлів}} відновлено",
-       "cannotundelete": "Ð\9fомилка Ð²Ñ\96дновленнÑ\8f:\n$1",
+       "cannotundelete": "ЧаÑ\81Ñ\82ина Ð°Ð±Ð¾ Ð²Ñ\81Ñ\8f Ð¿Ñ\80оÑ\86едÑ\83Ñ\80а Ð²Ñ\96дновленнÑ\8f Ð·Ð°Ð·Ð½Ð°Ð»Ð° Ð½ÐµÐ²Ð´Ð°Ñ\87Ñ\96:\n$1",
        "undeletedpage": "'''Сторінка «$1» відновлена'''\n\nДив. [[Special:Log/delete|список вилучень]], щоб дізнатися про останні вилучення та відновлення.",
        "undelete-header": "Список нещодавно вилучених сторінок можна переглянути в [[Special:Log/delete|журналі вилучень]].",
        "undelete-search-title": "Пошук видалених сторінок",
        "sp-contributions-newbies-sub": "Внесок новачків",
        "sp-contributions-newbies-title": "Внесок з нових облікових записів",
        "sp-contributions-blocklog": "журнал блокувань",
-       "sp-contributions-suppresslog": "вилÑ\83Ñ\87ений Ð²Ð½ÐµÑ\81ок ÐºÐ¾Ñ\80иÑ\81Ñ\82Ñ\83ваÑ\87а",
-       "sp-contributions-deleted": "вилучені редагування користувача",
+       "sp-contributions-suppresslog": "пÑ\80иÑ\85ований Ð²Ð½ÐµÑ\81ок {{GENDER:$1|коÑ\80иÑ\81Ñ\82Ñ\83ваÑ\87а|коÑ\80иÑ\81Ñ\82Ñ\83ваÑ\87ки}}",
+       "sp-contributions-deleted": "вилучені редагування {{GENDER:$1|користувача|користувачки}}",
        "sp-contributions-uploads": "завантаження",
        "sp-contributions-logs": "журнали",
        "sp-contributions-talk": "обговорення",
        "unblock": "Розблокувати користувача",
        "blockip": "Заблокувати {{GENDER:$1|користувача|користувачку}}",
        "blockip-legend": "Блокування користувача",
-       "blockiptext": "Використовуйте форму нижче, щоб заблокувати можливість редагування зазначеній IP-адресі або користувачу.\nЦе слід робити лише для запобігання порушенням і у відповідності до [[{{MediaWiki:Policy-url}}|правил]].\nОбов'язково заповніть причину нижче, бажано дати інформативну вичерпну інформацію (наприклад, послатися на конкретні правила, дати посилання на редагування користувача, які призвели до блокування). Можна конкретизувати причину блокування на сторінці обговорення користувача.\nВи можете заблокувати діапазони IP-адрес, використовуючи [https://uk.wikipedia.org/wiki/CIDR]-синтаксис. Максимально допустимий діапазон — /$1 для протоколу IPv4 та /$2 для протоколу IPv6.",
+       "blockiptext": "Використовуйте форму нижче, щоб заблокувати можливість редагування зазначеній IP-адресі або користувачу.\nЦе слід робити лише для запобігання порушенням і у відповідності до [[{{MediaWiki:Policy-url}}|правил]].\nОбов'язково заповніть причину нижче, бажано дати інформативну вичерпну інформацію (наприклад, послатися на конкретні правила, дати посилання на редагування користувача, які призвели до блокування). Можна конкретизувати причину блокування на сторінці обговорення користувача.\nВи можете заблокувати діапазони IP-адрес, використовуючи [https://uk.wikipedia.org/wiki/CIDR CIDR]-синтаксис. Максимально допустимий діапазон — /$1 для протоколу IPv4 та /$2 для протоколу IPv6.",
        "ipaddressorusername": "IP-адреса або ім'я користувача:",
        "ipbexpiry": "Термін:",
        "ipbreason": "Причина:",
        "unblockip": "Розблокувати IP-адресу",
        "unblockiptext": "Використовуйте подану нижче форму, щоб відновити можливість збереження з раніше заблокованої IP-адреси.",
        "ipusubmit": "Зняти це блокування",
-       "unblocked": "[[User:$1|$1]] {{GENDER:$1|розблокований|розблокована}}",
-       "unblocked-range": "$1 розблоковано",
-       "unblocked-id": "Блокування $1 було зняте",
-       "unblocked-ip": "[[Special:Contributions/$1|$1]] вже розлоковано.",
+       "unblocked": "[[User:$1|$1]] {{GENDER:$1|розблокований|розблокована}}.",
+       "unblocked-range": "$1 розблоковано.",
+       "unblocked-id": "Блокування $1 було зняте.",
+       "unblocked-ip": "[[Special:Contributions/$1|$1]] розблоковано.",
        "blocklist": "Заблоковані користувачі",
        "ipblocklist": "Список заблокованих адрес та користувачів",
        "ipblocklist-legend": "Пошук заблокованого користувача",
        "logentry-delete-delete": "$1 {{GENDER:$2|вилучив|вилучила}} сторінку $3",
        "logentry-delete-restore": "$1 {{GENDER:$2|відновив|відновила}} сторінку $3",
        "logentry-delete-event": "$1 {{GENDER:$2|змінив|змінила}} видимість {{PLURAL:$5 запису журналу|$5 записів журналу}} на $3: $4",
-       "logentry-delete-revision": "$1 {{GENDER:$2|змінив|змінила}} видимість {{PLURAL:$5 версії|$5 версій}} на сторінці $3: $4",
+       "logentry-delete-revision": "$1 {{GENDER:$2|змінив|змінила}} видимість {{PLURAL:$5|$5 версії|$5 версій}} на сторінці $3: $4",
        "logentry-delete-event-legacy": "$1 {{GENDER:$2|змінив|змінила}} видимість записів журналу подій $3",
        "logentry-delete-revision-legacy": "$1 {{GENDER:$2|змінив|змінила}} видимість версій на сторінці $3",
        "logentry-suppress-delete": "$1 {{GENDER:$2|подавив|подавила}} сторінку $3",
        "mw-widgets-dateinput-placeholder-month": "РРРР-ММ",
        "mw-widgets-titleinput-description-new-page": "сторінка ще не існує",
        "mw-widgets-titleinput-description-redirect": "перенаправлення на $1",
-       "api-error-blacklisted": "Будь ласка, виберіть іншу, більш зрозумілу назву.",
        "sessionmanager-tie": "Не можна поєднувати кілька типів автентифікації запиту: $1.",
        "sessionprovider-generic": "сесій $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "сесій на основі кук",
        "log-action-filter-newusers": "Тип створення облікового запису:",
        "log-action-filter-patrol": "Тип патрулювання:",
        "log-action-filter-protect": "Тип захисту:",
-       "log-action-filter-rights": "Тип зміни прав",
-       "log-action-filter-suppress": "Тип приховування",
+       "log-action-filter-rights": "Тип зміни прав:",
+       "log-action-filter-suppress": "Тип приховування:",
        "log-action-filter-upload": "Тип завантаження:",
        "log-action-filter-all": "Усі",
        "log-action-filter-block-block": "Блокування",
        "linkaccounts-submit": "Пов'язати облікові записи",
        "unlinkaccounts": "Відв'язати облікові записи",
        "unlinkaccounts-success": "Обліковий запис було відв'язано.",
-       "authenticationdatachange-ignored": "Неопрацьована зміна облікових даних. Можливо, жоден з провайдерів не був налаштований?"
+       "authenticationdatachange-ignored": "Неопрацьована зміна облікових даних. Можливо, жоден з провайдерів не був налаштований?",
+       "userjsispublic": "Будь ласка, зверніть увагу: підсторінки JavaScript не повинні містити конфіденційних даних, бо їх можуть бачити інші користувачі.",
+       "usercssispublic": "Будь ласка, зверніть увагу: підсторінки CSS не повинні містити конфіденційних даних, бо їх можуть бачити інші користувачі."
 }
index 1ccc6f6..ec98058 100644 (file)
@@ -35,8 +35,9 @@
        "tog-hideminor": "حالیہ تبدیلیوں میں معمولی ترامیم چھپائیں",
        "tog-hidepatrolled": "حالیہ تبدیلیوں میں گشتی ترامیم چھپائیں",
        "tog-newpageshidepatrolled": "جدید صفحات کی فہرست میں مراجعت شدہ صفحات چھپائیں",
-       "tog-extendwatchlist": "حالیہ ترین تبدیلیوں کے بجائے جملہ تبدیلیاں دیکھنے کے لیے زیر نظر فہرست کی توسیع کریں",
-       "tog-usenewrc": "حالیہ تبدیلیاں اور زیر نظر فہرست میں تبدیلیوں کو بلحاظ صفحہ گروہ بند کیجئے",
+       "tog-hidecategorization": "صفحات کی زمرہ بندی چھپائیں",
+       "tog-extendwatchlist": "حالیہ ترین تبدیلیوں کی بجائے تمام تبدیلیاں دیکھنے کے لیے زیر نظر فہرست کو وسیع کریں",
+       "tog-usenewrc": "حالیہ تبدیلیاں اور زیر نظر فہرست میں تبدیلیوں کو بلحاظ صفحہ گروہ بند کریں",
        "tog-numberheadings": "سرخیوں کو خودکار نمبر دیں",
        "tog-showtoolbar": "آلات ترمیم دکھائیں",
        "tog-editondblclick": "دو کلک پر صفحات کی ترمیم کریں",
@@ -45,6 +46,7 @@
        "tog-watchdefault": "میرے ترمیم شدہ صفحات اور فائلوں کو میری زیر نظر فہرست میں شامل کریں",
        "tog-watchmoves": "میرے منتقل کردہ صفحات اور فائلوں کو میری زیر نظر فہرست میں شامل کریں",
        "tog-watchdeletion": "میرے حذف کردہ صفحات اور فائلوں کو میری زیر نظر فہرست میں شامل کریں",
+       "tog-watchuploads": "میری اپلوڈ کردہ نئی فائلوں کو زیر نظر فہرست میں شامل کریں",
        "tog-watchrollback": "میرے استرجع کردہ صفحات کو میری زیر نظر فہرست میں شامل کریں",
        "tog-minordefault": "ہمیشہ میری تمام ترامیم کو معمولی ترمیم کے طور پر نشان زد کریں",
        "tog-previewontop": "خانہ ترمیم سے پہلے نمائش دکھائیں",
        "tog-watchlisthidebots": "زیرِنظر فہرست سے روبہ جاتی ترامیم چھپائیں",
        "tog-watchlisthideminor": "زیرِنظر فہرست سے معمولی ترامیم چھپائیں",
        "tog-watchlisthideliu": "زیرِنظر فہرست سے داخلِ نوشتہ شدہ صارفین کی ترامیم چھپائیں",
+       "tog-watchlistreloadautomatically": "کسی مقطار میں تبدیلی کے بعد زیر نظر فہرست کو خودکار طور پر تازہ کریں (جاوا اسکرپٹ درکار)",
        "tog-watchlisthideanons": "زیرِنظر فہرست سے نامعلوم صارفین کی ترامیم چھپائیں",
        "tog-watchlisthidepatrolled": "زیرِنظر فہرست سے مراجع شدہ ترامیم چھپائیں",
-       "tog-ccmeonemails": "دیگر صارفین کو ارسال کردہ برقی خطوط کی نقول مجھے بھی ارسال کریں۔",
-       "tog-diffonly": "فرق کے نیچے صفحے کے مشمولات نہ دکھائیں",
+       "tog-watchlisthidecategorization": "صفحات کی زمرہ بندی چھپائیں",
+       "tog-ccmeonemails": "دیگر صارفین کو ارسال کردہ برقی خطوط کی نقل مجھے بھی ارسال کریں۔",
+       "tog-diffonly": "فرق کے نیچے صفحے کے مندرجات نہ دکھائیں",
        "tog-showhiddencats": "پوشیدہ زمرہ جات دکھائیں",
-       "tog-norollbackdiff": "استرجع Ú©Û\8c Ø§Ù\86جاÙ\85 Ø¯Û\81Û\8c Ú©Û\92 Ø¨Ø¹Ø¯ Ù\81رÙ\82 ØªØ±Ú© Ú©Ø±یں",
+       "tog-norollbackdiff": "استرجع Ú©Û\92 Ø¨Ø¹Ø¯ Ù\81رÙ\82 Ù\86Û\81 Ø¯Ú©Ú¾Ø§Ø¦یں",
        "tog-useeditwarning": "غیر محفوظ تبدیلیاں چھوڑنے پر مجھے آگاہ کریں",
        "tog-prefershttps": "لاگ ان رہنے کے دوران ہمیشہ محفوظ کنیکشن استعمال کریں",
        "underline-always": "ہمیشہ",
        "underline-never": "کبھی نہیں",
-       "underline-default": "جلد یا براؤزر کا ڈیفالٹ",
+       "underline-default": "پوشاک یا براؤزر کا طے شدہ",
        "editfont-style": "خانۂ ترمیم کا فانٹ:",
-       "editfont-default": "براؤزر کا ڈیفالٹ",
+       "editfont-default": "براؤزر کا طے شدہ",
        "editfont-monospace": "مونوسپیسڈ فونٹ",
        "editfont-sansserif": "سنس سیرف فونٹ",
        "editfont-serif": "سیرف فونٹ",
        "april-gen": "اپريل",
        "may-gen": "مئی",
        "june-gen": "جون",
-       "july-gen": "جولائ",
+       "july-gen": "جولائی",
        "august-gen": "اگست",
        "september-gen": "ستمبر",
        "october-gen": "اکتوبر",
        "generic-pool-error": "ہم معذرت خواہ ہیں! معیلات (سرورز) پر اِس وقت اِضافی بوجھ ہے.\nصارفین کی کثیر تعداد اِس وقت یہی صفحہ ملاحظہ کرنے کی کوشش کررہی ہے.\nبرائے مہربانی!دوبارہ کوشش کرنے سے پہلے ذرا انتظار فرمائیے.",
        "pool-errorunknown": "نامعلوم خطا",
        "poolcounter-usage-error": "استعمال میں خامی: $1",
-       "aboutsite": "تعارف {{SITENAME}}",
+       "aboutsite": "{{SITENAME}} کا تعارف",
        "aboutpage": "Project:تعارف",
        "copyright": "تمام مواد $1 کے تحت میسر ہے، جب تک کوئی دوسری وجہ نا ہو۔",
        "copyrightpage": "{{ns:project}}:حقوق تصانیف",
        "policy-url": "Project:حکمتِ عملی",
        "portal": "دیوان عام",
        "portal-url": "Project:دیوان عام",
-       "privacy": "اصÙ\88Ù\84 Ø¨Ø±Ø§Û\93 Ø§Ø®Ù\81ائÛ\92 Ø±Ø§Ø²",
+       "privacy": "اخÙ\81ائÛ\92 Ø±Ø§Ø² Ú©Û\92 Ø§ØµÙ\88Ù\84",
        "privacypage": "Project:اصولِ اخفائے راز",
        "badaccess": "خطائے اجازت",
        "badaccess-group0": "آپ متمنی عمل کا اجراء کرنے کے مُجاز نہیں۔",
        "nosuchaction": "کوئی سا عمل نہیں",
        "nosuchactiontext": "URL کی جانب سے مختص کیا گیا عمل درست نہیں.\nآپ نے شاید URL غلط لکھا، یا کسی غیر صحیح ربط کی پیروی کی ہے.\n{{اِس سے SITENAME کے زیرِ استعمال مصنع لطیف میں کھٹمل کی نشاندہی کا بھی اندیشہ ہے}}.",
        "nosuchspecialpage": "کوئی ایسا خاص صفحہ نہیں",
-       "nospecialpagetext": "<strong>آپ نے ایک ناقص خاص صفحہ کی درخواست کی ہے.</strong>\n\n{{درست خاص صفحات کی ایک فہرست [[Special:SpecialPages|{{int:specialpages}}]] پر دیکھی جاسکتی ہے}}.",
+       "nospecialpagetext": "<strong>آپ نے ایک غیر موجود خصوصی صفحہ کی درخواست کی ہے۔</strong>\n\nدرست خاص صفحات کی ایک فہرست [[Special:SpecialPages|{{int:specialpages}}]] پر دیکھی جاسکتی ہے۔",
        "error": "خطاء",
        "databaseerror": "خطائے ڈیٹابیس",
        "databaseerror-text": "ڈیٹا بیس کیوری میں خامی پیدا ہوگئی ہے.\nیہ سافٹ ویئر میں ایک مسئلے (بگ) کی نشاندہی کر سکتے ہیں.",
        "protectedpagetext": "اس صفحہ کو تدوین سے محفوظ رکھنے کیلیے مقفل کر دیا گیا ہے۔",
        "viewsourcetext": "آپ صرف مسودہ دیکھ سکتے ہیں اور اسکی نقل اتار سکتے ہیں۔",
        "viewyourtext": "آپ اس مواد کو دیکھ سکتے ہیں اور اٹھا (کاپی) سکتے ہیں <strong>آپ کی ترامیم</strong> اس صفحہ پر۔",
-       "protectedinterface": "یہ صفحہ سوفٹ وئیر کے لیے انٹرفیس متن فراہم کرتا ہے، اور ناجائزاستعمال کے سدِباب کے لیے اِسے مقفل کیا گیا ہے.",
+       "protectedinterface": "{{fmbox \n| type = editnotice\n| image = [[File:Namespace MediaWiki.svg|45px]]\n| text = '''اس [[mw:Manual:System message|انٹرفیس پیغام]] یا [[ویکیپیڈیا:جلد|جلد]] کی دستاویز [[mw:Manual:Interface/{{PAGENAME}}|میڈیاویکی ڈاٹ آرگ]] یا [[translatewiki:MediaWiki:{{PAGENAME}}/qqq|ٹرانسلیٹ ویکی ڈاٹ نیٹ]] پر بنائی جا سکتی ہے۔''' <br /> چونکہ یہ صفحہ میڈیاویکی سوفٹویئر کے لیے انٹرفیس متن فراہم کرتا ہے، اس لیے اس میں محض [[ویکیپیڈیا:منتظمین|منتظمین]] ہی ترمیم کر سکتے ہیں۔ البتہ اگر آپ کو اس میں کسی قسم کی ترمیم مطلوب ہو تو ذیل میں موجود بٹن پر کلک کرکے اس پیغام میں ترمیم کی درخواست کر سکتے ہیں۔\n<div class=\"center\" style=\"margin-top: 4px;\">{{درخواست ترمیم روانہ کریں|type=full}}</div>\n}}{{editnotice load\n| notice action = view \n}}",
        "editinginterface": "<strong>انتباہ: </strong> آپ ایک ایسے صفحے میں ترمیم کر رہے ہیں جو سوفٹ ویئر کے لیے انٹرفیس متن فراہم کرتا ہے۔ اس صفحہ میں کی جانے والی تبدیلی سے اس ویکی پر دیگر صارفین کے لیے انٹرفیس متاثر ہوگی۔",
        "translateinterface": "تمام ویکیوں میں تبدیلی یا شامل کرنے کے لیے، [https://translatewiki.net/ translatewiki.net]کو استعمال کریں ، میڈیا ویکی دارالترجمہ.",
        "namespaceprotected": "آپ کو '''$1''' فضائے نام میں صفحات تدوین کرنے کی اِجازت نہیں ہے.",
        "userlogin-yourname": "صارف نام",
        "userlogin-yourname-ph": "اپنا صارف نام درج کریں",
        "createacct-another-username-ph": "صارف نام درج کریں",
-       "yourpassword": "کلمۂ شناخت",
+       "yourpassword": "پاس ورڈ:",
        "userlogin-yourpassword": "کلمۂ شناخت",
        "userlogin-yourpassword-ph": "اپنا کلمہ شناخت دیں",
        "createacct-yourpassword-ph": "ایک پاس ورڈ داخل کریں",
        "yourpasswordagain": "کلمۂ شناخت دوبارہ لکھیں",
        "createacct-yourpasswordagain": "کلمۂ اجازت تصدیق کریں",
        "createacct-yourpasswordagain-ph": "پاس ورڈ پھر داخل کریں",
-       "remembermypassword": "اِس متصفح پر میرے داخلِ نوشتگی معلومات یاد رکھو (زیادہ سے زیادہ $1 {{PLURAL:$1|دِن|ایام}} کیلئے)",
        "userlogin-remembermypassword": "مجھے داخل رکھے",
        "userlogin-signwithsecure": "محفوظ رابطہ (کنکشن) استعمال کریں",
        "yourdomainname": "آپکا ڈومین",
        "nologin": "کیا آپ نے کھاتہ نہیں بنایا ہوا؟ '''$1'''۔",
        "nologinlink": "کھاتا بنائیں",
        "createaccount": "کھاتہ کھولیں",
-       "gotaccount": "پہلے سے کھاتہ بنا ہوا ہے? '''$1'''.",
+       "gotaccount": "پہلے سے کھاتہ بنا ہوا ہے؟ '''$1'''۔",
        "gotaccountlink": "داخل ہوجائیے",
        "userlogin-resetlink": "داخلِ نوشتہ ہونے کی تفاصیل بھول گئے ہیں؟",
        "userlogin-resetpassword-link": "کلمہ شناخت بھول گئے؟",
        "throttled-mailpassword": "گزشتہ {{PLURAL:$1|گھنٹے|$1 گھنٹوں}} کے دوران پہلے سے ہی پارلفظ (پاسورڈ) کی تبدیلی کے لیے برقی خط بھیجا گيا ہے۔\nناجائز استعمال کے سدّباب کیلئے، {{PLURAL:$1|گھنٹہ|$1 گھنٹوں}} کے دوران صرف ایک برقی خط بھیجا جاسکتا ہے۔",
        "mailerror": "مسلہ دوران ترسیل خط:$1",
        "acct_creation_throttle_hit": "آپکی آئی.پی کے ذریعے اِس ویکی پر آنے والے صارفین نے پچھلے ایک دِن میں {{PLURAL:$1|1 کھاتہ بنایا ہے|$1 کھاتے بنائے ہیں}}، جو کہ مذکورہ وقت میں کافی ہیں.\nلہٰذا، آپکی آئی.پی استعمال کرنے والے صارفین اِس وقت مزید کھاتے نہیں بناسکتے.",
-       "emailauthenticated": "آپکے برقی ڈاک پتہ کی تصدیق تاریخ $2 بوقت $3 بجے کو ہوئی۔",
+       "emailauthenticated": "آپ کے برقی ڈاک پتہ کی تصدیق مورخہ $2 بوقت $3 بجے ہوئی۔",
        "emailnotauthenticated": "آپ کے برقی پتہ کی ابھی تصدیق نہیں ہوئی ہے۔\nدرج ذیل میں سے کسی بھی چیز کیلئے آپ کے برقی پتہ پر برقی ڈاک ارسال نہیں کی جائے گی۔",
        "noemailprefs": "اِن خصائص کو کام میں لانے کیلئے اپنے ترجیحات میں برقی ڈاک کا پتہ متعین کیجئے.",
        "emailconfirmlink": "اپنے برقی پتہ کی تصدیق کیجئے",
        "minoredit": "معمولی ترمیم",
        "watchthis": "یہ صفحہ زیر نظر کیجیۓ",
        "savearticle": "محفوظ",
+       "savechanges": "تبدیلیاں محفوظ کریں",
+       "publishpage": "شائع کریں",
+       "publishchanges": "تبدیلیاں شائع کریں",
        "preview": "نمائش",
        "showpreview": "نمائش",
-       "showdiff": "تبدÛ\8cÙ\84Û\8cاں Ø¯Ú©Ú¾Ø§Ø¤",
+       "showdiff": "تبدÛ\8cÙ\84Û\8cاں Ø¯Ú©Ú¾Ø§Ø¦Û\8cÚº",
        "anoneditwarning": "<strong>انتباہ:</strong> آپ ویکیپیڈیا میں داخل نہیں ہوئے ہیں۔ لہذا اگر آپ اس صفحہ میں کوئی ترمیم کرتے ہیں تو آپکا آئی پی ایڈریس (IP) اس صفحہ کے تاریخچہ ترمیم میں محفوظ ہوجائے گا۔ اگر آپ  <strong>[$1 لاگ ان]</strong> ہوتے ہیں یا کھاتہ نہ ہونے کی صورت میں <strong>[$2 کھاتہ بنا لیتے ہیں]</strong> تو تو آپ کی ترامیم آپ کے صارف نام سے محفوظ ہوگی، جنھیں آپ کسی بھی وقت ملاحظہ کر سکتے ہیں۔",
        "missingsummary": "'''انتباہ:''' آپ نے ترمیمی خلاصہ مہیّا نہیں کیا.\nاگر آپ نے محفوظ کا بٹن دوبارہ دبایا تو آپ کی ترمیم بغیر کسی خلاصہ کے محفوظ ہوجائے گی.",
        "missingcommenttext": "براہِ کرم! تبصرہ نیچے درج کیجئے.",
        "newarticle": "(نیا)",
        "newarticletext": "آپ نے ایک ایسے صفحے کے ربط کی پیروی کی ہے جو کہ ابھی موجود نہیں ہے.\nیہ صفحہ تخلیق کرنے کیلئے درج ذیل خانہ میں متن درج کیجئے (مزید معلومات کیلئے [$1 صفحۂ معاونت] ملاحظہ فرمائیے).\nاگر آپ یہاں غلطی سے پہنچے ہیں تو پچھلے صفحے پر واپس جانے کیلئے اپنے متصفح پر '''back''' کا بٹن ٹک کیجئے.",
        "anontalkpagetext": "----''یہ صفحہ ایک ایسے صارف کا ہے جنہوں نے یا تو اب تک اپنا کھاتا نہیں بنایا یا پھر وہ اسے استعمال نہیں کر رہے/ رہی ہیں۔ لہٰذا ہمیں انکی شناخت کے لئے ایک عددی آئی پی پتہ استعمال کرنا پڑرہا ہے۔ اس قسم کا آئی پی پتہ ایک سے زائد صارفین کے لئے مشترک بھی ہوسکتا ہے۔ اگر آپکی موجودہ حیثیت ایک گمنام صارف کی ہے اور آپ محسوس کریں کہ اس صفحہ پر آپکی جانب منسوب یہ بیان غیرضروری ہے تو براہ کرم [[Special:CreateAccount|کھاتہ بنائیں]] یا [[Special:UserLogin|داخلِ نوشتہ]] ہوجائیے تاکہ مستقبل میں آپکو گمنام صارفین میں شمار کرنے سے پرہیز کیا جاسکے۔\"",
-       "noarticletext": "اِس صفحہ میں فی الحال کوئی متن موجود نہیں ہے.\nآپ دیگں صفحات میں [[Special:Search/{{PAGENAME}}|اِس صفحہ کے عنوان کیلئے تلاش کرسکتے ہیں]]، <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} متعلقہ نوشتہ جات تلاش کرسکتے ہیں],\nیا [{{fullurl:{{FULLPAGENAME}}|action=edit}} اِس صفحہ میں ترمیم کرسکتے ہیں]</span>",
-       "noarticletext-nopermission": "اس صفحہ میں فی الحال کوئی متن موجود نہیں ہے.\nآپ دیگں صفحات میں [[Special:Search/{{PAGENAME}}|اِس صفحہ کے عنوان کیلئے]] یا <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} متعلقہ نوشتہ جات تلاش کرسکتے ہیں]</span>",
+       "noarticletext": "اِس صفحہ میں فی الحال کوئی متن موجود نہیں ہے۔\nآپ دیگر صفحات میں [[Special:Search/{{PAGENAME}}|اِس صفحہ کے عنوان کو تلاش کر سکتے ہیں]]، <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} متعلقہ نوشتہ جات میں تلاش کر سکتے ہیں]،\nیا [{{fullurl:{{FULLPAGENAME}}|action=edit}} اِس صفحہ کو تخلیق کر سکتے ہیں]</span>۔",
+       "noarticletext-nopermission": "اس صفحہ میں فی الحال کوئی متن موجود نہیں ہے۔\nآپ دیگر صفحات میں [[Special:Search/{{PAGENAME}}|اِس صفحہ کے عنوان کے لیے]] یا <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} متعلقہ نوشتہ جات تلاش کرسکتے ہیں]</span>",
        "userpage-userdoesnotexist-view": "صارف کھاتہ \"$1\" مندرج نہیں ہے۔",
        "updated": "(اپ ڈیٹڈ)",
        "note": "'''نوٹ:'''",
        "editingold": "'''انتباہ: آپ اس صفحے کا ایک پرانا مسودہ مرتب کررہے ہیں۔ اگر آپ اسے محفوظ کرتے ہیں تو اس صفحے کے اس پرانے مسودے سے اب تک کی جانے والی تمام تدوین ضائع ہو جاۓ گی۔'''",
        "yourdiff": "تضادات",
        "copyrightwarning": "یہ یادآوری کرلیجیۓ کہ {{SITENAME}} میں تمام تحریری شراکت جی این یو آزاد مسوداتی اجازہ ($2)کے تحت تصور کی جاتی ہے (مزید تفصیل کیلیۓ $1 دیکھیۓ)۔ اگر آپ اس بات سے متفق نہیں کہ آپکی تحریر میں ترمیمات کری جائیں اور اسے آزادانہ (جیسے ضرورت ہو) استعمال کیا جاۓ تو براۓ کرم اپنی تصانیف یہاں داخل نہ کیجیۓ۔ اگر آپ یہاں اپنی تحریر جمع کراتے ہیں تو آپ اس بات کا بھی اقرار کر رہے ہیں کہ، اسے آپ نے خود تصنیف کیا ہے یا دائرہ ءعام (پبلک ڈومین) سے حاصل کیا ہے یا اس جیسے کسی اور آذاد وسیلہ سے۔'''بلااجازت ایسا کام داخل نہ کیجیۓ جسکا حق ِطبع و نشر محفوظ ہو!'''",
+       "protectedpagewarning": "<strong>انتباہ: اس صفحہ میں ترمیم کاری کو مقفل کر دیا گیا ہے اور محض انتظامی اختیارات کے حامل صارفین ہی اس میں ترمیم کر سکتے ہیں۔</strong>\nحوالہ کے لیے ذیل میں نوشتہ جاتی اندراج فراہم کیا گیا ہے:",
+       "semiprotectedpagewarning": "<strong>اطلاع:</strong> اس صفحہ کو یوں مقفل کیا جاچکا ہے کہ اس میں صرف اندراج شدہ صارفین ہی ترمیم کرسکتے ہیں۔\nحوالہ کے لیے ذیل میں تازہ ترین نوشتہ جاتی اندراج دیا گیا ہے:",
+       "cascadeprotectedwarning": "<strong>انتباہ:</strong> اس صفحہ میں ترمیم کاری کو مقفل کر دیا گیا ہے اور محض انتظامی اختیارات کے حامل صارفین ہی اس میں ترمیم کر سکتے ہیں۔ اسے مقفل کرنے کی وجہ یہ ہے کہ پیش نظر صفحہ درج ذیل محفوظ {{PLURAL:$1|صفحہ|صفحات}} کی آبشاری حفاظت میں شامل ہے:",
        "templatesused": "اِس صفحہ پر مستعمل {{PLURAL:$1|سانچہ|سانچے}}:",
        "templatesusedpreview": "اِس پیش منظر میں مستعمل {{PLURAL:$1|سانچہ|سانچے}}:",
        "templatesusedsection": "اِس قطعہ میں مستعمل {{PLURAL:$1|سانچہ|سانچے}}:",
        "defaultmessagetext": "طے شدہ پیغام کا متن",
        "content-model-text": "سادہ متن",
        "content-model-javascript": "جاوا اسکرپٹ",
+       "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|تبادلہ خیال]]) کی جانب سے کی گئی ترمیم $1 رد کردی گئی ہے۔",
        "viewpagelogs": "اس صفحہ کیلیے نوشتہ جات دیکھیے",
        "nohistory": "اِس صفحہ کیلئے کوئی تدوینی تاریخچہ موجود نہیں ہے.",
        "currentrev": "حـالیـہ تـجدید",
-       "currentrev-asof": "حاÙ\84Û\8cÛ\81 Ù\86ظرثاÙ\86Û\8c بمطابق $1",
-       "revisionasof": "تجدید بمطابق $1",
+       "currentrev-asof": "حاÙ\84Û\8cÛ\81 Ù\86سخÛ\81 بمطابق $1",
+       "revisionasof": "نسخہ بمطابق $1",
        "revision-info": "نظرثانی بتاریخ $1 از {{GENDER:$6|$2}}$7",
        "previousrevision": "←پرانی تدوین",
        "nextrevision": "→اگلا اعادہ",
        "history-show-deleted": "صرف حذف شدہ",
        "histfirst": "قدیم ترین",
        "histlast": "تازہ ترین",
-       "historysize": "({{PLURAL:$1|1 لکمہ|$1 لکم}})",
+       "historysize": "({{PLURAL:$1|1 بائٹ|$1 بائٹ}})",
        "historyempty": "(خالی)",
        "history-feed-title": "تاریخچۂ نظرثانی",
        "history-feed-description": "ویکی پر اِس صفحہ کا تاریخچۂ نظرثانی",
        "history-feed-item-nocomment": "بہ $2 $1",
        "history-feed-empty": "درخواست شدہ صفحہ موجود نہیں.\nیا تو یہ ویکی سے حذف کیا گیا ہے اور یا اِس کا نام تبدیل کردیا گیا ہے.\nآپ متعلقہ نئے صفحات کیلئے [[Special:Search|ویکی پر تلاش]] کرسکتے ہیں.",
+       "history-edit-tags": "منتخب نظرثانیوں کے ٹیگوں میں ترمیم کریں",
        "rev-deleted-comment": "(تبصرہ حذف کی گيا ہے)",
        "rev-deleted-user": "(صارف نام حذف کیا گيا ہے)",
        "rev-delundel": "دکھاؤ/چھپاؤ",
        "searchdisabled": "{{SITENAME}} تلاش غیرفعال.\nآپ فی الحال گوگل کے ذریعے تلاش کرسکتے ہیں.\nیاد رکھئے کہ اُن کے {{SITENAME}} اشاریے ممکناً پرانے ہوسکتے ہیں.",
        "preferences": "ترجیحات",
        "mypreferences": "ترجیحات",
-       "prefs-edits": "تدÙ\88Û\8cÙ\86ات Ú©Û\8c ØªØ¹Ø¯Ø§Ø¯:",
+       "prefs-edits": "تعداد ØªØ±Ø§Ù\85Û\8cÙ\85:",
        "prefs-skin": "جِلد",
        "skin-preview": "پیش منظر",
-       "datedefault": "کوئی ترجیحات نہیں",
+       "datedefault": "کوئی ترجیح نہیں",
        "prefs-user-pages": "صارف صفحات",
-       "prefs-personal": "Ù\86Ù\85اÛ\8cÛ\82 ØµØ§Ø±Ù\81",
+       "prefs-personal": "پرÙ\88Ù\81ائÙ\84",
        "prefs-rc": "حالیہ تبدیلیاں",
        "prefs-watchlist": "زیرِنظر فہرست",
+       "prefs-editwatchlist": "زیر نظر فہرست میں ترمیم کریں",
+       "prefs-editwatchlist-label": "اپنی زیر نظر فہرست کے مندرجات میں ترمیم کریں:",
+       "prefs-editwatchlist-raw": "زیر نظر خام فہرست میں ترمیم کریں",
        "prefs-editwatchlist-clear": "اپنی زیر نظر فہرست صاف کریں",
-       "prefs-watchlist-days": "زیرِنظر فہرست میں نظر آنے والے ایام:",
-       "prefs-watchlist-days-max": "زیادا سے زیادہ $1 {{PLURAL:$1|یوم|ایام}}",
-       "prefs-watchlist-edits": "عرÛ\8cض Ø²Û\8cرÙ\90Ù\86ظرفہرست میں نظر آنے والی تبدیلیوں کی زیادہ سے زیادہ تعداد:",
-       "prefs-watchlist-edits-max": "(زیادہ سے زیادہ تعداد: 1000)",
-       "prefs-watchlist-token": "کلید زیرنظر فہرست:",
+       "prefs-watchlist-days": "زیر نظر فہرست میں نظر آنے والے ایام:",
+       "prefs-watchlist-days-max": "زیادہ سے زیادہ $1 دن",
+       "prefs-watchlist-edits": "تÙ\88سÛ\8cع Ø´Ø¯Û\81 Ø²Û\8cر Ù\86ظر فہرست میں نظر آنے والی تبدیلیوں کی زیادہ سے زیادہ تعداد:",
+       "prefs-watchlist-edits-max": "زیادہ سے زیادہ تعداد: 1000",
+       "prefs-watchlist-token": "زیر نظر فہرست کی کلید:",
        "prefs-misc": "دیگر",
-       "prefs-resetpass": "کلمۂ شناخت تبدیل کیجئے",
+       "prefs-resetpass": "پاس ورڈ تبدیل کریں",
        "prefs-changeemail": "برقی ڈاک پتہ (e-mail address) تبدیل کریں",
        "prefs-setemail": "برقی پتہ دیں",
-       "prefs-email": "اختÛ\8cاراتÙ\90 Ø¨Ø±Ù\82Û\8c Ú\88اک",
+       "prefs-email": "برÙ\82Û\8c Ø®Ø· Ú©Û\92 Ø§Ø®ØªÛ\8cارات",
        "prefs-rendering": "ظاہریت",
        "saveprefs": "محفوظ",
-       "restoreprefs": "تÙ\85اÙ\85 Ø¨Û\92Ù\86Ù\82ص ØªØ±ØªÛ\8cبات بحال کریں",
-       "prefs-editing": "تدÙ\88Û\8cÙ\86",
+       "restoreprefs": "تÙ\85اÙ\85 Ø§Ø¨ØªØ¯Ø§Ø¦Û\8c ØªØ±ØªÛ\8cبات Ú©Ù\88 بحال کریں",
+       "prefs-editing": "ترÙ\85Û\8cÙ\85 Ú©Ø§Ø±Û\8c",
        "rows": "صفیں:",
        "columns": "قطاریں:",
        "searchresultshead": "تلاش",
+       "stub-threshold": "نامکمل ربط کے فارمیٹ کی حد ($1):",
        "stub-threshold-sample-link": "نمونہ",
        "stub-threshold-disabled": "غیر فعال",
-       "recentchangesdays": "حاÙ\84Û\8cÛ\81 ØªØ¨Ø¯Û\8cÙ\84Û\8cÙ\88Úº Ù\85Û\8cÚº Ø¯Ú©Ú¾Ø§Ø¦Û\8c جانے والے ایّام:",
-       "recentchangesdays-max": "(زیادہ سے زیادہ $1 {{PLURAL:$1|دن|ایام}})",
+       "recentchangesdays": "حاÙ\84Û\8cÛ\81 ØªØ¨Ø¯Û\8cÙ\84Û\8cÙ\88Úº Ù\85Û\8cÚº Ø¯Ú©Ú¾Ø§Ø¦Û\92 جانے والے ایّام:",
+       "recentchangesdays-max": "زیادہ سے زیادہ $1 دن",
        "recentchangescount": "دکھائی جانے والی ترامیم کی تعداد:",
-       "prefs-help-recentchangescount": "اِس میں حالیہ تبدیلیاں، تواریخِ صفحہ اور نوشتہ جات شامل ہیں.",
+       "prefs-help-recentchangescount": "اِس میں حالیہ تبدیلیاں، تاریخچے اور نوشتہ جات شامل ہیں۔",
+       "prefs-help-watchlist-token2": "یہ آپ کی زیر نظر فہرست کے ویب فیڈ کی خفیہ کلید ہے۔\nاسے خفیہ رکھیں، تاکہ کوئی دوسرا شخص آپ کی زیر نظر فہرست نہ دیکھ سکے۔\nاگر آپ کو کلید تبدیل کرنی ہو تو [[Special:ResetTokens|یہاں کلک کریں]]۔",
        "savedprefs": "آپ کی ترجیحات محفوظ ہوگئیں۔",
        "timezonelegend": "منطقۂ وقت:",
        "localtime": "مقامی وقت:",
-       "servertime": "سرور وقت:",
+       "timezoneuseserverdefault": "ویکی کا طے شدہ استعمال کریں ($1)",
+       "timezoneuseoffset": "دیگر (فرق درج کریں)",
+       "servertime": "سرور کا وقت:",
+       "guesstimezone": "براؤزر کا وقت استعمال کریں",
        "timezoneregion-africa": "افریقہ",
        "timezoneregion-america": "امریکہ",
        "timezoneregion-antarctica": "انٹارکٹیکا",
        "prefs-searchoptions": "تلاش",
        "prefs-namespaces": "جائے نام",
        "default": "طے شدہ",
-       "prefs-files": "مسلات",
-       "prefs-custom-css": "خودساختہ CSS",
-       "prefs-custom-js": "خودساختہ JS",
-       "prefs-emailconfirm-label": "برقی پتہ کی تصدیق:",
-       "youremail": "٭ برقی خط",
+       "prefs-files": "فائلیں",
+       "prefs-custom-css": "شخصی سی ایس ایس",
+       "prefs-custom-js": "شخصی جاوا اسکرپٹ",
+       "prefs-common-css-js": "جملہ پوشاکوں کے لیے مشترکہ سی ایس ایس/جاوا اسکرپٹ:",
+       "prefs-emailconfirm-label": "برقی خط کی تصدیق:",
+       "youremail": "برقی خط:",
        "username": "صارف:",
-       "prefs-memberingroups": "{{PLURAL:$1|گروہ|گروہوں}} کا رُکن:",
+       "prefs-memberingroups": "{{PLURAL:$1|گروہ|گروہوں}} {{GENDER:$2|کا رکن|کی رکن}}:",
        "prefs-registration": "وقتِ اندراج:",
        "yourrealname": "* اصلی نام",
        "yourlanguage": "زبان:",
        "yourvariant": "متغیّر:",
-       "yournick": "دستخط",
+       "yournick": "شخصی دستخط:",
+       "prefs-help-signature": "تبادلۂ خیال صفحات پر تبصرہ تحریر کرنے کے بعد یہ \"<nowiki>~~~~</nowiki>\" علامتیں درج کرنی چاہئیں، یہ علامتیں از خود آپ کے دستخط اور وقت میں تبدیل ہو جائیں گی۔",
        "badsig": "ناقص خام دستخط.\nHTML tags جانچئے.",
        "badsiglength": "آپ کا دستخط کافی طویل ہے.\nیہ $1 {{PLURAL:$1|حرف|حروف}} سے زیادہ نہیں ہونا چاہئے.",
        "yourgender": "جنس:",
-       "gender-unknown": "آپ Ú©Û\92 ØªØ°Ú©Ø±Û\81 Ú©Û\92 Ù\88Ù\82تØ\8c Ø³Ù\88Ù\81Ù¹Ù\88Û\8cئر ØºÛ\8cر Ø¬Ø§Ù\86بدار Ø¬Ù\86سÛ\8c Ø§Ù\84Ù\81اظ Ø§Ø³ØªØ¹Ù\85اÙ\84 Ú©Ø±Û\92 Ú¯Ø§ Ø§Ú¯Ø± Ù\85Ù\85Ú©Ù\86 Û\81Ù\88",
+       "gender-unknown": "اگر Ù\85Ù\85Ú©Ù\86 Û\81Ù\88 ØªÙ\88 Ø¢Ù¾ Ú©Û\92 ØªØ°Ú©Ø±Û\81 Ú©Û\92 Ù\88Ù\82ت Ø³Ø§Ù\81Ù¹ Ù\88Û\8cئر ØºÛ\8cر Ø¬Ø§Ù\86بدار Ø¬Ù\86سÛ\8c Ø§Ù\84Ù\81اظ Ø§Ø³ØªØ¹Ù\85اÙ\84 Ú©Ø±Û\92 Ú¯Ø§",
        "gender-male": "مرد",
        "gender-female": "عورت",
-       "prefs-help-gender": "اختÛ\8cارÛ\8c: Ù\85صÙ\86عâ\80\8cÙ\84Ø·Û\8cÙ\81 Ú©Û\8c Ø·Ø±Ù\81 Ø³Û\92 ØµØ­Û\8cØ­â\80\8cاÙ\84جÙ\86س ØªØ®Ø§Ø·Ø¨ Ú©Û\8cÙ\84ئÛ\92 Ø§Ø³ØªØ¹Ù\85اÙ\84 Û\81Ù\88تا Û\81Û\92. Û\8cÛ\81 Ù\85عÙ\84Ù\88Ù\85ات Ø¹Ø§Ù\85 Û\81Ù\88Ú¯Û\8c.",
+       "prefs-help-gender": "اس ØªØ±Ø¬Û\8cØ­ Ú©Û\8c ØªØ±ØªÛ\8cب Ø§Ø®ØªÛ\8cارÛ\8c Û\81Û\92Û\94\nآپ Ø§Ù\88ر Ø¯Û\8cگر ØµØ§Ø±Ù\81Û\8cÙ\86 Ú©Û\92 Ù\84Û\8cÛ\92 Ø§Ø² Ø±Ù\88ئÛ\92 Ù\82Ù\88اعد Ù\85Ù\86اسب Ø¬Ù\86سÛ\8c Ø§Ù\84Ù\81اظ Ú©Û\92 Ø§Ù\86تخاب Ú©Û\92 Ù\84Û\8cÛ\92 Ø³Ø§Ù\81Ù¹ Ù\88Û\8cئر Ø§Ø³ Ù\82در Ú©Ù\88 Ø§Ø³ØªØ¹Ù\85اÙ\84 Ú©Ø±ØªØ§ Û\81Û\92Û\94\nÛ\8cÛ\81 Ù\85عÙ\84Ù\88Ù\85ات Ø¹Ø§Ù\85 Û\81Ù\88Ú¯Û\8cÛ\94",
        "email": "برقی خط",
        "prefs-help-realname": "حقیقی نام اختیاری ہے۔\nاگر آپ اسے مہیّا کرتے ہیں، تو اسے آپ کے کام کیلئے آپ کو انتساب دینے کیلئے استعمال کیا جائے گا۔",
-       "prefs-help-email": "برقی ڈاک کا پتہ اختیاری ہے، لیکن یہ اُس وقت مفید ثابت ہوسکتا ہے جب آپ اپنا پارلفظ بھول گئے ہوں.",
-       "prefs-help-email-others": "آپ یہ بھی منتخب کرسکتے ہیں کہ دوسرے صارفین آپ کے تبادلۂ خیال صفحہ پر ایک ربط کے ذریعے آپ کو برقی ڈاک بھیجیں.\nجب دوسرے صارفین آپ سے رابطہ کرتے ہیں تو آپ کا برقی ڈاک کا پتہ افشا نہیں کیا جاتا۔",
+       "prefs-help-email": "برقی ڈاک پتے کا اندراج اختیاری ہے، عموماً اس کی ضرورت اس وقت پڑتی ہے جب آپ اپنا پاس ورڈ بھول چکے ہوں اور نیا پاس ورڈ رکھنا چاہتے ہوں۔",
+       "prefs-help-email-others": "یہ ممکن ہے کہ آپ دیگر صارفین کو اس بات کی اجازت دیں کہ وہ آپ کے صارف یا تبادلۂ خیال صفحہ پر موجود ربط کے ذریعہ آپ کو برقی خط بھیج سکیں۔\nجب صارفین اس طرح آپ سے رابطہ کریں گے تو انہیں آپ کا برقی ڈاک پتہ نظر نہیں آئے گا۔",
        "prefs-help-email-required": "برقی ڈاک پتہ چاہئے.",
        "prefs-info": "بنیادی معلومات",
        "prefs-i18n": "بین الاقوامیت",
        "prefs-signature": "دستخط",
-       "prefs-dateformat": "Ø´Ú©Ù\84بÙ\86دÙ\90 ØªØ§Ø±Û\8cØ®",
+       "prefs-dateformat": "تارÛ\8cØ® Ú©Û\8c ØªØ±ØªÛ\8cب",
        "prefs-timeoffset": "وقت کی ترتیب",
        "prefs-advancedediting": "اعلی اختیارات",
        "prefs-editor": "خانہ ترمیم",
        "prefs-advancedrendering": "اعلی اختیارات",
        "prefs-advancedsearchoptions": "اعلی اختیارات",
        "prefs-advancedwatchlist": "اعلی اختیارات",
+       "prefs-displayrc": "نمائش کے اختیارات",
+       "prefs-displaywatchlist": "نمائش کے اختیارات",
        "prefs-tokenwatchlist": "ٹوکن",
-       "prefs-diffs": "Ù\81رÙ\88Ù\82",
+       "prefs-diffs": "فرق",
        "userrights": "حقوقِ صارف کی نظامت",
        "userrights-lookup-user": "گروہائے صارف کا انتظام",
        "userrights-user-editname": "کوئی اسم‌صارف داخل کیجئے:",
        "group-user-member": "صارف",
        "group-autoconfirmed-member": "خودتوثیق شدہ صارف",
        "group-bot-member": "خودکار صارف",
-       "group-sysop-member": "منتظم",
+       "group-sysop-member": "{{GENDER:$1|منتظم}}",
        "group-bureaucrat-member": "{{GENDER:$1|مامور اداری}}",
        "group-suppress-member": "{{GENDER:$1|suppressor}}",
        "grouppage-user": "{{ns:project}}:صارفین",
        "grouppage-autoconfirmed": "{{ns:project}}:خود توثیق شدہ صارف",
        "grouppage-bot": "{{ns:project}}:روبہ جات",
        "grouppage-sysop": "{{ns:project}}:منتظمین",
-       "grouppage-bureaucrat": "بیورو کریٹ",
+       "grouppage-bureaucrat": "{{ns:project}}:مامورین اداری",
        "right-upload": "ملفات زبراثقال (اپ لوڈ) کریں",
        "right-writeapi": "اے پی آئی لکھائی کا استعمال",
        "right-delete": "صفحات حذف کریں",
        "recentchanges-legend-heading": "<strong>اختیارات</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (نیز [[Special:NewPages|جدید صفحات کی فہرست]]) ملاحظہ فرمائیں",
        "recentchanges-submit": "دکھائیں",
-       "rcnotefrom": "ذیل میں <strong>$3, $4</strong> سے کی گئی {{PLURAL:$5|تبدیلی|تبدیلیاں}} <strong>$1</strong> تک دکھائی جا رہی ہیں۔",
-       "rclistfrom": "$3 $2 سےنئی تبدیلیاں دکھانا شروع کریں",
+       "rcnotefrom": "ذیل میں <strong>$2</strong> سے کی گئی {{PLURAL:$5|تبدیلی|تبدیلیاں}} <strong>$1</strong> تک دکھائی جا رہی ہیں۔",
+       "rclistfrom": "$2، $3ء سے ہونے والی نئی تبدیلیاں دکھائیں",
        "rcshowhideminor": "معمولی ترامیم $1",
        "rcshowhideminor-show": "دکھائیں",
        "rcshowhideminor-hide": "چھپائیں",
        "newpageletter": "نیا ..",
        "boteditletter": " خودکار",
        "rc_categories_any": "کوئی بھی منتخب",
-       "rc-change-size-new": "$1 {{PLURAL:$1|بائٹ|بائٹس}} تبدیلی کے بعد",
+       "rc-change-size-new": "$1 {{PLURAL:$1|بائٹ|بائٹ}} تبدیلی کے بعد",
        "rc-enhanced-expand": "تفصیلات دکھائیں",
        "rc-enhanced-hide": "تفصیلات چھپائیے",
        "recentchangeslinked": "متعلقہ تبدیلیاں",
        "shortpages": "چھوٹے صفحات",
        "longpages": "طویل ترین صفحات",
        "deadendpages": "مردہ صفحات",
-       "protectedpages": "محفوظ شدہ صفحات",
+       "protectedpages": "محفوظ کردہ صفحات",
        "protectedpages-noredirect": "رجوع مکررات چھپائیں",
        "protectedpages-timestamp": "وقت کی مہر",
        "protectedpages-page": "صفحہ",
        "speciallogtitlelabel": "ہدف (عنوان یا {{ns:user}}:صارف نام برائے صارف):",
        "log": "نوشتہ جات",
        "logeventslist-submit": "دکھائیں",
+       "checkbox-select": "$1 کو منتخب کریں",
+       "checkbox-all": "سب",
+       "checkbox-none": "کچھ نہیں",
+       "checkbox-invert": "برعکس",
        "allpages": "تمام صفحات",
        "nextpage": "اگلا صفحہ ($1)",
        "prevpage": "پچھلا صفحہ ($1)",
        "trackingcategories-msg": "کھوجی زمرہ",
        "trackingcategories-name": "پیغام کا عنوان",
        "trackingcategories-desc": "زمرہ کی شمولیت کا معیار",
+       "restricted-displaytitle-ignored": "صفحات مع نظرانداز کردہ عناوین",
        "trackingcategories-disabled": "زمرہ غیر فعال ہے",
        "mailnologintext": "دیگر ارکان کو برقی خط ارسال کرنے کیلیۓ لازم ہے کہ آپ [[Special:UserLogin|داخل شدہ]] حالت میں ہوں اور آپ کی [[Special:Preferences|ترجیحات]] ایک درست برقی خط کا پتا درج ہو۔",
        "emailuser": "صارف کو برقی خط لکھیں",
        "deleteotherreason": "دوسری/اِضافی وجہ:",
        "deletereasonotherlist": "دوسری وجہ",
        "rollback": "ترمیمات سابقہ حالت پرواپس",
-       "rollbacklink": "واپس سابقہ حالت",
+       "rollbacklink": "استرجع کریں",
        "rollbacklinkcount": "استرجع $1 {{PLURAL:$1|ترمیم|ترامیم}}",
+       "rollbacklinkcount-morethan": "$1 {{PLURAL:$1|ترمیم|ترامیم}} سے زیادہ کا استرجع",
        "rollbackfailed": "سابقہ حالت پر واپسی ناکام",
        "cantrollback": "تدوین ثانی کا اعادہ نہیں کیا جاسکتا؛ کیونکہ اس میں آخری بار حصہ لینے والا ہی اس صفحہ کا واحد کاتب ہے۔",
        "changecontentmodel-title-label": "صفحہ کا عنوان",
        "changecontentmodel-reason-label": "وجہ:",
+       "log-name-contentmodel": "نوشتہ تبدیلی نمونہ مواد",
+       "logentry-contentmodel-change": "$1 نے صفحہ $3 کے مواد کی ساخت کو \"$4\" سے \"$5\" میں {{GENDER:$2|تبدیل کیا}}",
        "protectlogpage": "نوشتۂ محفوظ شدگی",
        "protectedarticle": "\"[[$1]]\" کومحفوظ کردیا",
        "unprotectedarticle": "\"[[$1]]\" کوغیر محفوظ کیا",
+       "movedarticleprotection": "نے \"[[$2]]\" کا درجہ حفاظت \"[[$1]]\" کی جانب منتقل کیا",
        "prot_1movedto2": "[[$1]] بجانب [[$2]] منتقل",
        "protectcomment": "وجہ:",
        "protect-default": "تمام صارفین کو اہل بناؤ",
        "undelete-search-submit": "تلاش",
        "undelete-no-results": "حذف شدہ صفحات میں ایسا کوئی صفحہ نہیں ملا",
        "undelete-show-file-submit": "ہاں",
-       "namespace": "Ù\81ضائÛ\92 Ù\86اÙ\85:",
+       "namespace": "Ù\86اÙ\85 Ù\81ضا:",
        "invert": "انتخاب بالعکس",
        "tooltip-invert": "منتخب شدہ فضائے نام (اور مُلحقہ فضائے نام) میں شامل صفحات کی تبدیلیوں کو چُھپانے کیلئے اِس خانہ کو ٹِک کریں۔",
-       "namespace_association": "متعلقہ فضا",
+       "namespace_association": "Ù\85تعÙ\84Ù\82Û\81 Ù\86اÙ\85 Ù\81ضا",
        "blanknamespace": "(مرکز)",
        "contributions": "{{GENDER:$1|صارف}} شراکتیں",
        "contributions-title": "مساہماتِ صارف برائے $1",
        "sp-contributions-search": "تلاش برائے مساہمات",
        "sp-contributions-username": "آئی.پی پتہ یا اسمِ صارف:",
        "sp-contributions-toponly": "صرف حالیہ ترین نظرثانی ترمیمات دِکھاؤ",
+       "sp-contributions-hideminor": "معمولی ترامیم چھپائیں",
        "sp-contributions-submit": "تلاش",
        "whatlinkshere": "ادھر کونسا ربط ہے",
        "whatlinkshere-title": "\"$1\" سے مربوط صفحات",
        "whatlinkshere-next": "{{PLURAL:$1|اگلا|اگلے $1}}",
        "whatlinkshere-links": "روابط ←",
        "whatlinkshere-hideredirs": "رجوع مکررات $1",
-       "whatlinkshere-hidetrans": "$1 ØªØ¶Ù\85Û\8cÙ\86ات",
+       "whatlinkshere-hidetrans": "$1 Ø§Ø³ØªØ¹Ù\85اÙ\84ات",
        "whatlinkshere-hidelinks": "روابط $1",
        "whatlinkshere-hideimages": "روابطِ تصاویر $1",
        "whatlinkshere-filters": "فلٹرذ",
        "blocklink": "پابندی لگائیں",
        "unblocklink": "پابندی ختم",
        "change-blocklink": "پابندی میں تبدیلی",
-       "contribslink": "شراکت",
+       "contribslink": "شراکتیں",
        "blocklogpage": "نوشتۂ پابندی",
        "block-log-flags-nocreate": "کھاتے کی تخلیق غیرفعال",
        "move-page": "منتقلی $1",
        "move-watch": "صفحہ زیر نظر",
        "movepagebtn": "مـنـتـقـل",
        "pagemovedsub": "انتقال کامیاب",
-       "movepage-moved": "'''\"$1\" منتقل کردیا گیا بطرف \"$2\"'''",
+       "movepage-moved": "<strong>\"$1\" کو \"$2\" کی جانب منتقل کر دیا گیا</strong>",
        "movepage-moved-redirect": "رجوع مکرر تخلیق کر دیا گیا۔",
+       "movepage-moved-noredirect": "رجوع مکرر کو بننے سے روک دیا گیا ہے۔",
        "articleexists": "اس عنوان سے کوئی صفحہ پہلے ہی موجود ہے، یا آپکا منتخب کردہ نام مستعمل نہیں۔ براۓ مہربانی دوسرا نام منتخب کیجیۓ۔",
        "movelogpage": "نوشتۂ منتقلی",
        "movereason": "وجہ:",
        "allmessages-filter-translate": "ترجمہ",
        "thumbnail-more": "چوڑا کریں",
        "import": "درآمد صفحات",
-       "tooltip-pt-userpage": "آپ کا صارفی صفحہ",
-       "tooltip-pt-mytalk": "آپ Ú©Ø§ ØµÙ\81Ø­Û\82 Ú¯Ù\81تگÙ\88",
+       "tooltip-pt-userpage": "آپ کا صارف صفحہ",
+       "tooltip-pt-mytalk": "آپ Ú©Ø§ ØªØ¨Ø§Ø¯Ù\84Û\81 Ø®Û\8cاÙ\84 ØµÙ\81Ø­Û\81",
        "tooltip-pt-preferences": "آپ کی ترجیحات",
        "tooltip-pt-watchlist": "اُن صفحات کی فہرست جن کی تبدیلیاں آپ کی زیرِنظر ہیں",
-       "tooltip-pt-mycontris": "آپ کی شراکت کی فہرست",
+       "tooltip-pt-mycontris": "آپ کی شراکتوں کی فہرست",
        "tooltip-pt-login": "آپ کیلئے داخلِ نوشتہ ہونا اچھا ہے؛ تاہم، یہ ضروری نہیں",
        "tooltip-pt-logout": "خارجِ نوشتہ ہوجائیں",
        "tooltip-pt-createaccount": "آپ کو مدعو کیا جاتا ہے کہ کھاتہ بنائیں۔تاہم کھاتہ بنانا لازم نہیں۔",
        "tooltip-rollback": "پچھلے صارف کی کی گئی اِس صفحے پر استرجع شدہ ترامیم کو ایک کلِک میں واپس کریں",
        "tooltip-undo": "''استرجع'' اس ترمیم کو پچھلی ترمیم کے جانب واپس کردیگا اور نمائشی انداز میں خانہ ترمیم کھول دے گا۔ آپ مختصراً سبب بیان کرنے کے بھی مجاز ہونگے۔",
        "tooltip-summary": "مختصر خلاصہ درج کریں",
+       "common.css": "body,\ntextarea {\n    font-family: Amiri;\n}",
        "anonymous": "{{SITENAME}} گمنام صارف",
        "others": "دیگر",
        "pageinfo-visiting-watchers": "تعداد ناظرین جنہوں نے حالیہ ترامیم کا مشاہدہ کیا",
        "pageinfo-hidden-categories": "پوشیدہ {{PLURAL:$1|زمرہ|زمرہ جات}} ($1)",
        "pageinfo-toolboxlink": "معلومات صفحہ",
+       "pageinfo-category-info": "زمرے کی معلومات",
+       "pageinfo-category-pages": "تعداد صفحات",
+       "pageinfo-category-subcats": "تعداد ذیلی زمرہ جات",
+       "pageinfo-category-files": "تعداد املاف",
        "markaspatrolledtext": "اس صفحہ کو بطور مراجعت شدہ نشان زد کریں",
+       "markedaspatrollederrornotify": "بطور مراجعت نشان زد نہیں کیا جا سکا۔",
        "deletedrevision": "حذف شدہ پرانی ترمیم $1۔",
        "previousdiff": "← پُرانی تدوین",
        "nextdiff": "صفحہ کا نام:",
+       "imagemaxsize": "تصویر کی جسامت کی حد:<br /><em>(فائل کے توضیحی صفحات کے لیے)</em>",
+       "thumbsize": "تھمب نیل کی جسامت:",
        "file-info-size": "\n$1 × $2 عکصر (پکسلز)، حجم ملف: $3، MIME قسم: $4",
        "file-nohires": "اس سے بڑی تصمیم دستیاب نہیں۔",
        "show-big-image": "اصل ملف",
        "show-big-image-preview": "اس نمائش کا حجم:$1",
+       "show-big-image-other": "دیگر {{PLURAL:$2|تجویز|تجویزیں}}: $1۔",
        "show-big-image-size": "$1 × $2 pixels",
        "newimages": "نئی فائلوں کی گیلری",
        "ilsubmit": "تلاش",
        "bydate": "بالحاظ تاریخ",
        "weeks": "{{PLURAL:$1|$1ہفتہ| $1  ہفتے}}",
+       "ago": "$1 قبل",
+       "minutes-ago": "$1 {{PLURAL:$1|منٹ|منٹ}} قبل",
+       "seconds-ago": "$1 {{PLURAL:$1|سیکنڈ|سیکنڈ}} قبل",
        "bad_image_list": "شکلبند درج ذیل ہے:\n\nصرف فہرستی عناصر (* سے شروع ہونے والی لکیری) شامل کی جاتی ہیں۔\nکسی لکیر میں پہلا ربط کوئی خراب ملف کا ہونا چاہئے۔\nاُسی لکیر میں باقی آنے والے ربط کو مستثنیٰ قرار دیا جاتا ہے، مثلاً صفحات جہاں ملف لکیر کے وسط میں آسکتا ہے۔",
        "metadata": "میٹا ڈیٹا",
        "metadata-help": "اِس ملف میں اِضافی معلومات شامل ہیں، جو کہ شاید اُس رقمی کیمرے یا سکینر سے آئے ہیں جس کے ذریعے یہ ملف بنائی گئی تھی۔\nاگر ملف اپنی اصل حالت میں نہیں رہی ہے تو کچھ تفاصیل ترمیم شدہ ملف کی مکمل طور پر عکاسی نہیں کرپائیں گے۔",
        "metadata-collapse": "طویل تفاصیل چھپاؤ",
+       "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": "لمباوی دکھاوت",
        "autosumm-blank": "تمام مندرجات حذف",
        "autoredircomment": "[[$1]] سے رجوع مکرر",
        "autosumm-new": "نیا صفحہ: $1",
+       "size-bytes": "$1 بائٹ",
        "watchlisttools-view": "متعلقہ تبدیلیاں دیکھیں",
        "watchlisttools-edit": "زیرِنظرفہرست دیکھیں اور تدوین کریں",
        "watchlisttools-raw": "خام زیرِنظرفہرست تدوین کریں",
        "hijri-calendar-m10": "شوال",
        "hijri-calendar-m11": "ذوالقعدہ",
        "hijri-calendar-m12": "ذوالحجہ",
+       "restricted-displaytitle": "<strong>انتباہ!:</strong> عنوان \"$1\" کو نظر انداز کر دیا گیا ہے کیونکہ یہ متعلقہ صفحہ کے عنوان کا حقیقی متبادل نہیں ہے۔",
        "version": "ورژن",
        "version-specialpages": "خاص صفحات",
        "version-other": "دیگر",
        "specialpages": "خصوصی صفحات",
        "specialpages-group-pages": "فہارست صفحات",
        "tag-filter": "[[Special:Tags|لوحہ]] فلٹر:",
-       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ٹیگ|ٹیگز}}]]: $2)",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ٹیگ|ٹیگ}}]]: $2)",
        "tags-source-header": "ماخذ",
        "tags-active-header": "فعال؟",
        "tags-active-yes": "ہاں",
        "htmlform-no": "نہیں",
        "htmlform-yes": "ہاں",
        "logentry-delete-delete": "$1 {{GENDER:$2|حذف کیا گیا}} صفحہ $3",
-       "logentry-move-move": "$1 نے صفحہ $3 کو بجانب $4 منتقل کیا",
+       "logentry-move-move": "$1 نے صفحہ $3 کو $4 کی جانب منتقل کیا",
        "logentry-newusers-create": "صارف کھاتہ $1 {{GENDER:$2|بنایا گیا}}",
+       "logentry-protect-move_prot": "$1 نے ترتیب درجہ حفاظت $4 سے $3 کی طرف {{GENDER:$2|منتقل کی}}",
+       "logentry-protect-protect": "$1 نے $3 کو {{GENDER:$2|محفوظ کیا}}  $4",
        "logentry-protect-modify": "$1 نے $3 کا درجۂ حفاظت {{GENDER:$2|تبدیل کیا}} $4",
+       "logentry-rights-rights": "$1 نے {{GENDER:$6|$3}} کی گروہی رکنیت از $4 تا $5 {{GENDER:$2|تبدیل کی}}",
        "logentry-upload-upload": "$1 {{GENDER:$2|اپلوڈ}} $3",
        "rightsnone": "(کچھ نہیں)",
        "revdelete-summary": "خلاصۂ تدوین",
index 7b604e7..71b389f 100644 (file)
        "minoredit": "Bu kichik tahrir",
        "watchthis": "Sahifani kuzatish",
        "savearticle": "Saqla",
+       "publishpage": "Sahifani chop et",
+       "publishchanges": "Oʻzgarishlarni chop et",
        "preview": "Ko‘rib chiqish",
        "showpreview": "Koʻrib chiqish",
        "showdiff": "Kiritilgan o‘zgarishlar",
        "undo-success": "Tahrirni bekor qilish imkoniyati bor. Iltimos, solishtirish oynasini koʻrib chiqib, aynan shu oʻzgarishlarni bekor qilmoqchiligingizga ishonch hosil qiling va undan keyin «Saqla» tugmasini bosing.",
        "undo-failure": "Keyingi tahrirlar bilan chalkashib ketgani sababli, ushbu tahrirni alohida oʻzini bekor qilishni iloji yoʻq.",
        "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|mun.]]) tomonidan qilingan $1-sonli tahrir qaytarildi",
-       "cantcreateaccounttitle": "Ro‘yxatdan o‘tib bo‘lmadi",
        "cantcreateaccount-text": "[[User:$3|$3]] ushbu IP manzil (<strong>$1</strong>) orqali ro‘yxatdan o‘tishni bloklab qo‘ygan.\n\n$3 <em>$2</em>ni sabab qilib ko‘rsatdi",
        "cantcreateaccount-range-text": "[[User:$3|$3]] <strong>$1</strong> sohaga tegishli IP manzillar, shu jumladan sizning IP manzilingiz (<strong>$4</strong>), orqali ro‘yxatdan o‘tishni bloklab qo‘ygan.\n\n$3 <em>$2</em>ni sabab qilib ko‘rsatdi",
        "viewpagelogs": "Ushbu sahifaga doir qaydlarni koʻrsat",
index 882ea01..42c3a28 100644 (file)
        "passwordreset-emailtext-user": "El utente $1 da {{SITENAME}} (probabilmente ti steso) ga richiesto l'invio de na password nova par {{SITENAME}} ($4). {{PLURAL:$3|El profiło utente asocià|I profiłi utenti asociadi}} a sto indiriso de posta ełetronega łi xe:\n\n$2\n\n{{PLURAL:$3|Sta password tenporanea ła scadarà|Ste password tenporanee łe scadarà}} dopo {{PLURAL:$5|un dì|$5 dì}}.\n\nSarìa mejo acedare e deçidare na nova password sùito. Se no te si stà ti a fare ła richiesta, o se te te ghe ricordà ła password originałe e no te vołi pi canbiarla, te połi ignorare sto mesajo e continuar doparare ła to password vecia.",
        "passwordreset-emailelement": "Nome utente: \n$1\n\nPassword tenporanea: \n$2",
        "passwordreset-emailsentemail": "Xe stà invià na mail de reset password.",
-       "passwordreset-emailsent-capture": "Xe stà invià na mail de reset password: el contegù xe riportà cuà de seguito.",
-       "passwordreset-emailerror-capture": "Xe stà generà na mail de reset password, riportà cuà de seguito. L'invio a {{GENDER:$2|l'utente}} no xe riusido: $1",
        "changeemail": "Canbia indiriso de posta ełetronega",
        "changeemail-header": "Canbia el indiriso de posta ełetronega del account",
        "changeemail-no-info": "Te ghe da aver efetuà l'aceso par acedare a sta pajina diretamente.",
        "minoredit": "Segna come canbiamento picenin",
        "watchthis": "Tien d'ocio sta pagina",
        "savearticle": "Salva sta pagina",
+       "publishpage": "Pùblica la pàjina",
+       "publishchanges": "Pùblica i to canbiamenti",
        "preview": "Anteprima",
        "showpreview": "Varda anteprima",
        "showdiff": "Mostra canbiamenti",
        "undo-failure": "No se pol mìa anular la modifica, par via de un conflito con modifiche intermedie.",
        "undo-norev": "La modifica no la pode vegner anulà parché no la esiste o la xe stà scancelà.",
        "undo-summary": "Anulà la modifica $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|Discussion]] | [[Special:Contributions/$2|{{MediaWiki:Contribslink}}]])",
-       "cantcreateaccounttitle": "Inpossibile registrar l'utente",
        "cantcreateaccount-text": "La creazion de account novi a partir da sto indirizo IP ('''$1''') la xe stà blocà da [[User:$3|$3]].\n\nLa motivazion del bloco fornìa da $3 la xe sta qua: ''$2''",
        "viewpagelogs": "Varda i registri de sta pagina",
        "nohistory": "La cronołogia de łe version de sta pàxena no la xè reperibiłe.",
        "mw-widgets-dateinput-placeholder-day": "AAAA-MM-GG",
        "mw-widgets-dateinput-placeholder-month": "AAAA-MM",
        "mw-widgets-titleinput-description-redirect": "rimando a $1",
-       "api-error-blacklisted": "Par piaser siełi un titoło difarente e descritivo.",
        "randomrootpage": "Pagina raìsa caxuale"
 }
index f2c9f0c..31b213f 100644 (file)
        "tagline": "Từ {{SITENAME}}",
        "help": "Trợ giúp",
        "search": "Tìm kiếm",
+       "search-ignored-headings": " #<!-- để yên dòng này --> <pre>\n# Công cụ tìm kiếm sẽ bỏ qua các đề mục này.\n# Các thay đổi trên danh sách này sẽ có hiệu lực một khi trang có đề mục được đưa vào chỉ mục.\n# Để bắt trang phải được đưa lại vào chỉ mục, thực hiện một sửa đổi vô hiệu quả.\n# Cú pháp:\n#   * Tất cả mọi thứ từ ký hiệu “#” để cuối dòng là chú thích.\n#   * Mỗi dòng có nội dung là đúng tên đề mục để bỏ qua, phân biệt chữ hoa/thường.\nTham khảo\nChú thích\nLiên kết ngoài\nXem thêm\nĐọc thêm\n #</pre> <!-- để yên dòng này -->",
        "searchbutton": "Tìm kiếm",
        "go": "Xem",
        "searcharticle": "Xem",
        "yourpasswordagain": "Gõ lại mật khẩu",
        "createacct-yourpasswordagain": "Xác nhận lại mật khẩu",
        "createacct-yourpasswordagain-ph": "Nhập mật khẩu lần nữa",
-       "remembermypassword": "Nhớ thông tin đăng nhập của tôi trên máy tính này (cho đến $1 ngày)",
        "userlogin-remembermypassword": "Giữ trạng thái đăng nhập",
        "userlogin-signwithsecure": "Sử dụng kết nối an toàn",
        "cannotloginnow-title": "Không thể đăng nhập lúc này",
        "changepassword-throttled": "Bạn đã thử đăng nhập gần đây nhiều lần quá. Xin chờ $1 trước khi bạn thử lần nữa.",
        "botpasswords": "Mật khẩu Bot",
        "botpasswords-summary": "<em>Mật khẩu bot</em> cho phép truy cập một tài khoản người dùng qua API mà không sử dụng thông tin chứng nhận chính của tài khoản. Các quyền người dùng có thể bị hạn chế khi đăng nhập dùng mật khẩu bot.\n\nNếu bạn không hiểu tại sao cần sử dụng mật khẩu bot, có lẽ bạn không nên sử dụng nó. Không ai bao giờ có lý do chính đáng để yêu cầu bạn tạo ra một mật khẩu bot và cung cấp nó cho họ.",
-       "botpasswords-disabled": "Mật khẩu Bot bị vô hiệu hoá.",
+       "botpasswords-disabled": "Mật khẩu bot bị vô hiệu hóa.",
        "botpasswords-no-central-id": "Để sử dụng mật khẩu bot, bạn phải đăng nhập vào một tài khoản tập trung.",
        "botpasswords-existing": "Mật khẩu bot hiện tại",
        "botpasswords-createnew": "Tạo một mật khẩu mới bot",
        "passwordreset-emailelement": "Tên người dùng: \n$1\n\nMật khẩu tạm: \n$2",
        "passwordreset-emailsentemail": "Nếu đây là đúng địa chỉ thư điện tử của tài khoản của bạn, một thư điện tử dùng để tái tạo mật khẩu sẽ được gửi cho bạn.",
        "passwordreset-emailsentusername": "Nếu một địa chỉ thư điện tử đã gắn với tên người dùng này thì một thư điện tử để đặt lại mật khẩu sẽ được gửi đến.",
-       "passwordreset-emailsent-capture": "Thư điện tử để tái tạo mật khẩu đã được gửi, nội dung như sau.",
-       "passwordreset-emailerror-capture": "Chúng tôi đã tạo thư tái tạo mật khẩu dưới đây, nhưng không thể gửi đến {{GENDER:$2}}người dùng: $1",
        "passwordreset-emailsent-capture2": "Đã gửi {{PLURAL:$1|thư điện tử|các thư điện tử}} để đặt lại mật khẩu. {{PLURAL:$1|Tên người dùng và mật khẩu|Các tên người dùng và mật khẩu}} được liệt kê dưới đây.",
        "passwordreset-emailerror-capture2": "Không gửi được thư điện tử đến {{GENDER:$2}}người dùng: $1 {{PLURAL:$3|Tên người dùng và mật khẩu|Các tên người dùng và mật khẩu}} được liệt kê dưới đây.",
        "passwordreset-nocaller": "Yêu cầu hàm gọi",
        "passwordreset-nodata": "Cả tên người dùng và địa chỉ thư điện tử bị thiếu",
        "changeemail": "Đổi hoặc gỡ địa chỉ thư điện tử",
        "changeemail-header": "Điền biểu mẫu này để đổi địa chỉ thư điện tử của bạn. Nếu bạn muốn gỡ địa chỉ thư điện tử nào khỏi tài khoản của bạn, để trống hộp địa chỉ thư điện tử mới và lưu biểu mẫu.",
-       "changeemail-passwordrequired": "Bạn sẽ cần phải nhập mật khẩu của bạn để xác nhận thay đổi này.",
        "changeemail-no-info": "Bạn phải đăng nhập mới có thể truy cập trực tiếp trang này.",
        "changeemail-oldemail": "Địa chỉ thư điện tử hiện tại:",
        "changeemail-newemail": "Địa chỉ thư điện tử mới:",
        "minoredit": "Sửa đổi nhỏ",
        "watchthis": "Theo dõi trang này",
        "savearticle": "Lưu trang",
-       "publishpage": "Xuất bản trang",
+       "savechanges": "Lưu các thay đổi",
+       "publishpage": "Đăng trang",
+       "publishchanges": "Đăng thay đổi",
        "preview": "Xem trước",
        "showpreview": "Xem trước",
        "showdiff": "Xem thay đổi",
        "content-model-css": "CSS",
        "content-json-empty-object": "Đối tượng trống",
        "content-json-empty-array": "Mảng trống",
+       "deprecated-self-close-category": "Trang có thẻ HTML tự đóng không hợp lệ",
+       "deprecated-self-close-category-desc": "Trang này chứa thẻ HTML tự đóng không hợp lệ, ví dụ <code>&lt;b/></code> hoặc <code>&lt;span/></code>. Tí nữa cách trình bày các thẻ này sẽ thay đổi để tuân theo tiêu chuẩn HTML5, nên sự sử dụng chúng trong mã wiki bị phản đối.",
        "duplicate-args-warning": "<strong>Cảnh báo:</strong> [[:$1]] đang gọi  [[:$2]] với nhiều hơn một giá trị cho thông số “$3”. Chỉ giá trị cuối cùng mới được sử dụng.",
        "duplicate-args-category": "Trang đưa đối số thừa vào bản mẫu",
        "duplicate-args-category-desc": "Trang đưa một đối số nhiều lần vào một bản mẫu được nhúng, thí dụ <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> hoặc <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "undo-nochange": "Hình như sửa đổi này đã được lùi lại rồi.",
        "undo-summary": "Đã lùi lại sửa đổi $1 của [[Special:Contributions/$2|$2]] ([[User talk:$2|thảo luận]])",
        "undo-summary-username-hidden": "Đã lùi lại sửa đổi $1 của một người dùng ẩn",
-       "cantcreateaccounttitle": "Không thể mở tài khoản",
        "cantcreateaccount-text": "Chức năng tài tạo khoản từ địa chỉ IP này ('''$1''') đã bị [[User:$3|$3]] cấm.\n\nLý do được $3 đưa ra là ''$2''",
        "cantcreateaccount-range-text": "[[User:$3|$3]] đã cấm các địa chỉ IP trong dãy <strong>$1</strong>, bao gồm địa chỉ IP của bạn (<strong>$4</strong>), không được mở tài khoản.\n\nLý do được đưa ra bởi $3 là <em>$2</em>",
        "viewpagelogs": "Xem nhật trình của trang này",
        "grant-group-high-volume": "Hoạt động với tần số cao",
        "grant-group-customization": "Tùy biến và tùy chọn",
        "grant-group-administration": "Thực hiện các hành động bảo quản",
+       "grant-group-private-information": "Truy cập dữ liệu cá nhân của bạn",
        "grant-group-other": "Hoạt động khác",
        "grant-blockusers": "Cấm và bỏ cấm người dùng",
        "grant-createaccount": "Mở tài khoản",
        "grant-highvolume": "Sửa đổi tốc độ cao",
        "grant-oversight": "Ẩn người dùng và phiên bản",
        "grant-patrol": "Tuần tra các thay đổi trang",
+       "grant-privateinfo": "Truy cập dữ liệu cá nhân",
        "grant-protect": "Khóa và mở khóa các trang",
        "grant-rollback": "Lùi một loạt thay đổi vào một trang",
        "grant-sendemail": "Gửi thư điện tử cho người dùng khác",
        "action-applychangetags": "áp dụng các thẻ cùng với những thay đổi của bạn",
        "action-changetags": "thêm và loại bỏ các thẻ tùy ý trên các phiên bản riêng và các mục nhật trình",
        "action-deletechangetags": "xóa thẻ khỏi cơ sở dữ liệu",
+       "action-purge": "làm mới trang này",
        "nchanges": "$1 thay đổi",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|sau lần truy cập vừa rồi}}",
        "enhancedrc-history": "lịch sử",
        "file-thumbnail-no": "Tên tập tin bắt đầu bằng <strong>$1</strong>.\nCó vẻ đây là bản thu nhỏ của hình gốc ''(thumbnail)''.\nNếu bạn có hình ở độ phân giải tối đa, xin hãy tải bản đó lên, nếu không xin hãy đổi lại tên tập tin.",
        "fileexists-forbidden": "Đã có tập tin với tên gọi này, và nó không thể bị ghi đè.\nNếu bạn vẫn muốn tải tập tin của bạn lên, xin hãy quay lại và sử dụng một tên khác. [[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Một tập tin với tên này đã tồn tại ở kho tập tin dùng chung.\nNếu bạn vẫn muốn tải tập tin của bạn lên, xin hãy quay lại và dùng một tên khác. [[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "Tập tin tải lên này là bản sao y hệt với phiên bản hiện tại của <strong>[[:$1]]</strong>.",
+       "fileexists-duplicate-version": "Tập tin tải lên này là bản sao y hệt với {{PLURAL:$2|một phiên bản trước đây|các phiên bản trước đây}} của <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Tập tin này có vẻ là bản sao của {{PLURAL:$1|tập tin|các  tập tin}} sau:",
        "file-deleted-duplicate": "Một tập tin giống hệt như tập tin này ([[:$1]]) đã từng bị xóa trước đây. Bạn nên xem lại lịch sử xóa tập tin trước khi tiếp tục tải nó lên lại.",
        "file-deleted-duplicate-notitle": "Một tập tin giống hệt như tập tin này đã từng bị xóa và tên bị xóa hẳn trước đây.\nBạn nên xin một người có quyền xem dữ liệu tập tin bị xóa hẳn xem lại trường hợp này trước khi tiếp tục tải nó lên lại.",
        "uploadstash-errclear": "Việc dọn sạch các tập tin bị thất bại.",
        "uploadstash-refresh": "Làm mới danh sách tập tin",
        "uploadstash-thumbnail": "xem hình thu nhỏ",
+       "uploadstash-exception": "Không thể lưu tập tin vào hàng đợi tải lên ($1): “$2”.",
        "invalid-chunk-offset": "Khúc lệch (chunk offset) không hợp lệ",
        "img-auth-accessdenied": "Không cho phép truy cập",
        "img-auth-nopathinfo": "Thiếu PATH_INFO.\nMáy chủ của bạn không được thiết lập để truyền thông tin này.\nCó thể do nó dựa trên CGI và không hỗ trợ img_auth.\nXem [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization hướng dẫn điều khiển truy cập hình ảnh].",
        "filerevert-submit": "Lùi lại",
        "filerevert-success": "'''[[Media:$1|$1]]''' đã được lùi về [$4 phiên bản lúc $3, $2].",
        "filerevert-badversion": "Không tồn tại phiên bản trước đó của tập tin tại thời điểm trên.",
+       "filerevert-identical": "Phiên bản hiện tại của tập tin đã y hệt với phiên bản được chọn.",
        "filedelete": "Xóa $1",
        "filedelete-legend": "Xóa tập tin",
        "filedelete-intro": "Bạn sắp xóa tập tin '''[[Media:$1|$1]]''' cùng với tất cả lịch sử của nó.",
        "watchnologin": "Chưa đăng nhập",
        "addwatch": "Thêm vào danh sách theo dõi",
        "addedwatchtext": "“[[:$1]]” cùng trang thảo luận đã vào [[Special:Watchlist|danh sách theo dõi]] của bạn.",
+       "addedwatchtext-talk": "“[[:$1]]” cùng trang đi kèm đã vào [[Special:Watchlist|danh sách theo dõi]] của bạn.",
        "addedwatchtext-short": "Trang “$1” đã được thêm vào danh sách theo dõi của bạn.",
        "removewatch": "Gỡ khỏi danh sách theo dõi",
        "removedwatchtext": "“[[:$1]]” cùng trang thảo luận đã được đưa ra khỏi [[Special:Watchlist|danh sách theo dõi]] của bạn.",
+       "removedwatchtext-talk": "“[[:$1]]” cùng trang đi kèm đã được đưa ra khỏi [[Special:Watchlist|danh sách theo dõi]] của bạn.",
        "removedwatchtext-short": "Trang “$1” đã được xóa khỏi danh sách theo dõi của bạn.",
        "watch": "Theo dõi",
        "watchthispage": "Theo dõi trang này",
        "rollbacklinkcount-morethan": "lùi tất cả hơn $1 sửa đổi",
        "rollbackfailed": "Lùi sửa đổi không thành công",
        "rollback-missingparam": "Yêu cầu thiếu những tham số bắt buộc.",
+       "rollback-missingrevision": "Không thể tải dữ liệu phiên bản.",
        "cantrollback": "Không lùi sửa đổi được;\nngười viết trang cuối cùng cũng là tác giả duy nhất của trang này.",
        "alreadyrolled": "Không thể lùi tất cả sửa đổi cuối của [[User:$2|$2]] ([[User talk:$2|thảo luận]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) tại [[:$1]]; ai đó đã thực hiện sửa đổi hoặc thực hiện lùi tất cả rồi.\n\nSửa đổi cuối cùng tại trang do [[User:$3|$3]] ([[User talk:$3|thảo luận]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) thực hiện.",
        "editcomment": "Tóm lược sửa đổi: <em>$1</em>.",
        "undeletehistorynoadmin": "Trang này đã bị xóa.\nLý do xóa trang được hiển thị dưới đây, cùng với thông tin về những người đã sửa đổi trang này trước khi bị xóa.\nChỉ có bảo quản viên mới xem được văn bản đầy đủ của những phiên bản trang bị xóa.",
        "undelete-revision": "Phiên bản đã xóa của $1 (vào lúc $4 tại $5) do $3 sửa đổi:",
        "undeleterevision-missing": "Phiên bản này không hợp lệ hay không tồn tại. Đây có thể là một địa chỉ sai, hoặc là phiên bản đã được phục hồi hoặc đã xóa khỏi kho lưu trữ.",
+       "undeleterevision-duplicate-revid": "Không thể phục hồi {{PLURAL:$1|một phiên bản|$1 phiên bản}} vì <code>rev_id</code> của {{PLURAL:$1|nó|chúng}} đã được sử dụng.",
        "undelete-nodiff": "Không tìm thấy phiên bản cũ hơn.",
        "undeletebtn": "Phục hồi",
        "undeletelink": "xem lại/phục hồi",
        "undeletedrevisions": "$1 phiên bản được phục hồi",
        "undeletedrevisions-files": "$1 phiên bản và $2 tập tin đã được phục hồi",
        "undeletedfiles": "$1 tập tin đã được phục hồi",
-       "cannotundelete": "Phục hồi thất bại:\n$1",
+       "cannotundelete": "Phục hồi bị thất bại một phần hoặc hoàn toàn:\n$1",
        "undeletedpage": "'''$1 đã được khôi phục'''\n\nXem nhật trình xóa và phục hồi các trang gần đây tại [[Special:Log/delete|nhật trình xóa]].",
        "undelete-header": "Xem các trang bị xóa gần đây tại [[Special:Log/delete|nhật trình xóa]].",
        "undelete-search-title": "Tìm kiếm trang đã bị xóa",
        "sp-contributions-newbies-sub": "Các thành viên mới",
        "sp-contributions-newbies-title": "Đóng góp của các thành viên mới",
        "sp-contributions-blocklog": "nhật trình cấm",
-       "sp-contributions-suppresslog": "đóng góp của người dùng đã bị xóa hẳn",
-       "sp-contributions-deleted": "đóng góp đã bị xóa của thành viên",
+       "sp-contributions-suppresslog": "đóng góp của {{GENDER:$1}}người dùng đã bị xóa hẳn",
+       "sp-contributions-deleted": "đóng góp đã bị xóa của {{GENDER:$1}}thành viên",
        "sp-contributions-uploads": "tập tin tải lên",
        "sp-contributions-logs": "nhật trình",
        "sp-contributions-talk": "thảo luận",
        "mw-widgets-dateinput-placeholder-month": "YYYY-MM (năm-tháng)",
        "mw-widgets-titleinput-description-new-page": "trang này chưa tồn tại",
        "mw-widgets-titleinput-description-redirect": "đổi hướng đến $1",
-       "api-error-blacklisted": "Xin vui lòng chọn một tên khác miêu tả đầy đủ.",
        "sessionmanager-tie": "Không thể kết hợp nhiều yêu cầu xác thực loại: $1.",
        "sessionprovider-generic": "phiên $1",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "phiên dựa trên cookie",
        "log-action-filter-patrol": "Kiểu tuần tra:",
        "log-action-filter-protect": "Loại bảo vệ:",
        "log-action-filter-rights": "Kiểu thay đổi quyền:",
-       "log-action-filter-suppress": "Kiểu ẩn giấu",
+       "log-action-filter-suppress": "Kiểu ẩn giấu:",
        "log-action-filter-upload": "Loại tải lên:",
        "log-action-filter-all": "Tất cả",
        "log-action-filter-block-block": "Khối",
        "linkaccounts-submit": "Liên kết tài khoản",
        "unlinkaccounts": "Gỡ liên kết tài khoản",
        "unlinkaccounts-success": "Đã gỡ liên kết tài khoản.",
-       "authenticationdatachange-ignored": "Tác vụ thay đổi dữ liệu xác thực không được xử lý. Có lẽ nhà cung cấp chưa được cấu hình?"
+       "authenticationdatachange-ignored": "Tác vụ thay đổi dữ liệu xác thực không được xử lý. Có lẽ nhà cung cấp chưa được cấu hình?",
+       "userjsispublic": "Xin lưu ý: Các trang con JavaScript không nên chứa dữ liệu bí mật, vì những người dùng khác có thể xem các trang này.",
+       "usercssispublic": "Xin lưu ý: Các trang con CSS không nên chứa dữ liệu bí mật, vì những người dùng khác có thể xem các trang này."
 }
index fa7133a..71f37b9 100644 (file)
        "yourpasswordagain": "Klavolös dönu letavödi:",
        "createacct-yourpasswordagain": "Fümedolös letavödi",
        "createacct-yourpasswordagain-ph": "Penolös letavödi dönu",
-       "remembermypassword": "Dakipolöd ninädamanünis obik in nünöm at (muiko {{PLURAL:$1|del|dels}} $1)",
        "userlogin-remembermypassword": "Dakipön obi penunädöl",
        "yourdomainname": "Domen olik:",
        "password-change-forbidden": "No kanol votükön letavödis su el wiki at.",
        "minoredit": "Votükam pülik",
        "watchthis": "Galädolöd padi at",
        "savearticle": "Dakipolöd padi",
+       "publishpage": "Dabükön padi",
+       "publishchanges": "Dabükön votükamis",
        "preview": "Büologed",
        "showpreview": "Jonolöd padalogoti",
        "showdiff": "Jonolöd votükamis",
        "undo-norev": "No eplöpos ad sädunön redakami at, bi no dabinon u pämoükon.",
        "undo-summary": "Äsädunon votükami $1 fa [[Special:Contributions/$2|$2]] ([[User talk:$2|Bespikapad]])",
        "undo-summary-username-hidden": "Sädunön revidi: $1 fa geban peklenädöl",
-       "cantcreateaccounttitle": "Kal no kanon pajafön",
        "cantcreateaccount-text": "Kalijaf se ladet-IP at ('''$1''') peblokon fa geban: [[User:$3|$3]].\n\nKod blokama fa el $3 pegivöl binon ''$2''",
        "viewpagelogs": "Jonön jenotalisedis pada at",
        "nohistory": "Pad at no labon redakamajenotemi.",
index a977529..9937693 100644 (file)
        "yourpassword": "Vosse sicret",
        "userlogin-yourpassword": "Sicret",
        "yourpasswordagain": "Ritapez vosse sicret",
-       "remembermypassword": "Rimimbrer m' sicret inte les sessions (nén dpus ki po $1 {{PLURAL:$1|djoû|djoûs}})",
        "yourdomainname": "Vosse dominne",
        "login": "S' elodjî",
        "nav-login-createaccount": "Ahiver on conte, udon-bén s' elodjî",
        "minoredit": "Ci n' est k' ene tchitcheye",
        "watchthis": "Shuve cist årtike",
        "savearticle": "Schaper l' pådje",
+       "savechanges": "Schaper l' pådje",
        "preview": "Vey divant",
        "showpreview": "Vey divant",
        "showdiff": "Vey les candjmints",
        "editwarning-warning": "Cwiter cisse pådje ci vos frè piede tos les candjmints ki vos avoz fwait.\nSi vos estoz elodjî, vos ploz dismete cist adviertixhmint ci dins l' linwete «Boesse di tecse» di vos preferinces.",
        "post-expand-template-inclusion-warning": "'''Asteme:''' I gn a trop di modeles dins cisse pådje ci.\nSacwants di zels ni seront nén eployîs.",
        "post-expand-template-inclusion-category": "Pådjes ki l' inclusion d' modeles est foû limite",
-       "cantcreateaccounttitle": "Vos n' ploz nén ahiver-st on conte.",
        "viewpagelogs": "Vey les djournås po cisse pådje ci",
        "nohistory": "I n' a pont d' istwere des modêyes po cisse pådje chal.",
        "currentrev": "Modêye d' asteure",
        "duration-years": "$1 anêye{{PLURAL:$1||s}}",
        "duration-decades": "$1 dijhinne{{PLURAL:$1||s}} d' anêyes",
        "duration-centuries": "$1 sieke{{PLURAL:$1||s}}",
-       "duration-millennia": "$1 meynaire{{PLURAL:$1||s}}",
-       "api-error-blacklisted": "S' i vs plait, tchoezixhoz èn ôte tite, pus esplicant."
+       "duration-millennia": "$1 meynaire{{PLURAL:$1||s}}"
 }
index a8f32f3..e9877a5 100644 (file)
        "createacct-emailoptional": "电子信地址(填弗填由你)",
        "createacct-email-ph": "畀你侬个电子信地址打进去",
        "createacct-another-email-ph": "电子信地址打进去",
-       "createaccountmail": "用临时随便密码发到指定个电子信地址",
+       "createaccountmail": "拿临时随机密码发到指定个电邮地址",
        "createacct-realname": "真名字(随意)",
        "createaccountreason": "理由:",
        "createacct-reason": "理由:",
        "noemail": "用户\"$1\"弗曾登记电子邮件地址。",
        "noemailcreate": "侬要提供只有效个电子邮件地址",
        "passwordsent": "用户\"$1\"个新密码已经寄往登记个电子邮件地址。\n请收着仔再登录。",
-       "blocked-mailpassword": "侬个IP地址处于查封状态,弗允许编辑,为仔安全起见,密码恢复功能已经禁用。",
+       "blocked-mailpassword": "侬个IP地址处于查封状态,弗允许编辑。为仔防止乱用,箇只IP地址个密码恢复功能已经禁用。",
        "eauthentsent": "一封确认信已经发送到指定个电子邮箱地址。垃拉其他邮件发送到本账号之前,侬必须首先按照箇封信里向个指示,确认箇只邮箱真实有效。",
        "throttled-mailpassword": "密码转设电子信徕最近$1个钟头里发畀你侬哉。保险点,密码转设电子信$1个钟头只一垡好发。",
        "mailerror": "发送邮件错误:$1",
        "passwordreset-emailtitle": "{{SITENAME}}上个账号详细信息",
        "passwordreset-emailelement": "用户名:\n$1\n\n临时密码:\n$2",
        "changeemail": "更改或删脱电子邮箱地址",
-       "changeemail-passwordrequired": "侬需要输入密码来确认本次更改。",
        "changeemail-no-info": "侬必须登录著才好直接进入箇只页面。",
        "changeemail-oldemail": "当前电子邮件地址:",
        "changeemail-newemail": "新个电子邮件地址:",
        "session_fail_preview": "弗好意思!由于会话数据落失,我伲弗好处理侬个编辑。\n\n侬作兴已经登出哉。<strong>请核实侬啊曾登录勒上,再试一遍</strong>。如果仍旧弗来事,请[[Special:UserLogout|登出]]著再重新登录,并确保侬个浏览器允许本站个cookie。",
        "session_fail_preview_html": "弗好意思!因为会话数据落失,我伲弗好处理侬个编辑。\n\n<em>由于{{SITENAME}}允许使用原始个HTML,为著防范JavaScript攻击,预览已经囥脱。</em>\n\n<strong>如果箇是一趟合法个编辑,请再试一遍。</strong>如果仍旧弗来事,请[[Special:UserLogout|登出]]著再重新登录,并确保侬个浏览器允许本站个cookie。",
        "token_suffix_mismatch": "<strong>由于侬用户端里向个编辑令牌毁损仔一些标点符号字元,为防止编辑个文字损坏,侬个编辑已经畀回头。</strong>箇种情况通常出现垃拉使用含有交关bug、以网络为主个匿名代理服务个辰光。",
-       "editing": "来里编写$1",
+       "editing": "来里编写“$1”",
        "creating": "创建“$1”",
-       "editingsection": "来里编辑$1(段落)",
+       "editingsection": "来里编写“$1”(段落)",
        "editingcomment": "垃许编辑 $1 (新段落)",
        "editconflict": "编辑冲突: $1",
        "explainconflict": "有人垃拉侬开始编辑之后更改仔页面。上头个文字框内显示个是箇歇本页个内容。侬个修改显示垃拉下底只文字框里向。侬应当拿侬个修改合并到现有个文本里向。<strong>只有</strong>上头文字框里向个内容会得垃侬点击“{{int:savearticle}}”之后畀保存。",
        "node-count-exceeded-category": "页面个节点数超出限制",
        "parser-unstrip-recursion-limit": "递归超过限制 ($1)",
        "converter-manual-rule-error": "来手动语言转换规则当中查着错误",
-       "undo-success": "箇只编辑可以撤销。\n请检查下头个比较,确定侬确实想撤销,然后保存下底个更改完成撤销编辑。",
+       "undo-success": "箇只编辑可以撤销。请检查下头个比较,确定侬确实想撤销,再保存下底个更改完成撤销编辑。",
        "undo-failure": "由于相互冲突个中途编辑,箇只编辑弗好撤销。",
        "undo-norev": "由于其版本弗存在或已删除,此编辑弗好撤销。",
        "undo-nochange": "箇届编辑看出来已经畀撤销。",
        "undo-summary": "撤销由[[Special:Contributions/$2|$2]]([[User talk:$2|讨论]])作出个版本$1",
-       "cantcreateaccounttitle": "弗好开户",
        "cantcreateaccount-text": "从箇只IP地址 (<b>$1</b>) 创建账户已经畀[[User:$3|$3]]禁止。\n\n$3封禁个原因是''$2''",
        "viewpagelogs": "望箇页日志",
        "nohistory": "该只页面呒不编辑历史。",
        "rightslog": "用户权限日志",
        "action-read": "讀箇頁",
        "action-edit": "编辑箇只页面",
-       "action-createpage": "å\81\9aæ\96°é \81",
-       "action-createtalk": "å\81\9aè¨\8eè«\96é \81",
+       "action-createpage": "建ç«\8b该å\8fªé¡µé\9d¢",
+       "action-createtalk": "建ç«\8b该å\8fªè®¨è®ºé¡µ",
        "action-minoredit": "標小編寫",
        "action-move": "移箇頁",
        "action-move-subpages": "移箇頁搭兒頁",
        "enhancedrc-history": "歷史",
        "recentchanges": "近段辰光个改动",
        "recentchanges-legend": "近段辰光个改动选项",
-       "recentchanges-summary": "登该个页面浪跟踪最近对维基百科个改动。",
+       "recentchanges-summary": "登该个页面浪跟踪最近对本站个改动。",
        "recentchanges-feed-description": "跟踪此订阅垃拉 wiki 高头个最近更改。",
        "recentchanges-label-newpage": "箇编辑建立着新页",
        "recentchanges-label-minor": "箇是小编写",
        "rc-enhanced-hide": "拿细节囥脱",
        "recentchangeslinked": "搭界个改动",
        "recentchangeslinked-feed": "搭界个改动",
-       "recentchangeslinked-toolbox": "相关变化",
+       "recentchangeslinked-toolbox": "搭界个改动",
        "recentchangeslinked-title": "搭“$1”有关个改动",
        "recentchangeslinked-summary": "箇页列出个是对链到某只指定页面个页面近段辰光个修订(或者是对指定分类个成员)。\n徕[[Special:Watchlist|你侬个关注表]]里个页用'''粗体'''显示。",
        "recentchangeslinked-page": "页面名称:",
        "undelete-search-submit": "搜尋",
        "namespace": "名字空间:",
        "invert": "反选择",
-       "tooltip-invert": "æ\89\93ä¸\8aæ\89\8eå\8b¾æ\9d¥å\9b¥è\84±é\80\89å®\9aå\90\8då­\97空é\97´ä¸ªæ\94¹å\8a¨ï¼\88å¦\82æ\9e\9cå\8b¾é\80\89æ\9c\89å\85³名字空间,箇么一道囥脱)",
-       "namespace_association": "æ\9c\89å\85³个名字空间",
+       "tooltip-invert": "æ\89\93ä¸\8aæ\89\8eå\8b¾æ\9d¥å\9b¥è\84±é\80\89å®\9aå\90\8då­\97空é\97´ä¸ªæ\94¹å\8a¨ï¼\88å¦\82æ\9e\9cå\8b¾é\80\89æ\90­ç\95\8c个名字空间,箇么一道囥脱)",
+       "namespace_association": "æ\90­ç\95\8c个名字空间",
        "tooltip-namespace_association": "打上扎勾来加上搭选定名字空间搭界个讨论或主题名字空间",
        "blanknamespace": "(主)",
        "contributions": "{{GENDER:$1|用户}}贡献",
        "year": "从箇年往前:",
        "sp-contributions-newbies": "只显示新用户个贡献",
        "sp-contributions-blocklog": "查封记录",
-       "sp-contributions-deleted": "删脱个用户贡献",
+       "sp-contributions-deleted": "删脱个{{GENDER:$1|用户}}贡献",
        "sp-contributions-talk": "讲张",
        "sp-contributions-search": "寻贡献记录",
        "sp-contributions-username": "IP地址要勿用户名:",
+       "sp-contributions-toponly": "只显示阿末只版本个编辑",
        "sp-contributions-submit": "搜寻",
-       "whatlinkshere": "有啥链到箇里",
+       "whatlinkshere": "链进来点啥",
        "whatlinkshere-title": "链接到“$1”个页面",
        "whatlinkshere-page": "页面:",
        "linkshere": "下头个页链到[[:$1]]:",
-       "nolinkshere": "呒有页链到 '''[[:$1]]'''。",
+       "nolinkshere": "呒不页面链到<strong>[[:$1]]</strong>。",
        "isredirect": "转戳页",
        "istemplate": "包括",
        "isimage": "文件鏈接",
        "tooltip-ca-nstab-category": "望分类页",
        "tooltip-minoredit": "标作小编写",
        "tooltip-save": "保存侬个修改",
+       "tooltip-publish": "发布侬个改动",
        "tooltip-preview": "预览倷个更改。请勒拉保存前头用用俚。",
        "tooltip-diff": "显示侬对文本个修改",
        "tooltip-compareselectedversions": "查看本页面两只选定个修订版个差异。",
index 8f2c434..803c6a2 100644 (file)
        "yourpasswordagain": "ווידער אריינקלאפן פאסווארט",
        "createacct-yourpasswordagain": "באשטעטיקן פאסווארט",
        "createacct-yourpasswordagain-ph": "ארײַנגעבן פאסווארט נאכאמאל",
-       "remembermypassword": "געדענקען מיין אַריינלאָגירן אין דעם דאָזיקן בראַוזער (ביז $1 {{PLURAL:$1|טאָג|טעג}})",
        "userlogin-remembermypassword": "לאז מיך בלײַבן ארײַנלאגירט",
        "userlogin-signwithsecure": "ניצן זיכערן סארווער",
        "cannotloginnow-title": "קען נישט אריינלאגירן אצינד",
        "password-change-forbidden": "איר קען נישט ענדערן פאסווערטער אויף דער וויקי.",
        "externaldberror": "עס איז אדער פארגעקומען אן אויטענטיקאציע דאטנבאזע פֿעלער אדער איר זענט נישט ערמעגליכט צו דערהיינטיגן אייער דרויסנדיגע קאנטע.",
        "login": "אַרײַנלאָגירן",
+       "login-security": "וועריפיצירט אייער אידענטיטעט",
        "nav-login-createaccount": "ארײַנלאָגירן / זיך אײַנשרײַבן",
        "userlogin": "ארײַנלאָגירן / זיך אײַנשרײַבן",
        "userloginnocreate": "אַרײַנלאגירן",
        "userlogin-resetpassword-link": "פֿאַרגעסן אײַער פאַסווארט?",
        "userlogin-helplink2": "הילף מיט ארײַנלאגירן",
        "userlogin-loggedin": "איר זענט שוין אריינלאגירט ווי {{GENDER:$1|$1}}.\nניצט די פארעם אונטן כדי אריינלאגירן ווי אן אנדער באניצער.",
+       "userlogin-reauth": "איר דארפט נאכאמאל אריינלאגירן צו באשטעטיקן אז איר זענט {{GENDER:$1|$1}}.",
        "userlogin-createanother": "שאפֿן נאך א קאנטע",
        "createacct-emailrequired": "בליצפּאָסט אַדרעס",
        "createacct-emailoptional": "בליצפאסט אדרעס (אפציאנאל)",
        "createaccountreason": "אורזאַך:",
        "createacct-reason": "אורזאך",
        "createacct-reason-ph": "פֿארוואס שאפֿט איר נאך א קאנטע",
+       "createacct-reason-help": "מעלדונג געוויזן אין קאנטע־שאפֿונג לאגבוך",
        "createacct-submit": "שאפֿט אײַער קאנטע",
        "createacct-another-submit": "שאַפֿן קאנטע",
+       "createacct-continue-submit": "פֿארטזעצן שאפֿן קאנטע",
+       "createacct-another-continue-submit": "פֿארטזעצן שאפֿן קאנטע",
        "createacct-benefit-heading": "{{SITENAME}} איז געמאכט דורך מענטשן ווי איר.",
        "createacct-benefit-body1": "{{PLURAL:$1|רעדאַקטירונג|$1 רעדאַקטירונגען}}",
        "createacct-benefit-body2": "$1 {{PLURAL:$1|בלאַט|בלעטער}}",
        "changepassword-success": "אייער פאַסווארט איז געטוישט געווארן!",
        "changepassword-throttled": "איר האט געפרוווט צופֿיל מאל אריינלאגירן.\nזייט אזוי גוט און וואַרט $1 איידער איר פרוווט נאכאמאל.",
        "botpasswords": "באט פאסווערטער",
+       "botpasswords-disabled": "באט פאסווערטער זענען אומאקטיווירט.",
+       "botpasswords-existing": "עקזיסטירנדע באט פאסווערטער",
+       "botpasswords-createnew": "שאפֿן א ניי באט פאסווארט",
        "botpasswords-label-appid": "באט נאמען:",
        "botpasswords-label-create": "שאַפֿן",
        "botpasswords-label-update": "דערהײַנטיקן",
        "botpasswords-label-cancel": "אַנולירן",
        "botpasswords-label-delete": "אויסמעקן",
        "botpasswords-label-resetpassword": "ווידערשטעלן פאַסווארט",
+       "botpasswords-label-restrictions": "באניץ באגרענעצונגען:",
        "botpasswords-label-grants-column": "נאכגעגעבן",
+       "botpasswords-bad-appid": "דער באט נאמען \"$1\" איז אומגילטיק.",
        "botpasswords-created-title": "באט פאסווארט געשאפן",
        "botpasswords-created-body": "דאס באט פאסווארט פאר באט־נאמען \"$1\" פון באניצער \"$2\" איז געשאפן געווארן.",
        "botpasswords-updated-title": "באט פאסווארט דערהיינטיקט",
        "passwordreset-emailelement": "באַניצער נאָמען: \n$1\n\nפראוויזארישער פּאַראָל: \n$2",
        "passwordreset-emailsentemail": "טאמער איז דער ע־פאסט אדרעס פארקניפט מיט אייער קאנטע, וועט מען שיקן א פאסווארט צוריקשטעלן ע-פּאָסט.",
        "passwordreset-emailsentusername": "טאמער איז פאראן אן ע־פאסט אדרעס פארקניפט מיט דעם באניצער־נאמען, וועט מען שיקן א פאסווארט צוריקשטעלן ע-פּאָסט.",
-       "passwordreset-emailsent-capture": "מען האט געשיקט א פאסווארט צוריקשטעלן בליצבריוו, וואס ווערט געוויזן אונטן.",
-       "passwordreset-emailerror-capture": "מען האט געשאפן א פאסווארט צוריקשטעלן בליצבריוו, וואס ווערט געוויזן אונטן, אבער שיקן צום {{GENDER:$2|באניצער}}איז דורכגעפאלן: $1",
        "passwordreset-nocaller": "מען דארף פֿארזארגן א רופֿער",
+       "passwordreset-nosuchcaller": "רופֿער איז נישט פֿאראן: $1",
        "passwordreset-invalideamil": "אומגילטיקער ע־פאסט אדרעס",
        "changeemail": "ענדערן אדער אראפנעמען ע-פּאָסט אַדרעס",
        "changeemail-header": "דערגאַנצט די פֿאָרעם צו ענדערן אייער ע-פּאָסט אַדרעס .\nטאמער ווילט איר אראפנעמען די צוארדנונג פון איינעם פון אייערע ע־פאסט אדרעסן פו אייער קאנטע, לאזט ליידיג דעם נייעם ע־פאסט אדרעס ווען איר גיט איין די פֿארעם.",
-       "changeemail-passwordrequired": "איר וועט דארפן איינגעבן אייער פאסווארט צו באשטעטיגן די ענדערונג.",
        "changeemail-no-info": "איר דאַרפֿט זיין אַרײַנלאגירט צוצוקומען גלײַך צו דעם דאָזיגן בלאַט.",
        "changeemail-oldemail": "קראַנטער ע-פּאָסט אַדרעס:",
        "changeemail-newemail": "נײַער בליצפּאָסט אַדרעס:",
        "minoredit": "דאס איז א מינערדיגע ענדערונג",
        "watchthis": "טוט אױפֿפּאַסן דעם בלאט",
        "savearticle": "אויפהיטן בלאַט",
-       "publishpage": "פובליצירן בלאט",
+       "savechanges": "אויפֿהיטן ענדערונגען",
+       "publishpage": "פובליקירן בלאַט",
+       "publishchanges": "פובליקירן ענדערונגען",
        "preview": "פֿאראויסקוק",
        "showpreview": "ווייזן פאָרױסקוק",
        "showdiff": "ווײַז די ענדערונגען",
        "content-json-empty-object": "ליידיגער אביעקט",
        "content-json-empty-array": "ליידיגער אריי",
        "duplicate-args-warning": "<strong>ווארענונג:</strong> [[:$1]] רופט [[:$2]] מיט מער ווי איין ווערט פארן פאראמעטער \"$3\". נאר דעם לעצטן ווערט וועט מען ניצן.",
-       "duplicate-args-category": "×\91×\9c×¢×\98ער ×\95×\95×\90ס × ×\99צ×\9f ×\92×¢×\98×\90פ×\9c×\98×¢ ×\90ר×\92×\95×\9e×¢× ×\98×\9f ×\90×\99×\9f ×\9e×\95ס×\98ער ×¨×\95פ×\9f",
+       "duplicate-args-category": "×\91×\9c×¢×\98ער ×\95×\95×\90ס × ×\99צ×\9f ×\92×¢×\98×\90פ×\9c×\98×¢ ×\90ר×\92×\95×\9e×¢× ×\98×\9f ×\90×\99×\9f ×\90 ×\9e×\95ס×\98ער־ר×\95×£",
        "duplicate-args-category-desc": "דער בלאט אנטהאלט מוסטער־אויפרופן וואס ניצן דופליקאטן פון ארגומענטן, ווי למשל <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> or <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "'''אזהרה:''' דער בלאט אנטהאלט צופיל טייערע פארזירער רופן.\n\nער דארף האבן ווינציגער פון  $2 {{PLURAL:$2|רוף|רופן}}, אבער אצינד {{PLURAL:$1|איז דא $1 רוף|זענען דא $1 רופן}}.",
        "expensive-parserfunction-category": "בלעטער מיט צופֿיל טייערע פאַרזער פֿונקציאן רופֿן",
        "undo-nochange": "אויבערפלעכטלעך איז די רעדאקטירונג שוין געווארן אנולירט.",
        "undo-summary": "זיי מבטל רי-ווערסיע $1 פון [[Special:Contributions/$2|$2]] ([[User talk:$2|רעדן]])",
        "undo-summary-username-hidden": "זײַט מבטל ווערסיע $1 פון א באהאלטענעם באניצער",
-       "cantcreateaccounttitle": "מען קען נישט באשאפֿן קאנטע",
        "cantcreateaccount-text": "שאפֿן קאנטעס פון דעם IP אדרעס (<b>$1</b>) איז געווארן בלאקירט דורך [[User:$3|$3]]. די סיבה געגעבן פֿון $3 איז \"$2\".",
        "cantcreateaccount-range-text": "שאפן קאנטעס פון IP אדרעסן אינעם אפשטאנד <strong>$1</strong>, וואס נעמט אײַן אײַער IP אדרעס (<strong>$4</strong>), איז בלאקירט געווארן דורך [[User:$3|$3]].\n\nדער גרונט געגעבן פון $3 איז <em>$2</em>",
        "viewpagelogs": "װײַזן לאָג-ביכער פֿאַר דעם בלאַט",
        "right-passwordreset": "באַקוקן פאַסווארט צוריקשטעלן ע־בריוו",
        "right-managechangetags": "שאפן און (אומ)אקטיווירן [[Special:Tags|טאגן]]",
        "right-applychangetags": "אנווענדן [[Special:Tags|טאגן]] צוזאמען מיט ענדערונגען",
+       "grant-generic": "\"$1\" רעכטן־בינטל",
        "grant-group-page-interaction": "אינטעראגירן מיט בלעטער",
        "grant-group-file-interaction": "אינטעראגירן מיט מעדיע",
+       "grant-group-watchlist-interaction": "אינטעראגירן מיט אייער אויפֿפאסונג־ליסטע",
        "grant-group-email": "שיקן ע־פאסט",
        "grant-createaccount": "שאַפֿן קאנטעס",
        "grant-editmywatchlist": "רעדאקטירן אײַער אויפֿפאסונג ליסטע",
        "undeletedrevisions": "{{PLURAL:$1|1 רעוויזיע|$1 רעוויזיעס}} צוריקגעשטעלט",
        "undeletedrevisions-files": "{{PLURAL:$1|1 רעוויזיע|$1 רעוויזיעס}} און  {{PLURAL:$2|1 טעקע|$2 טעקעס}} צוריקגעשטעלט",
        "undeletedfiles": "{{PLURAL:$1|1 טעקע|$1 טעקעס}} צוריקגעשטעלט",
-       "cannotundelete": "צוריקשטעלונג איז דורכגעפאלן: $1",
+       "cannotundelete": "×\98×\99×\99×\9c ×\90×\93ער ×\92×\90רע ×¦×\95ר×\99קש×\98×¢×\9c×\95× ×\92 ×\90×\99×\96 ×\93×\95ר×\9b×\92עפ×\90×\9c×\9f: $1",
        "undeletedpage": "'''דער בלאט $1 איז געווארן צוריקגעשטעלט.'''\n\nזעט דעם [[Special:Log/delete| אויסמעקן לאג]] פֿאר א ליסטע פון די לעצטע אויסגעמעקטע און צוריקגעשטעלטע בלעטער.",
        "undelete-header": "זעט [[Special:Log/delete|דעם אויסמעקונג זשורנאַל]] פֿאַר בלעטער וואָס זענען לעצטנס געווארן אויסגעמעקט recently deleted pages.",
        "undelete-search-title": "זוכן אויסגעמעקטע בלעטער",
        "sp-contributions-newbies-sub": "פאר נייע קאנטעס",
        "sp-contributions-newbies-title": "ביישטייערונגען פון נייע באַניצער",
        "sp-contributions-blocklog": "בלאקירן לאג",
-       "sp-contributions-suppresslog": "אונטערדריקטע באַניצער בײַשטײַערונגען",
+       "sp-contributions-suppresslog": "אונטערדריקטע {{GENDER:$1|באַניצער}} בײַשטײַערונגען",
        "sp-contributions-deleted": "אויסגעמעקטע באַניצער בײַשטײַערונגען",
        "sp-contributions-uploads": "אַרויפֿלאָדונגען",
        "sp-contributions-logs": "לאגביכער",
        "tooltip-ca-nstab-category": "באקוקט דעם קאטעגאריע בלאט",
        "tooltip-minoredit": "באצייכענען דאס אלס מינערדיגע ענדערונג",
        "tooltip-save": "אויפֿהיטן אייערע ענדערונגען",
+       "tooltip-publish": "פובליקירן אייערע ענדערונגען",
        "tooltip-preview": "פֿארויסדיגע ווײַזונג, זײַט אזוי גוט באניצט די געלעגנהייט פֿארן אויפֿהיטן!",
        "tooltip-diff": "ווײַזן אייערע ענדערונגען צום טעקסט",
        "tooltip-compareselectedversions": "פארגלײַכם די צוויי ווערסיעס פון דעם בלאט",
        "log-action-filter-upload": "טיפ ארויפֿלאד:",
        "log-action-filter-all": "אַלע",
        "log-action-filter-delete-delete": "אויסמעקן בלאט",
+       "log-action-filter-import-interwiki": "אריבערוויקי אימפארט",
        "log-action-filter-protect-unprotect": "אראפנעמען שיץ"
 }
index d65b7c8..73f4629 100644 (file)
@@ -7,7 +7,8 @@
                        "Urhixidur",
                        "아라",
                        "Macofe",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Wikicology"
                ]
        },
        "tog-underline": "Ìfàlàsábẹ́ àwọn àjápọ̀:",
        "otherlanguages": "Àwọn èdè míràn",
        "redirectedfrom": "(Àtúnjúwe láti $1)",
        "redirectpagesub": "Ojúewé àtúnjúwe",
+       "redirectto": "Àtúnjúwe sí:",
        "lastmodifiedat": "Àtunṣe ojúewé yi gbẹ̀yìn wáyé ni ago $2, ọjọ́ọdún $1.",
        "viewcount": "A ti wo ojúewé yi ni {{PLURAL:$1|ẹ̀kan péré|iye ìgbà $1}}.",
        "protectedpage": "Ojúewé oníàbò",
        "nstab-template": "Àdàkọ",
        "nstab-help": "Ojúewé ìrànlọ́wọ́",
        "nstab-category": "Ẹ̀ka",
+       "mainpage-nstab": "Ojúewé Àkọ́kọ́",
        "nosuchaction": "Kò sí irú ìgbéṣe báun",
        "nosuchactiontext": "Ìgbéṣe tí URL yìí tọ́kasí kò tọ́.\nÓ ṣe é ṣe kó jẹ́ pé ẹ ṣe àṣìṣe URL ọ̀hún, tàbí kó jẹ́ pé ẹ tẹ̀lé ìjápọ̀ tí kò tọ́.\nÓ sì le jẹ́ pé kòkòrò wà nínú software tí {{SITENAME}} nlò.",
        "nosuchspecialpage": "Kò sí irú ojúewé pàtàkì báun",
        "gotaccountlink": "Ẹ w'ọlé",
        "userlogin-resetlink": "À bí ẹ gbàgbé ìwọlé yín?",
        "userlogin-resetpassword-link": "Ṣé ẹ ti gbàgbé ọ̀rọ̀ìpamọ́ yín?",
+       "userlogin-helplink2": "Ìrànlọ́wọ́ láti wọlé",
        "userlogin-loggedin": "Ẹ pilẹ̀ ti jáwọlé bíi {{GENDER:$1|$1}}.\nẸ lo fọ́ọ̀mù ìsàlẹ̀ látí jáwọlé gẹ́gẹ́ bíi oníṣe míràn.",
        "userlogin-createanother": "Ìdá àkópamọ́ míràn",
        "createacct-emailrequired": "Àdírẹ̀sì email",
        "passwordreset-emailtext-user": "Oníṣe $1 lórí {{SITENAME}} tọrọ àtúntò ọ̀rọ̀ìpamọ́ yín fùn {{SITENAME}} ($4). {{PLURAL:$3|Àkópamọ́|Àwọn àkópamọ́}} oníṣe ìsàlẹ̀ yìí ní ìbáṣe pọ̀ mọ́ àdírẹ̀sì e-mail yìí:\n\n$2\n\n{{PLURAL:$3|Ọ̀rọ̀ìpamọ́ onígbàdíẹ̀ yìí|Àwọn ọ̀rọ̀ìpamọ́ onígbàdíẹ̀ wọ̀nyí}} yíò dópin lẹ́yìn {{PLURAL:$5|ọjọ́ kan|ọjọ́ $5}}.\nẸ gbọ́dọ̀ lọ yan ọ̀rọ̀ìpamọ́ tuntun báyìí. Tóbá jẹ́ pé ẹ̀lòmíràn ló ṣe ìtọrọ yìí, tàbí tọ́bá jẹ́ pé ẹ ti rántí ọ̀rọ̀ìpamọ́ àtètèkọ́ṣe yín, tí ẹ kọ̀ sí fẹ́ yíipadà mọ́, ẹ lé ṣàìkàsí ìránṣẹ́ yìí, kí ẹ sì tẹ̀síwájú ní lo ọ̀rọ̀ìpamọ́ àtijọ́ yín.",
        "passwordreset-emailelement": "Orúkọ oníṣe: \n$1\n\nỌ̀rọ̀ìpamọ́ ìgbàdíẹ̀: \n$2",
        "passwordreset-emailsentemail": "E-mail àtúntò ọ̀rọ̀ìpamọ́ ti jẹ́ fífiránṣẹ́.",
-       "passwordreset-emailsent-capture": "E-mail àtúntò ọ̀rọ̀ìpamọ́ kan ti jẹ́ fífiránṣẹ́. Òhun nìyí nísàlẹ̀.",
-       "passwordreset-emailerror-capture": "E-mail ìyípadà ọ̀rọ̀ìpamọ́ jẹ́ dídá, òhun lóhàn nísàlẹ̀ yìí, sùgbọ́n ìfiránṣẹ́ rẹ̀ sí {{GENDER:$2|oníṣe}} náà kùnà: $1",
        "changeemail": "Ìyípadà àdírẹ̀sì E-mail",
        "changeemail-header": "Ìyípadà àdírẹ̀sì e-mail àkópamọ́",
        "changeemail-no-info": "Ẹ gbódọ̀ wọlé láti bósí ojúewé yìí tààrà.",
        "subject": "Orí ọ̀rọ̀/àkọlé:",
        "minoredit": "Àtúnṣe kékeré nìyí",
        "watchthis": "M'ójútó ojúewé yìí",
-       "savearticle": "Ìmúpamọ́ ojúewé",
+       "savearticle": "Ìdásí ojúewé",
+       "publishpage": "Ṣàtẹ̀jáde ojú ewé",
+       "publishchanges": "Ṣàtẹ̀jáde àtúnṣe",
        "preview": "Àyẹ̀wò",
        "showpreview": "Àkọ́yẹ̀wò",
        "showdiff": "Ìfihàn àwọn àtúnṣe",
        "undo-failure": "Àtúnṣe náà kò ṣe é múkúrò nítorí títakora àwọn àtúnṣe inú àrin.",
        "undo-norev": "Àtúnṣe náà kò ṣe é múkúrò nítorí pé kò sí tàbí pé ó ti jẹ́ píparẹ́.",
        "undo-summary": "Ìmúkúrò àtúnyẹ̀wò $1 ti [[Special:Contributions/$2|$2]] ([[User talk:$2|ọ̀rọ̀]])",
-       "cantcreateaccounttitle": "Ìforúkọsílẹ̀ kò se é se",
        "cantcreateaccount-text": "[[User:$3|$3]] ti dènà dídá àkópamọ́ láti orí àdírẹ́ẹ̀sì IP yìí ('''$1''').\n\nÌdí tí $3 ṣe ṣèyí ni ''$2''",
        "viewpagelogs": "Ẹ wo àkọsílẹ̀ ìṣẹ̀lẹ̀ fún ojúewé yìí",
        "nohistory": "Kò sí ìtàn àtùnṣe fún ojúewé yìí.",
        "shown-title": "{{PLURAL:$1|Ìfihàn èsì $1|Ìfihàn àwọn èsì $1}} nínú ojúewé kọ̀ọ̀kan",
        "viewprevnext": "Ẹ wo ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "'''Ojúewé tó ún jẹ́ \"[[:$1]]\" wà lórí wiki yìí'''",
-       "searchmenu-new": "'''Dá ojúewé \"[[:$1]]\" sí orí wiki yìí!'''",
+       "searchmenu-new": "<strong>Dá ojúewé \"[[:$1]]\" sí orí wiki yìí!</strong> {{PLURAL:$2|0=|Wo ojúewé tí a wá rí pẹ̀lú ìṣewárí rẹ.|Wo àwọn èsì ìṣewárí tí a rí.}}",
        "searchprofile-articles": "Àwọn ojúewé Àkóónú",
        "searchprofile-images": "Amóhùnmáwòrán",
        "searchprofile-everything": "Èyíkéyìí",
        "searchrelated": "tóbáramu",
        "searchall": "gbogbo",
        "showingresults": "Ìfihàn nísàlẹ̀ títí dé {{PLURAL:$1|èsì '''1'''|àwọn èsì '''$1'''}} láti ìbẹ̀rẹ̀ ní #'''$2'''.",
+       "search-showingresults": "{{PLURAL:$4|Èsì <strong>$1</strong> fún <strong>$3</strong>|Àwọn èsì <strong>$1 - $2</strong> fún <strong>$3</strong>}}",
        "search-nonefound": "Kò sí àwọn èsì kankan tóbáramu mọ́ ìtọrọ.",
        "powersearch-legend": "Àwárí kíkúnrẹ́rẹ́",
        "powersearch-ns": "Àwárí nínú orúkọàyè:",
        "rcnotefrom": "Àwọn àtúnṣe láti ''''$2''' (títí dé '''$1''' hàn) lábẹ́.",
        "rclistfrom": "Àfihàn àwọn àtúnṣe tuntun nípa bíbẹ̀rẹ̀ láti $3 $2",
        "rcshowhideminor": "$1 àwọn àtúnṣe kékéèké",
+       "rcshowhideminor-show": "Fi hàn",
        "rcshowhideminor-hide": "Ìbòmọ́lẹ̀",
        "rcshowhidebots": "$1 àwọn bot",
        "rcshowhidebots-show": "Ìfihàn",
+       "rcshowhidebots-hide": "Fi pamọ́",
        "rcshowhideliu": "$1 àwọn oníṣe aforúkọsílẹ̀",
        "rcshowhideliu-hide": "Ìbòmọ́lẹ̀",
        "rcshowhideanons": "$1 àwọn oníṣe aláìlórúkọ",
+       "rcshowhideanons-show": "Fi hàn",
        "rcshowhideanons-hide": "Ìbòmọ́lẹ̀",
        "rcshowhidepatr": "$1 àwọn àtúnṣe ọlùṣọ́",
        "rcshowhidemine": "$1 àwọn àtúnṣe mi",
+       "rcshowhidemine-show": "Fi hàn",
        "rcshowhidemine-hide": "Ìbòmọ́lẹ̀",
        "rclinks": "Ṣ'àfihàn àtúnṣe $1 tó kẹ̀yìn ní ọjọ́ $2 sẹ́yìn<br />$3",
        "diff": "ìyàtọ̀",
        "querypage-disabled": "Ojúewé pàtàkì yìí jẹ́ ìdálẹ́kun nítorí ìsiṣẹ́.",
        "booksources": "Àwọn orísun ìwé",
        "booksources-search-legend": "Àwáàrí fún áwọn ìwé ìtọ́ka",
+       "booksources-search": "Ṣàwárí",
        "booksources-text": "Nísàlẹ̀ ni àtòjọ àwọn àjápọ̀ mọ́ àwọn ibiìtakùn míràn tí wọ́n únta ìwé tuntun àti ìwé àtijọ́, wọ́n sì le ní ọ̀rọ̀ ẹ̀kúnrẹ́rẹ́ nípa àwọn ìwé tí ẹ únwá:",
        "booksources-invalid-isbn": "ISBN náà kò dà bíi pé ó jẹ́ oníìbámu; ẹ yẹ̀ ẹ́ wò bóyá àsìṣe wà láti ibi tó jẹ́ kíkọ wá.",
        "specialloguserlabel": "Olùṣe:",
        "contributions": "Àwọn àfikún {{GENDER:$1|oníṣe}}",
        "contributions-title": "Àwọn àfikún oníṣe fún $1",
        "mycontris": "Àwọn àfikún",
+       "anoncontribs": "Àwọn àfikún",
        "contribsub2": "Fún {{GENDER:$3|$1}} ($2)",
        "nocontribs": "Kò sí àtúnṣe tuntun tó bá àwárí mu.",
        "uctop": "(lówọ́)",
        "whatlinkshere-links": "← àwọn ìjápọ̀",
        "whatlinkshere-hideredirs": "$1 àtúnjúwe",
        "whatlinkshere-hidetrans": "$1 ìkómọ́ra",
-       "whatlinkshere-hidelinks": "$1 ìjápọ̀",
+       "whatlinkshere-hidelinks": "Ìjápọ̀ $1",
        "whatlinkshere-hideimages": "$1 àwọn ìjápọ̀ fáìlì",
        "whatlinkshere-filters": "Ajọ̀",
        "autoblockid": "Ìdínàaláraẹni #$1",
        "import-logentry-interwiki-detail": "{{PLURAL:$1|Àtúnyẹ̀wò|Àwọn àtúnyẹ̀wò}} $1 láti $2",
        "javascripttest": "Ìdánwò JavaScript",
        "javascripttest-qunit-intro": "Ẹ wo [$1 ìwé aṣàlàyé ìdánwò] ní mediawiki.org.",
-       "tooltip-pt-userpage": "Ojúewé oníṣe yín",
+       "tooltip-pt-userpage": "Ojúewé oníṣe rẹ",
        "tooltip-pt-anonuserpage": "Ojúewé oníṣe fún àdírẹ́ẹ̀sì IP tí ẹ únlò láti ṣàtúnṣe",
-       "tooltip-pt-mytalk": "Ojúewé ọ̀rọ̀ yín",
+       "tooltip-pt-mytalk": "Ojúewé ọ̀rọ̀ {{GENDER:|rẹ}}",
        "tooltip-pt-anontalk": "Ọ̀rọ̀ nípa àtúnṣe láti àdírẹ́ẹ̀sì IP yìí",
-       "tooltip-pt-preferences": "Àwọn ìfẹ́ràn mi",
+       "tooltip-pt-preferences": "Àwọn ìfẹ́ràn rẹ",
        "tooltip-pt-watchlist": "Àkójọ àwọn ojúewé tí ẹ̀ ún mójútó bóyá wọ́nyí padà",
-       "tooltip-pt-mycontris": "Àkójọ àwọn àfikún yín",
+       "tooltip-pt-mycontris": "Àtójọ àwọn àfikún {{GENDER:|rẹ}}",
        "tooltip-pt-login": "A gbà yín níyànjú kí ẹwọlé, bótilẹ̀jẹ́pẹ́ kò pọndandan.",
        "tooltip-pt-logout": "Ìjáde",
        "tooltip-pt-createaccount": "Ó dára kí ẹ dá àkópamọ́ kí ẹ sì ṣe ìtẹ̀jáwọlé, ṣùgbọ́n kò pọn dandan",
        "tooltip-ca-talk": "Ìfọ̀rọ̀wérọ̀ nípa ohun inú ojúewé yìí",
-       "tooltip-ca-edit": "Ẹ le ṣe àtúnṣe sí ojúewé yìí.\nẸ jọ̀wọ́ ẹ lo bọtini àyẹ̀wò kí ẹ tó fipamọ́.",
+       "tooltip-ca-edit": "S'àtúnṣe ojúewé yi",
        "tooltip-ca-addsection": "Ẹ bẹ̀rẹ̀ abẹlẹ tuntun",
        "tooltip-ca-viewsource": "Àbò wà lórí ojúewé yìí.\nẸ le wo àmìọ̀rọ̀ rẹ̀.",
        "tooltip-ca-history": "Àwọn àtúnṣe tókọjá sí ojúewé yìí",
        "tooltip-t-recentchangeslinked": "Àwọn àtúnṣe tuntun nínú àwọn ojúewé tójápọ̀ láti inú ojúewé yìí",
        "tooltip-feed-rss": "RSS feed fùn ojúewé yìí",
        "tooltip-feed-atom": "Atom feed fún ojúewé yìí",
-       "tooltip-t-contributions": "Ẹ wo àkójọ àwọn àfikún oníṣe yìí",
+       "tooltip-t-contributions": "Àtòjọ àwọn àfikún {{GENDER:$1|oníṣe yìí}}",
        "tooltip-t-emailuser": "Ẹ fi e-mail ránṣẹ́ sí oníṣe yìí",
        "tooltip-t-upload": "Ìrùsókè àwọn fáìlì",
        "tooltip-t-specialpages": "Àkójọ gbogbo àwọn ojúewé pàtàkì",
        "tooltip-ca-nstab-main": "Ìfihàn inú ojúewé",
        "tooltip-ca-nstab-user": "Ẹ wo ojúewé oníṣe",
        "tooltip-ca-nstab-media": "Ẹ wò ojúewé amóhùnmáwòrán",
-       "tooltip-ca-nstab-special": "Ojúewé yìí ṣe pàtàkì, ẹ kò le è ṣ'àtúnṣe rẹ̀",
+       "tooltip-ca-nstab-special": "Ojúewé pàtàkì nì yí, kò le è ṣeẹ́túnṣe",
        "tooltip-ca-nstab-project": "Ẹ wo ojúewé iṣẹ́ọwọ́",
        "tooltip-ca-nstab-image": "Ẹ wo ojúewé faili",
        "tooltip-ca-nstab-mediawiki": "Iwo ìránṣẹ́ sístẹ́mù",
        "revdelete-uname-unhid": "orúkọ oníṣe kò pamọ́",
        "revdelete-restricted": "ṣe ìmúlò ìpàlà fún àwọn olúmójútó",
        "revdelete-unrestricted": "yọ ìpàlà fún àwọn olúmójútó",
-       "logentry-move-move": "$1 ṣeyípòdà ojúewé $3 sí $4",
+       "logentry-move-move": "$1 {{GENDER:$2|ṣeyípòdà}} ojúewé $3 sí $4",
        "logentry-move-move-noredirect": "$1 ṣeyípòdà ojúewé $3 sí $4 láìfi àtúnjúwe sílẹ̀",
        "logentry-move-move_redir": "$1 ṣeyípòdà ojúewé $3 sí $4 lórí àtúnjúwe",
        "logentry-move-move_redir-noredirect": "$1 ṣeyípòdà ojúewé $3 sí $4 lórí àtúnjúwe láìfi àtúnjúwe sílẹ̀",
        "logentry-rights-rights": "$1 yí ìjọ́mọ ẹgbẹ́ padà fún $3 láti $4 sí $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|yí}} ọmọ ẹgbẹ́ padà fún $3",
        "logentry-rights-autopromote": "$1 jẹ́ {{GENDER:$2|gbígbénípòga}} nífúnraẹni láti $4 sí $5",
+       "logentry-upload-upload": "Ó dàfikún",
        "rightsnone": "(kòsí)",
        "revdelete-summary": "àkótán àtúnṣe",
        "feedback-adding": "Ìfikún ìdáhùn sí ojúewé...",
index 61bf918..84ec923 100644 (file)
@@ -26,7 +26,8 @@
                        "Yueman",
                        "CRCHF",
                        "Shinjiman",
-                       "Macofe"
+                       "Macofe",
+                       "Jdforrester"
                ]
        },
        "tog-underline": "連結加底線:",
        "yourpasswordagain": "再輸入密碼:",
        "createacct-yourpasswordagain": "確認密碼",
        "createacct-yourpasswordagain-ph": "入多次密碼",
-       "remembermypassword": "響呢個瀏覽器度記住我嘅登入資料 (最高維持$1{{PLURAL:$1|日|日}})",
        "userlogin-remembermypassword": "記住我有簽到",
        "userlogin-signwithsecure": "用安全連線",
        "yourdomainname": "你嘅網域:",
        "passwordreset-emailtext-user": "{{SITENAME}}用戶$1請求更改閣下喺{{SITENAME}}道嘅密碼$4。同爾個電子郵件有關聯嘅用戶包括:\n\n$2\n\n{{PLURAL:$3|爾個|爾啲}}臨時密碼會喺{{$5}}日之後失效。\n\n如果係閣下自己請求改密碼嘅,請馬上登錄{{SITENAME}}並且更改密碼。如果閣下諗返起自己個密碼,或者根本無申請過改密碼嘅話,請忽略爾條訊息,繼續用返舊密碼。",
        "passwordreset-emailelement": "用戶名:\n$1\n\n臨時密碼:\n$2",
        "passwordreset-emailsentemail": "若果你個戶口有登記電郵,密碼重設電郵經已送出。",
-       "passwordreset-emailsent-capture": "密碼重設電郵經已送出,下面有顯示。",
-       "passwordreset-emailerror-capture": "密碼重設電郵經已送出,下面有顯示,但送畀{{GENDER:$2|user}}時失敗: $1",
        "changeemail": "改或者剷走電郵地址",
        "changeemail-header": "填好呢份表去改戶口電郵地址。若果你想剷走你戶口個電郵地址,填表時請將電郵地址欄留空。",
-       "changeemail-passwordrequired": "你要入密碼去確認改動。",
        "changeemail-no-info": "你一定要簽到咗去直接入來呢一版。",
        "changeemail-oldemail": "而家個電郵地址:",
        "changeemail-newemail": "新電郵地址:",
        "minoredit": "呢個係小修改",
        "watchthis": "睇實呢一頁",
        "savearticle": "儲存呢頁",
+       "publishpage": "發佈呢頁",
+       "publishchanges": "發佈修改",
        "preview": "預覽",
        "showpreview": "顯示預覽",
        "showdiff": "顯示差異",
        "undo-nochange": "呢個編輯睇嚟經已一早取消咗。",
        "undo-summary": "取消由[[Special:Contributions/$2|$2]] ([[User talk:$2|對話]])所做嘅修訂 $1",
        "undo-summary-username-hidden": "取消匿埋咗嘅用戶嘅修改版本 $1",
-       "cantcreateaccounttitle": "唔可以開新戶口",
        "cantcreateaccount-text": "由呢個IP地址 ('''$1''') 開嘅新戶口已經被[[User:$3|$3]]封鎖。\n\n當中俾$3封鎖嘅原因係''$2''",
        "cantcreateaccount-range-text": "由呢個IP地址範圍<strong>$1</strong>(包括你個IP <strong>$4</strong>)開嘅新戶口已經畀[[User:$3|$3]]封鎖咗。\n\n$3畀嘅理由係<em>$2</em>",
        "viewpagelogs": "睇呢頁嘅日誌",
        "suppressionlog": "廢止日誌",
        "suppressionlogtext": "下面係刪除同埋由操作員牽涉到內容封鎖嘅一覽。\n睇吓[[Special:BlockList|封鎖一覽]]去睇現時進行緊嘅禁止同埋封鎖表。",
        "mergehistory": "合併頁歷史",
-       "mergehistory-header": "呢一版可以畀你去合併一個來源頁嘅修訂記錄到另一個新頁。\n請確認呢次更改會繼續保留嗰版之前嘅歷史。",
+       "mergehistory-header": "呢一版可以畀你去合併一個來源頁嘅修訂記錄到另一個新頁。\n請確認呢次更改會繼續保留嗰版之前嘅歷史連續性。",
        "mergehistory-box": "合併兩版嘅修訂:",
        "mergehistory-from": "來源頁:",
        "mergehistory-into": "目的頁:",
index b978126..2d38231 100644 (file)
@@ -90,7 +90,9 @@
                        "Apflu",
                        "飞舞回堂前",
                        "Cosine02",
-                       "Arthur2e5"
+                       "Arthur2e5",
+                       "Myy730",
+                       "SolidBlock"
                ]
        },
        "tog-underline": "链接下划线:",
        "tagline": "来自{{SITENAME}}",
        "help": "帮助",
        "search": "搜索",
+       "search-ignored-headings": " #<!-- 请将此行保持原样 --> <pre>\n# 将被搜索忽略的标题。\n# 对此页面的更改会在带标题的页面被索引时产生影响。\n# 您可以通过进行空编辑来强制页面重新索引。\n# 语法如下:\n#  * 所有以“#”开头的行都是注释。\n#  * 所有非空白行都是要忽略的标题。\n参考文献\n外部链接\n参见\n #</pre> <!-- 请将此行保持原样 -->",
        "searchbutton": "搜索",
        "go": "前往",
        "searcharticle": "前往",
        "history": "页面历史",
        "history_short": "历史",
        "updatedmarker": "更新于我上次访问后",
-       "printableversion": "打印版本",
+       "printableversion": "打印版本",
        "permalink": "固定链接",
        "print": "打印",
        "view": "查看",
        "youhavenewmessagesmanyusers": "您有来自多个用户的$1($2)。",
        "newmessageslinkplural": "{{PLURAL:$1|新信息|999=新消息}}",
        "newmessagesdifflinkplural": "最后{{PLURAL:$1|更改|999=更改}}",
-       "youhavenewmessagesmulti": "在$1有新信息",
+       "youhavenewmessagesmulti": "在$1有新信息",
        "editsection": "编辑",
        "editold": "编辑",
        "viewsourceold": "查看源代码",
        "databaseerror-query": "查询:$1",
        "databaseerror-function": "函数:$1",
        "databaseerror-error": "错误:$1",
-       "transaction-duration-limit-exceeded": "因为写入时间($1)超过了$2{{PLURAL:$2|秒}}的限制,为防止创建大量复制延迟,此次处理已被中止。如果您正在同时更改很多项目,请尝试进行多次小规模操作。",
+       "transaction-duration-limit-exceeded": "因为写入时间($1)超过了$2的限制,为防止创建大量复制延迟,此次处理已被中止。如果您正在同时更改很多项目,请尝试进行多次小规模操作。",
        "laggedslavemode": "<strong>警告:</strong>页面中可能没有包含最近的更新。",
        "readonly": "数据库被锁定",
        "enterlockreason": "请输入锁定的原因,这包括预计解除锁定的时间",
        "yourpasswordagain": "请再次输入密码:",
        "createacct-yourpasswordagain": "确认密码",
        "createacct-yourpasswordagain-ph": "请再次输入密码",
-       "remembermypassword": "在该浏览器记住我的登录状态(最长$1天)",
        "userlogin-remembermypassword": "记住我的登录状态",
        "userlogin-signwithsecure": "使用安全连接",
+       "cannotlogin-title": "不能登录",
+       "cannotlogin-text": "无法登录。",
        "cannotloginnow-title": "现在不能登录",
        "cannotloginnow-text": "当使用$1时无法登录。",
+       "cannotcreateaccount-title": "无法创建账户",
+       "cannotcreateaccount-text": "此wiki没有启用直接账户创建。",
        "yourdomainname": "您的域名:",
        "password-change-forbidden": "您不能在本wiki上更改密码。",
        "externaldberror": "验证数据库出错或您被禁止更新您的外部账号。",
        "password-login-forbidden": "这个用户名称及密码的使用是被禁止的。",
        "mailmypassword": "重置密码",
        "passwordremindertitle": "{{SITENAME}}的新临时密码",
-       "passwordremindertext": "有人(可能是您,来自IP地址$1)已请求{{SITENAME}}的新密码($4)。\n用户“$2”的一个新临时密码现在已被设置好为“$3”。\n如果这个动作是您所指示的,您便需要立即登录并选择一个新的密码。\n您的临时密码会于$5天内过期。\n\n如果是其他人发出了该请求,或者您已经记起了您的密码并不准备改变它,您可以忽略此消息并继续使用您的旧密码。",
+       "passwordremindertext": "有人(可能是您,来自IP地址$1)已请求{{SITENAME}}的新密码($4)。用户“$2”的临时密码现在已被设置为“$3”。如果这个动作是您所指示的,您便需要立即登录并设置一个新的密码。您的临时密码会于{{PLURAL:$5|一天|$5天}}内过期。\n\n如果是其他人发出了该请求,或者您已经记起了您的密码并不准备改变它,您可以忽略此消息并继续使用您的旧密码。",
        "noemail": "用户\"$1\"没有登记电子邮件地址。",
        "noemailcreate": "您需要提供一个有效的电子邮件地址",
        "passwordsent": "用户\"$1\"的新密码已经寄往所登记的电子邮件地址。\n请在收到后再登录。",
        "passwordreset-emailelement": "用户名:\n$1\n\n临时密码:\n$2",
        "passwordreset-emailsentemail": "如果此邮件地址与您的账户相关联的话,将发送一封密码重置邮件。",
        "passwordreset-emailsentusername": "如果有邮件地址与此用户名相关联的话,将发送一封密码重置邮件。",
-       "passwordreset-emailsent-capture": "密码重设电子邮件已发送,并在下面显示。",
-       "passwordreset-emailerror-capture": "重置密码邮件已生成,但是无法向{{GENDER:$2|下列用户}} 发送:$1",
        "passwordreset-emailsent-capture2": "密码重置{{PLURAL:$1|邮件}}已发送。{{PLURAL:$1|用户名和密码|用户名和密码列表}}在下方显示。",
        "passwordreset-emailerror-capture2": "向{{GENDER:$2|用户}}发送电子邮件失败:$1 {{PLURAL:$3|用户名和密码|用户名和密码列表}}在下方显示。",
        "passwordreset-nocaller": "必须提供一个调用方",
        "passwordreset-nodata": "用户名和电子邮件地址均未提供",
        "changeemail": "更改或移除电子邮件地址",
        "changeemail-header": "完成此表格以更改您的电子邮件地址。如果您希望从您的账户中移除任何关联的电子邮件地址,请在提交表格时将新电子邮件地址留空。",
-       "changeemail-passwordrequired": "您需要输入您的密码以确认此次更改。",
        "changeemail-no-info": "\n您必须登录以直接访问本页。",
        "changeemail-oldemail": "当前电子邮件地址:",
        "changeemail-newemail": "新的电子邮件地址:",
        "changeemail-newemail-help": "此字段应留空,如果您希望移除您的电子邮件地址的话。如果电子邮件地址被移除,您将无法重置忘记的密码,并将不会接收来自此wiki的电子邮件。",
        "changeemail-none": "(无)",
-       "changeemail-password": "的{{SITENAME}}密码:",
+       "changeemail-password": "的{{SITENAME}}密码:",
        "changeemail-submit": "更改电子邮件地址",
        "changeemail-throttled": "您最近尝试了太多次登录。请等待$1后再试。",
        "changeemail-nochange": "请输入一个不同的新的电子邮件地址。",
        "clearyourcache": "<strong>注意:</strong>在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。\n* <strong>Firefox或Safari:</strong>按住<em>Shift</em>的同时单击<em>刷新</em>,或按<em>Ctrl-F5</em>或<em>Ctrl-R</em>(Mac为<em>⌘-R</em>)\n* <strong>Google Chrome:</strong>按<em>Ctrl-Shift-R</em>(Mac为<em>⌘-Shift-R</em>)\n* <strong>Internet Explorer:</strong>按住<em>Ctrl</em>的同时单击<em>刷新</em>,或按<em>Ctrl-F5</em>\n* <strong>Opera:</strong>前往<em>菜单 → 设置</em>(Mac为<em>Opera → Preferences</em>),然后<em>隐私和安全 → 清除浏览数据 → 缓存的图片和文件</em>。",
        "usercssyoucanpreview": "<strong>提示:</strong>在保存前请用“{{int:showpreview}}”按钮来测试您新的 CSS 。",
        "userjsyoucanpreview": "<strong>提示:</strong>在保存前请用“{{int:showpreview}}”按钮来测试您新的 JavaScript 。",
-       "usercsspreview": "<strong>请记住您现在只是在预览的用户CSS。它尚未保存!</strong>",
+       "usercsspreview": "<strong>请记住您现在只是在预览的用户CSS。它尚未保存!</strong>",
        "userjspreview": "<strong>请记住您现在只是在测试/预览您的用户JavaScript。它尚未保存!</strong>",
-       "sitecsspreview": "<strong>请记住现在只是在预览该CSS。它尚未保存!</strong>",
+       "sitecsspreview": "<strong>请记住现在只是在预览该CSS。它尚未保存!</strong>",
        "sitejspreview": "<strong>请记住您现在只是在预览该JavaScript代码。它尚未保存!</strong>",
        "userinvalidcssjstitle": "<strong>警告:</strong>不存在皮肤“$1”。注意自定义的 .css 和 .js 页要使用小写标题,例如,{{ns:user}}:Foo/vector.css 不同于 {{ns:user}}:Foo/Vector.css。",
        "updated": "(已更新)",
        "content-model-css": "CSS",
        "content-json-empty-object": "空的对象",
        "content-json-empty-array": "空的数组",
+       "deprecated-self-close-category": "使用无效自封闭HTML标签的页面",
+       "deprecated-self-close-category-desc": "页面包含无效的自封闭HTML标签,例如<code>&lt;b/></code>或<code>&lt;span/></code>。这些标签的行为将很快被更改以与HTML5规格相一致,所以它们在wiki文本中的使用已弃用。",
        "duplicate-args-warning": "<strong>警告:</strong>[[:$1]]正在调用超过一个[[:$2]]中“$3”参数的值。只有最后提供的值会被使用。",
        "duplicate-args-category": "调用重复模板参数的页面",
        "duplicate-args-category-desc": "页面包含调用了重复参数的模板,例如<code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code>或<code><nowiki>{{foo|bar|1=baz}}</nowiki></code>。",
        "undo-nochange": "这次编辑似乎已被撤销。",
        "undo-summary": "撤销[[Special:Contributions/$2|$2]]([[User talk:$2|讨论]])的版本$1",
        "undo-summary-username-hidden": "取消由一匿名用户所作的版本$1",
-       "cantcreateaccounttitle": "无法创建账户",
        "cantcreateaccount-text": "从该IP地址(<strong>$1</strong>)创建账户已被[[User:$3|$3]]禁止。\n\n$3的理由是<em>$2</em>",
        "cantcreateaccount-range-text": "从该IP地址段<strong>$1</strong>的账户创建已被[[User:$3|$3]]禁止,而这也包括了您的IP地址(<strong>$4</strong>)。\n\n$3给出的原因是<em>$2</em>",
        "viewpagelogs": "查看该页面的日志",
        "last": "之前",
        "page_first": "首页",
        "page_last": "末页",
-       "histlegend": "差异选择:选中要对比的版本的单选按钮,按Enter键或下方的按钮。<br />\n说明:<strong>({{int:cur}})</strong>=与最后版本之间的差异,<strong>({{int:last}})</strong>=与上一版本之间的差异,<strong>{{int:minoreditletter}}</strong>=小编辑。",
+       "histlegend": "差异选择:选中要对比的版本的单选按钮,按Enter键或下方的按钮。<br />说明:<strong>({{int:cur}})</strong>=与最后版本之间的差异,<strong>({{int:last}})</strong>=与上一版本之间的差异,<strong>{{int:minoreditletter}}</strong>=小编辑。",
        "history-fieldset-title": "浏览历史",
        "history-show-deleted": "仅显示已删除的版本",
        "histfirst": "最旧",
        "rev-deleted-comment": "(编辑摘要被移除)",
        "rev-deleted-user": "(用户名被删除)",
        "rev-deleted-event": "(日志详情已移除)",
-       "rev-deleted-user-contribs": "[用户名或IP地址被除 - 编辑在贡献中隐藏]",
+       "rev-deleted-user-contribs": "[用户名或IP地址被除 - 编辑在贡献中隐藏]",
        "rev-deleted-text-permission": "本页面版本已被<strong>删除</strong>。详情请见[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]。",
        "rev-suppressed-text-permission": "此页面修订已经被<strong>监督隐藏</strong>。详细信息可在[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 监督日志]中找到。",
        "rev-deleted-text-unhide": "本页面版本已被<strong>删除</strong>。详情请见[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]。如果您想继续操作,您仍然可以[$1 查看本版本]。",
        "powersearch-togglenone": "全不选",
        "powersearch-remember": "记住选择用于以后的搜索",
        "search-external": "外部搜索",
-       "searchdisabled": "{{SITENAME}}的搜索已被禁用。您可以暂时使用搜索引擎进行搜索,须注意他们索引的{{SITENAME}}内容可能不是最新的。",
+       "searchdisabled": "{{SITENAME}}的搜索已被禁用。您可以暂时使用搜索引擎进行搜索,须注意索引的{{SITENAME}}内容可能不是最新的。",
        "search-error": "搜索时发生错误:$1",
        "preferences": "设置",
-       "mypreferences": "设置",
+       "mypreferences": "参数设置",
        "prefs-edits": "编辑数:",
        "prefsnologintext2": "请登录以更改您的用户设置。",
        "prefs-skin": "皮肤",
        "grant-group-high-volume": "执行大量活动",
        "grant-group-customization": "自定义与设置",
        "grant-group-administration": "执行管理操作",
+       "grant-group-private-information": "访问有关您的私有数据",
        "grant-group-other": "杂项活动",
        "grant-blockusers": "封禁与解封用户",
        "grant-createaccount": "创建账户",
        "grant-highvolume": "大容量编辑",
        "grant-oversight": "隐藏用户和阻止修订",
        "grant-patrol": "巡查对页面的更改",
+       "grant-privateinfo": "访问私有信息",
        "grant-protect": "保护页面和取消页面保护",
        "grant-rollback": "回退对页面的更改",
        "grant-sendemail": "给其他用户发送电子邮件",
        "action-applychangetags": "连同您的更改应用标签",
        "action-changetags": "在个别修订和日志记录中添加和移除任意标签",
        "action-deletechangetags": "从数据库删除标签",
+       "action-purge": "刷新此页面",
        "nchanges": "$1次更改",
        "enhancedrc-since-last-visit": "{{PLURAL:$1|上次访问后}}$1个",
        "enhancedrc-history": "历史",
        "file-thumbnail-no": "文件名以<strong>$1</strong>开始。它似乎是缩小的图像<em>(缩略图)</em>。如果您有完整分辨率的该图像,请上传它,否则请更改文件名。",
        "fileexists-forbidden": "已存在相同名称的文件,且不能覆盖;请返回并用一个新的名称来上传此文件。[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "共享文件库中存在该名称的文件。如果您仍想上传你的文件,请返回使用其他名称。[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "上传的文件与<strong>[[:$1]]</strong>的当前版本完全相同。",
+       "fileexists-duplicate-version": "上传的文件与<strong>[[:$1]]</strong>的{{PLURAL:$2|旧版本}}完全相同。",
        "file-exists-duplicate": "本文件是以下{{PLURAL:$1|文件}}的副本:",
        "file-deleted-duplicate": "一个相同名称的文件 ([[:$1]]) 在先前删除过。您应该在重新上传之前检查一下该文件之删除纪录。",
        "file-deleted-duplicate-notitle": "之前有与此相同的文件被删除和取消标题。您应该询问查看过改文件数据的任何人以复查重新上传时的诸多问题。",
        "uploadstash-errclear": "清除文件失败。",
        "uploadstash-refresh": "更新文件列表",
        "uploadstash-thumbnail": "显示缩略图",
+       "uploadstash-exception": "不能将上传内容存储至暂存处($1):“$2”。",
        "invalid-chunk-offset": "无效区块偏移量",
        "img-auth-accessdenied": "拒绝访问",
        "img-auth-nopathinfo": "PATH_INFO缺失。\n您的服务器尚未设置传送该信息。\n它可能基于CGI,因而不支持img_auth。\n请参见https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization。",
        "filerevert-submit": "恢复",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong>已经恢复至[$4 $2 $3的版本]。",
        "filerevert-badversion": "文件并无所请求时间戳下的早期本地版本。",
+       "filerevert-identical": "文件的当前版本已与选择的版本相同。",
        "filedelete": "删除$1",
        "filedelete-legend": "删除文件",
        "filedelete-intro": "您将要删除文件<strong>[[Media:$1|$1]]</strong>及其全部历史。",
        "trackingcategories-disabled": "分类被禁用",
        "mailnologin": "无电子邮件地址",
        "mailnologintext": "您必须[[Special:UserLogin|登录]]并在您的[[Special:Preferences|系统设置]]中拥有有效的电子邮件地址才能向其他用户发送电子邮件。",
-       "emailuser": "ç\94µé\82®è\81\94ç³»",
+       "emailuser": "ç\94µé\82®è\81\94ç»\9cæ­¤ç\94¨æ\88·",
        "emailuser-title-target": "电邮联系该{{GENDER:$1|用户}}",
        "emailuser-title-notarget": "电邮联系",
        "emailpagetext": "您可以使用下面的表格发送电子邮件信息至该{{GENDER:$1|用户}}。您在[[Special:Preferences|系统设置]]中输入的电子邮件地址将显示为邮件的“发件人”地址,所以该用户将可以直接回复您。",
        "defemailsubject": "来自{{SITENAME}}用户“$1”的电子邮件",
        "usermaildisabled": "用户电子邮件停用",
-       "usermaildisabledtext": "不能发送电子邮件至本wiki的其他用户",
+       "usermaildisabledtext": "不能发送电子邮件至本wiki的其他用户",
        "noemailtitle": "无电子邮件地址",
        "noemailtext": "该用户还没有指定一个有效的电子邮件地址。",
        "nowikiemailtext": "该用户已经选择不接收来自其他用户的电子邮件。",
        "watchnologin": "未登录",
        "addwatch": "添加至监视列表",
        "addedwatchtext": "“[[:$1]]”及其讨论页已加入至您的[[Special:Watchlist|监视列表]]中。",
+       "addedwatchtext-talk": "“[[:$1]]”及其相关联页面已加入至您的[[Special:Watchlist|监视列表]]中。",
        "addedwatchtext-short": "页面“$1”已加入您的监视列表。",
        "removewatch": "从监视列表中删除",
        "removedwatchtext": "“[[:$1]]”及其讨论页已从您的[[Special:Watchlist|监视列表]]中移除。",
+       "removedwatchtext-talk": "“[[:$1]]”及其相关联页面已从您的[[Special:Watchlist|监视列表]]中移除。",
        "removedwatchtext-short": "页面“$1”已从您的监视列表移除。",
        "watch": "监视",
        "watchthispage": "监视本页",
        "rollbacklinkcount-morethan": "回退超过$1次的编辑",
        "rollbackfailed": "回退失败",
        "rollback-missingparam": "请求中缺少必需参数。",
+       "rollback-missingrevision": "无法加载修订版本数据。",
        "cantrollback": "无法恢复编辑,最后贡献者是该页面的唯一作者。",
        "alreadyrolled": "无法回退[[User:$2|$2]]([[User talk:$2|讨论]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]])对[[:$1]]的编辑,其他人已经编辑或者回退了该页。\n\n本页最后的编辑者是[[User:$3|$3]]([[User talk:$3|讨论]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])。",
        "editcomment": "编辑摘要:<em>$1</em>。",
        "undeletehistorynoadmin": "这个页面已被删除。删除原因显示在下方编辑摘要中,被删除前的所有版本文本连同删除前贡献用户的细节信息只对管理员可见。",
        "undelete-revision": "$1由$3(在$4 $5)所编写的已删除版本:",
        "undeleterevision-missing": "无效或丢失的版本。您可能使用了错误的链接,或者此版本已经被从存档中恢复或移除。",
+       "undeleterevision-duplicate-revid": "{{PLURAL:$1|$1个修订版本}}不能被恢复,因为{{PLURAL:$1|它|它们}}的<code>rev_id</code>已在使用中。",
        "undelete-nodiff": "找不到先前的版本。",
        "undeletebtn": "还原",
        "undeletelink": "查看/还原",
        "undeletedrevisions": "还原{{PLURAL:$1|$1个版本}}",
        "undeletedrevisions-files": "还原{{PLURAL:$1|$1个版本}}和{{PLURAL:$2|$2个文件}}",
        "undeletedfiles": "还原{{PLURAL:$1|$1个文件}}",
-       "cannotundelete": "恢复删除失败:\n$1",
+       "cannotundelete": "部分或全部还原删除失败:$1",
        "undeletedpage": "<strong>$1已经被还原</strong>\n\n最近的删除和还原记录请见[[Special:Log/delete|删除日志]]。",
        "undelete-header": "如要查询最近的记录请参阅[[Special:Log/delete|删除日志]]。",
        "undelete-search-title": "搜索已删除页面",
        "sp-contributions-newbies-sub": "新账户的贡献",
        "sp-contributions-newbies-title": "新账户的用户贡献",
        "sp-contributions-blocklog": "封禁日志",
-       "sp-contributions-suppresslog": "被隐藏的用户贡献",
-       "sp-contributions-deleted": "被删除的用户贡献",
+       "sp-contributions-suppresslog": "被屏蔽的{{GENDER:$1|用户}}贡献",
+       "sp-contributions-deleted": "被删除的{{GENDER:$1|用户}}贡献",
        "sp-contributions-uploads": "上传",
        "sp-contributions-logs": "日志",
        "sp-contributions-talk": "讨论",
        "sp-contributions-userrights": "用户权限管理",
-       "sp-contributions-blocked-notice": "è¿\99ä½\8dç\94¨æ\88·ç\8e°æ\97¶æ­£å\9c¨è¢«å°\81é\94\81中ã\80\82\næ\9c\80è¿\91ç\9a\84å°\81é\94\81æ\97¥å¿\97项ç\9b®å\9c¨ä¸\8bé\9d¢æ\8f\90ä¾\9b以便参考:",
-       "sp-contributions-blocked-notice-anon": "è¿\99个IPå\9c°å\9d\80ç\8e°æ\97¶æ­£å\9c¨è¢«å°\81é\94\81中ã\80\82\næ\9c\80è¿\91ç\9a\84å°\81é\94\81æ\97¥å¿\97项ç\9b®å\9c¨ä¸\8bé\9d¢æ\8f\90ä¾\9b以便参考:",
+       "sp-contributions-blocked-notice": "è¿\99ä½\8dç\94¨æ\88·ç\9b®å\89\8dæ­£å\9c¨è¢«å°\81ç¦\81ã\80\82æ\9c\80è¿\91ç\9a\84å°\81ç¦\81æ\97¥å¿\97è®°å½\95å\9c¨ä¸\8bé\9d¢æ\8f\90ä¾\9b以ä¾\9b参考:",
+       "sp-contributions-blocked-notice-anon": "è¿\99个IPå\9c°å\9d\80ç\9b®å\89\8dæ­£å\9c¨è¢«å°\81ç¦\81ã\80\82æ\9c\80è¿\91ç\9a\84å°\81ç¦\81æ\97¥å¿\97è®°å½\95å\9c¨ä¸\8bé\9d¢æ\8f\90ä¾\9b以ä¾\9b参考:",
        "sp-contributions-search": "搜索贡献",
        "sp-contributions-username": "IP地址或用户名:",
        "sp-contributions-toponly": "仅显示最后版本的编辑",
        "pageinfo-article-id": "页面ID",
        "pageinfo-language": "页面内容语言",
        "pageinfo-content-model": "页面内容类型",
+       "pageinfo-content-model-change": "更改",
        "pageinfo-robot-policy": "爬虫索引",
        "pageinfo-robot-index": "允许",
        "pageinfo-robot-noindex": "不允许",
        "markaspatrolleddiff": "标记为已巡查",
        "markaspatrolledtext": "标记此页面为已巡查",
        "markaspatrolledtext-file": "将此文件版本标记为已巡查",
-       "markedaspatrolled": "标记为已查",
+       "markedaspatrolled": "标记为已查",
        "markedaspatrolledtext": "[[:$1]]的已选中版本已被标识为已巡查。",
        "rcpatroldisabled": "最近更改巡查已禁用",
        "rcpatroldisabledtext": "最近更改巡查功能目前已关闭。",
-       "markedaspatrollederror": "不能标志为已检查",
-       "markedaspatrollederrortext": "需要指定一个版本以标记为已巡查。",
-       "markedaspatrollederror-noautopatrol": "你不能把自己的更改标记为已检查。",
+       "markedaspatrollederror": "不能标记为已巡查",
+       "markedaspatrollederrortext": "需要指定一个版本以标记为已巡查。",
+       "markedaspatrollederror-noautopatrol": "您不被允许将您自己的更改标记为已巡查。",
        "markedaspatrollednotify": "$1的更改已被标记为已巡查。",
        "markedaspatrollederrornotify": "标记为已巡查失败。",
        "patrol-log-page": "巡查日志",
        "watchlistedit-raw-submit": "更新监视列表",
        "watchlistedit-raw-done": "您的监视列表已经更新。",
        "watchlistedit-raw-added": "$1个标题被添加:",
-       "watchlistedit-raw-removed": "$1个标题被除:",
+       "watchlistedit-raw-removed": "$1个标题被除:",
        "watchlistedit-clear-title": "清空监视列表",
        "watchlistedit-clear-legend": "清空监视列表",
        "watchlistedit-clear-explain": "所有标题将从您的监视列表中移除",
        "version-license-not-found": "未找到此扩展相关的详细授权信息。",
        "version-credits-title": "$1贡献者名单",
        "version-credits-not-found": "未找到此扩展相关的详细制作人信息。",
-       "version-poweredby-credits": "本Wiki由'''[https://www.mediawiki.org/ MediaWiki]'''驱动,版权所有 © 2001-$1 $2。",
+       "version-poweredby-credits": "本Wiki由<strong>[https://www.mediawiki.org/ MediaWiki]</strong>驱动,版权所有 © 2001-$1 $2。",
        "version-poweredby-others": "其他",
        "version-poweredby-translators": "translatewiki.net上的翻译者",
        "version-credits-summary": "我们感谢下列人士为[[Special:Version|MediaWiki]]作出的贡献。",
        "dberr-info": "(无法访问数据库:$1)",
        "dberr-info-hidden": "(无法访问数据库)",
        "dberr-usegoogle": "在此期间您可以尝试用 Google 来搜索。",
-       "dberr-outofdate": "须注意他们索引出来的内容可能不是最新的。",
+       "dberr-outofdate": "须注意索引出来的内容可能不是最新的。",
        "dberr-cachederror": "这是所请求页面的缓存副本,可能不是最新的。",
        "htmlform-invalid-input": "您输入的内容存在问题",
        "htmlform-select-badoption": "您指定的值不是有效选项。",
        "logentry-delete-revision": "$1{{GENDER:$2|更改}}页面$3的{{PLURAL:$5|$5个版本}}的可见性:$4",
        "logentry-delete-event-legacy": "$1{{GENDER:$2|更改}}$3的日志事件的可见性",
        "logentry-delete-revision-legacy": "$1{{GENDER:$2|更改}}页面$3的版本的可见性",
-       "logentry-suppress-delete": "$1{{GENDER:$2|已隐藏}}页面$3",
+       "logentry-suppress-delete": "$1{{GENDER:$2|已屏蔽}}页面$3",
        "logentry-suppress-event": "$1秘密地{{GENDER:$2|更改}}$3的{{PLURAL:$5|$5个日志事件}}的可见性:$4",
        "logentry-suppress-revision": "$1秘密地{{GENDER:$2|更改}}页面$3的{{PLURAL:$5|$5个版本}}的可见性:$4",
        "logentry-suppress-event-legacy": "$1秘密地{{GENDER:$2|更改}}$3的日志事件的可见性",
        "revdelete-uname-unhid": "公开用户名",
        "revdelete-restricted": "应用对管理员的限制",
        "revdelete-unrestricted": "删除对管理员的限制",
-       "logentry-block-block": "$1{{GENDER:$2|封禁了}}{{GENDER:$4|$3}},期限为$5 $6",
+       "logentry-block-block": "$1{{GENDER:$2|封禁了}}{{GENDER:$4|$3}},到期时间为$5 $6",
        "logentry-block-unblock": "$1{{GENDER:$2|解封了}}{{GENDER:$4|$3}}",
        "logentry-block-reblock": "$1将{{GENDER:$4|$3}}的封禁设置{{GENDER:$2|更改为}}持续时间$5 $6",
        "logentry-suppress-block": "$1{{GENDER:$2|封禁了}}{{GENDER:$4|$3}},持续时间$5 $6",
        "mw-widgets-dateinput-no-date": "没有选定日期",
        "mw-widgets-titleinput-description-new-page": "页面不存在",
        "mw-widgets-titleinput-description-redirect": "重定向至$1",
-       "api-error-blacklisted": "请选择其他描述性的标题。",
        "sessionmanager-tie": "不能结合多个请求的身份验证类型:$1。",
        "sessionprovider-generic": "$1会话",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "基于cookie的会话",
        "log-action-filter-newusers": "账户创建的类型:",
        "log-action-filter-patrol": "巡查类型:",
        "log-action-filter-protect": "保护类型:",
-       "log-action-filter-rights": "权限更改的类型",
-       "log-action-filter-suppress": "屏蔽的类型",
+       "log-action-filter-rights": "权限更改的类型",
+       "log-action-filter-suppress": "屏蔽的类型",
        "log-action-filter-upload": "上传类型:",
        "log-action-filter-all": "全部",
        "log-action-filter-block-block": "封禁",
        "log-action-filter-upload-upload": "新上传",
        "log-action-filter-upload-overwrite": "重新上传",
        "authmanager-authn-not-in-progress": "身份验证尚未进行,或会话数据丢失。请从头重新开始。",
-       "authmanager-authn-no-primary": "提供的证书不能被验证。",
+       "authmanager-authn-no-primary": "提供的凭据不能被认证。",
        "authmanager-authn-no-local-user": "提供的证书没有与该wiki上的任何用户相关联。",
        "authmanager-authn-no-local-user-link": "提供的证书有效,但没有与该wiki上的任何用户相关联。请通过不同方式登录,或创建一个新用户,然后您将拥有一个把您之前的证书链接到对应账户的选项。",
        "authmanager-authn-autocreate-failed": "所有账户的自动创建失败:$1",
        "linkaccounts-submit": "链接帐户",
        "unlinkaccounts": "取消链接账户",
        "unlinkaccounts-success": "账户已取消链接。",
-       "authenticationdatachange-ignored": "身份验证数据更改未处理。也许没有配置的提供者?"
+       "authenticationdatachange-ignored": "身份验证数据更改未处理。也许没有配置的提供者?",
+       "userjsispublic": "请注意:JavaScript子页面不应包含机密数据,因为它们可以被其他用户查看。",
+       "usercssispublic": "请注意:CSS子页面不应包含机密数据,因为它们可以被其他用户查看。"
 }
index bf80abe..cd86b2d 100644 (file)
@@ -73,7 +73,8 @@
                        "Zerng07",
                        "Reke",
                        "Kly",
-                       "Cosine02"
+                       "Cosine02",
+                       "一個正常人"
                ]
        },
        "tog-underline": "底線標示連結:",
        "tagline": "出自 {{SITENAME}}",
        "help": "說明",
        "search": "搜尋",
+       "search-ignored-headings": " #<!-- leave this line exactly as it is --> <pre>\n# 在搜尋中要忽略的標題。\n# 更該此檔案以快速對己列入索引的頁面標題生效。\n# 您可以編輯時不變更內容以強制頁面重新索引。\n# 語法如下:\n#   * 任何以 \"#\" 字元開頭的行至結尾會做為註解。\n#   * 任何非空白行代表要忽略的標題。\n參考文獻\n外部連結\n參見\n #</pre> <!-- leave this line exactly as it is -->",
        "searchbutton": "搜尋",
        "go": "前往",
        "searcharticle": "執行",
        "databaseerror-query": "查詢:$1",
        "databaseerror-function": "功能:$1",
        "databaseerror-error": "錯誤:$1",
-       "transaction-duration-limit-exceeded": "為了避免造成大量備援延遲,因寫入時間 ($1) 已超出了 $2 {{PLURAL:$2|秒|秒}}限制,此次操作已被中止。\n若您一次修改了許多項目,可嘗試分批處理。",
+       "transaction-duration-limit-exceeded": "為了避免造成大量備援延遲,因寫入時間 ($1) 已超出了 $2 限制,此次操作已被中止。\n若您一次修改了許多項目,可嘗試分批處理。",
        "laggedslavemode": "<strong>警告:</strong>頁面可能不包含最近的更新。",
        "readonly": "資料庫已鎖定",
        "enterlockreason": "請輸入鎖定的原因,包括估計重新開放的時間",
        "yourpasswordagain": "再輸入密碼一次:",
        "createacct-yourpasswordagain": "確認密碼",
        "createacct-yourpasswordagain-ph": "再次輸入密碼",
-       "remembermypassword": "在瀏覽器上記住我的登入資訊 (上限 $1 {{PLURAL:$1|天}})",
        "userlogin-remembermypassword": "記住我的登入狀態",
        "userlogin-signwithsecure": "使用安全連線",
        "cannotloginnow-title": "現在無法登入",
        "passwordreset-emailelement": "使用者名稱:\n$1\n\n臨時密碼:\n$2",
        "passwordreset-emailsentemail": "若此確實為您帳號所登記的電子郵件地址,將會寄出重設密碼的信件給您。",
        "passwordreset-emailsentusername": "若此確實為您使用者名稱所登記的電子郵件地址,將會寄出重設密碼的信件給您。",
-       "passwordreset-emailsent-capture": "已寄出重設密碼的電子郵件,並於下方顯示。",
-       "passwordreset-emailerror-capture": "下列為重設密碼的電子郵件內容,傳送給{{GENDER:$2|使用者}}失敗:$1",
        "passwordreset-emailsent-capture2": "密碼重設{{PLURAL:$1|郵件|郵件}}已寄出,{{PLURAL:$1|使用者名稱與密碼|使用者名稱與密碼}}如下顯示。",
        "passwordreset-emailerror-capture2": "寄發電子郵件給{{GENDER:$2|使用者}}失敗:$1,{{PLURAL:$3|使用者名稱與密碼|使用者名稱與密碼}}如下顯示。",
        "passwordreset-nocaller": "必須提供 caller",
        "passwordreset-nodata": "未提供使用者名稱或是電子郵件地址",
        "changeemail": "變更或移除電子郵件地址",
        "changeemail-header": "請填寫此表單來變更您的電子郵件地址,若您想要移除您帳號所連結的所有電子郵件地址,請於新電子郵件地址欄位留空。",
-       "changeemail-passwordrequired": "您須要輸入您的密碼來確認此次變更。",
        "changeemail-no-info": "您必須登入方可直接存取此頁面。",
        "changeemail-oldemail": "目前的電子郵件地址:",
        "changeemail-newemail": "新的電子郵件地址:",
        "content-model-css": "CSS",
        "content-json-empty-object": "空物件",
        "content-json-empty-array": "空陣列",
+       "deprecated-self-close-category": "使用無效 Self-closed HTML 標籤的頁面",
+       "deprecated-self-close-category-desc": "頁面包含無效的 Self-closed HTML 標籤,如 <code>&lt;b/></code> or <code>&lt;span/></code>。這些標籤的模式將會更改為與 HTML5 規格一致,因此 wikitext 的這種用法已停用。",
        "duplicate-args-warning": "<strong>警告:</strong> [[:$1]] 呼叫 [[:$2]] 的 \"$3\" 參數使用了超過一次,僅會使用提供的最後一個參數值。",
        "duplicate-args-category": "模板呼叫時使用重複的參數的頁面",
        "duplicate-args-category-desc": "該頁面包含重複使用參數的模板呼叫,如 <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> 或 <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>。",
        "undo-nochange": "此編輯已被還原。",
        "undo-summary": "取消由 [[Special:Contributions/$2|$2]] ([[User talk:$2|對話]]) 所作出的修訂 $1",
        "undo-summary-username-hidden": "還原隱藏使用者的修訂 $1",
-       "cantcreateaccounttitle": "無法建立帳號",
        "cantcreateaccount-text": "自這個 IP 位址 (<strong>$1</strong>) 建立帳號已經被 [[User:$3|$3]] 封鎖。\n\n$3 封鎖的原因是 <em>$2</em>",
        "cantcreateaccount-range-text": "來自 IP 位址範圍 <strong>$1</strong>,包含您的 IP 位址 (<strong>$4</strong>) 所建立的帳號已經被 [[User:$3|$3]] 封鎖。\n\n$3 封鎖的原因是 <em>$2</em>",
        "viewpagelogs": "檢視此頁面的日誌",
        "suppressionlog": "禁止顯示日誌",
        "suppressionlogtext": "以下清單為管理員透過刪除或封鎖所隱藏的內容。\n請至 [[Special:BlockList|封鎖清單]] 取得目前已封鎖的清單。",
        "mergehistory": "合併頁面歷史",
-       "mergehistory-header": "這頁可以讓您合併一個來源頁面的歷史到另一個新頁面中。\n請確認這次更改會繼續保留該頁面先前的歷史版本。",
+       "mergehistory-header": "這頁可以讓您合併一個來源頁面的歷史到另一個新頁面中。\n請確認這次更改能夠繼續保留該頁面先前歷史版本的連續性。",
        "mergehistory-box": "合併兩個頁面的修訂:",
        "mergehistory-from": "來源頁面:",
        "mergehistory-into": "目標頁面:",
        "grant-group-high-volume": "執行大量活動",
        "grant-group-customization": "自訂與偏好設定",
        "grant-group-administration": "執行管理操作",
+       "grant-group-private-information": "存取關於您的隱私資料",
        "grant-group-other": "其他活動",
        "grant-blockusers": "封鎖與解除封鎖使用者",
        "grant-createaccount": "建立帳號",
        "grant-highvolume": "大量編輯",
        "grant-oversight": "隱藏使用者和禁止顯示修訂",
        "grant-patrol": "巡邏頁面的變更",
+       "grant-privateinfo": "存取隱私資訊",
        "grant-protect": "保護與取消保護頁面",
        "grant-rollback": "還原頁面的變更",
        "grant-sendemail": "傳送電子郵件聯絡其他使用者",
        "action-applychangetags": "連同您的變更一起套用標籤",
        "action-changetags": "加入與移除任何於各別修訂與日誌項目的標籤",
        "action-deletechangetags": "從資料庫刪除標籤",
+       "action-purge": "刷新此頁面",
        "nchanges": "$1 次變更",
        "enhancedrc-since-last-visit": "{{PLURAL:$1|自上次拜訪}}已有 $1",
        "enhancedrc-history": "歷史",
        "file-thumbnail-no": "檔案名稱以 <strong>$1</strong> 為開頭。\n似乎已為縮小的圖片 <em>(縮圖)</em>。\n若您有原始大小的圖片,應上傳原始圖片,否則請變更檔名稱。",
        "fileexists-forbidden": "已存在相同名稱的檔案,且無法覆蓋。\n若您仍要上傳此檔案,請返回上一頁並使用其他名稱。\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "共用檔案庫中已存在此名稱的檔案。\n若您仍要上傳此檔案,請返回上一頁並使用其他名稱。\n[[File:$1|thumb|center|$1]]",
+       "fileexists-no-change": "上傳的檔案與目前版本的 <strong>[[:$1]]</strong> 完全相同。",
+       "fileexists-duplicate-version": "上傳的檔案與{{PLURAL:$2|較舊版本|較舊版本}}的 <strong>[[:$1]]</strong> 完全相同。",
        "file-exists-duplicate": "此檔案與下列{{PLURAL:$1|一|多}}個檔案重複:",
        "file-deleted-duplicate": "與此檔案完全相同的檔案 ([[:$1]]) 在先前已被刪除。\n您應在重新上傳之前確認該檔案的刪除日誌。",
        "file-deleted-duplicate-notitle": "與此檔案完全相同的檔案在先前已被刪除,且禁止顯示該標題。\n您在重新上傳前,應請求有權力檢視隱藏檔案的使用者重新審查。",
        "upload-http-error": "發生 HTTP 錯誤:$1",
        "upload-copy-upload-invalid-domain": "此網域不允許複製上傳的檔案。",
        "upload-foreign-cant-upload": "此 wiki 未設定可上傳來自遠端檔案庫的請求的檔案。",
+       "upload-foreign-cant-load-config": "載入要上傳檔案到遠端檔案儲存庫的設定失敗。",
+       "upload-dialog-disabled": "此 wiki 已關閉使用此對話框上傳檔案的功能。",
        "upload-dialog-title": "上傳檔案",
        "upload-dialog-button-cancel": "取消",
        "upload-dialog-button-done": "完成",
        "filerevert-submit": "還原",
        "filerevert-success": "<strong>[[Media:$1|$1]]</strong> 已經還原到 [$4 於 $2 $3 的版本]。",
        "filerevert-badversion": "查無此檔案先前於指定時間的本地版本。",
+       "filerevert-identical": "目前版本的檔案與選擇的版本完全相同。",
        "filedelete": "刪除 $1",
        "filedelete-legend": "刪除檔案",
        "filedelete-intro": "您現正要刪除檔案 <strong>[[Media:$1|$1]]</strong> 與其所有歷史版本。",
        "nrevisions": "$1 次修訂",
        "nimagelinks": "被 $1 個頁面使用",
        "ntransclusions": "被 $1 個頁面使用",
-       "specialpage-empty": "此報表查無任何結果。",
+       "specialpage-empty": "此報表查無任何結果。",
        "lonelypages": "孤立頁面",
        "lonelypagestext": "下列頁面尚未被 {{SITENAME}} 中的其它頁面連結或引用。",
        "uncategorizedpages": "未分類的頁面",
        "watchnologin": "尚未登入",
        "addwatch": "新增至監視清單",
        "addedwatchtext": "已於[[Special:Watchlist|您的監視清單]]新增頁面 \"[[:$1]]\" 及其討論頁面。\n未來對此頁面及其關聯的對話頁面的變更將會在此清單中列出。",
+       "addedwatchtext-talk": "\"[[:$1]]\" 及相關的頁面已加入至您的 [[Special:Watchlist|監視清單]]。",
        "addedwatchtext-short": "已於您的監視清單新增頁面 \"$1\"。",
        "removewatch": "從監視清單中移除",
        "removedwatchtext": "已於[[Special:Watchlist|您的監視清單]]移除頁面 \"[[:$1]]\" 及其討論頁面。",
+       "removedwatchtext-talk": "已自您的 [[Special:Watchlist|監視清單]] 移除 \"[[:$1]]\" 及相關的頁面。",
        "removedwatchtext-short": "已於您的監視清單移除頁面 \"$1\"。",
        "watch": "監視",
        "watchthispage": "監視此頁面",
        "rollbacklinkcount-morethan": "還原超過 $1 次{{PLURAL:$1|編輯}}",
        "rollbackfailed": "還原失敗",
        "rollback-missingparam": "請求缺少必要參數。",
+       "rollback-missingrevision": "無法載入修訂資料。",
        "cantrollback": "無法還原編輯;\n此頁面的最後貢獻者是唯一的作者。",
        "alreadyrolled": "無法還原由 [[User:$2|$2]] ([[User talk:$2|對話]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]] 所作的最後一次編輯 [[:$1]],已有其他人編輯或還原了該頁面。\n\n最後一次編輯該頁面的使用者是 [[User:$3|$3]] ([[User talk:$3|對話]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])。",
        "editcomment": "編輯摘要為:<em>$1</em>。",
        "undeletehistorynoadmin": "已刪除此頁面。\n以下摘要顯示刪除原因與刪除前所有編輯過此頁面的使用者詳細資料。\n這些已刪除的實際文字修訂僅對管理員可用。",
        "undelete-revision": "被 $3 刪除的 $1 (於 $4 $5) 修訂:",
        "undeleterevision-missing": "無效或遺失的修訂。\n您可能使用了錯誤的連結,或該修訂已從封存中還原或刪除。",
+       "undeleterevision-duplicate-revid": "無法還原 {{PLURAL:$1|1 個修訂|$1 修訂}},因{{PLURAL:$1|修訂的|修訂的}} <code>rev_id</code> 已在使用中。",
        "undelete-nodiff": "查無先前的修訂。",
        "undeletebtn": "還原",
        "undeletelink": "檢視/還原",
        "undeletedrevisions": "{{PLURAL:$1|$1 個修訂}}已還原",
        "undeletedrevisions-files": "{{PLURAL:$1|$1 個修訂}}與 {{PLURAL:$2|$2 個檔案}}已還原",
        "undeletedfiles": "{{PLURAL:$1|$1}} 個檔案已還原",
-       "cannotundelete": "取消刪除失敗:\n$1",
+       "cannotundelete": "部份或全部的取消刪除失敗:\n$1",
        "undeletedpage": "<strong>$1 已還原</strong>\n\n請參考 [[Special:Log/delete|刪除日誌]] 以查詢最近刪除及還原的記錄。",
        "undelete-header": "請參考 [[Special:Log/delete|刪除日誌]] 查詢最近刪除的頁面。",
        "undelete-search-title": "搜尋已刪除頁面",
        "sp-contributions-newbies-sub": "新帳號的貢獻",
        "sp-contributions-newbies-title": "新帳號的使用者貢獻",
        "sp-contributions-blocklog": "封鎖記錄",
-       "sp-contributions-suppresslog": "已禁止顯示的使用者貢獻",
-       "sp-contributions-deleted": "已刪除的使用者貢獻",
+       "sp-contributions-suppresslog": "已禁止顯示的{{GENDER:$1|使用者}}貢獻",
+       "sp-contributions-deleted": "已刪除的{{GENDER:$1|使用者}}貢獻",
        "sp-contributions-uploads": "上傳",
        "sp-contributions-logs": "日誌",
        "sp-contributions-talk": "對話",
        "unblock": "解除封鎖使用者",
        "blockip": "封鎖{{GENDER:$1|使用者}}",
        "blockip-legend": "封鎖使用者",
-       "blockiptext": "填寫以下表單可封鎖特定 IP 位址或使用者名稱的存取權限。\n這個動作應用來避免破壞行為,可根據 [[{{MediaWiki:Policy-url}}|管理政策]]。\n請在下方填寫一個具體的原因 (例如:引述一段破壞頁面的事實)。\n您可以使用 [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] 語法格式封鎖 IP 範圍,最大允許的範圍 IPv4 為 /$1、IPv6 為 /$2。",
+       "blockiptext": "填寫以下表單可封鎖特定 IP 位址或使用者的編輯權限。\n只有為了防止破壞,並符合[[{{MediaWiki:Policy-url}}|方針或政策]]的情況下方可採取此行動。\n請在下方填寫一個具體的原因(例如:引述一個被破壞的頁面)。\n您可以使用[//zh.wikipedia.org/wiki/无类别域间路由 CIDR]語法格式封鎖 IP 範圍,最大允許的範圍 IPv4 為 /$1、IPv6 為 /$2。",
        "ipaddressorusername": "IP 位址或使用者名稱:",
        "ipbexpiry": "期限:",
        "ipbreason": "原因:",
        "ipbreason-dropdown": "*常見的封鎖原因\n** 填寫不實資訊\n** 刪除頁面內容\n** 散佈外部廣告連結\n** 在頁面填寫無意義文字\n** 無禮的行為、攻擊/騷擾別人\n** 濫用多個帳號\n** 使用不受歡迎的使用者名稱",
        "ipb-hardblock": "禁止使用此 IP 位址登入的使用者編輯",
-       "ipbcreateaccount": "é\98²æ­¢å¸³è\99\9f建ç«\8b",
+       "ipbcreateaccount": "é\98²æ­¢å»ºç«\8bæ\96°å¸³è\99\9f",
        "ipbemailban": "禁止使用者傳送電子郵件",
        "ipbenableautoblock": "自動封鎖此使用者最後使用的 IP 位址,以及所有之後嘗試編輯使用的 IP 位址",
        "ipbsubmit": "封鎖此使用者",
        "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",
        "ipbhidename": "在編輯及清單中隱藏使用者名稱",
        "ipbwatchuser": "監視這位使用者的使用者頁面及其對話頁面",
-       "ipb-disableusertalk": "é\98²æ­¢æ­¤ä½¿ç\94¨è\80\85å\9c¨å°\81æ\9c\9fé\96\93編輯ä»\96自己的對話頁面",
+       "ipb-disableusertalk": "é\98»æ­¢æ­¤ä½¿ç\94¨è\80\85å\9c¨å°\81ç¦\81æ\9c\9fé\96\93編輯自己的對話頁面",
        "ipb-change-block": "使用現有設定重新封鎖使用者",
        "ipb-confirm": "確認封鎖",
        "badipaddress": "無效的 IP 位址",
        "watchlistedit-normal-legend": "從監視清單中移除標題",
        "watchlistedit-normal-explain": "下方顯示在您監視清單中的標題。\n要移除標題,請勾選該標題旁的核選方塊並點選 \"{{int:Watchlistedit-normal-submit}}\"。\n您也可 [[Special:EditWatchlist/raw|編輯原始監視清單]]。",
        "watchlistedit-normal-submit": "移除標題",
-       "watchlistedit-normal-done": "已於您的監視清單中移除 $1 個標題:",
+       "watchlistedit-normal-done": "已於您的監視清單中移除 {{PLURAL:$1|$1}} 個標題:",
        "watchlistedit-raw-title": "編輯原始監視清單",
        "watchlistedit-raw-legend": "編輯原始監視清單",
        "watchlistedit-raw-explain": "下方顯示在您監視清單中的標題,您可透過編輯來新增與移除清單項目,一個標題一行。\n完成編輯後,請點選 \"{{int:Watchlistedit-raw-submit}}\"。\n您也可 [[Special:EditWatchlist|使用標準編輯器]]。",
        "limitreport-cputime-value": "$1 秒",
        "limitreport-walltime": "實際使用時間",
        "limitreport-walltime-value": "$1 秒",
-       "limitreport-ppvisitednodes": "預處理器已訪問節點計數",
+       "limitreport-ppvisitednodes": "前置處理器造訪節點計數",
        "limitreport-ppgeneratednodes": "預處理器產生節點次數",
        "limitreport-postexpandincludesize": "展開後的引用大小",
        "limitreport-postexpandincludesize-value": "$1/$2 個{{PLURAL:$2|位元組}}",
        "mw-widgets-dateinput-no-date": "未選擇日期",
        "mw-widgets-titleinput-description-new-page": "頁面不存在",
        "mw-widgets-titleinput-description-redirect": "重新導向至 $1",
-       "api-error-blacklisted": "請選擇另一個更具描述性的標題。",
        "sessionmanager-tie": "無法合併多個請求認証類型:$1。",
        "sessionprovider-generic": "$1 連線階段",
        "sessionprovider-mediawiki-session-cookiesessionprovider": "以 cookie 為基礎的連線階段",
        "linkaccounts-success-text": "已連結帳號。",
        "linkaccounts-submit": "連結帳號",
        "unlinkaccounts": "取消連結帳號",
-       "unlinkaccounts-success": "已取消連結帳號。"
+       "unlinkaccounts-success": "已取消連結帳號。",
+       "authenticationdatachange-ignored": "認証資料變更未被處理,可能未設定提供者?",
+       "userjsispublic": "請注意:JavaScript 子頁面可被其他使用者檢視,不應包含憑証資料。",
+       "usercssispublic": "請注意:CSS 子頁面可被其他使用者檢視,不應包含憑証資料。"
 }
index 2c577ed..e778856 100644 (file)
@@ -8,6 +8,7 @@
  *
  */
 
+$fallback = 'sk';
 $fallback8bitEncoding = 'cp1250';
 
 $namespaceNames = [
@@ -87,7 +88,7 @@ $specialPageAliases = [
        'Listbots'                  => [ 'Seznam_botů', 'Seznam_botu' ],
        'ListDuplicatedFiles'       => [ 'Seznam_duplicitních_souborů' ],
        'Listfiles'                 => [ 'Seznam_souborů', 'Seznam_souboru' ],
-       'Listgrants'                => [ 'Seznam_grantů' ],
+       'Listgrants'                => [ 'Seznam_skupin_oprávnění', 'Seznam_skupin_opravneni', 'Seznam_grantů' ],
        'Listgrouprights'           => [ 'Práva_uživatelských_skupin', 'Seznam_uživatelských_práv', 'Seznam_uzivatelskych_prav' ],
        'Listredirects'             => [ 'Seznam_přesměrování', 'Seznam_presmerovani' ],
        'Listusers'                 => [ 'Uživatelé', 'Uzivatele', 'Seznam_uživatelů', 'Seznam_uzivatelu' ],
index 1cf80ce..d7a7cc8 100644 (file)
@@ -38,5 +38,5 @@ $namespaceAliases = [
        'श्रेणी_चर्चा' => NS_CATEGORY_TALK,
        'मिडिया' => NS_MEDIA,
        'उपेगकर्तो' => NS_USER,
-       'उपेगकर्तो चर्चा' => NS_USER_TALK
+       'उपेगकर्तो_चर्चा' => NS_USER_TALK
 ];
index 18c115b..cc555b6 100644 (file)
@@ -15,7 +15,7 @@ $rtl = true;
 
 $namespaceNames = [
        NS_MEDIA            => 'رسنۍ',
-       NS_SPECIAL          => 'Ú\81اÙ\86Ú«ړی',
+       NS_SPECIAL          => 'Ú\81اÙ\86Ú¯ړی',
        NS_TALK             => 'خبرې_اترې',
        NS_USER             => 'کارن',
        NS_USER_TALK        => 'د_کارن_خبرې_اترې',
@@ -33,6 +33,7 @@ $namespaceNames = [
 ];
 
 $namespaceAliases = [
+       'ځﺎﻧګړی' => NS_SPECIAL,
        'کارونکی' => NS_USER,
        'د_کارونکي_خبرې_اترې' => NS_USER_TALK,
        'انځور' => NS_FILE,
index 299fd13..2d04f63 100644 (file)
@@ -33,6 +33,8 @@
  * @author לערי ריינהארט
  */
 
+$fallback = 'cs';
+
 $specialPageAliases = [
        'Activeusers'               => [ 'AktívniPoužívatelia' ],
        'Allmessages'               => [ 'VšetkySprávy' ],
@@ -248,8 +250,8 @@ $namespaceNames = [
        NS_MEDIA            => 'Médiá',
        NS_SPECIAL          => 'Špeciálne',
        NS_TALK             => 'Diskusia',
-       NS_USER             => 'Redaktor',
-       NS_USER_TALK        => 'Diskusia_s_redaktorom',
+       NS_USER             => 'Užívateľ',
+       NS_USER_TALK        => 'Diskusia_s_užívateľom',
        NS_PROJECT_TALK     => 'Diskusia_k_{{GRAMMAR:datív|$1}}',
        NS_FILE             => 'Súbor',
        NS_FILE_TALK        => 'Diskusia_k_súboru',
@@ -265,6 +267,8 @@ $namespaceNames = [
 
 $namespaceAliases = [
        "Komentár"               => NS_TALK,
+       'Redaktor'               => NS_USER,
+       'Diskusia_s_redaktorom'  => NS_USER_TALK,
        "Komentár_k_redaktorovi" => NS_USER_TALK,
        "Komentár_k_Wikipédii"   => NS_PROJECT_TALK,
        'Obrázok' => NS_FILE,
@@ -273,6 +277,11 @@ $namespaceAliases = [
        "Komentár_k_MediaWiki"   => NS_MEDIAWIKI_TALK,
 ];
 
+$namespaceGenderAliases = [
+       NS_USER => [ 'male' => 'Užívateľ', 'female' => 'Užívateľka' ],
+       NS_USER_TALK => [ 'male' => 'Diskusia_s_užívateľom', 'female' => 'Diskusia_s_užívateľkou' ],
+];
+
 $separatorTransformTable = [
        ',' => "\xc2\xa0",
        '.' => ','
index 4ff8d2d..4383ab4 100644 (file)
 
 $fallback = 'kn';
 
+$namespaceNames = [
+       NS_MEDIA            => 'ಮಾದ್ಯಮೊ',
+       NS_SPECIAL          => 'ವಿಸೇಸೊ',
+       NS_TALK             => 'ಪಾತೆರ',
+       NS_USER             => 'ಬಳಕೆದಾರೆ',
+       NS_USER_TALK        => 'ಬಳಕೆದಾರೆ_ಪಾತೆರ',
+       NS_PROJECT_TALK     => '$1_ಪಾತೆರ',
+       NS_FILE             => 'ಫೈಲ್',
+       NS_FILE_TALK        => 'ಫೈಲ್_ಪಾತೆರ',
+       NS_MEDIAWIKI        => 'ಮಾದ್ಯಮೊ_ವಿಕಿ',
+       NS_MEDIAWIKI_TALK   => 'ಮಾದ್ಯಮೊ_ವಿಕಿ_ಪಾತೆರ',
+       NS_TEMPLATE         => 'ಟೆಂಪ್ಲೇಟ್',
+       NS_TEMPLATE_TALK    => 'ಟೆಂಪ್ಲೇಟ್_ಪಾತೆರ',
+       NS_HELP             => 'ಸಕಾಯೊ',
+       NS_HELP_TALK        => 'ಸಕಾಯೊ_ಪಾತೆರ',
+       NS_CATEGORY         => 'ವರ್ಗೊ',
+       NS_CATEGORY_TALK    => 'ವರ್ಗೊ_ಪಾತೆರ',
+];
+
index 27d8161..2216de1 100644 (file)
@@ -37,6 +37,7 @@ define( 'DO_MAINTENANCE', RUN_MAINTENANCE_IF_MAIN ); // original name, harmless
 $maintClass = false;
 
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Abstract maintenance class for quickly writing and churning out
@@ -108,7 +109,7 @@ abstract class Maintenance {
        private $mDb = null;
 
        /** @var float UNIX timestamp */
-       private $lastSlaveWait = 0.0;
+       private $lastReplicationWait = 0.0;
 
        /**
         * Used when creating separate schema files.
@@ -123,6 +124,12 @@ abstract class Maintenance {
         */
        private $config;
 
+       /**
+        * @see Maintenance::requireExtension
+        * @var array
+        */
+       private $requiredExtensions = [];
+
        /**
         * Used to read the options in the order they were passed.
         * Useful for option chaining (Ex. dumpBackup.php). It will
@@ -506,6 +513,82 @@ abstract class Maintenance {
                $this->config = $config;
        }
 
+       /**
+        * Indicate that the specified extension must be
+        * loaded before the script can run.
+        *
+        * This *must* be called in the constructor.
+        *
+        * @since 1.28
+        * @param string $name
+        */
+       protected function requireExtension( $name ) {
+               $this->requiredExtensions[] = $name;
+       }
+
+       /**
+        * Verify that the required extensions are installed
+        *
+        * @since 1.28
+        */
+       public function checkRequiredExtensions() {
+               $registry = ExtensionRegistry::getInstance();
+               $missing = [];
+               foreach ( $this->requiredExtensions as $name ) {
+                       if ( !$registry->isLoaded( $name ) ) {
+                               $missing[] = $name;
+                       }
+               }
+
+               if ( $missing ) {
+                       $joined = implode( ', ', $missing );
+                       $msg = "The following extensions are required to be installed "
+                               . "for this script to run: $joined. Please enable them and then try again.";
+                       $this->error( $msg, 1 );
+               }
+
+       }
+
+       /**
+        * Set triggers like when to try to run deferred updates
+        * @since 1.28
+        */
+       public function setTriggers() {
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               self::setLBFactoryTriggers( $lbFactory );
+       }
+
+       /**
+        * @param LBFactory $LBFactory
+        * @since 1.28
+        */
+       public static function setLBFactoryTriggers( LBFactory $LBFactory ) {
+               // Hook into period lag checks which often happen in long-running scripts
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $lbFactory->setWaitForReplicationListener(
+                       __METHOD__,
+                       function () {
+                               global $wgCommandLineMode;
+                               // Check config in case of JobRunner and unit tests
+                               if ( $wgCommandLineMode ) {
+                                       DeferredUpdates::tryOpportunisticExecute( 'run' );
+                               }
+                       }
+               );
+               // Check for other windows to run them. A script may read or do a few writes
+               // to the master but mostly be writing to something else, like a file store.
+               $lbFactory->getMainLB()->setTransactionListener(
+                       __METHOD__,
+                       function ( $trigger ) {
+                               global $wgCommandLineMode;
+                               // Check config in case of JobRunner and unit tests
+                               if ( $wgCommandLineMode && $trigger === IDatabase::TRIGGER_COMMIT ) {
+                                       DeferredUpdates::tryOpportunisticExecute( 'run' );
+                               }
+                       }
+               );
+       }
+
        /**
         * Run a child maintenance script. Pass all of the current arguments
         * to it.
@@ -865,7 +948,7 @@ abstract class Maintenance {
 
                // Description ...
                if ( $this->mDescription ) {
-                       $this->output( "\n" . $this->mDescription . "\n" );
+                       $this->output( "\n" . wordwrap( $this->mDescription, $screenWidth ) . "\n" );
                }
                $output = "\nUsage: php " . basename( $this->mSelf );
 
@@ -1144,7 +1227,7 @@ abstract class Maintenance {
         * If not set, wfGetDB() will be used.
         * This function has the same parameters as wfGetDB()
         *
-        * @param integer $db DB index (DB_SLAVE/DB_MASTER)
+        * @param integer $db DB index (DB_REPLICA/DB_MASTER)
         * @param array $groups; default: empty array
         * @param string|bool $wiki; default: current wiki
         * @return IDatabase
@@ -1181,23 +1264,29 @@ abstract class Maintenance {
        }
 
        /**
-        * Commit the transcation on a DB handle and wait for slaves to catch up
+        * Commit the transcation on a DB handle and wait for replica DBs to catch up
         *
         * This method makes it clear that commit() is called from a maintenance script,
         * which has outermost scope. This is safe, unlike $dbw->commit() called in other places.
         *
         * @param IDatabase $dbw
         * @param string $fname Caller name
-        * @return bool Whether the slave wait succeeded
+        * @return bool Whether the replica DB wait succeeded
         * @since 1.27
         */
        protected function commitTransaction( IDatabase $dbw, $fname ) {
                $dbw->commit( $fname );
+               try {
+                       $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+                       $lbFactory->waitForReplication(
+                               [ 'timeout' => 30, 'ifWritesSince' => $this->lastReplicationWait ]
+                       );
+                       $this->lastReplicationWait = microtime( true );
 
-               $ok = wfWaitForSlaves( $this->lastSlaveWait, false, '*', 30 );
-               $this->lastSlaveWait = microtime( true );
-
-               return $ok;
+                       return true;
+               } catch ( DBReplicationWaitError $e ) {
+                       return false;
+               }
        }
 
        /**
@@ -1399,6 +1488,14 @@ abstract class Maintenance {
 
                return fgets( STDIN, 1024 );
        }
+
+       /**
+        * Call this to set up the autoloader to allow classes to be used from the
+        * tests directory.
+        */
+       public static function requireTestsAutoloader() {
+               require_once __DIR__ . '/../tests/common/TestsAutoLoader.php';
+       }
 }
 
 /**
index 2555475..a348e85 100644 (file)
@@ -4,7 +4,7 @@ help:
        @echo "Run 'make man' to run the doxygen generation with man pages."
 
 test:
-       php tests/parserTests.php --quiet
+       php tests/parser/parserTests.php --quiet
 
 doc:
        php mwdocgen.php --all
diff --git a/maintenance/archives/patch-add-rc_name_type_patrolled_timestamp_index.sql b/maintenance/archives/patch-add-rc_name_type_patrolled_timestamp_index.sql
new file mode 100644 (file)
index 0000000..aa54e75
--- /dev/null
@@ -0,0 +1,2 @@
+-- @since 1.28
+CREATE INDEX /*i*/rc_name_type_patrolled_timestamp ON /*_*/recentchanges (rc_namespace, rc_type, rc_patrolled, rc_timestamp);
diff --git a/maintenance/archives/patch-pl-tl-il-nonunique.sql b/maintenance/archives/patch-pl-tl-il-nonunique.sql
new file mode 100644 (file)
index 0000000..8e1715b
--- /dev/null
@@ -0,0 +1,11 @@
+-- Make reorderings of UNIQUE indices non-UNIQUE
+-- Since 1.24, these indices have been non-UNIQUE in tables.sql.
+-- However, an earlier update from 1.15 that made the indices
+-- UNIQUE was not removed until 1.28 (T78513).
+
+DROP INDEX /*i*/pl_namespace ON /*_*/pagelinks;
+CREATE INDEX /*i*/pl_namespace ON /*_*/pagelinks (pl_namespace, pl_title, pl_from);
+DROP INDEX /*i*/tl_namespace ON /*_*/templatelinks;
+CREATE INDEX /*i*/tl_namespace ON /*_*/templatelinks (tl_namespace, tl_title, tl_from);
+DROP INDEX /*i*/il_to ON /*_*/imagelinks;
+CREATE INDEX /*i*/il_to ON /*_*/imagelinks (il_to, il_from);
diff --git a/maintenance/archives/patch-pl-tl-il-unique.sql b/maintenance/archives/patch-pl-tl-il-unique.sql
deleted file mode 100644 (file)
index a356670..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
---
--- patch-pl-tl-il-unique-index.sql
---
--- Make reorderings of UNIQUE indices UNIQUE as well
-
-DROP INDEX /*i*/pl_namespace ON /*_*/pagelinks;
-CREATE UNIQUE INDEX /*i*/pl_namespace ON /*_*/pagelinks (pl_namespace, pl_title, pl_from);
-DROP INDEX /*i*/tl_namespace ON /*_*/templatelinks;
-CREATE UNIQUE INDEX /*i*/tl_namespace ON /*_*/templatelinks (tl_namespace, tl_title, tl_from);
-DROP INDEX /*i*/il_to ON /*_*/imagelinks;
-CREATE UNIQUE INDEX /*i*/il_to ON /*_*/imagelinks (il_to, il_from);
diff --git a/maintenance/archives/patch-revision-page-rev-index-nonunique.sql b/maintenance/archives/patch-revision-page-rev-index-nonunique.sql
new file mode 100644 (file)
index 0000000..dbb0325
--- /dev/null
@@ -0,0 +1,5 @@
+-- Makes rev_page_id index non-unique
+ALTER TABLE /*_*/revision
+DROP INDEX /*i*/rev_page_id;
+
+CREATE INDEX /*i*/rev_page_id ON /*_*/revision (rev_page, rev_id);
index 4947cb1..a39ac0a 100644 (file)
@@ -1,7 +1,7 @@
 -- Split user table into two parts:
 --   user
 --   user_rights
--- The later contains only the permissions of the user. This way,
+-- The latter contains only the permissions of the user. This way,
 -- you can store the accounts for several wikis in one central
 -- database but keep user rights local to the wiki.
 
index db3af92..38daf64 100644 (file)
@@ -302,7 +302,7 @@ class BackupDumper extends Maintenance {
 
                $dbr = $this->forcedDb;
                if ( $this->forcedDb === null ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                }
                $this->maxCount = $dbr->selectField( $table, "MAX($field)", '', __METHOD__ );
                $this->startTime = microtime( true );
@@ -322,7 +322,7 @@ class BackupDumper extends Maintenance {
                }
 
                $this->lb = wfGetLBFactory()->newMainLB();
-               $db = $this->lb->getConnection( DB_SLAVE, 'dump' );
+               $db = $this->lb->getConnection( DB_REPLICA, 'dump' );
 
                // Discourage the server from disconnecting us if it takes a long time
                // to read out the big ol' batch query.
index 0a89bfa..884e307 100644 (file)
@@ -118,7 +118,7 @@ class BenchmarkParse extends Maintenance {
         * @return bool|string Revision ID, or false if not found or error
         */
        function getRevIdForTime( Title $title, $timestamp ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $id = $dbr->selectField(
                        [ 'revision', 'page' ],
index a550d12..9fa6632 100644 (file)
@@ -41,8 +41,6 @@ class ChangePassword extends Maintenance {
        }
 
        public function execute() {
-               global $wgDisableAuthManager;
-
                if ( $this->hasOption( "user" ) ) {
                        $user = User::newFromName( $this->getOption( 'user' ) );
                } elseif ( $this->hasOption( "userid" ) ) {
@@ -55,17 +53,13 @@ class ChangePassword extends Maintenance {
                }
                $password = $this->getOption( 'password' );
                try {
-                       if ( $wgDisableAuthManager ) {
-                               $user->setPassword( $password );
-                       } else {
-                               $status = $user->changeAuthenticationData( [
-                                       'username' => $user->getName(),
-                                       'password' => $password,
-                                       'retype' => $password,
-                               ] );
-                               if ( !$status->isGood() ) {
-                                       throw new PasswordError( $status->getWikiText( null, null, 'en' ) );
-                               }
+                       $status = $user->changeAuthenticationData( [
+                               'username' => $user->getName(),
+                               'password' => $password,
+                               'retype' => $password,
+                       ] );
+                       if ( !$status->isGood() ) {
+                               throw new PasswordError( $status->getWikiText( null, null, 'en' ) );
                        }
                        $user->saveSettings();
                        $this->output( "Password set for " . $user->getName() . "\n" );
index 097ad1f..6eafc96 100644 (file)
@@ -36,7 +36,7 @@ class CheckBadRedirects extends Maintenance {
 
        public function execute() {
                $this->output( "Fetching redirects...\n" );
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $result = $dbr->select(
                        [ 'page' ],
                        [ 'page_namespace', 'page_title', 'page_latest' ],
index f05d15c..3e57393 100644 (file)
@@ -37,7 +37,7 @@ class CheckImages extends Maintenance {
 
        public function execute() {
                $start = '';
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $numImages = 0;
                $numGood = 0;
index eeec9d1..8416c8a 100644 (file)
@@ -40,7 +40,7 @@ class CheckLess extends Maintenance {
                // NOTE (phuedx, 2014-03-26) wgAutoloadClasses isn't set up
                // by either of the dependencies at the top of the file, so
                // require it here.
-               require_once __DIR__ . '/../tests/TestsAutoLoader.php';
+               self::requireTestsAutoloader();
 
                // If phpunit isn't available by autoloader try pulling it in
                if ( !class_exists( 'PHPUnit_Framework_TestCase' ) ) {
index 6c66da4..e6d9547 100644 (file)
@@ -40,7 +40,7 @@ class CheckUsernames extends Maintenance {
        }
 
        function execute() {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $maxUserId = 0;
                do {
index 6931259..2da45ca 100644 (file)
 require_once __DIR__ . '/cleanupTable.inc';
 
 /**
- * Maintenance script to clean up broken page links when somebody turns on $wgCapitalLinks.
+ * Maintenance script to clean up broken page links when somebody turns
+ * on or off $wgCapitalLinks.
  *
  * @ingroup Maintenance
  */
 class CapsCleanup extends TableCleanup {
 
        private $user;
+       private $namespace;
 
        public function __construct() {
                parent::__construct();
@@ -47,25 +49,66 @@ class CapsCleanup extends TableCleanup {
        }
 
        public function execute() {
-               global $wgCapitalLinks;
-
-               if ( $wgCapitalLinks ) {
-                       $this->error( "\$wgCapitalLinks is on -- no need for caps links cleanup.", true );
-               }
-
                $this->user = User::newSystemUser( 'Conversion script', [ 'steal' => true ] );
 
                $this->namespace = intval( $this->getOption( 'namespace', 0 ) );
+
+               if ( MWNamespace::isCapitalized( $this->namespace ) ) {
+                       $this->output( "Will be moving pages to first letter capitalized titles" );
+                       $callback = 'processRowToUppercase';
+               } else {
+                       $this->output( "Will be moving pages to first letter lowercase titles" );
+                       $callback = 'processRowToLowercase';
+               }
+
                $this->dryrun = $this->hasOption( 'dry-run' );
 
                $this->runTable( [
                        'table' => 'page',
                        'conds' => [ 'page_namespace' => $this->namespace ],
                        'index' => 'page_id',
-                       'callback' => 'processRow' ] );
+                       'callback' => $callback ] );
        }
 
-       protected function processRow( $row ) {
+       protected function processRowToUppercase( $row ) {
+               global $wgContLang;
+
+               $current = Title::makeTitle( $row->page_namespace, $row->page_title );
+               $display = $current->getPrefixedText();
+               $lower = $row->page_title;
+               $upper = $wgContLang->ucfirst( $row->page_title );
+               if ( $upper == $lower ) {
+                       $this->output( "\"$display\" already uppercase.\n" );
+
+                       return $this->progress( 0 );
+               }
+
+               $target = Title::makeTitle( $row->page_namespace, $upper );
+               if ( $target->exists() ) {
+                       // Prefix "CapsCleanup" to bypass the conflict
+                       $target = Title::newFromText( __CLASS__ . '/' . $display );
+               }
+               $ok = $this->movePage(
+                       $current,
+                       $target,
+                       'Converting page title to first-letter uppercase',
+                       false
+               );
+               if ( $ok ) {
+                       $this->progress( 1 );
+                       if ( $row->page_namespace == $this->namespace ) {
+                               $talk = $target->getTalkPage();
+                               $row->page_namespace = $talk->getNamespace();
+                               if ( $talk->exists() ) {
+                                       return $this->processRowToUppercase( $row );
+                               }
+                       }
+               }
+
+               return $this->progress( 0 );
+       }
+
+       protected function processRowToLowercase( $row ) {
                global $wgContLang;
 
                $current = Title::makeTitle( $row->page_namespace, $row->page_title );
@@ -79,35 +122,51 @@ class CapsCleanup extends TableCleanup {
                }
 
                $target = Title::makeTitle( $row->page_namespace, $lower );
-               $targetDisplay = $target->getPrefixedText();
                if ( $target->exists() ) {
+                       $targetDisplay = $target->getPrefixedText();
                        $this->output( "\"$display\" skipped; \"$targetDisplay\" already exists\n" );
 
                        return $this->progress( 0 );
                }
 
-               if ( $this->dryrun ) {
-                       $this->output( "\"$display\" -> \"$targetDisplay\": DRY RUN, NOT MOVED\n" );
-                       $ok = true;
-               } else {
-                       $mp = new MovePage( $current, $target );
-                       $status = $mp->move( $this->user, 'Converting page titles to lowercase', true );
-                       $ok = $status->isOK() ? 'OK' : $status->getWikiText( false, false, 'en' );
-                       $this->output( "\"$display\" -> \"$targetDisplay\": $ok\n" );
-               }
+               $ok = $this->movePage( $current, $target, 'Converting page titles to lowercase', true );
                if ( $ok === true ) {
                        $this->progress( 1 );
                        if ( $row->page_namespace == $this->namespace ) {
                                $talk = $target->getTalkPage();
                                $row->page_namespace = $talk->getNamespace();
                                if ( $talk->exists() ) {
-                                       return $this->processRow( $row );
+                                       return $this->processRowToLowercase( $row );
                                }
                        }
                }
 
                return $this->progress( 0 );
        }
+
+       /**
+        * @param Title $current
+        * @param Title $target
+        * @param string $reason
+        * @param bool $createRedirect
+        * @return bool Success
+        */
+       private function movePage( Title $current, Title $target, $reason, $createRedirect ) {
+               $display = $current->getPrefixedText();
+               $targetDisplay = $target->getPrefixedText();
+
+               if ( $this->dryrun ) {
+                       $this->output( "\"$display\" -> \"$targetDisplay\": DRY RUN, NOT MOVED\n" );
+                       $ok = 'OK';
+               } else {
+                       $mp = new MovePage( $current, $target );
+                       $status = $mp->move( $this->user, $reason, $createRedirect );
+                       $ok = $status->isOK() ? 'OK' : $status->getWikiText( false, false, 'en' );
+                       $this->output( "\"$display\" -> \"$targetDisplay\": $ok\n" );
+               }
+
+               return $ok === 'OK';
+       }
 }
 
 $maintClass = "CapsCleanup";
diff --git a/maintenance/cleanupEmptyCategories.php b/maintenance/cleanupEmptyCategories.php
new file mode 100644 (file)
index 0000000..b8a246e
--- /dev/null
@@ -0,0 +1,204 @@
+<?php
+/**
+ * Clean up empty categories in the category 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';
+
+/**
+ * Maintenance script to clean up empty categories in the category table.
+ *
+ * @ingroup Maintenance
+ * @since 1.28
+ */
+class CleanupEmptyCategories extends LoggedUpdateMaintenance {
+
+       public function __construct() {
+               parent::__construct();
+               $this->addDescription(
+                       <<<TEXT
+This script will clean up the category table by removing entries for empty
+categories without a description page and adding entries for empty categories
+with a description page. It will print out progress indicators every batch. The
+script is perfectly safe to run on large, live wikis, and running it multiple
+times is harmless. You may want to use the throttling options if it's causing
+too much load; they will not affect correctness.
+
+If the script is stopped and later resumed, you can use the --mode and --begin
+options with the last printed progress indicator to pick up where you left off.
+
+When the script has finished, it will make a note of this in the database, and
+will not run again without the --force option.
+TEXT
+               );
+
+               $this->addOption(
+                       'mode',
+                       '"add" empty categories with description pages, "remove" empty categories '
+                       . 'without description pages, or "both"',
+                       false,
+                       true
+               );
+               $this->addOption(
+                       'begin',
+                       'Only do categories whose names are alphabetically after the provided name',
+                       false,
+                       true
+               );
+               $this->addOption(
+                       'throttle',
+                       'Wait this many milliseconds after each batch. Default: 0',
+                       false,
+                       true
+               );
+       }
+
+       protected function getUpdateKey() {
+               return 'cleanup empty categories';
+       }
+
+       protected function doDBUpdates() {
+               $mode = $this->getOption( 'mode', 'both' );
+               $begin = $this->getOption( 'begin', '' );
+               $throttle = $this->getOption( 'throttle', 0 );
+
+               if ( !in_array( $mode, [ 'add', 'remove', 'both' ] ) ) {
+                       $this->output( "--mode must be 'add', 'remove', or 'both'.\n" );
+                       return false;
+               }
+
+               $dbw = $this->getDB( DB_MASTER );
+
+               $throttle = intval( $throttle );
+
+               if ( $mode === 'add' || $mode === 'both' ) {
+                       if ( $begin !== '' ) {
+                               $where = [ 'page_title > ' . $dbw->addQuotes( $begin ) ];
+                       } else {
+                               $where = [];
+                       }
+
+                       $this->output( "Adding empty categories with description pages...\n" );
+                       while ( true ) {
+                               # Find which category to update
+                               $rows = $dbw->select(
+                                       [ 'page', 'category' ],
+                                       'page_title',
+                                       array_merge( $where, [
+                                               'page_namespace' => NS_CATEGORY,
+                                               'cat_title' => null,
+                                       ] ),
+                                       __METHOD__,
+                                       [
+                                               'ORDER BY' => 'page_title',
+                                               'LIMIT' => $this->mBatchSize,
+                                       ],
+                                       [
+                                               'category' => [ 'LEFT JOIN', 'page_title = cat_title' ],
+                                       ]
+                               );
+                               if ( !$rows || $rows->numRows() <= 0 ) {
+                                       # Done, hopefully.
+                                       break;
+                               }
+
+                               foreach ( $rows as $row ) {
+                                       $name = $row->page_title;
+                                       $where = [ 'page_title > ' . $dbw->addQuotes( $name ) ];
+
+                                       # Use the row to update the category count
+                                       $cat = Category::newFromName( $name );
+                                       if ( !is_object( $cat ) ) {
+                                               $this->output( "The category named $name is not valid?!\n" );
+                                       } else {
+                                               $cat->refreshCounts();
+                                       }
+                               }
+                               $this->output( "--mode=$mode --begin=$name\n" );
+
+                               wfWaitForSlaves();
+                               usleep( $throttle * 1000 );
+                       }
+
+                       $begin = '';
+               }
+
+               if ( $mode === 'remove' || $mode === 'both' ) {
+                       if ( $begin !== '' ) {
+                               $where = [ 'cat_title > ' . $dbw->addQuotes( $begin ) ];
+                       } else {
+                               $where = [];
+                       }
+                       $i = 0;
+
+                       $this->output( "Removing empty categories without description pages...\n" );
+                       while ( true ) {
+                               # Find which category to update
+                               $rows = $dbw->select(
+                                       [ 'category', 'page' ],
+                                       'cat_title',
+                                       array_merge( $where, [
+                                               'page_title' => null,
+                                               'cat_pages' => 0,
+                                       ] ),
+                                       __METHOD__,
+                                       [
+                                               'ORDER BY' => 'cat_title',
+                                               'LIMIT' => $this->mBatchSize,
+                                       ],
+                                       [
+                                               'page' => [ 'LEFT JOIN', [
+                                                       'page_namespace' => NS_CATEGORY, 'page_title = cat_title'
+                                               ] ],
+                                       ]
+                               );
+                               if ( !$rows || $rows->numRows() <= 0 ) {
+                                       # Done, hopefully.
+                                       break;
+                               }
+                               foreach ( $rows as $row ) {
+                                       $name = $row->cat_title;
+                                       $where = [ 'cat_title > ' . $dbw->addQuotes( $name ) ];
+
+                                       # Use the row to update the category count
+                                       $cat = Category::newFromName( $name );
+                                       if ( !is_object( $cat ) ) {
+                                               $this->output( "The category named $name is not valid?!\n" );
+                                       } else {
+                                               $cat->refreshCounts();
+                                       }
+                               }
+
+                               $this->output( "--mode=remove --begin=$name\n" );
+
+                               wfWaitForSlaves();
+                               usleep( $throttle * 1000 );
+                       }
+               }
+
+               $this->output( "Category cleanup complete.\n" );
+
+               return true;
+       }
+}
+
+$maintClass = 'CleanupEmptyCategories';
+require_once RUN_MAINTENANCE_IF_MAIN;
index 2f3f013..4e47cfb 100644 (file)
@@ -64,7 +64,7 @@ class CleanupSpam extends Maintenance {
                        $this->output( "Finding spam on " . count( $wgLocalDatabases ) . " wikis\n" );
                        $found = false;
                        foreach ( $wgLocalDatabases as $wikiID ) {
-                               $dbr = $this->getDB( DB_SLAVE, [], $wikiID );
+                               $dbr = $this->getDB( DB_REPLICA, [], $wikiID );
 
                                $count = $dbr->selectField( 'externallinks', 'COUNT(*)',
                                        [ 'el_index' . $dbr->buildLike( $like ) ], __METHOD__ );
@@ -83,7 +83,7 @@ class CleanupSpam extends Maintenance {
                } else {
                        // Clean up spam on this wiki
 
-                       $dbr = $this->getDB( DB_SLAVE );
+                       $dbr = $this->getDB( DB_REPLICA );
                        $res = $dbr->select( 'externallinks', [ 'DISTINCT el_from' ],
                                [ 'el_index' . $dbr->buildLike( $like ) ], __METHOD__ );
                        $count = $dbr->numRows( $res );
index a18b81e..3ace09c 100644 (file)
@@ -106,7 +106,7 @@ class TableCleanup extends Maintenance {
         * @throws MWException
         */
        public function runTable( $params ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                if ( array_diff( array_keys( $params ),
                        [ 'table', 'conds', 'index', 'callback' ] )
index 4f23ef0..650fae0 100644 (file)
@@ -78,7 +78,7 @@ class TitleCleanup extends TableCleanup {
        protected function fileExists( $name ) {
                // XXX: Doesn't actually check for file existence, just presence of image record.
                // This is reasonable, since cleanupImages.php only iterates over the image table.
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $row = $dbr->selectRow( 'image', [ 'img_name' ], [ 'img_name' => $name ], __METHOD__ );
 
                return $row !== false;
index 13b239f..ce19974 100644 (file)
@@ -37,7 +37,7 @@ class ClearInterwikiCache extends Maintenance {
 
        public function execute() {
                global $wgLocalDatabases, $wgMemc;
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $res = $dbr->select( 'interwiki', [ 'iw_prefix' ], false );
                $prefixes = [];
                foreach ( $res as $row ) {
index 245b613..8bd060f 100644 (file)
@@ -35,7 +35,7 @@ class CompareParserCache extends Maintenance {
        public function execute() {
                $pages = $this->getOption( 'maxpages' );
 
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $totalsec = 0.0;
                $scanned = 0;
index 7c87e10..0554949 100644 (file)
@@ -66,7 +66,7 @@ class ConvertExtensionToRegistration extends Maintenance {
                $processor = new ReflectionClass( 'ExtensionProcessor' );
                $settings = $processor->getProperty( 'globalSettings' );
                $settings->setAccessible( true );
-               return $settings->getValue() + $this->formerGlobals;
+               return array_merge( $settings->getValue(), $this->formerGlobals );
        }
 
        public function execute() {
@@ -120,7 +120,7 @@ class ConvertExtensionToRegistration extends Maintenance {
                                $this->hasWarning = true;
                        } elseif ( strpos( $name, $configPrefix ) === 0 ) {
                                // Most likely a config setting
-                               $this->json['config'][substr( $name, strlen( $configPrefix ) )] = $value;
+                               $this->json['config'][substr( $name, strlen( $configPrefix ) )] = [ 'value' => $value ];
                        } elseif ( $configPrefix !== 'wg' && strpos( $name, 'wg' ) === 0 ) {
                                // Warn about this
                                $this->output( 'Warning: Skipped global "' . $name . '" (' .
@@ -230,6 +230,13 @@ class ConvertExtensionToRegistration extends Maintenance {
 
        public function handleHooks( $realName, $value ) {
                foreach ( $value as $hookName => &$handlers ) {
+                       if ( $hookName === 'UnitTestsList' ) {
+                               $this->output( "Note: the UnitTestsList hook is no longer necessary as " .
+                                       "long as your tests are located in the \"tests/phpunit/\" directory. " .
+                                       "Please see <https://www.mediawiki.org/wiki/Manual:PHP_unit_testing/" .
+                                       "Writing_unit_tests_for_extensions#Register_your_tests> for more details.\n"
+                               );
+                       }
                        foreach ( $handlers as $func ) {
                                if ( $func instanceof Closure ) {
                                        $this->error( "Error: Closures cannot be converted to JSON. " .
index 3591b9c..1872716 100644 (file)
@@ -56,8 +56,6 @@ class CreateAndPromote extends Maintenance {
        }
 
        public function execute() {
-               global $wgDisableAuthManager;
-
                $username = $this->getArg( 0 );
                $password = $this->getArg( 1 );
                $force = $this->hasOption( 'force' );
@@ -122,17 +120,13 @@ class CreateAndPromote extends Maintenance {
                if ( $password ) {
                        # Try to set the password
                        try {
-                               if ( $wgDisableAuthManager ) {
-                                       $user->setPassword( $password );
-                               } else {
-                                       $status = $user->changeAuthenticationData( [
-                                               'username' => $user->getName(),
-                                               'password' => $password,
-                                               'retype' => $password,
-                                       ] );
-                                       if ( !$status->isGood() ) {
-                                               throw new PasswordError( $status->getWikiText( null, null, 'en' ) );
-                                       }
+                               $status = $user->changeAuthenticationData( [
+                                       'username' => $user->getName(),
+                                       'password' => $password,
+                                       'retype' => $password,
+                               ] );
+                               if ( !$status->isGood() ) {
+                                       throw new PasswordError( $status->getWikiText( null, null, 'en' ) );
                                }
                                if ( $exists ) {
                                        $this->output( "Password set.\n" );
index d07a8d4..af05a81 100644 (file)
@@ -54,7 +54,7 @@ class DeleteArchivedFiles extends Maintenance {
                $this->output( "Searching for and deleting archived files...\n" );
                $res = $dbw->select(
                        'filearchive',
-                       [ 'fa_id', 'fa_storage_group', 'fa_storage_key', 'fa_sha1' ],
+                       [ 'fa_id', 'fa_storage_group', 'fa_storage_key', 'fa_sha1', 'fa_name' ],
                        '',
                        __METHOD__
                );
@@ -67,9 +67,19 @@ class DeleteArchivedFiles extends Maintenance {
                                continue;
                        }
 
+                       /** @var LocalFile $file */
+                       $file = $repo->newFile( $row->fa_name );
+                       try {
+                               $file->lock();
+                       } catch ( LocalFileLockError $e ) {
+                               $this->error( "Could not acquire lock on '{$row->fa_name}', skipping\n" );
+                               continue;
+                       }
+
                        $group = $row->fa_storage_group;
                        $id = $row->fa_id;
-                       $path = $repo->getZonePath( 'deleted' ) . '/' . $repo->getDeletedHashPath( $key ) . $key;
+                       $path = $repo->getZonePath( 'deleted' ) .
+                               '/' . $repo->getDeletedHashPath( $key ) . $key;
                        if ( isset( $row->fa_sha1 ) ) {
                                $sha1 = $row->fa_sha1;
                        } else {
@@ -96,6 +106,7 @@ class DeleteArchivedFiles extends Maintenance {
                                $this->output( "Notice - file '$key' is still in use\n" );
                        } elseif ( !$repo->quickPurge( $path ) ) {
                                $this->output( "Unable to remove file $path, skipping\n" );
+                               $file->unlock();
                                continue; // don't delete even with --force
                        } else {
                                $needForce = false;
@@ -105,12 +116,14 @@ class DeleteArchivedFiles extends Maintenance {
                                if ( $this->hasOption( 'force' ) ) {
                                        $this->output( "Got --force, deleting DB entry\n" );
                                } else {
+                                       $file->unlock();
                                        continue;
                                }
                        }
 
                        $count++;
                        $dbw->delete( 'filearchive', [ 'fa_id' => $id ], __METHOD__ );
+                       $file->unlock();
                }
 
                $this->commitTransaction( $dbw, __METHOD__ );
index 35a7ca7..69f4f89 100644 (file)
@@ -41,7 +41,7 @@ class DeleteDefaultMessages extends Maintenance {
                global $wgUser;
 
                $this->output( "Checking existence of old default messages..." );
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $res = $dbr->select( [ 'page', 'revision' ],
                        [ 'page_namespace', 'page_title' ],
                        [
index 000e5f9..95bd089 100644 (file)
@@ -99,6 +99,13 @@ require_once "$IP/includes/Setup.php";
 // Initialize main config instance
 $maintenance->setConfig( ConfigFactory::getDefaultInstance()->makeConfig( 'main' ) );
 
+// Sanity-check required extensions are installed
+$maintenance->checkRequiredExtensions();
+
+// A good time when no DBs have writes pending is around lag checks.
+// This avoids having long running scripts just OOM and lose all the updates.
+$maintenance->setTriggers();
+
 // Do the work
 $maintenance->execute();
 
index 1faeb8a..ff4e894 100644 (file)
@@ -44,7 +44,7 @@ class DumpLinks extends Maintenance {
        }
 
        public function execute() {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $result = $dbr->select( [ 'pagelinks', 'page' ],
                        [
                                'page_id',
index cfb59c6..d0bda4e 100644 (file)
@@ -222,7 +222,7 @@ TEXT
 
                // 2. The Connection, through the load balancer.
                try {
-                       $this->db = $this->lb->getConnection( DB_SLAVE, 'dump' );
+                       $this->db = $this->lb->getConnection( DB_REPLICA, 'dump' );
                } catch ( Exception $e ) {
                        throw new MWException( __METHOD__
                                . " rotating DB failed to obtain new database (" . $e->getMessage() . ")" );
index 5b446d8..8d63fe5 100644 (file)
@@ -76,7 +76,7 @@ By default, outputs relative paths against the parent directory of $wgUploadDire
         * @param bool $shared True to pass shared-dir settings to hash func
         */
        function fetchUsed( $shared ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $image = $dbr->tableName( 'image' );
                $imagelinks = $dbr->tableName( 'imagelinks' );
 
@@ -97,7 +97,7 @@ By default, outputs relative paths against the parent directory of $wgUploadDire
         * @param bool $shared True to pass shared-dir settings to hash func
         */
        function fetchLocal( $shared ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $result = $dbr->select( 'image',
                        [ 'img_name' ],
                        '',
index 7e1b527..2ed1efa 100644 (file)
@@ -49,7 +49,7 @@ class FetchText extends Maintenance {
         * note that the text string itself is *not* followed by newline
         */
        public function execute() {
-               $db = $this->getDB( DB_SLAVE );
+               $db = $this->getDB( DB_REPLICA );
                $stdin = $this->getStdin();
                while ( !feof( $stdin ) ) {
                        $line = fgets( $stdin );
index bc1b34a..94b7fb4 100644 (file)
@@ -71,6 +71,9 @@ class DeprecatedInterfaceFinder extends FileAwareNodeVisitor {
         * indicating that it is a hard-deprecated interface.
         */
        public function isHardDeprecated( PhpParser\Node $node ) {
+               if ( !$node->stmts ) {
+                       return false;
+               }
                foreach ( $node->stmts as $stmt ) {
                        if (
                                $stmt instanceof PhpParser\Node\Expr\FuncCall
@@ -142,7 +145,7 @@ class FindDeprecated extends Maintenance {
                $files = $this->getFiles();
                $chunkSize = ceil( count( $files ) / 72 );
 
-               $parser = new PhpParser\Parser( new PhpParser\Lexer\Emulative );
+               $parser = ( new PhpParser\ParserFactory )->create( PhpParser\ParserFactory::PREFER_PHP7 );
                $traverser = new PhpParser\NodeTraverser;
                $finder = new DeprecatedInterfaceFinder;
                $traverser->addVisitor( $finder );
index 232151b..460b553 100644 (file)
@@ -47,7 +47,7 @@ class FixDefaultJsonContentPages extends LoggedUpdateMaintenance {
                        return true;
                }
 
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $namespaces = [
                        NS_MEDIAWIKI => $dbr->buildLike( $dbr->anyString(), '.json' ),
                        NS_USER => $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString(), '.json' ),
index 0592d2d..1d6f31d 100644 (file)
@@ -54,7 +54,7 @@ class FixDoubleRedirects extends Maintenance {
                        $title = null;
                }
 
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                // See also SpecialDoubleRedirects
                $tables = [
index f4674cb..37fd44f 100644 (file)
@@ -80,7 +80,7 @@ class FixUserRegistration extends Maintenance {
                                        $this->output( "Could not find registration for #$id NULL\n" );
                                }
                        }
-                       $this->output( "Waiting for slaves..." );
+                       $this->output( "Waiting for replica DBs..." );
                        wfWaitForSlaves();
                        $this->output( " done.\n" );
                } while ( $res->numRows() >= $this->mBatchSize );
index 8b1d86d..0c278bc 100644 (file)
@@ -10,12 +10,11 @@ require_once __DIR__ . '/../includes/utils/AutoloadGenerator.php';
 $base = dirname( __DIR__ );
 
 $generator = new AutoloadGenerator( $base, 'local' );
-foreach ( [ 'includes', 'languages', 'maintenance', 'mw-config' ] as $dir ) {
-       $generator->readDir( $base . '/' . $dir );
-}
-foreach ( glob( $base . '/*.php' ) as $file ) {
-       $generator->readFile( $file );
-}
+$generator->initMediaWikiDefault();
 
 // Write out the autoload
-$generator->generateAutoload( 'maintenance/generateLocalAutoload.php' );
+$fileinfo = $generator->getTargetFileinfo();
+file_put_contents(
+       $fileinfo['filename'],
+       $generator->getAutoload( 'maintenance/generateLocalAutoload.php' )
+);
index f5cc6f6..87af5b8 100644 (file)
@@ -113,7 +113,7 @@ class GenerateSitemap extends Maintenance {
        public $timestamp;
 
        /**
-        * A database slave object
+        * A database replica DB object
         *
         * @var object
         */
@@ -196,7 +196,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->dbr = $this->getDB( DB_SLAVE );
+               $this->dbr = $this->getDB( DB_REPLICA );
                $this->generateNamespaces();
                $this->timestamp = wfTimestamp( TS_ISO_8601, wfTimestampNow() );
                $this->findex = fopen( "{$this->fspath}sitemap-index-{$this->identifier}.xml", 'wb' );
diff --git a/maintenance/getReplicaServer.php b/maintenance/getReplicaServer.php
new file mode 100644 (file)
index 0000000..6e0a1fe
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Reports the hostname of a replica DB server.
+ *
+ * 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';
+
+/**
+ * Maintenance script that reports the hostname of a replica DB server.
+ *
+ * @ingroup Maintenance
+ */
+class GetSlaveServer extends Maintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->addOption( "group", "Query group to check specifically" );
+               $this->addDescription( 'Report the hostname of a replica DB server' );
+       }
+
+       public function execute() {
+               global $wgAllDBsAreLocalhost;
+               if ( $wgAllDBsAreLocalhost ) {
+                       $host = 'localhost';
+               } elseif ( $this->hasOption( 'group' ) ) {
+                       $db = $this->getDB( DB_REPLICA, $this->getOption( 'group' ) );
+                       $host = $db->getServer();
+               } else {
+                       $lb = wfGetLB();
+                       $i = $lb->getReaderIndex();
+                       $host = $lb->getServerName( $i );
+               }
+               $this->output( "$host\n" );
+       }
+}
+
+$maintClass = "GetSlaveServer";
+require_once RUN_MAINTENANCE_IF_MAIN;
index 81228cc..2160928 100644 (file)
@@ -1,55 +1,3 @@
 <?php
-/**
- * Reports the hostname of a slave server.
- *
- * 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';
-
-/**
- * Maintenance script that reports the hostname of a slave server.
- *
- * @ingroup Maintenance
- */
-class GetSlaveServer extends Maintenance {
-       public function __construct() {
-               parent::__construct();
-               $this->addOption( "group", "Query group to check specifically" );
-               $this->addDescription( 'Report the hostname of a slave server' );
-       }
-
-       public function execute() {
-               global $wgAllDBsAreLocalhost;
-               if ( $wgAllDBsAreLocalhost ) {
-                       $host = 'localhost';
-               } elseif ( $this->hasOption( 'group' ) ) {
-                       $db = $this->getDB( DB_SLAVE, $this->getOption( 'group' ) );
-                       $host = $db->getServer();
-               } else {
-                       $lb = wfGetLB();
-                       $i = $lb->getReaderIndex();
-                       $host = $lb->getServerName( $i );
-               }
-               $this->output( "$host\n" );
-       }
-}
-
-$maintClass = "GetSlaveServer";
-require_once RUN_MAINTENANCE_IF_MAIN;
+// B/C alias
+require_once ( __DIR__ . '/getReplicaServer.php' );
diff --git a/maintenance/hhvm/makeRepo.php b/maintenance/hhvm/makeRepo.php
new file mode 100644 (file)
index 0000000..a0fe381
--- /dev/null
@@ -0,0 +1,161 @@
+<?php
+
+require __DIR__ . '/../Maintenance.php';
+
+class HHVMMakeRepo extends Maintenance {
+       function __construct() {
+               parent::__construct();
+               $this->addDescription( 'Compile PHP sources for this MediaWiki instance, ' .
+                       'and generate an HHVM bytecode file to be used with HHVM\'s ' .
+                       'RepoAuthoritative mode. The MediaWiki core installation path and ' .
+                       'all registered extensions are automatically searched for the file ' .
+                       'extensions *.php, *.inc, *.php5 and *.phtml.' );
+               $this->addOption( 'output', 'Output filename', true, true, 'o' );
+               $this->addOption( 'input-dir', 'Add an input directory. ' .
+                       'This can be specified multiple times.', false, true, 'd', true );
+               $this->addOption( 'exclude-dir', 'Directory to exclude. ' .
+                       'This can be specified multiple times.', false, true, false, true );
+               $this->addOption( 'extension', 'Extra file extension', false, true, false, true );
+               $this->addOption( 'hhvm', 'Location of HHVM binary', false, true );
+               $this->addOption( 'base-dir', 'The root of all source files. ' .
+                       'This must match hhvm.server.source_root in the server\'s configuration file. ' .
+                       'By default, the MW core install path will be used.',
+                       false, true );
+               $this->addOption( 'verbose', 'Log level 0-3', false, true, 'v' );
+       }
+
+       private static function startsWith( $subject, $search ) {
+               return substr( $subject, 0, strlen( $search ) === $search );
+       }
+
+       function execute() {
+               global $wgExtensionCredits, $IP;
+
+               $dirs = [ $IP ];
+
+               foreach ( $wgExtensionCredits as $type => $extensions ) {
+                       foreach ( $extensions as $extension ) {
+                               if ( isset( $extension['path'] )
+                                       && !self::startsWith( $extension['path'], $IP )
+                               ) {
+                                       $dirs[] = dirname( $extension['path'] );
+                               }
+                       }
+               }
+
+               $dirs = array_merge( $dirs, $this->getOption( 'input-dir', [] ) );
+               $fileExts =
+                       [
+                               'php' => true,
+                               'inc' => true,
+                               'php5' => true,
+                               'phtml' => true
+                       ] +
+                       array_flip( $this->getOption( 'extension', [] ) );
+
+               $dirs = array_unique( $dirs );
+
+               $baseDir = $this->getOption( 'base-dir', $IP );
+               $excludeDirs = array_map( 'realpath', $this->getOption( 'exclude-dir', [] ) );
+
+               if ( $baseDir !== '' && substr( $baseDir, -1 ) !== '/' ) {
+                       $baseDir .= '/';
+               }
+
+               $unfilteredFiles = [ "$IP/LocalSettings.php" ];
+               foreach ( $dirs as $dir ) {
+                       $this->appendDir( $unfilteredFiles, $dir );
+               }
+
+               $files = [];
+               foreach ( $unfilteredFiles as $file ) {
+                       $dotPos = strrpos( $file, '.' );
+                       $slashPos = strrpos( $file, '/' );
+                       if ( $dotPos === false || $slashPos === false || $dotPos < $slashPos ) {
+                               continue;
+                       }
+                       $extension = substr( $file, $dotPos + 1 );
+                       if ( !isset( $fileExts[$extension] ) ) {
+                               continue;
+                       }
+                       $canonical = realpath( $file );
+                       foreach ( $excludeDirs as $excluded ) {
+                               if ( self::startsWith( $canonical, $excluded ) ) {
+                                       continue 2;
+                               }
+                       }
+                       if ( self::startsWith( $file, $baseDir ) ) {
+                               $file = substr( $file, strlen( $baseDir ) );
+                       }
+                       $files[] = $file;
+               }
+
+               $files = array_unique( $files );
+
+               print "Found " . count( $files ) . " files in " .
+                       count( $dirs ) . " directories\n";
+
+               $tmpDir = wfTempDir() . '/mw-make-repo' . mt_rand( 0, 1<<31 );
+               if ( !mkdir( $tmpDir ) ) {
+                       $this->error( 'Unable to create temporary directory', 1 );
+               }
+               file_put_contents( "$tmpDir/file-list", implode( "\n", $files ) );
+
+               $hhvm = $this->getOption( 'hhvm', 'hhvm' );
+               $verbose = $this->getOption( 'verbose', 3 );
+               $cmd = wfEscapeShellArg(
+                       $hhvm,
+                       '--hphp',
+                   '--target', 'hhbc',
+                       '--format', 'binary',
+                       '--force', '1',
+                       '--keep-tempdir', '1',
+                       '--log', $verbose,
+                       '-v', 'AllVolatile=true',
+                       '--input-dir', $baseDir,
+                       '--input-list', "$tmpDir/file-list",
+                       '--output-dir', $tmpDir );
+               print "$cmd\n";
+               passthru( $cmd, $ret );
+               if ( $ret ) {
+                       $this->cleanupTemp( $tmpDir );
+                       $this->error( "Error: HHVM returned error code $ret", 1 );
+               }
+               if ( !rename( "$tmpDir/hhvm.hhbc", $this->getOption( 'output' ) ) ) {
+                       $this->cleanupTemp( $tmpDir );
+                       $this->error( "Error: unable to rename output file", 1 );
+               }
+               $this->cleanupTemp( $tmpDir );
+               return 0;
+       }
+
+       private function cleanupTemp( $tmpDir ) {
+               if ( file_exists( "$tmpDir/hhvm.hhbc" ) ) {
+                       unlink( "$tmpDir/hhvm.hhbc" );
+               }
+               if ( file_exists( "$tmpDir/Stats.js" ) ) {
+                       unlink( "$tmpDir/Stats.js" );
+               }
+
+               unlink( "$tmpDir/file-list" );
+               rmdir( $tmpDir );
+       }
+
+       private function appendDir( &$files, $dir ) {
+               $iter = new RecursiveIteratorIterator(
+                       new RecursiveDirectoryIterator(
+                               $dir,
+                               FilesystemIterator::UNIX_PATHS
+                       ),
+                       RecursiveIteratorIterator::LEAVES_ONLY
+               );
+               foreach ( $iter as $file => $fileInfo ) {
+                       if ( $fileInfo->isFile() ) {
+                               $files[] = $file;
+                       }
+               }
+       }
+}
+
+$maintClass = 'HHVMMakeRepo';
+require RUN_MAINTENANCE_IF_MAIN;
diff --git a/maintenance/hhvm/run-server b/maintenance/hhvm/run-server
new file mode 100755 (executable)
index 0000000..2d71b87
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/hhvm -f
+<?php
+
+require __DIR__ . '/../Maintenance.php';
+
+class RunHipHopServer extends Maintenance {
+       function __construct() {
+               parent::__construct();
+       }
+
+       function execute() {
+               global $IP;
+
+               passthru(
+                       'cd ' . wfEscapeShellArg( $IP ) . " && " .
+                       wfEscapeShellArg(
+                               'hhvm',
+                               '-c', __DIR__."/server.conf",
+                               '--mode=server',
+                               '--port=8080'
+                       ),
+                       $ret
+               );
+               exit( $ret );
+       }
+}
+$maintClass = 'RunHipHopServer';
+require_once RUN_MAINTENANCE_IF_MAIN;
diff --git a/maintenance/hhvm/server.conf b/maintenance/hhvm/server.conf
new file mode 100644 (file)
index 0000000..558bdad
--- /dev/null
@@ -0,0 +1,30 @@
+Log {
+       Level = Warning
+       UseLogFile = true
+       NativeStackTrace = true
+       InjectedStackTrace = true
+}
+Debug {
+       FullBacktrace = true
+       ServerStackTrace = true
+       ServerErrorMessage = true
+       TranslateSource = true
+}
+Server {
+       EnableStaticContentCache = false
+       EnableStaticContentFromDisk = true
+       AlwaysUseRelativePath = true
+}
+VirtualHost {
+       * {
+               ServerName = localhost
+               Pattern = .
+               RewriteRules {
+                       * {
+                               pattern = ^/wiki/(.*)$
+                               to = /index.php?title=$1
+                               qsa = true
+                       }
+               }
+       }
+}
diff --git a/maintenance/hiphop/run-server b/maintenance/hiphop/run-server
deleted file mode 100755 (executable)
index 2d71b87..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/hhvm -f
-<?php
-
-require __DIR__ . '/../Maintenance.php';
-
-class RunHipHopServer extends Maintenance {
-       function __construct() {
-               parent::__construct();
-       }
-
-       function execute() {
-               global $IP;
-
-               passthru(
-                       'cd ' . wfEscapeShellArg( $IP ) . " && " .
-                       wfEscapeShellArg(
-                               'hhvm',
-                               '-c', __DIR__."/server.conf",
-                               '--mode=server',
-                               '--port=8080'
-                       ),
-                       $ret
-               );
-               exit( $ret );
-       }
-}
-$maintClass = 'RunHipHopServer';
-require_once RUN_MAINTENANCE_IF_MAIN;
diff --git a/maintenance/hiphop/server.conf b/maintenance/hiphop/server.conf
deleted file mode 100644 (file)
index 558bdad..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-Log {
-       Level = Warning
-       UseLogFile = true
-       NativeStackTrace = true
-       InjectedStackTrace = true
-}
-Debug {
-       FullBacktrace = true
-       ServerStackTrace = true
-       ServerErrorMessage = true
-       TranslateSource = true
-}
-Server {
-       EnableStaticContentCache = false
-       EnableStaticContentFromDisk = true
-       AlwaysUseRelativePath = true
-}
-VirtualHost {
-       * {
-               ServerName = localhost
-               Pattern = .
-               RewriteRules {
-                       * {
-                               pattern = ^/wiki/(.*)$
-                               to = /index.php?title=$1
-                               qsa = true
-                       }
-               }
-       }
-}
index c653a5f..ac700ef 100644 (file)
@@ -297,8 +297,8 @@ if ( $count > 0 ) {
 
                        if ( $doProtect ) {
                                # Protect the file
-                               echo "\nWaiting for slaves...\n";
-                               // Wait for slaves.
+                               echo "\nWaiting for replica DBs...\n";
+                               // Wait for replica DBs.
                                sleep( 2.0 ); # Why this sleep?
                                wfWaitForSlaves();
 
index 88ee9d7..5531ffc 100644 (file)
@@ -63,7 +63,16 @@ class ImportTextFiles extends Maintenance {
                        if ( file_exists( $arg ) ) {
                                $files[$arg] = file_get_contents( $arg );
                        } else {
-                               $this->error( "Fatal error: The file '$arg' does not exist!", 1 );
+                               // use glob to support the Windows shell, which doesn't automatically
+                               // expand wildcards
+                               $found = false;
+                               foreach ( glob( $arg ) as $filename ) {
+                                       $found = true;
+                                       $files[$filename] = file_get_contents( $filename );
+                               }
+                               if ( !$found ) {
+                                       $this->error( "Fatal error: The file '$arg' does not exist!", 1 );
+                               }
                        }
                };
 
index c219b9b..6b06da7 100644 (file)
@@ -29,11 +29,10 @@ class InitEditCount extends Maintenance {
                parent::__construct();
                $this->addOption( 'quick', 'Force the update to be done in a single query' );
                $this->addOption( 'background', 'Force replication-friendly mode; may be inefficient but
-               avoids locking tables or lagging slaves with large updates;
-               calculates counts on a slave if possible.
+               avoids locking tables or lagging replica DBs with large updates;
+               calculates counts on a replica DB if possible.
 
-Background mode will be automatically used if the server is MySQL 4.0
-(which does not support subqueries) or if multiple servers are listed
+Background mode will be automatically used if multiple servers are listed
 in the load balancer, usually indicating a replication environment.' );
                $this->addDescription( 'Batch-recalculate user_editcount fields from the revision table' );
        }
@@ -46,19 +45,18 @@ in the load balancer, usually indicating a replication environment.' );
                $dbver = $dbw->getServerVersion();
 
                // Autodetect mode...
-               $backgroundMode = wfGetLB()->getServerCount() > 1 ||
-                       ( $dbw instanceof DatabaseMysql );
-
                if ( $this->hasOption( 'background' ) ) {
                        $backgroundMode = true;
                } elseif ( $this->hasOption( 'quick' ) ) {
                        $backgroundMode = false;
+               } else {
+                       $backgroundMode = wfGetLB()->getServerCount() > 1;
                }
 
                if ( $backgroundMode ) {
                        $this->output( "Using replication-friendly background mode...\n" );
 
-                       $dbr = $this->getDB( DB_SLAVE );
+                       $dbr = $this->getDB( DB_REPLICA );
                        $chunkSize = 100;
                        $lastUser = $dbr->selectField( 'user', 'MAX(user_id)', '', __METHOD__ );
 
@@ -96,7 +94,6 @@ in the load balancer, usually indicating a replication environment.' );
                                wfWaitForSlaves();
                        }
                } else {
-                       // Subselect should work on modern MySQLs etc
                        $this->output( "Using single-query mode...\n" );
                        $sql = "UPDATE $user SET user_editcount=(SELECT COUNT(*) FROM $revision WHERE rev_user=user_id)";
                        $dbw->query( $sql );
index 4899143..b1e0fa4 100644 (file)
@@ -26,7 +26,7 @@ imdb|http://www.imdb.com/find?q=$1&tt=on|0|
 jargonfile|http://sunir.org/apps/meta.pl?wiki=JargonFile&redirect=$1|0|
 kmwiki|http://kmwiki.wikispaces.com/$1|0|
 linuxwiki|http://linuxwiki.de/$1|0|
-lojban|http://www.lojban.org/tiki/tiki-index.php?page=$1|0|
+lojban|http://mw.lojban.org/papri/$1|0|
 lqwiki|http://wiki.linuxquestions.org/wiki/$1|0|
 lugkr|http://www.lug-kr.de/wiki/$1|0|
 meatball|http://www.usemod.com/cgi-bin/mb.pl?$1|0|
@@ -61,6 +61,7 @@ webseitzwiki|http://webseitz.fluxent.com/wiki/$1|0|
 wiki|http://c2.com/cgi/wiki?$1|0|
 wikia|http://www.wikia.com/wiki/$1|0|
 wikibooks|https://en.wikibooks.org/wiki/$1|0|https://en.wikibooks.org/w/api.php
+wikidata|https://www.wikidata.org/wiki/$1|0|https://www.wikidata.org/w/api.php
 wikif1|http://www.wikif1.org/$1|0|
 wikihow|http://www.wikihow.com/$1|0|http://www.wikihow.com/api.php
 wikinfo|http://wikinfo.co/English/index.php/$1|0|
index 12352e7..b7d1a84 100644 (file)
@@ -63,6 +63,7 @@ REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local,iw_api) VALUES
 ('wiki','http://c2.com/cgi/wiki?$1',0,''),
 ('wikia','http://www.wikia.com/wiki/$1',0,''),
 ('wikibooks','https://en.wikibooks.org/wiki/$1',0,'https://en.wikibooks.org/w/api.php'),
+('wikidata','https://www.wikidata.org/wiki/$1',0,'https://www.wikidata.org/w/api.php'),
 ('wikif1','http://www.wikif1.org/$1',0,''),
 ('wikihow','http://www.wikihow.com/$1',0,'http://www.wikihow.com/api.php'),
 ('wikinfo','http://wikinfo.co/English/index.php/$1',0,''),
index d9e2c50..068ee8c 100644 (file)
@@ -64,7 +64,8 @@
                                        "mw.Feedback*",
                                        "mw.Upload*",
                                        "mw.ForeignUpload",
-                                       "mw.ForeignStructuredUpload*"
+                                       "mw.ForeignStructuredUpload*",
+                                       "mw.GallerySlideshow"
                                ]
                        },
                        {
index 05f091f..bced265 100644 (file)
@@ -201,7 +201,7 @@ U+090C1郁|U+090C1郁|U+09B31鬱|
 U+0915D酝|U+0919E醞|U+09196醖|
 U+09170酰|U+09170酰|U+091AF醯|
 U+09178酸|U+09178酸|U+075E0痠|
-U+091C7采|U+091C7采|U+063A1採|U+057F0埰|
+U+091C7采|U+091C7采|U+063A1採|U+05BC0寀|
 U+091CC里|U+091CC里|U+088E1裡|U+088CF裏|
 U+0949F钟|U+0937E鍾|U+09418鐘|
 U+094A9钩|U+0920E鈎|U+09264鉤|
index a81422d..bae5fd8 100644 (file)
 伏著 伏着
 視著 视着
 視著名      视著名
+視著作      视著作
+視著者      视著者
+視著稱      视著称
 蓋著 盖着
 蓋著名      盖著名
 蓋著稱      盖著称
 功能變數名稱     域名
 音效卡      声卡
 字型大小   字号
-字型檔      字库
 欄位 字段
 非同步      异步
 匯流排      总线
index 7bd993f..31e02c2 100644 (file)
 一地里      一地裏
 一年里      一年裏
 中文里      中文裏
+英文里      英文裏
+古文里      古文裏
+经文里      經文裏
+论文里      論文裏
+譯文里      譯文裏
+原文里      原文裏
+正文里      正文裏
+下文里      下文裏
+条文里      條文裏
+画里 畫裏
 事里 事裏
 井里 井裏
 作品里      作品裏
 会里 會裏
 村里的      村裏的
 村里有      村裏有
+区里的      區裏的
+区里有      區裏有
 森林里      森林裏
 棺材里      棺材裏
 树林里      樹林裏
 电视里      電視裏
 公寓里      公寓裏
 窝里斗      窩裏鬥
+镇里 鎮裏
 苑裡 苑裡
 霄裡 霄裡
 岸裡 岸裡
 伏著 伏着
 視著 視着
 視著名      視著名
+視著作      視著作
+視著者      視著者
+視著稱      視著稱
 蓋著 蓋着
 蓋著名      蓋著名
 蓋著稱      蓋著稱
index 9674aae..8618a93 100644 (file)
 偵蒐 侦搜
 情蒐 情搜
 蘋果 苹果
+蘋婆 苹婆
 於之莹      於之莹
 陆徵祥      陆徵祥
 瞭臺 瞭台
index b840408..075deab 100644 (file)
 划着独木舟        划著獨木舟
 着眼于      著眼於
 桃金娘      桃金孃
+粘膜 黏膜
 缺省 預設
 以太网      乙太網
 光盘 光碟
 光驱 光碟機
 声卡 音效卡
-字库 字型檔
 字段 欄位
 存盘 存檔
 控件 控制項
 撒切尔      柴契爾
 戴卓爾      柴契爾
 摩根士丹利        摩根史坦利
-拉普兰      拉布蘭
 戴克里先   戴克里先
 戈爾巴喬夫        戈巴契夫
 戈尔巴乔夫        戈巴契夫
index 83a7a54..3062c1e 100644 (file)
 复苏 復甦
 苹果 蘋果
 苹果干      蘋果乾
+苹婆 蘋婆
 龜山庄      龜山庄
 寶山庄      寶山庄
 員山庄      員山庄
 冬冬鼓      鼕鼕鼓
 苧麻 苧麻
 张柏芝      張栢芝
+杜琪峰      杜琪峯
index 56a5e21..dd38a30 100644 (file)
 皺彆
 一彆頭
 并州
+幽并
+併力
 ,並力
-,并力攻
 ,并力討
+兼併
 併兼
 併骨
 併網
 扁擬穀盜蟲
 不穀
 辟穀
-米穀
-田穀
 脫穀機
 年穀
 礱穀
 錢穀
 息穀
 殖穀
-川穀
 曬穀
 臧穀亡羊
 種穀
 月球曆表
 伊爾汗曆表
 延曆
+萬曆
+永曆
+聖人曆
+羅馬曆
+羅馬歷史
+羅馬歷代
+曆數書
+曆局
+授時曆
+顓頊曆
 共和歷史
 厤物之意
 爰定祥厤
 磨麵
 莜麵
 雲吞麵
+拌麵
+乾拌麵
 冷面相
 糞穢衊面
 僕僕
 松山庄
 香山庄
 中庄子
+新庄子
 田庄英雄
 本庄
 庄司
 乾姐
 乾紅葡萄酒
 乾白葡萄酒
+抽乾
+排乾
+排幹部
+吸乾
 楨幹
 新幹縣
 誰幹的
 鍥而不捨
 稜登
 繃扒弔拷
-不弔
+不弔
 不通弔慶
 陪弔
 盆弔
 鬼谷子
 谷子敬
 洪谷子
-西米谷
-世田谷
 聖馬爾谷日
 澀谷區
 開山闢谷
 鬥敗
 鬥戰
 窩裡鬥
+亂鬥
 石樑
 木樑
 藏歷史
 裡面
 這裡
 中文裡
+英文裡
+古文裡
+經文裡
+論文裡
+譯文裡
+原文裡
+正文裡
+下文裡
+條文裡
+畫裡
 洞裡
 洞里薩
 界裡
 公寓里弄
 村裡的
 村裡有
+鎮裡
+區裡的
+區裡有
+實驗裡
+註裡
 裏白 #植物常用名
 烏蘇里 #分詞用
 首發
 涂醒哲
 涂善妮
 涂敏恆
+涂爾幹
 故云
 強制作用
 鬱南
 卜云吉
 黎吉雲
 代表
°´ç\84¡æ\80\9cå¥\88
+怜奈
 于冠華
 于雲鶴
 于忠肅集
 不太準
 非常準
 很準
-萬曆
-永曆
 囓蟲
 勳勞
 勳績
 煙臺
 太醜
 御製
-聖人曆
 電影後
 封為后
 皮托管
 白面包青天
 天神之后
-羅馬曆
-羅馬歷史
-羅馬歷代
-曆數書
-曆局
 你誇
 誇你
 誇我
 控制
 限制
 強制
+改制成
+考試制度
 體徵
 綜合徵
 价川
 琺瑯
 菜餚
 梁啓超
-改制成
 王添灯
 腌臢
 風颳
index 12cfed8..ea087a6 100644 (file)
@@ -336,9 +336,9 @@ CREATE INDEX /*i*/cl_timestamp ON /*_*/categorylinks (cl_to,cl_timestamp);
 CREATE INDEX /*i*/cl_collation_ext ON /*_*/categorylinks (cl_collation, cl_to, cl_type, cl_from);
 
 --
--- Track all existing categories.  Something is a category if 1) it has an en-
--- try somewhere in categorylinks, or 2) it once did.  Categories might not
--- have corresponding pages, so they need to be tracked separately.
+-- Track all existing categories. Something is a category if 1) it has an entry
+-- somewhere in categorylinks, or 2) it has a description page. Categories
+-- might not have corresponding pages, so they need to be tracked separately.
 --
 CREATE TABLE /*_*/category (
   -- Primary key
@@ -861,6 +861,7 @@ CREATE INDEX /*i*/new_name_timestamp ON /*_*/recentchanges (rc_new,rc_namespace,
 CREATE INDEX /*i*/rc_ip ON /*_*/recentchanges (rc_ip);
 CREATE INDEX /*i*/rc_ns_usertext ON /*_*/recentchanges (rc_namespace, rc_user_text);
 CREATE INDEX /*i*/rc_user_text ON /*_*/recentchanges (rc_user_text, rc_timestamp);
+CREATE INDEX /*i*/rc_name_type_patrolled_timestamp ON /*_*/recentchanges (rc_namespace, rc_type, rc_patrolled, rc_timestamp);
 
 
 CREATE TABLE /*_*/watchlist (
index 8fd25a6..506bc9c 100644 (file)
@@ -596,6 +596,8 @@ class NamespaceConflictChecker extends Maintenance {
 
                $this->db->delete( 'page', [ 'page_id' => $id ], __METHOD__ );
 
+               $this->commitTransaction( $this->db, __METHOD__ );
+
                /* Call LinksDeletionUpdate to delete outgoing links from the old title,
                 * and update category counts.
                 *
@@ -605,9 +607,8 @@ class NamespaceConflictChecker extends Maintenance {
                 * accidentally introduce an assumption of title validity to the code we
                 * are calling.
                 */
-               $update = new LinksDeletionUpdate( $wikiPage );
-               $update->doUpdate();
-               $this->commitTransaction( $this->db, __METHOD__ );
+               DeferredUpdates::addUpdate( new LinksDeletionUpdate( $wikiPage ) );
+               DeferredUpdates::doUpdates();
 
                return true;
        }
diff --git a/maintenance/oracle/archives/patch-add-rc_name_type_patrolled_timestamp_index.sql b/maintenance/oracle/archives/patch-add-rc_name_type_patrolled_timestamp_index.sql
new file mode 100644 (file)
index 0000000..cd0d396
--- /dev/null
@@ -0,0 +1,4 @@
+define mw_prefix='{$wgDBprefix}';
+
+CREATE INDEX &mw_prefix.recentchanges_i08 ON &mw_prefix.recentchanges (rc_namespace, rc_type, rc_patrolled, rc_timestamp);
+
index 27c3030..d9369c9 100644 (file)
@@ -434,6 +434,7 @@ CREATE INDEX &mw_prefix.recentchanges_i04 ON &mw_prefix.recentchanges (rc_new,rc
 CREATE INDEX &mw_prefix.recentchanges_i05 ON &mw_prefix.recentchanges (rc_ip);
 CREATE INDEX &mw_prefix.recentchanges_i06 ON &mw_prefix.recentchanges (rc_namespace, rc_user_text);
 CREATE INDEX &mw_prefix.recentchanges_i07 ON &mw_prefix.recentchanges (rc_user_text, rc_timestamp);
+CREATE INDEX &mw_prefix.recentchanges_i08 ON &mw_prefix.recentchanges (rc_namespace, rc_type, rc_patrolled, rc_timestamp);
 
 CREATE TABLE &mw_prefix.watchlist (
   wl_id                     NUMBER     NOT NULL,
index 942f3f2..7557a42 100644 (file)
@@ -91,7 +91,7 @@ class PopulateFilearchiveSha1 extends LoggedUpdateMaintenance {
                                break;
                        }
 
-                       // print status and let slaves catch up
+                       // print status and let replica DBs catch up
                        $this->output( sprintf(
                                "id %d done (up to %d), %5.3f%%  \r", $lastId, $endId, $lastId / $endId * 100 ) );
                        wfWaitForSlaves();
index dcb9933..5e44faf 100644 (file)
@@ -73,7 +73,7 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
         * @return int
         */
        protected function doLenUpdates( $table, $idCol, $prefix, $fields ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $dbw = $this->getDB( DB_MASTER );
                $start = $dbw->selectField( $table, "MIN($idCol)", false, __METHOD__ );
                $end = $dbw->selectField( $table, "MAX($idCol)", false, __METHOD__ );
index 61cb198..95c87c0 100644 (file)
@@ -446,6 +446,7 @@ CREATE INDEX rc_namespace_title ON recentchanges (rc_namespace, rc_title);
 CREATE INDEX rc_cur_id          ON recentchanges (rc_cur_id);
 CREATE INDEX new_name_timestamp ON recentchanges (rc_new, rc_namespace, rc_timestamp);
 CREATE INDEX rc_ip              ON recentchanges (rc_ip);
+CREATE INDEX rc_name_type_patrolled_timestamp ON recentchanges (rc_namespace, rc_type, rc_patrolled, rc_timestamp);
 
 
 CREATE SEQUENCE watchlist_wl_id_seq;
index 5b035e1..615d1fe 100644 (file)
@@ -106,7 +106,7 @@ class PurgeChangedFiles extends Maintenance {
                }
 
                // Validate the timestamps
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $this->startTimestamp = $dbr->timestamp( $this->getOption( 'starttime' ) );
                $this->endTimestamp = $dbr->timestamp( $this->getOption( 'endtime' ) );
 
@@ -137,7 +137,7 @@ class PurgeChangedFiles extends Maintenance {
         */
        protected function purgeFromLogType( $type ) {
                $repo = RepoGroup::singleton()->getLocalRepo();
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                foreach ( self::$typeMappings[$type] as $logType => $logActions ) {
                        $this->verbose( "Scanning for {$logType}/" . implode( ',', $logActions ) . "\n" );
@@ -164,8 +164,12 @@ class PurgeChangedFiles extends Maintenance {
                                        continue;
                                }
 
-                               // Purge current version and any versions in oldimage table
+                               // Purge current version and its thumbnails
                                $file->purgeCache();
+                               // Purge the old versions and their thumbnails
+                               foreach ( $file->getHistory() as $oldFile ) {
+                                       $oldFile->purgeCache();
+                               }
 
                                if ( $logType === 'delete' ) {
                                        // If there is an orphaned storage file... delete it
index 58a4640..b354399 100644 (file)
@@ -65,7 +65,7 @@ class PurgeChangedPages extends Maintenance {
                        }
                }
 
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $minTime = $dbr->timestamp( $this->getOption( 'starttime' ) );
                $maxTime = $dbr->timestamp( $this->getOption( 'endtime' ) );
 
index 21d1169..5ca7918 100644 (file)
@@ -86,7 +86,7 @@ class PurgeList extends Maintenance {
         * @param int|bool $namespace
         */
        private function purgeNamespace( $namespace = false ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $startId = 0;
                if ( $namespace === false ) {
                        $conds = [];
index b67bc36..649557e 100644 (file)
@@ -48,7 +48,7 @@ class RebuildFileCache extends Maintenance {
        }
 
        public function execute() {
-               global $wgUseFileCache, $wgReadOnly, $wgContentNamespaces, $wgRequestTime;
+               global $wgUseFileCache, $wgReadOnly, $wgRequestTime;
                global $wgOut;
                if ( !$wgUseFileCache ) {
                        $this->error( "Nothing to do -- \$wgUseFileCache is disabled.", true );
@@ -70,7 +70,7 @@ class RebuildFileCache extends Maintenance {
 
                $this->output( "Building content page file cache from page {$start}!\n" );
 
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $overwrite = $this->getOption( 'overwrite', false );
                $start = ( $start > 0 )
                        ? $start
@@ -94,7 +94,7 @@ class RebuildFileCache extends Maintenance {
                while ( $blockEnd <= $end ) {
                        // Get the pages
                        $res = $dbr->select( 'page', [ 'page_namespace', 'page_title', 'page_id' ],
-                               [ 'page_namespace' => $wgContentNamespaces,
+                               [ 'page_namespace' => MWNamespace::getContentNamespaces(),
                                        "page_id BETWEEN $blockStart AND $blockEnd" ],
                                [ 'ORDER BY' => 'page_id ASC', 'USE INDEX' => 'PRIMARY' ]
                        );
index f67739b..3157186 100644 (file)
@@ -127,7 +127,7 @@ class ImageBuilder extends Maintenance {
                $this->init( $count, $table );
                $this->output( "Processing $table...\n" );
 
-               $result = $this->getDB( DB_SLAVE )->select( $table, '*', [], __METHOD__ );
+               $result = $this->getDB( DB_REPLICA )->select( $table, '*', [], __METHOD__ );
 
                foreach ( $result as $row ) {
                        $update = call_user_func( $callback, $row, null );
index d2ee6fc..95822ca 100644 (file)
@@ -41,7 +41,7 @@ class RebuildAll extends Maintenance {
 
        public function execute() {
                // Rebuild the text index
-               if ( $this->getDB( DB_SLAVE )->getType() != 'postgres' ) {
+               if ( $this->getDB( DB_REPLICA )->getType() != 'postgres' ) {
                        $this->output( "** Rebuilding fulltext search index (if you abort "
                                . "this will break searching; run this script again to fix):\n" );
                        $rebuildText = $this->runChild( 'RebuildTextIndex', 'rebuildtextindex.php' );
index ea5b6f9..e075501 100644 (file)
@@ -47,7 +47,7 @@ class RefreshFileHeaders extends Maintenance {
                $end = str_replace( ' ', '_', $this->getOption( 'end', '' ) ); // page on img_name
 
                $count = 0;
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                do {
                        $conds = [ "img_name > {$dbr->addQuotes( $start )}" ];
                        if ( strlen( $end ) ) {
index bdbb347..aea966f 100644 (file)
@@ -139,6 +139,7 @@ class RefreshImageMetadata extends Maintenance {
                        }
 
                        foreach ( $res as $row ) {
+                               // LocalFile will upgrade immediately here if obsolete
                                $file = $repo->newFileFromRow( $row );
                                if ( $file->getUpgraded() ) {
                                        // File was upgraded.
index e91a3d3..24c8c11 100644 (file)
@@ -74,7 +74,7 @@ class RefreshLinks extends Maintenance {
                $end = null, $redirectsOnly = false, $oldRedirectsOnly = false
        ) {
                $reportingInterval = 100;
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                if ( $start === null ) {
                        $start = 1;
@@ -237,8 +237,9 @@ class RefreshLinks extends Maintenance {
                        return;
                }
 
-               $updates = $content->getSecondaryDataUpdates( $page->getTitle() );
-               DataUpdate::runUpdates( $updates );
+               foreach ( $content->getSecondaryDataUpdates( $page->getTitle() ) as $update ) {
+                       DeferredUpdates::addUpdate( $update );
+               }
        }
 
        /**
@@ -257,7 +258,7 @@ class RefreshLinks extends Maintenance {
        ) {
                wfWaitForSlaves();
                $this->output( "Deleting illegal entries from the links tables...\n" );
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                do {
                        // Find the start of the next chunk. This is based only
                        // on existent page_ids.
@@ -298,7 +299,7 @@ class RefreshLinks extends Maintenance {
         */
        private function dfnCheckInterval( $start = null, $end = null, $batchSize = 100 ) {
                $dbw = $this->getDB( DB_MASTER );
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $linksTables = [ // table name => page_id field
                        'pagelinks' => 'pl_from',
index 0f67317..1034005 100644 (file)
@@ -23,7 +23,7 @@ class RemoveInvalidEmails extends Maintenance {
        }
        public function execute() {
                $this->commit = $this->hasOption( 'commit' );
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $dbw = $this->getDB( DB_MASTER );
                $lastId = 0;
                do {
index e3c99ed..ec8fcfe 100644 (file)
@@ -45,7 +45,7 @@ class RemoveUnusedAccounts extends Maintenance {
                # Do an initial scan for inactive accounts and report the result
                $this->output( "Checking for unused user accounts...\n" );
                $del = [];
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $res = $dbr->select( 'user', [ 'user_id', 'user_name', 'user_touched' ], '', __METHOD__ );
                if ( $this->hasOption( 'ignore-groups' ) ) {
                        $excludedGroups = explode( ',', $this->getOption( 'ignore-groups' ) );
@@ -107,7 +107,7 @@ class RemoveUnusedAccounts extends Maintenance {
         * @return bool
         */
        private function isInactiveAccount( $id, $master = false ) {
-               $dbo = $this->getDB( $master ? DB_MASTER : DB_SLAVE );
+               $dbo = $this->getDB( $master ? DB_MASTER : DB_REPLICA );
                $checks = [
                        'revision' => 'rev',
                        'archive' => 'ar',
index 131a569..481da98 100644 (file)
@@ -67,8 +67,9 @@ class ResetUserTokens extends Maintenance {
                        wfCountDown( 5 );
                }
 
+               // We list user by user_id from one of the replica DBs
                // We list user by user_id from one of the slave database
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $where = [];
                if ( $this->nullsOnly ) {
index 10c107e..5ad7d4e 100644 (file)
@@ -95,7 +95,7 @@ class RollbackEdits extends Maintenance {
         * @return array
         */
        private function getRollbackTitles( $user ) {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $titles = [];
                $results = $dbr->select(
                        [ 'page', 'revision' ],
index 2feae02..a5e7a2f 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Run a database query in batches and wait for slaves. This is used on large
+ * Run a database query in batches and wait for replica DBs. This is used on large
  * wikis to prevent replication lag from going through the roof when executing
  * large write queries.
  *
@@ -26,7 +26,7 @@
 require_once __DIR__ . '/Maintenance.php';
 
 /**
- * Maintenance script to run a database query in batches and wait for slaves.
+ * Maintenance script to run a database query in batches and wait for replica DBs.
  *
  * @ingroup Maintenance
  */
@@ -34,7 +34,7 @@ class BatchedQueryRunner extends Maintenance {
        public function __construct() {
                parent::__construct();
                $this->addDescription(
-                       "Run a query repeatedly until it affects 0 rows, and wait for slaves in between.\n" .
+                       "Run a query repeatedly until it affects 0 rows, and wait for replica DBs in between.\n" .
                                "NOTE: You need to set a LIMIT clause yourself." );
        }
 
index c3c2391..2e011fe 100644 (file)
@@ -53,8 +53,6 @@ class RunJobs extends Maintenance {
        }
 
        public function execute() {
-               global $wgCommandLineMode;
-
                if ( $this->hasOption( 'procs' ) ) {
                        $procs = intval( $this->getOption( 'procs' ) );
                        if ( $procs < 1 || $procs > 1000 ) {
@@ -70,10 +68,6 @@ class RunJobs extends Maintenance {
                $outputJSON = ( $this->getOption( 'result' ) === 'json' );
                $wait = $this->hasOption( 'wait' );
 
-               // Enable DBO_TRX for atomicity; JobRunner manages transactions
-               // and works well in web server mode already (@TODO: this is a hack)
-               $wgCommandLineMode = false;
-
                $runner = new JobRunner( LoggerFactory::getInstance( 'runJobs' ) );
                if ( !$outputJSON ) {
                        $runner->setDebugHandler( [ $this, 'debugInternal' ] );
@@ -111,8 +105,6 @@ class RunJobs extends Maintenance {
 
                        sleep( 1 );
                }
-
-               $wgCommandLineMode = true;
        }
 
        /**
index 1d3e43a..5a15165 100644 (file)
@@ -52,8 +52,8 @@ class ShowSiteStats extends Maintenance {
                        'ss_images' => 'Number of images',
                ];
 
-               // Get cached stats from slave database
-               $dbr = $this->getDB( DB_SLAVE );
+               // Get cached stats from a replica DB
+               $dbr = $this->getDB( DB_REPLICA );
                $stats = $dbr->selectRow( 'site_stats', '*', '', __METHOD__ );
 
                // Get maximum size for each column
index 1aceace..e6a30a3 100644 (file)
@@ -34,10 +34,13 @@ class MwSql extends Maintenance {
                parent::__construct();
                $this->addDescription( 'Send SQL queries to a MediaWiki database. ' .
                        'Takes a file name containing SQL as argument or runs interactively.' );
-               $this->addOption( 'query', 'Run a single query instead of running interactively', false, true );
+               $this->addOption( 'query',
+                       'Run a single query instead of running interactively', false, true );
                $this->addOption( 'cluster', 'Use an external cluster by name', false, true );
-               $this->addOption( 'wikidb', 'The database wiki ID to use if not the current one', false, true );
-               $this->addOption( 'slave', 'Use a slave server (either "any" or by name)', false, true );
+               $this->addOption( 'wikidb',
+                       'The database wiki ID to use if not the current one', false, true );
+               $this->addOption( 'replicadb',
+                       'Replica DB server to use instead of the master DB (can be "any")', false, true );
        }
 
        public function execute() {
@@ -50,30 +53,28 @@ class MwSql extends Maintenance {
                        $lb = wfGetLB( $wiki );
                }
                // Figure out which server to use
-               if ( $this->hasOption( 'slave' ) ) {
-                       $server = $this->getOption( 'slave' );
-                       if ( $server === 'any' ) {
-                               $index = DB_SLAVE;
-                       } else {
-                               $index = null;
-                               $serverCount = $lb->getServerCount();
-                               for ( $i = 0; $i < $serverCount; ++$i ) {
-                                       if ( $lb->getServerName( $i ) === $server ) {
-                                               $index = $i;
-                                               break;
-                                       }
-                               }
-                               if ( $index === null ) {
-                                       $this->error( "No slave server configured with the name '$server'.", 1 );
+               $replicaDB = $this->getOption( 'replicadb', $this->getOption( 'slave', '' ) );
+               if ( $replicaDB === 'any' ) {
+                       $index = DB_REPLICA;
+               } elseif ( $replicaDB != '' ) {
+                       $index = null;
+                       $serverCount = $lb->getServerCount();
+                       for ( $i = 0; $i < $serverCount; ++$i ) {
+                               if ( $lb->getServerName( $i ) === $replicaDB ) {
+                                       $index = $i;
+                                       break;
                                }
                        }
+                       if ( $index === null ) {
+                               $this->error( "No replica DB server configured with the name '$server'.", 1 );
+                       }
                } else {
                        $index = DB_MASTER;
                }
                // Get a DB handle (with this wiki's DB selected) from the appropriate load balancer
                $db = $lb->getConnection( $index, [], $wiki );
-               if ( $this->hasOption( 'slave' ) && $db->getLBInfo( 'master' ) !== null ) {
-                       $this->error( "The server selected ({$db->getServer()}) is not a slave.", 1 );
+               if ( $replicaDB != '' && $db->getLBInfo( 'master' ) !== null ) {
+                       $this->error( "The server selected ({$db->getServer()}) is not a replica DB.", 1 );
                }
 
                if ( $this->hasArg( 0 ) ) {
index 454c506..e74a86c 100644 (file)
@@ -132,7 +132,7 @@ class SqliteMaintenance extends Maintenance {
                        $this->error( "Error: SQLite support not found\n" );
                }
                $files = [ $this->getOption( 'check-syntax' ) ];
-               $files += $this->mArgs;
+               $files = array_merge( $files, $this->mArgs );
                $result = Sqlite::checkSqlSyntax( $files );
                if ( $result === true ) {
                        $this->output( "SQL syntax check: no errors detected.\n" );
index d69e5b9..bb9f4ea 100644 (file)
@@ -57,7 +57,7 @@ class CheckStorage {
        ];
 
        function check( $fix = false, $xml = '' ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                if ( $fix ) {
                        print "Checking, will fix errors if possible...\n";
                } else {
@@ -462,7 +462,7 @@ class CheckStorage {
                        return;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $dbw = wfGetDB( DB_MASTER );
                $dbr->ping();
                $dbw->ping();
@@ -507,7 +507,7 @@ class CheckStorage {
                }
 
                // Find text row again
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $oldId = $dbr->selectField( 'revision', 'rev_text_id', [ 'rev_id' => $id ], __METHOD__ );
                if ( !$oldId ) {
                        echo "Missing revision row for rev_id $id\n";
index ba924bf..289d22d 100644 (file)
@@ -237,7 +237,7 @@ class CompressOld extends Maintenance {
        ) {
                $loadStyle = self::LS_CHUNKED;
 
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $dbw = $this->getDB( DB_MASTER );
 
                # Set up external storage
index 39e06a3..437bfcd 100644 (file)
@@ -36,7 +36,7 @@ class DumpRev extends Maintenance {
        }
 
        public function execute() {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $row = $dbr->selectRow(
                        [ 'text', 'revision' ],
                        [ 'old_flags', 'old_text' ],
index 0ea52ca..b444f31 100644 (file)
@@ -42,7 +42,7 @@ class FixBug20757 extends Maintenance {
        }
 
        function execute() {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $dbw = $this->getDB( DB_MASTER );
 
                $dryRun = $this->getOption( 'dry-run' );
@@ -57,10 +57,8 @@ class FixBug20757 extends Maintenance {
 
                $totalRevs = $dbr->selectField( 'text', 'MAX(old_id)', false, __METHOD__ );
 
-               if ( $dbr->getType() == 'mysql' ) {
-                       // In MySQL 4.1+, the binary field old_text has a non-working LOWER() function
-                       $lowerLeft = 'LOWER(CONVERT(LEFT(old_text,22) USING latin1))';
-               }
+               // In MySQL 4.1+, the binary field old_text has a non-working LOWER() function
+               $lowerLeft = 'LOWER(CONVERT(LEFT(old_text,22) USING latin1))';
 
                while ( true ) {
                        print "ID: $startId / $totalRevs\r";
@@ -283,7 +281,7 @@ class FixBug20757 extends Maintenance {
                                unset( $this->mapCache[$key] );
                        }
 
-                       $dbr = $this->getDB( DB_SLAVE );
+                       $dbr = $this->getDB( DB_REPLICA );
                        $map = [];
                        $res = $dbr->select( 'revision',
                                [ 'rev_id', 'rev_text_id' ],
index 80dc7f9..e117992 100644 (file)
@@ -51,7 +51,7 @@ if ( !defined( 'MEDIAWIKI' ) ) {
 function moveToExternal( $cluster, $maxID, $minID = 1 ) {
        $fname = 'moveToExternal';
        $dbw = wfGetDB( DB_MASTER );
-       $dbr = wfGetDB( DB_SLAVE );
+       $dbr = wfGetDB( DB_REPLICA );
 
        $count = $maxID - $minID + 1;
        $blockSize = 1000;
index d775830..d7d0b84 100644 (file)
@@ -39,11 +39,11 @@ class OrphanStats extends Maintenance {
        protected function &getDB( $cluster, $groups = [], $wiki = false ) {
                $lb = wfGetLBFactory()->getExternalLB( $cluster );
 
-               return $lb->getConnection( DB_SLAVE );
+               return $lb->getConnection( DB_REPLICA );
        }
 
        public function execute() {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                if ( !$dbr->tableExists( 'blob_orphans' ) ) {
                        $this->error( "blob_orphans doesn't seem to exist, need to run trackBlobs.php first", true );
                }
index 292a25d..a0efcb8 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 use MediaWiki\Logger\LegacyLogger;
+use MediaWiki\MediaWikiServices;
 
 $optionsWithArgs = RecompressTracked::getOptionsWithArgs();
 require __DIR__ . '/../commandLine.inc';
@@ -60,17 +61,17 @@ class RecompressTracked {
        public $numProcs = 1;
        public $numBatches = 0;
        public $pageBlobClass, $orphanBlobClass;
-       public $slavePipes, $slaveProcs, $prevSlaveId;
+       public $replicaPipes, $replicaProcs, $prevReplicaId;
        public $copyOnly = false;
        public $isChild = false;
-       public $slaveId = false;
+       public $replicaId = false;
        public $noCount = false;
        public $debugLog, $infoLog, $criticalLog;
        public $store;
 
        private static $optionsWithArgs = [
                'procs',
-               'slave-id',
+               'replica-id',
                'debug-log',
                'info-log',
                'critical-log'
@@ -81,7 +82,7 @@ class RecompressTracked {
                'procs' => 'numProcs',
                'copy-only' => 'copyOnly',
                'child' => 'isChild',
-               'slave-id' => 'slaveId',
+               'replica-id' => 'replicaId',
                'debug-log' => 'debugLog',
                'info-log' => 'infoLog',
                'critical-log' => 'criticalLog',
@@ -109,8 +110,8 @@ class RecompressTracked {
                $this->store = new ExternalStoreDB;
                if ( !$this->isChild ) {
                        $GLOBALS['wgDebugLogPrefix'] = "RCT M: ";
-               } elseif ( $this->slaveId !== false ) {
-                       $GLOBALS['wgDebugLogPrefix'] = "RCT {$this->slaveId}: ";
+               } elseif ( $this->replicaId !== false ) {
+                       $GLOBALS['wgDebugLogPrefix'] = "RCT {$this->replicaId}: ";
                }
                $this->pageBlobClass = function_exists( 'xdiff_string_bdiff' ) ?
                        'DiffHistoryBlob' : 'ConcatenatedGzipHistoryBlob';
@@ -140,21 +141,21 @@ class RecompressTracked {
 
        function logToFile( $msg, $file ) {
                $header = '[' . date( 'd\TH:i:s' ) . '] ' . wfHostname() . ' ' . posix_getpid();
-               if ( $this->slaveId !== false ) {
-                       $header .= "({$this->slaveId})";
+               if ( $this->replicaId !== false ) {
+                       $header .= "({$this->replicaId})";
                }
                $header .= ' ' . wfWikiID();
                LegacyLogger::emit( sprintf( "%-50s %s\n", $header, $msg ), $file );
        }
 
        /**
-        * Wait until the selected slave has caught up to the master.
-        * This allows us to use the slave for things that were committed in a
+        * Wait until the selected replica DB has caught up to the master.
+        * This allows us to use the replica DB for things that were committed in a
         * previous part of this batch process.
         */
        function syncDBs() {
                $dbw = wfGetDB( DB_MASTER );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $pos = $dbw->getMasterPos();
                $dbr->masterPosWait( $pos, 100000 );
        }
@@ -179,10 +180,10 @@ class RecompressTracked {
                }
 
                $this->syncDBs();
-               $this->startSlaveProcs();
+               $this->startReplicaProcs();
                $this->doAllPages();
                $this->doAllOrphans();
-               $this->killSlaveProcs();
+               $this->killReplicaProcs();
        }
 
        /**
@@ -190,7 +191,7 @@ class RecompressTracked {
         * @return bool
         */
        function checkTrackingTable() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                if ( !$dbr->tableExists( 'blob_tracking' ) ) {
                        $this->critical( "Error: blob_tracking table does not exist" );
 
@@ -212,10 +213,10 @@ class RecompressTracked {
         * This necessary because text recompression is slow: loading, compressing and
         * writing are all slow.
         */
-       function startSlaveProcs() {
+       function startReplicaProcs() {
                $cmd = 'php ' . wfEscapeShellArg( __FILE__ );
                foreach ( self::$cmdLineOptionMap as $cmdOption => $classOption ) {
-                       if ( $cmdOption == 'slave-id' ) {
+                       if ( $cmdOption == 'replica-id' ) {
                                continue;
                        } elseif ( in_array( $cmdOption, self::$optionsWithArgs ) && isset( $this->$classOption ) ) {
                                $cmd .= " --$cmdOption " . wfEscapeShellArg( $this->$classOption );
@@ -227,7 +228,7 @@ class RecompressTracked {
                        ' --wiki ' . wfEscapeShellArg( wfWikiID() ) .
                        ' ' . call_user_func_array( 'wfEscapeShellArg', $this->destClusters );
 
-               $this->slavePipes = $this->slaveProcs = [];
+               $this->replicaPipes = $this->replicaProcs = [];
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
                        $pipes = [];
                        $spec = [
@@ -236,28 +237,28 @@ class RecompressTracked {
                                [ 'file', 'php://stderr', 'w' ]
                        ];
                        MediaWiki\suppressWarnings();
-                       $proc = proc_open( "$cmd --slave-id $i", $spec, $pipes );
+                       $proc = proc_open( "$cmd --replica-id $i", $spec, $pipes );
                        MediaWiki\restoreWarnings();
                        if ( !$proc ) {
-                               $this->critical( "Error opening slave process: $cmd" );
+                               $this->critical( "Error opening replica DB process: $cmd" );
                                exit( 1 );
                        }
-                       $this->slaveProcs[$i] = $proc;
-                       $this->slavePipes[$i] = $pipes[0];
+                       $this->replicaProcs[$i] = $proc;
+                       $this->replicaPipes[$i] = $pipes[0];
                }
-               $this->prevSlaveId = -1;
+               $this->prevReplicaId = -1;
        }
 
        /**
         * Gracefully terminate the child processes
         */
-       function killSlaveProcs() {
-               $this->info( "Waiting for slave processes to finish..." );
+       function killReplicaProcs() {
+               $this->info( "Waiting for replica DB processes to finish..." );
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
-                       $this->dispatchToSlave( $i, 'quit' );
+                       $this->dispatchToReplica( $i, 'quit' );
                }
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
-                       $status = proc_close( $this->slaveProcs[$i] );
+                       $status = proc_close( $this->replicaProcs[$i] );
                        if ( $status ) {
                                $this->critical( "Warning: child #$i exited with status $status" );
                        }
@@ -266,22 +267,22 @@ class RecompressTracked {
        }
 
        /**
-        * Dispatch a command to the next available slave.
-        * This may block until a slave finishes its work and becomes available.
+        * Dispatch a command to the next available replica DB.
+        * This may block until a replica DB finishes its work and becomes available.
         */
        function dispatch( /*...*/ ) {
                $args = func_get_args();
-               $pipes = $this->slavePipes;
+               $pipes = $this->replicaPipes;
                $numPipes = stream_select( $x = [], $pipes, $y = [], 3600 );
                if ( !$numPipes ) {
-                       $this->critical( "Error waiting to write to slaves. Aborting" );
+                       $this->critical( "Error waiting to write to replica DBs. Aborting" );
                        exit( 1 );
                }
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
-                       $slaveId = ( $i + $this->prevSlaveId + 1 ) % $this->numProcs;
-                       if ( isset( $pipes[$slaveId] ) ) {
-                               $this->prevSlaveId = $slaveId;
-                               $this->dispatchToSlave( $slaveId, $args );
+                       $replicaId = ( $i + $this->prevReplicaId + 1 ) % $this->numProcs;
+                       if ( isset( $pipes[$replicaId] ) ) {
+                               $this->prevReplicaId = $replicaId;
+                               $this->dispatchToReplica( $replicaId, $args );
 
                                return;
                        }
@@ -291,21 +292,21 @@ class RecompressTracked {
        }
 
        /**
-        * Dispatch a command to a specified slave
-        * @param int $slaveId
+        * Dispatch a command to a specified replica DB
+        * @param int $replicaId
         * @param array|string $args
         */
-       function dispatchToSlave( $slaveId, $args ) {
+       function dispatchToReplica( $replicaId, $args ) {
                $args = (array)$args;
                $cmd = implode( ' ', $args );
-               fwrite( $this->slavePipes[$slaveId], "$cmd\n" );
+               fwrite( $this->replicaPipes[$replicaId], "$cmd\n" );
        }
 
        /**
         * Move all tracked pages to the new clusters
         */
        function doAllPages() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $i = 0;
                $startId = 0;
                if ( $this->noCount ) {
@@ -366,7 +367,7 @@ class RecompressTracked {
                if ( $current == $end || $this->numBatches >= $this->reportingInterval ) {
                        $this->numBatches = 0;
                        $this->info( "$label: $current / $end" );
-                       wfWaitForSlaves();
+                       MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->waitForReplication();
                }
        }
 
@@ -374,7 +375,7 @@ class RecompressTracked {
         * Move all orphan text to the new clusters
         */
        function doAllOrphans() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $startId = 0;
                $i = 0;
                if ( $this->noCount ) {
@@ -464,7 +465,7 @@ class RecompressTracked {
                                case 'quit':
                                        return;
                        }
-                       wfWaitForSlaves();
+                       MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->waitForReplication();
                }
        }
 
@@ -480,7 +481,7 @@ class RecompressTracked {
                } else {
                        $titleText = '[deleted]';
                }
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Finish any incomplete transactions
                if ( !$this->copyOnly ) {
@@ -491,6 +492,7 @@ class RecompressTracked {
                $startId = 0;
                $trx = new CgzCopyTransaction( $this, $this->pageBlobClass );
 
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
                while ( true ) {
                        $res = $dbr->select(
                                [ 'blob_tracking', 'text' ],
@@ -532,7 +534,7 @@ class RecompressTracked {
                                        $this->debug( "$titleText: committing blob with " . $trx->getSize() . " items" );
                                        $trx->commit();
                                        $trx = new CgzCopyTransaction( $this, $this->pageBlobClass );
-                                       wfWaitForSlaves();
+                                       $lbFactory->waitForReplication();
                                }
                        }
                }
@@ -590,7 +592,8 @@ class RecompressTracked {
         * @param array $conds
         */
        function finishIncompleteMoves( $conds ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
 
                $startId = 0;
                $conds = array_merge( $conds, [
@@ -615,7 +618,7 @@ class RecompressTracked {
                                $startId = $row->bt_text_id;
                                $this->moveTextRow( $row->bt_text_id, $row->bt_new_url );
                                if ( $row->bt_text_id % 10 == 0 ) {
-                                       wfWaitForSlaves();
+                                       $lbFactory->waitForReplication();
                                }
                        }
                }
@@ -659,7 +662,8 @@ class RecompressTracked {
 
                $trx = new CgzCopyTransaction( $this, $this->orphanBlobClass );
 
-               $res = wfGetDB( DB_SLAVE )->select(
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               $res = wfGetDB( DB_REPLICA )->select(
                        [ 'text', 'blob_tracking' ],
                        [ 'old_id', 'old_text', 'old_flags' ],
                        [
@@ -682,7 +686,7 @@ class RecompressTracked {
                                $this->debug( "[orphan]: committing blob with " . $trx->getSize() . " rows" );
                                $trx->commit();
                                $trx = new CgzCopyTransaction( $this, $this->orphanBlobClass );
-                               wfWaitForSlaves();
+                               $lbFactory->waitForReplication();
                        }
                }
                $this->debug( "[orphan]: committing blob with " . $trx->getSize() . " rows" );
@@ -762,7 +766,7 @@ class CgzCopyTransaction {
 
                /* Check to see if the target text_ids have been moved already.
                 *
-                * We originally read from the slave, so this can happen when a single
+                * We originally read from the replica DB, so this can happen when a single
                 * text_id is shared between multiple pages. It's rare, but possible
                 * if a delete/move/undelete cycle splits up a null edit.
                 *
index 33a9f96..8ca8bb2 100644 (file)
@@ -37,7 +37,7 @@ if ( !defined( 'MEDIAWIKI' ) ) {
 function resolveStubs() {
        $fname = 'resolveStubs';
 
-       $dbr = wfGetDB( DB_SLAVE );
+       $dbr = wfGetDB( DB_REPLICA );
        $maxID = $dbr->selectField( 'text', 'MAX(old_id)', false, $fname );
        $blockSize = 10000;
        $numBlocks = intval( $maxID / $blockSize ) + 1;
@@ -73,7 +73,7 @@ function resolveStub( $id, $stubText, $flags ) {
        $stub = unserialize( $stubText );
        $flags = explode( ',', $flags );
 
-       $dbr = wfGetDB( DB_SLAVE );
+       $dbr = wfGetDB( DB_REPLICA );
        $dbw = wfGetDB( DB_MASTER );
 
        if ( strtolower( get_class( $stub ) ) !== 'historyblobstub' ) {
index 04d2557..c23f508 100644 (file)
@@ -23,7 +23,7 @@ require_once __DIR__ . '/../Maintenance.php';
 
 class StorageTypeStats extends Maintenance {
        function execute() {
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
 
                $endId = $dbr->selectField( 'text', 'MAX(old_id)', false, __METHOD__ );
                if ( !$endId ) {
index 2692a07..90d8d03 100644 (file)
@@ -47,7 +47,7 @@ if ( isset( $options['limit'] ) ) {
 }
 $type = isset( $options['type'] ) ? $options['type'] : 'ConcatenatedGzipHistoryBlob';
 
-$dbr = $this->getDB( DB_SLAVE );
+$dbr = $this->getDB( DB_REPLICA );
 $res = $dbr->select(
        [ 'page', 'revision', 'text' ],
        '*',
index 214f5b1..a2dc376 100644 (file)
@@ -67,7 +67,7 @@ class TrackBlobs {
 
        function checkIntegrity() {
                echo "Doing integrity check...\n";
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                // Scan for HistoryBlobStub objects in the text table (bug 20757)
 
@@ -117,7 +117,7 @@ class TrackBlobs {
 
        function getTextClause() {
                if ( !$this->textClause ) {
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = wfGetDB( DB_REPLICA );
                        $this->textClause = '';
                        foreach ( $this->clusters as $cluster ) {
                                if ( $this->textClause != '' ) {
@@ -147,7 +147,7 @@ class TrackBlobs {
         */
        function trackRevisions() {
                $dbw = wfGetDB( DB_MASTER );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
 
                $textClause = $this->getTextClause();
                $startId = 0;
@@ -219,9 +219,9 @@ class TrackBlobs {
         * archive table counts as orphan for our purposes.
         */
        function trackOrphanText() {
-               # Wait until the blob_tracking table is available in the slave
+               # Wait until the blob_tracking table is available in the replica DB
                $dbw = wfGetDB( DB_MASTER );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = wfGetDB( DB_REPLICA );
                $pos = $dbw->getMasterPos();
                $dbr->masterPosWait( $pos, 100000 );
 
@@ -317,7 +317,7 @@ class TrackBlobs {
                        echo "Searching for orphan blobs in $cluster...\n";
                        $lb = wfGetLBFactory()->getExternalLB( $cluster );
                        try {
-                               $extDB = $lb->getConnection( DB_SLAVE );
+                               $extDB = $lb->getConnection( DB_REPLICA );
                        } catch ( DBConnectionError $e ) {
                                if ( strpos( $e->error, 'Unknown database' ) !== false ) {
                                        echo "No database on $cluster\n";
index 4a74322..82149a6 100644 (file)
@@ -268,9 +268,9 @@ class SyncFileBackend extends Maintenance {
                        sleep( 10 ); // wait and retry copy again
                        $status = $dst->doQuickOperations( $ops, [ 'bypassReadOnly' => 1 ] );
                }
-               $ellapsed_ms = floor( ( microtime( true ) - $t_start ) * 1000 );
+               $elapsed_ms = floor( ( microtime( true ) - $t_start ) * 1000 );
                if ( $status->isOK() && $this->getOption( 'verbose' ) ) {
-                       $this->output( "Synchronized these file(s) [{$ellapsed_ms}ms]:\n" .
+                       $this->output( "Synchronized these file(s) [{$elapsed_ms}ms]:\n" .
                                implode( "\n", $dPaths ) . "\n" );
                }
 
index 89aeb9c..b5c14e3 100644 (file)
@@ -369,7 +369,7 @@ CREATE TABLE /*_*/revision (
 ) /*$wgDBTableOptions*/ MAX_ROWS=10000000 AVG_ROW_LENGTH=1024;
 -- In case tables are created as MyISAM, use row hints for MySQL <5.0 to avoid 4GB limit
 
-CREATE UNIQUE INDEX /*i*/rev_page_id ON /*_*/revision (rev_page, rev_id);
+CREATE INDEX /*i*/rev_page_id ON /*_*/revision (rev_page, rev_id);
 CREATE INDEX /*i*/rev_timestamp ON /*_*/revision (rev_timestamp);
 CREATE INDEX /*i*/page_timestamp ON /*_*/revision (rev_page,rev_timestamp);
 CREATE INDEX /*i*/user_timestamp ON /*_*/revision (rev_user,rev_timestamp);
@@ -623,9 +623,9 @@ CREATE INDEX /*i*/cl_timestamp ON /*_*/categorylinks (cl_to,cl_timestamp);
 CREATE INDEX /*i*/cl_collation_ext ON /*_*/categorylinks (cl_collation, cl_to, cl_type, cl_from);
 
 --
--- Track all existing categories.  Something is a category if 1) it has an en-
--- try somewhere in categorylinks, or 2) it once did.  Categories might not
--- have corresponding pages, so they need to be tracked separately.
+-- Track all existing categories. Something is a category if 1) it has an entry
+-- somewhere in categorylinks, or 2) it has a description page. Categories
+-- might not have corresponding pages, so they need to be tracked separately.
 --
 CREATE TABLE /*_*/category (
   -- Primary key
@@ -1136,6 +1136,7 @@ CREATE INDEX /*i*/new_name_timestamp ON /*_*/recentchanges (rc_new,rc_namespace,
 CREATE INDEX /*i*/rc_ip ON /*_*/recentchanges (rc_ip);
 CREATE INDEX /*i*/rc_ns_usertext ON /*_*/recentchanges (rc_namespace, rc_user_text);
 CREATE INDEX /*i*/rc_user_text ON /*_*/recentchanges (rc_user_text, rc_timestamp);
+CREATE INDEX /*i*/rc_name_type_patrolled_timestamp ON /*_*/recentchanges (rc_namespace, rc_type, rc_patrolled, rc_timestamp);
 
 
 CREATE TABLE /*_*/watchlist (
index f47e13c..9d7cc0e 100644 (file)
@@ -7,7 +7,7 @@ require_once __DIR__ . '/Maintenance.php';
 class TidyUpBug37714 extends Maintenance {
        public function execute() {
                // Search for all log entries which are about changing the visability of other log entries.
-               $result = $this->getDB( DB_SLAVE )->select(
+               $result = $this->getDB( DB_REPLICA )->select(
                        'logging',
                        [ 'log_id', 'log_params' ],
                        [
@@ -21,7 +21,7 @@ class TidyUpBug37714 extends Maintenance {
 
                foreach ( $result as $row ) {
                        $ids = explode( ',', explode( "\n", $row->log_params )[0] );
-                       $result = $this->getDB( DB_SLAVE )->select( // Work out what log entries were changed here.
+                       $result = $this->getDB( DB_REPLICA )->select( // Work out what log entries were changed here.
                                'logging',
                                'log_type',
                                [ 'log_id' => $ids ],
index dff5aec..213195d 100644 (file)
@@ -46,7 +46,7 @@ class UpdateArticleCount extends Maintenance {
                if ( $this->hasOption( 'use-master' ) ) {
                        $dbr = $this->getDB( DB_MASTER );
                } else {
-                       $dbr = $this->getDB( DB_SLAVE, 'vslow' );
+                       $dbr = $this->getDB( DB_REPLICA, 'vslow' );
                }
                $counter = new SiteStatsInit( $dbr );
                $result = $counter->articles();
index 922cc87..e754e3c 100644 (file)
@@ -34,7 +34,7 @@ require_once __DIR__ . '/Maintenance.php';
  */
 class UpdateCollation extends Maintenance {
        const BATCH_SIZE = 100; // Number of rows to process in one batch
-       const SYNC_INTERVAL = 5; // Wait for slaves after this many batches
+       const SYNC_INTERVAL = 5; // Wait for replica DBs after this many batches
 
        public $sizeHistogram = [];
 
@@ -70,7 +70,7 @@ TEXT
                global $wgCategoryCollation;
 
                $dbw = $this->getDB( DB_MASTER );
-               $dbr = $this->getDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_REPLICA );
                $force = $this->getOption( 'force' );
                $dryRun = $this->getOption( 'dry-run' );
                $verboseStats = $this->getOption( 'verbose-stats' );
@@ -101,7 +101,7 @@ TEXT
                        'STRAIGHT_JOIN' // per T58041
                ];
 
-               if ( $force || $dryRun ) {
+               if ( $force ) {
                        $collationConds = [];
                } else {
                        if ( $this->hasOption( 'previous-collation' ) ) {
@@ -132,7 +132,11 @@ TEXT
 
                                return;
                        }
-                       $this->output( "Fixing collation for $count rows.\n" );
+                       if ( $dryRun ) {
+                               $this->output( "$count rows would be updated.\n" );
+                       } else {
+                               $this->output( "Fixing collation for $count rows.\n" );
+                       }
                        wfWaitForSlaves();
                }
                $count = 0;
@@ -220,7 +224,7 @@ TEXT
                        $this->output( "$count done.\n" );
 
                        if ( !$dryRun && ++$batchCount % self::SYNC_INTERVAL == 0 ) {
-                               $this->output( "Waiting for slaves ... " );
+                               $this->output( "Waiting for replica DBs ... " );
                                wfWaitForSlaves();
                                $this->output( "done\n" );
                        }
diff --git a/maintenance/updateExtensionJsonSchema.php b/maintenance/updateExtensionJsonSchema.php
new file mode 100644 (file)
index 0000000..427769f
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+
+require_once __DIR__ . '/Maintenance.php';
+
+class UpdateExtensionJsonSchema extends Maintenance {
+
+       public function __construct() {
+               parent::__construct();
+               $this->addDescription( 'Updates extension.json files to the latest manifest_version' );
+               $this->addArg( 'path', 'Location to the extension.json or skin.json you wish to convert',
+                       /* $required = */ true );
+       }
+
+       public function execute() {
+               $filename = $this->getArg( 0 );
+               if ( !is_readable( $filename ) ) {
+                       $this->error( "Error: Unable to read $filename", 1 );
+               }
+
+               $json = FormatJson::decode( file_get_contents( $filename ), true );
+               if ( $json === null ) {
+                       $this->error( "Error: Invalid JSON", 1 );
+               }
+
+               if ( !isset( $json['manifest_version'] ) ) {
+                       $json['manifest_version'] = 1;
+               }
+
+               if ( $json['manifest_version'] == ExtensionRegistry::MANIFEST_VERSION ) {
+                       $this->output( "Already at the latest version: {$json['manifest_version']}\n" );
+                       return;
+               }
+
+               while ( $json['manifest_version'] !== ExtensionRegistry::MANIFEST_VERSION ) {
+                       $json['manifest_version'] += 1;
+                       $func = "updateTo{$json['manifest_version']}";
+                       $this->$func( $json );
+               }
+
+               file_put_contents( $filename, FormatJson::encode( $json, "\t", FormatJson::ALL_OK ) . "\n" );
+               $this->output( "Updated to {$json['manifest_version']}...\n" );
+       }
+
+       protected function updateTo2( &$json ) {
+               if ( isset( $json['config'] ) ) {
+                       $config = $json['config'];
+                       $json['config'] = [];
+                       if ( isset( $config['_prefix'] ) ) {
+                               $json = wfArrayInsertAfter( $json, [
+                                       'config_prefix' => $config['_prefix']
+                               ], 'config' );
+                               unset( $config['_prefix'] );
+                       }
+
+                       foreach ( $config as $name => $value ) {
+                               if ( $name[0] !== '@' ) {
+                                       $json['config'][$name] = [ 'value' => $value ];
+                                       if ( isset( $value[ExtensionRegistry::MERGE_STRATEGY] ) ) {
+                                               $json['config'][$name]['merge_strategy'] = $value[ExtensionRegistry::MERGE_STRATEGY];
+                                               unset( $value[ExtensionRegistry::MERGE_STRATEGY] );
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+$maintClass = 'UpdateExtensionJsonSchema';
+require_once RUN_MAINTENANCE_IF_MAIN;
index 8b24b90..5ea3828 100644 (file)
@@ -109,7 +109,7 @@ class UpdateSpecialPages extends Maintenance {
                                                } while ( !wfGetLB()->pingAll() );
                                                $this->output( "Reconnected\n\n" );
                                        }
-                                       # Wait for the slave to catch up
+                                       # Wait for the replica DB to catch up
                                        wfWaitForSlaves();
                                } else {
                                        $this->output( "cheap, skipped\n" );
@@ -153,7 +153,7 @@ class UpdateSpecialPages extends Maintenance {
                                        $this->output( $minutes . 'm ' );
                                }
                                $this->output( sprintf( "%.2fs\n", $seconds ) );
-                               # Wait for the slave to catch up
+                               # Wait for the replica DB to catch up
                                wfWaitForSlaves();
                        }
                }
index 4b0a817..c657c03 100644 (file)
@@ -140,8 +140,8 @@ class UserOptions {
                $ret = [];
                $defaultOptions = User::getDefaultOptions();
 
-               // We list user by user_id from one of the slave database
-               $dbr = wfGetDB( DB_SLAVE );
+               // We list user by user_id from one of the replica DBs
+               $dbr = wfGetDB( DB_REPLICA );
                $result = $dbr->select( 'user',
                        [ 'user_id' ],
                        [],
@@ -194,8 +194,8 @@ class UserOptions {
        private function CHANGER() {
                $this->warn();
 
-               // We list user by user_id from one of the slave database
-               $dbr = wfGetDB( DB_SLAVE );
+               // We list user by user_id from one of the replica DBs
+               $dbr = wfGetDB( DB_REPLICA );
                $result = $dbr->select( 'user',
                        [ 'user_id' ],
                        [],
index 9cf7b2b..bd34a50 100644 (file)
@@ -8,7 +8,7 @@ class ValidateRegistrationFile extends Maintenance {
                $this->addArg( 'path', 'Path to extension.json/skin.json file.', true );
        }
        public function execute() {
-               if ( !class_exists( 'JsonSchema\Uri\UriRetriever' ) ) {
+               if ( !class_exists( 'JsonSchema\Validato' ) ) {
                        $this->error( 'The JsonSchema library cannot be found, please install it through composer.', 1 );
                }
 
@@ -38,11 +38,8 @@ class ValidateRegistrationFile extends Maintenance {
                        $this->output( "Warning: $path is using a deprecated schema, and should be updated to "
                                . ExtensionRegistry::MANIFEST_VERSION . "\n" );
                }
-               $retriever = new JsonSchema\Uri\UriRetriever();
-               $schema = $retriever->retrieve( 'file://' . $schemaPath );
-
-               $validator = new JsonSchema\Validator();
-               $validator->check( $data, $schema );
+               $validator = new JsonSchema\Validator;
+               $validator->check( $data, (object) [ '$ref' => 'file://' . $schemaPath ] );
                if ( $validator->isValid() ) {
                        $this->output( "$path validates against the version $version schema!\n" );
                } else {
index 68e0fad..9a9d84d 100644 (file)
@@ -5,7 +5,7 @@ you can override classes used by the installer.
 You can override 3 classes:
 * LocalSettingsGenerator - generates LocalSettings.php
 * WebInstaller - web installer UI
-* CliInstaller - command line installer
+* CliInstaller - command-line installer
 
 Example override:
 
index d96dcf9..3b75c3a 100644 (file)
     "grunt-contrib-watch": "1.0.0",
     "grunt-jscs": "2.8.0",
     "grunt-jsonlint": "1.0.7",
-    "grunt-karma": "0.12.2",
-    "grunt-stylelint": "0.4.0",
-    "karma": "0.13.22",
-    "karma-chrome-launcher": "0.2.2",
-    "karma-firefox-launcher": "0.1.7",
-    "karma-qunit": "0.1.9",
+    "grunt-karma": "2.0.0",
+    "grunt-stylelint": "0.6.0",
+    "karma": "1.1.0",
+    "karma-chrome-launcher": "1.0.1",
+    "karma-firefox-launcher": "1.0.0",
+    "karma-qunit": "1.0.0",
     "qunitjs": "1.22.0",
-    "stylelint-config-wikimedia": "0.2.2"
+    "stylelint-config-wikimedia": "0.3.0"
   }
 }
index 7f4e132..7055f36 100644 (file)
@@ -52,6 +52,7 @@ return [
 
        // Scripts managed by the current user (stored in their user space)
        'user' => [ 'class' => 'ResourceLoaderUserModule' ],
+       'user.styles' => [ 'class' => 'ResourceLoaderUserStylesModule' ],
 
        // Scripts generated based on the current user's preferences
        'user.cssprefs' => [ 'class' => 'ResourceLoaderUserCSSPrefsModule' ],
@@ -159,11 +160,15 @@ return [
                'targets' => [ 'mobile', 'desktop' ],
        ],
        'jquery.appear' => [
+               'deprecated' => [
+                       'message' => 'Please use "mediawiki.viewport" instead.',
+               ],
                'scripts' => 'resources/lib/jquery/jquery.appear.js',
        ],
        'jquery.arrowSteps' => [
                'scripts' => 'resources/src/jquery/jquery.arrowSteps.js',
                'styles' => 'resources/src/jquery/jquery.arrowSteps.css',
+               'targets' => [ 'desktop', 'mobile' ],
        ],
        'jquery.async' => [
                'scripts' => 'resources/lib/jquery/jquery.async.js',
@@ -328,8 +333,12 @@ return [
        'jquery.spinner' => [
                'scripts' => 'resources/src/jquery/jquery.spinner.js',
                'styles' => 'resources/src/jquery/jquery.spinner.css',
+               'targets' => [ 'desktop', 'mobile' ],
        ],
        'jquery.jStorage' => [
+               'deprecated' => [
+                       'message' => 'Please use "mediawiki.storage" instead.',
+               ],
                'scripts' => 'resources/lib/jquery/jquery.jStorage.js',
                'dependencies' => 'json',
        ],
@@ -367,6 +376,7 @@ return [
        /* jQuery Tipsy */
 
        'jquery.tipsy' => [
+               'deprecated' => true,
                'scripts' => 'resources/src/jquery.tipsy/jquery.tipsy.js',
                'styles' => 'resources/src/jquery.tipsy/jquery.tipsy.css',
        ],
@@ -374,6 +384,9 @@ return [
        /* jQuery UI */
 
        'jquery.ui.core' => [
+               'deprecated' => [
+                       'message' => 'Please use "mediawiki.ui.button" or "oojs-ui" instead.',
+               ],
                'scripts' => 'resources/lib/jquery.ui/jquery.ui.core.js',
                'dependencies' => [
                        'jquery.ui.core.styles',
@@ -557,6 +570,8 @@ return [
                'group' => 'jquery.ui',
        ],
        'jquery.ui.position' => [
+               'deprecated' => true,
+               'targets' => [ 'mobile', 'desktop' ],
                'scripts' => 'resources/lib/jquery.ui/jquery.ui.position.js',
                'group' => 'jquery.ui',
        ],
@@ -652,11 +667,13 @@ return [
                'group' => 'jquery.ui',
        ],
        'jquery.ui.widget' => [
+               'deprecated' => true,
                'scripts' => 'resources/lib/jquery.ui/jquery.ui.widget.js',
                'group' => 'jquery.ui',
        ],
        // Effects
        'jquery.effects.core' => [
+               'deprecated' => true,
                'scripts' => 'resources/lib/jquery.ui/jquery.ui.effect.js',
                'group' => 'jquery.ui',
        ],
@@ -841,7 +858,6 @@ return [
                'class' => 'ResourceLoaderRawFileModule',
                // Keep in sync with maintenance/jsduck/eg-iframe.html
                'scripts' => [
-                       'resources/lib/phpjs-sha1/sha1.js',
                        'resources/src/mediawiki/mediawiki.js',
                        'resources/src/mediawiki/mediawiki.requestIdleCallback.js',
                        'resources/src/mediawiki/mediawiki.errorLogger.js',
@@ -921,6 +937,7 @@ return [
                        'mediawiki.api.edit',
                        'json',
                ],
+               'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.api.user' => [
                'scripts' => 'resources/src/mediawiki/api/user.js',
@@ -940,6 +957,7 @@ return [
                'dependencies' => [
                        'mediawiki.api',
                ],
+               'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.api.rollback' => [
                'scripts' => 'resources/src/mediawiki/api/rollback.js',
@@ -949,7 +967,7 @@ return [
        ],
        'mediawiki.content.json' => [
                'position' => 'top',
-               'styles' => 'resources/src/mediawiki/mediawiki.content.json.css',
+               'styles' => 'resources/src/mediawiki/mediawiki.content.json.less',
        ],
        'mediawiki.confirmCloseWindow' => [
                'scripts' => [
@@ -966,17 +984,21 @@ return [
                ],
                'dependencies' => [
                        'jquery.footHovzer',
-                       'jquery.tipsy',
                ],
-               'position' => 'bottom',
-       ],
-       'mediawiki.debug.init' => [
-               'scripts' => 'resources/src/mediawiki/mediawiki.debug.init.js',
-               'dependencies' => 'mediawiki.debug',
                // Uses a custom mw.config variable that is set in debughtml,
                // must be loaded on the bottom
                'position' => 'bottom',
        ],
+       'mediawiki.diff.styles' => [
+               'position' => 'top',
+               'styles' => [
+                       'resources/src/mediawiki/mediawiki.diff.styles.css',
+                       'resources/src/mediawiki/mediawiki.diff.styles.print.css' => [
+                               'media' => 'print'
+                       ],
+               ],
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
        'mediawiki.feedback' => [
                'scripts' => 'resources/src/mediawiki/mediawiki.feedback.js',
                'styles' => 'resources/src/mediawiki/mediawiki.feedback.css',
@@ -1011,6 +1033,7 @@ return [
                        'feedback-thanks-title',
                        'feedback-useragent'
                ],
+               'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.feedlink' => [
                'position' => 'top',
@@ -1054,7 +1077,17 @@ return [
                'styles' => 'resources/src/mediawiki/mediawiki.hlist.css',
        ],
        'mediawiki.htmlform' => [
-               'scripts' => 'resources/src/mediawiki/mediawiki.htmlform.js',
+               'scripts' => [
+                       'resources/src/mediawiki/htmlform/htmlform.js',
+                       'resources/src/mediawiki/htmlform/autocomplete.js',
+                       'resources/src/mediawiki/htmlform/autoinfuse.js',
+                       'resources/src/mediawiki/htmlform/checkmatrix.js',
+                       'resources/src/mediawiki/htmlform/cloner.js',
+                       'resources/src/mediawiki/htmlform/hide-if.js',
+                       'resources/src/mediawiki/htmlform/multiselect.js',
+                       'resources/src/mediawiki/htmlform/selectandother.js',
+                       'resources/src/mediawiki/htmlform/selectorother.js',
+               ],
                'dependencies' => [
                        'mediawiki.RegExp',
                        'jquery.byteLimit',
@@ -1066,18 +1099,28 @@ return [
                ],
                'targets' => [ 'desktop', 'mobile' ],
        ],
+       'mediawiki.htmlform.ooui' => [
+               'scripts' => [
+                       'resources/src/mediawiki/htmlform/htmlform.Element.js',
+               ],
+               'dependencies' => [
+                       'oojs-ui-core',
+               ],
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
        'mediawiki.htmlform.styles' => [
-               'styles' => 'resources/src/mediawiki/mediawiki.htmlform.css',
+               'styles' => 'resources/src/mediawiki/htmlform/styles.css',
                'position' => 'top',
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.htmlform.ooui.styles' => [
-               'styles' => 'resources/src/mediawiki/mediawiki.htmlform.ooui.css',
+               'styles' => 'resources/src/mediawiki/htmlform/ooui.styles.css',
                'position' => 'top',
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.icon' => [
                'styles' => 'resources/src/mediawiki/mediawiki.icon.less',
+               'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.inspect' => [
                'scripts' => 'resources/src/mediawiki/mediawiki.inspect.js',
@@ -1113,7 +1156,7 @@ return [
        'mediawiki.notification' => [
                'styles' => [
                        'resources/src/mediawiki/mediawiki.notification.common.css',
-                       'resources/src/mediawiki/mediawiki.notification.hideForPrint.css'
+                       'resources/src/mediawiki/mediawiki.notification.print.css'
                                => [ 'media' => 'print' ],
                ],
                'skinStyles' => [
@@ -1127,6 +1170,20 @@ return [
                'scripts' => 'resources/src/mediawiki/mediawiki.notify.js',
                'targets' => [ 'desktop', 'mobile' ],
        ],
+       'mediawiki.notification.convertmessagebox' => [
+               'dependencies' => [
+                       'mediawiki.notification',
+               ],
+               'scripts' => 'resources/src/mediawiki/mediawiki.notification.convertmessagebox.js',
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
+       'mediawiki.notification.convertmessagebox.styles' => [
+               'position' => 'top',
+               'styles' => [
+                       'resources/src/mediawiki/mediawiki.notification.convertmessagebox.styles.less',
+               ],
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
        'mediawiki.RegExp' => [
                'scripts' => 'resources/src/mediawiki/mediawiki.RegExp.js',
                'targets' => [ 'desktop', 'mobile' ],
@@ -1298,7 +1355,6 @@ return [
                        'filename-thumb-name',
                        'badfilename',
                        'protectedpagetext',
-                       'api-error-blacklisted', // HACK
                ],
        ],
        'mediawiki.ForeignStructuredUpload.BookletLayout' => [
@@ -1394,16 +1450,13 @@ return [
                'class' => 'ResourceLoaderEditToolbarModule',
                'scripts' => 'resources/src/mediawiki.toolbar/toolbar.js',
                'styles' => 'resources/src/mediawiki.toolbar/toolbar.less',
+               'dependencies' => 'jquery.textSelection',
                'position' => 'top',
        ],
        'mediawiki.experiments' => [
                'scripts' => 'resources/src/mediawiki/mediawiki.experiments.js',
                'targets' => [ 'desktop', 'mobile' ],
        ],
-       'mediawiki.raggett' => [
-               'styles' => 'resources/src/mediawiki/mediawiki.raggett.css',
-               'targets' => [ 'desktop', 'mobile' ],
-       ],
 
        /* MediaWiki Action */
 
@@ -1441,7 +1494,7 @@ return [
                        'jquery.spinner',
                        'jquery.textSelection',
                        'mediawiki.api',
-                       'mediawiki.action.history.diff',
+                       'mediawiki.diff.styles',
                        'mediawiki.util',
                        'mediawiki.jqueryMsg',
                ],
@@ -1468,11 +1521,12 @@ return [
                'position' => 'top',
                'styles' => 'resources/src/mediawiki.action/mediawiki.action.history.styles.css',
        ],
+       // using this module is deprecated, for diff styles use mediawiki.diff.styles instead
        'mediawiki.action.history.diff' => [
                'position' => 'top',
                'styles' => [
-                       'resources/src/mediawiki.action/mediawiki.action.history.diff.css',
-                       'resources/src/mediawiki.action/mediawiki.action.history.diff.print.css' => [
+                       'resources/src/mediawiki/mediawiki.diff.styles.css',
+                       'resources/src/mediawiki/mediawiki.diff.styles.print.css' => [
                                'media' => 'print'
                        ],
                ],
@@ -1638,6 +1692,7 @@ return [
 
        'mediawiki.libs.jpegmeta' => [
                'scripts' => 'resources/src/mediawiki.libs/mediawiki.libs.jpegmeta.js',
+               'targets' => [ 'desktop', 'mobile' ],
        ],
 
        /* MediaWiki Page */
@@ -1651,12 +1706,24 @@ return [
        ],
        'mediawiki.page.gallery.styles' => [
                'styles' => [
-                       'resources/src/mediawiki/page/gallery-print.css' => [ 'media' => 'print' ],
+                       'resources/src/mediawiki/page/gallery.print.css' => [ 'media' => 'print' ],
                        'resources/src/mediawiki/page/gallery.css',
                ],
                'position' => 'top',
                'targets' => [ 'desktop', 'mobile' ],
        ],
+       'mediawiki.page.gallery.slideshow' => [
+               'scripts' => 'resources/src/mediawiki/page/gallery-slideshow.js',
+               'position' => 'top',
+               'dependencies' => [
+                       'mediawiki.api',
+                       'mediawiki.Title',
+                       'oojs',
+                       'oojs-ui-core',
+                       'oojs-ui-widgets',
+                       'oojs-ui.styles.icons-media'
+               ]
+       ],
        'mediawiki.page.ready' => [
                'scripts' => 'resources/src/mediawiki/page/ready.js',
                'dependencies' => [
@@ -1874,7 +1941,13 @@ return [
                'dependencies' => [
                        'mediawiki.language',
                        'mediawiki.confirmCloseWindow',
-                       'mediawiki.notification',
+                       'mediawiki.notification.convertmessagebox',
+               ],
+       ],
+       'mediawiki.special.userrights' => [
+               'scripts' => 'resources/src/mediawiki.special/mediawiki.special.userrights.js',
+               'dependencies' => [
+                       'mediawiki.notification.convertmessagebox',
                ],
        ],
        'mediawiki.special.preferences.styles' => [
index b31fe82..c3a287d 100644 (file)
@@ -68,6 +68,9 @@ return call_user_func( function () {
                        'es5-shim',
                        'oojs',
                        'oojs-ui-core.styles',
+                       'oojs-ui.styles.icons',
+                       'oojs-ui.styles.indicators',
+                       'oojs-ui.styles.textures',
                        'mediawiki.language',
                ],
                'targets' => [ 'desktop', 'mobile' ],
@@ -78,14 +81,6 @@ return call_user_func( function () {
                'styles' => 'resources/src/oojs-ui-local.css', // HACK, see inside the file
                'skinStyles' => $getSkinSpecific( 'core' ),
                'targets' => [ 'desktop', 'mobile' ],
-               // ResourceLoaderImageModule doesn't support 'skipFunction', so instead we set this up so that
-               // this module is skipped together with its dependencies. Nothing else depends on these modules.
-               'dependencies' => [
-                       'oojs-ui.styles.icons',
-                       'oojs-ui.styles.indicators',
-                       'oojs-ui.styles.textures',
-               ],
-               'skipFunction' => 'resources/src/oojs-ui-styles-skip.js',
        ];
 
        // Additional widgets and layouts module.
diff --git a/resources/assets/licenses/README b/resources/assets/licenses/README
new file mode 100644 (file)
index 0000000..dae2549
--- /dev/null
@@ -0,0 +1,4 @@
+These license icons are used in LocalSettings.php files that are generated by
+the installer. Although "Public domain" has been removed from the installer as
+an option, the public-domain.png image needs to remain here to support older
+installations that refer to it in LocalSettings.php.
index d2a998c..999fae0 100644 (file)
@@ -4,7 +4,8 @@
                        "Calak",
                        "Muhammed taha",
                        "Serwan",
-                       "Pirehelokan"
+                       "Pirehelokan",
+                       "Sarchia"
                ]
        },
        "ooui-toolbar-more": "زیاتر",
@@ -16,5 +17,7 @@
        "ooui-dialog-process-dismiss": "لێگەڕان",
        "ooui-dialog-process-retry": "دیسان ھەوڵ بدە",
        "ooui-dialog-process-continue": "درێژە بدە",
-       "ooui-selectfile-placeholder": "ھیچ فایلێک ھەڵنەبژێراوە"
+       "ooui-selectfile-button-select": "پەڕگەیەک دەستنیشان بکە",
+       "ooui-selectfile-placeholder": "ھیچ فایلێک ھەڵنەبژێراوە",
+       "ooui-selectfile-dragdrop-placeholder": "پەڕگەکان بخەرە ئێرە"
 }
index b74cd06..f3c7e3c 100644 (file)
@@ -4,11 +4,24 @@
                        "Lloffiwr",
                        "Robin Owain",
                        "ОйЛ",
-                       "DChan (WMF)"
+                       "DChan (WMF)",
+                       "Jdforrester"
                ]
        },
        "ooui-outline-control-move-down": "Symud yr eitem i lawr",
        "ooui-outline-control-move-up": "Symud yr eitem i fyny",
        "ooui-outline-control-remove": "Tynnu'r eitem",
-       "ooui-toolbar-more": "Rhagor"
+       "ooui-toolbar-more": "Rhagor",
+       "ooui-toolgroup-expand": "Mwy",
+       "ooui-toolgroup-collapse": "Llai",
+       "ooui-dialog-message-accept": "Iawn",
+       "ooui-dialog-message-reject": "Canslo",
+       "ooui-dialog-process-error": "Aeth rhywbeth o’i le",
+       "ooui-dialog-process-dismiss": "Gadael",
+       "ooui-dialog-process-retry": "Ailgeisio",
+       "ooui-dialog-process-continue": "Parhau",
+       "ooui-selectfile-button-select": "Dewis ffeil",
+       "ooui-selectfile-not-supported": "Nid oes modd dewis ffeil",
+       "ooui-selectfile-placeholder": "Dim ffeil wedi'i dewis",
+       "ooui-selectfile-dragdrop-placeholder": "Gollwng ffeil yma"
 }
index 30e3efa..e193fb0 100644 (file)
@@ -8,12 +8,18 @@
                        "Palnatoke",
                        "Simeondahl",
                        "Tehnix",
-                       "Macofe"
+                       "Macofe",
+                       "Peter Alberti"
                ]
        },
        "ooui-outline-control-move-down": "Flyt ned",
        "ooui-outline-control-move-up": "Flyt op",
        "ooui-toolbar-more": "Mere",
        "ooui-toolgroup-expand": "Mere",
+       "ooui-toolgroup-collapse": "Færre",
+       "ooui-dialog-message-accept": "OK",
+       "ooui-dialog-message-reject": "Afbryd",
+       "ooui-dialog-process-error": "Noget gik galt",
+       "ooui-dialog-process-retry": "Prøv igen",
        "ooui-dialog-process-continue": "Fortsæt"
 }
index 881ff67..bf6b087 100644 (file)
@@ -6,11 +6,24 @@
                        "Kghbln",
                        "Marmase",
                        "Mirzali",
-                       "Se4598"
+                       "Se4598",
+                       "Kumkumuk"
                ]
        },
        "ooui-outline-control-move-down": "Bendi bere cêr",
        "ooui-outline-control-move-up": "Bendi bere cor",
        "ooui-outline-control-remove": "Obcey wedare",
-       "ooui-toolbar-more": "Zewbi"
+       "ooui-toolbar-more": "Zewbi",
+       "ooui-toolgroup-expand": "Dehana",
+       "ooui-toolgroup-collapse": "Deha tayn",
+       "ooui-dialog-message-accept": "TEMAM",
+       "ooui-dialog-message-reject": "Bıtexelne",
+       "ooui-dialog-process-error": "Tayê çi ğelet şi...",
+       "ooui-dialog-process-dismiss": "Racın",
+       "ooui-dialog-process-retry": "Fına bıcerbın",
+       "ooui-dialog-process-continue": "Dewam ke",
+       "ooui-selectfile-button-select": "Yu dosya weçinê",
+       "ooui-selectfile-not-supported": "Dosya weçinayış desteg nêvine na",
+       "ooui-selectfile-placeholder": "Dosya nêwçineya",
+       "ooui-selectfile-dragdrop-placeholder": "Dosya tiyara ake"
 }
index 59b7ccd..326baaf 100644 (file)
@@ -15,7 +15,7 @@
        "ooui-dialog-message-accept": "Sobib",
        "ooui-dialog-message-reject": "Loobu",
        "ooui-dialog-process-error": "Midagi läks valesti",
-       "ooui-dialog-process-dismiss": "Hülga",
+       "ooui-dialog-process-dismiss": "Sule",
        "ooui-dialog-process-retry": "Proovi uuesti",
        "ooui-dialog-process-continue": "Jätka",
        "ooui-selectfile-button-select": "Vali fail",
index 3894417..0c84bd1 100644 (file)
@@ -13,8 +13,8 @@
                        "Hwangjy9"
                ]
        },
-       "ooui-outline-control-move-down": "í\95­ëª©ì\9d\84 ì\95\84ë\9e\98ë¡\9c ì\98®ê¸°ê¸°",
-       "ooui-outline-control-move-up": "í\95­ëª©ì\9d\84 ì\9c\84ë¡\9c ì\98®ê¸°ê¸°",
+       "ooui-outline-control-move-down": "í\95­ëª©ì\9d\84 ì\95\84ë\9e\98ë¡\9c ì\9d´ë\8f\99",
+       "ooui-outline-control-move-up": "í\95­ëª©ì\9d\84 ì\9c\84ë¡\9c ì\9d´ë\8f\99",
        "ooui-outline-control-remove": "항목 제거",
        "ooui-toolbar-more": "더 보기",
        "ooui-toolgroup-expand": "더 보기",
index 4bf5dac..679f1a6 100644 (file)
@@ -6,7 +6,8 @@
                        "Elioqoshi",
                        "GretaDoci",
                        "Gertakapllani",
-                       "Techlik"
+                       "Techlik",
+                       "Liridon"
                ]
        },
        "ooui-outline-control-move-down": "Zhvendose artikullin më poshtë",
@@ -21,6 +22,8 @@
        "ooui-dialog-process-dismiss": "Largoje",
        "ooui-dialog-process-retry": "Provo përsëri",
        "ooui-dialog-process-continue": "Vazhdo",
+       "ooui-selectfile-button-select": "Përzgjidhni një skedë",
        "ooui-selectfile-not-supported": "Skedari i përzgjedhur nuk përkrahet",
-       "ooui-selectfile-placeholder": "Nuk është zgjedhur asnjë skedar"
+       "ooui-selectfile-placeholder": "Nuk është zgjedhur asnjë skedar",
+       "ooui-selectfile-dragdrop-placeholder": "Vendose skedën këtu"
 }
index 026bbb3..b272331 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:08Z
+ * Date: 2016-08-16T21:13:48Z
  */
 ( function ( OO ) {
 
@@ -27,6 +27,15 @@ OO.ui.ApexTheme = function OoUiApexTheme() {
 
 OO.inheritClass( OO.ui.ApexTheme, OO.ui.Theme );
 
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ApexTheme.prototype.getDialogTransitionDuration = function () {
+       return 250;
+};
+
 /* Instantiation */
 
 OO.ui.theme = new OO.ui.ApexTheme();
index fa5ec0a..c13ac7a 100644 (file)
@@ -1,21 +1,23 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:11Z
+ * Date: 2016-08-16T21:13:53Z
  */
 .oo-ui-element-hidden {
        display: none !important;
+       /* stylelint-disable-line declaration-no-important */
 }
 .oo-ui-buttonElement > .oo-ui-buttonElement-button {
        cursor: pointer;
        display: inline-block;
        vertical-align: middle;
-       font: inherit;
+       font-family: inherit;
+       font-size: inherit;
        line-height: normal;
        white-space: nowrap;
        -webkit-touch-callout: none;
 .oo-ui-buttonElement.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
        cursor: default;
 }
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover,
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
+       z-index: 2;
+}
 .oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator,
 .oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
 .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
 }
 .oo-ui-fieldLayout > .oo-ui-fieldLayout-help .oo-ui-fieldLayout-help-content {
        padding: 0.5em 0.75em;
-       line-height: 1.5em;
+       line-height: 1.5;
 }
 .oo-ui-fieldLayout:last-child {
        margin-bottom: 0;
        color: #d45353;
 }
 .oo-ui-fieldLayout-messages .oo-ui-labelWidget {
-       padding: 0;
-       line-height: 1.875em;
+       padding: 0.1em 0;
+       line-height: 1.5em;
        vertical-align: middle;
 }
 .oo-ui-actionFieldLayout {
 }
 .oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help .oo-ui-fieldsetLayout-help-content {
        padding: 0.5em 0.75em;
-       line-height: 1.5em;
+       line-height: 1.5;
 }
 .oo-ui-fieldsetLayout + .oo-ui-fieldsetLayout,
 .oo-ui-fieldsetLayout + .oo-ui-formLayout {
        overflow: auto;
        resize: none;
 }
+.oo-ui-textInputWidget [type="number"] {
+       -moz-appearance: textfield;
+}
+.oo-ui-textInputWidget [type="number"]::-webkit-outer-spin-button,
+.oo-ui-textInputWidget [type="number"]::-webkit-inner-spin-button {
+       -webkit-appearance: none;
+       margin: 0;
+}
 .oo-ui-textInputWidget [type="search"] {
        -webkit-appearance: textfield;
 }
 }
 .oo-ui-menuSelectWidget {
        position: absolute;
+       width: 100%;
+       z-index: 4;
        background-color: #ffffff;
        margin-top: -1px;
        border: 1px solid #cccccc;
 .oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle {
        cursor: pointer;
 }
-.oo-ui-dropdownWidget > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
 .oo-ui-dropdownWidget:last-child {
        margin-right: 0;
 }
        max-width: 50em;
        margin-right: 0.5em;
 }
-.oo-ui-comboBoxInputWidget > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
 .oo-ui-comboBoxInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
        cursor: pointer;
 }
-.oo-ui-comboBoxInputWidget-php input::-webkit-calendar-picker-indicator {
-       opacity: 0 !important;
+.oo-ui-comboBoxInputWidget-php ::-webkit-calendar-picker-indicator {
+       opacity: 0;
        position: absolute;
        right: 0;
        top: 0;
 .oo-ui-checkboxMultioptionWidget .oo-ui-checkboxInputWidget {
        margin-right: 0;
 }
+.oo-ui-progressBarWidget {
+       max-width: 50em;
+       background-color: #ffffff;
+       border: 1px solid #cccccc;
+       border-radius: 0.25em;
+       overflow: hidden;
+}
+.oo-ui-progressBarWidget-bar {
+       height: 1em;
+       border-right: 1px solid #cccccc;
+       -webkit-transition: width 250ms ease, margin-left 250ms ease;
+          -moz-transition: width 250ms ease, margin-left 250ms ease;
+               transition: width 250ms ease, margin-left 250ms ease;
+       background-color: #cde7f4;
+       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #eaf4fa), color-stop(100%, #b0d9ee));
+       background-image: -webkit-linear-gradient(top, #eaf4fa 0, #b0d9ee 100%);
+       background-image:    -moz-linear-gradient(top, #eaf4fa 0, #b0d9ee 100%);
+       background-image:         linear-gradient(to bottom, #eaf4fa 0, #b0d9ee 100%);
+       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffeaf4fa', endColorstr='#ffb0d9ee' )";
+}
+.oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
+       -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+          -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+               animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+       width: 40%;
+       margin-left: -10%;
+       border-left: 1px solid #a6cee1;
+}
+.oo-ui-progressBarWidget.oo-ui-widget-disabled {
+       opacity: 0.6;
+}
+@-webkit-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-moz-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
index 03380b2..4e8f65c 100644 (file)
@@ -1,21 +1,23 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:11Z
+ * Date: 2016-08-16T21:13:53Z
  */
 .oo-ui-element-hidden {
        display: none !important;
+       /* stylelint-disable-line declaration-no-important */
 }
 .oo-ui-buttonElement > .oo-ui-buttonElement-button {
        cursor: pointer;
        display: inline-block;
        vertical-align: middle;
-       font: inherit;
+       font-family: inherit;
+       font-size: inherit;
        line-height: normal;
        white-space: nowrap;
        -webkit-touch-callout: none;
 .oo-ui-buttonElement.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
        cursor: default;
 }
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover,
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
+.oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
+       z-index: 2;
+}
 .oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator,
 .oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
 .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        text-decoration: none;
 }
 .oo-ui-buttonElement > .oo-ui-buttonElement-button:focus {
+       border-radius: 2px;
        outline: 0;
 }
 .oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
        margin-left: 0;
 }
 .oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
+       margin-right: 0.25em;
        margin-left: 0.46875em;
 }
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button .oo-ui-indicatorElement-indicator {
 }
 .oo-ui-buttonElement-framed > input.oo-ui-buttonElement-button,
 .oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
-       line-height: 1.2em;
+       line-height: 1.2;
 }
 .oo-ui-buttonElement-framed.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
        position: absolute;
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
        background-color: #d9d9d9;
        border-color: #d9d9d9;
-       box-shadow: none;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button {
        background-color: #999999;
        color: #ffffff;
+       border-color: #999999;
+       z-index: 3;
+}
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-buttonElement-active > .oo-ui-buttonElement-button:focus {
+       border-color: #347bff;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button {
        color: #347bff;
 }
 .oo-ui-fieldLayout > .oo-ui-fieldLayout-help .oo-ui-fieldLayout-help-content {
        padding: 0.5em 0.75em;
-       line-height: 1.5em;
+       line-height: 1.5;
 }
 .oo-ui-fieldLayout:last-child {
        margin-bottom: 0;
        margin-bottom: 1.25em;
 }
 .oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
-       padding: 0.25em 0.25em 0.25em 1em;
+       padding: 0.25em 0.25em 0.25em 0.5em;
 }
 .oo-ui-fieldLayout.oo-ui-fieldLayout-align-top.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-labelElement-label {
        padding-top: 0.25em;
 }
 .oo-ui-fieldLayout-messages .oo-ui-labelWidget {
        display: table-cell;
-       padding: 0;
-       line-height: 1.875em;
+       padding: 0.1em 0;
+       line-height: 1.5;
        vertical-align: middle;
 }
 .oo-ui-actionFieldLayout {
 }
 .oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help .oo-ui-fieldsetLayout-help-content {
        padding: 0.5em 0.75em;
-       line-height: 1.5em;
+       line-height: 1.5;
 }
 .oo-ui-fieldsetLayout + .oo-ui-fieldsetLayout,
 .oo-ui-fieldsetLayout + .oo-ui-formLayout {
        margin-top: 2em;
 }
 .oo-ui-fieldsetLayout > .oo-ui-labelElement-label {
-       font-size: 1.1em;
        margin-bottom: 0.5em;
-       padding: 0.25em 0;
+       font-size: 1.1em;
        font-weight: bold;
 }
 .oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-labelElement-label {
        padding-left: 2em;
-       line-height: 1.8em;
+       line-height: 1.8;
 }
 .oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-iconElement-icon {
        left: 0;
        background-color: #eeeeee;
 }
 .oo-ui-optionWidget .oo-ui-labelElement-label {
-       line-height: 1.5em;
+       line-height: 1.5;
 }
 .oo-ui-selectWidget-depressed .oo-ui-optionWidget-selected,
 .oo-ui-selectWidget-pressed .oo-ui-optionWidget-pressed,
        background-color: transparent;
 }
 .oo-ui-radioOptionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       padding: 0.25em 0.25em 0.25em 1em;
+       padding: 0.25em 0.25em 0.25em 0.5em;
 }
 .oo-ui-radioOptionWidget .oo-ui-radioInputWidget {
        margin-right: 0;
 .oo-ui-iconWidget {
        display: inline-block;
        vertical-align: middle;
-       line-height: 2.5em;
+       line-height: 2.5;
 }
 .oo-ui-iconWidget.oo-ui-widget-disabled {
        opacity: 0.2;
 .oo-ui-indicatorWidget {
        display: inline-block;
        vertical-align: middle;
-       line-height: 2.5em;
+       line-height: 2.5;
        margin: 0.46875em;
 }
 .oo-ui-indicatorWidget.oo-ui-widget-disabled {
        border-radius: 0;
        margin-left: -1px;
 }
-.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed .oo-ui-buttonElement-button:focus {
-       z-index: 2;
-}
 .oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:first-child .oo-ui-buttonElement-button {
        border-bottom-left-radius: 2px;
        border-top-left-radius: 2px;
        border-bottom-right-radius: 2px;
        border-top-right-radius: 2px;
 }
+.oo-ui-buttonGroupWidget.oo-ui-widget-enabled .oo-ui-buttonElement .oo-ui-buttonElement-button:focus {
+       border-color: #347bff;
+       z-index: 3;
+}
 .oo-ui-popupWidget {
        position: absolute;
        /* @noflip */
        background-position: center center;
        background-origin: border-box;
        background-size: 0 0;
-       border: 1px solid #777777;
+       border: 1px solid #767676;
        border-radius: 2px;
 }
 .oo-ui-checkboxInputWidget [type="checkbox"]:checked + span {
        background-size: 100% 100%;
 }
 .oo-ui-checkboxInputWidget [type="checkbox"]:active + span {
-       background-color: #cccccc;
-       border-color: #cccccc;
+       background-color: #767676;
+       border-color: #767676;
 }
 .oo-ui-checkboxInputWidget [type="checkbox"]:focus + span {
        border-width: 2px;
        padding: 0.25em 0;
 }
 .oo-ui-checkboxMultiselectInputWidget .oo-ui-fieldLayout .oo-ui-fieldLayout-body .oo-ui-labelElement-label {
-       line-height: 1.5em;
+       line-height: 1.5;
 }
 .oo-ui-dropdownInputWidget {
        position: relative;
        background-position: center center;
        background-origin: border-box;
        background-size: 0 0;
-       border: 1px solid #777777;
+       border: 1px solid #767676;
        border-radius: 100%;
 }
 .oo-ui-radioInputWidget [type="radio"]:checked + span {
        background-size: 100% 100%;
 }
 .oo-ui-radioInputWidget [type="radio"]:active + span {
-       background-color: #cccccc;
-       border-color: #cccccc;
+       background-color: #767676;
+       border-color: #767676;
 }
 .oo-ui-radioInputWidget [type="radio"]:focus + span {
        border-width: 2px;
        padding: 0.25em 0;
 }
 .oo-ui-radioSelectInputWidget .oo-ui-fieldLayout .oo-ui-fieldLayout-body .oo-ui-labelElement-label {
-       line-height: 1.5em;
+       line-height: 1.5;
 }
 .oo-ui-textInputWidget {
        position: relative;
        overflow: auto;
        resize: none;
 }
+.oo-ui-textInputWidget [type="number"] {
+       -moz-appearance: textfield;
+}
+.oo-ui-textInputWidget [type="number"]::-webkit-outer-spin-button,
+.oo-ui-textInputWidget [type="number"]::-webkit-inner-spin-button {
+       -webkit-appearance: none;
+       margin: 0;
+}
 .oo-ui-textInputWidget [type="search"] {
        -webkit-appearance: textfield;
 }
        border-radius: 2px;
 }
 .oo-ui-textInputWidget textarea {
-       line-height: 1.275em;
+       line-height: 1.275;
 }
 .oo-ui-textInputWidget .oo-ui-pendingElement-pending {
        background-color: transparent;
           -moz-transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
                transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
 }
+.oo-ui-textInputWidget.oo-ui-widget-enabled input:hover,
+.oo-ui-textInputWidget.oo-ui-widget-enabled textarea:hover {
+       border-color: #aaaaaa;
+}
 .oo-ui-textInputWidget.oo-ui-widget-enabled input:focus,
 .oo-ui-textInputWidget.oo-ui-widget-enabled textarea:focus {
        outline: 0;
        color: #777777;
        text-shadow: 0 1px 1px #ffffff;
 }
+.oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly]:hover,
+.oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly]:hover {
+       border-color: #cccccc;
+}
 .oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly]:focus,
 .oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly]:focus {
        border-color: #cccccc;
        box-shadow: inset 0 0 0 0.1em #cccccc;
 }
+.oo-ui-textInputWidget.oo-ui-widget-enabled :-moz-placeholder {
+       color: #595959;
+       opacity: 1;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled ::-moz-placeholder {
+       color: #595959;
+       opacity: 1;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled :-ms-input-placeholder {
+       color: #595959;
+}
+.oo-ui-textInputWidget.oo-ui-widget-enabled ::-webkit-input-placeholder {
+       color: #595959;
+}
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input,
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea {
        border-color: #ff0000;
 }
+.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input:hover,
+.oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea:hover {
+       border-color: #ff0000;
+}
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input:focus,
 .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea:focus {
        border-color: #ff0000;
 }
 .oo-ui-textInputWidget > .oo-ui-labelElement-label {
        padding: 0.4em;
-       line-height: 1.5em;
+       line-height: 1.5;
        color: #888888;
 }
 .oo-ui-textInputWidget-labelPosition-after.oo-ui-indicatorElement > .oo-ui-labelElement-label {
 }
 .oo-ui-menuSelectWidget {
        position: absolute;
+       width: 100%;
+       z-index: 4;
        background-color: #ffffff;
        margin-top: -1px;
        border: 1px solid #aaaaaa;
        position: relative;
        width: 100%;
        max-width: 50em;
-       background-color: #ffffff;
        margin-right: 0.5em;
 }
 .oo-ui-dropdownWidget-handle {
 .oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle {
        cursor: pointer;
 }
-.oo-ui-dropdownWidget > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
 .oo-ui-dropdownWidget:last-child {
        margin-right: 0;
 }
 .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        margin: 0 1em;
 }
-.oo-ui-dropdownWidget:hover .oo-ui-dropdownWidget-handle {
+.oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle {
+       background-color: #ffffff;
+       -webkit-transition: border-color 100ms;
+          -moz-transition: border-color 100ms;
+               transition: border-color 100ms;
+}
+.oo-ui-dropdownWidget.oo-ui-widget-enabled:hover .oo-ui-dropdownWidget-handle {
        border-color: #aaaaaa;
 }
 .oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
        max-width: 50em;
        margin-right: 0.5em;
 }
-.oo-ui-comboBoxInputWidget > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
 .oo-ui-comboBoxInputWidget.oo-ui-widget-enabled > .oo-ui-indicatorElement-indicator {
        cursor: pointer;
 }
-.oo-ui-comboBoxInputWidget-php input::-webkit-calendar-picker-indicator {
-       opacity: 0 !important;
+.oo-ui-comboBoxInputWidget-php ::-webkit-calendar-picker-indicator {
+       opacity: 0;
        position: absolute;
        right: 0;
        top: 0;
        overflow: hidden;
 }
 .oo-ui-multioptionWidget .oo-ui-labelElement-label {
-       line-height: 1.5em;
+       line-height: 1.5;
 }
 .oo-ui-multioptionWidget.oo-ui-widget-disabled {
        color: #cccccc;
        vertical-align: middle;
 }
 .oo-ui-checkboxMultioptionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       padding: 0.25em 0.25em 0.25em 1em;
+       padding: 0.25em 0.25em 0.25em 0.5em;
 }
 .oo-ui-checkboxMultioptionWidget .oo-ui-checkboxInputWidget {
        margin-right: 0;
 }
+.oo-ui-progressBarWidget {
+       max-width: 50em;
+       background-color: #ffffff;
+       border: 1px solid #cccccc;
+       border-radius: 2px;
+       overflow: hidden;
+}
+.oo-ui-progressBarWidget-bar {
+       background-color: #dddddd;
+       height: 1em;
+       -webkit-transition: width 200ms, margin-left 200ms;
+          -moz-transition: width 200ms, margin-left 200ms;
+               transition: width 200ms, margin-left 200ms;
+}
+.oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
+       -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+          -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+               animation: oo-ui-progressBarWidget-slide 2s infinite linear;
+       width: 40%;
+       margin-left: -10%;
+       border-left-width: 1px;
+}
+.oo-ui-progressBarWidget.oo-ui-widget-disabled {
+       opacity: 0.6;
+}
+@-webkit-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@-moz-keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
+@keyframes oo-ui-progressBarWidget-slide {
+       from {
+               margin-left: -40%;
+       }
+       to {
+               margin-left: 100%;
+       }
+}
index e230a49..cd1a3de 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:08Z
+ * Date: 2016-08-16T21:13:48Z
  */
 ( function ( OO ) {
 
@@ -1634,6 +1634,18 @@ OO.ui.Theme.prototype.updateElementClasses = function ( element ) {
                .addClass( classes.on.join( ' ' ) );
 };
 
+/**
+ * Get the transition duration in milliseconds for dialogs opening/closing
+ *
+ * The dialog should be fully rendered this many milliseconds after the
+ * ready process has executed.
+ *
+ * @return {number} Transition duration in milliseconds
+ */
+OO.ui.Theme.prototype.getDialogTransitionDuration = function () {
+       return 0;
+};
+
 /**
  * The TabIndexedElement class is an attribute mixin used to add additional functionality to an
  * element created by another class. The mixin provides a ‘tabIndex’ property, which specifies the
@@ -2014,6 +2026,7 @@ OO.ui.mixin.ButtonElement.prototype.toggleFramed = function ( framed ) {
 OO.ui.mixin.ButtonElement.prototype.setActive = function ( value ) {
        this.active = !!value;
        this.$element.toggleClass( 'oo-ui-buttonElement-active', this.active );
+       this.updateThemeClasses();
        return this;
 };
 
@@ -3727,6 +3740,7 @@ OO.ui.IndicatorWidget.static.tagName = 'span';
  * @class
  * @extends OO.ui.Widget
  * @mixins OO.ui.mixin.LabelElement
+ * @mixins OO.ui.mixin.TitledElement
  *
  * @constructor
  * @param {Object} [config] Configuration options
@@ -6981,7 +6995,7 @@ OO.ui.CheckboxMultiselectWidget.prototype.getRelativeFocusableItem = function (
  * @param {jQuery.Event} e
  */
 OO.ui.CheckboxMultiselectWidget.prototype.onClick = function ( e ) {
-       var $options, checked,
+       var $options, lastClickedIndex, nowClickedIndex, i, direction, wasSelected, items,
                $lastClicked = this.$lastClicked,
                $nowClicked = $( e.target ).closest( '.oo-ui-checkboxMultioptionWidget' )
                        .not( '.oo-ui-widget-disabled' );
@@ -6989,19 +7003,32 @@ OO.ui.CheckboxMultiselectWidget.prototype.onClick = function ( e ) {
        // Allow selecting multiple options at once by Shift-clicking them
        if ( $lastClicked && $nowClicked.length && e.shiftKey ) {
                $options = this.$group.find( '.oo-ui-checkboxMultioptionWidget' );
-               checked = $nowClicked.find( 'input' ).prop( 'checked' );
-
-               $options
-                       .slice(
-                               Math.min( $options.index( $lastClicked ), $options.index( $nowClicked ) ),
-                               Math.max( $options.index( $lastClicked ), $options.index( $nowClicked ) ) + 1
-                       )
-                       .find( 'input' )
-                       .filter( function () {
-                               return !this.disabled;
-                       } )
-                       .prop( 'checked', checked )
-                       .trigger( 'change' );
+               lastClickedIndex = $options.index( $lastClicked );
+               nowClickedIndex = $options.index( $nowClicked );
+               // If it's the same item, either the user is being silly, or it's a fake event generated by the
+               // browser. In either case we don't need custom handling.
+               if ( nowClickedIndex !== lastClickedIndex ) {
+                       items = this.items;
+                       wasSelected = items[ nowClickedIndex ].isSelected();
+                       direction = nowClickedIndex > lastClickedIndex ? 1 : -1;
+
+                       // This depends on the DOM order of the items and the order of the .items array being the same.
+                       for ( i = lastClickedIndex; i !== nowClickedIndex; i += direction ) {
+                               if ( !items[ i ].isDisabled() ) {
+                                       items[ i ].setSelected( !wasSelected );
+                               }
+                       }
+                       // For the now-clicked element, use immediate timeout to allow the browser to do its own
+                       // handling first, then set our value. The order in which events happen is different for
+                       // clicks on the <input> and on the <label> and there are additional fake clicks fired for
+                       // non-click actions that change the checkboxes.
+                       e.preventDefault();
+                       setTimeout( function () {
+                               if ( !items[ nowClickedIndex ].isDisabled() ) {
+                                       items[ nowClickedIndex ].setSelected( !wasSelected );
+                               }
+                       } );
+               }
        }
 
        if ( $nowClicked.length ) {
@@ -7299,6 +7326,103 @@ OO.ui.FloatingMenuSelectWidget.prototype.toggle = function ( visible ) {
        return this;
 };
 
+/**
+ * Progress bars visually display the status of an operation, such as a download,
+ * and can be either determinate or indeterminate:
+ *
+ * - **determinate** process bars show the percent of an operation that is complete.
+ *
+ * - **indeterminate** process bars use a visual display of motion to indicate that an operation
+ *   is taking place. Because the extent of an indeterminate operation is unknown, the bar does
+ *   not use percentages.
+ *
+ * The value of the `progress` configuration determines whether the bar is determinate or indeterminate.
+ *
+ *     @example
+ *     // Examples of determinate and indeterminate progress bars.
+ *     var progressBar1 = new OO.ui.ProgressBarWidget( {
+ *         progress: 33
+ *     } );
+ *     var progressBar2 = new OO.ui.ProgressBarWidget();
+ *
+ *     // Create a FieldsetLayout to layout progress bars
+ *     var fieldset = new OO.ui.FieldsetLayout;
+ *     fieldset.addItems( [
+ *        new OO.ui.FieldLayout( progressBar1, {label: 'Determinate', align: 'top'}),
+ *        new OO.ui.FieldLayout( progressBar2, {label: 'Indeterminate', align: 'top'})
+ *     ] );
+ *     $( 'body' ).append( fieldset.$element );
+ *
+ * @class
+ * @extends OO.ui.Widget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {number|boolean} [progress=false] The type of progress bar (determinate or indeterminate).
+ *  To create a determinate progress bar, specify a number that reflects the initial percent complete.
+ *  By default, the progress bar is indeterminate.
+ */
+OO.ui.ProgressBarWidget = function OoUiProgressBarWidget( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.ProgressBarWidget.parent.call( this, config );
+
+       // Properties
+       this.$bar = $( '<div>' );
+       this.progress = null;
+
+       // Initialization
+       this.setProgress( config.progress !== undefined ? config.progress : false );
+       this.$bar.addClass( 'oo-ui-progressBarWidget-bar' );
+       this.$element
+               .attr( {
+                       role: 'progressbar',
+                       'aria-valuemin': 0,
+                       'aria-valuemax': 100
+               } )
+               .addClass( 'oo-ui-progressBarWidget' )
+               .append( this.$bar );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ProgressBarWidget, OO.ui.Widget );
+
+/* Static Properties */
+
+OO.ui.ProgressBarWidget.static.tagName = 'div';
+
+/* Methods */
+
+/**
+ * Get the percent of the progress that has been completed. Indeterminate progresses will return `false`.
+ *
+ * @return {number|boolean} Progress percent
+ */
+OO.ui.ProgressBarWidget.prototype.getProgress = function () {
+       return this.progress;
+};
+
+/**
+ * Set the percent of the process completed or `false` for an indeterminate process.
+ *
+ * @param {number|boolean} progress Progress percent or `false` for indeterminate
+ */
+OO.ui.ProgressBarWidget.prototype.setProgress = function ( progress ) {
+       this.progress = progress;
+
+       if ( progress !== false ) {
+               this.$bar.css( 'width', this.progress + '%' );
+               this.$element.attr( 'aria-valuenow', this.progress );
+       } else {
+               this.$bar.css( 'width', '' );
+               this.$element.removeAttr( 'aria-valuenow' );
+       }
+       this.$element.toggleClass( 'oo-ui-progressBarWidget-indeterminate', progress === false );
+};
+
 /**
  * InputWidget is the base class for all input widgets, which
  * include {@link OO.ui.TextInputWidget text inputs}, {@link OO.ui.CheckboxInputWidget checkbox inputs},
@@ -7580,7 +7704,7 @@ OO.ui.InputWidget.prototype.restorePreInfuseState = function ( state ) {
  * ButtonInputWidget is used to submit HTML forms and is intended to be used within
  * a OO.ui.FormLayout. If you do not need the button to work with HTML forms, you probably
  * want to use OO.ui.ButtonWidget instead. Button input widgets can be rendered as either an
- * HTML `<button/>` (the default) or an HTML `<input/>` tags. See the
+ * HTML `<button>` (the default) or an HTML `<input>` tags. See the
  * [OOjs UI documentation on MediaWiki] [1] for more information.
  *
  *     @example
@@ -7605,8 +7729,8 @@ OO.ui.InputWidget.prototype.restorePreInfuseState = function ( state ) {
  * @constructor
  * @param {Object} [config] Configuration options
  * @cfg {string} [type='button'] The value of the HTML `'type'` attribute: 'button', 'submit' or 'reset'.
- * @cfg {boolean} [useInputTag=false] Use an `<input/>` tag instead of a `<button/>` tag, the default.
- *  Widgets configured to be an `<input/>` do not support {@link #icon icons} and {@link #indicator indicators},
+ * @cfg {boolean} [useInputTag=false] Use an `<input>` tag instead of a `<button>` tag, the default.
+ *  Widgets configured to be an `<input>` do not support {@link #icon icons} and {@link #indicator indicators},
  *  non-plaintext {@link #label labels}, or {@link #value values}. In general, useInputTag should only
  *  be set to `true` when there’s need to support IE 6 in a form with multiple buttons.
  */
@@ -7671,7 +7795,7 @@ OO.ui.ButtonInputWidget.prototype.getInputElement = function ( config ) {
 /**
  * Set label value.
  *
- * If #useInputTag is `true`, the label is set as the `value` of the `<input/>` tag.
+ * If #useInputTag is `true`, the label is set as the `value` of the `<input>` tag.
  *
  * @param {jQuery|string|Function|null} label Label nodes, text, a function that returns nodes or
  *  text, or `null` for no label
@@ -7697,7 +7821,7 @@ OO.ui.ButtonInputWidget.prototype.setLabel = function ( label ) {
 /**
  * Set the value of the input.
  *
- * This method is disabled for button inputs configured as {@link #useInputTag <input/> tags}, as
+ * This method is disabled for button inputs configured as {@link #useInputTag <input> tags}, as
  * they do not support {@link #value values}.
  *
  * @param {string} value New value
@@ -8564,7 +8688,8 @@ OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
        // Events
        this.$input.on( {
                keypress: this.onKeyPress.bind( this ),
-               blur: this.onBlur.bind( this )
+               blur: this.onBlur.bind( this ),
+               focus: this.onFocus.bind( this )
        } );
        this.$input.one( {
                focus: this.onElementAttach.bind( this )
@@ -8576,6 +8701,7 @@ OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
                change: 'onChange',
                disable: 'onDisable'
        } );
+       this.on( 'change', OO.ui.debounce( this.onDebouncedChange.bind( this ), 250 ) );
 
        // Initialization
        this.$element
@@ -8718,6 +8844,16 @@ OO.ui.TextInputWidget.prototype.onBlur = function () {
        this.setValidityFlag();
 };
 
+/**
+ * Handle focus events.
+ *
+ * @private
+ * @param {jQuery.Event} e Focus event
+ */
+OO.ui.TextInputWidget.prototype.onFocus = function () {
+       this.setValidityFlag( true );
+};
+
 /**
  * Handle element attach events.
  *
@@ -8739,10 +8875,19 @@ OO.ui.TextInputWidget.prototype.onElementAttach = function () {
  */
 OO.ui.TextInputWidget.prototype.onChange = function () {
        this.updateSearchIndicator();
-       this.setValidityFlag();
        this.adjustSize();
 };
 
+/**
+ * Handle debounced change events.
+ *
+ * @param {string} value
+ * @private
+ */
+OO.ui.TextInputWidget.prototype.onDebouncedChange = function () {
+       this.setValidityFlag();
+};
+
 /**
  * Handle disable events.
  *
index 15d4d44..ab1e9ea 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:08Z
+ * Date: 2016-08-16T21:13:48Z
  */
 ( function ( OO ) {
 
@@ -48,9 +48,7 @@ OO.ui.MediaWikiTheme.prototype.getElementClasses = function ( element ) {
        if ( element.supports( [ 'hasFlag' ] ) ) {
                isFramed = element.supports( [ 'isFramed' ] ) && element.isFramed();
                isActive = element.supports( [ 'isActive' ] ) && element.isActive();
-               if (
-                       ( isFramed && ( isActive || element.isDisabled() || element.hasFlag( 'primary' ) ) )
-               ) {
+               if ( isFramed && ( isActive || element.isDisabled() || element.hasFlag( 'primary' ) ) ) {
                        // Button with a dark background, use white icon
                        variants.invert = true;
                } else if ( !isFramed && element.isDisabled() ) {
@@ -72,6 +70,13 @@ OO.ui.MediaWikiTheme.prototype.getElementClasses = function ( element ) {
        return classes;
 };
 
+/**
+ * @inheritdoc
+ */
+OO.ui.MediaWikiTheme.prototype.getDialogTransitionDuration = function () {
+       return 250;
+};
+
 /* Instantiation */
 
 OO.ui.theme = new OO.ui.MediaWikiTheme();
index cb5eeda..75fd654 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:11Z
+ * Date: 2016-08-16T21:13:53Z
  */
 .oo-ui-popupTool .oo-ui-popupWidget-popup,
 .oo-ui-popupTool .oo-ui-popupWidget-anchor {
        clear: both;
 }
 .oo-ui-toolbar-bar {
-       line-height: 1em;
+       line-height: 1;
        position: relative;
 }
 .oo-ui-toolbar-actions {
index 09730d4..0b55308 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:11Z
+ * Date: 2016-08-16T21:13:53Z
  */
 .oo-ui-popupTool .oo-ui-popupWidget-popup,
 .oo-ui-popupTool .oo-ui-popupWidget-anchor {
        width: 1.875em;
 }
 .oo-ui-toolGroupTool > .oo-ui-popupToolGroup.oo-ui-labelElement > .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       line-height: 2.1em;
+       line-height: 2.1;
 }
 .oo-ui-toolGroup {
        display: inline-block;
        vertical-align: middle;
+       border-right: 1px solid #cccccc;
        border-radius: 0;
-       border-right: 1px solid #dddddd;
 }
 .oo-ui-toolGroup-empty {
        display: none;
@@ -56,9 +56,6 @@
 .oo-ui-toolbar-narrow .oo-ui-toolGroup + .oo-ui-toolGroup {
        margin-left: 0;
 }
-.oo-ui-toolGroup .oo-ui-toolGroup .oo-ui-widget-enabled {
-       border-right: 0 !important;
-}
 .oo-ui-barToolGroup > .oo-ui-iconElement-icon,
 .oo-ui-barToolGroup > .oo-ui-labelElement-label {
        display: none;
        width: 1.875em;
 }
 .oo-ui-barToolGroup > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-title {
-       line-height: 2.1em;
+       line-height: 2.1;
        padding: 0 0.4em;
 }
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-title {
+       color: #555555;
+}
 .oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:hover {
-       border-color: rgba(0, 0, 0, 0.2);
        background-color: #eeeeee;
 }
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool > a.oo-ui-tool-link .oo-ui-tool-title {
-       color: #555555;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled {
-       border-color: rgba(0, 0, 0, 0.2);
-       box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled.oo-ui-tool-active {
        background-color: #e5e5e5;
+       box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
 }
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled:hover {
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled.oo-ui-tool-active:hover {
        background-color: #eeeeee;
 }
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
-       border-left-color: rgba(0, 0, 0, 0.1);
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
-}
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.2;
+.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled.oo-ui-tool-active:active {
+       background-color: #e5e5e5;
 }
 .oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled > .oo-ui-tool-link .oo-ui-iconElement-icon {
        opacity: 0.7;
 .oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:hover > .oo-ui-tool-link .oo-ui-iconElement-icon {
        opacity: 0.9;
 }
-.oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-widget-enabled:active {
-       background-color: #e7e7e7;
-}
-.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > a.oo-ui-tool-link .oo-ui-tool-title {
+.oo-ui-barToolGroup.oo-ui-widget-enabled .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-tool-title,
+.oo-ui-barToolGroup.oo-ui-widget-disabled .oo-ui-tool > .oo-ui-tool-link .oo-ui-tool-title {
        color: #cccccc;
 }
-.oo-ui-barToolGroup.oo-ui-widget-disabled > .oo-ui-toolGroup-tools > .oo-ui-tool > a.oo-ui-tool-link .oo-ui-iconElement-icon {
+.oo-ui-barToolGroup.oo-ui-widget-enabled .oo-ui-tool.oo-ui-widget-disabled > .oo-ui-tool-link .oo-ui-iconElement-icon,
+.oo-ui-barToolGroup.oo-ui-widget-disabled .oo-ui-tool > .oo-ui-tool-link .oo-ui-iconElement-icon {
        opacity: 0.2;
 }
 .oo-ui-popupToolGroup {
        min-width: 3.75em;
 }
 .oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
-       line-height: 2.6em;
+       line-height: 2.6;
        margin: 0 1em;
 }
 .oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
 .oo-ui-toolbar-narrow .oo-ui-popupToolGroup.oo-ui-labelElement.oo-ui-indicatorElement .oo-ui-popupToolGroup-handle .oo-ui-labelElement-label {
        margin-right: 1.75em;
 }
-.oo-ui-popupToolGroup.oo-ui-widget-enabled .oo-ui-popupToolGroup-handle:hover {
-       background-color: #eeeeee;
-}
-.oo-ui-popupToolGroup.oo-ui-widget-enabled .oo-ui-popupToolGroup-handle:active {
-       background-color: #e5e5e5;
-}
 .oo-ui-popupToolGroup-handle {
        padding: 0.3125em;
        height: 2.5em;
 .oo-ui-toolbar-narrow .oo-ui-popupToolGroup-handle .oo-ui-iconElement-icon {
        left: 0;
 }
+.oo-ui-popupToolGroup.oo-ui-widget-enabled .oo-ui-popupToolGroup-handle:hover {
+       background-color: #eeeeee;
+}
+.oo-ui-popupToolGroup.oo-ui-widget-enabled .oo-ui-popupToolGroup-handle:active {
+       background-color: #e5e5e5;
+}
 .oo-ui-popupToolGroup-header {
-       line-height: 2.6em;
+       line-height: 2.6;
        margin: 0 0.6em;
        font-weight: bold;
 }
 }
 .oo-ui-popupToolGroup .oo-ui-tool-link {
        padding: 0.4em 0.625em;
-       box-sizing: border-box;
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
 }
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-iconElement-icon {
        height: 2.5em;
 }
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel,
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-title {
-       line-height: 2em;
+       line-height: 2;
 }
 .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel {
        color: #888888;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
-.oo-ui-listToolGroup.oo-ui-popupToolGroup-active {
-       border-color: rgba(0, 0, 0, 0.2);
-}
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
-       border-color: rgba(0, 0, 0, 0.2);
        background-color: #eeeeee;
 }
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:active {
-       background-color: #e7e7e7;
-}
 .oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover .oo-ui-tool-link .oo-ui-iconElement-icon {
        opacity: 0.9;
 }
 .oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled {
-       border-color: rgba(0, 0, 0, 0.1);
        box-shadow: inset 0 0.07em 0.07em 0 rgba(0, 0, 0, 0.07);
        background-color: #e5e5e5;
 }
-.oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled {
-       border-top-color: rgba(0, 0, 0, 0.1);
-}
 .oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled:hover {
-       border-color: rgba(0, 0, 0, 0.2);
        background-color: #eeeeee;
 }
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-accel {
-       color: #dddddd;
-}
-.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-listToolGroup.oo-ui-widget-disabled {
+.oo-ui-listToolGroup.oo-ui-widget-disabled,
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-title,
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-accel {
        color: #cccccc;
 }
 .oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
-.oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon {
+.oo-ui-listToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon,
+.oo-ui-listToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-iconElement-icon {
        opacity: 0.2;
 }
 .oo-ui-menuToolGroup .oo-ui-tool {
 .oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
        background-color: #eeeeee;
 }
-.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-tool-title {
-       color: #cccccc;
-}
-.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-link .oo-ui-iconElement-icon {
-       opacity: 0.2;
-}
-.oo-ui-menuToolGroup.oo-ui-widget-disabled {
+.oo-ui-menuToolGroup.oo-ui-widget-disabled,
+.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-tool-title {
        color: #cccccc;
 }
 .oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator,
-.oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon {
+.oo-ui-menuToolGroup.oo-ui-widget-disabled .oo-ui-iconElement-icon,
+.oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-disabled .oo-ui-iconElement-icon {
        opacity: 0.2;
 }
 .oo-ui-toolbar {
        clear: both;
 }
 .oo-ui-toolbar-bar {
-       line-height: 1em;
+       line-height: 1;
        position: relative;
 }
 .oo-ui-toolbar-actions {
index 97a8fc9..18fda57 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:08Z
+ * Date: 2016-08-16T21:13:48Z
  */
 ( function ( OO ) {
 
@@ -1292,24 +1292,26 @@ OO.ui.ToolFactory.prototype.extract = function ( collection, used ) {
        var i, len, item, name, tool,
                names = [];
 
-       if ( collection === '*' ) {
-               for ( name in this.registry ) {
-                       tool = this.registry[ name ];
-                       if (
-                               // Only add tools by group name when auto-add is enabled
-                               tool.static.autoAddToCatchall &&
-                               // Exclude already used tools
-                               ( !used || !used[ name ] )
-                       ) {
-                               names.push( name );
-                               if ( used ) {
-                                       used[ name ] = true;
+       collection = !Array.isArray( collection ) ? [ collection ] : collection;
+
+       for ( i = 0, len = collection.length; i < len; i++ ) {
+               item = collection[ i ];
+               if ( item === '*' ) {
+                       for ( name in this.registry ) {
+                               tool = this.registry[ name ];
+                               if (
+                                       // Only add tools by group name when auto-add is enabled
+                                       tool.static.autoAddToCatchall &&
+                                       // Exclude already used tools
+                                       ( !used || !used[ name ] )
+                               ) {
+                                       names.push( name );
+                                       if ( used ) {
+                                               used[ name ] = true;
+                                       }
                                }
                        }
-               }
-       } else if ( Array.isArray( collection ) ) {
-               for ( i = 0, len = collection.length; i < len; i++ ) {
-                       item = collection[ i ];
+               } else {
                        // Allow plain strings as shorthand for named tools
                        if ( typeof item === 'string' ) {
                                item = { name: item };
index 68156c7..b0e87af 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:11Z
+ * Date: 2016-08-16T21:13:53Z
  */
 .oo-ui-draggableElement-handle,
 .oo-ui-draggableElement-handle.oo-ui-widget {
 .oo-ui-draggableGroupElement-horizontal .oo-ui-draggableElement {
        display: inline-block;
 }
-.oo-ui-lookupElement > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
 .oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous > .oo-ui-panelLayout-scrollable {
        overflow-y: hidden;
 }
@@ -89,6 +85,8 @@
        left: 0;
        right: 0;
        bottom: 0;
+       /* stylelint-disable declaration-no-important */
+       /* stylelint-enable declaration-no-important */
 }
 .oo-ui-menuLayout-menu,
 .oo-ui-menuLayout-content {
        padding: 0;
        background-color: transparent;
 }
-.oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
-       position: relative;
-}
 .oo-ui-buttonOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
 .oo-ui-buttonOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
        position: static;
        left: 2.25em;
        margin-left: -2px;
 }
-.oo-ui-progressBarWidget {
-       max-width: 50em;
-       background-color: #ffffff;
-       border: 1px solid #cccccc;
-       border-radius: 0.25em;
-       overflow: hidden;
-}
-.oo-ui-progressBarWidget-bar {
-       height: 1em;
-       border-right: 1px solid #cccccc;
-       -webkit-transition: width 250ms ease, margin-left 250ms ease;
-          -moz-transition: width 250ms ease, margin-left 250ms ease;
-               transition: width 250ms ease, margin-left 250ms ease;
-       background-color: #cde7f4;
-       background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #eaf4fa), color-stop(100%, #b0d9ee));
-       background-image: -webkit-linear-gradient(top, #eaf4fa 0, #b0d9ee 100%);
-       background-image:    -moz-linear-gradient(top, #eaf4fa 0, #b0d9ee 100%);
-       background-image:         linear-gradient(to bottom, #eaf4fa 0, #b0d9ee 100%);
-       -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffeaf4fa', endColorstr='#ffb0d9ee' )";
-}
-.oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
-       -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-          -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-               animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-       width: 40%;
-       margin-left: -10%;
-       border-left: 1px solid #a6cee1;
-}
-.oo-ui-progressBarWidget.oo-ui-widget-disabled {
-       opacity: 0.6;
-}
-@-webkit-keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
-@-moz-keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
-@keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
 .oo-ui-selectFileWidget {
        display: inline-block;
        vertical-align: middle;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
-       position: absolute;
-       top: 0;
-       bottom: 0;
-       left: 0;
-       right: 0;
-       text-overflow: ellipsis;
-}
-.oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-fileType {
-       float: right;
-}
 .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator,
-.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
+.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon {
        position: absolute;
 }
 .oo-ui-widget-disabled .oo-ui-selectFileWidget-info {
            -ms-user-select: none;
                user-select: none;
 }
-.oo-ui-selectFileWidget .oo-ui-selectFileWidget-clearButton {
+.oo-ui-selectFileWidget-label {
+       position: absolute;
+       top: 0;
+       bottom: 0;
+       left: 0;
+       right: 0;
+       text-overflow: ellipsis;
+}
+.oo-ui-selectFileWidget-fileType {
+       display: none;
+}
+.oo-ui-selectFileWidget-clearButton {
+       position: absolute;
        z-index: 2;
 }
 .oo-ui-selectFileWidget-dropTarget {
+       position: relative;
        cursor: default;
-       height: 5.5em;
-       padding: 0;
+       height: 8.815em;
 }
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel,
-.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton {
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton,
+.oo-ui-selectFileWidget-dropTarget .oo-ui-iconElement-icon {
        display: none;
 }
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail {
-       height: 5.5em;
-       width: 5.5em;
+       width: 7.815em;
        position: absolute;
-       background-size: cover;
+       top: 0.5em;
+       bottom: 0.5em;
+       left: 0.5em;
        background-position: center center;
+       background-repeat: no-repeat;
+       background-size: contain;
 }
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail.oo-ui-pendingElement-pending {
+       background-repeat: repeat;
        background-size: auto;
 }
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail > .oo-ui-selectFileWidget-noThumbnail-icon {
        opacity: 0.4;
-       background-color: #cccccc;
-       height: 5.5em;
-       width: 5.5em;
+       height: 7.815em;
+       width: 7.815em;
 }
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
-       border: 0;
-       background: none;
        display: block;
        height: 100%;
        width: auto;
-       margin-left: 5.5em;
+       margin-left: 8.815em;
+       border: 0;
 }
-.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-label {
+       display: block;
        position: relative;
+       top: inherit;
 }
-.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-fileName {
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-fileName {
        display: block;
+       padding-top: 0.5em;
        padding-right: 2.375em;
-       overflow: hidden;
-       text-overflow: ellipsis;
 }
-.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-fileType {
-       display: block;
-       float: none;
-}
-.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
-       position: absolute;
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-clearButton {
        right: 0.5em;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail,
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
+.oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail,
+.oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
        display: none;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton {
+.oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton {
        display: block;
-       margin: 0.7em;
+       margin: 2.2em 1em 1em;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget {
+.oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget {
        text-align: center;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
+.oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info,
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
        margin: 0;
 }
 .oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-clearButton,
 .oo-ui-selectFileWidget-empty.oo-ui-widget-enabled .oo-ui-selectFileWidget-dropLabel {
        display: block;
 }
-.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget {
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled.oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget {
        -webkit-touch-callout: none;
        -webkit-user-select: none;
           -moz-user-select: none;
            -ms-user-select: none;
                user-select: none;
 }
-.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget,
-.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget .oo-ui-buttonElement-button,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget .oo-ui-buttonElement-button {
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled.oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled.oo-ui-selectFileWidget-dropTarget .oo-ui-buttonElement-button,
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget .oo-ui-buttonElement-button {
        cursor: no-drop;
 }
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget {
+       height: auto;
+}
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-label {
+       padding: 1em;
+}
 .oo-ui-selectFileWidget:last-child {
        margin-right: 0;
 }
        border-radius: 0.25em 0 0 0.25em;
        border-width: 1px 0 1px 1px;
 }
-.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
-       top: 0;
-       right: 0;
-       height: 2.3em;
-       margin-right: 0.775em;
-}
 .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon {
        top: 0;
        left: 0;
        height: 2.3em;
        margin-left: 0.3em;
 }
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
+.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
+       top: 0;
+       right: 0;
+       height: 2.3em;
+       margin-right: 0.775em;
+}
+.oo-ui-selectFileWidget-label {
+       -webkit-box-sizing: border-box;
+          -moz-box-sizing: border-box;
+               box-sizing: border-box;
+       left: 0.5em;
+       right: 2.175em;
        line-height: 2.3em;
        margin: 0;
        overflow: hidden;
        white-space: nowrap;
-       -webkit-box-sizing: border-box;
-          -moz-box-sizing: border-box;
-               box-sizing: border-box;
        text-overflow: ellipsis;
-       left: 0.5em;
-       right: 0.5em;
 }
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileType {
+.oo-ui-selectFileWidget-fileType {
        color: #888888;
+       display: block;
+       margin-top: 0.25em;
 }
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
+.oo-ui-selectFileWidget-clearButton {
        top: 0;
+       right: 0;
        width: 1.875em;
        margin-right: 0;
 }
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+.oo-ui-selectFileWidget-clearButton .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
        height: 2.3em;
 }
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
 .oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-label {
        color: #cccccc;
 }
-.oo-ui-selectFileWidget.oo-ui-iconElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+.oo-ui-selectFileWidget.oo-ui-iconElement .oo-ui-selectFileWidget-label {
        left: 2.475em;
 }
-.oo-ui-selectFileWidget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
-       right: 2.175em;
-}
-.oo-ui-selectFileWidget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-clearButton {
-       right: 0;
-}
-.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-label {
        right: 4.2625em;
 }
-.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-clearButton {
+.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-clearButton {
        right: 2.0875em;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-label,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-label {
        right: 0.5em;
 }
-.oo-ui-selectFileWidget-empty.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label,
-.oo-ui-selectFileWidget-notsupported.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+.oo-ui-selectFileWidget-empty.oo-ui-indicatorElement .oo-ui-selectFileWidget-label,
+.oo-ui-selectFileWidget-notsupported.oo-ui-indicatorElement .oo-ui-selectFileWidget-label,
+.oo-ui-selectFileWidget-empty.oo-ui-indicatorElement .oo-ui-selectFileWidget-label,
+.oo-ui-selectFileWidget-notsupported.oo-ui-indicatorElement .oo-ui-selectFileWidget-label {
        right: 2em;
 }
-.oo-ui-selectFileWidget-supported.oo-ui-widget-enabled.oo-ui-selectFileWidget-canDrop .oo-ui-selectFileWidget-dropTarget {
+.oo-ui-selectFileWidget-supported.oo-ui-widget-enabled.oo-ui-selectFileWidget-canDrop.oo-ui-selectFileWidget-dropTarget {
        background-color: #e1f3ff;
 }
-.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
 .oo-ui-selectFileWidget-dropTarget {
        background-color: #ffffff;
        border: 1px solid #aaaaaa;
-       margin-bottom: 0.5em;
        vertical-align: middle;
        border-radius: 0.25em;
 }
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button {
        border-radius: 0.25em;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget {
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-label {
+       line-height: 1.4;
+       overflow: inherit;
+       white-space: normal;
+}
+.oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget {
        border-style: dashed;
 }
+.oo-ui-selectFileWidget.oo-ui-widget-disabled.oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled.oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget {
+       background-color: #f3f3f3;
+       color: #cccccc;
+       border-color: #dddddd;
+       text-shadow: 0 1px 1px #ffffff;
+}
+.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info,
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-info,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info {
+       background-color: #f3f3f3;
+       color: #cccccc;
+       border-color: #dddddd;
+       text-shadow: 0 1px 1px #ffffff;
+}
+.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
+.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator,
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
 .oo-ui-outlineOptionWidget {
        position: relative;
        cursor: pointer;
 .oo-ui-capsuleMultiselectWidget-group {
        display: inline;
 }
-.oo-ui-capsuleMultiselectWidget > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
 .oo-ui-capsuleMultiselectWidget-handle {
        background-color: #ffffff;
        cursor: text;
 .oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
        width: 100%;
 }
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget [type="number"]::-webkit-outer-spin-button,
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget [type="number"]::-webkit-inner-spin-button {
-       -webkit-appearance: none;
-       margin: 0;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget [type="number"] {
-       -moz-appearance: textfield;
-}
 .oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
        white-space: nowrap;
 }
index 048e732..9632bac 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:11Z
+ * Date: 2016-08-16T21:13:53Z
  */
 .oo-ui-draggableElement-handle,
 .oo-ui-draggableElement-handle.oo-ui-widget {
 .oo-ui-draggableGroupElement-horizontal .oo-ui-draggableElement {
        display: inline-block;
 }
-.oo-ui-lookupElement > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
 .oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous > .oo-ui-panelLayout-scrollable {
        overflow-y: hidden;
 }
@@ -95,6 +91,8 @@
        left: 0;
        right: 0;
        bottom: 0;
+       /* stylelint-disable declaration-no-important */
+       /* stylelint-enable declaration-no-important */
 }
 .oo-ui-menuLayout-menu,
 .oo-ui-menuLayout-content {
 .oo-ui-buttonSelectWidget:focus {
        outline: 0;
 }
-.oo-ui-buttonSelectWidget:focus .oo-ui-buttonOptionWidget.oo-ui-optionWidget-selected .oo-ui-buttonElement-button {
-       border-color: #347bff;
-       box-shadow: inset 0 0 0 1px #347bff;
-       z-index: 2;
-}
 .oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
        border-radius: 0;
        margin-left: -1px;
        border-bottom-right-radius: 2px;
        border-top-right-radius: 2px;
 }
+.oo-ui-buttonSelectWidget.oo-ui-widget-enabled:focus .oo-ui-buttonOptionWidget.oo-ui-optionWidget-selected .oo-ui-buttonElement-button {
+       border-color: #347bff;
+       box-shadow: inset 0 0 0 1px #347bff;
+}
 .oo-ui-buttonOptionWidget {
        display: inline-block;
        padding: 0;
 }
-.oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
-       position: relative;
-}
 .oo-ui-buttonOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon,
 .oo-ui-buttonOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator {
        position: static;
           -moz-transform: translateZ(0);
            -ms-transform: translateZ(0);
                transform: translateZ(0);
-       height: 2em;
        width: 3.5em;
-       border: 1px solid #777777;
+       min-height: 26px;
+       height: 2em;
+       border: 1px solid #767676;
        border-radius: 1em;
        background-color: #ffffff;
        margin-right: 0.5em;
                transition: border-color 100ms;
 }
 .oo-ui-toggleSwitchWidget-grip {
-       top: 0.35em;
+       top: 0.3125em;
        min-width: 16px;
-       width: 1.2em;
+       width: 1.25em;
        min-height: 16px;
-       height: 1.2em;
-       border-radius: 1.2em;
-       background-color: #555555;
+       height: 1.25em;
+       border-radius: 1.25em;
        -webkit-transition: left 100ms, margin-left 100ms;
           -moz-transition: left 100ms, margin-left 100ms;
                transition: left 100ms, margin-left 100ms;
        left: 1.9em;
        margin-left: -2px;
 }
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on {
-       background-color: #347bff;
-       border-color: #347bff;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
-       background-color: #ffffff;
-       box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled .oo-ui-toggleSwitchWidget-grip {
+       border: 1px solid #767676;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover {
        border-color: #2962cc;
 }
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover.oo-ui-toggleWidget-on {
-       background-color: #2962cc;
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover .oo-ui-toggleSwitchWidget-grip {
        border-color: #2962cc;
 }
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active,
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active:hover {
+       background-color: #767676;
+       border-color: #347bff;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active .oo-ui-toggleSwitchWidget-grip,
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active:hover .oo-ui-toggleSwitchWidget-grip {
+       background-color: #ffffff;
+       border-color: #ffffff;
+       box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
+}
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus {
        border-color: #347bff;
        box-shadow: inset 0 0 0 1px #347bff;
        outline: 0;
 }
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus.oo-ui-toggleWidget-on {
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus .oo-ui-toggleSwitchWidget-grip {
        border-color: #347bff;
 }
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus.oo-ui-toggleWidget-on:before {
-       border-color: #ffffff;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active,
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active:hover {
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on {
        background-color: #347bff;
        border-color: #347bff;
 }
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active .oo-ui-toggleSwitchWidget-grip,
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active:hover .oo-ui-toggleSwitchWidget-grip {
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
        background-color: #ffffff;
+       border-color: #ffffff;
        box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
 }
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on:hover {
+       background-color: #2962cc;
+       border-color: #2962cc;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on:active,
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on:active:hover {
+       background-color: #1f4999;
+       border-color: #1f4999;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on:focus {
+       border-color: #347bff;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on:focus:before {
+       border-color: #ffffff;
+}
 .oo-ui-toggleSwitchWidget.oo-ui-widget-disabled {
        background-color: #dddddd;
        border-color: #dddddd;
 .oo-ui-toggleSwitchWidget.oo-ui-widget-disabled .oo-ui-toggleSwitchWidget-grip {
        background-color: #ffffff;
 }
-.oo-ui-progressBarWidget {
-       max-width: 50em;
-       background-color: #ffffff;
-       border: 1px solid #cccccc;
-       border-radius: 2px;
-       overflow: hidden;
-}
-.oo-ui-progressBarWidget-bar {
-       background-color: #dddddd;
-       height: 1em;
-       -webkit-transition: width 200ms, margin-left 200ms;
-          -moz-transition: width 200ms, margin-left 200ms;
-               transition: width 200ms, margin-left 200ms;
-}
-.oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
-       -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-          -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-               animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-       width: 40%;
-       margin-left: -10%;
-       border-left-width: 1px;
-}
-.oo-ui-progressBarWidget.oo-ui-widget-disabled {
-       opacity: 0.6;
-}
-@-webkit-keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
-@-moz-keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
-@keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
 .oo-ui-selectFileWidget {
        display: inline-block;
        vertical-align: middle;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
-       position: absolute;
-       top: 0;
-       bottom: 0;
-       left: 0;
-       right: 0;
-       text-overflow: ellipsis;
-}
-.oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-fileType {
-       float: right;
-}
 .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator,
-.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
+.oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon {
        position: absolute;
 }
 .oo-ui-widget-disabled .oo-ui-selectFileWidget-info {
            -ms-user-select: none;
                user-select: none;
 }
-.oo-ui-selectFileWidget .oo-ui-selectFileWidget-clearButton {
+.oo-ui-selectFileWidget-label {
+       position: absolute;
+       top: 0;
+       bottom: 0;
+       left: 0;
+       right: 0;
+       text-overflow: ellipsis;
+}
+.oo-ui-selectFileWidget-fileType {
+       display: none;
+}
+.oo-ui-selectFileWidget-clearButton {
+       position: absolute;
        z-index: 2;
 }
 .oo-ui-selectFileWidget-dropTarget {
+       position: relative;
        cursor: default;
-       height: 5.5em;
-       padding: 0;
+       height: 8.815em;
 }
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel,
-.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton {
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton,
+.oo-ui-selectFileWidget-dropTarget .oo-ui-iconElement-icon {
        display: none;
 }
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail {
-       height: 5.5em;
-       width: 5.5em;
+       width: 7.815em;
        position: absolute;
-       background-size: cover;
+       top: 0.5em;
+       bottom: 0.5em;
+       left: 0.5em;
        background-position: center center;
+       background-repeat: no-repeat;
+       background-size: contain;
 }
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail.oo-ui-pendingElement-pending {
+       background-repeat: repeat;
        background-size: auto;
 }
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail > .oo-ui-selectFileWidget-noThumbnail-icon {
        opacity: 0.4;
-       background-color: #cccccc;
-       height: 5.5em;
-       width: 5.5em;
+       height: 7.815em;
+       width: 7.815em;
 }
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
-       border: 0;
-       background: none;
        display: block;
        height: 100%;
        width: auto;
-       margin-left: 5.5em;
+       margin-left: 8.815em;
+       border: 0;
 }
-.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-label {
+       display: block;
        position: relative;
+       top: inherit;
 }
-.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-fileName {
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-fileName {
        display: block;
+       padding-top: 0.5em;
        padding-right: 2.375em;
-       overflow: hidden;
-       text-overflow: ellipsis;
 }
-.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-fileType {
-       display: block;
-       float: none;
-}
-.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
-       position: absolute;
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-clearButton {
        right: 0.5em;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail,
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
+.oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail,
+.oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
        display: none;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton {
+.oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton {
        display: block;
-       margin: 0.7em;
+       margin: 2.2em 1em 1em;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget {
+.oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget {
        text-align: center;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
+.oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info,
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info {
        margin: 0;
 }
 .oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-clearButton,
 .oo-ui-selectFileWidget-empty.oo-ui-widget-enabled .oo-ui-selectFileWidget-dropLabel {
        display: block;
 }
-.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget {
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled.oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget {
        -webkit-touch-callout: none;
        -webkit-user-select: none;
           -moz-user-select: none;
            -ms-user-select: none;
                user-select: none;
 }
-.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget,
-.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget .oo-ui-buttonElement-button,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget .oo-ui-buttonElement-button {
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled.oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled.oo-ui-selectFileWidget-dropTarget .oo-ui-buttonElement-button,
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget .oo-ui-buttonElement-button {
        cursor: no-drop;
 }
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget {
+       height: auto;
+}
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-label {
+       padding: 1em;
+}
 .oo-ui-selectFileWidget:last-child {
        margin-right: 0;
 }
        height: 2.3em;
        margin-left: 0.5em;
 }
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label {
+.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
+       top: 0;
+       right: 0;
+       height: 2.3em;
+       margin-right: 0.775em;
+}
+.oo-ui-selectFileWidget-label {
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
        display: block;
-       line-height: 2.3em;
+       right: 2.375em;
+       line-height: 2.3;
        margin: 0;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
-       left: 0;
-       right: 0;
        padding-left: 0.5em;
 }
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-label > .oo-ui-selectFileWidget-fileType {
+.oo-ui-selectFileWidget-fileType {
        color: #888888;
+       display: block;
+       margin-top: 0.25em;
 }
-.oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
+.oo-ui-selectFileWidget-clearButton {
        top: 0;
        right: 0;
-       height: 2.3em;
-       margin-right: 0.775em;
-}
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton {
-       top: 0;
        min-width: 24px;
        width: 1.875em;
        margin-right: 0;
 }
-.oo-ui-selectFileWidget-info > .oo-ui-selectFileWidget-clearButton .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
+.oo-ui-selectFileWidget-clearButton .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
        height: 2.3em;
 }
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info,
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info {
-       background-color: #f3f3f3;
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-       border-color: #dddddd;
-       cursor: default;
-}
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator,
-.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
-       opacity: 0.2;
-}
 .oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-label {
        color: #cccccc;
 }
-.oo-ui-selectFileWidget.oo-ui-iconElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+.oo-ui-selectFileWidget.oo-ui-iconElement .oo-ui-selectFileWidget-label {
        left: 2.875em;
 }
-.oo-ui-selectFileWidget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
-       right: 2.375em;
-}
-.oo-ui-selectFileWidget .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-clearButton {
-       right: 0;
-}
-.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-label {
        right: 4.4625em;
        padding-left: 0;
 }
-.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-clearButton {
+.oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-clearButton {
        right: 2.0875em;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
-       right: 0.5em;
-}
-.oo-ui-selectFileWidget-empty.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label,
-.oo-ui-selectFileWidget-notsupported.oo-ui-indicatorElement .oo-ui-selectFileWidget-info .oo-ui-selectFileWidget-label {
+.oo-ui-selectFileWidget-empty.oo-ui-indicatorElement .oo-ui-selectFileWidget-label,
+.oo-ui-selectFileWidget-notsupported.oo-ui-indicatorElement .oo-ui-selectFileWidget-label {
        right: 2em;
-       padding-left: 0;
 }
-.oo-ui-selectFileWidget-supported.oo-ui-widget-enabled.oo-ui-selectFileWidget-canDrop .oo-ui-selectFileWidget-dropTarget {
+.oo-ui-selectFileWidget-supported.oo-ui-widget-enabled.oo-ui-selectFileWidget-canDrop.oo-ui-selectFileWidget-dropTarget {
        background-color: #ebf2ff;
 }
-.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget {
-       border-color: #dddddd;
-       background-color: #f3f3f3;
-}
-.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info,
-.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel,
-.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel {
-       color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
-}
 .oo-ui-selectFileWidget-dropTarget {
        background-color: #ffffff;
        border: 1px solid #cccccc;
-       margin-bottom: 0.5em;
        vertical-align: middle;
        overflow: hidden;
        border-radius: 2px;
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-selectButton > .oo-ui-buttonElement-button {
        border-radius: 2px;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget {
+.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-label {
+       line-height: 1.4;
+       overflow: inherit;
+       white-space: normal;
+}
+.oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget {
        background-color: #eeeeee;
        border-style: dashed;
 }
+.oo-ui-selectFileWidget.oo-ui-widget-disabled.oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled.oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget {
+       background-color: #f3f3f3;
+       border-color: #dddddd;
+}
+.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info,
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-info,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info {
+       background-color: #f3f3f3;
+       color: #cccccc;
+       border-color: #dddddd;
+       text-shadow: 0 1px 1px #ffffff;
+}
+.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon,
+.oo-ui-selectFileWidget.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator,
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
+       opacity: 0.2;
+}
 .oo-ui-widget-disabled .oo-ui-selectFileWidget-dropLabel {
        display: none;
 }
 .oo-ui-capsuleMultiselectWidget-group {
        display: inline;
 }
-.oo-ui-capsuleMultiselectWidget > .oo-ui-menuSelectWidget {
-       z-index: 1;
-       width: 100%;
-}
 .oo-ui-capsuleMultiselectWidget-handle {
-       background-color: #ffffff;
-       cursor: text;
        min-height: 2.4em;
        margin-right: 0.5em;
        padding: 0.15em 0.25em;
 }
 .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-capsuleMultiselectWidget-content > input {
        border: 0;
-       line-height: 1.675em;
+       line-height: 1.675;
        margin: 0 0 0 0.2em;
        padding: 0;
        font-size: inherit;
        top: 0;
        margin: 0.3em;
 }
-.oo-ui-capsuleMultiselectWidget:hover .oo-ui-capsuleMultiselectWidget-handle {
+.oo-ui-capsuleMultiselectWidget.oo-ui-widget-enabled .oo-ui-capsuleMultiselectWidget-handle {
+       background-color: #ffffff;
+       cursor: text;
+       -webkit-transition: border-color 100ms;
+          -moz-transition: border-color 100ms;
+               transition: border-color 100ms;
+}
+.oo-ui-capsuleMultiselectWidget.oo-ui-widget-enabled:hover .oo-ui-capsuleMultiselectWidget-handle {
        border-color: #aaaaaa;
 }
 .oo-ui-capsuleMultiselectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiselectWidget-handle {
        text-shadow: 0 1px 1px #ffffff;
        border-color: #dddddd;
        background-color: #f3f3f3;
-       cursor: default;
 }
 .oo-ui-capsuleMultiselectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-iconElement-icon,
 .oo-ui-capsuleMultiselectWidget.oo-ui-widget-disabled .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-indicatorElement-indicator {
                box-sizing: border-box;
        vertical-align: middle;
        height: 1.7em;
-       line-height: 1.7em;
+       line-height: 1.7;
        background-color: #eeeeee;
        color: #555555;
        margin: 0.1em;
 .oo-ui-numberInputWidget-field > .oo-ui-textInputWidget {
        width: 100%;
 }
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget [type="number"]::-webkit-outer-spin-button,
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget [type="number"]::-webkit-inner-spin-button {
-       -webkit-appearance: none;
-       margin: 0;
-}
-.oo-ui-numberInputWidget-field > .oo-ui-textInputWidget [type="number"] {
-       -moz-appearance: textfield;
-}
 .oo-ui-numberInputWidget-field > .oo-ui-buttonWidget {
        white-space: nowrap;
 }
index c4486d3..7a38633 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:08Z
+ * Date: 2016-08-16T21:13:48Z
  */
 ( function ( OO ) {
 
@@ -1426,10 +1426,12 @@ OO.ui.StackLayout.prototype.updateHiddenState = function ( items, selectedItem )
                for ( i = 0, len = items.length; i < len; i++ ) {
                        if ( !selectedItem || selectedItem !== items[ i ] ) {
                                items[ i ].$element.addClass( 'oo-ui-element-hidden' );
+                               items[ i ].$element.attr( 'aria-hidden', 'true' );
                        }
                }
                if ( selectedItem ) {
                        selectedItem.$element.removeClass( 'oo-ui-element-hidden' );
+                       selectedItem.$element.removeAttr( 'aria-hidden' );
                }
        }
 };
@@ -1553,6 +1555,7 @@ OO.ui.MenuLayout.prototype.toggleMenu = function ( showMenu ) {
                this.$element
                        .toggleClass( 'oo-ui-menuLayout-showMenu', this.showMenu )
                        .toggleClass( 'oo-ui-menuLayout-hideMenu', !this.showMenu );
+               this.$menu.attr( 'aria-hidden', this.showMenu ? 'false' : 'true' );
        }
 
        return this;
@@ -3692,13 +3695,17 @@ OO.mixinClass( OO.ui.CapsuleMultiselectWidget, OO.ui.mixin.IconElement );
 
 /**
  * Construct a OO.ui.CapsuleItemWidget (or a subclass thereof) from given label and data.
+ * May return `null` if the given label and data are not valid.
  *
  * @protected
  * @param {Mixed} data Custom data of any type.
  * @param {string} label The label text.
- * @return {OO.ui.CapsuleItemWidget}
+ * @return {OO.ui.CapsuleItemWidget|null}
  */
 OO.ui.CapsuleMultiselectWidget.prototype.createItemWidget = function ( data, label ) {
+       if ( label === '' ) {
+               return null;
+       }
        return new OO.ui.CapsuleItemWidget( { data: data, label: label } );
 };
 
@@ -3748,7 +3755,9 @@ OO.ui.CapsuleMultiselectWidget.prototype.setItemsFromData = function ( datas ) {
                if ( !item ) {
                        item = widget.createItemWidget( data, label );
                }
-               widget.addItems( [ item ], i );
+               if ( item ) {
+                       widget.addItems( [ item ], i );
+               }
        } );
 
        if ( items.length ) {
@@ -3776,9 +3785,12 @@ OO.ui.CapsuleMultiselectWidget.prototype.addItemsFromData = function ( datas ) {
                if ( !widget.getItemFromData( data ) ) {
                        item = menu.getItemFromData( data );
                        if ( item ) {
-                               items.push( widget.createItemWidget( data, item.label ) );
+                               item = widget.createItemWidget( data, item.label );
                        } else if ( widget.allowArbitrary ) {
-                               items.push( widget.createItemWidget( data, String( data ) ) );
+                               item = widget.createItemWidget( data, String( data ) );
+                       }
+                       if ( item ) {
+                               items.push( item );
                        }
                }
        } );
@@ -3797,13 +3809,15 @@ OO.ui.CapsuleMultiselectWidget.prototype.addItemsFromData = function ( datas ) {
  * @return {boolean} Whether the item was added or not
  */
 OO.ui.CapsuleMultiselectWidget.prototype.addItemFromLabel = function ( label ) {
-       var item = this.menu.getItemFromLabel( label, true );
+       var item, items;
+       item = this.menu.getItemFromLabel( label, true );
        if ( item ) {
                this.addItemsFromData( [ item.data ] );
                return true;
-       } else if ( this.allowArbitrary && this.$input.val().trim() !== '' ) {
+       } else if ( this.allowArbitrary ) {
+               items = this.getItems();
                this.addItemsFromData( [ label ] );
-               return true;
+               return !OO.compare( this.getItems(), items );
        }
        return false;
 };
@@ -4351,8 +4365,8 @@ OO.ui.SelectFileWidget = function OoUiSelectFileWidget( config ) {
                this.selectButton.setIcon( 'upload' );
                this.$thumbnail = $( '<div>' ).addClass( 'oo-ui-selectFileWidget-thumbnail' );
                this.setPendingElement( this.$thumbnail );
-               this.$dropTarget = $( '<div>' )
-                       .addClass( 'oo-ui-selectFileWidget-dropTarget' )
+               this.$element
+                       .addClass( 'oo-ui-selectFileWidget-dropTarget oo-ui-selectFileWidget' )
                        .on( {
                                click: this.onDropTargetClick.bind( this )
                        } )
@@ -4364,7 +4378,6 @@ OO.ui.SelectFileWidget = function OoUiSelectFileWidget( config ) {
                                        .addClass( 'oo-ui-selectFileWidget-dropLabel' )
                                        .text( OO.ui.msg( 'ooui-selectfile-dragdrop-placeholder' ) )
                        );
-               this.$element.append( this.$dropTarget );
        } else {
                this.$element
                        .addClass( 'oo-ui-selectFileWidget' )
@@ -4490,12 +4503,12 @@ OO.ui.SelectFileWidget.prototype.updateUI = function () {
                                }.bind( this ) ).always( function () {
                                        this.popPending();
                                }.bind( this ) );
-                               this.$dropTarget.off( 'click' );
+                               this.$element.off( 'click' );
                        }
                } else {
                        if ( this.showDropTarget ) {
-                               this.$dropTarget.off( 'click' );
-                               this.$dropTarget.on( {
+                               this.$element.off( 'click' );
+                               this.$element.on( {
                                        click: this.onDropTargetClick.bind( this )
                                } );
                                this.$thumbnail
@@ -4757,103 +4770,6 @@ OO.ui.SelectFileWidget.prototype.setDisabled = function ( disabled ) {
        return this;
 };
 
-/**
- * Progress bars visually display the status of an operation, such as a download,
- * and can be either determinate or indeterminate:
- *
- * - **determinate** process bars show the percent of an operation that is complete.
- *
- * - **indeterminate** process bars use a visual display of motion to indicate that an operation
- *   is taking place. Because the extent of an indeterminate operation is unknown, the bar does
- *   not use percentages.
- *
- * The value of the `progress` configuration determines whether the bar is determinate or indeterminate.
- *
- *     @example
- *     // Examples of determinate and indeterminate progress bars.
- *     var progressBar1 = new OO.ui.ProgressBarWidget( {
- *         progress: 33
- *     } );
- *     var progressBar2 = new OO.ui.ProgressBarWidget();
- *
- *     // Create a FieldsetLayout to layout progress bars
- *     var fieldset = new OO.ui.FieldsetLayout;
- *     fieldset.addItems( [
- *        new OO.ui.FieldLayout( progressBar1, {label: 'Determinate', align: 'top'}),
- *        new OO.ui.FieldLayout( progressBar2, {label: 'Indeterminate', align: 'top'})
- *     ] );
- *     $( 'body' ).append( fieldset.$element );
- *
- * @class
- * @extends OO.ui.Widget
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {number|boolean} [progress=false] The type of progress bar (determinate or indeterminate).
- *  To create a determinate progress bar, specify a number that reflects the initial percent complete.
- *  By default, the progress bar is indeterminate.
- */
-OO.ui.ProgressBarWidget = function OoUiProgressBarWidget( config ) {
-       // Configuration initialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.ProgressBarWidget.parent.call( this, config );
-
-       // Properties
-       this.$bar = $( '<div>' );
-       this.progress = null;
-
-       // Initialization
-       this.setProgress( config.progress !== undefined ? config.progress : false );
-       this.$bar.addClass( 'oo-ui-progressBarWidget-bar' );
-       this.$element
-               .attr( {
-                       role: 'progressbar',
-                       'aria-valuemin': 0,
-                       'aria-valuemax': 100
-               } )
-               .addClass( 'oo-ui-progressBarWidget' )
-               .append( this.$bar );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.ProgressBarWidget, OO.ui.Widget );
-
-/* Static Properties */
-
-OO.ui.ProgressBarWidget.static.tagName = 'div';
-
-/* Methods */
-
-/**
- * Get the percent of the progress that has been completed. Indeterminate progresses will return `false`.
- *
- * @return {number|boolean} Progress percent
- */
-OO.ui.ProgressBarWidget.prototype.getProgress = function () {
-       return this.progress;
-};
-
-/**
- * Set the percent of the process completed or `false` for an indeterminate process.
- *
- * @param {number|boolean} progress Progress percent or `false` for indeterminate
- */
-OO.ui.ProgressBarWidget.prototype.setProgress = function ( progress ) {
-       this.progress = progress;
-
-       if ( progress !== false ) {
-               this.$bar.css( 'width', this.progress + '%' );
-               this.$element.attr( 'aria-valuenow', this.progress );
-       } else {
-               this.$bar.css( 'width', '' );
-               this.$element.removeAttr( 'aria-valuenow' );
-       }
-       this.$element.toggleClass( 'oo-ui-progressBarWidget-indeterminate', !progress );
-};
-
 /**
  * SearchWidgets combine a {@link OO.ui.TextInputWidget text input field}, where users can type a search query,
  * and a menu of search results, which is displayed beneath the query
@@ -5039,24 +4955,20 @@ OO.ui.NumberInputWidget = function OoUiNumberInputWidget( config ) {
                this.minusButton = new OO.ui.ButtonWidget( $.extend(
                        {
                                disabled: this.isDisabled(),
-                               tabIndex: -1
-                       },
-                       config.minusButton,
-                       {
+                               tabIndex: -1,
                                classes: [ 'oo-ui-numberInputWidget-minusButton' ],
                                label: '−'
-                       }
+                       },
+                       config.minusButton
                ) );
                this.plusButton = new OO.ui.ButtonWidget( $.extend(
                        {
                                disabled: this.isDisabled(),
-                               tabIndex: -1
-                       },
-                       config.plusButton,
-                       {
+                               tabIndex: -1,
                                classes: [ 'oo-ui-numberInputWidget-plusButton' ],
                                label: '+'
-                       }
+                       },
+                       config.plusButton
                ) );
        }
 
index b55dd25..2f6c1a0 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:11Z
+ * Date: 2016-08-16T21:13:53Z
  */
 .oo-ui-actionWidget.oo-ui-pendingElement-pending {
        background-image: /* @embed */ url(themes/apex/images/textures/pending.gif);
        width: 0;
        height: 0;
        overflow: hidden;
+       z-index: 4;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-active {
        width: auto;
index 727e874..465e17b 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:11Z
+ * Date: 2016-08-16T21:13:53Z
  */
 .oo-ui-window {
        background: transparent;
 }
 .oo-ui-messageDialog-title {
        font-size: 1.5em;
-       line-height: 1em;
+       line-height: 1;
        color: #000000;
 }
 .oo-ui-messageDialog-message {
        font-size: 0.9em;
-       line-height: 1.25em;
+       line-height: 1.25;
        color: #555555;
 }
 .oo-ui-messageDialog-message-verbose {
        font-size: 1.1em;
-       line-height: 1.5em;
+       line-height: 1.5;
        text-align: left;
 }
 .oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget {
-       border-right: 1px solid #e5e5e5;
+       border-right: 1px solid #cccccc;
        margin: 0;
 }
 .oo-ui-messageDialog-actions-horizontal .oo-ui-actionWidget:last-child {
        border-right-width: 0;
 }
 .oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget {
-       border-bottom: 1px solid #e5e5e5;
+       border-bottom: 1px solid #cccccc;
        margin: 0;
 }
 .oo-ui-messageDialog-actions-vertical .oo-ui-actionWidget:last-child {
        margin-right: 0;
 }
 .oo-ui-messageDialog-actions .oo-ui-actionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       text-align: center;
        line-height: 3.4;
+       text-align: center;
 }
 .oo-ui-messageDialog-actions .oo-ui-actionWidget:hover {
        background-color: rgba(0, 0, 0, 0.05);
        width: 0;
        height: 0;
        overflow: hidden;
+       z-index: 4;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-active {
        width: auto;
index 8234b6d..510399d 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.5
+ * OOjs UI v0.17.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-06-29T13:27:08Z
+ * Date: 2016-08-16T21:13:48Z
  */
 ( function ( OO ) {
 
@@ -2135,10 +2135,18 @@ OO.ui.Window.prototype.initialize = function () {
  * @param {jQuery.Event} event Focus event
  */
 OO.ui.Window.prototype.onFocusTrapFocused = function ( event ) {
-       if ( this.$focusTrapBefore.is( event.target ) ) {
-               OO.ui.findFocusable( this.$content, true ).focus();
+       var backwards = this.$focusTrapBefore.is( event.target ),
+               element = OO.ui.findFocusable( this.$content, backwards );
+       if ( element ) {
+               // There's a focusable element inside the content, at the front or
+               // back depending on which focus trap we hit; select it.
+               element.focus();
        } else {
-               // this.$content is the part of the focus cycle, and is the first focusable element
+               // There's nothing focusable inside the content. As a fallback,
+               // this.$content is focusable, and focusing it will keep our focus
+               // properly trapped. It's not a *meaningful* focus, since it's just
+               // the content-div for the Window, but it's better than letting focus
+               // escape into the page.
                this.$content.focus();
        }
 };
@@ -2512,11 +2520,10 @@ OO.ui.Dialog.prototype.getSetupProcess = function ( data ) {
        return OO.ui.Dialog.parent.prototype.getSetupProcess.call( this, data )
                .next( function () {
                        var config = this.constructor.static,
-                               actions = data.actions !== undefined ? data.actions : config.actions;
+                               actions = data.actions !== undefined ? data.actions : config.actions,
+                               title = data.title !== undefined ? data.title : config.title;
 
-                       this.title.setLabel(
-                               data.title !== undefined ? data.title : this.constructor.static.title
-                       );
+                       this.title.setLabel( title ).setTitle( title );
                        this.actions.add( this.getActionWidgets( actions ) );
 
                        this.$element.on( 'keydown', this.onDialogKeyDownHandler );
diff --git a/resources/lib/oojs-ui/themes/apex/icons-alerts.json b/resources/lib/oojs-ui/themes/apex/icons-alerts.json
new file mode 100644 (file)
index 0000000..892b8db
--- /dev/null
@@ -0,0 +1,34 @@
+{
+       "prefix": "oo-ui-icon",
+       "intro": "@import '../../../../src/styles/common';",
+       "images": {
+               "bell": { "file": "images/icons/bell.svg" },
+               "bellOn": { "file": {
+                       "ltr": "images/icons/bellOn-ltr.svg",
+                       "rtl": "images/icons/bellOn-rtl.svg"
+               } },
+               "eye": { "file": "images/icons/eye.svg" },
+               "eyeClosed": { "file": "images/icons/eyeClosed.svg" },
+               "message": { "file": {
+                       "ltr": "images/icons/message-ltr.svg",
+                       "rtl": "images/icons/message-rtl.svg"
+               } },
+               "signature": { "file": {
+                       "ltr": "images/icons/signature-ltr.svg",
+                       "rtl": "images/icons/signature-rtl.svg"
+               } },
+               "speechBubble": { "file": {
+                       "ltr": "images/icons/speechBubble-ltr.svg",
+                       "rtl": "images/icons/speechBubble-rtl.svg"
+               } },
+               "speechBubbleAdd": { "file": {
+                       "ltr": "images/icons/speechBubbleAdd-ltr.svg",
+                       "rtl": "images/icons/speechBubbleAdd-rtl.svg"
+               } },
+               "speechBubbles": { "file": {
+                       "ltr": "images/icons/speechBubbles-ltr.svg",
+                       "rtl": "images/icons/speechBubbles-rtl.svg"
+               } },
+               "tray": { "file": "images/icons/tray.svg" }
+       }
+}
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/bell.png b/resources/lib/oojs-ui/themes/apex/images/icons/bell.png
new file mode 100644 (file)
index 0000000..91e8397
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/bell.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/bell.svg b/resources/lib/oojs-ui/themes/apex/images/icons/bell.svg
new file mode 100644 (file)
index 0000000..09b65f2
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M17.5 13V8c0-3-2.3-5-5.5-5S6.5 5 6.5 8v5c0 2 0 3-2 3v1h15v-1c-2 0-2-1-2-3zM12 19H9c0 1 1.6 2 3 2s3-1 3-2h-3z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/bellOn-ltr.png b/resources/lib/oojs-ui/themes/apex/images/icons/bellOn-ltr.png
new file mode 100644 (file)
index 0000000..fb13de1
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/bellOn-ltr.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/bellOn-ltr.svg b/resources/lib/oojs-ui/themes/apex/images/icons/bellOn-ltr.svg
new file mode 100644 (file)
index 0000000..855581b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M17.8 13.7L19.5 9c1-2.8-.5-5.5-3.5-6.6-3-1.1-5.9 0-6.9 2.8L7.4 9.9c-.7 1.9-1 2.8-2.9 2.1l-.3 1 14.1 5.1.3-.9c-1.9-.7-1.5-1.6-.8-3.5zM12 18.8l-2.8-1c-.3.9.8 2.4 2.1 2.9s3.2.1 3.5-.9l-2.8-1z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/bellOn-rtl.png b/resources/lib/oojs-ui/themes/apex/images/icons/bellOn-rtl.png
new file mode 100644 (file)
index 0000000..b05fd4c
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/bellOn-rtl.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/bellOn-rtl.svg b/resources/lib/oojs-ui/themes/apex/images/icons/bellOn-rtl.svg
new file mode 100644 (file)
index 0000000..e1f2961
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M6.21 13.7L4.51 9c-1-2.8.5-5.5 3.5-6.6 3-1.1 5.9 0 6.9 2.8l1.7 4.7c.7 1.9 1 2.8 2.9 2.1l.3 1-14.1 5.1-.3-.9c1.9-.7 1.5-1.6.8-3.5zm5.8 5.1l2.8-1c.3.9-.8 2.4-2.1 2.9s-3.2.1-3.5-.9l2.8-1z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/eye.png b/resources/lib/oojs-ui/themes/apex/images/icons/eye.png
new file mode 100644 (file)
index 0000000..62c82c0
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/eye.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/eye.svg b/resources/lib/oojs-ui/themes/apex/images/icons/eye.svg
new file mode 100644 (file)
index 0000000..343e9cf
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M12 8c-5 0-11 6-11 6s6 6 11 6 11-6 11-6-6-6-11-6zm0 10c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z"/>
+    <circle cx="12" cy="14" r="2"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/eyeClosed.png b/resources/lib/oojs-ui/themes/apex/images/icons/eyeClosed.png
new file mode 100644 (file)
index 0000000..1ac37d6
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/eyeClosed.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/eyeClosed.svg b/resources/lib/oojs-ui/themes/apex/images/icons/eyeClosed.svg
new file mode 100644 (file)
index 0000000..d17f2c8
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M19.4 12.7c.7-.8 1.2-1.7 1.4-2.7h-1.6c-.9 2.5-3.9 4.4-7.7 4.6h-.1c-3.7-.2-6.8-2.1-7.7-4.6H2.2c.2 1 .8 1.9 1.4 2.7l-2 2 .7.7 2-2c.8.6 1.7 1.2 2.7 1.7l-1 2.8.9.3 1-2.8c1 .3 2 .6 3.1.6v3h1v-3c1.1-.1 2.2-.3 3.1-.6l1 2.8.9-.3-1-2.8c1-.4 1.9-1 2.6-1.7l2 2 .7-.7-1.9-2z"/>
+</svg>
index e881d7c..a9943cc 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/flag-rtl.png and b/resources/lib/oojs-ui/themes/apex/images/icons/flag-rtl.png differ
index 0f7e7ce..397a3da 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <path d="M11 6.5V5c1.4-1.5 5.2-1.2 6 0V4h1v15h-1v-7c-.8-.8-3.4-.9-5-.5V13c-1.2 1.5-4.3 1.2-5 0V6c.7.7 2.7.9 4 .5z"/>
-</svg>
+       <path d="M10.3 7.5V6c1.4-1.5 5.2-1.2 6 0V5h1v15h-1v-7c-.8-.8-3.4-.9-5-.5V14c-1.2 1.5-4.3 1.2-5 0V7c.7.7 2.7.9 4 .5z"/>
+</svg>
\ No newline at end of file
index 033adde..780cb87 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/flagUndo-ltr.png and b/resources/lib/oojs-ui/themes/apex/images/icons/flagUndo-ltr.png differ
index 516a3ea..4bbd61c 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <path d="M18 2L2.03 17.97l.97 1 4-4V19h1v-5.03l2.688-2.69c.81-.02 1.645.053 2.312.22V13c1.2 1.5 4.3 1.2 5 0V6c-.493.493-1.635.725-2.72.688L19 2.968zM7 4v7.03l6.438-6.436C11.778 3.61 8.71 3.934 8 5V4z"/>
-</svg>
+       <path d="M19.9 19.6l-16-16-1.1 1.1L6 7.9V20h1v-7c.6-.6 2-.8 3.4-.7l8.4 8.4 1.1-1.1zM17 14V7c-.7.7-2.7.9-4 .5V6c-1.2-1.3-3.9-1.3-5.4-.5l8.9 9c.3-.2.4-.3.5-.5z"/>
+</svg>
\ No newline at end of file
index fca73fd..fa3696a 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/flagUndo-rtl.png and b/resources/lib/oojs-ui/themes/apex/images/icons/flagUndo-rtl.png differ
index 8ed159e..5d94425 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <path d="M7 2l-1 .97 3.72 3.717C8.634 6.727 7.492 6.494 7 6v7c.7 1.2 3.8 1.5 5 0v-1.5c.667-.167 1.5-.24 2.313-.22L17 13.97V19h1v-4.03l4 4 .97-1zm7.22 2c-.952-.03-1.932.163-2.658.594L18 11.03V4h-1v1c-.4-.6-1.556-.963-2.78-1z"/>
-</svg>
+       <path d="M3.5 19.6l16-16 1.1 1.1-3.2 3.2V20h-1v-7c-.6-.6-2-.8-3.4-.7l-8.4 8.4-1.1-1.1zM6.3 14V7c.7.7 2.7.9 4 .5V6c1.2-1.3 3.9-1.3 5.4-.5l-8.9 9c-.3-.2-.4-.3-.5-.5z"/>
+</svg>
\ No newline at end of file
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/message-ltr.png b/resources/lib/oojs-ui/themes/apex/images/icons/message-ltr.png
new file mode 100644 (file)
index 0000000..2171c1f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/message-ltr.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/message-ltr.svg b/resources/lib/oojs-ui/themes/apex/images/icons/message-ltr.svg
new file mode 100644 (file)
index 0000000..842d312
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M21 9c0-1.7-1.3-3-3-3H3v3l9 4 9-4zM3 11v6c0 1.7 1.3 3 3 3h15v-9l-9 4-9-4z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/message-rtl.png b/resources/lib/oojs-ui/themes/apex/images/icons/message-rtl.png
new file mode 100644 (file)
index 0000000..fba7920
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/message-rtl.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/message-rtl.svg b/resources/lib/oojs-ui/themes/apex/images/icons/message-rtl.svg
new file mode 100644 (file)
index 0000000..0a22c75
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M3 9c0-1.7 1.3-3 3-3h15v3l-9 4-9-4zm18 2v6c0 1.7-1.3 3-3 3H3v-9l9 4 9-4z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/signature-ltr.png b/resources/lib/oojs-ui/themes/apex/images/icons/signature-ltr.png
new file mode 100644 (file)
index 0000000..f68a7bf
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/signature-ltr.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/signature-ltr.svg b/resources/lib/oojs-ui/themes/apex/images/icons/signature-ltr.svg
new file mode 100644 (file)
index 0000000..03b34cf
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M0 20h24v1H0v-1zm6-8l-1-1-2 2-2-2-1 1 2 2-2 2 1 1 2-2 2 2 1-1-2-2zm15.6 3.7c-.9-.5-1.9-.5-2.7 0-1.5.9-3.1.4-3.1.4-.4-.2-.8-.4-1.1-.6 2.2-.6 4.4-1.8 6-3.9 1.1-1.2 2.5-3.9.4-6-.7-.7-1.6-1.1-2.7-1-1.4.1-2.8.9-3.9 2.1-.9 1.1-3.1 4.5-2.3 7.5 0 .1 0 .2.1.3-2.3.3-4.2.2-4.4.1v1.5c.7.1 2.7.2 5.1-.2.5.7 1.3 1.2 2.3 1.6.1 0 2.4.8 4.5-.6.5-.3.9-.1 1.1 0 .4.2.7.6.7 1H23c0-.8-.6-1.7-1.4-2.2zm-8-1.7c-.5-2.2 1.1-5.1 2-6.2.8-.9 1.8-1.5 2.8-1.6h.1c.6 0 1.1.2 1.5.6 1.6 1.6-.4 3.9-.5 4-1.5 2-3.7 3-5.8 3.5l-.1-.3z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/signature-rtl.png b/resources/lib/oojs-ui/themes/apex/images/icons/signature-rtl.png
new file mode 100644 (file)
index 0000000..9166e8d
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/signature-rtl.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/signature-rtl.svg b/resources/lib/oojs-ui/themes/apex/images/icons/signature-rtl.svg
new file mode 100644 (file)
index 0000000..326281a
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M24 20H0v1h24v-1zm-6-8l1-1 2 2 2-2 1 1-2 2 2 2-1 1-2-2-2 2-1-1 2-2zM2.4 15.7c.9-.5 1.9-.5 2.7 0 1.5.9 3.1.4 3.1.4.4-.2.8-.4 1.1-.6-2.2-.6-4.4-1.8-6-3.9-1.1-1.2-2.5-3.9-.4-6 .7-.7 1.6-1.1 2.7-1 1.4.1 2.8.9 3.9 2.1.9 1.1 3.1 4.5 2.3 7.5 0 .1 0 .2-.1.3 2.3.3 4.2.2 4.4.1v1.5c-.7.1-2.7.2-5.1-.2-.5.7-1.3 1.2-2.3 1.6-.1 0-2.4.8-4.5-.6-.5-.3-.9-.1-1.1 0-.4.2-.7.6-.7 1H1c0-.8.6-1.7 1.4-2.2zm8-1.7c.5-2.2-1.1-5.1-2-6.2-.8-.9-1.8-1.5-2.8-1.6h-.1c-.6 0-1.1.2-1.5.6-1.6 1.6.4 3.9.5 4 1.5 2 3.7 3 5.8 3.5l.1-.3z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-ltr.png b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-ltr.png
new file mode 100644 (file)
index 0000000..2eedf9f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-ltr.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-ltr.svg b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-ltr.svg
new file mode 100644 (file)
index 0000000..83d47c1
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M19 20H2l3-3V6h17v11c0 1.7-1.3 3-3 3z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-rtl.png b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-rtl.png
new file mode 100644 (file)
index 0000000..0d14443
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-rtl.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-rtl.svg b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubble-rtl.svg
new file mode 100644 (file)
index 0000000..e7dd668
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M5 20h17l-3-3V6H2v11c0 1.7 1.3 3 3 3z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-ltr.png b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-ltr.png
new file mode 100644 (file)
index 0000000..aafac18
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-ltr.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-ltr.svg b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-ltr.svg
new file mode 100644 (file)
index 0000000..c7134c3
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M5 6v11l-3 3h17c1.7 0 3-1.3 3-3V6H5zm8 3h1v3h3v1h-3v3h-1v-3h-3v-1h3V9z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-rtl.png b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-rtl.png
new file mode 100644 (file)
index 0000000..9254844
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-rtl.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-rtl.svg b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbleAdd-rtl.svg
new file mode 100644 (file)
index 0000000..08462e0
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M2 6v11c0 1.7 1.3 3 3 3h17l-3-3V6H2zm8 3h1v3h3v1h-3v3h-1v-3H7v-1h3V9z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-ltr.png b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-ltr.png
new file mode 100644 (file)
index 0000000..f2e0564
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-ltr.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-ltr.svg b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-ltr.svg
new file mode 100644 (file)
index 0000000..d683218
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M20 9v9l2 2H8V9h12zM3 4h12v4H7v7H1l2-2V4z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-rtl.png b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-rtl.png
new file mode 100644 (file)
index 0000000..9c67f3f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-rtl.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-rtl.svg b/resources/lib/oojs-ui/themes/apex/images/icons/speechBubbles-rtl.svg
new file mode 100644 (file)
index 0000000..db548a4
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M3 9v9l-2 2h14V9H3zm17-5H8v4h8v7h6l-2-2V4z"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/tray.png b/resources/lib/oojs-ui/themes/apex/images/icons/tray.png
new file mode 100644 (file)
index 0000000..e5c5e8f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/tray.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/tray.svg b/resources/lib/oojs-ui/themes/apex/images/icons/tray.svg
new file mode 100644 (file)
index 0000000..d7ab69b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M3 13.35l1.8-7.2c.2-.996.81-1.8 1.8-1.8h10.8c.99 0 1.6.867 1.8 1.8l1.8 7.2v4.5c0 .99-.81 1.8-1.8 1.8H4.8c-.99 0-1.8-.81-1.8-1.8v-4.5zm6.96 1.8h4.08c-.49.557-1.212.9-2.04.9a2.68 2.68 0 0 1-2.04-.9h4.08c.414-.472.66-1.098.66-1.8h4.14l-1.44-7.2H6.6l-1.44 7.2H9.3c0 .702.246 1.328.66 1.8z" id="tray"/>
+</svg>
index 79f644e..6894d6e 100644 (file)
@@ -35,6 +35,7 @@
                "speechBubbles": { "file": {
                        "ltr": "images/icons/speechBubbles-ltr.svg",
                        "rtl": "images/icons/speechBubbles-rtl.svg"
-               } }
+               } },
+               "tray": { "file": "images/icons/tray.svg" }
        }
 }
index 2f0c960..05911d3 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bell-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bell-invert.png differ
index 7ed3635..645c9cc 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
-    <path d="M17.5 14V9c0-3-2.3-5-5.5-5S6.5 6 6.5 9v5c0 2 0 3-2 3v1h15v-1c-2 0-2-1-2-3zM12 20H9c0 1 1.6 2 3 2s3-1 3-2h-3z"/>
+    <path d="M17.5 13V8c0-3-2.3-5-5.5-5S6.5 5 6.5 8v5c0 2 0 3-2 3v1h15v-1c-2 0-2-1-2-3zM12 19H9c0 1 1.6 2 3 2s3-1 3-2h-3z"/>
 </svg>
index b86d5f7..91e8397 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bell.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bell.png differ
index 787ed14..09b65f2 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <path d="M17.5 14V9c0-3-2.3-5-5.5-5S6.5 6 6.5 9v5c0 2 0 3-2 3v1h15v-1c-2 0-2-1-2-3zM12 20H9c0 1 1.6 2 3 2s3-1 3-2h-3z"/>
+    <path d="M17.5 13V8c0-3-2.3-5-5.5-5S6.5 5 6.5 8v5c0 2 0 3-2 3v1h15v-1c-2 0-2-1-2-3zM12 19H9c0 1 1.6 2 3 2s3-1 3-2h-3z"/>
 </svg>
index 1c0de72..fb21370 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr-invert.png differ
index c032294..07de130 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
-    <path d="M17.8 14.7l1.7-4.7c1-2.8-.5-5.5-3.5-6.6s-5.9 0-6.9 2.8l-1.7 4.7c-.7 1.9-1 2.8-2.9 2.1l-.3 1 14.1 5.1.3-.9c-1.9-.7-1.5-1.6-.8-3.5zM12 19.8l-2.8-1c-.3.9.8 2.4 2.1 2.9s3.2.1 3.5-.9l-2.8-1z"/>
+    <path d="M17.8 13.7L19.5 9c1-2.8-.5-5.5-3.5-6.6-3-1.1-5.9 0-6.9 2.8L7.4 9.9c-.7 1.9-1 2.8-2.9 2.1l-.3 1 14.1 5.1.3-.9c-1.9-.7-1.5-1.6-.8-3.5zM12 18.8l-2.8-1c-.3.9.8 2.4 2.1 2.9s3.2.1 3.5-.9l-2.8-1z"/>
 </svg>
index 1f9ae71..fb13de1 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-ltr.png differ
index f68a8e0..855581b 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <path d="M17.8 14.7l1.7-4.7c1-2.8-.5-5.5-3.5-6.6s-5.9 0-6.9 2.8l-1.7 4.7c-.7 1.9-1 2.8-2.9 2.1l-.3 1 14.1 5.1.3-.9c-1.9-.7-1.5-1.6-.8-3.5zM12 19.8l-2.8-1c-.3.9.8 2.4 2.1 2.9s3.2.1 3.5-.9l-2.8-1z"/>
+    <path d="M17.8 13.7L19.5 9c1-2.8-.5-5.5-3.5-6.6-3-1.1-5.9 0-6.9 2.8L7.4 9.9c-.7 1.9-1 2.8-2.9 2.1l-.3 1 14.1 5.1.3-.9c-1.9-.7-1.5-1.6-.8-3.5zM12 18.8l-2.8-1c-.3.9.8 2.4 2.1 2.9s3.2.1 3.5-.9l-2.8-1z"/>
 </svg>
index c21e1c0..d0ebae5 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl-invert.png differ
index 34ec94b..30617cb 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
-    <path d="M6.21 14.7L4.51 10c-1-2.8.5-5.5 3.5-6.6 3-1.1 5.9 0 6.9 2.8l1.7 4.7c.7 1.9 1 2.8 2.9 2.1l.3 1-14.1 5.1-.3-.9c1.9-.7 1.5-1.6.8-3.5zm5.8 5.1l2.8-1c.3.9-.8 2.4-2.1 2.9s-3.2.1-3.5-.9l2.8-1z"/>
+    <path d="M6.21 13.7L4.51 9c-1-2.8.5-5.5 3.5-6.6 3-1.1 5.9 0 6.9 2.8l1.7 4.7c.7 1.9 1 2.8 2.9 2.1l.3 1-14.1 5.1-.3-.9c1.9-.7 1.5-1.6.8-3.5zm5.8 5.1l2.8-1c.3.9-.8 2.4-2.1 2.9s-3.2.1-3.5-.9l2.8-1z"/>
 </svg>
index 81d9a0a..b05fd4c 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/bellOn-rtl.png differ
index 92bcef5..e1f2961 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <path d="M6.21 14.7L4.51 10c-1-2.8.5-5.5 3.5-6.6 3-1.1 5.9 0 6.9 2.8l1.7 4.7c.7 1.9 1 2.8 2.9 2.1l.3 1-14.1 5.1-.3-.9c1.9-.7 1.5-1.6.8-3.5zm5.8 5.1l2.8-1c.3.9-.8 2.4-2.1 2.9s-3.2.1-3.5-.9l2.8-1z"/>
+    <path d="M6.21 13.7L4.51 9c-1-2.8.5-5.5 3.5-6.6 3-1.1 5.9 0 6.9 2.8l1.7 4.7c.7 1.9 1 2.8 2.9 2.1l.3 1-14.1 5.1-.3-.9c1.9-.7 1.5-1.6.8-3.5zm5.8 5.1l2.8-1c.3.9-.8 2.4-2.1 2.9s-3.2.1-3.5-.9l2.8-1z"/>
 </svg>
index e0b8859..159bdb9 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl-invert.png differ
index 3946c4a..dbc78c6 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
-    <path d="M11 6.5V5c1.4-1.5 5.2-1.2 6 0V4h1v15h-1v-7c-.8-.8-3.4-.9-5-.5V13c-1.2 1.5-4.3 1.2-5 0V6c.7.7 2.7.9 4 .5z"/>
-</svg>
+       <path d="M10.3 7.5V6c1.4-1.5 5.2-1.2 6 0V5h1v15h-1v-7c-.8-.8-3.4-.9-5-.5V14c-1.2 1.5-4.3 1.2-5 0V7c.7.7 2.7.9 4 .5z"/>
+</svg>
\ No newline at end of file
index e881d7c..a9943cc 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flag-rtl.png differ
index 0f7e7ce..397a3da 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <path d="M11 6.5V5c1.4-1.5 5.2-1.2 6 0V4h1v15h-1v-7c-.8-.8-3.4-.9-5-.5V13c-1.2 1.5-4.3 1.2-5 0V6c.7.7 2.7.9 4 .5z"/>
-</svg>
+       <path d="M10.3 7.5V6c1.4-1.5 5.2-1.2 6 0V5h1v15h-1v-7c-.8-.8-3.4-.9-5-.5V14c-1.2 1.5-4.3 1.2-5 0V7c.7.7 2.7.9 4 .5z"/>
+</svg>
\ No newline at end of file
index 4985911..8338017 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr-invert.png differ
index 0d074de..0614672 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
-    <path d="M18 2L2.03 17.97l.97 1 4-4V19h1v-5.03l2.688-2.69c.81-.02 1.645.053 2.312.22V13c1.2 1.5 4.3 1.2 5 0V6c-.493.493-1.635.725-2.72.688L19 2.968zM7 4v7.03l6.438-6.436C11.778 3.61 8.71 3.934 8 5V4z"/>
-</svg>
+       <path d="M19.9 19.6l-16-16-1.1 1.1L6 7.9V20h1v-7c.6-.6 2-.8 3.4-.7l8.4 8.4 1.1-1.1zM17 14V7c-.7.7-2.7.9-4 .5V6c-1.2-1.3-3.9-1.3-5.4-.5l8.9 9c.3-.2.4-.3.5-.5z"/>
+</svg>
\ No newline at end of file
index 033adde..780cb87 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-ltr.png differ
index 516a3ea..4bbd61c 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <path d="M18 2L2.03 17.97l.97 1 4-4V19h1v-5.03l2.688-2.69c.81-.02 1.645.053 2.312.22V13c1.2 1.5 4.3 1.2 5 0V6c-.493.493-1.635.725-2.72.688L19 2.968zM7 4v7.03l6.438-6.436C11.778 3.61 8.71 3.934 8 5V4z"/>
-</svg>
+       <path d="M19.9 19.6l-16-16-1.1 1.1L6 7.9V20h1v-7c.6-.6 2-.8 3.4-.7l8.4 8.4 1.1-1.1zM17 14V7c-.7.7-2.7.9-4 .5V6c-1.2-1.3-3.9-1.3-5.4-.5l8.9 9c.3-.2.4-.3.5-.5z"/>
+</svg>
\ No newline at end of file
index 4e1a6d1..4f9c3ad 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl-invert.png differ
index 0653df3..66a0c05 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
-    <path d="M7 2l-1 .97 3.72 3.717C8.634 6.727 7.492 6.494 7 6v7c.7 1.2 3.8 1.5 5 0v-1.5c.667-.167 1.5-.24 2.313-.22L17 13.97V19h1v-4.03l4 4 .97-1zm7.22 2c-.952-.03-1.932.163-2.658.594L18 11.03V4h-1v1c-.4-.6-1.556-.963-2.78-1z"/>
-</svg>
+       <path d="M3.5 19.6l16-16 1.1 1.1-3.2 3.2V20h-1v-7c-.6-.6-2-.8-3.4-.7l-8.4 8.4-1.1-1.1zM6.3 14V7c.7.7 2.7.9 4 .5V6c1.2-1.3 3.9-1.3 5.4-.5l-8.9 9c-.3-.2-.4-.3-.5-.5z"/>
+</svg>
\ No newline at end of file
index fca73fd..fa3696a 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/flagUndo-rtl.png differ
index 8ed159e..5d94425 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <path d="M7 2l-1 .97 3.72 3.717C8.634 6.727 7.492 6.494 7 6v7c.7 1.2 3.8 1.5 5 0v-1.5c.667-.167 1.5-.24 2.313-.22L17 13.97V19h1v-4.03l4 4 .97-1zm7.22 2c-.952-.03-1.932.163-2.658.594L18 11.03V4h-1v1c-.4-.6-1.556-.963-2.78-1z"/>
-</svg>
+       <path d="M3.5 19.6l16-16 1.1 1.1-3.2 3.2V20h-1v-7c-.6-.6-2-.8-3.4-.7l-8.4 8.4-1.1-1.1zM6.3 14V7c.7.7 2.7.9 4 .5V6c1.2-1.3 3.9-1.3 5.4-.5l-8.9 9c-.3-.2-.4-.3-.5-.5z"/>
+</svg>
\ No newline at end of file
index 4c74c3e..9cd13be 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stop-invert.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stop-invert.png differ
index 8b49792..2d060f3 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
-    <path d="M12 6c3.9 0 7 3.1 7 7s-3.1 7-7 7-7-3.1-7-7 3.1-7 7-7m0-1c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm-3 5h6v6H9z"/>
+       <path d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3 11.1H9v-6h6v6z"/>
 </svg>
index 12a1912..77dd75f 100644 (file)
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/stop.png and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/stop.png differ
index aa25e3d..4631b85 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <path d="M12 6c3.9 0 7 3.1 7 7s-3.1 7-7 7-7-3.1-7-7 3.1-7 7-7m0-1c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm-3 5h6v6H9z"/>
+       <path d="M12 5c-4.4 0-8 3.6-8 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm3 11.1H9v-6h6v6z"/>
 </svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-invert.png
new file mode 100644 (file)
index 0000000..6196d39
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-invert.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray-invert.svg
new file mode 100644 (file)
index 0000000..9cd3854
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #ffffff }</style>
+    <path d="M3 13.35l1.8-7.2c.2-.996.81-1.8 1.8-1.8h10.8c.99 0 1.6.867 1.8 1.8l1.8 7.2v4.5c0 .99-.81 1.8-1.8 1.8H4.8c-.99 0-1.8-.81-1.8-1.8v-4.5zm6.96 1.8h4.08c-.49.557-1.212.9-2.04.9a2.68 2.68 0 0 1-2.04-.9h4.08c.414-.472.66-1.098.66-1.8h4.14l-1.44-7.2H6.6l-1.44 7.2H9.3c0 .702.246 1.328.66 1.8z" id="tray"/>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray.png
new file mode 100644 (file)
index 0000000..e5c5e8f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/tray.svg
new file mode 100644 (file)
index 0000000..d7ab69b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path d="M3 13.35l1.8-7.2c.2-.996.81-1.8 1.8-1.8h10.8c.99 0 1.6.867 1.8 1.8l1.8 7.2v4.5c0 .99-.81 1.8-1.8 1.8H4.8c-.99 0-1.8-.81-1.8-1.8v-4.5zm6.96 1.8h4.08c-.49.557-1.212.9-2.04.9a2.68 2.68 0 0 1-2.04-.9h4.08c.414-.472.66-1.098.66-1.8h4.14l-1.44-7.2H6.6l-1.44 7.2H9.3c0 .702.246 1.328.66 1.8z" id="tray"/>
+</svg>
diff --git a/resources/lib/phpjs-sha1/sha1.js b/resources/lib/phpjs-sha1/sha1.js
deleted file mode 100644 (file)
index 93c533d..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-function sha1(str) {
-  //  discuss at: http://phpjs.org/functions/sha1/
-  // original by: Webtoolkit.info (http://www.webtoolkit.info/)
-  // improved by: Michael White (http://getsprink.com)
-  // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
-  //    input by: Brett Zamir (http://brett-zamir.me)
-  //   example 1: sha1('Kevin van Zonneveld');
-  //   returns 1: '54916d2e62f65b3afa6e192e6a601cdbe5cb5897'
-
-  var rotate_left = function (n, s) {
-    var t4 = (n << s) | (n >>> (32 - s));
-    return t4;
-  };
-
-  /*var lsb_hex = function (val) {
-   // Not in use; needed?
-    var str="";
-    var i;
-    var vh;
-    var vl;
-
-    for ( i=0; i<=6; i+=2 ) {
-      vh = (val>>>(i*4+4))&0x0f;
-      vl = (val>>>(i*4))&0x0f;
-      str += vh.toString(16) + vl.toString(16);
-    }
-    return str;
-  };*/
-
-  var cvt_hex = function (val) {
-    var str = '';
-    var i;
-    var v;
-
-    for (i = 7; i >= 0; i--) {
-      v = (val >>> (i * 4)) & 0x0f;
-      str += v.toString(16);
-    }
-    return str;
-  };
-
-  var blockstart;
-  var i, j;
-  var W = new Array(80);
-  var H0 = 0x67452301;
-  var H1 = 0xEFCDAB89;
-  var H2 = 0x98BADCFE;
-  var H3 = 0x10325476;
-  var H4 = 0xC3D2E1F0;
-  var A, B, C, D, E;
-  var temp;
-
-  // utf8_encode
-  str = unescape(encodeURIComponent(str));
-  var str_len = str.length;
-
-  var word_array = [];
-  for (i = 0; i < str_len - 3; i += 4) {
-    j = str.charCodeAt(i) << 24 | str.charCodeAt(i + 1) << 16 | str.charCodeAt(i + 2) << 8 | str.charCodeAt(i + 3);
-    word_array.push(j);
-  }
-
-  switch (str_len % 4) {
-  case 0:
-    i = 0x080000000;
-    break;
-  case 1:
-    i = str.charCodeAt(str_len - 1) << 24 | 0x0800000;
-    break;
-  case 2:
-    i = str.charCodeAt(str_len - 2) << 24 | str.charCodeAt(str_len - 1) << 16 | 0x08000;
-    break;
-  case 3:
-    i = str.charCodeAt(str_len - 3) << 24 | str.charCodeAt(str_len - 2) << 16 | str.charCodeAt(str_len - 1) <<
-      8 | 0x80;
-    break;
-  }
-
-  word_array.push(i);
-
-  while ((word_array.length % 16) != 14) {
-    word_array.push(0);
-  }
-
-  word_array.push(str_len >>> 29);
-  word_array.push((str_len << 3) & 0x0ffffffff);
-
-  for (blockstart = 0; blockstart < word_array.length; blockstart += 16) {
-    for (i = 0; i < 16; i++) {
-      W[i] = word_array[blockstart + i];
-    }
-    for (i = 16; i <= 79; i++) {
-      W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
-    }
-
-    A = H0;
-    B = H1;
-    C = H2;
-    D = H3;
-    E = H4;
-
-    for (i = 0; i <= 19; i++) {
-      temp = (rotate_left(A, 5) + ((B & C) | (~B & D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
-      E = D;
-      D = C;
-      C = rotate_left(B, 30);
-      B = A;
-      A = temp;
-    }
-
-    for (i = 20; i <= 39; i++) {
-      temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
-      E = D;
-      D = C;
-      C = rotate_left(B, 30);
-      B = A;
-      A = temp;
-    }
-
-    for (i = 40; i <= 59; i++) {
-      temp = (rotate_left(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
-      E = D;
-      D = C;
-      C = rotate_left(B, 30);
-      B = A;
-      A = temp;
-    }
-
-    for (i = 60; i <= 79; i++) {
-      temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
-      E = D;
-      D = C;
-      C = rotate_left(B, 30);
-      B = A;
-      A = temp;
-    }
-
-    H0 = (H0 + A) & 0x0ffffffff;
-    H1 = (H1 + B) & 0x0ffffffff;
-    H2 = (H2 + C) & 0x0ffffffff;
-    H3 = (H3 + D) & 0x0ffffffff;
-    H4 = (H4 + E) & 0x0ffffffff;
-  }
-
-  temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);
-  return temp.toLowerCase();
-}
index 6ff2e01..e52d6a7 100644 (file)
@@ -197,7 +197,7 @@ $.fn.updateTooltipAccessKeys.getAccessKeyLabel = getAccessKeyLabel;
  * getAccessKeyPrefix
  *
  * @method updateTooltipAccessKeys_getAccessKeyPrefix
- * @deprecated 1.27 Use #getAccessKeyModifiers
+ * @deprecated since 1.27 Use #getAccessKeyModifiers
  */
 $.fn.updateTooltipAccessKeys.getAccessKeyPrefix = function ( ua ) {
        return getAccessKeyModifiers( ua ).join( '-' ) + '-';
index 501e898..ac60e8f 100644 (file)
                                        }
 
                                } else if ( $collapsible.parent().is( 'li' ) &&
-                                       $collapsible.parent().children( '.mw-collapsible' ).size() === 1
+                                       $collapsible.parent().children( '.mw-collapsible' ).length === 1 &&
+                                       $collapsible.find( '> .mw-collapsible-toggle' ).length === 0
                                ) {
                                        // special case of one collapsible in <li> tag
                                        $toggleLink = buildDefaultToggleLink();
index 50bfa87..62be0d8 100644 (file)
                        for ( key in ts.collationTable ) {
                                // Check hasOwn to be safe
                                if ( ts.collationTable.hasOwnProperty( key ) ) {
-                                       keys.push( key );
+                                       keys.push( mw.RegExp.escape( key ) );
                                }
                        }
                        if ( keys.length ) {
-                               ts.collationRegex = new RegExp( '[' + keys.join( '' ) + ']', 'ig' );
+                               ts.collationRegex = new RegExp( keys.join( '|' ), 'ig' );
                        }
                }
        }
index 07c7d76..6c63957 100644 (file)
@@ -9,7 +9,11 @@
        $( function () {
                var idleTimeout = 3000,
                        api = new mw.Api(),
-                       pending = null,
+                       timer,
+                       pending,
+                       lastText,
+                       lastSummary,
+                       lastTextHash,
                        $form = $( '#editform' ),
                        $text = $form.find( '#wpTextbox1' ),
                        $summary = $form.find( '#wpSummary' ),
                        model = $form.find( '[name=model]' ).val(),
                        format = $form.find( '[name=format]' ).val(),
                        revId = $form.find( '[name=parentRevId]' ).val(),
-                       lastText = $text.textSelection( 'getContents' ),
-                       timer = null;
+                       lastPriority = 0,
+                       PRIORITY_LOW = 1,
+                       PRIORITY_HIGH = 2;
 
                // Send a request to stash the edit to the API.
                // If a request is in progress, abort it since its payload is stale and the API
                // may limit concurrent stash parses.
                function stashEdit() {
-                       if ( pending ) {
-                               pending.abort();
-                       }
-
                        api.getToken( 'csrf' ).then( function ( token ) {
-                               lastText = $text.textSelection( 'getContents' );
+                               var req, params,
+                                       textChanged = isTextChanged(),
+                                       priority = textChanged ? PRIORITY_HIGH : PRIORITY_LOW;
+
+                               if ( pending ) {
+                                       if ( lastPriority > priority ) {
+                                               // Stash request for summary change should wait on pending text change stash
+                                               pending.then( checkStash );
+                                               return;
+                                       }
+                                       pending.abort();
+                               }
 
-                               pending = api.post( {
+                               // Update the "last" tracking variables
+                               lastSummary = $summary.textSelection( 'getContents' );
+                               lastPriority = priority;
+                               if ( textChanged ) {
+                                       lastText = $text.textSelection( 'getContents' );
+                                       // Reset hash
+                                       lastTextHash = null;
+                               }
+
+                               params = {
                                        action: 'stashedit',
                                        token: token,
                                        title: mw.config.get( 'wgPageName' ),
                                        section: section,
                                        sectiontitle: '',
-                                       text: lastText,
-                                       summary: $summary.textSelection( 'getContents' ),
+                                       summary: lastSummary,
                                        contentmodel: model,
                                        contentformat: format,
                                        baserevid: revId
+                               };
+                               if ( lastTextHash ) {
+                                       params.stashedtexthash = lastTextHash;
+                               } else {
+                                       params.text = lastText;
+                               }
+
+                               req = api.post( params );
+                               pending = req;
+                               req.then( function ( data ) {
+                                       if ( req === pending ) {
+                                               pending = null;
+                                       }
+                                       if ( data.stashedit && data.stashedit.texthash ) {
+                                               lastTextHash = data.stashedit.texthash;
+                                       } else {
+                                               // Request failed or text hash expired;
+                                               // include the text in a future stash request.
+                                               lastTextHash = null;
+                                       }
                                } );
                        } );
                }
 
-               // Check if edit body text changed since the last stashEdit() call or if no edit
-               // stash calls have yet been made
-               function isChanged() {
-                       var newText = $text.textSelection( 'getContents' );
-                       return newText !== lastText;
+               // Whether the body text content changed since the last stashEdit()
+               function isTextChanged() {
+                       return lastText !== $text.textSelection( 'getContents' );
+               }
+
+               // Whether the edit summary has changed since the last stashEdit()
+               function isSummaryChanged() {
+                       return lastSummary !== $summary.textSelection( 'getContents' );
                }
 
-               function onEditorIdle() {
-                       if ( !isChanged() ) {
+               // Check whether text or summary have changed and call stashEdit()
+               function checkStash() {
+                       if ( !isTextChanged() && !isSummaryChanged() ) {
                                return;
                        }
 
                        stashEdit();
                }
 
-               function onTextKeyUp( e ) {
+               function onKeyUp( e ) {
                        // Ignore keystrokes that don't modify text, like cursor movements.
                        // See <http://www.javascripter.net/faq/keycodes.htm> and
                        // <http://www.quirksmode.org/js/keys.html>. We don't have to be exhaustive,
                        }
 
                        clearTimeout( timer );
-                       timer = setTimeout( onEditorIdle, idleTimeout );
+                       timer = setTimeout( checkStash, idleTimeout );
+               }
+
+               function onSummaryFocus() {
+                       // Summary typing is usually near the end of the workflow and involves less pausing.
+                       // Re-stash more frequently in hopes of capturing the final summary before submission.
+                       idleTimeout = 1000;
+                       // Stash now since the text is likely the final version. The re-stashes based on the
+                       // summary are targeted at caching edit checks that need the final summary.
+                       checkStash();
+               }
+
+               function onTextFocus() {
+                       // User returned to the text field... reset stash rate to default
+                       idleTimeout = 3000;
                }
 
                function onFormLoaded() {
                                // probably save the page soon
                                || $.inArray( $form.find( '#mw-edit-mode' ).val(), [ 'preview', 'diff' ] ) > -1
                        ) {
-                               stashEdit();
+                               checkStash();
                        }
                }
 
-               // We don't attempt to stash new section edits because in such cases
-               // the parser output varies on the edit summary (since it determines
-               // the new section's name).
+               // We don't attempt to stash new section edits because in such cases the parser output
+               // varies on the edit summary (since it determines the new section's name).
                if ( $form.find( 'input[name=wpSection]' ).val() === 'new' ) {
                        return;
                }
 
-               $text.on( { change: onEditorIdle, keyup: onTextKeyUp } );
-               $summary.on( { focus: onEditorIdle } );
+               $text.on( {
+                       change: checkStash,
+                       keyup: onKeyUp,
+                       focus: onTextFocus
+               } );
+               $summary.on( {
+                       focus: onSummaryFocus,
+                       focusout: checkStash,
+                       keyup: onKeyUp
+               } );
                onFormLoaded();
-
        } );
 }( mediaWiki, jQuery ) );
index 4209aa1..0ee4058 100644 (file)
 .editButtons input:first-child {
        margin-left: .1em;
 }
+
+/*
+ * Add a bit of margin space between the preview and the toolbar.
+ * This replaces the ugly <p><br /></p> we used to insert into the page source
+ */
+#wikiPreview.ontop {
+       margin-bottom: 1em;
+}
diff --git a/resources/src/mediawiki.action/mediawiki.action.history.diff.css b/resources/src/mediawiki.action/mediawiki.action.history.diff.css
deleted file mode 100644 (file)
index 9db6777..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*!
- * Diff rendering
- */
-table.diff {
-       border: none;
-       border-spacing: 4px;
-       margin: 0;
-       width: 100%;
-       /* Ensure that colums are of equal width */
-       table-layout: fixed;
-}
-
-table.diff td {
-       padding: 0.33em 0.5em;
-}
-
-table.diff td.diff-marker {
-       /* Compensate padding for increased font-size */
-       padding: 0.25em;
-}
-
-table.diff col.diff-marker {
-       width: 2%;
-}
-
-table.diff col.diff-content {
-       width: 48%;
-}
-
-table.diff td div {
-       /* Force-wrap very long lines such as URLs or page-widening char strings */
-       word-wrap: break-word;
-}
-
-td.diff-otitle,
-td.diff-ntitle {
-       text-align: center;
-}
-
-td.diff-lineno {
-       font-weight: bold;
-}
-
-td.diff-marker {
-       text-align: right;
-       font-weight: bold;
-       font-size: 1.25em;
-       line-height: 1.2;
-}
-
-td.diff-addedline,
-td.diff-deletedline,
-td.diff-context {
-       font-size: 88%;
-       line-height: 1.6;
-       vertical-align: top;
-       white-space: -moz-pre-wrap;
-       white-space: pre-wrap;
-       border-style: solid;
-       border-width: 1px 1px 1px 4px;
-       border-radius: 0.33em;
-}
-
-td.diff-addedline {
-       border-color: #a3d3ff;
-}
-
-td.diff-deletedline {
-       border-color: #ffe49c;
-}
-
-td.diff-context {
-       background: #f9f9f9;
-       border-color: #e6e6e6;
-       color: #333;
-}
-
-.diffchange {
-       font-weight: bold;
-       text-decoration: none;
-}
-
-td.diff-addedline .diffchange,
-td.diff-deletedline .diffchange {
-       border-radius: 0.33em;
-       padding: 0.25em 0;
-}
-
-td.diff-addedline .diffchange {
-       background: #d8ecff;
-}
-
-td.diff-deletedline .diffchange {
-       background: #feeec8;
-}
diff --git a/resources/src/mediawiki.action/mediawiki.action.history.diff.print.css b/resources/src/mediawiki.action/mediawiki.action.history.diff.print.css
deleted file mode 100644 (file)
index 76b5c9b..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*!
- * Diff rendering
- */
-td.diff-context,
-td.diff-addedline .diffchange,
-td.diff-deletedline .diffchange {
-       background-color: transparent;
-}
-
-td.diff-addedline .diffchange {
-       text-decoration: underline;
-}
-
-td.diff-deletedline .diffchange {
-       text-decoration: line-through;
-}
index a5508c6..de442e9 100644 (file)
@@ -91,30 +91,6 @@ abbr[title],
        }
 }
 
-/* Colored watchlist and recent changes numbers */
-.mw-plusminus-pos {
-       color: #006400; /* dark green */
-}
-
-.mw-plusminus-neg {
-       color: #8b0000; /* dark red */
-}
-
-.mw-plusminus-null {
-       color: #aaa; /* gray */
-}
-
-/*
- * Bidi-isolate these numbers.
- * See https://phabricator.wikimedia.org/T93484
- */
-.mw-plusminus-pos,
-.mw-plusminus-neg,
-.mw-plusminus-null {
-       unicode-bidi: -moz-isolate;
-       unicode-bidi: isolate;
-}
-
 /* Comment portions of RC entries */
 span.comment {
        font-style: italic;
@@ -122,14 +98,6 @@ span.comment {
        unicode-bidi: isolate;
 }
 
-/**
- * Add a bit of margin space between the preview and the toolbar.
- * This replaces the ugly <p><br /></p> we used to insert into the page source
- */
-#wikiPreview.ontop {
-       margin-bottom: 1em;
-}
-
 /* Stop floats from intruding into edit area in previews */
 #editform,
 #toolbar,
@@ -724,36 +692,10 @@ ol:lang(or) li {
        unicode-bidi: embed;
 }
 
-/* Correct user & content directionality when viewing a diff */
-.diff-currentversion-title,
-.diff {
-       direction: ltr;
-       unicode-bidi: embed;
-}
-
-/* @noflip */ .diff-contentalign-right td {
-       direction: rtl;
-       unicode-bidi: embed;
-}
-
-/* @noflip */ .diff-contentalign-left td {
-       direction: ltr;
-       unicode-bidi: embed;
-}
-
-.diff-multi,
-.diff-otitle,
-.diff-ntitle,
-.diff-lineno {
-       direction: ltr !important;
-       unicode-bidi: embed;
-}
-
 #mw-revision-info,
 #mw-revision-info-current,
 #mw-revision-nav {
        direction: ltr;
-       display: inline;
 }
 
 /* Images */
index 85bf9f6..d9cdf5a 100644 (file)
@@ -253,3 +253,10 @@ div.tright {
 div.tleft {
        margin: .5em 1.4em 1.3em 0;
 }
+
+/* Hide elements that are marked as "empty" according to legacy Tidy rules,
+ * except if a client script removes the mw-hide-empty-elt class from the body
+ */
+body.mw-hide-empty-elt .mw-empty-elt {
+       display: none;
+}
diff --git a/resources/src/mediawiki.skinning/images/external link icons.svg b/resources/src/mediawiki.skinning/images/external link icons.svg
deleted file mode 100644 (file)
index 6a67993..0000000
+++ /dev/null
@@ -1,697 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="13"
-   height="110"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.48.5 r10040"
-   sodipodi:docname="external link icons.svg">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="15.999999"
-     inkscape:cx="10.40536"
-     inkscape:cy="65.686256"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer5"
-     showgrid="true"
-     fit-margin-top="0"
-     fit-margin-left="0"
-     fit-margin-right="0"
-     fit-margin-bottom="0"
-     inkscape:window-width="1283"
-     inkscape:window-height="711"
-     inkscape:window-x="1790"
-     inkscape:window-y="-6"
-     inkscape:window-maximized="0">
-    <inkscape:grid
-       type="xygrid"
-       id="grid3246"
-       empspacing="4"
-       visible="true"
-       enabled="true"
-       snapvisiblegridlinesonly="true"
-       originx="0px"
-       originy="-27.999997px" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:groupmode="layer"
-     id="layer2"
-     inkscape:label="base"
-     style="display:none"
-     transform="translate(-505,-869.36218)">
-    <rect
-       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none"
-       id="rect4646"
-       width="13"
-       height="12.999996"
-       x="505"
-       y="885.36218" />
-    <rect
-       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none"
-       id="rect4646-4"
-       width="13"
-       height="12.999998"
-       x="505"
-       y="901.36218" />
-    <rect
-       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none"
-       id="rect4646-4-6"
-       width="13"
-       height="12.999996"
-       x="505"
-       y="917.36218"
-       inkscape:export-filename="/home/rahah/elvidishu/steak/unreal/dev/skins/external link icons/mail.png"
-       inkscape:export-xdpi="90"
-       inkscape:export-ydpi="90" />
-    <rect
-       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none"
-       id="rect4646-4-6-9"
-       width="13"
-       height="12.999996"
-       x="505"
-       y="933.36218" />
-    <rect
-       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none"
-       id="rect4646-4-6-6"
-       width="13"
-       height="12.999996"
-       x="505"
-       y="950.36218" />
-    <rect
-       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none"
-       id="rect4646-4-6-2"
-       width="13"
-       height="12.999998"
-       x="505"
-       y="966.36218" />
-    <rect
-       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
-       id="rect4646-44"
-       width="13"
-       height="12.999996"
-       x="505"
-       y="869.36218" />
-  </g>
-  <g
-     inkscape:label="sketch 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(-505,-869.36218)"
-     style="display:none"
-     sodipodi:insensitive="true">
-    <path
-       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 507,870.36218 0,5 3,0 4,4 0,-13 -4,4 z"
-       id="path3194"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccc" />
-    <path
-       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 517,869.36218 c 1,2 1,5 0,7"
-       id="path3196"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-    <path
-       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 520,867.36218 c 2,2 2,9 0,11"
-       id="path3198"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-    <path
-       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 507.93861,989.90562 0,15.99988 13,0 0,-10.99988 -5,-5 z"
-       id="path3200"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccc" />
-    <path
-       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 506.93861,918.90546 0,11.5 15,0 0,-11.5 z"
-       id="path3202"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 506.93861,918.90546 7.5,6 7.5,-6"
-       id="path3204"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccc" />
-    <path
-       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 507.93861,890.90546 3,0"
-       id="path3212"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-    <path
-       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 507.93861,893.90546 3,0"
-       id="path3214"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-    <path
-       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 507.93861,899.90546 3,0"
-       id="path3218"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-    <path
-       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 507.93861,902.90546 3,0"
-       id="path3220"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-    <path
-       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 517.93861,890.90546 3,0"
-       id="path3222"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-    <path
-       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 507.93861,896.90546 13,0"
-       id="path3224"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-    <path
-       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 517.93861,893.90546 3,0"
-       id="path3226"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-    <path
-       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 517.93861,899.90546 3,0"
-       id="path3230"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-    <path
-       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 517.93861,902.90546 3,0"
-       id="path3232"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-    <path
-       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 510.93861,890.90546 0,12 7,0 0,-12 z"
-       id="path3206"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 507.93861,888.90546 0,16"
-       id="path3208"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-    <path
-       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 520.93861,888.90546 0,16"
-       id="path3210"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-    <path
-       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 515.93861,989.90562 0,5 5,0"
-       id="path3234"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccc" />
-    <path
-       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 513.93861,969.40546 c -2,0 -5,0 -7,0 l 0,10.99995 11,5e-5 c 0,-2.33332 0,-4.66668 0,-7"
-       id="path3236"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none"
-       d="m 513.93861,976.40546 5,-4 3,3 0,-10 -10,0 3,3 -4,5"
-       id="path3242"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccc" />
-    <path
-       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 506.93861,940.40546 15,0 0,10 -6,0 -6,4 1,-4 -4,0 z"
-       id="path3244"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccc" />
-    <path
-       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 509.93861,972.40546 c 2,1 4,3 5,5"
-       id="path4641"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-  </g>
-  <g
-     inkscape:groupmode="layer"
-     id="layer4"
-     inkscape:label="sketch 2"
-     style="display:none"
-     transform="translate(0,-6.0000106)">
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 8.4921988,1.0623917 -4.0641234,4.064123 -3.2512987,0 0,5.6897733 3.2512987,0 4.0641234,4.064124 z m -1.6256494,4.064124 0,5.6897733 -1.6256493,-1.6256493 -2.438474,-1e-6 0,-2.438474 2.438474,1e-6 z"
-       id="path4755-9"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
-       d="m 9.3050235,5.1265157 c 0,-0.812824 0.8128245,-1.625649 1.6256495,-0.812824 0,0 0.812825,0.812824 0.812825,3.251298 0,2.4384743 -0.812825,3.2512993 -0.812825,3.2512993 -0.812825,0.812825 -1.6256495,0 -1.6256495,-0.812825 0,0 0.8128245,-0.8128243 0.8128245,-2.4384743 0,-1.625649 -0.8128245,-2.438474 -0.8128245,-2.438474 z"
-       id="path4760-1"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccsccsc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
-       d="m 11.743498,3.5008667 c 0,-0.812825 0.812824,-1.625649 1.625649,-0.812825 0,0 1.625649,1.62565 1.625649,4.876948 0,3.2512993 -1.625649,4.8769483 -1.625649,4.8769483 -0.812825,0.812825 -1.625649,0 -1.625649,-0.812824 0,0 1.625649,-1.62565 1.625649,-4.0641243 0,-2.438474 -1.625649,-4.064123 -1.625649,-4.064123 z"
-       id="path4762-8"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccsccsc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 2.8024261,23.008658 0,0.812825 10.5667209,0 0,-0.812825 z"
-       id="path4772"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 2.8024261,33.575379 10.5667209,0 0,-0.812825 -10.5667209,0 z"
-       id="path4774"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 2.8024261,25.447132 0,0.812824 2.438474,0 0,-0.812824 z m 8.1282469,0 0,0.812824 2.438474,0 0,-0.812824 z"
-       id="path4782"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 2.8024261,30.32408 0,0.812824 2.438474,0 0,-0.812824 z m 8.1282469,0 0,0.812824 2.438474,0 0,-0.812824 z"
-       id="path4778"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 2.8024261,28.698431 10.5667209,0 0,-0.812825 -10.5667209,0 z"
-       id="path4780"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
-       d="m 5.2409001,27.479194 0,1.625649 5.6897729,0 0,-1.625649 z"
-       id="path4793"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 4.4280754,23.008658 0,10.56672 7.3154226,0 0,-10.56672 z m 1.6256494,1.625649 4.0641232,0 0,7.315422 -4.0641232,0 z"
-       id="path4768"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 1.9896014,21.383009 1.6256493,0 0,13.818019 -1.6256493,0 z"
-       id="path4764"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 12.556322,21.383009 1.62565,0 0,13.818019 -1.62565,0 z"
-       id="path4766"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
-       d="m 1.9896014,44.954923 6.096185,5.689773 6.0961856,-5.689773 -0.812825,-0.812825 -1.625649,0.813196 -3.6577116,3.251298 -3.657711,-3.251298 -1.6256493,-0.813196 z"
-       id="path4800-4"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 1.1767767,43.329273 0,11.379545 13.8180193,0 0,-11.379545 z m 1.6256494,1.625649 10.5667209,0 0,8.128247 -10.5667209,0 z"
-       id="path4795-8"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 1.9896014,62.024243 -0.8128247,0.812824 0,8.128247 0.8128247,0.812825 2.438474,0 -0.8128247,3.251297 5.6897728,-3.251297 4.8769485,0 0.812824,-0.812825 0,-8.128247 -0.812824,-0.812824 z m 0.8128247,1.625649 10.5667209,0 0,6.502598 -4.0641235,0 -3.5815088,2.133664 0.9144278,-2.133664 -3.8355164,0 z"
-       id="path4802-8"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccccccccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 4.4280754,82.344856 10.5667206,0 0,10.56672 -3.251298,-3.25129 -3.2512992,2.43847 -0.8128247,-0.81282 4.0641239,-3.2513 1.625649,1.62565 0,-5.68978 -5.6897729,0 1.6256494,1.62565 -3.2512987,4.06413 -0.8128247,-0.81283 2.438474,-3.2513 z"
-       id="path4807-5"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccccccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 8.4921988,85.596156 -7.3154221,0 0,10.56672 10.5667213,0 0,-7.31542 -1.62565,0.81283 0,4.87694 -7.3154219,0 0,-7.31542 4.876948,0 z"
-       id="path4809-7"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
-       d="m 8.4921988,103.47831 0,4.87694 4.8769482,0 0,-1.62565 -3.251299,0 0,-3.25129 z"
-       id="path4818-67"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 1.9896014,102.66548 0,13.81802 12.1923706,0 0,-10.13799 -3.657711,-3.68003 z m 1.6256493,1.62565 6.5025973,0 2.438474,2.43847 0,8.12825 -8.9410713,0 z"
-       id="path4813-2"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
-       d="m 3.6152507,88.847456 c 0,-0.81282 0.8128247,-0.81282 0.8128247,-0.81282 2.438474,0.81282 4.0641234,2.43847 4.8769481,4.87694 0,0 0,0.81283 -0.8128247,0.81283 -1.6256494,-2.43847 -2.438474,-3.2513 -4.8769481,-4.87695 z"
-       id="path4822-8"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-  </g>
-  <g
-     inkscape:groupmode="layer"
-     id="layer3"
-     inkscape:label="Layer"
-     style="display:none"
-     transform="translate(-10,-26.000007)">
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none"
-       d="m 19,20.999995 -4,4 -3,0 0,6 3,0 4,4 z m -1,2.5 0,9 -2.6,-2.5 -2.4,0 0,-4 2.5,0 z"
-       id="path4755"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none"
-       d="m 19.75,24.999995 c 0,-1 0.75,-1 0.75,-1 0,0 1.5,1.029412 1.5,3.5 0,2.470588 -1.5,3.5 -1.5,3.5 0,0 -0.75,0 -0.75,-1 0,0 1,-0.852941 1,-2.5 0,-1.647059 -1,-2.5 -1,-2.5 z"
-       id="path4760"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccsccsc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none"
-       d="m 22.25,23.999995 c 0,-1 0.75,-1 0.75,-1 0,0 2,1.205882 2,4.5 0,3.294118 -2,4.5 -2,4.5 0,0 -0.75,0 -0.75,-1 0,0 1.5,-1.029412 1.5,-3.5 0,-2.470588 -1.5,-3.5 -1.5,-3.5 z"
-       id="path4762"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccsccsc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none"
-       d="m 31.4375,79.875 -1,1 0,10 1,1 3,0 -1,4 7,-4 6,0 1,-1 0,-10 -1,-1 z m 1,2 13,0 0,8 -5,0 -4.40625,2.625 1.125,-2.625 -4.71875,0 z"
-       id="path4802"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccccccccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none"
-       d="m 35.125,98.8125 13,0 0,13 -4,-4 -4,3 -1,-1 5,-4 2,2 0,-7 -7,0 2,2 -4,5 -1,-1 3,-4 z"
-       id="path4807"
-       inkscape:connector-curvature="0" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none"
-       d="m 40.125,102.8125 -9,0 0,13 13,0 0,-9 -2,1 0,6 -9,0 0,-9 6,0 z"
-       id="path4809"
-       inkscape:connector-curvature="0" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none"
-       d="m 34.125,106.8125 c 0,-1 1,-1 1,-1 3,1 5,3 6,6 0,0 0,1 -1,1 -2,-3 -3,-4 -6,-6 z"
-       id="path4822"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;fill-rule:evenodd;stroke:none"
-       d="m 12,50.499995 0,1 3,0 0,-1 -3,0 z m 9,0 0,1 3,0 0,-1 -3,0 z"
-       id="rect4841"
-       inkscape:connector-curvature="0" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;fill-rule:evenodd;stroke:none"
-       d="m 12,44.499995 0,1 3,0 0,-1 -3,0 z m 9,0 0,1 3,0 0,-1 -3,0 z"
-       id="rect4843"
-       inkscape:connector-curvature="0" />
-    <path
-       inkscape:connector-curvature="0"
-       style="fill:#15a5ea;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
-       d="m 12,52.999995 0,1 3,0 0,-1 z m 9,0 0,1 3,0 0,-1 z"
-       id="rect4843-1"
-       sodipodi:nodetypes="cccccccccc" />
-    <path
-       inkscape:connector-curvature="0"
-       style="fill:#15a5ea;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
-       d="m 12,41.999995 0,1 3,0 0,-1 z m 9,0 0,1 3,0 0,-1 z"
-       id="rect4843-17"
-       sodipodi:nodetypes="cccccccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
-       d="m 12,47.499995 0,1 3,0 0,-1 z m 9,0 0,1 3,0 0,-1 z"
-       id="rect4841-9"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccc" />
-    <rect
-       style="fill:#15a5ea;fill-opacity:1;fill-rule:evenodd;stroke:none"
-       id="rect4835"
-       width="7"
-       height="2"
-       x="4"
-       y="27"
-       transform="translate(10,19.999995)" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none"
-       d="m 2,21 1,0 0,14 -1,0 z"
-       id="path4826"
-       inkscape:connector-curvature="0"
-       transform="translate(10,19.999995)"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none"
-       d="m 13,21 1,0 0,14 -1,0 z"
-       id="path4828"
-       inkscape:connector-curvature="0"
-       transform="translate(10,19.999995)"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none"
-       d="m 4,22 0,12 8,0 0,-12 z m 1,1 6,0 0,10 -6,0 z"
-       transform="translate(10,19.999995)"
-       id="path4830"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccc" />
-    <rect
-       style="fill:#3366bb;fill-opacity:1;fill-rule:evenodd;stroke:none"
-       id="rect4837"
-       width="8"
-       height="1"
-       x="4"
-       y="22"
-       transform="translate(10,19.999995)" />
-    <rect
-       style="fill:#3366bb;fill-opacity:1;fill-rule:evenodd;stroke:none"
-       id="rect4839"
-       width="8"
-       height="1"
-       x="4"
-       y="33"
-       transform="translate(10,19.999995)" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none"
-       d="m 12,64.999995 6,5 6,-5 0.53033,-1.45299 -1.28033,0.45299 -5.25,4.5 -5.25,-4.5 -1.236136,-0.53033 z"
-       id="path4910"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none"
-       d="m 11,62.999995 0,10 14,0 0,-10 z m 1,1 12,0 0,8 -12,0 z"
-       id="path4905"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
-       d="m 19,121 0,5 5,0 -1,-1 -3,0 0,-3 z"
-       id="path4818-6"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 12,121 0,14 12,0 0,-10 -4,-4 z m 1,1 6.5,0 3.5,3.5 0,8.5 -10,0 z"
-       id="path4813-3"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccccc" />
-  </g>
-  <g
-     inkscape:groupmode="layer"
-     id="layer5"
-     inkscape:label="Layer#1"
-     style="opacity:0.98999999"
-     transform="translate(0,-6.0000106)">
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 7,23.000004 -3,3 -3,0 0,4 3,0 3,3 z m -1,2.500001 0,5 -1.5,-1.500001 -2.5,0 0,-2 2.5,0 z"
-       id="path4755-9-5"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
-       d="m 7.4319858,26.075368 c 0,-0.621323 0.6213237,-1.242647 1.2426477,-0.621323 0,0 0.6213228,0.621323 0.6213228,2.485294 0,1.863971 -0.6213228,2.485294 -0.6213228,2.485294 -0.621324,0.621324 -1.2426477,0 -1.2426477,-0.621323 0,0 0.6213237,-0.621324 0.6213237,-1.863971 0,-1.242648 -0.6213237,-1.863971 -0.6213237,-1.863971 z"
-       id="path4760-1-7"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccsccsc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
-       d="m 9.2959563,24.832721 c 0,-0.621324 0.6213228,-1.242647 1.2426477,-0.621324 0,0 1.242646,1.242648 1.242646,3.727942 0,2.485294 -1.242646,3.727941 -1.242646,3.727941 -0.6213249,0.621324 -1.2426477,0 -1.2426477,-0.621323 0,0 1.2426477,-1.242647 1.2426477,-3.106618 0,-1.863971 -1.2426477,-3.106618 -1.2426477,-3.106618 z"
-       id="path4762-8-3"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccsccsc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:0.99215686;stroke:none;display:inline"
-       d="m 2.4916056,40.000004 0,0.621323 8.0979164,0 0,-0.621323 z"
-       id="path4772-4"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:0.99215686;stroke:none;display:inline"
-       d="m 2.4916056,48.000004 8.0979164,0 0,-0.621324 -8.0979164,0 z"
-       id="path4774-6"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:0.99215686;stroke:none;display:inline"
-       d="m 2.4916056,41.919118 0,0.621323 1.8687499,0 0,-0.621323 z m 6.2291665,0 0,0.621323 1.8687499,0 0,-0.621323 z"
-       id="path4782-3"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:0.99215686;stroke:none;display:inline"
-       d="m 2.4916056,45.589344 0,0.621322 1.8687499,0 0,-0.621322 z m 6.2291665,0 0,0.621322 1.8687499,0 0,-0.621322 z"
-       id="path4778-9"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:0.99215686;stroke:none;display:inline"
-       d="m 2.4916056,44.404412 8.0979164,0 0,-0.621323 -8.0979164,0 z"
-       id="path4780-0"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
-       d="m 4.3603555,43.472427 0,1.242647 4.3604166,0 0,-1.242647 z"
-       id="path4793-0"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 4,40.000004 0,8 5,0 0,-8 z m 1,1 3,0 0,6 -3,0 z"
-       id="path4768-5"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 2,39.000004 1,0 0,10 -1,0 z"
-       id="path4764-1"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 10,39.000004 1,0 0,10 -1,0 z"
-       id="path4766-6"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
-       d="M 1.8400738,58.055766 6.5000005,62.405031 11.159927,58.055766 10.538604,57.434442 9.2959563,58.056049 6.5000005,60.541343 3.7040445,58.056049 2.4613977,57.434442 z"
-       id="path4800-4-9"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccccc"
-       inkscape:export-filename="/home/rahah/elvidishu/steak/unreal/dev/skins/external link icons/mail.png"
-       inkscape:export-xdpi="90"
-       inkscape:export-ydpi="90" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 1,57.000008 0,8 11,0 0,-8 z m 1,1 9,0 0,6 -9,0 z"
-       id="path4795-8-2"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccc"
-       inkscape:export-filename="/home/rahah/elvidishu/steak/unreal/dev/skins/external link icons/mail.png"
-       inkscape:export-xdpi="90"
-       inkscape:export-ydpi="90" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="M 1.6213238,72.000004 1,72.621328 l 0,5.757352 0.6213238,0.621324 1.6894529,0 L 2.6894528,81.727943 7,79.000004 l 4.378677,0 L 12,78.37868 12,72.621328 11.378677,72.000004 z m 0.3786762,1 9,0 0,5 -4,0 L 4.0978858,79.896603 5,78.000004 l -3,0 z"
-       id="path4802-8-9"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccccccccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 5,88.000004 7,0 0,7 -2,-2 -3,2 0,-1 3,-2.249999 1,1 0,-3.750001 -3.75,0 1,1 -2.25,3 -1,0 2,-3 z"
-       id="path4807-5-6"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccccccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 7,90.000004 -5,0 0,8 8,0 0,-5 -1,0 0,4 -6,0 0,-6 4,0 z"
-       id="path4809-7-3"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
-       d="m 7.5,105.00001 0,3 2.5,0 1,-1 -2.5,0 0,-3 z"
-       id="path4818-67-7"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccc" />
-    <path
-       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
-       d="m 3,104 0,10 8,1e-5 0,-7 -2.5,-3 z m 1,1 4,1e-5 2,2.50001 0,5.49999 -6,0 z"
-       id="path4813-2-2"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccccc" />
-    <path
-       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
-       d="m 4.0827206,92.533089 c 0,-0.621319 0.6213239,-0.621319 0.6213239,-0.621319 1.8639706,0.621319 3.1066175,1.863968 3.7279413,3.727935 0,0 0,0.621328 -0.6213238,0.621328 C 6.5680151,94.397065 5.9466913,93.775738 4.0827206,92.533089 z"
-       id="path4822-8-2"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#3465ba;fill-opacity:1;stroke:none;display:inline"
-       d="m 1,8.500006 0,7.5001 1,-0.9872 0,-6.0129 2,0 2,1 4,-2e-4 0,2.0002 1,0 0,-2.5 -0.5,-0.5002 -4.5,2e-4 -2,-1 -2.5,0 z"
-       id="path3209"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccccccccccccc" />
-    <path
-       style="fill:#16a4e8;fill-opacity:1;stroke:none;display:inline"
-       d="m 6,11.000106 -1,-10e-5 -2.5,0 -0.5,0.5 -1,4.5001 10,-10e-5 0,-4.4998 -0.5,-0.5 z m 0,1 4,-10e-5 0,3 -7.75,-1e-4 0.75,-3.0001 2,0 z"
-       id="path3215"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccccccccccc" />
-    <path
-       style="fill:#3465ba;fill-opacity:1;stroke:none;display:inline"
-       d="m 2,17.000006 0,1 8,0 0,-1 z"
-       id="path3247"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#3465ba;fill-opacity:1;stroke:none;display:inline"
-       d="m 5,15.500006 0,2 2,0 0,-2 c 0,-0.5 -2,-0.5 -2,0 z"
-       id="path3249"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-  </g>
-</svg>
diff --git a/resources/src/mediawiki.skinning/images/external-link-icons.svg b/resources/src/mediawiki.skinning/images/external-link-icons.svg
new file mode 100644 (file)
index 0000000..6a67993
--- /dev/null
@@ -0,0 +1,697 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="13"
+   height="110"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.5 r10040"
+   sodipodi:docname="external link icons.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="15.999999"
+     inkscape:cx="10.40536"
+     inkscape:cy="65.686256"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer5"
+     showgrid="true"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1283"
+     inkscape:window-height="711"
+     inkscape:window-x="1790"
+     inkscape:window-y="-6"
+     inkscape:window-maximized="0">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3246"
+       empspacing="4"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true"
+       originx="0px"
+       originy="-27.999997px" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="base"
+     style="display:none"
+     transform="translate(-505,-869.36218)">
+    <rect
+       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none"
+       id="rect4646"
+       width="13"
+       height="12.999996"
+       x="505"
+       y="885.36218" />
+    <rect
+       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none"
+       id="rect4646-4"
+       width="13"
+       height="12.999998"
+       x="505"
+       y="901.36218" />
+    <rect
+       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none"
+       id="rect4646-4-6"
+       width="13"
+       height="12.999996"
+       x="505"
+       y="917.36218"
+       inkscape:export-filename="/home/rahah/elvidishu/steak/unreal/dev/skins/external link icons/mail.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <rect
+       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none"
+       id="rect4646-4-6-9"
+       width="13"
+       height="12.999996"
+       x="505"
+       y="933.36218" />
+    <rect
+       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none"
+       id="rect4646-4-6-6"
+       width="13"
+       height="12.999996"
+       x="505"
+       y="950.36218" />
+    <rect
+       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none"
+       id="rect4646-4-6-2"
+       width="13"
+       height="12.999998"
+       x="505"
+       y="966.36218" />
+    <rect
+       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
+       id="rect4646-44"
+       width="13"
+       height="12.999996"
+       x="505"
+       y="869.36218" />
+  </g>
+  <g
+     inkscape:label="sketch 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-505,-869.36218)"
+     style="display:none"
+     sodipodi:insensitive="true">
+    <path
+       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 507,870.36218 0,5 3,0 4,4 0,-13 -4,4 z"
+       id="path3194"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccc" />
+    <path
+       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 517,869.36218 c 1,2 1,5 0,7"
+       id="path3196"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 520,867.36218 c 2,2 2,9 0,11"
+       id="path3198"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 507.93861,989.90562 0,15.99988 13,0 0,-10.99988 -5,-5 z"
+       id="path3200"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccc" />
+    <path
+       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 506.93861,918.90546 0,11.5 15,0 0,-11.5 z"
+       id="path3202"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 506.93861,918.90546 7.5,6 7.5,-6"
+       id="path3204"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccc" />
+    <path
+       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 507.93861,890.90546 3,0"
+       id="path3212"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 507.93861,893.90546 3,0"
+       id="path3214"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 507.93861,899.90546 3,0"
+       id="path3218"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 507.93861,902.90546 3,0"
+       id="path3220"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 517.93861,890.90546 3,0"
+       id="path3222"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 507.93861,896.90546 13,0"
+       id="path3224"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 517.93861,893.90546 3,0"
+       id="path3226"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 517.93861,899.90546 3,0"
+       id="path3230"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 517.93861,902.90546 3,0"
+       id="path3232"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 510.93861,890.90546 0,12 7,0 0,-12 z"
+       id="path3206"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 507.93861,888.90546 0,16"
+       id="path3208"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 520.93861,888.90546 0,16"
+       id="path3210"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 515.93861,989.90562 0,5 5,0"
+       id="path3234"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccc" />
+    <path
+       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 513.93861,969.40546 c -2,0 -5,0 -7,0 l 0,10.99995 11,5e-5 c 0,-2.33332 0,-4.66668 0,-7"
+       id="path3236"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none"
+       d="m 513.93861,976.40546 5,-4 3,3 0,-10 -10,0 3,3 -4,5"
+       id="path3242"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccc" />
+    <path
+       style="fill:none;stroke:#0066ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 506.93861,940.40546 15,0 0,10 -6,0 -6,4 1,-4 -4,0 z"
+       id="path3244"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccc" />
+    <path
+       style="fill:none;stroke:#5b9dff;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 509.93861,972.40546 c 2,1 4,3 5,5"
+       id="path4641"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cc" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer4"
+     inkscape:label="sketch 2"
+     style="display:none"
+     transform="translate(0,-6.0000106)">
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 8.4921988,1.0623917 -4.0641234,4.064123 -3.2512987,0 0,5.6897733 3.2512987,0 4.0641234,4.064124 z m -1.6256494,4.064124 0,5.6897733 -1.6256493,-1.6256493 -2.438474,-1e-6 0,-2.438474 2.438474,1e-6 z"
+       id="path4755-9"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
+       d="m 9.3050235,5.1265157 c 0,-0.812824 0.8128245,-1.625649 1.6256495,-0.812824 0,0 0.812825,0.812824 0.812825,3.251298 0,2.4384743 -0.812825,3.2512993 -0.812825,3.2512993 -0.812825,0.812825 -1.6256495,0 -1.6256495,-0.812825 0,0 0.8128245,-0.8128243 0.8128245,-2.4384743 0,-1.625649 -0.8128245,-2.438474 -0.8128245,-2.438474 z"
+       id="path4760-1"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccsccsc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
+       d="m 11.743498,3.5008667 c 0,-0.812825 0.812824,-1.625649 1.625649,-0.812825 0,0 1.625649,1.62565 1.625649,4.876948 0,3.2512993 -1.625649,4.8769483 -1.625649,4.8769483 -0.812825,0.812825 -1.625649,0 -1.625649,-0.812824 0,0 1.625649,-1.62565 1.625649,-4.0641243 0,-2.438474 -1.625649,-4.064123 -1.625649,-4.064123 z"
+       id="path4762-8"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccsccsc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 2.8024261,23.008658 0,0.812825 10.5667209,0 0,-0.812825 z"
+       id="path4772"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 2.8024261,33.575379 10.5667209,0 0,-0.812825 -10.5667209,0 z"
+       id="path4774"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 2.8024261,25.447132 0,0.812824 2.438474,0 0,-0.812824 z m 8.1282469,0 0,0.812824 2.438474,0 0,-0.812824 z"
+       id="path4782"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 2.8024261,30.32408 0,0.812824 2.438474,0 0,-0.812824 z m 8.1282469,0 0,0.812824 2.438474,0 0,-0.812824 z"
+       id="path4778"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 2.8024261,28.698431 10.5667209,0 0,-0.812825 -10.5667209,0 z"
+       id="path4780"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
+       d="m 5.2409001,27.479194 0,1.625649 5.6897729,0 0,-1.625649 z"
+       id="path4793"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 4.4280754,23.008658 0,10.56672 7.3154226,0 0,-10.56672 z m 1.6256494,1.625649 4.0641232,0 0,7.315422 -4.0641232,0 z"
+       id="path4768"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 1.9896014,21.383009 1.6256493,0 0,13.818019 -1.6256493,0 z"
+       id="path4764"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 12.556322,21.383009 1.62565,0 0,13.818019 -1.62565,0 z"
+       id="path4766"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
+       d="m 1.9896014,44.954923 6.096185,5.689773 6.0961856,-5.689773 -0.812825,-0.812825 -1.625649,0.813196 -3.6577116,3.251298 -3.657711,-3.251298 -1.6256493,-0.813196 z"
+       id="path4800-4"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 1.1767767,43.329273 0,11.379545 13.8180193,0 0,-11.379545 z m 1.6256494,1.625649 10.5667209,0 0,8.128247 -10.5667209,0 z"
+       id="path4795-8"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 1.9896014,62.024243 -0.8128247,0.812824 0,8.128247 0.8128247,0.812825 2.438474,0 -0.8128247,3.251297 5.6897728,-3.251297 4.8769485,0 0.812824,-0.812825 0,-8.128247 -0.812824,-0.812824 z m 0.8128247,1.625649 10.5667209,0 0,6.502598 -4.0641235,0 -3.5815088,2.133664 0.9144278,-2.133664 -3.8355164,0 z"
+       id="path4802-8"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccccccccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 4.4280754,82.344856 10.5667206,0 0,10.56672 -3.251298,-3.25129 -3.2512992,2.43847 -0.8128247,-0.81282 4.0641239,-3.2513 1.625649,1.62565 0,-5.68978 -5.6897729,0 1.6256494,1.62565 -3.2512987,4.06413 -0.8128247,-0.81283 2.438474,-3.2513 z"
+       id="path4807-5"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 8.4921988,85.596156 -7.3154221,0 0,10.56672 10.5667213,0 0,-7.31542 -1.62565,0.81283 0,4.87694 -7.3154219,0 0,-7.31542 4.876948,0 z"
+       id="path4809-7"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
+       d="m 8.4921988,103.47831 0,4.87694 4.8769482,0 0,-1.62565 -3.251299,0 0,-3.25129 z"
+       id="path4818-67"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 1.9896014,102.66548 0,13.81802 12.1923706,0 0,-10.13799 -3.657711,-3.68003 z m 1.6256493,1.62565 6.5025973,0 2.438474,2.43847 0,8.12825 -8.9410713,0 z"
+       id="path4813-2"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
+       d="m 3.6152507,88.847456 c 0,-0.81282 0.8128247,-0.81282 0.8128247,-0.81282 2.438474,0.81282 4.0641234,2.43847 4.8769481,4.87694 0,0 0,0.81283 -0.8128247,0.81283 -1.6256494,-2.43847 -2.438474,-3.2513 -4.8769481,-4.87695 z"
+       id="path4822-8"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="Layer"
+     style="display:none"
+     transform="translate(-10,-26.000007)">
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none"
+       d="m 19,20.999995 -4,4 -3,0 0,6 3,0 4,4 z m -1,2.5 0,9 -2.6,-2.5 -2.4,0 0,-4 2.5,0 z"
+       id="path4755"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none"
+       d="m 19.75,24.999995 c 0,-1 0.75,-1 0.75,-1 0,0 1.5,1.029412 1.5,3.5 0,2.470588 -1.5,3.5 -1.5,3.5 0,0 -0.75,0 -0.75,-1 0,0 1,-0.852941 1,-2.5 0,-1.647059 -1,-2.5 -1,-2.5 z"
+       id="path4760"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccsccsc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none"
+       d="m 22.25,23.999995 c 0,-1 0.75,-1 0.75,-1 0,0 2,1.205882 2,4.5 0,3.294118 -2,4.5 -2,4.5 0,0 -0.75,0 -0.75,-1 0,0 1.5,-1.029412 1.5,-3.5 0,-2.470588 -1.5,-3.5 -1.5,-3.5 z"
+       id="path4762"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccsccsc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none"
+       d="m 31.4375,79.875 -1,1 0,10 1,1 3,0 -1,4 7,-4 6,0 1,-1 0,-10 -1,-1 z m 1,2 13,0 0,8 -5,0 -4.40625,2.625 1.125,-2.625 -4.71875,0 z"
+       id="path4802"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccccccccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none"
+       d="m 35.125,98.8125 13,0 0,13 -4,-4 -4,3 -1,-1 5,-4 2,2 0,-7 -7,0 2,2 -4,5 -1,-1 3,-4 z"
+       id="path4807"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none"
+       d="m 40.125,102.8125 -9,0 0,13 13,0 0,-9 -2,1 0,6 -9,0 0,-9 6,0 z"
+       id="path4809"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none"
+       d="m 34.125,106.8125 c 0,-1 1,-1 1,-1 3,1 5,3 6,6 0,0 0,1 -1,1 -2,-3 -3,-4 -6,-6 z"
+       id="path4822"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;fill-rule:evenodd;stroke:none"
+       d="m 12,50.499995 0,1 3,0 0,-1 -3,0 z m 9,0 0,1 3,0 0,-1 -3,0 z"
+       id="rect4841"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;fill-rule:evenodd;stroke:none"
+       d="m 12,44.499995 0,1 3,0 0,-1 -3,0 z m 9,0 0,1 3,0 0,-1 -3,0 z"
+       id="rect4843"
+       inkscape:connector-curvature="0" />
+    <path
+       inkscape:connector-curvature="0"
+       style="fill:#15a5ea;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
+       d="m 12,52.999995 0,1 3,0 0,-1 z m 9,0 0,1 3,0 0,-1 z"
+       id="rect4843-1"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       inkscape:connector-curvature="0"
+       style="fill:#15a5ea;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
+       d="m 12,41.999995 0,1 3,0 0,-1 z m 9,0 0,1 3,0 0,-1 z"
+       id="rect4843-17"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
+       d="m 12,47.499995 0,1 3,0 0,-1 z m 9,0 0,1 3,0 0,-1 z"
+       id="rect4841-9"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <rect
+       style="fill:#15a5ea;fill-opacity:1;fill-rule:evenodd;stroke:none"
+       id="rect4835"
+       width="7"
+       height="2"
+       x="4"
+       y="27"
+       transform="translate(10,19.999995)" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none"
+       d="m 2,21 1,0 0,14 -1,0 z"
+       id="path4826"
+       inkscape:connector-curvature="0"
+       transform="translate(10,19.999995)"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none"
+       d="m 13,21 1,0 0,14 -1,0 z"
+       id="path4828"
+       inkscape:connector-curvature="0"
+       transform="translate(10,19.999995)"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none"
+       d="m 4,22 0,12 8,0 0,-12 z m 1,1 6,0 0,10 -6,0 z"
+       transform="translate(10,19.999995)"
+       id="path4830"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <rect
+       style="fill:#3366bb;fill-opacity:1;fill-rule:evenodd;stroke:none"
+       id="rect4837"
+       width="8"
+       height="1"
+       x="4"
+       y="22"
+       transform="translate(10,19.999995)" />
+    <rect
+       style="fill:#3366bb;fill-opacity:1;fill-rule:evenodd;stroke:none"
+       id="rect4839"
+       width="8"
+       height="1"
+       x="4"
+       y="33"
+       transform="translate(10,19.999995)" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none"
+       d="m 12,64.999995 6,5 6,-5 0.53033,-1.45299 -1.28033,0.45299 -5.25,4.5 -5.25,-4.5 -1.236136,-0.53033 z"
+       id="path4910"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none"
+       d="m 11,62.999995 0,10 14,0 0,-10 z m 1,1 12,0 0,8 -12,0 z"
+       id="path4905"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
+       d="m 19,121 0,5 5,0 -1,-1 -3,0 0,-3 z"
+       id="path4818-6"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 12,121 0,14 12,0 0,-10 -4,-4 z m 1,1 6.5,0 3.5,3.5 0,8.5 -10,0 z"
+       id="path4813-3"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccccc" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="Layer#1"
+     style="opacity:0.98999999"
+     transform="translate(0,-6.0000106)">
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 7,23.000004 -3,3 -3,0 0,4 3,0 3,3 z m -1,2.500001 0,5 -1.5,-1.500001 -2.5,0 0,-2 2.5,0 z"
+       id="path4755-9-5"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
+       d="m 7.4319858,26.075368 c 0,-0.621323 0.6213237,-1.242647 1.2426477,-0.621323 0,0 0.6213228,0.621323 0.6213228,2.485294 0,1.863971 -0.6213228,2.485294 -0.6213228,2.485294 -0.621324,0.621324 -1.2426477,0 -1.2426477,-0.621323 0,0 0.6213237,-0.621324 0.6213237,-1.863971 0,-1.242648 -0.6213237,-1.863971 -0.6213237,-1.863971 z"
+       id="path4760-1-7"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccsccsc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
+       d="m 9.2959563,24.832721 c 0,-0.621324 0.6213228,-1.242647 1.2426477,-0.621324 0,0 1.242646,1.242648 1.242646,3.727942 0,2.485294 -1.242646,3.727941 -1.242646,3.727941 -0.6213249,0.621324 -1.2426477,0 -1.2426477,-0.621323 0,0 1.2426477,-1.242647 1.2426477,-3.106618 0,-1.863971 -1.2426477,-3.106618 -1.2426477,-3.106618 z"
+       id="path4762-8-3"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccsccsc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:0.99215686;stroke:none;display:inline"
+       d="m 2.4916056,40.000004 0,0.621323 8.0979164,0 0,-0.621323 z"
+       id="path4772-4"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:0.99215686;stroke:none;display:inline"
+       d="m 2.4916056,48.000004 8.0979164,0 0,-0.621324 -8.0979164,0 z"
+       id="path4774-6"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:0.99215686;stroke:none;display:inline"
+       d="m 2.4916056,41.919118 0,0.621323 1.8687499,0 0,-0.621323 z m 6.2291665,0 0,0.621323 1.8687499,0 0,-0.621323 z"
+       id="path4782-3"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:0.99215686;stroke:none;display:inline"
+       d="m 2.4916056,45.589344 0,0.621322 1.8687499,0 0,-0.621322 z m 6.2291665,0 0,0.621322 1.8687499,0 0,-0.621322 z"
+       id="path4778-9"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:0.99215686;stroke:none;display:inline"
+       d="m 2.4916056,44.404412 8.0979164,0 0,-0.621323 -8.0979164,0 z"
+       id="path4780-0"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
+       d="m 4.3603555,43.472427 0,1.242647 4.3604166,0 0,-1.242647 z"
+       id="path4793-0"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 4,40.000004 0,8 5,0 0,-8 z m 1,1 3,0 0,6 -3,0 z"
+       id="path4768-5"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 2,39.000004 1,0 0,10 -1,0 z"
+       id="path4764-1"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 10,39.000004 1,0 0,10 -1,0 z"
+       id="path4766-6"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
+       d="M 1.8400738,58.055766 6.5000005,62.405031 11.159927,58.055766 10.538604,57.434442 9.2959563,58.056049 6.5000005,60.541343 3.7040445,58.056049 2.4613977,57.434442 z"
+       id="path4800-4-9"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccc"
+       inkscape:export-filename="/home/rahah/elvidishu/steak/unreal/dev/skins/external link icons/mail.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 1,57.000008 0,8 11,0 0,-8 z m 1,1 9,0 0,6 -9,0 z"
+       id="path4795-8-2"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccc"
+       inkscape:export-filename="/home/rahah/elvidishu/steak/unreal/dev/skins/external link icons/mail.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="M 1.6213238,72.000004 1,72.621328 l 0,5.757352 0.6213238,0.621324 1.6894529,0 L 2.6894528,81.727943 7,79.000004 l 4.378677,0 L 12,78.37868 12,72.621328 11.378677,72.000004 z m 0.3786762,1 9,0 0,5 -4,0 L 4.0978858,79.896603 5,78.000004 l -3,0 z"
+       id="path4802-8-9"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccccccccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 5,88.000004 7,0 0,7 -2,-2 -3,2 0,-1 3,-2.249999 1,1 0,-3.750001 -3.75,0 1,1 -2.25,3 -1,0 2,-3 z"
+       id="path4807-5-6"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 7,90.000004 -5,0 0,8 8,0 0,-5 -1,0 0,4 -6,0 0,-6 4,0 z"
+       id="path4809-7-3"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
+       d="m 7.5,105.00001 0,3 2.5,0 1,-1 -2.5,0 0,-3 z"
+       id="path4818-67-7"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccc" />
+    <path
+       style="fill:#3366bb;fill-opacity:1;stroke:none;display:inline"
+       d="m 3,104 0,10 8,1e-5 0,-7 -2.5,-3 z m 1,1 4,1e-5 2,2.50001 0,5.49999 -6,0 z"
+       id="path4813-2-2"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccccc" />
+    <path
+       style="fill:#15a5ea;fill-opacity:1;stroke:none;display:inline"
+       d="m 4.0827206,92.533089 c 0,-0.621319 0.6213239,-0.621319 0.6213239,-0.621319 1.8639706,0.621319 3.1066175,1.863968 3.7279413,3.727935 0,0 0,0.621328 -0.6213238,0.621328 C 6.5680151,94.397065 5.9466913,93.775738 4.0827206,92.533089 z"
+       id="path4822-8-2"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#3465ba;fill-opacity:1;stroke:none;display:inline"
+       d="m 1,8.500006 0,7.5001 1,-0.9872 0,-6.0129 2,0 2,1 4,-2e-4 0,2.0002 1,0 0,-2.5 -0.5,-0.5002 -4.5,2e-4 -2,-1 -2.5,0 z"
+       id="path3209"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccccccccc" />
+    <path
+       style="fill:#16a4e8;fill-opacity:1;stroke:none;display:inline"
+       d="m 6,11.000106 -1,-10e-5 -2.5,0 -0.5,0.5 -1,4.5001 10,-10e-5 0,-4.4998 -0.5,-0.5 z m 0,1 4,-10e-5 0,3 -7.75,-1e-4 0.75,-3.0001 2,0 z"
+       id="path3215"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccccccccccc" />
+    <path
+       style="fill:#3465ba;fill-opacity:1;stroke:none;display:inline"
+       d="m 2,17.000006 0,1 8,0 0,-1 z"
+       id="path3247"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#3465ba;fill-opacity:1;stroke:none;display:inline"
+       d="m 5,15.500006 0,2 2,0 0,-2 c 0,-0.5 -2,-0.5 -2,0 z"
+       id="path3249"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+  </g>
+</svg>
index 3d90307..5c3715d 100644 (file)
 
                capsuleWidget: {
                        getApiValue: function () {
-                               return this.getItemsData().join( '|' );
+                               var items = this.getItemsData();
+                               if ( items.join( '' ).indexOf( '|' ) === -1 ) {
+                                       return items.join( '|' );
+                               } else {
+                                       return '\x1f' + items.join( '\x1f' );
+                               }
                        },
                        setApiValue: function ( v ) {
-                               this.setItemsFromData( v === undefined || v === '' ? [] : String( v ).split( '|' ) );
+                               if ( v === undefined || v === '' || v === '\x1f' ) {
+                                       this.setItemsFromData( [] );
+                               } else {
+                                       v = String( v );
+                                       if ( v.indexOf( '\x1f' ) !== 0 ) {
+                                               this.setItemsFromData( v.split( '|' ) );
+                                       } else {
+                                               this.setItemsFromData( v.substr( 1 ).split( '\x1f' ) );
+                                       }
+                               }
                        },
                        apiCheckValid: function () {
                                var ok = this.getApiValue() !== undefined || suppressErrors;
index b27d2a5..a06aa95 100644 (file)
        unicode-bidi: -moz-isolate;
        unicode-bidi: isolate;
 }
-
-/* Comment portions of RC entries */
-span.comment {
-       font-style: italic;
-       unicode-bidi: -moz-isolate;
-       unicode-bidi: isolate;
-}
index f2019e7..4b8eced 100644 (file)
@@ -115,9 +115,3 @@ table.mw-userrights-groups * td,
 table.mw-userrights-groups * th {
        padding-right: 1.5em;
 }
-
-/* Special:Upload */
-p.mw-upload-editlicenses {
-       font-size: 90%;
-       text-align: right;
-}
index 6d88c51..9af81b8 100644 (file)
@@ -2,6 +2,10 @@
  * JavaScript for Special:MovePage
  */
 jQuery( function () {
+       // Infuse for pretty dropdown
        OO.ui.infuse( 'wpNewTitle' );
+       // Limit to 255 bytes, not characters
        OO.ui.infuse( 'wpReason' ).$input.byteLimit();
+       // Infuse for nicer "help" popup
+       OO.ui.infuse( 'wpMovetalk-field' );
 } );
index a756f22..163e85d 100644 (file)
@@ -3,10 +3,9 @@
  */
 ( function ( mw, $ ) {
        $( function () {
-               var $preftoc, $preferences, $fieldsets,
-                       labelFunc,
-                       $tzSelect, $tzTextbox, $localtimeHolder, servertime,
-                       allowCloseWindow, notif;
+               var $preftoc, $preferences, $fieldsets, labelFunc,
+                       $tzSelect, $tzTextbox, $localtimeHolder, servertime, allowCloseWindow,
+                       convertmessagebox = require( 'mediawiki.notification.convertmessagebox' );
 
                labelFunc = function () {
                        return this.id.replace( /^mw-prefsection/g, 'preftab' );
                        }
                }
 
-               // Check for messageboxes (.successbox, .warningbox, .errorbox) to replace with notifications
-               if ( $( '.mw-preferences-messagebox' ).length ) {
-                       // If there is a #mw-preferences-success box and javascript is enabled, use a slick notification instead!
-                       if ( $( '#mw-preferences-success' ).length ) {
-                               notif = mw.notification.notify( mw.message( 'savedprefs' ), { autoHide: false } );
-                               // 'change' event not reliable!
-                               $( '#preftoc, .prefsection' ).one( 'change keydown mousedown', function () {
-                                       if ( notif ) {
-                                               notif.close();
-                                               notif = null;
-                                       }
-                               } );
-                       }
-               }
+               // Check for successbox to replace with notifications
+               convertmessagebox();
 
                // Enable keyboard users to use left and right keys to switch tabs
                $preftoc.on( 'keydown', function ( event ) {
index 4c9c41e..c7a5f77 100644 (file)
        zoom: 1;
 }
 
-/* When JS is enabled, .mw-preferences-messageboxes are replaced with mw.notifications */
-.client-js .mw-preferences-messagebox {
-       display: none;
-}
-
 .client-nojs #preftoc {
        display: none;
 }
index e7a5865..a523d5b 100644 (file)
@@ -37,7 +37,7 @@
        margin-left: 0;
        float: left;
 }
-.mw-search-interwiki-header {
+.mw-search-visualclear {
        clear: both;
 }
 .mw-search-results li {
index b916248..283df85 100644 (file)
@@ -8,3 +8,8 @@
        color: #705000;
        background-color: #fdf1d1;
 }
+
+p.mw-upload-editlicenses {
+       font-size: 90%;
+       text-align: right;
+}
diff --git a/resources/src/mediawiki.special/mediawiki.special.userrights.js b/resources/src/mediawiki.special/mediawiki.special.userrights.js
new file mode 100644 (file)
index 0000000..0643988
--- /dev/null
@@ -0,0 +1,8 @@
+/*!
+ * JavaScript for Special:UserRights
+ */
+( function () {
+       var convertmessagebox = require( 'mediawiki.notification.convertmessagebox' );
+       // Replace successbox with notifications
+       convertmessagebox();
+}() );
index ceb93c2..d65b284 100644 (file)
@@ -40,8 +40,3 @@
 #mw-editbutton-hr {
        .background-image("images/@{button-hr}");
 }
-
-// Awful workaround for T113868, while it awaits a better fix.
-#mw-t113868 {
-       background-image: url( images/ar/button_bold.png ), url( images/ar/button_headline.png ), url( images/ar/button_italic.png ), url( images/ar/button_link.png ), url( images/ar/button_nowiki.png ), url( images/be-tarask/button_bold.png ), url( images/be-tarask/button_italic.png ), url( images/be-tarask/button_link.png ), url( images/de/button_bold.png ), url( images/de/button_italic.png ), url( images/en/button_bold.png ), url( images/en/button_extlink.png ), url( images/en/button_headline.png ), url( images/en/button_hr.png ), url( images/en/button_image.png ), url( images/en/button_italic.png ), url( images/en/button_link.png ), url( images/en/button_media.png ), url( images/en/button_nowiki.png ), url( images/en/button_sig.png ), url( images/fa/button_bold.png ), url( images/fa/button_headline.png ), url( images/fa/button_italic.png ), url( images/fa/button_link.png ), url( images/fa/button_nowiki.png ), url( images/ksh/button_italic.png ), url( images/ru/button_bold.png ), url( images/ru/button_italic.png ), url( images/ru/button_link.png );
-}
index 9b9d324..0035601 100644 (file)
 
 .mw-ui-icon {
        position: relative;
+       line-height: @iconSize;
        min-height: @iconSize;
        min-width: @iconSize;
 
        // Standalone icons
        //
        // Markup:
-       // <div class="mw-ui-icon mw-ui-icon-element mw-ui-icon-ok">OK</div><br/>
-       // <div class="mw-ui-icon mw-ui-icon-element mw-ui-icon-ok mw-ui-button mw-ui-progressive">OK</div><br/>
+       // <div class="mw-ui-icon mw-ui-icon-element mw-ui-icon-ok">OK</div><br>
+       // <div class="mw-ui-icon mw-ui-icon-element mw-ui-icon-ok mw-ui-button mw-ui-progressive">OK</div><br>
        // <button class="mw-ui-icon mw-ui-icon-ok mw-ui-icon-element mw-ui-button mw-ui-quiet" title="">Close</button>
        //
        // Styleguide 6.1.1.
                        margin-right: @iconGutterWidth;
                }
        }
-}
+
+       // Icons small for elements like indicators
+       //
+       // Markup:
+       // <div class="mw-ui-icon mw-ui-icon-small mw-ui-icon-help"></div>
+       //
+       // Styleguide 6.1.3
+       &.mw-ui-icon-small:before {
+               background-size: 66.67% auto; // 66.67% of 24px equals 16px
+       }
+}
\ No newline at end of file
index 0bdf02e..4d86cfd 100644 (file)
                        prop: [ 'info' ],
                        titles: titles
                } ).done( function ( response ) {
+                       var
+                               normalized = {},
+                               pages = {};
+                       $.each( response.query.normalized || [], function ( index, data ) {
+                               normalized[ data.fromencoded ? decodeURIComponent( data.from ) : data.from ] = data.to;
+                       } );
                        $.each( response.query.pages, function ( index, page ) {
-                               var title = new ForeignTitle( page.title ).getPrefixedText();
-                               cache.existenceCache[ title ] = !page.missing;
+                               pages[ page.title ] = !page.missing;
+                       } );
+                       $.each( titles, function ( index, title ) {
+                               var normalizedTitle = title;
+                               while ( normalized[ normalizedTitle ] ) {
+                                       normalizedTitle = normalized[ normalizedTitle ];
+                               }
+                               cache.existenceCache[ title ] = pages[ normalizedTitle ];
                                queue[ title ].resolve( cache.existenceCache[ title ] );
                        } );
                } );
index 45f52b7..ffb7736 100644 (file)
         * @inheritdoc
         */
        CSP.createItemWidget = function ( data ) {
+               var title = mw.Title.makeTitle( NS_CATEGORY, data );
+               if ( !title ) {
+                       return null;
+               }
                return new mw.widgets.CategoryCapsuleItemWidget( {
                        apiUrl: this.api.apiUrl || undefined,
-                       title: mw.Title.makeTitle( NS_CATEGORY, data )
+                       title: title
                } );
        };
 
        CSP.getItemFromData = function ( data ) {
                // This is a bit of a hack... We have to canonicalize the data in the same way that
                // #createItemWidget and CategoryCapsuleItemWidget will do, otherwise we won't find duplicates.
-               data = mw.Title.makeTitle( NS_CATEGORY, data ).getMainText();
-               return OO.ui.mixin.GroupElement.prototype.getItemFromData.call( this, data );
+               var title = mw.Title.makeTitle( NS_CATEGORY, data );
+               if ( !title ) {
+                       return null;
+               }
+               return OO.ui.mixin.GroupElement.prototype.getItemFromData.call( this, title.getMainText() );
        };
 
        /**
index 7b7ef3d..86018a4 100644 (file)
                border-radius: 0.1em;
                line-height: 1.275em;
                background-color: #fff;
+
+               > .oo-ui-labelElement-label {
+                       padding: 0;
+               }
        }
 
        &.oo-ui-indicatorElement .mw-widget-dateInputWidget-handle > .oo-ui-indicatorElement-indicator {
index 84533aa..37e6e1a 100644 (file)
                this.$label.attr( 'href', config.url );
                this.$element.addClass( 'mw-widget-titleOptionWidget' );
 
+               // OOUI OptionWidgets make an effort to not be tab accessible, but
+               // adding a link inside them would undo that. So, explicitly make it
+               // not tabbable.
+               this.$label.attr( 'tabindex', '-1' );
+
                // Allow opening the link in new tab, but not regular navigation.
                this.$label.on( 'click', function ( e ) {
                        // Don't interfere with special clicks (e.g. to open in new tab)
index fdd4a8a..1732407 100644 (file)
@@ -96,6 +96,8 @@
        /**
         * Get a promise which resolves with an API repsonse for suggested
         * links for the current query.
+        *
+        * @return {jQuery.Promise} Suggestions promise
         */
        mw.widgets.TitleWidget.prototype.getSuggestionsPromise = function () {
                var req,
index 899daa5..f51403f 100644 (file)
@@ -35,6 +35,9 @@
         * @constructor
         * @param {string|mw.Uri} url URL pointing to another wiki's `api.php` endpoint.
         * @param {Object} [options] See mw.Api.
+        * @param {Object} [options.anonymous=false] Perform all requests anonymously. Use this option if
+        *     the target wiki may otherwise not accept cross-origin requests, or if you don't need to
+        *     perform write actions or read restricted information and want to avoid the overhead.
         *
         * @author Bartosz Dziewoński
         * @author Jon Robson
                }
 
                this.apiUrl = String( url );
+               this.anonymous = options && options.anonymous;
 
                options = $.extend( /*deep=*/ true,
                        {
                                ajax: {
                                        url: this.apiUrl,
                                        xhrFields: {
-                                               withCredentials: true
+                                               withCredentials: this.anonymous ? false : true
                                        }
                                },
                                parameters: {
         * @return {string}
         */
        CoreForeignApi.prototype.getOrigin = function () {
-               var origin = location.protocol + '//' + location.hostname;
+               var origin;
+               if ( this.anonymous ) {
+                       return '*';
+               }
+               origin = location.protocol + '//' + location.hostname;
                if ( location.port ) {
                        origin += ':' + location.port;
                }
index 3e010d0..b7579ff 100644 (file)
@@ -9,6 +9,9 @@
         *     `options` to mw.Api constructor.
         * @property {Object} defaultOptions.parameters Default query parameters for API requests.
         * @property {Object} defaultOptions.ajax Default options for jQuery#ajax.
+        * @property {boolean} defaultOptions.useUS Whether to use U+001F when joining multi-valued
+        *     parameters (since 1.28). Default is true if ajax.url is not set, false otherwise for
+        *     compatibility.
         * @private
         */
        var defaultOptions = {
@@ -95,6 +98,8 @@
                        options.ajax.url = String( options.ajax.url );
                }
 
+               options = $.extend( { useUS: !options.ajax || !options.ajax.url }, options );
+
                options.parameters = $.extend( {}, defaultOptions.parameters, options.parameters );
                options.ajax = $.extend( {}, defaultOptions.ajax, options.ajax );
 
                 *
                 * @private
                 * @param {Object} parameters (modified in-place)
+                * @param {boolean} useUS Whether to use U+001F when joining multi-valued parameters.
                 */
-               preprocessParameters: function ( parameters ) {
+               preprocessParameters: function ( parameters, useUS ) {
                        var key;
                        // Handle common MediaWiki API idioms for passing parameters
                        for ( key in parameters ) {
                                // Multiple values are pipe-separated
                                if ( $.isArray( parameters[ key ] ) ) {
-                                       parameters[ key ] = parameters[ key ].join( '|' );
+                                       if ( !useUS || parameters[ key ].join( '' ).indexOf( '|' ) === -1 ) {
+                                               parameters[ key ] = parameters[ key ].join( '|' );
+                                       } else {
+                                               parameters[ key ] = '\x1f' + parameters[ key ].join( '\x1f' );
+                                       }
                                }
                                // Boolean values are only false when not given at all
                                if ( parameters[ key ] === false || parameters[ key ] === undefined ) {
                                delete parameters.token;
                        }
 
-                       this.preprocessParameters( parameters );
+                       this.preprocessParameters( parameters, this.defaults.useUS );
 
                        // If multipart/form-data has been requested and emulation is possible, emulate it
                        if (
                 */
                postWithToken: function ( tokenType, params, ajaxOptions ) {
                        var api = this,
-                               abortable;
+                               abortedPromise = $.Deferred().reject( 'http',
+                                       { textStatus: 'abort', exception: 'abort' } ).promise(),
+                               abortable,
+                               aborted;
 
-                       return ( abortable = api.getToken( tokenType, params.assert ) ).then( function ( token ) {
+                       return api.getToken( tokenType, params.assert ).then( function ( token ) {
                                params.token = token;
+                               // Request was aborted while token request was running, but we
+                               // don't want to unnecessarily abort token requests, so abort
+                               // a fake request instead
+                               if ( aborted ) {
+                                       return abortedPromise;
+                               }
+
                                return ( abortable = api.post( params, ajaxOptions ) ).then(
                                        // If no error, return to caller as-is
                                        null,
                                                        api.badToken( tokenType );
                                                        // Try again, once
                                                        params.token = undefined;
-                                                       return ( abortable = api.getToken( tokenType, params.assert ) ).then( function ( token ) {
+                                                       abortable = null;
+                                                       return api.getToken( tokenType, params.assert ).then( function ( token ) {
                                                                params.token = token;
-                                                               return ( abortable = api.post( params, ajaxOptions ) ).promise();
+                                                               if ( aborted ) {
+                                                                       return abortedPromise;
+                                                               }
+
+                                                               return ( abortable = api.post( params, ajaxOptions ) );
                                                        } );
                                                }
 
                                        }
                                );
                        } ).promise( { abort: function () {
-                               abortable.abort();
+                               if ( abortable ) {
+                                       abortable.abort();
+                               } else {
+                                       aborted = true;
+                               }
                        } } );
                },
 
index df21eb2..a1a4999 100644 (file)
                 * Get a set of messages.
                 *
                 * @param {Array} messages Messages to retrieve
+                * @param {Object} [options] Additional parameters for the API call
                 * @return {jQuery.Promise}
                 */
-               getMessages: function ( messages ) {
-                       return this.get( {
+               getMessages: function ( messages, options ) {
+                       options = options || {};
+                       return this.get( $.extend( {
                                action: 'query',
                                meta: 'allmessages',
                                ammessages: messages,
                                amlang: mw.config.get( 'wgUserLanguage' ),
                                formatversion: 2
-                       } ).then( function ( data ) {
+                       }, options ) ).then( function ( data ) {
                                var result = {};
 
                                $.each( data.query.allmessages, function ( i, obj ) {
                },
 
                /**
-                * Loads a set of mesages and add them to mw.messages.
+                * Loads a set of messages and add them to mw.messages.
                 *
                 * @param {Array} messages Messages to retrieve
+                * @param {Object} [options] Additional parameters for the API call
                 * @return {jQuery.Promise}
                 */
-               loadMessages: function ( messages ) {
-                       return this.getMessages( messages ).then( $.proxy( mw.messages, 'set' ) );
+               loadMessages: function ( messages, options ) {
+                       return this.getMessages( messages, options ).then( $.proxy( mw.messages, 'set' ) );
+               },
+
+               /**
+                * Loads a set of messages and add them to mw.messages. Only messages that are not already known
+                * are loaded. If all messages are known, the returned promise is resolved immediately.
+                *
+                * @param {Array} messages Messages to retrieve
+                * @param {Object} [options] Additional parameters for the API call
+                * @return {jQuery.Promise}
+                */
+               loadMessagesIfMissing: function ( messages, options ) {
+                       var missing = messages.filter( function ( msg ) {
+                               return !mw.message( msg ).exists();
+                       } );
+
+                       if ( missing.length === 0 ) {
+                               return $.Deferred().resolve();
+                       }
+
+                       return this.getMessages( missing, options ).then( $.proxy( mw.messages, 'set' ) );
                }
        } );
 
index 0af2a75..069fbbf 100644 (file)
                                value = options[ name ] === null ? null : String( options[ name ] );
 
                                // Can we bundle this option, or does it need a separate request?
-                               bundleable =
-                                       ( value === null || value.indexOf( '|' ) === -1 ) &&
-                                       ( name.indexOf( '|' ) === -1 && name.indexOf( '=' ) === -1 );
+                               if ( this.defaults.useUS ) {
+                                       bundleable = name.indexOf( '=' ) === -1;
+                               } else {
+                                       bundleable =
+                                               ( value === null || value.indexOf( '|' ) === -1 ) &&
+                                               ( name.indexOf( '|' ) === -1 && name.indexOf( '=' ) === -1 );
+                               }
 
                                if ( bundleable ) {
                                        if ( value !== null ) {
index eb2b3fc..cdc4dbf 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * @class mw.Api.plugin.rollback
- * @since 1.27
+ * @since 1.28
  */
 ( function ( mw, $ ) {
 
diff --git a/resources/src/mediawiki/htmlform/autocomplete.js b/resources/src/mediawiki/htmlform/autocomplete.js
new file mode 100644 (file)
index 0000000..8157975
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * HTMLForm enhancements:
+ * Set up autocomplete fields.
+ */
+( function ( mw, $ ) {
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               var $autocomplete = $root.find( '.mw-htmlform-autocomplete' );
+               if ( $autocomplete.length ) {
+                       mw.loader.using( 'jquery.suggestions', function () {
+                               $autocomplete.suggestions( {
+                                       fetch: function ( val ) {
+                                               var $el = $( this );
+                                               $el.suggestions( 'suggestions',
+                                                       $.grep( $el.data( 'autocomplete' ), function ( v ) {
+                                                               return v.indexOf( val ) === 0;
+                                                       } )
+                                               );
+                                       }
+                               } );
+                       } );
+               }
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/autoinfuse.js b/resources/src/mediawiki/htmlform/autoinfuse.js
new file mode 100644 (file)
index 0000000..f2e0f4d
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * HTMLForm enhancements:
+ * Infuse some OOjs UI HTMLForm fields (those which benefit from always being infused).
+ */
+( function ( mw, $ ) {
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               var $oouiNodes, modules, extraModules;
+
+               $oouiNodes = $root.find( '.mw-htmlform-field-autoinfuse' );
+               if ( $oouiNodes.length ) {
+                       // The modules are preloaded (added server-side in HTMLFormField, and the individual fields
+                       // which need extra ones), but this module doesn't depend on them. Wait until they're loaded.
+                       modules = [ 'mediawiki.htmlform.ooui' ];
+                       $oouiNodes.each( function () {
+                               var data = $( this ).data( 'mw-modules' );
+                               if ( data ) {
+                                       // We can trust this value, 'data-mw-*' attributes are banned from user content in Sanitizer
+                                       extraModules = data.split( ',' );
+                                       modules.push.apply( modules, extraModules );
+                               }
+                       } );
+                       mw.loader.using( modules ).done( function () {
+                               $oouiNodes.each( function () {
+                                       OO.ui.infuse( this );
+                               } );
+                       } );
+               }
+
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/checkmatrix.js b/resources/src/mediawiki/htmlform/checkmatrix.js
new file mode 100644 (file)
index 0000000..b825f12
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * HTMLForm enhancements:
+ * Show fancy tooltips for checkmatrix fields.
+ */
+( function ( mw ) {
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               var $matrixTooltips = $root.find( '.mw-htmlform-matrix .mw-htmlform-tooltip' );
+               if ( $matrixTooltips.length ) {
+                       mw.loader.using( 'jquery.tipsy', function () {
+                               $matrixTooltips.tipsy( { gravity: 's' } );
+                       } );
+               }
+       } );
+
+}( mediaWiki ) );
diff --git a/resources/src/mediawiki/htmlform/cloner.js b/resources/src/mediawiki/htmlform/cloner.js
new file mode 100644 (file)
index 0000000..ab81580
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * HTMLForm enhancements:
+ * Add/remove cloner clones without having to resubmit the form.
+ */
+( function ( mw, $ ) {
+
+       var cloneCounter = 0;
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               $root.find( '.mw-htmlform-cloner-delete-button' ).filter( ':input' ).click( function ( ev ) {
+                       ev.preventDefault();
+                       $( this ).closest( 'li.mw-htmlform-cloner-li' ).remove();
+               } );
+
+               $root.find( '.mw-htmlform-cloner-create-button' ).filter( ':input' ).click( function ( ev ) {
+                       var $ul, $li, html;
+
+                       ev.preventDefault();
+
+                       $ul = $( this ).prev( 'ul.mw-htmlform-cloner-ul' );
+
+                       html = $ul.data( 'template' ).replace(
+                               new RegExp( mw.RegExp.escape( $ul.data( 'uniqueId' ) ), 'g' ),
+                               'clone' + ( ++cloneCounter )
+                       );
+
+                       $li = $( '<li>' )
+                               .addClass( 'mw-htmlform-cloner-li' )
+                               .html( html )
+                               .appendTo( $ul );
+
+                       mw.hook( 'htmlform.enhance' ).fire( $li );
+               } );
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/hide-if.js b/resources/src/mediawiki/htmlform/hide-if.js
new file mode 100644 (file)
index 0000000..0fbbcbe
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * HTMLForm enhancements:
+ * Set up 'hide-if' behaviors for form fields that have them.
+ */
+( function ( mw, $ ) {
+
+       /*jshint -W024*/
+
+       /**
+        * Helper function for hide-if to find the nearby form field.
+        *
+        * Find the closest match for the given name, "closest" being the minimum
+        * level of parents to go to find a form field matching the given name or
+        * ending in array keys matching the given name (e.g. "baz" matches
+        * "foo[bar][baz]").
+        *
+        * @ignore
+        * @private
+        * @param {jQuery} $el
+        * @param {string} name
+        * @return {jQuery|OO.ui.Widget|null}
+        */
+       function hideIfGetField( $el, name ) {
+               var $found, $p, $widget,
+                       suffix = name.replace( /^([^\[]+)/, '[$1]' );
+
+               function nameFilter() {
+                       return this.name === name ||
+                               ( this.name === ( 'wp' + name ) ) ||
+                               this.name.slice( -suffix.length ) === suffix;
+               }
+
+               for ( $p = $el.parent(); $p.length > 0; $p = $p.parent() ) {
+                       $found = $p.find( '[name]' ).filter( nameFilter );
+                       if ( $found.length ) {
+                               $widget = $found.closest( '.oo-ui-widget[data-ooui]' );
+                               if ( $widget.length ) {
+                                       return OO.ui.Widget.static.infuse( $widget );
+                               }
+                               return $found;
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Helper function for hide-if to return a test function and list of
+        * dependent fields for a hide-if specification.
+        *
+        * @ignore
+        * @private
+        * @param {jQuery} $el
+        * @param {Array} spec
+        * @return {Array}
+        * @return {Array} return.0 Dependent fields, array of jQuery objects or OO.ui.Widgets
+        * @return {Function} return.1 Test function
+        */
+       function hideIfParse( $el, spec ) {
+               var op, i, l, v, field, $field, fields, func, funcs, getVal;
+
+               op = spec[ 0 ];
+               l = spec.length;
+               switch ( op ) {
+                       case 'AND':
+                       case 'OR':
+                       case 'NAND':
+                       case 'NOR':
+                               funcs = [];
+                               fields = [];
+                               for ( i = 1; i < l; i++ ) {
+                                       if ( !$.isArray( spec[ i ] ) ) {
+                                               throw new Error( op + ' parameters must be arrays' );
+                                       }
+                                       v = hideIfParse( $el, spec[ i ] );
+                                       fields = fields.concat( v[ 0 ] );
+                                       funcs.push( v[ 1 ] );
+                               }
+
+                               l = funcs.length;
+                               switch ( op ) {
+                                       case 'AND':
+                                               func = function () {
+                                                       var i;
+                                                       for ( i = 0; i < l; i++ ) {
+                                                               if ( !funcs[ i ]() ) {
+                                                                       return false;
+                                                               }
+                                                       }
+                                                       return true;
+                                               };
+                                               break;
+
+                                       case 'OR':
+                                               func = function () {
+                                                       var i;
+                                                       for ( i = 0; i < l; i++ ) {
+                                                               if ( funcs[ i ]() ) {
+                                                                       return true;
+                                                               }
+                                                       }
+                                                       return false;
+                                               };
+                                               break;
+
+                                       case 'NAND':
+                                               func = function () {
+                                                       var i;
+                                                       for ( i = 0; i < l; i++ ) {
+                                                               if ( !funcs[ i ]() ) {
+                                                                       return true;
+                                                               }
+                                                       }
+                                                       return false;
+                                               };
+                                               break;
+
+                                       case 'NOR':
+                                               func = function () {
+                                                       var i;
+                                                       for ( i = 0; i < l; i++ ) {
+                                                               if ( funcs[ i ]() ) {
+                                                                       return false;
+                                                               }
+                                                       }
+                                                       return true;
+                                               };
+                                               break;
+                               }
+
+                               return [ fields, func ];
+
+                       case 'NOT':
+                               if ( l !== 2 ) {
+                                       throw new Error( 'NOT takes exactly one parameter' );
+                               }
+                               if ( !$.isArray( spec[ 1 ] ) ) {
+                                       throw new Error( 'NOT parameters must be arrays' );
+                               }
+                               v = hideIfParse( $el, spec[ 1 ] );
+                               fields = v[ 0 ];
+                               func = v[ 1 ];
+                               return [ fields, function () {
+                                       return !func();
+                               } ];
+
+                       case '===':
+                       case '!==':
+                               if ( l !== 3 ) {
+                                       throw new Error( op + ' takes exactly two parameters' );
+                               }
+                               field = hideIfGetField( $el, spec[ 1 ] );
+                               if ( !field ) {
+                                       return [ [], function () {
+                                               return false;
+                                       } ];
+                               }
+                               v = spec[ 2 ];
+
+                               if ( !( field instanceof jQuery ) ) {
+                                       // field is a OO.ui.Widget
+                                       if ( field.supports( 'isSelected' ) ) {
+                                               getVal = function () {
+                                                       var selected = field.isSelected();
+                                                       return selected ? field.getValue() : '';
+                                               };
+                                       } else {
+                                               getVal = function () {
+                                                       return field.getValue();
+                                               };
+                                       }
+                               } else {
+                                       $field = $( field );
+                                       if ( $field.prop( 'type' ) === 'radio' || $field.prop( 'type' ) === 'checkbox' ) {
+                                               getVal = function () {
+                                                       var $selected = $field.filter( ':checked' );
+                                                       return $selected.length ? $selected.val() : '';
+                                               };
+                                       } else {
+                                               getVal = function () {
+                                                       return $field.val();
+                                               };
+                                       }
+                               }
+
+                               switch ( op ) {
+                                       case '===':
+                                               func = function () {
+                                                       return getVal() === v;
+                                               };
+                                               break;
+                                       case '!==':
+                                               func = function () {
+                                                       return getVal() !== v;
+                                               };
+                                               break;
+                               }
+
+                               return [ [ field ], func ];
+
+                       default:
+                               throw new Error( 'Unrecognized operation \'' + op + '\'' );
+               }
+       }
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               $root.find( '.mw-htmlform-hide-if' ).each( function () {
+                       var v, i, fields, test, func, spec, self, modules, data,extraModules,
+                               $el = $( this );
+
+                       modules = [];
+                       if ( $el.is( '[data-ooui]' ) ) {
+                               modules.push( 'mediawiki.htmlform.ooui' );
+                               data = $el.data( 'mw-modules' );
+                               if ( data ) {
+                                       // We can trust this value, 'data-mw-*' attributes are banned from user content in Sanitizer
+                                       extraModules = data.split( ',' );
+                                       modules.push.apply( modules, extraModules );
+                               }
+                       }
+
+                       mw.loader.using( modules ).done( function () {
+                               if ( $el.is( '[data-ooui]' ) ) {
+                                       // self should be a FieldLayout that mixes in mw.htmlform.Element
+                                       self = OO.ui.FieldLayout.static.infuse( $el );
+                                       spec = self.hideIf;
+                                       // The original element has been replaced with infused one
+                                       $el = self.$element;
+                               } else {
+                                       self = $el;
+                                       spec = $el.data( 'hideIf' );
+                               }
+
+                               if ( !spec ) {
+                                       return;
+                               }
+
+                               v = hideIfParse( $el, spec );
+                               fields = v[ 0 ];
+                               test = v[ 1 ];
+                               // The .toggle() method works mostly the same for jQuery objects and OO.ui.Widget
+                               func = function () {
+                                       self.toggle( !test() );
+                               };
+                               for ( i = 0; i < fields.length; i++ ) {
+                                       // The .on() method works mostly the same for jQuery objects and OO.ui.Widget
+                                       fields[ i ].on( 'change', func );
+                               }
+                               func();
+                       } );
+               } );
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/htmlform.Element.js b/resources/src/mediawiki/htmlform/htmlform.Element.js
new file mode 100644 (file)
index 0000000..37474f6
--- /dev/null
@@ -0,0 +1,45 @@
+( function ( mw ) {
+
+       mw.htmlform = {};
+
+       /**
+        * Allows custom data specific to HTMLFormField to be set for OOjs UI forms. This picks up the
+        * extra config from a matching PHP widget (defined in HTMLFormElement.php) when constructed using
+        * OO.ui.infuse().
+        *
+        * Currently only supports passing 'hide-if' data.
+        *
+        * @ignore
+        */
+       mw.htmlform.Element = function ( config ) {
+               // Configuration initialization
+               config = config || {};
+
+               // Properties
+               this.hideIf = config.hideIf;
+
+               // Initialization
+               if ( this.hideIf ) {
+                       this.$element.addClass( 'mw-htmlform-hide-if' );
+               }
+       };
+
+       mw.htmlform.FieldLayout = function ( config ) {
+               // Parent constructor
+               mw.htmlform.FieldLayout.parent.call( this, config );
+               // Mixin constructors
+               mw.htmlform.Element.call( this, config );
+       };
+       OO.inheritClass( mw.htmlform.FieldLayout, OO.ui.FieldLayout );
+       OO.mixinClass( mw.htmlform.FieldLayout, mw.htmlform.Element );
+
+       mw.htmlform.ActionFieldLayout = function ( config ) {
+               // Parent constructor
+               mw.htmlform.ActionFieldLayout.parent.call( this, config );
+               // Mixin constructors
+               mw.htmlform.Element.call( this, config );
+       };
+       OO.inheritClass( mw.htmlform.ActionFieldLayout, OO.ui.ActionFieldLayout );
+       OO.mixinClass( mw.htmlform.ActionFieldLayout, mw.htmlform.Element );
+
+}( mediaWiki ) );
diff --git a/resources/src/mediawiki/htmlform/htmlform.js b/resources/src/mediawiki/htmlform/htmlform.js
new file mode 100644 (file)
index 0000000..19f8f3e
--- /dev/null
@@ -0,0 +1,7 @@
+( function ( mw, $ ) {
+
+       $( function () {
+               mw.hook( 'htmlform.enhance' ).fire( $( document ) );
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/images/question.png b/resources/src/mediawiki/htmlform/images/question.png
new file mode 100644 (file)
index 0000000..acce58c
Binary files /dev/null and b/resources/src/mediawiki/htmlform/images/question.png differ
diff --git a/resources/src/mediawiki/htmlform/images/question.svg b/resources/src/mediawiki/htmlform/images/question.svg
new file mode 100644 (file)
index 0000000..98fbe8d
--- /dev/null
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="21.059" height="21.06"><path fill="#575757" d="M10.529 0c-5.814 0-10.529 4.714-10.529 10.529s4.715 10.53 10.529 10.53c5.816 0 10.529-4.715 10.529-10.53s-4.712-10.529-10.529-10.529zm-.002 16.767c-.861 0-1.498-.688-1.498-1.516 0-.862.637-1.534 1.498-1.534.828 0 1.5.672 1.5 1.534 0 .827-.672 1.516-1.5 1.516zm2.137-6.512c-.723.568-1 .931-1 1.739v.5h-2.205v-.603c0-1.517.449-2.136 1.154-2.688.707-.552 1.139-.845 1.139-1.637 0-.672-.414-1.051-1.24-1.051-.707 0-1.328.189-1.982.638l-1.051-1.807c.861-.604 1.93-1.034 3.342-1.034 1.912 0 3.516 1.051 3.516 3.066-.001 1.43-.794 2.188-1.673 2.877z"/></svg>
\ No newline at end of file
diff --git a/resources/src/mediawiki/htmlform/multiselect.js b/resources/src/mediawiki/htmlform/multiselect.js
new file mode 100644 (file)
index 0000000..a8786ef
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * HTMLForm enhancements:
+ * Convert multiselect fields from checkboxes to Chosen selector when requested.
+ */
+( function ( mw, $ ) {
+
+       function addMulti( $oldContainer, $container ) {
+               var name = $oldContainer.find( 'input:first-child' ).attr( 'name' ),
+                       oldClass = ( ' ' + $oldContainer.attr( 'class' ) + ' ' ).replace( /(mw-htmlform-field-HTMLMultiSelectField|mw-chosen|mw-htmlform-dropdown)/g, '' ),
+                       $select = $( '<select>' ),
+                       dataPlaceholder = mw.message( 'htmlform-chosen-placeholder' );
+               oldClass = $.trim( oldClass );
+               $select.attr( {
+                       name: name,
+                       multiple: 'multiple',
+                       'data-placeholder': dataPlaceholder.plain(),
+                       'class': 'htmlform-chzn-select mw-input ' + oldClass
+               } );
+               $oldContainer.find( 'input' ).each( function () {
+                       var $oldInput = $( this ),
+                       checked = $oldInput.prop( 'checked' ),
+                       $option = $( '<option>' );
+                       $option.prop( 'value', $oldInput.prop( 'value' ) );
+                       if ( checked ) {
+                               $option.prop( 'selected', true );
+                       }
+                       $option.text( $oldInput.prop( 'value' ) );
+                       $select.append( $option );
+               } );
+               $container.append( $select );
+       }
+
+       function convertCheckboxesToMulti( $oldContainer, type ) {
+               var $fieldLabel = $( '<td>' ),
+               $td = $( '<td>' ),
+               $fieldLabelText = $( '<label>' ),
+               $container;
+               if ( type === 'tr' ) {
+                       addMulti( $oldContainer, $td );
+                       $container = $( '<tr>' );
+                       $container.append( $td );
+               } else if ( type === 'div' ) {
+                       $fieldLabel = $( '<div>' );
+                       $container = $( '<div>' );
+                       addMulti( $oldContainer, $container );
+               }
+               $fieldLabel.attr( 'class', 'mw-label' );
+               $fieldLabelText.text( $oldContainer.find( '.mw-label label' ).text() );
+               $fieldLabel.append( $fieldLabelText );
+               $container.prepend( $fieldLabel );
+               $oldContainer.replaceWith( $container );
+               return $container;
+       }
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               if ( $root.find( '.mw-htmlform-dropdown' ).length ) {
+                       mw.loader.using( 'jquery.chosen', function () {
+                               $root.find( '.mw-htmlform-dropdown' ).each( function () {
+                                       var type = this.nodeName.toLowerCase(),
+                                               $converted = convertCheckboxesToMulti( $( this ), type );
+                                       $converted.find( '.htmlform-chzn-select' ).chosen( { width: 'auto' } );
+                               } );
+                       } );
+               }
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/ooui.styles.css b/resources/src/mediawiki/htmlform/ooui.styles.css
new file mode 100644 (file)
index 0000000..fc0fd6e
--- /dev/null
@@ -0,0 +1,25 @@
+/* OOUIHTMLForm styles */
+
+.mw-htmlform-ooui .mw-htmlform-submit-buttons {
+       margin-top: 1em;
+}
+
+.mw-htmlform-ooui .mw-htmlform-field-HTMLCheckMatrix,
+.mw-htmlform-ooui .mw-htmlform-matrix,
+.mw-htmlform-ooui .mw-htmlform-matrix tr {
+       width: 100%;
+}
+
+.mw-htmlform-ooui .mw-htmlform-matrix tr td.first {
+       margin-right: 5%;
+       width: 39%;
+}
+
+/* Flatlist styling for PHP widgets... */
+.mw-htmlform-flatlist .oo-ui-fieldLayout-align-inline,
+/* ...and for JS widgets */
+.mw-htmlform-flatlist .oo-ui-optionWidget,
+.mw-htmlform-flatlist .oo-ui-multioptionWidget {
+       display: inline-block;
+       margin-right: 1em;
+}
diff --git a/resources/src/mediawiki/htmlform/selectandother.js b/resources/src/mediawiki/htmlform/selectandother.js
new file mode 100644 (file)
index 0000000..95227d0
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * HTMLForm enhancements:
+ * Add a dynamic max length to the reason field of SelectAndOther.
+ */
+( function ( mw, $ ) {
+
+       // cache the separator to avoid object creation on each keypress
+       var colonSeparator = mw.message( 'colon-separator' ).text();
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               // This checks the length together with the value from the select field
+               // When the reason list is changed and the bytelimit is longer than the allowed,
+               // nothing is done
+               $root
+                       .find( '.mw-htmlform-select-and-other-field' )
+                       .each( function () {
+                               var $this = $( this ),
+                                       // find the reason list
+                                       $reasonList = $root.find( '#' + $this.data( 'id-select' ) ),
+                                       // cache the current selection to avoid expensive lookup
+                                       currentValReasonList = $reasonList.val();
+
+                               $reasonList.change( function () {
+                                       currentValReasonList = $reasonList.val();
+                               } );
+
+                               $this.byteLimit( function ( input ) {
+                                       // Should be built the same as in HTMLSelectAndOtherField::loadDataFromRequest
+                                       var comment = currentValReasonList;
+                                       if ( comment === 'other' ) {
+                                               comment = input;
+                                       } else if ( input !== '' ) {
+                                               // Entry from drop down menu + additional comment
+                                               comment += colonSeparator + input;
+                                       }
+                                       return comment;
+                               } );
+                       } );
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/selectorother.js b/resources/src/mediawiki/htmlform/selectorother.js
new file mode 100644 (file)
index 0000000..66879e9
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * HTMLForm enhancements:
+ * Animate the SelectOrOther fields, to only show the text field when 'other' is selected.
+ */
+( function ( mw, $ ) {
+
+       /**
+        * @class jQuery.plugin.htmlform
+        */
+
+       /**
+        * jQuery plugin to fade or snap to visible state.
+        *
+        * @param {boolean} [instantToggle=false]
+        * @return {jQuery}
+        * @chainable
+        */
+       $.fn.goIn = function ( instantToggle ) {
+               if ( instantToggle === true ) {
+                       return this.show();
+               }
+               return this.stop( true, true ).fadeIn();
+       };
+
+       /**
+        * jQuery plugin to fade or snap to hiding state.
+        *
+        * @param {boolean} [instantToggle=false]
+        * @return {jQuery}
+        * @chainable
+        */
+       $.fn.goOut = function ( instantToggle ) {
+               if ( instantToggle === true ) {
+                       return this.hide();
+               }
+               return this.stop( true, true ).fadeOut();
+       };
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               /**
+                * @ignore
+                * @param {boolean|jQuery.Event} instant
+                */
+               function handleSelectOrOther( instant ) {
+                       var $other = $root.find( '#' + $( this ).attr( 'id' ) + '-other' );
+                       $other = $other.add( $other.siblings( 'br' ) );
+                       if ( $( this ).val() === 'other' ) {
+                               $other.goIn( instant );
+                       } else {
+                               $other.goOut( instant );
+                       }
+               }
+
+               $root
+                       .on( 'change', '.mw-htmlform-select-or-other', handleSelectOrOther )
+                       .each( function () {
+                               handleSelectOrOther.call( this, true );
+                       } );
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/styles.css b/resources/src/mediawiki/htmlform/styles.css
new file mode 100644 (file)
index 0000000..1603130
--- /dev/null
@@ -0,0 +1,49 @@
+/* HTMLForm styles */
+
+table.mw-htmlform-nolabel td.mw-label {
+       display: none;
+}
+
+.mw-htmlform-invalid-input td.mw-input input {
+       border-color: #f00;
+}
+
+.mw-htmlform-flatlist div.mw-htmlform-flatlist-item {
+       display: inline;
+       margin-right: 1em;
+       white-space: nowrap;
+}
+
+/* HTMLCheckMatrix */
+
+.mw-htmlform-matrix td {
+       padding-left: 0.5em;
+       padding-right: 0.5em;
+}
+
+tr.mw-htmlform-vertical-label td.mw-label {
+       text-align: left !important;
+}
+
+.mw-icon-question {
+       /* SVG support using a transparent gradient to guarantee cross-browser
+        * compatibility (browsers able to understand gradient syntax support also SVG).
+        * http://pauginer.tumblr.com/post/36614680636/invisible-gradient-technique */
+       background-image: url( images/question.png );
+       /* @embed */
+       background-image: linear-gradient( transparent, transparent ), url( images/question.svg );
+       background-repeat: no-repeat;
+       background-size: 13px 13px;
+       display: inline-block;
+       height: 13px;
+       width: 13px;
+       margin-left: 4px;
+}
+
+.mw-icon-question:lang(ar),
+.mw-icon-question:lang(fa),
+.mw-icon-question:lang(ur) {
+       -webkit-transform: scaleX( -1 );
+       -ms-transform: scaleX( -1 );
+       transform: scaleX( -1 );
+}
diff --git a/resources/src/mediawiki/images/question.png b/resources/src/mediawiki/images/question.png
deleted file mode 100644 (file)
index acce58c..0000000
Binary files a/resources/src/mediawiki/images/question.png and /dev/null differ
diff --git a/resources/src/mediawiki/images/question.svg b/resources/src/mediawiki/images/question.svg
deleted file mode 100644 (file)
index 98fbe8d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="21.059" height="21.06"><path fill="#575757" d="M10.529 0c-5.814 0-10.529 4.714-10.529 10.529s4.715 10.53 10.529 10.53c5.816 0 10.529-4.715 10.529-10.53s-4.712-10.529-10.529-10.529zm-.002 16.767c-.861 0-1.498-.688-1.498-1.516 0-.862.637-1.534 1.498-1.534.828 0 1.5.672 1.5 1.534 0 .827-.672 1.516-1.5 1.516zm2.137-6.512c-.723.568-1 .931-1 1.739v.5h-2.205v-.603c0-1.517.449-2.136 1.154-2.688.707-.552 1.139-.845 1.139-1.637 0-.672-.414-1.051-1.24-1.051-.707 0-1.328.189-1.982.638l-1.051-1.807c.861-.604 1.93-1.034 3.342-1.034 1.912 0 3.516 1.051 3.516 3.066-.001 1.43-.794 2.188-1.673 2.877z"/></svg>
\ No newline at end of file
index ff4d1c2..e468768 100644 (file)
                '|&#x[0-9A-Fa-f]+;'
        ),
 
-       // From MediaWikiTitleCodec.php#L225 @26fcab1f18c568a41
-       // "Clean up whitespace" in function MediaWikiTitleCodec::splitTitleString()
-       rWhitespace = /[ _\u0009\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\s]+/g,
+       // From MediaWikiTitleCodec::splitTitleString() in PHP
+       // Note that this is not equivalent to /\s/, e.g. underscore is included, tab is not included.
+       rWhitespace = /[ _\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]+/g,
+
+       // From MediaWikiTitleCodec::splitTitleString() in PHP
+       rUnicodeBidi = /[\u200E\u200F\u202A-\u202E]/g,
 
        /**
         * Slightly modified from Flinfo. Credit goes to Lupo and Flominator.
                        replace: '',
                        generalRule: true
                },
-               // Space, underscore, tab, NBSP and other unusual spaces
-               {
-                       pattern: rWhitespace,
-                       replace: ' ',
-                       generalRule: true
-               },
-               // unicode bidi override characters: Implicit, Embeds, Overrides
-               {
-                       pattern: /[\u200E\u200F\u202A-\u202E]/g,
-                       replace: '',
-                       generalRule: true
-               },
                // control characters
                {
                        pattern: /[\x00-\x1f\x7f]/g,
                },
                // slash, colon (not supported by file systems like NTFS/Windows, Mac OS 9 [:], ext4 [/])
                {
-                       pattern: /[:\/#]/g,
+                       pattern: new RegExp( '[' + mw.config.get( 'wgIllegalFileChars', '' ) + ']', 'g' ),
                        replace: '-',
                        fileRule: true
                },
                namespace = defaultNamespace === undefined ? NS_MAIN : defaultNamespace;
 
                title = title
+                       // Strip Unicode bidi override characters
+                       .replace( rUnicodeBidi, '' )
                        // Normalise whitespace to underscores and remove duplicates
-                       .replace( /[ _\s]+/g, '_' )
+                       .replace( rWhitespace, '_' )
                        // Trim underscores
                        .replace( rUnderscoreTrim, '' );
 
 
                namespace = defaultNamespace === undefined ? NS_MAIN : defaultNamespace;
 
-               // Normalise whitespace and remove duplicates
-               title = $.trim( title.replace( rWhitespace, ' ' ) );
+               // Normalise additional whitespace
+               title = $.trim( title.replace( /\s/g, ' ' ) );
 
                // Process initial colon
                if ( title !== '' && title[ 0 ] === ':' ) {
index bbd0f1b..920835f 100644 (file)
                        layout.emit( 'fileUploaded' );
                }, function () {
                        // These errors will be thrown while the user is on the info page.
-                       // Pretty sure it's impossible to get a warning other than 'stashfailed' here, which should
-                       // really be an error...
-                       var errorMessage = layout.getErrorMessageForStateDetails();
-                       deferred.reject( errorMessage );
+                       layout.getErrorMessageForStateDetails().then( function ( errorMessage ) {
+                               deferred.reject( errorMessage );
+                       } );
                }, function ( progress ) {
                        var elapsedTime = new Date() - startTime,
                                estimatedTotalTime = ( 1 / progress ) * elapsedTime,
                                deferred.resolve();
                                layout.emit( 'fileSaved', layout.upload.getImageInfo() );
                        }, function () {
-                               var errorMessage = layout.getErrorMessageForStateDetails();
-                               deferred.reject( errorMessage );
+                               layout.getErrorMessageForStateDetails().then( function ( errorMessage ) {
+                                       deferred.reject( errorMessage );
+                               } );
                        } );
                } );
 
         * state and state details.
         *
         * @protected
-        * @return {OO.ui.Error} Error to display for given state and details.
+        * @return {jQuery.Promise} A Promise that will be resolved with an OO.ui.Error.
         */
        mw.Upload.BookletLayout.prototype.getErrorMessageForStateDetails = function () {
                var message,
                if ( state === mw.Upload.State.ERROR ) {
                        if ( !error ) {
                                // If there's an 'exception' key, this might be a timeout, or other connection problem
-                               return new OO.ui.Error(
+                               return $.Deferred().resolve( new OO.ui.Error(
                                        $( '<p>' ).msg( 'api-error-unknownerror', JSON.stringify( stateDetails ) ),
                                        { recoverable: false }
-                               );
+                               ) );
                        }
 
-                       // HACK We should either have a hook here to allow TitleBlacklist to handle this, or just have
-                       // TitleBlacklist produce sane error messages that can be displayed without arcane knowledge
-                       if ( error.info === 'TitleBlacklist prevents this title from being created' ) {
-                               // HACK Apparently the only reliable way to determine whether TitleBlacklist was involved
-                               return new OO.ui.Error(
-                                       // HACK TitleBlacklist doesn't have a sensible message, this one is from UploadWizard
-                                       $( '<p>' ).msg( 'api-error-blacklisted' ),
-                                       { recoverable: false }
-                               );
+                       // Errors in this format are produced by TitleBlacklist and AbuseFilter. Perhaps other
+                       // extensions will follow this format in the future.
+                       if ( error.message ) {
+                               return this.upload.getApi()
+                                       .then( function ( api ) {
+                                               // 'amenableparser' will expand templates and parser functions server-side.
+                                               // We still do the rest of wikitext parsing here (throught jqueryMsg).
+                                               return api.loadMessagesIfMissing( [ error.message.key ], { amenableparser: true } )
+                                                       .then( function () {
+                                                               if ( !mw.message( error.message.key ).exists() ) {
+                                                                       return $.Deferred().reject();
+                                                               }
+                                                               return new OO.ui.Error(
+                                                                       $( '<p>' ).msg( error.message.key, error.message.params || [] ),
+                                                                       { recoverable: false }
+                                                               );
+                                                       } );
+                                       } )
+                                       .then( null, function () {
+                                               // We failed when loading the error message, or it doesn't actually exist, fall back
+                                               return $.Deferred().resolve( new OO.ui.Error(
+                                                       $( '<p>' ).msg( 'api-error-unknownerror', JSON.stringify( stateDetails ) ),
+                                                       { recoverable: false }
+                                               ) );
+                                       } );
                        }
 
                        if ( error.code === 'protectedpage' ) {
                                        message = mw.message( 'api-error-unknownerror', JSON.stringify( stateDetails ) );
                                }
                        }
-                       return new OO.ui.Error(
+                       return $.Deferred().resolve( new OO.ui.Error(
                                $( '<p>' ).append( message.parseDom() ),
                                { recoverable: false }
-                       );
+                       ) );
                }
 
                if ( state === mw.Upload.State.WARNING ) {
                        // of importance. For example fixing the thumbnail like file name
                        // won't help the fact that the file already exists.
                        if ( warnings.stashfailed !== undefined ) {
-                               return new OO.ui.Error(
+                               return $.Deferred().resolve( new OO.ui.Error(
                                        $( '<p>' ).msg( 'api-error-stashfailed' ),
                                        { recoverable: false }
-                               );
+                               ) );
                        } else if ( warnings.exists !== undefined ) {
-                               return new OO.ui.Error(
+                               return $.Deferred().resolve( new OO.ui.Error(
                                        $( '<p>' ).msg( 'fileexists', 'File:' + warnings.exists ),
                                        { recoverable: false }
-                               );
+                               ) );
                        } else if ( warnings[ 'exists-normalized' ] !== undefined ) {
-                               return new OO.ui.Error(
+                               return $.Deferred().resolve( new OO.ui.Error(
                                        $( '<p>' ).msg( 'fileexists', 'File:' + warnings[ 'exists-normalized' ] ),
                                        { recoverable: false }
-                               );
+                               ) );
                        } else if ( warnings[ 'page-exists' ] !== undefined ) {
-                               return new OO.ui.Error(
+                               return $.Deferred().resolve( new OO.ui.Error(
                                        $( '<p>' ).msg( 'filepageexists', 'File:' + warnings[ 'page-exists' ] ),
                                        { recoverable: false }
-                               );
+                               ) );
                        } else if ( warnings.duplicate !== undefined ) {
-                               return new OO.ui.Error(
+                               return $.Deferred().resolve( new OO.ui.Error(
                                        $( '<p>' ).msg( 'api-error-duplicate', warnings.duplicate.length ),
                                        { recoverable: false }
-                               );
+                               ) );
                        } else if ( warnings[ 'thumb-name' ] !== undefined ) {
-                               return new OO.ui.Error(
+                               return $.Deferred().resolve( new OO.ui.Error(
                                        $( '<p>' ).msg( 'filename-thumb-name' ),
                                        { recoverable: false }
-                               );
+                               ) );
                        } else if ( warnings[ 'bad-prefix' ] !== undefined ) {
-                               return new OO.ui.Error(
+                               return $.Deferred().resolve( new OO.ui.Error(
                                        $( '<p>' ).msg( 'filename-bad-prefix', warnings[ 'bad-prefix' ] ),
                                        { recoverable: false }
-                               );
+                               ) );
                        } else if ( warnings[ 'duplicate-archive' ] !== undefined ) {
-                               return new OO.ui.Error(
+                               return $.Deferred().resolve( new OO.ui.Error(
                                        $( '<p>' ).msg( 'api-error-duplicate-archive', 1 ),
                                        { recoverable: false }
-                               );
+                               ) );
                        } else if ( warnings[ 'was-deleted' ] !== undefined ) {
-                               return new OO.ui.Error(
+                               return $.Deferred().resolve( new OO.ui.Error(
                                        $( '<p>' ).msg( 'api-error-was-deleted' ),
                                        { recoverable: false }
-                               );
+                               ) );
                        } else if ( warnings.badfilename !== undefined ) {
                                // Change the name if the current name isn't acceptable
                                // TODO This might not really be the best place to do this
                                this.setFilename( warnings.badfilename );
-                               return new OO.ui.Error(
+                               return $.Deferred().resolve( new OO.ui.Error(
                                        $( '<p>' ).msg( 'badfilename', warnings.badfilename )
-                               );
+                               ) );
                        } else {
-                               return new OO.ui.Error(
+                               return $.Deferred().resolve( new OO.ui.Error(
                                        // Let's get all the help we can if we can't pin point the error
                                        $( '<p>' ).msg( 'api-error-unknown-warning', JSON.stringify( stateDetails ) ),
                                        { recoverable: false }
-                               );
+                               ) );
                        }
                }
        };
diff --git a/resources/src/mediawiki/mediawiki.content.json.css b/resources/src/mediawiki/mediawiki.content.json.css
deleted file mode 100644 (file)
index 91fa02a..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*!
- * CSS for styling HTML-formatted JSON Schema objects
- *
- * @file
- * @author Munaf Assaf <massaf@wikimedia.org>
- */
-
-.mw-json {
-       border-collapse: collapse;
-       border-spacing: 0;
-       font-style: normal;
-}
-
-.mw-json th,
-.mw-json td {
-       border: 1px solid #808080;
-       font-size: 16px;
-       padding: 0.5em 1em;
-}
-
-.mw-json .value,
-.mw-json-single-value {
-       background-color: #dcfae3;
-       font-family: monospace, monospace;
-       white-space: pre-wrap;
-}
-
-.mw-json-single-value {
-       background-color: #eee;
-}
-
-.mw-json-empty {
-       background-color: #fff;
-       font-style: italic;
-}
-
-.mw-json tr {
-       margin-bottom: 0.5em;
-       background-color: #eee;
-}
-
-.mw-json th {
-       background-color: #fff;
-       font-weight: normal;
-}
-
-.mw-json caption {
-       /* For stylistic reasons, suppress the caption of the outermost table */
-       display: none;
-}
-
-.mw-json table caption {
-       color: #808080;
-       display: inline-block;
-       font-size: 10px;
-       font-style: italic;
-       margin-bottom: 0.5em;
-       text-align: left;
-}
diff --git a/resources/src/mediawiki/mediawiki.content.json.less b/resources/src/mediawiki/mediawiki.content.json.less
new file mode 100644 (file)
index 0000000..91fa02a
--- /dev/null
@@ -0,0 +1,59 @@
+/*!
+ * CSS for styling HTML-formatted JSON Schema objects
+ *
+ * @file
+ * @author Munaf Assaf <massaf@wikimedia.org>
+ */
+
+.mw-json {
+       border-collapse: collapse;
+       border-spacing: 0;
+       font-style: normal;
+}
+
+.mw-json th,
+.mw-json td {
+       border: 1px solid #808080;
+       font-size: 16px;
+       padding: 0.5em 1em;
+}
+
+.mw-json .value,
+.mw-json-single-value {
+       background-color: #dcfae3;
+       font-family: monospace, monospace;
+       white-space: pre-wrap;
+}
+
+.mw-json-single-value {
+       background-color: #eee;
+}
+
+.mw-json-empty {
+       background-color: #fff;
+       font-style: italic;
+}
+
+.mw-json tr {
+       margin-bottom: 0.5em;
+       background-color: #eee;
+}
+
+.mw-json th {
+       background-color: #fff;
+       font-weight: normal;
+}
+
+.mw-json caption {
+       /* For stylistic reasons, suppress the caption of the outermost table */
+       display: none;
+}
+
+.mw-json table caption {
+       color: #808080;
+       display: inline-block;
+       font-size: 10px;
+       font-style: italic;
+       margin-bottom: 0.5em;
+       text-align: left;
+}
diff --git a/resources/src/mediawiki/mediawiki.debug.init.js b/resources/src/mediawiki/mediawiki.debug.init.js
deleted file mode 100644 (file)
index 0f85e80..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-jQuery( function () {
-       mediaWiki.Debug.init();
-} );
index f721009..26c74a1 100644 (file)
                }
        };
 
+       $( function () {
+               debug.init();
+       } );
+
 }( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/mediawiki.diff.styles.css b/resources/src/mediawiki/mediawiki.diff.styles.css
new file mode 100644 (file)
index 0000000..327c9c8
--- /dev/null
@@ -0,0 +1,120 @@
+/*!
+ * Diff rendering
+ */
+table.diff {
+       border: none;
+       border-spacing: 4px;
+       margin: 0;
+       width: 100%;
+       /* Ensure that colums are of equal width */
+       table-layout: fixed;
+}
+
+table.diff td {
+       padding: 0.33em 0.5em;
+}
+
+table.diff td.diff-marker {
+       /* Compensate padding for increased font-size */
+       padding: 0.25em;
+}
+
+table.diff col.diff-marker {
+       width: 2%;
+}
+
+table.diff col.diff-content {
+       width: 48%;
+}
+
+table.diff td div {
+       /* Force-wrap very long lines such as URLs or page-widening char strings */
+       word-wrap: break-word;
+}
+
+td.diff-otitle,
+td.diff-ntitle {
+       text-align: center;
+}
+
+td.diff-lineno {
+       font-weight: bold;
+}
+
+td.diff-marker {
+       text-align: right;
+       font-weight: bold;
+       font-size: 1.25em;
+       line-height: 1.2;
+}
+
+td.diff-addedline,
+td.diff-deletedline,
+td.diff-context {
+       font-size: 88%;
+       line-height: 1.6;
+       vertical-align: top;
+       white-space: -moz-pre-wrap;
+       white-space: pre-wrap;
+       border-style: solid;
+       border-width: 1px 1px 1px 4px;
+       border-radius: 0.33em;
+}
+
+td.diff-addedline {
+       border-color: #a3d3ff;
+}
+
+td.diff-deletedline {
+       border-color: #ffe49c;
+}
+
+td.diff-context {
+       background: #f9f9f9;
+       border-color: #e6e6e6;
+       color: #333;
+}
+
+.diffchange {
+       font-weight: bold;
+       text-decoration: none;
+}
+
+td.diff-addedline .diffchange,
+td.diff-deletedline .diffchange {
+       border-radius: 0.33em;
+       padding: 0.25em 0;
+}
+
+td.diff-addedline .diffchange {
+       background: #d8ecff;
+}
+
+td.diff-deletedline .diffchange {
+       background: #feeec8;
+}
+
+/* Correct user & content directionality when viewing a diff */
+.diff-currentversion-title,
+.diff {
+       direction: ltr;
+       unicode-bidi: embed;
+}
+
+/* @noflip */ .diff-contentalign-right td {
+       direction: rtl;
+       unicode-bidi: embed;
+}
+
+/* @noflip */ .diff-contentalign-left td {
+       direction: ltr;
+       unicode-bidi: embed;
+}
+
+.diff-multi,
+.diff-otitle,
+.diff-ntitle,
+.diff-lineno {
+       direction: ltr !important;
+       unicode-bidi: embed;
+}
diff --git a/resources/src/mediawiki/mediawiki.diff.styles.print.css b/resources/src/mediawiki/mediawiki.diff.styles.print.css
new file mode 100644 (file)
index 0000000..76b5c9b
--- /dev/null
@@ -0,0 +1,16 @@
+/*!
+ * Diff rendering
+ */
+td.diff-context,
+td.diff-addedline .diffchange,
+td.diff-deletedline .diffchange {
+       background-color: transparent;
+}
+
+td.diff-addedline .diffchange {
+       text-decoration: underline;
+}
+
+td.diff-deletedline .diffchange {
+       text-decoration: line-through;
+}
diff --git a/resources/src/mediawiki/mediawiki.htmlform.css b/resources/src/mediawiki/mediawiki.htmlform.css
deleted file mode 100644 (file)
index 1603130..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/* HTMLForm styles */
-
-table.mw-htmlform-nolabel td.mw-label {
-       display: none;
-}
-
-.mw-htmlform-invalid-input td.mw-input input {
-       border-color: #f00;
-}
-
-.mw-htmlform-flatlist div.mw-htmlform-flatlist-item {
-       display: inline;
-       margin-right: 1em;
-       white-space: nowrap;
-}
-
-/* HTMLCheckMatrix */
-
-.mw-htmlform-matrix td {
-       padding-left: 0.5em;
-       padding-right: 0.5em;
-}
-
-tr.mw-htmlform-vertical-label td.mw-label {
-       text-align: left !important;
-}
-
-.mw-icon-question {
-       /* SVG support using a transparent gradient to guarantee cross-browser
-        * compatibility (browsers able to understand gradient syntax support also SVG).
-        * http://pauginer.tumblr.com/post/36614680636/invisible-gradient-technique */
-       background-image: url( images/question.png );
-       /* @embed */
-       background-image: linear-gradient( transparent, transparent ), url( images/question.svg );
-       background-repeat: no-repeat;
-       background-size: 13px 13px;
-       display: inline-block;
-       height: 13px;
-       width: 13px;
-       margin-left: 4px;
-}
-
-.mw-icon-question:lang(ar),
-.mw-icon-question:lang(fa),
-.mw-icon-question:lang(ur) {
-       -webkit-transform: scaleX( -1 );
-       -ms-transform: scaleX( -1 );
-       transform: scaleX( -1 );
-}
diff --git a/resources/src/mediawiki/mediawiki.htmlform.js b/resources/src/mediawiki/mediawiki.htmlform.js
deleted file mode 100644 (file)
index 4cc7f09..0000000
+++ /dev/null
@@ -1,440 +0,0 @@
-/**
- * Utility functions for jazzing up HTMLForm elements.
- *
- * @class jQuery.plugin.htmlform
- */
-( function ( mw, $ ) {
-
-       var cloneCounter = 0;
-
-       /**
-        * Helper function for hide-if to find the nearby form field.
-        *
-        * Find the closest match for the given name, "closest" being the minimum
-        * level of parents to go to find a form field matching the given name or
-        * ending in array keys matching the given name (e.g. "baz" matches
-        * "foo[bar][baz]").
-        *
-        * @private
-        * @param {jQuery} $el
-        * @param {string} name
-        * @return {jQuery|null}
-        */
-       function hideIfGetField( $el, name ) {
-               var $found, $p,
-                       suffix = name.replace( /^([^\[]+)/, '[$1]' );
-
-               function nameFilter() {
-                       return this.name === name ||
-                               ( this.name === ( 'wp' + name ) ) ||
-                               this.name.slice( -suffix.length ) === suffix;
-               }
-
-               for ( $p = $el.parent(); $p.length > 0; $p = $p.parent() ) {
-                       $found = $p.find( '[name]' ).filter( nameFilter );
-                       if ( $found.length ) {
-                               return $found;
-                       }
-               }
-               return null;
-       }
-
-       /**
-        * Helper function for hide-if to return a test function and list of
-        * dependent fields for a hide-if specification.
-        *
-        * @private
-        * @param {jQuery} $el
-        * @param {Array} spec
-        * @return {Array}
-        * @return {jQuery} return.0 Dependent fields
-        * @return {Function} return.1 Test function
-        */
-       function hideIfParse( $el, spec ) {
-               var op, i, l, v, $field, $fields, fields, func, funcs, getVal;
-
-               op = spec[ 0 ];
-               l = spec.length;
-               switch ( op ) {
-                       case 'AND':
-                       case 'OR':
-                       case 'NAND':
-                       case 'NOR':
-                               funcs = [];
-                               fields = [];
-                               for ( i = 1; i < l; i++ ) {
-                                       if ( !$.isArray( spec[ i ] ) ) {
-                                               throw new Error( op + ' parameters must be arrays' );
-                                       }
-                                       v = hideIfParse( $el, spec[ i ] );
-                                       fields = fields.concat( v[ 0 ].toArray() );
-                                       funcs.push( v[ 1 ] );
-                               }
-                               $fields = $( fields );
-
-                               l = funcs.length;
-                               switch ( op ) {
-                                       case 'AND':
-                                               func = function () {
-                                                       var i;
-                                                       for ( i = 0; i < l; i++ ) {
-                                                               if ( !funcs[ i ]() ) {
-                                                                       return false;
-                                                               }
-                                                       }
-                                                       return true;
-                                               };
-                                               break;
-
-                                       case 'OR':
-                                               func = function () {
-                                                       var i;
-                                                       for ( i = 0; i < l; i++ ) {
-                                                               if ( funcs[ i ]() ) {
-                                                                       return true;
-                                                               }
-                                                       }
-                                                       return false;
-                                               };
-                                               break;
-
-                                       case 'NAND':
-                                               func = function () {
-                                                       var i;
-                                                       for ( i = 0; i < l; i++ ) {
-                                                               if ( !funcs[ i ]() ) {
-                                                                       return true;
-                                                               }
-                                                       }
-                                                       return false;
-                                               };
-                                               break;
-
-                                       case 'NOR':
-                                               func = function () {
-                                                       var i;
-                                                       for ( i = 0; i < l; i++ ) {
-                                                               if ( funcs[ i ]() ) {
-                                                                       return false;
-                                                               }
-                                                       }
-                                                       return true;
-                                               };
-                                               break;
-                               }
-
-                               return [ $fields, func ];
-
-                       case 'NOT':
-                               if ( l !== 2 ) {
-                                       throw new Error( 'NOT takes exactly one parameter' );
-                               }
-                               if ( !$.isArray( spec[ 1 ] ) ) {
-                                       throw new Error( 'NOT parameters must be arrays' );
-                               }
-                               v = hideIfParse( $el, spec[ 1 ] );
-                               $fields = v[ 0 ];
-                               func = v[ 1 ];
-                               return [ $fields, function () {
-                                       return !func();
-                               } ];
-
-                       case '===':
-                       case '!==':
-                               if ( l !== 3 ) {
-                                       throw new Error( op + ' takes exactly two parameters' );
-                               }
-                               $field = hideIfGetField( $el, spec[ 1 ] );
-                               if ( !$field ) {
-                                       return [ $(), function () {
-                                               return false;
-                                       } ];
-                               }
-                               v = spec[ 2 ];
-
-                               if ( $field.first().prop( 'type' ) === 'radio' ||
-                                       $field.first().prop( 'type' ) === 'checkbox'
-                               ) {
-                                       getVal = function () {
-                                               var $selected = $field.filter( ':checked' );
-                                               return $selected.length ? $selected.val() : '';
-                                       };
-                               } else {
-                                       getVal = function () {
-                                               return $field.val();
-                                       };
-                               }
-
-                               switch ( op ) {
-                                       case '===':
-                                               func = function () {
-                                                       return getVal() === v;
-                                               };
-                                               break;
-                                       case '!==':
-                                               func = function () {
-                                                       return getVal() !== v;
-                                               };
-                                               break;
-                               }
-
-                               return [ $field, func ];
-
-                       default:
-                               throw new Error( 'Unrecognized operation \'' + op + '\'' );
-               }
-       }
-
-       /**
-        * jQuery plugin to fade or snap to visible state.
-        *
-        * @param {boolean} [instantToggle=false]
-        * @return {jQuery}
-        * @chainable
-        */
-       $.fn.goIn = function ( instantToggle ) {
-               if ( instantToggle === true ) {
-                       return this.show();
-               }
-               return this.stop( true, true ).fadeIn();
-       };
-
-       /**
-        * jQuery plugin to fade or snap to hiding state.
-        *
-        * @param {boolean} [instantToggle=false]
-        * @return {jQuery}
-        * @chainable
-        */
-       $.fn.goOut = function ( instantToggle ) {
-               if ( instantToggle === true ) {
-                       return this.hide();
-               }
-               return this.stop( true, true ).fadeOut();
-       };
-
-       /**
-        * Bind a function to the jQuery object via live(), and also immediately trigger
-        * the function on the objects with an 'instant' parameter set to true.
-        *
-        * @method liveAndTestAtStart
-        * @deprecated since 1.24 Use .on() and .each() directly.
-        * @param {Function} callback
-        * @param {boolean|jQuery.Event} callback.immediate True when the event is called immediately,
-        *  an event object when triggered from an event.
-        * @chainable
-        * @return {jQuery}
-        */
-       mw.log.deprecate( $.fn, 'liveAndTestAtStart', function ( callback ) {
-               return this
-                       // Can't really migrate to .on() generically, needs knowledge of
-                       // calling code to know the correct selector. Fix callers and
-                       // get rid of this .liveAndTestAtStart() hack.
-                       .live( 'change', callback )
-                       .each( function () {
-                               callback.call( this, true );
-                       } );
-       } );
-
-       function enhance( $root ) {
-               var $matrixTooltips, $autocomplete,
-                       // cache the separator to avoid object creation on each keypress
-                       colonSeparator = mw.message( 'colon-separator' ).text();
-
-               /**
-                * @ignore
-                * @param {boolean|jQuery.Event} instant
-                */
-               function handleSelectOrOther( instant ) {
-                       var $other = $root.find( '#' + $( this ).attr( 'id' ) + '-other' );
-                       $other = $other.add( $other.siblings( 'br' ) );
-                       if ( $( this ).val() === 'other' ) {
-                               $other.goIn( instant );
-                       } else {
-                               $other.goOut( instant );
-                       }
-               }
-
-               // Animate the SelectOrOther fields, to only show the text field when
-               // 'other' is selected.
-               $root
-                       .on( 'change', '.mw-htmlform-select-or-other', handleSelectOrOther )
-                       .each( function () {
-                               handleSelectOrOther.call( this, true );
-                       } );
-
-               // Add a dynamic max length to the reason field of SelectAndOther
-               // This checks the length together with the value from the select field
-               // When the reason list is changed and the bytelimit is longer than the allowed,
-               // nothing is done
-               $root
-                       .find( '.mw-htmlform-select-and-other-field' )
-                       .each( function () {
-                               var $this = $( this ),
-                                       // find the reason list
-                                       $reasonList = $root.find( '#' + $this.data( 'id-select' ) ),
-                                       // cache the current selection to avoid expensive lookup
-                                       currentValReasonList = $reasonList.val();
-
-                               $reasonList.change( function () {
-                                       currentValReasonList = $reasonList.val();
-                               } );
-
-                               $this.byteLimit( function ( input ) {
-                                       // Should be built the same as in HTMLSelectAndOtherField::loadDataFromRequest
-                                       var comment = currentValReasonList;
-                                       if ( comment === 'other' ) {
-                                               comment = input;
-                                       } else if ( input !== '' ) {
-                                               // Entry from drop down menu + additional comment
-                                               comment += colonSeparator + input;
-                                       }
-                                       return comment;
-                               } );
-                       } );
-
-               // Set up hide-if elements
-               $root.find( '.mw-htmlform-hide-if' ).each( function () {
-                       var v, $fields, test, func,
-                               $el = $( this ),
-                               spec = $el.data( 'hideIf' );
-
-                       if ( !spec ) {
-                               return;
-                       }
-
-                       v = hideIfParse( $el, spec );
-                       $fields = v[ 0 ];
-                       test = v[ 1 ];
-                       func = function () {
-                               if ( test() ) {
-                                       $el.hide();
-                               } else {
-                                       $el.show();
-                               }
-                       };
-                       $fields.on( 'change', func );
-                       func();
-               } );
-
-               function addMulti( $oldContainer, $container ) {
-                       var name = $oldContainer.find( 'input:first-child' ).attr( 'name' ),
-                               oldClass = ( ' ' + $oldContainer.attr( 'class' ) + ' ' ).replace( /(mw-htmlform-field-HTMLMultiSelectField|mw-chosen)/g, '' ),
-                               $select = $( '<select>' ),
-                               dataPlaceholder = mw.message( 'htmlform-chosen-placeholder' );
-                       oldClass = $.trim( oldClass );
-                       $select.attr( {
-                               name: name,
-                               multiple: 'multiple',
-                               'data-placeholder': dataPlaceholder.plain(),
-                               'class': 'htmlform-chzn-select mw-input ' + oldClass
-                       } );
-                       $oldContainer.find( 'input' ).each( function () {
-                               var $oldInput = $( this ),
-                               checked = $oldInput.prop( 'checked' ),
-                               $option = $( '<option>' );
-                               $option.prop( 'value', $oldInput.prop( 'value' ) );
-                               if ( checked ) {
-                                       $option.prop( 'selected', true );
-                               }
-                               $option.text( $oldInput.prop( 'value' ) );
-                               $select.append( $option );
-                       } );
-                       $container.append( $select );
-               }
-
-               function convertCheckboxesToMulti( $oldContainer, type ) {
-                       var $fieldLabel = $( '<td>' ),
-                       $td = $( '<td>' ),
-                       $fieldLabelText = $( '<label>' ),
-                       $container;
-                       if ( type === 'tr' ) {
-                               addMulti( $oldContainer, $td );
-                               $container = $( '<tr>' );
-                               $container.append( $td );
-                       } else if ( type === 'div' ) {
-                               $fieldLabel = $( '<div>' );
-                               $container = $( '<div>' );
-                               addMulti( $oldContainer, $container );
-                       }
-                       $fieldLabel.attr( 'class', 'mw-label' );
-                       $fieldLabelText.text( $oldContainer.find( '.mw-label label' ).text() );
-                       $fieldLabel.append( $fieldLabelText );
-                       $container.prepend( $fieldLabel );
-                       $oldContainer.replaceWith( $container );
-                       return $container;
-               }
-
-               if ( $root.find( '.mw-chosen' ).length ) {
-                       mw.loader.using( 'jquery.chosen', function () {
-                               $root.find( '.mw-chosen' ).each( function () {
-                                       var type = this.nodeName.toLowerCase(),
-                                               $converted = convertCheckboxesToMulti( $( this ), type );
-                                       $converted.find( '.htmlform-chzn-select' ).chosen( { width: 'auto' } );
-                               } );
-                       } );
-               }
-
-               $matrixTooltips = $root.find( '.mw-htmlform-matrix .mw-htmlform-tooltip' );
-               if ( $matrixTooltips.length ) {
-                       mw.loader.using( 'jquery.tipsy', function () {
-                               $matrixTooltips.tipsy( { gravity: 's' } );
-                       } );
-               }
-
-               // Set up autocomplete fields
-               $autocomplete = $root.find( '.mw-htmlform-autocomplete' );
-               if ( $autocomplete.length ) {
-                       mw.loader.using( 'jquery.suggestions', function () {
-                               $autocomplete.suggestions( {
-                                       fetch: function ( val ) {
-                                               var $el = $( this );
-                                               $el.suggestions( 'suggestions',
-                                                       $.grep( $el.data( 'autocomplete' ), function ( v ) {
-                                                               return v.indexOf( val ) === 0;
-                                                       } )
-                                               );
-                                       }
-                               } );
-                       } );
-               }
-
-               // Add/remove cloner clones without having to resubmit the form
-               $root.find( '.mw-htmlform-cloner-delete-button' ).filter( ':input' ).click( function ( ev ) {
-                       ev.preventDefault();
-                       $( this ).closest( 'li.mw-htmlform-cloner-li' ).remove();
-               } );
-
-               $root.find( '.mw-htmlform-cloner-create-button' ).filter( ':input' ).click( function ( ev ) {
-                       var $ul, $li, html;
-
-                       ev.preventDefault();
-
-                       $ul = $( this ).prev( 'ul.mw-htmlform-cloner-ul' );
-
-                       html = $ul.data( 'template' ).replace(
-                               new RegExp( mw.RegExp.escape( $ul.data( 'uniqueId' ) ), 'g' ),
-                               'clone' + ( ++cloneCounter )
-                       );
-
-                       $li = $( '<li>' )
-                               .addClass( 'mw-htmlform-cloner-li' )
-                               .html( html )
-                               .appendTo( $ul );
-
-                       enhance( $li );
-               } );
-
-               mw.hook( 'htmlform.enhance' ).fire( $root );
-
-       }
-
-       $( function () {
-               enhance( $( document ) );
-       } );
-
-       /**
-        * @class jQuery
-        * @mixins jQuery.plugin.htmlform
-        */
-}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/mediawiki.htmlform.ooui.css b/resources/src/mediawiki/mediawiki.htmlform.ooui.css
deleted file mode 100644 (file)
index 5b9d88c..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/* OOUIHTMLForm styles */
-
-.mw-htmlform-ooui-wrapper {
-       margin: 1em 0;
-}
-
-.mw-htmlform-ooui .mw-htmlform-submit-buttons {
-       margin-top: 1em;
-}
-
-.mw-htmlform-ooui .mw-htmlform-field-HTMLCheckMatrix,
-.mw-htmlform-ooui .mw-htmlform-matrix,
-.mw-htmlform-ooui .mw-htmlform-matrix tr {
-       width: 100%;
-}
-
-.mw-htmlform-ooui .mw-htmlform-matrix tr td.first {
-       margin-right: 5%;
-       width: 39%;
-}
-
-.mw-htmlform-flatlist .oo-ui-optionWidget,
-.mw-htmlform-flatlist .oo-ui-multioptionWidget {
-       display: inline-block;
-       margin-right: 1em;
-}
index fdb7adf..a74aef3 100644 (file)
                                        $.extend( stats, mw.loader.store.stats );
                                        try {
                                                raw = localStorage.getItem( mw.loader.store.getStoreKey() );
+                                               stats.totalSizeInBytes =  $.byteLength( raw );
                                                stats.totalSize = humanSize( $.byteLength( raw ) );
                                        } catch ( e ) {}
                                }
index 8d42b98..db5a4fb 100644 (file)
@@ -8,7 +8,6 @@
  * @singleton
  */
 /*jshint latedef:false */
-/*global sha1 */
 ( function ( $ ) {
        'use strict';
 
                trackHandlers = [],
                trackQueue = [];
 
+       /**
+        * FNV132 hash function
+        *
+        * This function implements the 32-bit version of FNV-1.
+        * It is equivalent to hash( 'fnv132', ... ) in PHP, except
+        * its output is base 36 rather than hex.
+        * See <https://en.wikipedia.org/wiki/FNV_hash_function>
+        *
+        * @private
+        * @param {string} str String to hash
+        * @return {string} hash as an seven-character base 36 string
+        */
+       function fnv132( str ) {
+               /*jshint bitwise:false */
+               var hash = 0x811C9DC5,
+                       i;
+
+               for ( i = 0; i < str.length; i++ ) {
+                       hash += ( hash << 1 ) + ( hash << 4 ) + ( hash << 7 ) + ( hash << 8 ) + ( hash << 24 );
+                       hash ^= str.charCodeAt( i );
+               }
+
+               hash = ( hash >>> 0 ).toString( 36 );
+               while ( hash.length < 7 ) {
+                       hash = '0' + hash;
+               }
+
+               return hash;
+       }
+
        /**
         * Create an object that can be read from or written to from methods that allow
         * interaction both with single and multiple properties at once.
                        log.deprecate = !Object.defineProperty ? function ( obj, key, val ) {
                                obj[ key ] = val;
                        } : function ( obj, key, val, msg ) {
+                               /*globals Set */
                                msg = 'Use of "' + key + '" is deprecated.' + ( msg ? ( ' ' + msg ) : '' );
+                               var logged, loggedIsSet, uniqueTrace;
+                               if ( window.Set ) {
+                                       logged = new Set();
+                                       loggedIsSet = true;
+                               } else {
+                                       logged = {};
+                                       loggedIsSet = false;
+                               }
+                               uniqueTrace = function () {
+                                       var trace = new Error().stack;
+                                       if ( loggedIsSet ) {
+                                               if ( logged.has( trace ) ) {
+                                                       return false;
+                                               }
+                                               logged.add( trace );
+                                               return true;
+                                       } else {
+                                               if ( logged.hasOwnProperty( trace ) ) {
+                                                       return false;
+                                               }
+                                               logged[ trace ] = 1;
+                                               return true;
+                                       }
+                               };
                                Object.defineProperty( obj, key, {
                                        configurable: true,
                                        enumerable: true,
                                        get: function () {
-                                               mw.track( 'mw.deprecate', key );
-                                               mw.log.warn( msg );
+                                               if ( uniqueTrace() ) {
+                                                       mw.track( 'mw.deprecate', key );
+                                                       mw.log.warn( msg );
+                                               }
                                                return val;
                                        },
                                        set: function ( newVal ) {
-                                               mw.track( 'mw.deprecate', key );
-                                               mw.log.warn( msg );
+                                               if ( uniqueTrace() ) {
+                                                       mw.track( 'mw.deprecate', key );
+                                                       mw.log.warn( msg );
+                                               }
                                                val = newVal;
                                        }
                                } );
                         * State machine:
                         *
                         * - `registered`:
-                        *    The module is known to the system but not yet requested.
+                        *    The module is known to the system but not yet required.
                         *    Meta data is registered via mw.loader#register. Calls to that method are
                         *    generated server-side by the startup module.
                         * - `loading`:
-                        *    The module is requested through mw.loader (either directly or as dependency of
-                        *    another module). The client will be fetching module contents from the server.
+                        *    The module was required through mw.loader (either directly or as dependency of
+                        *    another module). The client will fetch module contents from the server.
                         *    The contents are then stashed in the registry via mw.loader#implement.
                         * - `loaded`:
-                        *    The module has been requested from the server and stashed via mw.loader#implement.
-                        *    If the module has no more dependencies in-fight, the module will be executed
-                        *    right away. Otherwise execution is deferred, controlled via #handlePending.
+                        *    The module has been loaded from the server and stashed via mw.loader#implement.
+                        *    If the module has no more dependencies in-flight, the module will be executed
+                        *    immediately. Otherwise execution is deferred, controlled via #handlePending.
                         * - `executing`:
                         *    The module is being executed.
                         * - `ready`:
                                //
                                sources = {},
 
-                               // List of modules which will be loaded as when ready
-                               batch = [],
-
-                               // Pending queueModuleScript() requests
+                               // For queueModuleScript()
                                handlingPendingRequests = false,
                                pendingRequests = [],
 
                                /**
                                 * List of callback jobs waiting for modules to be ready.
                                 *
-                                * Jobs are created by #request() and run by #handlePending().
+                                * Jobs are created by #enqueue() and run by #handlePending().
                                 *
                                 * Typically when a job is created for a module, the job's dependencies contain
-                                * both the module being requested and all its recursive dependencies.
+                                * both the required module and all its recursive dependencies.
                                 *
                                 * Format:
                                 *
                                cssBuffer = '',
                                cssBufferTimer = null,
                                cssCallbacks = $.Callbacks(),
-                               isIE9 = document.documentMode === 9;
+                               isIE9 = document.documentMode === 9,
+                               rAF = window.requestAnimationFrame || setTimeout;
 
                        function getMarker() {
                                if ( !marker ) {
                         * The CSS will be appended to an existing ResourceLoader-created `<style>` tag
                         * or create a new one based on whether the given `cssText` is safe for extension.
                         *
+                        * @private
                         * @param {string} [cssText=cssBuffer] If called without cssText,
                         *  the internal buffer will be inserted instead.
                         * @param {Function} [callback]
                                        if ( !cssBuffer || cssText.slice( 0, '@import'.length ) !== '@import' ) {
                                                // Linebreak for somewhat distinguishable sections
                                                cssBuffer += '\n' + cssText;
-                                               // TODO: Using requestAnimationFrame would perform better by not injecting
-                                               // styles while the browser is busy painting.
                                                if ( !cssBufferTimer ) {
-                                                       cssBufferTimer = setTimeout( function () {
+                                                       cssBufferTimer = rAF( function () {
+                                                               // Wrap in anonymous function that takes no arguments
                                                                // Support: Firefox < 13
                                                                // Firefox 12 has non-standard behaviour of passing a number
                                                                // as first argument to a setTimeout callback.
                        }
 
                        /**
-                        * @since 1.26
+                        * @private
                         * @param {Array} modules List of module names
                         * @return {string} Hash of concatenated version hashes.
                         */
                                var hashes = $.map( modules, function ( module ) {
                                        return registry[ module ].version;
                                } );
-                               // Trim for consistency with server-side ResourceLoader::makeHash. It also helps
-                               // save precious space in the limited query string. Otherwise modules are more
-                               // likely to require multiple HTTP requests.
-                               return sha1( hashes.join( '' ) ).slice( 0, 12 );
+                               return fnv132( hashes.join( '' ) );
                        }
 
                        /**
                                                j -= 1;
                                                try {
                                                        if ( hasErrors ) {
-                                                               if ( $.isFunction( job.error ) ) {
+                                                               if ( typeof job.error === 'function' ) {
                                                                        job.error( new Error( 'Module ' + module + ' has failed dependencies' ), [ module ] );
                                                                }
                                                        } else {
-                                                               if ( $.isFunction( job.ready ) ) {
+                                                               if ( typeof job.ready === 'function' ) {
                                                                        job.ready();
                                                                }
                                                        }
                                }
 
                                // Resolves dynamic loader function and replaces it with its own results
-                               if ( $.isFunction( registry[ module ].dependencies ) ) {
+                               if ( typeof registry[ module ].dependencies === 'function' ) {
                                        registry[ module ].dependencies = registry[ module ].dependencies();
                                        // Ensures the module's dependencies are always in an array
                                        if ( typeof registry[ module ].dependencies !== 'object' ) {
                                        // Force jQuery behaviour to be for crossDomain. Otherwise jQuery would use
                                        // XHR for a same domain request instead of <script>, which changes the request
                                        // headers (potentially missing a cache hit), and reduces caching in general
-                                       // since browsers cache XHR much less (if at all). And XHR means we retreive
+                                       // since browsers cache XHR much less (if at all). And XHR means we retrieve
                                        // text, so we'd need to $.globalEval, which then messes up line numbers.
                                        crossDomain: true,
                                        cache: true
                                                legacyWait.always( function () {
                                                        if ( $.isArray( script ) ) {
                                                                nestedAddScript( script, markModuleReady, 0 );
-                                                       } else if ( $.isFunction( script ) ) {
+                                                       } else if ( typeof script === 'function' ) {
                                                                // Pass jQuery twice so that the signature of the closure which wraps
                                                                // the script can bind both '$' and 'jQuery'.
                                                                script( $, $, mw.loader.require, registry[ module ].module );
                        }
 
                        /**
-                        * Adds all dependencies to the queue with optional callbacks to be run
-                        * when the dependencies are ready or fail
+                        * Add one or more modules to the module load queue.
+                        *
+                        * See also #work().
                         *
                         * @private
                         * @param {string|string[]} dependencies Module name or array of string module names
                         * @param {Function} [ready] Callback to execute when all dependencies are ready
                         * @param {Function} [error] Callback to execute when any dependency fails
                         */
-                       function request( dependencies, ready, error ) {
+                       function enqueue( dependencies, ready, error ) {
                                // Allow calling by single module name
                                if ( typeof dependencies === 'string' ) {
                                        dependencies = [ dependencies ];
                        }
 
                        /**
-                        * Load modules from load.php
+                        * Make a network request to load modules from the server.
                         *
                         * @private
                         * @param {Object} moduleMap Module map, see #buildModulesString
                         * @param {string} sourceLoadScript URL of load.php
                         */
                        function doRequest( moduleMap, currReqBase, sourceLoadScript ) {
-                               var request = $.extend(
+                               var query = $.extend(
                                        { modules: buildModulesString( moduleMap ) },
                                        currReqBase
                                );
-                               request = sortQuery( request );
-                               addScript( sourceLoadScript + '?' + $.param( request ) );
+                               query = sortQuery( query );
+                               addScript( sourceLoadScript + '?' + $.param( query ) );
                        }
 
                        /**
                         * size of the startup module. This function changes those dependency lists back to
                         * arrays of strings.
                         *
+                        * @private
                         * @param {Array} modules Modules array
                         */
                        function resolveIndexedDependencies( modules ) {
-                               $.each( modules, function ( idx, module ) {
-                                       if ( module[ 2 ] ) {
-                                               module[ 2 ] = $.map( module[ 2 ], function ( dep ) {
-                                                       return typeof dep === 'number' ? modules[ dep ][ 0 ] : dep;
-                                               } );
+                               var i, j, deps;
+                               function resolveIndex( dep ) {
+                                       return typeof dep === 'number' ? modules[ dep ][ 0 ] : dep;
+                               }
+                               for ( i = 0; i < modules.length; i++ ) {
+                                       deps = modules[ i ][ 2 ];
+                                       if ( deps ) {
+                                               for ( j = 0; j < deps.length; j++ ) {
+                                                       deps[ j ] = resolveIndex( deps[ j ] );
+                                               }
+                                       }
+                               }
+                       }
+
+                       /**
+                        * Create network requests for a batch of modules.
+                        *
+                        * This is an internal method for #work(). This must not be called directly
+                        * unless the modules are already registered, and no request is in progress,
+                        * and the module state has already been set to `loading`.
+                        *
+                        * @private
+                        * @param {string[]} batch
+                        */
+                       function batchRequest( batch ) {
+                               var reqBase, splits, maxQueryLength, b, bSource, bGroup, bSourceGroup,
+                                       source, group, i, modules, sourceLoadScript,
+                                       currReqBase, currReqBaseLength, moduleMap, l,
+                                       lastDotIndex, prefix, suffix, bytesAdded;
+
+                               if ( !batch.length ) {
+                                       return;
+                               }
+
+                               // Always order modules alphabetically to help reduce cache
+                               // misses for otherwise identical content.
+                               batch.sort();
+
+                               // Build a list of query parameters common to all requests
+                               reqBase = {
+                                       skin: mw.config.get( 'skin' ),
+                                       lang: mw.config.get( 'wgUserLanguage' ),
+                                       debug: mw.config.get( 'debug' )
+                               };
+                               maxQueryLength = mw.config.get( 'wgResourceLoaderMaxQueryLength', 2000 );
+
+                               // Split module list by source and by group.
+                               splits = {};
+                               for ( b = 0; b < batch.length; b++ ) {
+                                       bSource = registry[ batch[ b ] ].source;
+                                       bGroup = registry[ batch[ b ] ].group;
+                                       if ( !hasOwn.call( splits, bSource ) ) {
+                                               splits[ bSource ] = {};
+                                       }
+                                       if ( !hasOwn.call( splits[ bSource ], bGroup ) ) {
+                                               splits[ bSource ][ bGroup ] = [];
+                                       }
+                                       bSourceGroup = splits[ bSource ][ bGroup ];
+                                       bSourceGroup.push( batch[ b ] );
+                               }
+
+                               for ( source in splits ) {
+
+                                       sourceLoadScript = sources[ source ];
+
+                                       for ( group in splits[ source ] ) {
+
+                                               // Cache access to currently selected list of
+                                               // modules for this group from this source.
+                                               modules = splits[ source ][ group ];
+
+                                               currReqBase = $.extend( {
+                                                       version: getCombinedVersion( modules )
+                                               }, reqBase );
+                                               // For user modules append a user name to the query string.
+                                               if ( group === 'user' && mw.config.get( 'wgUserName' ) !== null ) {
+                                                       currReqBase.user = mw.config.get( 'wgUserName' );
+                                               }
+                                               currReqBaseLength = $.param( currReqBase ).length;
+                                               // We may need to split up the request to honor the query string length limit,
+                                               // so build it piece by piece.
+                                               l = currReqBaseLength + 9; // '&modules='.length == 9
+
+                                               moduleMap = {}; // { prefix: [ suffixes ] }
+
+                                               for ( i = 0; i < modules.length; i++ ) {
+                                                       // Determine how many bytes this module would add to the query string
+                                                       lastDotIndex = modules[ i ].lastIndexOf( '.' );
+
+                                                       // If lastDotIndex is -1, substr() returns an empty string
+                                                       prefix = modules[ i ].substr( 0, lastDotIndex );
+                                                       suffix = modules[ i ].slice( lastDotIndex + 1 );
+
+                                                       bytesAdded = hasOwn.call( moduleMap, prefix )
+                                                               ? suffix.length + 3 // '%2C'.length == 3
+                                                               : modules[ i ].length + 3; // '%7C'.length == 3
+
+                                                       // If the url would become too long, create a new one,
+                                                       // but don't create empty requests
+                                                       if ( maxQueryLength > 0 && !$.isEmptyObject( moduleMap ) && l + bytesAdded > maxQueryLength ) {
+                                                               // This url would become too long, create a new one, and start the old one
+                                                               doRequest( moduleMap, currReqBase, sourceLoadScript );
+                                                               moduleMap = {};
+                                                               l = currReqBaseLength + 9;
+                                                               mw.track( 'resourceloader.splitRequest', { maxQueryLength: maxQueryLength } );
+                                                       }
+                                                       if ( !hasOwn.call( moduleMap, prefix ) ) {
+                                                               moduleMap[ prefix ] = [];
+                                                       }
+                                                       moduleMap[ prefix ].push( suffix );
+                                                       l += bytesAdded;
+                                               }
+                                               // If there's anything left in moduleMap, request that too
+                                               if ( !$.isEmptyObject( moduleMap ) ) {
+                                                       doRequest( moduleMap, currReqBase, sourceLoadScript );
+                                               }
+                                       }
+                               }
+                       }
+
+                       /**
+                        * Evaluate a batch of load.php responses retrieved from mw.loader.store.
+                        *
+                        * @private
+                        * @param {string[]} implementations Array containing pieces of JavaScript code in the
+                        *  form of calls to mw.loader#implement().
+                        * @param {Function} cb Callback in case of failure
+                        * @param {Error} cb.err
+                        */
+                       function batchEval( implementations, cb ) {
+                               if ( !implementations.length ) {
+                                       return;
+                               }
+                               mw.requestIdleCallback( function iterate( deadline ) {
+                                       while ( implementations[ 0 ] && deadline.timeRemaining() > 5 ) {
+                                               try {
+                                                       $.globalEval( implementations.shift() );
+                                               } catch ( err ) {
+                                                       cb( err );
+                                                       return;
+                                               }
+                                       }
+                                       if ( implementations[ 0 ] ) {
+                                               mw.requestIdleCallback( iterate );
                                        }
                                } );
                        }
                                addStyleTag: newStyleTag,
 
                                /**
-                                * Batch-request queued dependencies from the server.
+                                * Start loading of all queued module dependencies.
+                                *
+                                * @protected
                                 */
                                work: function () {
-                                       var     reqBase, splits, maxQueryLength, q, b, bSource, bGroup, bSourceGroup,
-                                               source, concatSource, origBatch, group, i, modules, sourceLoadScript,
-                                               currReqBase, currReqBaseLength, moduleMap, l,
-                                               lastDotIndex, prefix, suffix, bytesAdded;
-
-                                       // Build a list of request parameters common to all requests.
-                                       reqBase = {
-                                               skin: mw.config.get( 'skin' ),
-                                               lang: mw.config.get( 'wgUserLanguage' ),
-                                               debug: mw.config.get( 'debug' )
-                                       };
-                                       // Split module batch by source and by group.
-                                       splits = {};
-                                       maxQueryLength = mw.config.get( 'wgResourceLoaderMaxQueryLength', 2000 );
+                                       var q, batch, implementations, sourceModules;
+
+                                       batch = [];
 
                                        // Appends a list of modules from the queue to the batch
                                        for ( q = 0; q < queue.length; q++ ) {
-                                               // Only request modules which are registered
+                                               // Only load modules which are registered
                                                if ( hasOwn.call( registry, queue[ q ] ) && registry[ queue[ q ] ].state === 'registered' ) {
                                                        // Prevent duplicate entries
                                                        if ( $.inArray( queue[ q ], batch ) === -1 ) {
                                                }
                                        }
 
+                                       // Now that the queue has been processed into a batch, clear the queue.
+                                       // This MUST happen before we initiate any eval or network request. Otherwise,
+                                       // it is possible for a cached script to instantly trigger the same work queue
+                                       // again; all before we've cleared it causing each request to include modules
+                                       // which are already loaded.
+                                       queue = [];
+
+                                       if ( !batch.length ) {
+                                               return;
+                                       }
+
                                        mw.loader.store.init();
                                        if ( mw.loader.store.enabled ) {
-                                               concatSource = [];
-                                               origBatch = batch;
+                                               implementations = [];
+                                               sourceModules = [];
                                                batch = $.grep( batch, function ( module ) {
-                                                       var source = mw.loader.store.get( module );
-                                                       if ( source ) {
-                                                               concatSource.push( source );
+                                                       var implementation = mw.loader.store.get( module );
+                                                       if ( implementation ) {
+                                                               implementations.push( implementation );
+                                                               sourceModules.push( module );
                                                                return false;
                                                        }
                                                        return true;
                                                } );
-                                               try {
-                                                       $.globalEval( concatSource.join( ';' ) );
-                                               } catch ( err ) {
+                                               batchEval( implementations, function ( err ) {
                                                        // Not good, the cached mw.loader.implement calls failed! This should
                                                        // never happen, barring ResourceLoader bugs, browser bugs and PEBKACs.
                                                        // Depending on how corrupt the string is, it is likely that some
                                                        // modules' implement() succeeded while the ones after the error will
                                                        // never run and leave their modules in the 'loading' state forever.
-
                                                        // Since this is an error not caused by an individual module but by
                                                        // something that infected the implement call itself, don't take any
                                                        // risks and clear everything in this cache.
                                                        mw.loader.store.clear();
-                                                       // Re-add the ones still pending back to the batch and let the server
-                                                       // repopulate these modules to the cache.
-                                                       // This means that at most one module will be useless (the one that had
-                                                       // the error) instead of all of them.
                                                        mw.track( 'resourceloader.exception', { exception: err, source: 'store-eval' } );
-                                                       origBatch = $.grep( origBatch, function ( module ) {
+
+                                                       // Re-add the failed ones that are still pending back to the batch
+                                                       var failed = $.grep( sourceModules, function ( module ) {
                                                                return registry[ module ].state === 'loading';
                                                        } );
-                                                       batch = batch.concat( origBatch );
-                                               }
-                                       }
-
-                                       // Early exit if there's nothing to load...
-                                       if ( !batch.length ) {
-                                               return;
-                                       }
-
-                                       // The queue has been processed into the batch, clear up the queue.
-                                       queue = [];
-
-                                       // Always order modules alphabetically to help reduce cache
-                                       // misses for otherwise identical content.
-                                       batch.sort();
-
-                                       // Split batch by source and by group.
-                                       for ( b = 0; b < batch.length; b++ ) {
-                                               bSource = registry[ batch[ b ] ].source;
-                                               bGroup = registry[ batch[ b ] ].group;
-                                               if ( !hasOwn.call( splits, bSource ) ) {
-                                                       splits[ bSource ] = {};
-                                               }
-                                               if ( !hasOwn.call( splits[ bSource ], bGroup ) ) {
-                                                       splits[ bSource ][ bGroup ] = [];
-                                               }
-                                               bSourceGroup = splits[ bSource ][ bGroup ];
-                                               bSourceGroup.push( batch[ b ] );
+                                                       batchRequest( failed );
+                                               } );
                                        }
 
-                                       // Clear the batch - this MUST happen before we append any
-                                       // script elements to the body or it's possible that a script
-                                       // will be locally cached, instantly load, and work the batch
-                                       // again, all before we've cleared it causing each request to
-                                       // include modules which are already loaded.
-                                       batch = [];
-
-                                       for ( source in splits ) {
-
-                                               sourceLoadScript = sources[ source ];
-
-                                               for ( group in splits[ source ] ) {
-
-                                                       // Cache access to currently selected list of
-                                                       // modules for this group from this source.
-                                                       modules = splits[ source ][ group ];
-
-                                                       currReqBase = $.extend( {
-                                                               version: getCombinedVersion( modules )
-                                                       }, reqBase );
-                                                       // For user modules append a user name to the request.
-                                                       if ( group === 'user' && mw.config.get( 'wgUserName' ) !== null ) {
-                                                               currReqBase.user = mw.config.get( 'wgUserName' );
-                                                       }
-                                                       currReqBaseLength = $.param( currReqBase ).length;
-                                                       // We may need to split up the request to honor the query string length limit,
-                                                       // so build it piece by piece.
-                                                       l = currReqBaseLength + 9; // '&modules='.length == 9
-
-                                                       moduleMap = {}; // { prefix: [ suffixes ] }
-
-                                                       for ( i = 0; i < modules.length; i++ ) {
-                                                               // Determine how many bytes this module would add to the query string
-                                                               lastDotIndex = modules[ i ].lastIndexOf( '.' );
-
-                                                               // If lastDotIndex is -1, substr() returns an empty string
-                                                               prefix = modules[ i ].substr( 0, lastDotIndex );
-                                                               suffix = modules[ i ].slice( lastDotIndex + 1 );
-
-                                                               bytesAdded = hasOwn.call( moduleMap, prefix )
-                                                                       ? suffix.length + 3 // '%2C'.length == 3
-                                                                       : modules[ i ].length + 3; // '%7C'.length == 3
-
-                                                               // If the request would become too long, create a new one,
-                                                               // but don't create empty requests
-                                                               if ( maxQueryLength > 0 && !$.isEmptyObject( moduleMap ) && l + bytesAdded > maxQueryLength ) {
-                                                                       // This request would become too long, create a new one
-                                                                       // and fire off the old one
-                                                                       doRequest( moduleMap, currReqBase, sourceLoadScript );
-                                                                       moduleMap = {};
-                                                                       l = currReqBaseLength + 9;
-                                                                       mw.track( 'resourceloader.splitRequest', { maxQueryLength: maxQueryLength } );
-                                                               }
-                                                               if ( !hasOwn.call( moduleMap, prefix ) ) {
-                                                                       moduleMap[ prefix ] = [];
-                                                               }
-                                                               moduleMap[ prefix ].push( suffix );
-                                                               l += bytesAdded;
-                                                       }
-                                                       // If there's anything left in moduleMap, request that too
-                                                       if ( !$.isEmptyObject( moduleMap ) ) {
-                                                               doRequest( moduleMap, currReqBase, sourceLoadScript );
-                                                       }
-                                               }
-                                       }
+                                       batchRequest( batch );
                                },
 
                                /**
                                 * @param {string} [skip=null] Script body of the skip function
                                 */
                                register: function ( module, version, dependencies, group, source, skip ) {
-                                       var i;
+                                       var i, deps;
                                        // Allow multiple registration
                                        if ( typeof module === 'object' ) {
                                                resolveIndexedDependencies( module );
                                        if ( hasOwn.call( registry, module ) ) {
                                                throw new Error( 'module already registered: ' + module );
                                        }
+                                       if ( typeof dependencies === 'string' ) {
+                                               // A single module name
+                                               deps = [ dependencies ];
+                                       } else if ( typeof dependencies === 'object' || typeof dependencies === 'function' ) {
+                                               // Array of module names or a function that returns an array
+                                               deps = dependencies;
+                                       }
                                        // List the module as registered
                                        registry[ module ] = {
                                                // Exposed to execute() for mw.loader.implement() closures.
                                                        exports: {}
                                                },
                                                version: version !== undefined ? String( version ) : '',
-                                               dependencies: [],
+                                               dependencies: deps || [],
                                                group: typeof group === 'string' ? group : null,
                                                source: typeof source === 'string' ? source : 'local',
                                                state: 'registered',
                                                skip: typeof skip === 'string' ? skip : null
                                        };
-                                       if ( typeof dependencies === 'string' ) {
-                                               // Allow dependencies to be given as a single module name
-                                               registry[ module ].dependencies = [ dependencies ];
-                                       } else if ( typeof dependencies === 'object' || $.isFunction( dependencies ) ) {
-                                               // Allow dependencies to be given as an array of module names
-                                               // or a function which returns an array
-                                               registry[ module ].dependencies = dependencies;
-                                       }
                                },
 
                                /**
                                 * Implement a module given the components that make up the module.
                                 *
-                                * When #load or #using requests one or more modules, the server
+                                * When #load() or #using() requests one or more modules, the server
                                 * response contain calls to this function.
                                 *
                                 * @param {string} module Name of module
                                 * The reason css strings are not concatenated anymore is bug 31676. We now check
                                 * whether it's safe to extend the stylesheet.
                                 *
+                                * @protected
                                 * @param {Object} [messages] List of key/value pairs to be added to mw#messages.
                                 * @param {Object} [templates] List of key/value pairs to be added to mw#templates.
                                 */
                                 *         OO.compare( [ 1 ], [ 1 ] );
                                 *     } );
                                 *
-                                * @param {string|Array} dependencies Module name or array of modules names the callback
-                                *  dependends on to be ready before executing
+                                * Since MediaWiki 1.23 this also returns a promise.
+                                *
+                                * Since MediaWiki 1.28 the promise is resolved with a `require` function.
+                                *
+                                * @param {string|Array} dependencies Module name or array of modules names the
+                                *  callback depends on to be ready before executing
                                 * @param {Function} [ready] Callback to execute when all dependencies are ready
                                 * @param {Function} [error] Callback to execute if one or more dependencies failed
-                                * @return {jQuery.Promise}
-                                * @since 1.23 this returns a promise
+                                * @return {jQuery.Promise} With a `require` function
                                 */
                                using: function ( dependencies, ready, error ) {
                                        var deferred = $.Deferred();
                                        dependencies = resolve( dependencies );
                                        if ( allReady( dependencies ) ) {
                                                // Run ready immediately
-                                               deferred.resolve();
+                                               deferred.resolve( mw.loader.require );
                                        } else if ( anyFailed( dependencies ) ) {
                                                // Execute error immediately if any dependencies have errors
                                                deferred.reject(
                                                        dependencies
                                                );
                                        } else {
-                                               // Not all dependencies are ready: queue up a request
-                                               request( dependencies, deferred.resolve, deferred.reject );
+                                               // Not all dependencies are ready, add to the load queue
+                                               enqueue( dependencies, function () {
+                                                       deferred.resolve( mw.loader.require );
+                                               }, deferred.reject );
                                        }
 
                                        return deferred.promise();
                                        if ( allReady( filtered ) || anyFailed( filtered ) ) {
                                                return;
                                        }
-                                       // Since some modules are not yet ready, queue up a request.
-                                       request( filtered, undefined, undefined );
+                                       // Some modules are not yet ready, add to module load queue.
+                                       enqueue( filtered, undefined, undefined );
                                },
 
                                /**
                                        if ( !hasOwn.call( registry, module ) ) {
                                                mw.loader.register( module );
                                        }
-                                       if ( $.inArray( state, [ 'ready', 'error', 'missing' ] ) !== -1
-                                               && registry[ module ].state !== state ) {
+                                       registry[ module ].state = state;
+                                       if ( $.inArray( state, [ 'ready', 'error', 'missing' ] ) !== -1 ) {
                                                // Make sure pending modules depending on this one get executed if their
                                                // dependencies are now fulfilled!
-                                               registry[ module ].state = state;
                                                handlePending( module );
-                                       } else {
-                                               registry[ module ].state = state;
                                        }
                                },
 
                                /**
                                 * Get the exported value of a module.
                                 *
-                                * Module provide this value via their local `module.exports`.
+                                * Modules may provide this via their local `module.exports`.
                                 *
+                                * @protected
                                 * @since 1.27
-                                * @return {Array}
                                 */
                                require: function ( moduleName ) {
                                        var state = mw.loader.getState( moduleName );
 
-                                       // Only ready mudules can be required
+                                       // Only ready modules can be required
                                        if ( state !== 'ready' ) {
                                                // Module may've forgotten to declare a dependency
                                                throw new Error( 'Module "' + moduleName + '" is not loaded.' );
                                                        // Unversioned, private, or site-/user-specific
                                                        ( !descriptor.version || $.inArray( descriptor.group, [ 'private', 'user' ] ) !== -1 ) ||
                                                        // Partial descriptor
+                                                       // (e.g. skipped module, or style module with state=ready)
                                                        $.inArray( undefined, [ descriptor.script, descriptor.style,
                                                                        descriptor.messages, descriptor.templates ] ) !== -1
                                                ) {
                var loading = $.grep( mw.loader.getModuleNames(), function ( module ) {
                        return mw.loader.getState( module ) === 'loading';
                } );
-               // In order to use jQuery.when (which stops early if one of the promises got rejected)
-               // cast any loading failures into successes. We only need a callback, not the module.
-               loading = $.map( loading, function ( module ) {
-                       return mw.loader.using( module ).then( null, function () {
-                               return $.Deferred().resolve();
+               // We only need a callback, not any actual module. First try a single using()
+               // for all loading modules. If one fails, fall back to tracking each module
+               // separately via $.when(), this is expensive.
+               loading = mw.loader.using( loading ).then( null, function () {
+                       var all = $.map( loading, function ( module ) {
+                               return mw.loader.using( module ).then( null, function () {
+                                       return $.Deferred().resolve();
+                               } );
                        } );
+                       return $.when.apply( $, all );
                } );
-               $.when.apply( $, loading ).then( function () {
+               loading.then( function () {
                        mwPerformance.mark( 'mwLoadEnd' );
                        mw.hook( 'resourceloader.loadEnd' ).fire();
                } );
diff --git a/resources/src/mediawiki/mediawiki.notification.convertmessagebox.js b/resources/src/mediawiki/mediawiki.notification.convertmessagebox.js
new file mode 100644 (file)
index 0000000..5d46de6
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * Usage:
+ *
+ *     var convertmessagebox = require( 'mediawiki.notification.convertmessagebox' );
+ *
+ * @class mw.plugin.convertmessagebox
+ * @singleton
+ */
+( function ( mw, $ ) {
+       'use strict';
+
+       /**
+        * Convert a messagebox to a notification.
+        *
+        * Checks if a message box with class `.mw-notify-success`, `.mw-notify-warning`, or `.mw-notify-error`
+        * exists and converts it into a mw.Notification with the text of the element or a given message key.
+        *
+        * By default the notification will automatically hide after 5s, or when the user clicks the element.
+        * This can be overridden by setting attribute `data-mw-autohide="true"`.
+        *
+        * @param {Object} [options] Options
+        * @param {mw.Message} [options.msg] Message key (must be loaded already)
+        */
+       function convertmessagebox( options ) {
+               var $msgBox, type, autoHide, msg, notif,
+                       $successBox = $( '.mw-notify-success' ),
+                       $warningBox = $( '.mw-notify-warning' ),
+                       $errorBox = $( '.mw-notify-error' );
+
+               // If there is a message box and javascript is enabled, use a slick notification instead!
+               if ( $successBox.length ) {
+                       $msgBox = $successBox;
+                       type = 'info';
+               } else if ( $warningBox.length ) {
+                       $msgBox = $warningBox;
+                       type = 'warn';
+               } else if ( $errorBox.length ) {
+                       $msgBox = $errorBox;
+                       type = 'error';
+               } else {
+                       return;
+               }
+
+               autoHide = $msgBox.attr( 'data-mw-autohide' ) === 'true';
+
+               // If the msg param is given, use it, otherwise use the text of the successbox
+               msg = options && options.msg || $msgBox.text();
+               $msgBox.detach();
+
+               notif = mw.notification.notify( msg, { autoHide: autoHide, type: type } );
+               if ( !autoHide ) {
+                       // 'change' event not reliable!
+                       $( document ).one( 'keydown mousedown', function () {
+                               if ( notif ) {
+                                       notif.close();
+                                       notif = null;
+                               }
+                       } );
+               }
+       }
+
+       module.exports = convertmessagebox;
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/mediawiki.notification.convertmessagebox.styles.less b/resources/src/mediawiki/mediawiki.notification.convertmessagebox.styles.less
new file mode 100644 (file)
index 0000000..2371f4e
--- /dev/null
@@ -0,0 +1,7 @@
+.client-js {
+       .mw-notify-success,
+       .mw-notify-warning,
+       .mw-notify-error {
+               display: none;
+       }
+}
diff --git a/resources/src/mediawiki/mediawiki.notification.hideForPrint.css b/resources/src/mediawiki/mediawiki.notification.hideForPrint.css
deleted file mode 100644 (file)
index 4f9162e..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-.mw-notification-area {
-       display: none;
-}
diff --git a/resources/src/mediawiki/mediawiki.notification.print.css b/resources/src/mediawiki/mediawiki.notification.print.css
new file mode 100644 (file)
index 0000000..4f9162e
--- /dev/null
@@ -0,0 +1,3 @@
+.mw-notification-area {
+       display: none;
+}
diff --git a/resources/src/mediawiki/mediawiki.raggett.css b/resources/src/mediawiki/mediawiki.raggett.css
deleted file mode 100644 (file)
index 5318c18..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-.mw-empty-elt,
-.mw-empty-li {
-       display: none;
-}
index 78627fc..7bf73b6 100644 (file)
@@ -10,7 +10,8 @@
                $tocList = $toc.find( 'ul' ).eq( 0 );
 
                // Hide/show the table of contents element
-               function toggleToc() {
+               function toggleToc( e ) {
+                       e.preventDefault();
                        if ( $tocList.is( ':hidden' ) ) {
                                $tocList.slideDown( 'fast' );
                                $tocToggleLink.text( mw.msg( 'hidetoc' ) );
                        hideToc = mw.cookie.get( 'hidetoc' ) === '1';
 
                        $tocToggleLink = $( '<a href="#" id="togglelink"></a>' )
-                               .text( hideToc ? mw.msg( 'showtoc' ) : mw.msg( 'hidetoc' ) )
-                               .click( function ( e ) {
-                                       e.preventDefault();
-                                       toggleToc();
-                               } );
+                               .text( mw.msg( hideToc ? 'showtoc' : 'hidetoc' ) )
+                               .click( toggleToc );
 
                        $tocTitle.append(
                                $tocToggleLink
index 0fdd9aa..7df778f 100644 (file)
@@ -76,8 +76,8 @@
                                hexRnds[ i ] = byteToHex[ rnds[ i ] ];
                        }
 
-                       // Concatenation of two random integers with entrophy n and m
-                       // returns a string with entrophy n+m if those strings are independent
+                       // Concatenation of two random integers with entropy n and m
+                       // returns a string with entropy n+m if those strings are independent
                        return hexRnds.join( '' );
                },
 
index 2ce54e4..294b5de 100644 (file)
         */
        mw.log.deprecate( util, 'wikiGetlink', util.getUrl, 'Use mw.util.getUrl instead.' );
 
-       /**
-        * Access key prefix. Might be wrong for browsers implementing the accessKeyLabel property.
-        * @property {string} tooltipAccessKeyPrefix
-        * @deprecated since 1.24 Use the module jquery.accessKeyLabel instead.
-        */
-       mw.log.deprecate( util, 'tooltipAccessKeyPrefix', $.fn.updateTooltipAccessKeys.getAccessKeyPrefix(), 'Use jquery.accessKeyLabel instead.' );
-
-       /**
-        * Regex to match accesskey tooltips.
-        *
-        * Should match:
-        *
-        * - "[ctrl-option-x]"
-        * - "[alt-shift-x]"
-        * - "[ctrl-alt-x]"
-        * - "[ctrl-x]"
-        *
-        * The accesskey is matched in group $6.
-        *
-        * Will probably not work for browsers implementing the accessKeyLabel property.
-        *
-        * @property {RegExp} tooltipAccessKeyRegexp
-        * @deprecated since 1.24 Use the module jquery.accessKeyLabel instead.
-        */
-       mw.log.deprecate( util, 'tooltipAccessKeyRegexp', /\[(ctrl-)?(option-)?(alt-)?(shift-)?(esc-)?(.)\]$/, 'Use jquery.accessKeyLabel instead.' );
-
        /**
         * Add the appropriate prefix to the accesskey shown in the tooltip.
         *
diff --git a/resources/src/mediawiki/page/gallery-print.css b/resources/src/mediawiki/page/gallery-print.css
deleted file mode 100644 (file)
index 0c14865..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-li.gallerybox {
-       vertical-align: top;
-       display: inline-block;
-}
-
-ul.gallery, li.gallerybox {
-       zoom: 1;
-       *display: inline;
-}
-
-ul.gallery {
-       margin: 2px;
-       padding: 2px;
-       display: block;
-}
-
-li.gallerycaption {
-       font-weight: bold;
-       text-align: center;
-       display: block;
-       word-wrap: break-word;
-}
-
-li.gallerybox div.thumb {
-       text-align: center;
-       border: 1px solid #ccc;
-       margin: 2px;
-}
-
-div.gallerytext {
-       overflow: hidden;
-       font-size: 94%;
-       padding: 2px 4px;
-       word-wrap: break-word;
-}
diff --git a/resources/src/mediawiki/page/gallery-slideshow.js b/resources/src/mediawiki/page/gallery-slideshow.js
new file mode 100644 (file)
index 0000000..3b2c86e
--- /dev/null
@@ -0,0 +1,458 @@
+/*!
+ * mw.GallerySlideshow: Interface controls for the slideshow gallery
+ */
+( function ( mw, $, OO ) {
+       /**
+        * mw.GallerySlideshow encapsulates the user interface of the slideshow
+        * galleries. An object is instantiated for each `.mw-gallery-slideshow`
+        * element.
+        *
+        * @class mw.GallerySlideshow
+        * @uses mw.Title
+        * @uses mw.Api
+        * @param {jQuery} gallery The `<ul>` element of the gallery.
+        */
+       mw.GallerySlideshow = function ( gallery ) {
+               // Properties
+               this.$gallery = $( gallery );
+               this.$galleryCaption = this.$gallery.find( '.gallerycaption' );
+               this.$galleryBox = this.$gallery.find( '.gallerybox' );
+               this.$currentImage = null;
+               this.imageInfoCache = {};
+               if ( this.$gallery.parent().attr( 'id' ) !== 'mw-content-text' ) {
+                       this.$container = this.$gallery.parent();
+               }
+
+               // Initialize
+               this.drawCarousel();
+               this.setSizeRequirement();
+               this.toggleThumbnails( false );
+               this.showCurrentImage();
+
+               // Events
+               $( window ).on(
+                       'resize',
+                       OO.ui.debounce(
+                               this.setSizeRequirement.bind( this ),
+                               100
+                       )
+               );
+
+               // Disable thumbnails' link, instead show the image in the carousel
+               this.$galleryBox.on( 'click', function ( e ) {
+                       this.$currentImage = $( e.currentTarget );
+                       this.showCurrentImage();
+                       return false;
+               }.bind( this ) );
+       };
+
+       /* Properties */
+       /**
+        * @property {jQuery} $gallery The `<ul>` element of the gallery.
+        */
+
+       /**
+        * @property {jQuery} $galleryCaption The `<li>` that has the gallery caption.
+        */
+
+       /**
+        * @property {jQuery} $galleryBox Selection of `<li>` elements that have thumbnails.
+        */
+
+       /**
+        * @property {jQuery} $carousel The `<li>` elements that contains the carousel.
+        */
+
+       /**
+        * @property {jQuery} $interface The `<div>` elements that contains the interface buttons.
+        */
+
+       /**
+        * @property {jQuery} $img The `<img>` element that'll display the current image.
+        */
+
+       /**
+        * @property {jQuery} $imgLink The `<a>` element that links to the image's File page.
+        */
+
+       /**
+        * @property {jQuery} $imgCaption The `<p>` element that holds the image caption.
+        */
+
+       /**
+        * @property {jQuery} $imgContainer The `<div>` element that contains the image.
+        */
+
+       /**
+        * @property {jQuery} $currentImage The `<li>` element of the current image.
+        */
+
+       /**
+        * @property {jQuery} $container If the gallery contained in an element that is
+        *      not the main content element, then it stores that element.
+        */
+
+       /**
+        * @property {Object} imageInfoCache A key value pair of thumbnail URLs and image info.
+        */
+
+       /**
+        * @property {number} imageWidth Width of the image based on viewport size
+        */
+
+       /**
+        * @property {number} imageHeight Height of the image based on viewport size
+        *      the URLs in the required size.
+        */
+
+       /* Setup */
+       OO.initClass( mw.GallerySlideshow );
+
+       /* Methods */
+       /**
+        * Draws the carousel and the interface around it.
+        */
+       mw.GallerySlideshow.prototype.drawCarousel = function () {
+               var next, prev, toggle, interfaceElements, carouselStack;
+
+               this.$carousel = $( '<li>' ).addClass( 'gallerycarousel' );
+
+               // Buttons for the interface
+               prev = new OO.ui.ButtonWidget( {
+                       framed: false,
+                       icon: 'previous'
+               } ).on( 'click', this.prevImage.bind( this ) );
+
+               next = new OO.ui.ButtonWidget( {
+                       framed: false,
+                       icon: 'next'
+               } ).on( 'click', this.nextImage.bind( this ) );
+
+               toggle = new OO.ui.ButtonWidget( {
+                       framed: false,
+                       icon: 'imageGallery'
+               } ).on( 'click', this.toggleThumbnails.bind( this ) );
+
+               interfaceElements = new OO.ui.PanelLayout( {
+                       expanded: false,
+                       classes: [ 'mw-gallery-slideshow-buttons' ],
+                       $content: $( '<div>' ).append(
+                               prev.$element,
+                               toggle.$element,
+                               next.$element
+                       )
+               } );
+               this.$interface = interfaceElements.$element;
+
+               // Containers for the current image, caption etc.
+               this.$img = $( '<img>' );
+               this.$imgLink = $( '<a>' ).append( this.$img );
+               this.$imgCaption = $( '<p>' ).attr( 'class', 'mw-gallery-slideshow-caption' );
+               this.$imgContainer = $( '<div>' )
+                       .attr( 'class', 'mw-gallery-slideshow-img-container' )
+                       .append( this.$imgLink );
+
+               carouselStack = new OO.ui.StackLayout( {
+                       continuous: true,
+                       expanded: false,
+                       items: [
+                               interfaceElements,
+                               new OO.ui.PanelLayout( {
+                                       expanded: false,
+                                       $content: this.$imgContainer
+                               } ),
+                               new OO.ui.PanelLayout( {
+                                       expanded: false,
+                                       $content: this.$imgCaption
+                               } )
+                       ]
+               } );
+               this.$carousel.append( carouselStack.$element );
+
+               // Append below the caption or as the first element in the gallery
+               if ( this.$galleryCaption.length !== 0 ) {
+                       this.$galleryCaption.after( this.$carousel );
+               } else {
+                       this.$gallery.prepend( this.$carousel );
+               }
+       };
+
+       /**
+        * Sets the {@link #imageWidth} and {@link #imageHeight} properties
+        * based on the size of the window. Also flushes the
+        * {@link #imageInfoCache} as we'll now need URLs for a different
+        * size.
+        */
+       mw.GallerySlideshow.prototype.setSizeRequirement = function () {
+               var w, h;
+
+               if ( this.$container !== undefined ) {
+                       w = this.$container.width() * 0.9;
+                       h = ( this.$container.height() - this.getChromeHeight() ) * 0.9;
+               } else {
+                       w = this.$imgContainer.width();
+                       h = Math.min( $( window ).height() * ( 3 / 4 ), this.$imgContainer.width() ) - this.getChromeHeight();
+               }
+
+               // Only update and flush the cache if the size changed
+               if ( w !== this.imageWidth || h !== this.imageHeight ) {
+                       this.imageWidth = w;
+                       this.imageHeight = h;
+                       this.imageInfoCache = {};
+                       this.setImageSize();
+               }
+       };
+
+       /**
+        * Gets the height of the interface elements and the
+        * gallery's caption.
+        */
+       mw.GallerySlideshow.prototype.getChromeHeight = function () {
+               return this.$interface.outerHeight() + this.$galleryCaption.outerHeight();
+       };
+
+       /**
+        * Sets the height and width of {@link #$img} based on the
+        * proportion of the image and the values generated by
+        * {@link #setSizeRequirement}.
+        *
+        * @return {boolean} Whether or not the image was sized.
+        */
+       mw.GallerySlideshow.prototype.setImageSize = function () {
+               if ( this.$img === undefined || this.$thumbnail === undefined ) {
+                       return false;
+               }
+
+               // Reset height and width
+               this.$img
+                       .removeAttr( 'width' )
+                       .removeAttr( 'height' );
+
+               // Stretch image to take up the required size
+               if ( this.$thumbnail.width() > this.$thumbnail.height() ) {
+                       this.$img.attr( 'width', this.imageWidth + 'px' );
+               } else {
+                       this.$img.attr( 'height', this.imageHeight + 'px' );
+               }
+
+               // Make the image smaller in case the current image
+               // size is larger than the original file size.
+               this.getImageInfo( this.$thumbnail ).done( function ( info ) {
+                       // NOTE: There will be a jump when resizing the window
+                       // because the cache is cleared and this a new network request.
+                       if (
+                               info.thumbwidth < this.$img.width() ||
+                               info.thumbheight < this.$img.height()
+                       ) {
+                               this.$img.attr( 'width', info.thumbwidth + 'px' );
+                               this.$img.attr( 'height', info.thumbheight + 'px' );
+                       }
+               }.bind( this ) );
+
+               return true;
+       };
+
+       /**
+        * Displays the image set as {@link #$currentImage} in the carousel.
+        */
+       mw.GallerySlideshow.prototype.showCurrentImage = function () {
+               var imageLi = this.getCurrentImage(),
+                       caption = imageLi.find( '.gallerytext' );
+
+               // Highlight current thumbnail
+               this.$gallery
+                       .find( '.gallerybox.slideshow-current' )
+                       .removeClass( 'slideshow-current' );
+               imageLi.addClass( 'slideshow-current' );
+
+               // Show thumbnail stretched to the right size while the image loads
+               this.$thumbnail = imageLi.find( 'img' );
+               this.$img.attr( 'src', this.$thumbnail.attr( 'src' ) );
+               this.$imgLink.attr( 'href', imageLi.find( 'a' ).eq( 0 ).attr( 'href' ) );
+               this.setImageSize();
+
+               // Copy caption
+               this.$imgCaption
+                       .empty()
+                       .append( caption.clone() );
+
+               // Load image at the required size
+               this.loadImage( this.$thumbnail ).done( function ( info, $img ) {
+                       // Show this image to the user only if its still the current one
+                       if ( this.$thumbnail.attr( 'src' ) === $img.attr( 'src' ) ) {
+                               this.$img.attr( 'src', info.thumburl );
+                               this.setImageSize();
+
+                               // Keep the next image ready
+                               this.loadImage( this.getNextImage().find( 'img' ) );
+                       }
+               }.bind( this ) );
+       };
+
+       /**
+        * Loads the full image given the `<img>` element of the thumbnail.
+        *
+        * @param {Object} $img
+        * @return {jQuery.Promise} Resolves with the images URL and original
+        *      element once the image has loaded.
+        */
+       mw.GallerySlideshow.prototype.loadImage = function ( $img ) {
+               var img, d = $.Deferred();
+
+               this.getImageInfo( $img ).done( function ( info ) {
+                       img = new Image();
+                       img.src = info.thumburl;
+                       img.onload = function () {
+                               d.resolve( info, $img );
+                       };
+                       img.onerror = function () {
+                               d.reject();
+                       };
+               } ).fail( function () {
+                       d.reject();
+               } );
+
+               return d.promise();
+       };
+
+       /**
+        * Gets the image's info given an `<img>` element.
+        *
+        * @param {Object} $img
+        * @return {jQuery.Promise} Resolves with the image's info.
+        */
+       mw.GallerySlideshow.prototype.getImageInfo = function ( $img ) {
+               var api, title, params,
+                       imageSrc = $img.attr( 'src' );
+
+               // Reject promise if there is no thumbnail image
+               if ( $img[ 0 ] === undefined ) {
+                       return $.Deferred().reject();
+               }
+
+               if ( this.imageInfoCache[ imageSrc ] === undefined ) {
+                       api = new mw.Api();
+                       // TODO: This supports only gallery of images
+                       title = new mw.Title.newFromImg( $img );
+                       params = {
+                               action: 'query',
+                               formatversion: 2,
+                               titles: title.toString(),
+                               prop: 'imageinfo',
+                               iiprop: 'url'
+                       };
+
+                       // Check which dimension we need to request, based on
+                       // image and container proportions.
+                       if ( this.getDimensionToRequest( $img ) === 'height' ) {
+                               params.iiurlheight = this.imageHeight;
+                       } else {
+                               params.iiurlwidth = this.imageWidth;
+                       }
+
+                       this.imageInfoCache[ imageSrc ] = api.get( params ).then( function ( data ) {
+                               if ( OO.getProp( data, 'query', 'pages', 0, 'imageinfo', 0, 'thumburl' ) !== undefined ) {
+                                       return data.query.pages[ 0 ].imageinfo[ 0 ];
+                               } else {
+                                       return $.Deferred().reject();
+                               }
+                       } );
+               }
+
+               return this.imageInfoCache[ imageSrc ];
+       };
+
+       /**
+        * Given an image, the method checks whether to use the height
+        * or the width to request the larger image.
+        *
+        * @param {jQuery} $img
+        * @return {string}
+        */
+       mw.GallerySlideshow.prototype.getDimensionToRequest = function ( $img ) {
+               var ratio = $img.width() / $img.height();
+
+               if ( this.imageHeight * ratio <= this.imageWidth ) {
+                       return 'height';
+               } else {
+                       return 'width';
+               }
+       };
+
+       /**
+        * Toggles visibility of the thumbnails.
+        *
+        * @param {boolean} show Optional argument to control the state
+        */
+       mw.GallerySlideshow.prototype.toggleThumbnails = function ( show ) {
+               this.$galleryBox.toggle( show );
+               this.$carousel.toggleClass( 'mw-gallery-slideshow-thumbnails-toggled', show );
+       };
+
+       /**
+        * Getter method for {@link #$currentImage}
+        *
+        * @return {jQuery}
+        */
+       mw.GallerySlideshow.prototype.getCurrentImage = function () {
+               this.$currentImage = this.$currentImage || this.$galleryBox.eq( 0 );
+               return this.$currentImage;
+       };
+
+       /**
+        * Gets the image after the current one. Returns the first image if
+        * the current one is the last.
+        *
+        * @return {jQuery}
+        */
+       mw.GallerySlideshow.prototype.getNextImage = function () {
+               // Not the last image in the gallery
+               if ( this.$currentImage.next( '.gallerybox' )[ 0 ] !== undefined ) {
+                       return this.$currentImage.next( '.gallerybox' );
+               } else {
+                       return this.$galleryBox.eq( 0 );
+               }
+       };
+
+       /**
+        * Gets the image before the current one. Returns the last image if
+        * the current one is the first.
+        *
+        * @return {jQuery}
+        */
+       mw.GallerySlideshow.prototype.getPrevImage = function () {
+               // Not the first image in the gallery
+               if ( this.$currentImage.prev( '.gallerybox' )[ 0 ] !== undefined ) {
+                       return this.$currentImage.prev( '.gallerybox' );
+               } else {
+                       return this.$galleryBox.last();
+               }
+       };
+
+       /**
+        * Sets the {@link #$currentImage} to the next one and shows
+        * it in the carousel
+        */
+       mw.GallerySlideshow.prototype.nextImage = function () {
+               this.$currentImage = this.getNextImage();
+               this.showCurrentImage();
+       };
+
+       /**
+        * Sets the {@link #$currentImage} to the previous one and shows
+        * it in the carousel
+        */
+       mw.GallerySlideshow.prototype.prevImage = function () {
+               this.$currentImage = this.getPrevImage();
+               this.showCurrentImage();
+       };
+
+       // Bootstrap all slideshow galleries
+       $( function () {
+               $( '.mw-gallery-slideshow' ).each( function () {
+                       /*jshint -W031 */
+                       new mw.GallerySlideshow( this );
+                       /*jshint +W031 */
+               } );
+       } );
+}( mediaWiki, jQuery, OO ) );
index 4d43e6a..474d541 100644 (file)
@@ -1,6 +1,6 @@
 /* Galleries */
 /* These display attributes look nonsensical, but are needed to support IE and FF2 */
-/* Don't forget to update gallery-print.css */
+/* Don't forget to update gallery.print.css */
 li.gallerybox {
        vertical-align: top;
        display: -moz-inline-box;
@@ -45,6 +45,16 @@ div.gallerytext {
        word-wrap: break-word;
 }
 
+.galleryfilename {
+       display: block;
+}
+
+.galleryfilename-truncate {
+       white-space: nowrap;
+       overflow: hidden;
+       text-overflow: ellipsis;
+}
+
 /* new gallery stuff */
 ul.mw-gallery-nolines li.gallerybox div.thumb {
        background-color: transparent;
@@ -90,12 +100,83 @@ ul.mw-gallery-packed-hover li.gallerybox.mw-gallery-focused div.gallerytextwrapp
        bottom: 0;
        left: 0; /* Needed for IE */
        height: auto;
+       max-height: 40%;
+       overflow: hidden;
        font-weight: bold;
        margin: 2px; /* correspond to style on div.thumb */
 }
 
+ul.mw-gallery-packed-hover li.gallerybox:hover div.gallerytextwrapper p,
+ul.mw-gallery-packed-overlay li.gallerybox div.gallerytextwrapper p,
+ul.mw-gallery-packed-hover li.gallerybox.mw-gallery-focused div.gallerytextwrapper p {
+       text-overflow: ellipsis;
+       white-space: nowrap;
+       overflow: hidden;
+}
+
+ul.mw-gallery-packed-hover li.gallerybox div.gallerytextwrapper:hover,
+ul.mw-gallery-packed-overlay li.gallerybox div.gallerytextwrapper:hover,
+ul.mw-gallery-packed-hover li.gallerybox.mw-gallery-focused div.gallerytextwrapper:hover {
+       overflow: visible;
+       max-height: none;
+}
+
+ul.mw-gallery-packed-hover li.gallerybox div.gallerytextwrapper:hover p,
+ul.mw-gallery-packed-overlay li.gallerybox div.gallerytextwrapper:hover p,
+ul.mw-gallery-packed-hover li.gallerybox.mw-gallery-focused div.gallerytextwrapper:hover p {
+       text-overflow: clip;
+       white-space: normal;
+       overflow: visible;
+}
+
 ul.mw-gallery-packed-hover,
 ul.mw-gallery-packed-overlay,
 ul.mw-gallery-packed {
        text-align: center;
 }
+
+/* Slideshow */
+ul.gallery.mw-gallery-slideshow {
+       display: block;
+       margin: 4em 0;
+}
+
+ul.gallery.mw-gallery-slideshow .gallerycaption {
+       font-size: 1.3em;
+       margin: 0;
+}
+
+ul.gallery.mw-gallery-slideshow .gallerycarousel.mw-gallery-slideshow-thumbnails-toggled {
+       margin-bottom: 1.3em;
+}
+
+ul.gallery.mw-gallery-slideshow .mw-gallery-slideshow-buttons {
+       opacity: 0.5;
+       padding: 1.3em 0;
+}
+
+ul.gallery.mw-gallery-slideshow .mw-gallery-slideshow-buttons .oo-ui-buttonElement {
+       margin: 0 2em;
+}
+
+.mw-gallery-slideshow li.gallerybox.slideshow-current {
+       background: #efefef;
+}
+
+.mw-gallery-slideshow .gallerybox > div {
+       max-width: 120px;
+}
+
+ul.mw-gallery-slideshow li.gallerybox div.thumb {
+       border: none;
+       background: transparent;
+}
+
+ul.mw-gallery-slideshow li.gallerycarousel {
+       display: block;
+       text-align: center;
+}
+
+.mw-gallery-slideshow-img-container a {
+       display: block;
+}
diff --git a/resources/src/mediawiki/page/gallery.print.css b/resources/src/mediawiki/page/gallery.print.css
new file mode 100644 (file)
index 0000000..0c14865
--- /dev/null
@@ -0,0 +1,35 @@
+li.gallerybox {
+       vertical-align: top;
+       display: inline-block;
+}
+
+ul.gallery, li.gallerybox {
+       zoom: 1;
+       *display: inline;
+}
+
+ul.gallery {
+       margin: 2px;
+       padding: 2px;
+       display: block;
+}
+
+li.gallerycaption {
+       font-weight: bold;
+       text-align: center;
+       display: block;
+       word-wrap: break-word;
+}
+
+li.gallerybox div.thumb {
+       text-align: center;
+       border: 1px solid #ccc;
+       margin: 2px;
+}
+
+div.gallerytext {
+       overflow: hidden;
+       font-size: 94%;
+       padding: 2px 4px;
+       word-wrap: break-word;
+}
index 3b779d1..d228f3e 100644 (file)
@@ -36,7 +36,7 @@
 
        // Things outside the wikipage content
        $( function () {
-               var $nodes, $oouiNodes;
+               var $nodes;
 
                if ( !supportsPlaceholder ) {
                        // Exclude content to avoid hitting it twice for the (first) wikipage content
                // Add accesskey hints to the tooltips
                $( '[accesskey]' ).updateTooltipAccessKeys();
 
-               // Infuse OOUI widgets, if any are present
-               $oouiNodes = $( '[data-ooui]' );
-               if ( $oouiNodes.length ) {
-                       // FIXME: We should only load the widgets that are being infused
-                       mw.loader.using( [
-                               'mediawiki.widgets',
-                               'mediawiki.widgets.UserInputWidget',
-                               'mediawiki.widgets.SearchInputWidget'
-                       ] ).done( function () {
-                               $oouiNodes.each( function () {
-                                       OO.ui.infuse( this );
-                               } );
-                       } );
-               }
-
                $nodes = $( '.catlinks[data-mw="interface"]' );
                if ( $nodes.length ) {
                        /**
index d973d07..83d14b3 100644 (file)
@@ -2,7 +2,7 @@
  * Enhance rollback links by using asynchronous API requests,
  * rather than navigating to an action page.
  *
- * @since 1.27
+ * @since 1.28
  * @author Timo Tijhof
  */
 ( function ( mw, $ ) {
@@ -15,7 +15,7 @@
                                page = mw.util.getParamValue( 'title', url ),
                                user = mw.util.getParamValue( 'from', url );
 
-                       if ( !page || !user ) {
+                       if ( !page || user === null ) {
                                // Let native browsing handle the link
                                return true;
                        }
index cd37e33..282799a 100644 (file)
@@ -1,10 +1,5 @@
 ( function ( mw, $ ) {
 
-       // Support: MediaWiki < 1.26
-       // Cached HTML will not yet have this from OutputPage::getHeadScripts.
-       document.documentElement.className = document.documentElement.className
-               .replace( /(^|\s)client-nojs(\s|$)/, '$1client-js$2' );
-
        mw.page = {};
 
        $( function () {
index c59f5ba..b860dbd 100644 (file)
                $links.click( function ( e ) {
                        var action, api, $link;
 
-                       // Preload the notification module for mw.notify
-                       mw.loader.load( 'mediawiki.notification' );
-
                        action = mwUriGetAction( this.href );
 
                        if ( action !== 'watch' && action !== 'unwatch' ) {
 
                        updateWatchLink( $link, action, 'loading' );
 
+                       // Preload the notification module for mw.notify
+                       mw.loader.load( 'mediawiki.notification' );
+
                        api = new mw.Api();
 
                        api[ action ]( title )
diff --git a/resources/src/oojs-ui-styles-skip.js b/resources/src/oojs-ui-styles-skip.js
deleted file mode 100644 (file)
index 57c905a..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-/*!
- * Skip function for OOjs UI PHP style modules.
- *
- * The `<meta name="X-OOUI-PHP" />` is added to pages by OutputPage::enableOOUI().
- *
- * Looking for elements in the DOM might be expensive, but it's probably better than double-loading
- * 200 KB of CSS with embedded images because of bug T87871.
- */
-return !!jQuery( 'meta[name="X-OOUI-PHP"]' ).length;
index 62ee94e..d026cb0 100644 (file)
@@ -77,7 +77,7 @@ function isCompatible( str ) {
        var NORLQ, script;
        if ( !isCompatible() ) {
                // Undo class swapping in case of an unsupported browser.
-               // See OutputPage::getHeadScripts().
+               // See ResourceLoaderClientHtml::getDocumentAttributes().
                document.documentElement.className = document.documentElement.className
                        .replace( /(^|\s)client-js(\s|$)/, '$1client-nojs$2' );
 
diff --git a/tests/TestsAutoLoader.php b/tests/TestsAutoLoader.php
deleted file mode 100644 (file)
index 8b100a2..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-<?php
-/**
- * AutoLoader for the testing suite.
- *
- * 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 Testing
- */
-
-global $wgAutoloadClasses;
-$testDir = __DIR__;
-
-$wgAutoloadClasses += [
-
-       # tests
-       'DbTestPreviewer' => "$testDir/testHelpers.inc",
-       'DbTestRecorder' => "$testDir/testHelpers.inc",
-       'DelayedParserTest' => "$testDir/testHelpers.inc",
-       'ParserTestResult' => "$testDir/parser/ParserTestResult.php",
-       'TestFileIterator' => "$testDir/testHelpers.inc",
-       'TestFileDataProvider' => "$testDir/testHelpers.inc",
-       'TestRecorder' => "$testDir/testHelpers.inc",
-       'ITestRecorder' => "$testDir/testHelpers.inc",
-       'DjVuSupport' => "$testDir/testHelpers.inc",
-       'TidySupport' => "$testDir/testHelpers.inc",
-
-       # tests/phpunit
-       'MediaWikiTestCase' => "$testDir/phpunit/MediaWikiTestCase.php",
-       'MediaWikiPHPUnitTestListener' => "$testDir/phpunit/MediaWikiPHPUnitTestListener.php",
-       'MediaWikiLangTestCase' => "$testDir/phpunit/MediaWikiLangTestCase.php",
-       'ResourceLoaderTestCase' => "$testDir/phpunit/ResourceLoaderTestCase.php",
-       'ResourceLoaderTestModule' => "$testDir/phpunit/ResourceLoaderTestCase.php",
-       'ResourceLoaderFileModuleTestModule' => "$testDir/phpunit/ResourceLoaderTestCase.php",
-       'TestUser' => "$testDir/phpunit/includes/TestUser.php",
-       'TestUserRegistry' => "$testDir/phpunit/includes/TestUserRegistry.php",
-       'LessFileCompilationTest' => "$testDir/phpunit/LessFileCompilationTest.php",
-
-       # tests/phpunit/includes
-       'RevisionStorageTest' => "$testDir/phpunit/includes/RevisionStorageTest.php",
-       'TestingAccessWrapper' => "$testDir/phpunit/includes/TestingAccessWrapper.php",
-       'TestLogger' => "$testDir/phpunit/includes/TestLogger.php",
-
-       # tests/phpunit/includes/api
-       'ApiFormatTestBase' => "$testDir/phpunit/includes/api/format/ApiFormatTestBase.php",
-       'ApiQueryTestBase' => "$testDir/phpunit/includes/api/query/ApiQueryTestBase.php",
-       'ApiQueryContinueTestBase' => "$testDir/phpunit/includes/api/query/ApiQueryContinueTestBase.php",
-       'ApiTestCase' => "$testDir/phpunit/includes/api/ApiTestCase.php",
-       'ApiTestCaseUpload' => "$testDir/phpunit/includes/api/ApiTestCaseUpload.php",
-       'ApiTestContext' => "$testDir/phpunit/includes/api/ApiTestContext.php",
-       'MockApi' => "$testDir/phpunit/includes/api/MockApi.php",
-       'MockApiQueryBase' => "$testDir/phpunit/includes/api/MockApiQueryBase.php",
-       'UserWrapper' => "$testDir/phpunit/includes/api/UserWrapper.php",
-       'RandomImageGenerator' => "$testDir/phpunit/includes/api/RandomImageGenerator.php",
-
-       # tests/phpunit/includes/auth
-       'MediaWiki\\Auth\\AuthenticationRequestTestCase' =>
-               "$testDir/phpunit/includes/auth/AuthenticationRequestTestCase.php",
-
-       # tests/phpunit/includes/changes
-       'TestRecentChangesHelper' => "$testDir/phpunit/includes/changes/TestRecentChangesHelper.php",
-
-       # tests/phpunit/includes/content
-       'DummyContentHandlerForTesting' =>
-               "$testDir/phpunit/mocks/content/DummyContentHandlerForTesting.php",
-       'DummyContentForTesting' => "$testDir/phpunit/mocks/content/DummyContentForTesting.php",
-       'DummyNonTextContentHandler' => "$testDir/phpunit/mocks/content/DummyNonTextContentHandler.php",
-       'DummyNonTextContent' => "$testDir/phpunit/mocks/content/DummyNonTextContent.php",
-       'ContentHandlerTest' => "$testDir/phpunit/includes/content/ContentHandlerTest.php",
-       'JavaScriptContentTest' => "$testDir/phpunit/includes/content/JavaScriptContentTest.php",
-       'TextContentTest' => "$testDir/phpunit/includes/content/TextContentTest.php",
-       'WikitextContentTest' => "$testDir/phpunit/includes/content/WikitextContentTest.php",
-
-       # tests/phpunit/includes/db
-       'DatabaseTestHelper' => "$testDir/phpunit/includes/db/DatabaseTestHelper.php",
-
-       # tests/phpunit/includes/diff
-       'FakeDiffOp' => "$testDir/phpunit/includes/diff/FakeDiffOp.php",
-
-       # tests/phpunit/includes/logging
-       'LogFormatterTestCase' => "$testDir/phpunit/includes/logging/LogFormatterTestCase.php",
-
-       # tests/phpunit/includes/page
-       'WikiPageTest' => "$testDir/phpunit/includes/page/WikiPageTest.php",
-
-       # tests/phpunit/includes/password
-       'PasswordTestCase' => "$testDir/phpunit/includes/password/PasswordTestCase.php",
-
-       # tests/phpunit/includes/resourceloader
-       'ResourceLoaderImageModuleTest' =>
-               "$testDir/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php",
-       'ResourceLoaderImageModuleTestable' =>
-               "$testDir/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php",
-
-       # tests/phpunit/includes/session
-       'MediaWiki\\Session\\TestBagOStuff' => "$testDir/phpunit/includes/session/TestBagOStuff.php",
-       'MediaWiki\\Session\\TestUtils' => "$testDir/phpunit/includes/session/TestUtils.php",
-
-       # tests/phpunit/includes/specials
-       'SpecialPageTestBase' => "$testDir/phpunit/includes/specials/SpecialPageTestBase.php",
-       'SpecialPageExecutor' => "$testDir/phpunit/includes/specials/SpecialPageExecutor.php",
-
-       # tests/phpunit/languages
-       'LanguageClassesTestCase' => "$testDir/phpunit/languages/LanguageClassesTestCase.php",
-
-       # tests/phpunit/includes/libs
-       'GenericArrayObjectTest' => "$testDir/phpunit/includes/libs/GenericArrayObjectTest.php",
-
-       # tests/phpunit/maintenance
-       'DumpTestCase' => "$testDir/phpunit/maintenance/DumpTestCase.php",
-
-       # tests/phpunit/media
-       'FakeDimensionFile' => "$testDir/phpunit/includes/media/FakeDimensionFile.php",
-       'MediaWikiMediaTestCase' => "$testDir/phpunit/includes/media/MediaWikiMediaTestCase.php",
-
-       # tests/phpunit/mocks
-       'MockFSFile' => "$testDir/phpunit/mocks/filebackend/MockFSFile.php",
-       'MockFileBackend' => "$testDir/phpunit/mocks/filebackend/MockFileBackend.php",
-       'MockBitmapHandler' => "$testDir/phpunit/mocks/media/MockBitmapHandler.php",
-       'MockImageHandler' => "$testDir/phpunit/mocks/media/MockImageHandler.php",
-       'MockSvgHandler' => "$testDir/phpunit/mocks/media/MockSvgHandler.php",
-       'MockDjVuHandler' => "$testDir/phpunit/mocks/media/MockDjVuHandler.php",
-       'MockOggHandler' => "$testDir/phpunit/mocks/media/MockOggHandler.php",
-       'MockWebRequest' => "$testDir/phpunit/mocks/MockWebRequest.php",
-       'MediaWiki\\Session\\DummySessionBackend'
-               => "$testDir/phpunit/mocks/session/DummySessionBackend.php",
-       'DummySessionProvider' => "$testDir/phpunit/mocks/session/DummySessionProvider.php",
-
-       # tests/parser
-       'NewParserTest' => "$testDir/phpunit/includes/parser/NewParserTest.php",
-       'MediaWikiParserTest' => "$testDir/phpunit/includes/parser/MediaWikiParserTest.php",
-       'ParserTest' => "$testDir/parser/parserTest.inc",
-       'ParserTestParserHook' => "$testDir/parser/parserTestsParserHook.php",
-
-       # tests/phpunit/includes/site
-       'SiteTest' => "$testDir/phpunit/includes/site/SiteTest.php",
-       'TestSites' => "$testDir/phpunit/includes/site/TestSites.php",
-
-       # tests/phpunit/includes/specialpage
-       'SpecialPageTestHelper' => "$testDir/phpunit/includes/specialpage/SpecialPageTestHelper.php",
-];
index 35eb153..3f451f1 100644 (file)
@@ -21,14 +21,15 @@ mw-vagrant-host: &default
 mw-vagrant-guest:
   user_factory: true
   mediawiki_url: http://127.0.0.1/wiki/
+  headless: 'true'
 
 beta:
-  mediawiki_url: http://en.wikipedia.beta.wmflabs.org/wiki/
+  mediawiki_url: https://en.wikipedia.beta.wmflabs.org/wiki/
   mediawiki_user: Selenium_user
   # mediawiki_password: SET THIS IN THE ENVIRONMENT!
 
 test2:
-  mediawiki_url: http://test2.wikipedia.org/wiki/
+  mediawiki_url: https://test2.wikipedia.org/wiki/
   mediawiki_user: Selenium_user
   # mediawiki_password: SET THIS IN THE ENVIRONMENT!
 
diff --git a/tests/common/TestSetup.php b/tests/common/TestSetup.php
new file mode 100644 (file)
index 0000000..6c3ad07
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * Common code for test environment initialisation and teardown
+ */
+class TestSetup {
+       /**
+        * This should be called before Setup.php, e.g. from the finalSetup() method
+        * of a Maintenance subclass
+        */
+       public static function applyInitialConfig() {
+               global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType, $wgMainWANCache;
+               global $wgMainStash;
+               global $wgLanguageConverterCacheType, $wgUseDatabaseMessages;
+               global $wgLocaltimezone, $wgLocalisationCacheConf;
+               global $wgDevelopmentWarnings;
+               global $wgSessionProviders, $wgSessionPbkdf2Iterations;
+               global $wgJobTypeConf;
+               global $wgAuthManagerConfig, $wgAuth;
+
+               // wfWarn should cause tests to fail
+               $wgDevelopmentWarnings = true;
+
+               // Make sure all caches and stashes are either disabled or use
+               // in-process cache only to prevent tests from using any preconfigured
+               // cache meant for the local wiki from outside the test run.
+               // See also MediaWikiTestCase::run() which mocks CACHE_DB and APC.
+
+               // Disabled in DefaultSettings, override local settings
+               $wgMainWANCache =
+               $wgMainCacheType = CACHE_NONE;
+               // Uses CACHE_ANYTHING in DefaultSettings, use hash instead of db
+               $wgMessageCacheType =
+               $wgParserCacheType =
+               $wgSessionCacheType =
+               $wgLanguageConverterCacheType = 'hash';
+               // Uses db-replicated in DefaultSettings
+               $wgMainStash = 'hash';
+               // Use memory job queue
+               $wgJobTypeConf = [
+                       'default' => [ 'class' => 'JobQueueMemory', 'order' => 'fifo' ],
+               ];
+
+               $wgUseDatabaseMessages = false; # Set for future resets
+
+               // Assume UTC for testing purposes
+               $wgLocaltimezone = 'UTC';
+
+               $wgLocalisationCacheConf['storeClass'] = 'LCStoreNull';
+
+               // Generic MediaWiki\Session\SessionManager configuration for tests
+               // We use CookieSessionProvider because things might be expecting
+               // cookies to show up in a FauxRequest somewhere.
+               $wgSessionProviders = [
+                       [
+                               'class' => MediaWiki\Session\CookieSessionProvider::class,
+                               'args' => [ [
+                                       'priority' => 30,
+                                       'callUserSetCookiesHook' => true,
+                               ] ],
+                       ],
+               ];
+
+               // Single-iteration PBKDF2 session secret derivation, for speed.
+               $wgSessionPbkdf2Iterations = 1;
+
+               // Generic AuthManager configuration for testing
+               $wgAuthManagerConfig = [
+                       'preauth' => [],
+                       'primaryauth' => [
+                               [
+                                       'class' => MediaWiki\Auth\TemporaryPasswordPrimaryAuthenticationProvider::class,
+                                       'args' => [ [
+                                               'authoritative' => false,
+                                       ] ],
+                               ],
+                               [
+                                       'class' => MediaWiki\Auth\LocalPasswordPrimaryAuthenticationProvider::class,
+                                       'args' => [ [
+                                               'authoritative' => true,
+                                       ] ],
+                               ],
+                       ],
+                       'secondaryauth' => [],
+               ];
+               $wgAuth = new MediaWiki\Auth\AuthManagerAuthPlugin();
+
+               // Bug 44192 Do not attempt to send a real e-mail
+               Hooks::clear( 'AlternateUserMailer' );
+               Hooks::register(
+                       'AlternateUserMailer',
+                       function () {
+                               return false;
+                       }
+               );
+               // xdebug's default of 100 is too low for MediaWiki
+               ini_set( 'xdebug.max_nesting_level', 1000 );
+
+               // Bug T116683 serialize_precision of 100
+               // may break testing against floating point values
+               // treated with PHP's serialize()
+               ini_set( 'serialize_precision', 17 );
+
+               // TODO: we should call MediaWikiTestCase::prepareServices( new GlobalVarConfig() ) here.
+               // But PHPUnit may not be loaded yet, so we have to wait until just
+               // before PHPUnit_TextUI_Command::main() is executed.
+       }
+
+}
diff --git a/tests/common/TestsAutoLoader.php b/tests/common/TestsAutoLoader.php
new file mode 100644 (file)
index 0000000..2a985fe
--- /dev/null
@@ -0,0 +1,164 @@
+<?php
+/**
+ * AutoLoader for the testing suite.
+ *
+ * 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 Testing
+ */
+
+global $wgAutoloadClasses;
+$testDir = __DIR__ . "/..";
+
+$wgAutoloadClasses += [
+
+       # tests/common
+       'TestSetup' => "$testDir/common/TestSetup.php",
+
+       # tests/parser
+       'DbTestPreviewer' => "$testDir/parser/DbTestPreviewer.php",
+       'DbTestRecorder' => "$testDir/parser/DbTestRecorder.php",
+       'DjVuSupport' => "$testDir/parser/DjVuSupport.php",
+       'TestRecorder' => "$testDir/parser/TestRecorder.php",
+       'MultiTestRecorder' => "$testDir/parser/MultiTestRecorder.php",
+       'ParserTestRunner' => "$testDir/parser/ParserTestRunner.php",
+       'ParserTestParserHook' => "$testDir/parser/ParserTestParserHook.php",
+       'ParserTestPrinter' => "$testDir/parser/ParserTestPrinter.php",
+       'ParserTestResult' => "$testDir/parser/ParserTestResult.php",
+       'ParserTestResultNormalizer' => "$testDir/parser/ParserTestResultNormalizer.php",
+       'PhpunitTestRecorder' => "$testDir/parser/PhpunitTestRecorder.php",
+       'TestFileReader' => "$testDir/parser/TestFileReader.php",
+       'TestRecorder' => "$testDir/parser/TestRecorder.php",
+       'TidySupport' => "$testDir/parser/TidySupport.php",
+
+       # tests/phpunit
+       'MediaWikiTestCase' => "$testDir/phpunit/MediaWikiTestCase.php",
+       'MediaWikiPHPUnitTestListener' => "$testDir/phpunit/MediaWikiPHPUnitTestListener.php",
+       'MediaWikiLangTestCase' => "$testDir/phpunit/MediaWikiLangTestCase.php",
+       'ResourceLoaderTestCase' => "$testDir/phpunit/ResourceLoaderTestCase.php",
+       'ResourceLoaderTestModule' => "$testDir/phpunit/ResourceLoaderTestCase.php",
+       'ResourceLoaderFileModuleTestModule' => "$testDir/phpunit/ResourceLoaderTestCase.php",
+       'EmptyResourceLoader' => "$testDir/phpunit/ResourceLoaderTestCase.php",
+       'TestUser' => "$testDir/phpunit/includes/TestUser.php",
+       'TestUserRegistry' => "$testDir/phpunit/includes/TestUserRegistry.php",
+       'LessFileCompilationTest' => "$testDir/phpunit/LessFileCompilationTest.php",
+
+       # tests/phpunit/includes
+       'RevisionStorageTest' => "$testDir/phpunit/includes/RevisionStorageTest.php",
+       'TestingAccessWrapper' => "$testDir/phpunit/includes/TestingAccessWrapper.php",
+       'TestLogger' => "$testDir/phpunit/includes/TestLogger.php",
+
+       # tests/phpunit/includes/api
+       'ApiFormatTestBase' => "$testDir/phpunit/includes/api/format/ApiFormatTestBase.php",
+       'ApiQueryTestBase' => "$testDir/phpunit/includes/api/query/ApiQueryTestBase.php",
+       'ApiQueryContinueTestBase' => "$testDir/phpunit/includes/api/query/ApiQueryContinueTestBase.php",
+       'ApiTestCase' => "$testDir/phpunit/includes/api/ApiTestCase.php",
+       'ApiTestCaseUpload' => "$testDir/phpunit/includes/api/ApiTestCaseUpload.php",
+       'ApiTestContext' => "$testDir/phpunit/includes/api/ApiTestContext.php",
+       'MockApi' => "$testDir/phpunit/includes/api/MockApi.php",
+       'MockApiQueryBase' => "$testDir/phpunit/includes/api/MockApiQueryBase.php",
+       'UserWrapper' => "$testDir/phpunit/includes/api/UserWrapper.php",
+       'RandomImageGenerator' => "$testDir/phpunit/includes/api/RandomImageGenerator.php",
+
+       # tests/phpunit/includes/auth
+       'MediaWiki\\Auth\\AuthenticationRequestTestCase' =>
+               "$testDir/phpunit/includes/auth/AuthenticationRequestTestCase.php",
+
+       # tests/phpunit/includes/changes
+       'TestRecentChangesHelper' => "$testDir/phpunit/includes/changes/TestRecentChangesHelper.php",
+
+       # tests/phpunit/includes/content
+       'DummyContentHandlerForTesting' =>
+               "$testDir/phpunit/mocks/content/DummyContentHandlerForTesting.php",
+       'DummyContentForTesting' => "$testDir/phpunit/mocks/content/DummyContentForTesting.php",
+       'DummyNonTextContentHandler' => "$testDir/phpunit/mocks/content/DummyNonTextContentHandler.php",
+       'DummyNonTextContent' => "$testDir/phpunit/mocks/content/DummyNonTextContent.php",
+       'ContentHandlerTest' => "$testDir/phpunit/includes/content/ContentHandlerTest.php",
+       'JavaScriptContentTest' => "$testDir/phpunit/includes/content/JavaScriptContentTest.php",
+       'TextContentTest' => "$testDir/phpunit/includes/content/TextContentTest.php",
+       'WikitextContentTest' => "$testDir/phpunit/includes/content/WikitextContentTest.php",
+
+       # tests/phpunit/includes/db
+       'DatabaseTestHelper' => "$testDir/phpunit/includes/db/DatabaseTestHelper.php",
+
+       # tests/phpunit/includes/diff
+       'FakeDiffOp' => "$testDir/phpunit/includes/diff/FakeDiffOp.php",
+
+       # tests/phpunit/includes/logging
+       'LogFormatterTestCase' => "$testDir/phpunit/includes/logging/LogFormatterTestCase.php",
+
+       # tests/phpunit/includes/page
+       'WikiPageTest' => "$testDir/phpunit/includes/page/WikiPageTest.php",
+
+       # tests/phpunit/includes/parser
+       'ParserIntegrationTest' => "$testDir/phpunit/includes/parser/ParserIntegrationTest.php",
+
+       # tests/phpunit/includes/password
+       'PasswordTestCase' => "$testDir/phpunit/includes/password/PasswordTestCase.php",
+
+       # tests/phpunit/includes/resourceloader
+       'ResourceLoaderImageModuleTest' =>
+               "$testDir/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php",
+       'ResourceLoaderImageModuleTestable' =>
+               "$testDir/phpunit/includes/resourceloader/ResourceLoaderImageModuleTest.php",
+
+       # tests/phpunit/includes/session
+       'MediaWiki\\Session\\TestBagOStuff' => "$testDir/phpunit/includes/session/TestBagOStuff.php",
+       'MediaWiki\\Session\\TestUtils' => "$testDir/phpunit/includes/session/TestUtils.php",
+
+       # tests/phpunit/includes/site
+       'SiteTest' => "$testDir/phpunit/includes/site/SiteTest.php",
+       'TestSites' => "$testDir/phpunit/includes/site/TestSites.php",
+
+       # tests/phpunit/includes/specialpage
+       'SpecialPageTestHelper' => "$testDir/phpunit/includes/specialpage/SpecialPageTestHelper.php",
+
+       # tests/phpunit/includes/specials
+       'SpecialPageTestBase' => "$testDir/phpunit/includes/specials/SpecialPageTestBase.php",
+       'SpecialPageExecutor' => "$testDir/phpunit/includes/specials/SpecialPageExecutor.php",
+
+       # tests/phpunit/languages
+       'LanguageClassesTestCase' => "$testDir/phpunit/languages/LanguageClassesTestCase.php",
+
+       # tests/phpunit/includes/libs
+       'GenericArrayObjectTest' => "$testDir/phpunit/includes/libs/GenericArrayObjectTest.php",
+
+       # tests/phpunit/maintenance
+       'DumpTestCase' => "$testDir/phpunit/maintenance/DumpTestCase.php",
+
+       # tests/phpunit/media
+       'FakeDimensionFile' => "$testDir/phpunit/includes/media/FakeDimensionFile.php",
+       'MediaWikiMediaTestCase' => "$testDir/phpunit/includes/media/MediaWikiMediaTestCase.php",
+
+       # tests/phpunit/mocks
+       'MockFSFile' => "$testDir/phpunit/mocks/filebackend/MockFSFile.php",
+       'MockFileBackend' => "$testDir/phpunit/mocks/filebackend/MockFileBackend.php",
+       'MockBitmapHandler' => "$testDir/phpunit/mocks/media/MockBitmapHandler.php",
+       'MockImageHandler' => "$testDir/phpunit/mocks/media/MockImageHandler.php",
+       'MockSvgHandler' => "$testDir/phpunit/mocks/media/MockSvgHandler.php",
+       'MockDjVuHandler' => "$testDir/phpunit/mocks/media/MockDjVuHandler.php",
+       'MockOggHandler' => "$testDir/phpunit/mocks/media/MockOggHandler.php",
+       'MockMediaHandlerFactory' => "$testDir/phpunit/mocks/media/MockMediaHandlerFactory.php",
+       'MockWebRequest' => "$testDir/phpunit/mocks/MockWebRequest.php",
+       'MediaWiki\\Session\\DummySessionBackend'
+               => "$testDir/phpunit/mocks/session/DummySessionBackend.php",
+       'DummySessionProvider' => "$testDir/phpunit/mocks/session/DummySessionProvider.php",
+
+       # tests/suites
+       'ParserTestFileSuite' => "$testDir/phpunit/suites/ParserTestFileSuite.php",
+       'ParserTestTopLevelSuite' => "$testDir/phpunit/suites/ParserTestTopLevelSuite.php",
+];
diff --git a/tests/parser/DbTestPreviewer.php b/tests/parser/DbTestPreviewer.php
new file mode 100644 (file)
index 0000000..7809ab3
--- /dev/null
@@ -0,0 +1,204 @@
+<?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
+ * @ingroup Testing
+ */
+
+class DbTestPreviewer extends TestRecorder {
+       protected $filter; // /< Test name filter callback
+       protected $lb; // /< Database load balancer
+       protected $db; // /< Database connection to the main DB
+       protected $curRun; // /< run ID number for the current run
+       protected $prevRun; // /< run ID number for the previous run, if any
+       protected $results; // /< Result array
+
+       /**
+        * This should be called before the table prefix is changed
+        */
+       function __construct( $db, $filter = false ) {
+               $this->db = $db;
+               $this->filter = $filter;
+       }
+
+       /**
+        * Set up result recording; insert a record for the run with the date
+        * and all that fun stuff
+        */
+       function start() {
+               if ( !$this->db->tableExists( 'testrun', __METHOD__ )
+                       || !$this->db->tableExists( 'testitem', __METHOD__ )
+               ) {
+                       print "WARNING> `testrun` table not found in database.\n";
+                       $this->prevRun = false;
+               } else {
+                       // We'll make comparisons against the previous run later...
+                       $this->prevRun = $this->db->selectField( 'testrun', 'MAX(tr_id)' );
+               }
+
+               $this->results = [];
+       }
+
+       function record( $test, ParserTestResult $result ) {
+               $this->results[$test['desc']] = $result->isSuccess() ? 1 : 0;
+       }
+
+       function report() {
+               if ( $this->prevRun ) {
+                       // f = fail, p = pass, n = nonexistent
+                       // codes show before then after
+                       $table = [
+                               'fp' => 'previously failing test(s) now PASSING! :)',
+                               'pn' => 'previously PASSING test(s) removed o_O',
+                               'np' => 'new PASSING test(s) :)',
+
+                               'pf' => 'previously passing test(s) now FAILING! :(',
+                               'fn' => 'previously FAILING test(s) removed O_o',
+                               'nf' => 'new FAILING test(s) :(',
+                               'ff' => 'still FAILING test(s) :(',
+                       ];
+
+                       $prevResults = [];
+
+                       $res = $this->db->select( 'testitem', [ 'ti_name', 'ti_success' ],
+                               [ 'ti_run' => $this->prevRun ], __METHOD__ );
+                       $filter = $this->filter;
+
+                       foreach ( $res as $row ) {
+                               if ( !$filter || $filter( $row->ti_name ) ) {
+                                       $prevResults[$row->ti_name] = $row->ti_success;
+                               }
+                       }
+
+                       $combined = array_keys( $this->results + $prevResults );
+
+                       # Determine breakdown by change type
+                       $breakdown = [];
+                       foreach ( $combined as $test ) {
+                               if ( !isset( $prevResults[$test] ) ) {
+                                       $before = 'n';
+                               } elseif ( $prevResults[$test] == 1 ) {
+                                       $before = 'p';
+                               } else /* if ( $prevResults[$test] == 0 )*/ {
+                                       $before = 'f';
+                               }
+
+                               if ( !isset( $this->results[$test] ) ) {
+                                       $after = 'n';
+                               } elseif ( $this->results[$test] == 1 ) {
+                                       $after = 'p';
+                               } else /*if ( $this->results[$test] == 0 ) */ {
+                                       $after = 'f';
+                               }
+
+                               $code = $before . $after;
+
+                               if ( isset( $table[$code] ) ) {
+                                       $breakdown[$code][$test] = $this->getTestStatusInfo( $test, $after );
+                               }
+                       }
+
+                       # Write out results
+                       foreach ( $table as $code => $label ) {
+                               if ( !empty( $breakdown[$code] ) ) {
+                                       $count = count( $breakdown[$code] );
+                                       printf( "\n%4d %s\n", $count, $label );
+
+                                       foreach ( $breakdown[$code] as $differing_test_name => $statusInfo ) {
+                                               print "      * $differing_test_name  [$statusInfo]\n";
+                                       }
+                               }
+                       }
+               } else {
+                       print "No previous test runs to compare against.\n";
+               }
+
+               print "\n";
+       }
+
+       /**
+        * Returns a string giving information about when a test last had a status change.
+        * Could help to track down when regressions were introduced, as distinct from tests
+        * which have never passed (which are more change requests than regressions).
+        * @param string $testname
+        * @param string $after
+        * @return string
+        */
+       private function getTestStatusInfo( $testname, $after ) {
+               // If we're looking at a test that has just been removed, then say when it first appeared.
+               if ( $after == 'n' ) {
+                       $changedRun = $this->db->selectField( 'testitem',
+                               'MIN(ti_run)',
+                               [ 'ti_name' => $testname ],
+                               __METHOD__ );
+                       $appear = $this->db->selectRow( 'testrun',
+                               [ 'tr_date', 'tr_mw_version' ],
+                               [ 'tr_id' => $changedRun ],
+                               __METHOD__ );
+
+                       return "First recorded appearance: "
+                               . date( "d-M-Y H:i:s", strtotime( $appear->tr_date ) )
+                               . ", " . $appear->tr_mw_version;
+               }
+
+               // Otherwise, this test has previous recorded results.
+               // See when this test last had a different result to what we're seeing now.
+               $conds = [
+                       'ti_name' => $testname,
+                       'ti_success' => ( $after == 'f' ? "1" : "0" ) ];
+
+               if ( $this->curRun ) {
+                       $conds[] = "ti_run != " . $this->db->addQuotes( $this->curRun );
+               }
+
+               $changedRun = $this->db->selectField( 'testitem', 'MAX(ti_run)', $conds, __METHOD__ );
+
+               // If no record of ever having had a different result.
+               if ( is_null( $changedRun ) ) {
+                       if ( $after == "f" ) {
+                               return "Has never passed";
+                       } else {
+                               return "Has never failed";
+                       }
+               }
+
+               // Otherwise, we're looking at a test whose status has changed.
+               // (i.e. it used to work, but now doesn't; or used to fail, but is now fixed.)
+               // In this situation, give as much info as we can as to when it changed status.
+               $pre = $this->db->selectRow( 'testrun',
+                       [ 'tr_date', 'tr_mw_version' ],
+                       [ 'tr_id' => $changedRun ],
+                       __METHOD__ );
+               $post = $this->db->selectRow( 'testrun',
+                       [ 'tr_date', 'tr_mw_version' ],
+                       [ "tr_id > " . $this->db->addQuotes( $changedRun ) ],
+                       __METHOD__,
+                       [ "LIMIT" => 1, "ORDER BY" => 'tr_id' ]
+               );
+
+               if ( $post ) {
+                       $postDate = date( "d-M-Y H:i:s", strtotime( $post->tr_date ) ) . ", {$post->tr_mw_version}";
+               } else {
+                       $postDate = 'now';
+               }
+
+               return ( $after == "f" ? "Introduced" : "Fixed" ) . " between "
+                       . date( "d-M-Y H:i:s", strtotime( $pre->tr_date ) ) . ", " . $pre->tr_mw_version
+                       . " and $postDate";
+       }
+}
+
diff --git a/tests/parser/DbTestRecorder.php b/tests/parser/DbTestRecorder.php
new file mode 100644 (file)
index 0000000..0e94301
--- /dev/null
@@ -0,0 +1,84 @@
+<?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
+ * @ingroup Testing
+ */
+
+class DbTestRecorder extends TestRecorder {
+       public $version;
+       private $db;
+
+       public function __construct( IDatabase $db ) {
+               $this->db = $db;
+       }
+
+       /**
+        * Set up result recording; insert a record for the run with the date
+        * and all that fun stuff
+        */
+       function start() {
+               $this->db->begin( __METHOD__ );
+
+               if ( !$this->db->tableExists( 'testrun' )
+                       || !$this->db->tableExists( 'testitem' )
+               ) {
+                       print "WARNING> `testrun` table not found in database. Trying to create table.\n";
+                       $this->db->sourceFile( $this->db->patchPath( 'patch-testrun.sql' ) );
+                       echo "OK, resuming.\n";
+               }
+
+               $this->db->insert( 'testrun',
+                       [
+                               'tr_date' => $this->db->timestamp(),
+                               'tr_mw_version' => $this->version,
+                               'tr_php_version' => PHP_VERSION,
+                               'tr_db_version' => $this->db->getServerVersion(),
+                               'tr_uname' => php_uname()
+                       ],
+                       __METHOD__ );
+               if ( $this->db->getType() === 'postgres' ) {
+                       $this->curRun = $this->db->currentSequenceValue( 'testrun_id_seq' );
+               } else {
+                       $this->curRun = $this->db->insertId();
+               }
+       }
+
+       /**
+        * Record an individual test item's success or failure to the db
+        *
+        * @param array $test
+        * @param ParserTestResult $result
+        */
+       function record( $test, ParserTestResult $result ) {
+               $this->db->insert( 'testitem',
+                       [
+                               'ti_run' => $this->curRun,
+                               'ti_name' => $test['desc'],
+                               'ti_success' => $result->isSuccess() ? 1 : 0,
+                       ],
+                       __METHOD__ );
+       }
+
+       /**
+        * Commit transaction and clean up for result recording
+        */
+       function end() {
+               $this->db->commit( __METHOD__ );
+       }
+}
+
diff --git a/tests/parser/DjVuSupport.php b/tests/parser/DjVuSupport.php
new file mode 100644 (file)
index 0000000..4739be4
--- /dev/null
@@ -0,0 +1,58 @@
+<?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
+ * @ingroup Testing
+ */
+
+/**
+ * Initialize and detect the DjVu files support
+ */
+class DjVuSupport {
+
+       /**
+        * Initialises DjVu tools global with default values
+        */
+       public function __construct() {
+               global $wgDjvuRenderer, $wgDjvuDump, $wgDjvuToXML, $wgFileExtensions, $wgDjvuTxt;
+
+               $wgDjvuRenderer = $wgDjvuRenderer ? $wgDjvuRenderer : '/usr/bin/ddjvu';
+               $wgDjvuDump = $wgDjvuDump ? $wgDjvuDump : '/usr/bin/djvudump';
+               $wgDjvuToXML = $wgDjvuToXML ? $wgDjvuToXML : '/usr/bin/djvutoxml';
+               $wgDjvuTxt = $wgDjvuTxt ? $wgDjvuTxt : '/usr/bin/djvutxt';
+
+               if ( !in_array( 'djvu', $wgFileExtensions ) ) {
+                       $wgFileExtensions[] = 'djvu';
+               }
+       }
+
+       /**
+        * Returns true if the DjVu tools are usable
+        *
+        * @return bool
+        */
+       public function isEnabled() {
+               global $wgDjvuRenderer, $wgDjvuDump, $wgDjvuToXML, $wgDjvuTxt;
+
+               return is_executable( $wgDjvuRenderer )
+                       && is_executable( $wgDjvuDump )
+                       && is_executable( $wgDjvuToXML )
+                       && is_executable( $wgDjvuTxt );
+       }
+}
+
diff --git a/tests/parser/MultiTestRecorder.php b/tests/parser/MultiTestRecorder.php
new file mode 100644 (file)
index 0000000..5fbfecf
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * This is a TestRecorder representing a collection of other TestRecorders.
+ * It proxies calls to all constituent objects.
+ */
+class MultiTestRecorder extends TestRecorder {
+       private $recorders = [];
+
+       public function addRecorder( TestRecorder $recorder ) {
+               $this->recorders[] = $recorder;
+       }
+
+       private function proxy( $funcName, $args ) {
+               foreach ( $this->recorders as $recorder ) {
+                       call_user_func_array( [ $recorder, $funcName ], $args );
+               }
+       }
+
+       public function start() {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function startTest( $test ) {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function startSuite( $path ) {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function endSuite( $path ) {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function record( $test, ParserTestResult $result ) {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function warning( $message ) {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function skipped( $test, $subtest ) {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function report() {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+
+       public function end() {
+               $this->proxy( __FUNCTION__, func_get_args() );
+       }
+}
diff --git a/tests/parser/ParserTestParserHook.php b/tests/parser/ParserTestParserHook.php
new file mode 100644 (file)
index 0000000..5bf50ea
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/**
+ * A basic extension that's used by the parser tests to test whether input and
+ * arguments are passed to extensions properly.
+ *
+ * Copyright © 2005, 2006 Ævar Arnfjörð Bjarmason
+ *
+ * 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 Testing
+ * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+ */
+
+class ParserTestParserHook {
+
+       static function setup( &$parser ) {
+               $parser->setHook( 'tag', [ __CLASS__, 'dumpHook' ] );
+               $parser->setHook( 'tåg', [ __CLASS__, 'dumpHook' ] );
+               $parser->setHook( 'statictag', [ __CLASS__, 'staticTagHook' ] );
+               return true;
+       }
+
+       static function dumpHook( $in, $argv ) {
+               return "<pre>\n" .
+                       var_export( $in, true ) . "\n" .
+                       var_export( $argv, true ) . "\n" .
+                       "</pre>";
+       }
+
+       static function staticTagHook( $in, $argv, $parser ) {
+               if ( !count( $argv ) ) {
+                       $parser->static_tag_buf = $in;
+                       return '';
+               } elseif ( count( $argv ) === 1 && isset( $argv['action'] )
+                       && $argv['action'] === 'flush' && $in === null
+               ) {
+                       // Clear the buffer, we probably don't need to
+                       if ( isset( $parser->static_tag_buf ) ) {
+                               $tmp = $parser->static_tag_buf;
+                       } else {
+                               $tmp = '';
+                       }
+                       $parser->static_tag_buf = null;
+                       return $tmp;
+               } else { // wtf?
+                       return
+                               "\nCall this extension as <statictag>string</statictag> or as" .
+                               " <statictag action=flush/>, not in any other way.\n" .
+                               "text: " . var_export( $in, true ) . "\n" .
+                               "argv: " . var_export( $argv, true ) . "\n";
+               }
+       }
+}
diff --git a/tests/parser/ParserTestPrinter.php b/tests/parser/ParserTestPrinter.php
new file mode 100644 (file)
index 0000000..cad3a53
--- /dev/null
@@ -0,0 +1,326 @@
+<?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
+ * @ingroup Testing
+ */
+
+/**
+ * This is a TestRecorder responsible for printing information about progress,
+ * success and failure to the console. It is specific to the parserTests.php
+ * frontend.
+ */
+class ParserTestPrinter extends TestRecorder {
+       private $total;
+       private $success;
+       private $skipped;
+       private $term;
+       private $showDiffs;
+       private $showProgress;
+       private $showFailure;
+       private $showOutput;
+       private $useDwdiff;
+       private $markWhitespace;
+       private $xmlError;
+
+       function __construct( $term, $options ) {
+               $this->term = $term;
+               $options += [
+                       'showDiffs' => true,
+                       'showProgress' => true,
+                       'showFailure' => true,
+                       'showOutput' => false,
+                       'useDwdiff' => false,
+                       'markWhitespace' => false,
+               ];
+               $this->showDiffs = $options['showDiffs'];
+               $this->showProgress = $options['showProgress'];
+               $this->showFailure = $options['showFailure'];
+               $this->showOutput = $options['showOutput'];
+               $this->useDwdiff = $options['useDwdiff'];
+               $this->markWhitespace = $options['markWhitespace'];
+       }
+
+       public function start() {
+               $this->total = 0;
+               $this->success = 0;
+               $this->skipped = 0;
+       }
+
+       public function startTest( $test ) {
+               if ( $this->showProgress ) {
+                       $this->showTesting( $test['desc'] );
+               }
+       }
+
+       private function showTesting( $desc ) {
+               print "Running test $desc... ";
+       }
+
+       /**
+        * Show "Reading tests from ..."
+        *
+        * @param string $path
+        */
+       public function startSuite( $path ) {
+               print $this->term->color( 1 ) .
+                       "Running parser tests from \"$path\"..." .
+                       $this->term->reset() .
+                       "\n";
+       }
+
+       public function endSuite( $path ) {
+               print "\n";
+       }
+
+       public function record( $test, ParserTestResult $result ) {
+               $this->total++;
+               $this->success += ( $result->isSuccess() ? 1 : 0 );
+
+               if ( $result->isSuccess() ) {
+                       $this->showSuccess( $result );
+               } else {
+                       $this->showFailure( $result );
+               }
+       }
+
+       /**
+        * Print a happy success message.
+        *
+        * @param ParserTestResult $testResult
+        * @return bool
+        */
+       private function showSuccess( ParserTestResult $testResult ) {
+               if ( $this->showProgress ) {
+                       print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n";
+               }
+       }
+
+       /**
+        * Print a failure message and provide some explanatory output
+        * about what went wrong if so configured.
+        *
+        * @param ParserTestResult $testResult
+        * @return bool
+        */
+       private function showFailure( ParserTestResult $testResult ) {
+               if ( $this->showFailure ) {
+                       if ( !$this->showProgress ) {
+                               # In quiet mode we didn't show the 'Testing' message before the
+                               # test, in case it succeeded. Show it now:
+                               $this->showTesting( $testResult->getDescription() );
+                       }
+
+                       print $this->term->color( '31' ) . 'FAILED!' . $this->term->reset() . "\n";
+
+                       if ( $this->showOutput ) {
+                               print "--- Expected ---\n{$testResult->expected}\n";
+                               print "--- Actual ---\n{$testResult->actual}\n";
+                       }
+
+                       if ( $this->showDiffs ) {
+                               print $this->quickDiff( $testResult->expected, $testResult->actual );
+                               if ( !$this->wellFormed( $testResult->actual ) ) {
+                                       print "XML error: $this->xmlError\n";
+                               }
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * Run given strings through a diff and return the (colorized) output.
+        * Requires writable /tmp directory and a 'diff' command in the PATH.
+        *
+        * @param string $input
+        * @param string $output
+        * @param string $inFileTail Tailing for the input file name
+        * @param string $outFileTail Tailing for the output file name
+        * @return string
+        */
+       private function quickDiff( $input, $output,
+               $inFileTail = 'expected', $outFileTail = 'actual'
+       ) {
+               if ( $this->markWhitespace ) {
+                       $pairs = [
+                               "\n" => '¶',
+                               ' ' => '·',
+                               "\t" => '→'
+                       ];
+                       $input = strtr( $input, $pairs );
+                       $output = strtr( $output, $pairs );
+               }
+
+               # Windows, or at least the fc utility, is retarded
+               $slash = wfIsWindows() ? '\\' : '/';
+               $prefix = wfTempDir() . "{$slash}mwParser-" . mt_rand();
+
+               $infile = "$prefix-$inFileTail";
+               $this->dumpToFile( $input, $infile );
+
+               $outfile = "$prefix-$outFileTail";
+               $this->dumpToFile( $output, $outfile );
+
+               $shellInfile = wfEscapeShellArg( $infile );
+               $shellOutfile = wfEscapeShellArg( $outfile );
+
+               global $wgDiff3;
+               // we assume that people with diff3 also have usual diff
+               if ( $this->useDwdiff ) {
+                       $shellCommand = 'dwdiff -Pc';
+               } else {
+                       $shellCommand = ( wfIsWindows() && !$wgDiff3 ) ? 'fc' : 'diff -au';
+               }
+
+               $diff = wfShellExec( "$shellCommand $shellInfile $shellOutfile" );
+
+               unlink( $infile );
+               unlink( $outfile );
+
+               if ( $this->useDwdiff ) {
+                       return $diff;
+               } else {
+                       return $this->colorDiff( $diff );
+               }
+       }
+
+       /**
+        * Write the given string to a file, adding a final newline.
+        *
+        * @param string $data
+        * @param string $filename
+        */
+       private function dumpToFile( $data, $filename ) {
+               $file = fopen( $filename, "wt" );
+               fwrite( $file, $data . "\n" );
+               fclose( $file );
+       }
+
+       /**
+        * Colorize unified diff output if set for ANSI color output.
+        * Subtractions are colored blue, additions red.
+        *
+        * @param string $text
+        * @return string
+        */
+       private function colorDiff( $text ) {
+               return preg_replace(
+                       [ '/^(-.*)$/m', '/^(\+.*)$/m' ],
+                       [ $this->term->color( 34 ) . '$1' . $this->term->reset(),
+                               $this->term->color( 31 ) . '$1' . $this->term->reset() ],
+                       $text );
+       }
+
+       private function wellFormed( $text ) {
+               $html =
+                       Sanitizer::hackDocType() .
+                               '<html>' .
+                               $text .
+                               '</html>';
+
+               $parser = xml_parser_create( "UTF-8" );
+
+               # case folding violates XML standard, turn it off
+               xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
+
+               if ( !xml_parse( $parser, $html, true ) ) {
+                       $err = xml_error_string( xml_get_error_code( $parser ) );
+                       $position = xml_get_current_byte_index( $parser );
+                       $fragment = $this->extractFragment( $html, $position );
+                       $this->xmlError = "$err at byte $position:\n$fragment";
+                       xml_parser_free( $parser );
+
+                       return false;
+               }
+
+               xml_parser_free( $parser );
+
+               return true;
+       }
+
+       private function extractFragment( $text, $position ) {
+               $start = max( 0, $position - 10 );
+               $before = $position - $start;
+               $fragment = '...' .
+                       $this->term->color( 34 ) .
+                       substr( $text, $start, $before ) .
+                       $this->term->color( 0 ) .
+                       $this->term->color( 31 ) .
+                       $this->term->color( 1 ) .
+                       substr( $text, $position, 1 ) .
+                       $this->term->color( 0 ) .
+                       $this->term->color( 34 ) .
+                       substr( $text, $position + 1, 9 ) .
+                       $this->term->color( 0 ) .
+                       '...';
+               $display = str_replace( "\n", ' ', $fragment );
+               $caret = '   ' .
+                       str_repeat( ' ', $before ) .
+                       $this->term->color( 31 ) .
+                       '^' .
+                       $this->term->color( 0 );
+
+               return "$display\n$caret";
+       }
+
+       /**
+        * Show a warning to the user
+        */
+       public function warning( $message ) {
+               echo "$message\n";
+       }
+
+       /**
+        * Mark a test skipped
+        */
+       public function skipped( $test, $subtest ) {
+               if ( $this->showProgress ) {
+                       print $this->term->color( '1;33' ) . 'SKIPPED' . $this->term->reset() . "\n";
+               }
+               $this->skipped++;
+       }
+
+       public function report() {
+               if ( $this->total > 0 ) {
+                       $this->reportPercentage( $this->success, $this->total );
+               } else {
+                       print $this->term->color( 31 ) . "No tests found." . $this->term->reset() . "\n";
+               }
+       }
+
+       private function reportPercentage( $success, $total ) {
+               $ratio = wfPercent( 100 * $success / $total );
+               print $this->term->color( 1 ) . "Passed $success of $total tests ($ratio)";
+               if ( $this->skipped ) {
+                       print ", skipped {$this->skipped}";
+               }
+               print "... ";
+
+               if ( $success == $total ) {
+                       print $this->term->color( 32 ) . "ALL TESTS PASSED!";
+               } else {
+                       $failed = $total - $success;
+                       print $this->term->color( 31 ) . "$failed tests failed!";
+               }
+
+               print $this->term->reset() . "\n";
+
+               return ( $success == $total );
+       }
+}
+
index a7b3672..6396a01 100644 (file)
  * @since 1.22
  */
 class ParserTestResult {
-       /**
-        * Description of the parser test.
-        *
-        * This is usually the text used to describe a parser test in the .txt
-        * files.  It is initialized on a construction and you most probably
-        * never want to change it.
-        */
-       public $description;
+       /** The test info array */
+       public $test;
        /** Text that was expected */
        public $expected;
        /** Actual text rendered */
        public $actual;
 
        /**
-        * @param string $description A short text describing the parser test
-        *   usually the text in the parser test .txt file.  The description
-        *   is later available using the property $description.
+        * @param array $test The test info array from TestIterator
+        * @param string $expected The normalized expected output
+        * @param string $actual The actual output
         */
-       public function __construct( $description ) {
-               $this->description = $description;
+       public function __construct( $test, $expected, $actual ) {
+               $this->test = $test;
+               $this->expected = $expected;
+               $this->actual = $actual;
        }
 
        /**
@@ -41,4 +37,8 @@ class ParserTestResult {
        public function isSuccess() {
                return $this->expected === $this->actual;
        }
+
+       public function getDescription() {
+               return $this->test['desc'];
+       }
 }
diff --git a/tests/parser/ParserTestResultNormalizer.php b/tests/parser/ParserTestResultNormalizer.php
new file mode 100644 (file)
index 0000000..a15d09e
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+/**
+ * @file
+ * @ingroup Testing
+ */
+
+class ParserTestResultNormalizer {
+       protected $doc, $xpath, $invalid;
+
+       public static function normalize( $text, $funcs ) {
+               $norm = new self( $text );
+               if ( $norm->invalid ) {
+                       return $text;
+               }
+               foreach ( $funcs as $func ) {
+                       $norm->$func();
+               }
+               return $norm->serialize();
+       }
+
+       protected function __construct( $text ) {
+               $this->doc = new DOMDocument( '1.0', 'utf-8' );
+
+               // Note: parsing a supposedly XHTML document with an XML parser is not
+               // guaranteed to give accurate results. For example, it may introduce
+               // differences in the number of line breaks in <pre> tags.
+
+               MediaWiki\suppressWarnings();
+               if ( !$this->doc->loadXML( '<html><body>' . $text . '</body></html>' ) ) {
+                       $this->invalid = true;
+               }
+               MediaWiki\restoreWarnings();
+               $this->xpath = new DOMXPath( $this->doc );
+               $this->body = $this->xpath->query( '//body' )->item( 0 );
+       }
+
+       protected function removeTbody() {
+               foreach ( $this->xpath->query( '//tbody' ) as $tbody ) {
+                       while ( $tbody->firstChild ) {
+                               $child = $tbody->firstChild;
+                               $tbody->removeChild( $child );
+                               $tbody->parentNode->insertBefore( $child, $tbody );
+                       }
+                       $tbody->parentNode->removeChild( $tbody );
+               }
+       }
+
+       /**
+        * The point of this function is to produce a normalized DOM in which
+        * Tidy's output matches the output of html5depurate. Tidy both trims
+        * and pretty-prints, so this requires fairly aggressive treatment.
+        *
+        * In particular, note that Tidy converts <pre>x</pre> to <pre>\nx\n</pre>,
+        * which theoretically affects display since the second line break is not
+        * ignored by compliant HTML parsers.
+        *
+        * This function also removes empty elements, as does Tidy.
+        */
+       protected function trimWhitespace() {
+               foreach ( $this->xpath->query( '//text()' ) as $child ) {
+                       if ( strtolower( $child->parentNode->nodeName ) === 'pre' ) {
+                               // Just trim one line break from the start and end
+                               if ( substr_compare( $child->data, "\n", 0 ) === 0 ) {
+                                       $child->data = substr( $child->data, 1 );
+                               }
+                               if ( substr_compare( $child->data, "\n", -1 ) === 0 ) {
+                                       $child->data = substr( $child->data, 0, -1 );
+                               }
+                       } else {
+                               // Trim all whitespace
+                               $child->data = trim( $child->data );
+                       }
+                       if ( $child->data === '' ) {
+                               $child->parentNode->removeChild( $child );
+                       }
+               }
+       }
+
+       /**
+        * Serialize the XML DOM for comparison purposes. This does not generate HTML.
+        */
+       protected function serialize() {
+               return strtr( $this->doc->saveXML( $this->body ),
+                       [ '<body>' => '', '</body>' => '' ] );
+       }
+}
+
diff --git a/tests/parser/ParserTestRunner.php b/tests/parser/ParserTestRunner.php
new file mode 100644 (file)
index 0000000..ba7f8f8
--- /dev/null
@@ -0,0 +1,1591 @@
+<?php
+/**
+ * Generic backend for the MediaWiki parser test suite, used by both the
+ * standalone parserTests.php and the PHPUnit "parsertests" suite.
+ *
+ * Copyright © 2004, 2010 Brion Vibber <brion@pobox.com>
+ * https://www.mediawiki.org/
+ *
+ * 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
+ *
+ * @todo Make this more independent of the configuration (and if possible the database)
+ * @file
+ * @ingroup Testing
+ */
+use MediaWiki\MediaWikiServices;
+
+/**
+ * @ingroup Testing
+ */
+class ParserTestRunner {
+       /**
+        * @var bool $useTemporaryTables Use temporary tables for the temporary database
+        */
+       private $useTemporaryTables = true;
+
+       /**
+        * @var array $setupDone The status of each setup function
+        */
+       private $setupDone = [
+               'staticSetup' => false,
+               'perTestSetup' => false,
+               'setupDatabase' => false,
+               'setDatabase' => false,
+               'setupUploads' => false,
+       ];
+
+       /**
+        * Our connection to the database
+        * @var DatabaseBase
+        */
+       private $db;
+
+       /**
+        * Database clone helper
+        * @var CloneDatabase
+        */
+       private $dbClone;
+
+       /**
+        * @var DjVuSupport
+        */
+       private $djVuSupport;
+
+       /**
+        * @var TidySupport
+        */
+       private $tidySupport;
+
+       /**
+        * @var TidyDriverBase
+        */
+       private $tidyDriver = null;
+
+       /**
+        * @var TestRecorder
+        */
+       private $recorder;
+
+       /**
+        * The upload directory, or null to not set up an upload directory
+        *
+        * @var string|null
+        */
+       private $uploadDir = null;
+
+       /**
+        * The name of the file backend to use, or null to use MockFileBackend.
+        * @var string|null
+        */
+       private $fileBackendName;
+
+       /**
+        * A complete regex for filtering tests.
+        * @var string
+        */
+       private $regex;
+
+       /**
+        * A list of normalization functions to apply to the expected and actual
+        * output.
+        * @var array
+        */
+       private $normalizationFunctions = [];
+
+       /**
+        * @param TestRecorder $recorder
+        * @param array $options
+        */
+       public function __construct( TestRecorder $recorder, $options = [] ) {
+               $this->recorder = $recorder;
+
+               if ( isset( $options['norm'] ) ) {
+                       foreach ( $options['norm'] as $func ) {
+                               if ( in_array( $func, [ 'removeTbody', 'trimWhitespace' ] ) ) {
+                                       $this->normalizationFunctions[] = $func;
+                               } else {
+                                       $this->recorder->warning(
+                                               "Warning: unknown normalization option \"$func\"\n" );
+                               }
+                       }
+               }
+
+               if ( isset( $options['regex'] ) && $options['regex'] !== false ) {
+                       $this->regex = $options['regex'];
+               } else {
+                       # Matches anything
+                       $this->regex = '//';
+               }
+
+               $this->keepUploads = !empty( $options['keep-uploads'] );
+
+               $this->fileBackendName = isset( $options['file-backend'] ) ?
+                       $options['file-backend'] : false;
+
+               $this->runDisabled = !empty( $options['run-disabled'] );
+               $this->runParsoid = !empty( $options['run-parsoid'] );
+
+               $this->djVuSupport = new DjVuSupport();
+               $this->tidySupport = new TidySupport( !empty( $options['use-tidy-config'] ) );
+               if ( !$this->tidySupport->isEnabled() ) {
+                       $this->recorder->warning(
+                               "Warning: tidy is not installed, skipping some tests\n" );
+               }
+
+               if ( isset( $options['upload-dir'] ) ) {
+                       $this->uploadDir = $options['upload-dir'];
+               }
+       }
+
+       public function getRecorder() {
+               return $this->recorder;
+       }
+
+       /**
+        * Do any setup which can be done once for all tests, independent of test
+        * options, except for database setup.
+        *
+        * Public setup functions in this class return a ScopedCallback object. When
+        * this object is destroyed by going out of scope, teardown of the
+        * corresponding test setup is performed.
+        *
+        * Teardown objects may be chained by passing a ScopedCallback from a
+        * previous setup stage as the $nextTeardown parameter. This enforces the
+        * convention that teardown actions are taken in reverse order to the
+        * corresponding setup actions. When $nextTeardown is specified, a
+        * ScopedCallback will be returned which first tears down the current
+        * setup stage, and then tears down the previous setup stage which was
+        * specified by $nextTeardown.
+        *
+        * @param ScopedCallback|null $nextTeardown
+        * @return ScopedCallback
+        */
+       public function staticSetup( $nextTeardown = null ) {
+               // A note on coding style:
+
+               // The general idea here is to keep setup code together with
+               // corresponding teardown code, in a fine-grained manner. We have two
+               // arrays: $setup and $teardown. The code snippets in the $setup array
+               // are executed at the end of the method, before it returns, and the
+               // code snippets in the $teardown array are executed in reverse order
+               // when the ScopedCallback object is consumed.
+
+               // Because it is a common operation to save, set and restore global
+               // variables, we have an additional convention: when the array key of
+               // $setup is a string, the string is taken to be the name of the global
+               // variable, and the element value is taken to be the desired new value.
+
+               // It's acceptable to just do the setup immediately, instead of adding
+               // a closure to $setup, except when the setup action depends on global
+               // variable initialisation being done first. In this case, you have to
+               // append a closure to $setup after the global variable is appended.
+
+               // When you add to setup functions in this class, please keep associated
+               // setup and teardown actions together in the source code, and please
+               // add comments explaining why the setup action is necessary.
+
+               $setup = [];
+               $teardown = [];
+
+               $teardown[] = $this->markSetupDone( 'staticSetup' );
+
+               // Some settings which influence HTML output
+               $setup['wgSitename'] = 'MediaWiki';
+               $setup['wgServer'] = 'http://example.org';
+               $setup['wgServerName'] = 'example.org';
+               $setup['wgScriptPath'] = '';
+               $setup['wgScript'] = '/index.php';
+               $setup['wgResourceBasePath'] = '';
+               $setup['wgStylePath'] = '/skins';
+               $setup['wgExtensionAssetsPath'] = '/extensions';
+               $setup['wgArticlePath'] = '/wiki/$1';
+               $setup['wgActionPaths'] = [];
+               $setup['wgVariantArticlePath'] = false;
+               $setup['wgUploadNavigationUrl'] = false;
+               $setup['wgCapitalLinks'] = true;
+               $setup['wgNoFollowLinks'] = true;
+               $setup['wgNoFollowDomainExceptions'] = [ 'no-nofollow.org' ];
+               $setup['wgExternalLinkTarget'] = false;
+               $setup['wgExperimentalHtmlIds'] = false;
+               $setup['wgLocaltimezone'] = 'UTC';
+               $setup['wgHtml5'] = true;
+               $setup['wgDisableLangConversion'] = false;
+               $setup['wgDisableTitleConversion'] = false;
+
+               // "extra language links"
+               // see https://gerrit.wikimedia.org/r/111390
+               $setup['wgExtraInterlanguageLinkPrefixes'] = [ 'mul' ];
+
+               // All FileRepo changes should be done here by injecting services,
+               // there should be no need to change global variables.
+               RepoGroup::setSingleton( $this->createRepoGroup() );
+               $teardown[] = function () {
+                       RepoGroup::destroySingleton();
+               };
+
+               // Set up null lock managers
+               $setup['wgLockManagers'] = [ [
+                       'name' => 'fsLockManager',
+                       'class' => 'NullLockManager',
+               ], [
+                       'name' => 'nullLockManager',
+                       'class' => 'NullLockManager',
+               ] ];
+               $reset = function() {
+                       LockManagerGroup::destroySingletons();
+               };
+               $setup[] = $reset;
+               $teardown[] = $reset;
+
+               // This allows article insertion into the prefixed DB
+               $setup['wgDefaultExternalStore'] = false;
+
+               // This might slightly reduce memory usage
+               $setup['wgAdaptiveMessageCache'] = true;
+
+               // This is essential and overrides disabling of database messages in TestSetup
+               $setup['wgUseDatabaseMessages'] = true;
+               $reset = function () {
+                       MessageCache::destroyInstance();
+               };
+               $setup[] = $reset;
+               $teardown[] = $reset;
+
+               // It's not necessary to actually convert any files
+               $setup['wgSVGConverter'] = 'null';
+               $setup['wgSVGConverters'] = [ 'null' => 'echo "1">$output' ];
+
+               // Fake constant timestamp
+               Hooks::register( 'ParserGetVariableValueTs', 'ParserTestRunner::getFakeTimestamp' );
+               $teardown[] = function () {
+                       Hooks::clear( 'ParserGetVariableValueTs' );
+               };
+
+               $this->appendNamespaceSetup( $setup, $teardown );
+
+               // Set up interwikis and append teardown function
+               $teardown[] = $this->setupInterwikis();
+
+               // This affects title normalization in links. It invalidates
+               // MediaWikiTitleCodec objects.
+               $setup['wgLocalInterwikis'] = [ 'local', 'mi' ];
+               $reset = function () {
+                       $this->resetTitleServices();
+               };
+               $setup[] = $reset;
+               $teardown[] = $reset;
+
+               // Set up a mock MediaHandlerFactory
+               MediaWikiServices::getInstance()->disableService( 'MediaHandlerFactory' );
+               MediaWikiServices::getInstance()->redefineService(
+                       'MediaHandlerFactory',
+                       function() {
+                               return new MockMediaHandlerFactory();
+                       }
+               );
+               $teardown[] = function () {
+                       MediaWikiServices::getInstance()->resetServiceForTesting( 'MediaHandlerFactory' );
+               };
+
+               // SqlBagOStuff broke when using temporary tables on r40209 (bug 15892).
+               // It seems to have been fixed since (r55079?), but regressed at some point before r85701.
+               // This works around it for now...
+               global $wgObjectCaches;
+               $setup['wgObjectCaches'] = [ CACHE_DB => $wgObjectCaches['hash'] ] + $wgObjectCaches;
+               if ( isset( ObjectCache::$instances[CACHE_DB] ) ) {
+                       $savedCache = ObjectCache::$instances[CACHE_DB];
+                       ObjectCache::$instances[CACHE_DB] = new HashBagOStuff;
+                       $teardown[] = function () use ( $savedCache ) {
+                               ObjectCache::$instances[CACHE_DB] = $savedCache;
+                       };
+               }
+
+               $teardown[] = $this->executeSetupSnippets( $setup );
+
+               // Schedule teardown snippets in reverse order
+               return $this->createTeardownObject( $teardown, $nextTeardown );
+       }
+
+       private function appendNamespaceSetup( &$setup, &$teardown ) {
+               // Add a namespace shadowing a interwiki link, to test
+               // proper precedence when resolving links. (bug 51680)
+               $setup['wgExtraNamespaces'] = [
+                       100 => 'MemoryAlpha',
+                       101 => 'MemoryAlpha_talk'
+               ];
+               // Changing wgExtraNamespaces invalidates caches in MWNamespace and
+               // any live Language object, both on setup and teardown
+               $reset = function () {
+                       MWNamespace::getCanonicalNamespaces( true );
+                       $GLOBALS['wgContLang']->resetNamespaces();
+               };
+               $setup[] = $reset;
+               $teardown[] = $reset;
+       }
+
+       /**
+        * Create a RepoGroup object appropriate for the current configuration
+        * @return RepoGroup
+        */
+       protected function createRepoGroup() {
+               if ( $this->uploadDir ) {
+                       if ( $this->fileBackendName ) {
+                               throw new MWException( 'You cannot specify both use-filebackend and upload-dir' );
+                       }
+                       $backend = new FSFileBackend( [
+                               'name' => 'local-backend',
+                               'wikiId' => wfWikiID(),
+                               'basePath' => $this->uploadDir
+                       ] );
+               } elseif ( $this->fileBackendName ) {
+                       global $wgFileBackends;
+                       $name = $this->fileBackendName;
+                       $useConfig = false;
+                       foreach ( $wgFileBackends as $conf ) {
+                               if ( $conf['name'] === $name ) {
+                                       $useConfig = $conf;
+                               }
+                       }
+                       if ( $useConfig === false ) {
+                               throw new MWException( "Unable to find file backend \"$name\"" );
+                       }
+                       $useConfig['name'] = 'local-backend'; // swap name
+                       unset( $useConfig['lockManager'] );
+                       unset( $useConfig['fileJournal'] );
+                       $class = $useConfig['class'];
+                       $backend = new $class( $useConfig );
+               } else {
+                       # Replace with a mock. We do not care about generating real
+                       # files on the filesystem, just need to expose the file
+                       # informations.
+                       $backend = new MockFileBackend( [
+                               'name' => 'local-backend',
+                               'wikiId' => wfWikiID()
+                       ] );
+               }
+
+               return new RepoGroup(
+                       [
+                               'class' => 'LocalRepo',
+                               'name' => 'local',
+                               'url' => 'http://example.com/images',
+                               'hashLevels' => 2,
+                               'transformVia404' => false,
+                               'backend' => $backend
+                       ],
+                       []
+               );
+       }
+
+       /**
+        * Execute an array in which elements with integer keys are taken to be
+        * callable objects, and other elements are taken to be global variable
+        * set operations, with the key giving the variable name and the value
+        * giving the new global variable value. A closure is returned which, when
+        * executed, sets the global variables back to the values they had before
+        * this function was called.
+        *
+        * @see staticSetup
+        *
+        * @param array $setup
+        * @return closure
+        */
+       protected function executeSetupSnippets( $setup ) {
+               $saved = [];
+               foreach ( $setup as $name => $value ) {
+                       if ( is_int( $name ) ) {
+                               $value();
+                       } else {
+                               $saved[$name] = isset( $GLOBALS[$name] ) ? $GLOBALS[$name] : null;
+                               $GLOBALS[$name] = $value;
+                       }
+               }
+               return function () use ( $saved ) {
+                       $this->executeSetupSnippets( $saved );
+               };
+       }
+
+       /**
+        * Take a setup array in the same format as the one given to
+        * executeSetupSnippets(), and return a ScopedCallback which, when consumed,
+        * executes the snippets in the setup array in reverse order. This is used
+        * to create "teardown objects" for the public API.
+        *
+        * @see staticSetup
+        *
+        * @param array $teardown The snippet array
+        * @param ScopedCallback|null A ScopedCallback to consume
+        * @return ScopedCallback
+        */
+       protected function createTeardownObject( $teardown, $nextTeardown ) {
+               return new ScopedCallback( function() use ( $teardown, $nextTeardown ) {
+                       // Schedule teardown snippets in reverse order
+                       $teardown = array_reverse( $teardown );
+
+                       $this->executeSetupSnippets( $teardown );
+                       if ( $nextTeardown ) {
+                               ScopedCallback::consume( $nextTeardown );
+                       }
+               } );
+       }
+
+       /**
+        * Set a setupDone flag to indicate that setup has been done, and return
+        * the teardown closure. If the flag was already set, throw an exception.
+        *
+        * @param string $funcName The setup function name
+        * @return closure
+        */
+       protected function markSetupDone( $funcName ) {
+               if ( $this->setupDone[$funcName] ) {
+                       throw new MWException( "$funcName is already done" );
+               }
+               $this->setupDone[$funcName] = true;
+               return function () use ( $funcName ) {
+                       wfDebug( "markSetupDone unmarked $funcName" );
+                       $this->setupDone[$funcName] = false;
+               };
+       }
+
+       /**
+        * Ensure a given setup stage has been done, throw an exception if it has
+        * not.
+        */
+       protected function checkSetupDone( $funcName, $funcName2 = null ) {
+               if ( !$this->setupDone[$funcName]
+                       && ( $funcName === null || !$this->setupDone[$funcName2] )
+               ) {
+                       throw new MWException( "$funcName must be called before calling " .
+                               wfGetCaller() );
+               }
+       }
+
+       /**
+        * Determine whether a particular setup function has been run
+        *
+        * @param string $funcName
+        * @return boolean
+        */
+       public function isSetupDone( $funcName ) {
+               return isset( $this->setupDone[$funcName] ) ? $this->setupDone[$funcName] : false;
+       }
+
+       /**
+        * Insert hardcoded interwiki in the lookup table.
+        *
+        * This function insert a set of well known interwikis that are used in
+        * the parser tests. They can be considered has fixtures are injected in
+        * the interwiki cache by using the 'InterwikiLoadPrefix' hook.
+        * Since we are not interested in looking up interwikis in the database,
+        * the hook completely replace the existing mechanism (hook returns false).
+        *
+        * @return closure for teardown
+        */
+       private function setupInterwikis() {
+               # Hack: insert a few Wikipedia in-project interwiki prefixes,
+               # for testing inter-language links
+               Hooks::register( 'InterwikiLoadPrefix', function ( $prefix, &$iwData ) {
+                       static $testInterwikis = [
+                               'local' => [
+                                       'iw_url' => 'http://doesnt.matter.org/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 0 ],
+                               'wikipedia' => [
+                                       'iw_url' => 'http://en.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 0 ],
+                               'meatball' => [
+                                       'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 0 ],
+                               'memoryalpha' => [
+                                       'iw_url' => 'http://www.memory-alpha.org/en/index.php/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 0 ],
+                               'zh' => [
+                                       'iw_url' => 'http://zh.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ],
+                               'es' => [
+                                       'iw_url' => 'http://es.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ],
+                               'fr' => [
+                                       'iw_url' => 'http://fr.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ],
+                               'ru' => [
+                                       'iw_url' => 'http://ru.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ],
+                               'mi' => [
+                                       'iw_url' => 'http://mi.wikipedia.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ],
+                               'mul' => [
+                                       'iw_url' => 'http://wikisource.org/wiki/$1',
+                                       'iw_api' => '',
+                                       'iw_wikiid' => '',
+                                       'iw_local' => 1 ],
+                       ];
+                       if ( array_key_exists( $prefix, $testInterwikis ) ) {
+                               $iwData = $testInterwikis[$prefix];
+                       }
+
+                       // We only want to rely on the above fixtures
+                       return false;
+               } );// hooks::register
+
+               return function () {
+                       // Tear down
+                       Hooks::clear( 'InterwikiLoadPrefix' );
+               };
+       }
+
+       /**
+        * Reset the Title-related services that need resetting
+        * for each test
+        */
+       private function resetTitleServices() {
+               $services = MediaWikiServices::getInstance();
+               $services->resetServiceForTesting( 'TitleFormatter' );
+               $services->resetServiceForTesting( 'TitleParser' );
+               $services->resetServiceForTesting( '_MediaWikiTitleCodec' );
+               $services->resetServiceForTesting( 'LinkRenderer' );
+               $services->resetServiceForTesting( 'LinkRendererFactory' );
+       }
+
+       /**
+        * Remove last character if it is a newline
+        * @group utility
+        * @param string $s
+        * @return string
+        */
+       public static function chomp( $s ) {
+               if ( substr( $s, -1 ) === "\n" ) {
+                       return substr( $s, 0, -1 );
+               } else {
+                       return $s;
+               }
+       }
+
+       /**
+        * Run a series of tests listed in the given text files.
+        * Each test consists of a brief description, wikitext input,
+        * and the expected HTML output.
+        *
+        * Prints status updates on stdout and counts up the total
+        * number and percentage of passed tests.
+        *
+        * Handles all setup and teardown.
+        *
+        * @param array $filenames Array of strings
+        * @return bool True if passed all tests, false if any tests failed.
+        */
+       public function runTestsFromFiles( $filenames ) {
+               $ok = false;
+
+               $teardownGuard = $this->staticSetup();
+               $teardownGuard = $this->setupDatabase( $teardownGuard );
+               $teardownGuard = $this->setupUploads( $teardownGuard );
+
+               $this->recorder->start();
+               try {
+                       $ok = true;
+
+                       foreach ( $filenames as $filename ) {
+                               $testFileInfo = TestFileReader::read( $filename, [
+                                       'runDisabled' => $this->runDisabled,
+                                       'runParsoid' => $this->runParsoid,
+                                       'regex' => $this->regex ] );
+
+                               // Don't start the suite if there are no enabled tests in the file
+                               if ( !$testFileInfo['tests'] ) {
+                                       continue;
+                               }
+
+                               $this->recorder->startSuite( $filename );
+                               $ok = $this->runTests( $testFileInfo ) && $ok;
+                               $this->recorder->endSuite( $filename );
+                       }
+
+                       $this->recorder->report();
+               } catch ( DBError $e ) {
+                       $this->recorder->warning( $e->getMessage() );
+               }
+               $this->recorder->end();
+
+               ScopedCallback::consume( $teardownGuard );
+
+               return $ok;
+       }
+
+       /**
+        * Determine whether the current parser has the hooks registered in it
+        * that are required by a file read by TestFileReader.
+        */
+       public function meetsRequirements( $requirements ) {
+               foreach ( $requirements as $requirement ) {
+                       switch ( $requirement['type'] ) {
+                       case 'hook':
+                               $ok = $this->requireHook( $requirement['name'] );
+                               break;
+                       case 'functionHook':
+                               $ok = $this->requireFunctionHook( $requirement['name'] );
+                               break;
+                       case 'transparentHook':
+                               $ok = $this->requireTransparentHook( $requirement['name'] );
+                               break;
+                       }
+                       if ( !$ok ) {
+                               return false;
+                       }
+               }
+               return true;
+       }
+
+       /**
+        * Run the tests from a single file. staticSetup() and setupDatabase()
+        * must have been called already.
+        *
+        * @param array $testFileInfo Parsed file info returned by TestFileReader
+        * @return bool True if passed all tests, false if any tests failed.
+        */
+       public function runTests( $testFileInfo ) {
+               $ok = true;
+
+               $this->checkSetupDone( 'staticSetup' );
+
+               // Don't add articles from the file if there are no enabled tests from the file
+               if ( !$testFileInfo['tests'] ) {
+                       return true;
+               }
+
+               // If any requirements are not met, mark all tests from the file as skipped
+               if ( !$this->meetsRequirements( $testFileInfo['requirements'] ) ) {
+                       foreach ( $testFileInfo['tests'] as $test ) {
+                               $this->recorder->startTest( $test );
+                               $this->recorder->skipped( $test, 'required extension not enabled' );
+                       }
+                       return true;
+               }
+
+               // Add articles
+               $this->addArticles( $testFileInfo['articles'] );
+
+               // Run tests
+               foreach ( $testFileInfo['tests'] as $test ) {
+                       $this->recorder->startTest( $test );
+                       $result =
+                               $this->runTest( $test );
+                       if ( $result !== false ) {
+                               $ok = $ok && $result->isSuccess();
+                               $this->recorder->record( $test, $result );
+                       }
+               }
+
+               return $ok;
+       }
+
+       /**
+        * Get a Parser object
+        *
+        * @param string $preprocessor
+        * @return Parser
+        */
+       function getParser( $preprocessor = null ) {
+               global $wgParserConf;
+
+               $class = $wgParserConf['class'];
+               $parser = new $class( [ 'preprocessorClass' => $preprocessor ] + $wgParserConf );
+               ParserTestParserHook::setup( $parser );
+
+               return $parser;
+       }
+
+       /**
+        * Run a given wikitext input through a freshly-constructed wiki parser,
+        * and compare the output against the expected results.
+        * Prints status and explanatory messages to stdout.
+        *
+        * staticSetup() and setupWikiData() must be called before this function
+        * is entered.
+        *
+        * @param array $test The test parameters:
+        *  - test: The test name
+        *  - desc: The subtest description
+        *  - input: Wikitext to try rendering
+        *  - options: Array of test options
+        *  - config: Overrides for global variables, one per line
+        *
+        * @return ParserTestResult or false if skipped
+        */
+       public function runTest( $test ) {
+               wfDebug( __METHOD__.": running {$test['desc']}" );
+               $opts = $this->parseOptions( $test['options'] );
+               $teardownGuard = $this->perTestSetup( $test );
+
+               $context = RequestContext::getMain();
+               $user = $context->getUser();
+               $options = ParserOptions::newFromContext( $context );
+
+               if ( isset( $opts['djvu'] ) ) {
+                       if ( !$this->djVuSupport->isEnabled() ) {
+                               $this->recorder->skipped( $test,
+                                       'djvu binaries do not exist or are not executable' );
+                               return false;
+                       }
+               }
+
+               if ( isset( $opts['tidy'] ) ) {
+                       if ( !$this->tidySupport->isEnabled() ) {
+                               $this->recorder->skipped( $test, 'tidy extension is not installed' );
+                               return false;
+                       } else {
+                               $options->setTidy( true );
+                       }
+               }
+
+               if ( isset( $opts['title'] ) ) {
+                       $titleText = $opts['title'];
+               } else {
+                       $titleText = 'Parser test';
+               }
+
+               $local = isset( $opts['local'] );
+               $preprocessor = isset( $opts['preprocessor'] ) ? $opts['preprocessor'] : null;
+               $parser = $this->getParser( $preprocessor );
+               $title = Title::newFromText( $titleText );
+
+               if ( isset( $opts['pst'] ) ) {
+                       $out = $parser->preSaveTransform( $test['input'], $title, $user, $options );
+               } elseif ( isset( $opts['msg'] ) ) {
+                       $out = $parser->transformMsg( $test['input'], $options, $title );
+               } elseif ( isset( $opts['section'] ) ) {
+                       $section = $opts['section'];
+                       $out = $parser->getSection( $test['input'], $section );
+               } elseif ( isset( $opts['replace'] ) ) {
+                       $section = $opts['replace'][0];
+                       $replace = $opts['replace'][1];
+                       $out = $parser->replaceSection( $test['input'], $section, $replace );
+               } elseif ( isset( $opts['comment'] ) ) {
+                       $out = Linker::formatComment( $test['input'], $title, $local );
+               } elseif ( isset( $opts['preload'] ) ) {
+                       $out = $parser->getPreloadText( $test['input'], $title, $options );
+               } else {
+                       $output = $parser->parse( $test['input'], $title, $options, true, true, 1337 );
+                       $output->setTOCEnabled( !isset( $opts['notoc'] ) );
+                       $out = $output->getText();
+                       if ( isset( $opts['tidy'] ) ) {
+                               $out = preg_replace( '/\s+$/', '', $out );
+                       }
+
+                       if ( isset( $opts['showtitle'] ) ) {
+                               if ( $output->getTitleText() ) {
+                                       $title = $output->getTitleText();
+                               }
+
+                               $out = "$title\n$out";
+                       }
+
+                       if ( isset( $opts['showindicators'] ) ) {
+                               $indicators = '';
+                               foreach ( $output->getIndicators() as $id => $content ) {
+                                       $indicators .= "$id=$content\n";
+                               }
+                               $out = $indicators . $out;
+                       }
+
+                       if ( isset( $opts['ill'] ) ) {
+                               $out = implode( ' ', $output->getLanguageLinks() );
+                       } elseif ( isset( $opts['cat'] ) ) {
+                               $out = '';
+                               foreach ( $output->getCategories() as $name => $sortkey ) {
+                                       if ( $out !== '' ) {
+                                               $out .= "\n";
+                                       }
+                                       $out .= "cat=$name sort=$sortkey";
+                               }
+                       }
+               }
+
+               ScopedCallback::consume( $teardownGuard );
+
+               $expected = $test['result'];
+               if ( count( $this->normalizationFunctions ) ) {
+                       $expected = ParserTestResultNormalizer::normalize(
+                               $test['expected'], $this->normalizationFunctions );
+                       $out = ParserTestResultNormalizer::normalize( $out, $this->normalizationFunctions );
+               }
+
+               $testResult = new ParserTestResult( $test, $expected, $out );
+               return $testResult;
+       }
+
+       /**
+        * Use a regex to find out the value of an option
+        * @param string $key Name of option val to retrieve
+        * @param array $opts Options array to look in
+        * @param mixed $default Default value returned if not found
+        * @return mixed
+        */
+       private static function getOptionValue( $key, $opts, $default ) {
+               $key = strtolower( $key );
+
+               if ( isset( $opts[$key] ) ) {
+                       return $opts[$key];
+               } else {
+                       return $default;
+               }
+       }
+
+       /**
+        * Given the options string, return an associative array of options.
+        * @todo Move this to TestFileReader
+        *
+        * @param string $instring
+        * @return array
+        */
+       private function parseOptions( $instring ) {
+               $opts = [];
+               // foo
+               // foo=bar
+               // foo="bar baz"
+               // foo=[[bar baz]]
+               // foo=bar,"baz quux"
+               // foo={...json...}
+               $defs = '(?(DEFINE)
+                       (?<qstr>                                        # Quoted string
+                               "
+                               (?:[^\\\\"] | \\\\.)*
+                               "
+                       )
+                       (?<json>
+                               \{              # Open bracket
+                               (?:
+                                       [^"{}] |                                # Not a quoted string or object, or
+                                       (?&qstr) |                              # A quoted string, or
+                                       (?&json)                                # A json object (recursively)
+                               )*
+                               \}              # Close bracket
+                       )
+                       (?<value>
+                               (?:
+                                       (?&qstr)                        # Quoted val
+                               |
+                                       \[\[
+                                               [^]]*                   # Link target
+                                       \]\]
+                               |
+                                       [\w-]+                          # Plain word
+                               |
+                                       (?&json)                        # JSON object
+                               )
+                       )
+               )';
+               $regex = '/' . $defs . '\b
+                       (?<k>[\w-]+)                            # Key
+                       \b
+                       (?:\s*
+                               =                                               # First sub-value
+                               \s*
+                               (?<v>
+                                       (?&value)
+                                       (?:\s*
+                                               ,                               # Sub-vals 1..N
+                                               \s*
+                                               (?&value)
+                                       )*
+                               )
+                       )?
+                       /x';
+               $valueregex = '/' . $defs . '(?&value)/x';
+
+               if ( preg_match_all( $regex, $instring, $matches, PREG_SET_ORDER ) ) {
+                       foreach ( $matches as $bits ) {
+                               $key = strtolower( $bits['k'] );
+                               if ( !isset( $bits['v'] ) ) {
+                                       $opts[$key] = true;
+                               } else {
+                                       preg_match_all( $valueregex, $bits['v'], $vmatches );
+                                       $opts[$key] = array_map( [ $this, 'cleanupOption' ], $vmatches[0] );
+                                       if ( count( $opts[$key] ) == 1 ) {
+                                               $opts[$key] = $opts[$key][0];
+                                       }
+                               }
+                       }
+               }
+               return $opts;
+       }
+
+       private function cleanupOption( $opt ) {
+               if ( substr( $opt, 0, 1 ) == '"' ) {
+                       return stripcslashes( substr( $opt, 1, -1 ) );
+               }
+
+               if ( substr( $opt, 0, 2 ) == '[[' ) {
+                       return substr( $opt, 2, -2 );
+               }
+
+               if ( substr( $opt, 0, 1 ) == '{' ) {
+                       return FormatJson::decode( $opt, true );
+               }
+               return $opt;
+       }
+
+       /**
+        * Do any required setup which is dependent on test options.
+        *
+        * @see staticSetup() for more information about setup/teardown
+        *
+        * @param array $test Test info supplied by TestFileReader
+        * @param callable|null $nextTeardown
+        * @return ScopedCallback
+        */
+       public function perTestSetup( $test, $nextTeardown = null ) {
+               $teardown = [];
+
+               $this->checkSetupDone( 'setupDatabase', 'setDatabase' );
+               $teardown[] = $this->markSetupDone( 'perTestSetup' );
+
+               $opts = $this->parseOptions( $test['options'] );
+               $config = $test['config'];
+
+               // Find out values for some special options.
+               $langCode =
+                       self::getOptionValue( 'language', $opts, 'en' );
+               $variant =
+                       self::getOptionValue( 'variant', $opts, false );
+               $maxtoclevel =
+                       self::getOptionValue( 'wgMaxTocLevel', $opts, 999 );
+               $linkHolderBatchSize =
+                       self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
+
+               $setup = [
+                       'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ),
+                       'wgLanguageCode' => $langCode,
+                       'wgRawHtml' => self::getOptionValue( 'wgRawHtml', $opts, false ),
+                       'wgNamespacesWithSubpages' => [ 0 => isset( $opts['subpage'] ) ],
+                       'wgMaxTocLevel' => $maxtoclevel,
+                       'wgAllowExternalImages' => self::getOptionValue( 'wgAllowExternalImages', $opts, true ),
+                       'wgThumbLimits' => [ self::getOptionValue( 'thumbsize', $opts, 180 ) ],
+                       'wgDefaultLanguageVariant' => $variant,
+                       'wgLinkHolderBatchSize' => $linkHolderBatchSize,
+               ];
+
+               if ( $config ) {
+                       $configLines = explode( "\n", $config );
+
+                       foreach ( $configLines as $line ) {
+                               list( $var, $value )  = explode( '=', $line, 2 );
+                               $setup[$var] = eval( "return $value;" );
+                       }
+               }
+
+               /** @since 1.20 */
+               Hooks::run( 'ParserTestGlobals', [ &$setup ] );
+
+               // Create tidy driver
+               if ( isset( $opts['tidy'] ) ) {
+                       // Cache a driver instance
+                       if ( $this->tidyDriver === null ) {
+                               $this->tidyDriver = MWTidy::factory( $this->tidySupport->getConfig() );
+                       }
+                       $tidy = $this->tidyDriver;
+               } else {
+                       $tidy = false;
+               }
+               MWTidy::setInstance( $tidy );
+               $teardown[] = function () {
+                       MWTidy::destroySingleton();
+               };
+
+               // Set content language. This invalidates the magic word cache and title services
+               wfDebug( "Setting up language $langCode" );
+               $lang = Language::factory( $langCode );
+               $setup['wgContLang'] = $lang;
+               $reset = function () {
+                       MagicWord::clearCache();
+                       $this->resetTitleServices();
+               };
+               $setup[] = $reset;
+               $teardown[] = $reset;
+
+               // Make a user object with the same language
+               $user = new User;
+               $user->setOption( 'language', $langCode );
+               $setup['wgLang'] = $lang;
+
+               // We (re)set $wgThumbLimits to a single-element array above.
+               $user->setOption( 'thumbsize', 0 );
+
+               $setup['wgUser'] = $user;
+
+               // And put both user and language into the context
+               $context = RequestContext::getMain();
+               $context->setUser( $user );
+               $context->setLanguage( $lang );
+               $teardown[] = function () use ( $context ) {
+                       // Reset context to the restored globals
+                       $context->setUser( $GLOBALS['wgUser'] );
+                       $context->setLanguage( $GLOBALS['wgContLang'] );
+               };
+
+               $teardown[] = $this->executeSetupSnippets( $setup );
+
+               return $this->createTeardownObject( $teardown, $nextTeardown );
+       }
+
+       /**
+        * List of temporary tables to create, without prefix.
+        * Some of these probably aren't necessary.
+        * @return array
+        */
+       private function listTables() {
+               $tables = [ 'user', 'user_properties', 'user_former_groups', 'page', 'page_restrictions',
+                       'protected_titles', 'revision', 'text', 'pagelinks', 'imagelinks',
+                       'categorylinks', 'templatelinks', 'externallinks', 'langlinks', 'iwlinks',
+                       'site_stats', 'ipblocks', 'image', 'oldimage',
+                       'recentchanges', 'watchlist', 'interwiki', 'logging', 'log_search',
+                       'querycache', 'objectcache', 'job', 'l10n_cache', 'redirect', 'querycachetwo',
+                       'archive', 'user_groups', 'page_props', 'category'
+               ];
+
+               if ( in_array( $this->db->getType(), [ 'mysql', 'sqlite', 'oracle' ] ) ) {
+                       array_push( $tables, 'searchindex' );
+               }
+
+               // Allow extensions to add to the list of tables to duplicate;
+               // may be necessary if they hook into page save or other code
+               // which will require them while running tests.
+               Hooks::run( 'ParserTestTables', [ &$tables ] );
+
+               return $tables;
+       }
+
+       public function setDatabase( IDatabase $db ) {
+               $this->db = $db;
+               $this->setupDone['setDatabase'] = true;
+       }
+
+       /**
+        * Set up temporary DB tables.
+        *
+        * For best performance, call this once only for all tests. However, it can
+        * be called at the start of each test if more isolation is desired.
+        *
+        * @todo: This is basically an unrefactored copy of
+        * MediaWikiTestCase::setupAllTestDBs. They should be factored out somehow.
+        *
+        * Do not call this function from a MediaWikiTestCase subclass, since
+        * MediaWikiTestCase does its own DB setup. Instead use setDatabase().
+        *
+        * @see staticSetup() for more information about setup/teardown
+        *
+        * @param ScopedCallback|null $nextTeardown The next teardown object
+        * @return ScopedCallback The teardown object
+        */
+       public function setupDatabase( $nextTeardown = null ) {
+               global $wgDBprefix;
+
+               $this->db = wfGetDB( DB_MASTER );
+               $dbType = $this->db->getType();
+
+               if ( $dbType == 'oracle' ) {
+                       $suspiciousPrefixes = [ 'pt_', MediaWikiTestCase::ORA_DB_PREFIX ];
+               } else {
+                       $suspiciousPrefixes = [ 'parsertest_', MediaWikiTestCase::DB_PREFIX ];
+               }
+               if ( in_array( $wgDBprefix, $suspiciousPrefixes ) ) {
+                       throw new MWException( "\$wgDBprefix=$wgDBprefix suggests DB setup is already done" );
+               }
+
+               $teardown = [];
+
+               $teardown[] = $this->markSetupDone( 'setupDatabase' );
+
+               # CREATE TEMPORARY TABLE breaks if there is more than one server
+               if ( wfGetLB()->getServerCount() != 1 ) {
+                       $this->useTemporaryTables = false;
+               }
+
+               $temporary = $this->useTemporaryTables || $dbType == 'postgres';
+               $prefix = $dbType != 'oracle' ? 'parsertest_' : 'pt_';
+
+               $this->dbClone = new CloneDatabase( $this->db, $this->listTables(), $prefix );
+               $this->dbClone->useTemporaryTables( $temporary );
+               $this->dbClone->cloneTableStructure();
+
+               if ( $dbType == 'oracle' ) {
+                       $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
+                       # Insert 0 user to prevent FK violations
+
+                       # Anonymous user
+                       $this->db->insert( 'user', [
+                               'user_id' => 0,
+                               'user_name' => 'Anonymous' ] );
+               }
+
+               $teardown[] = function () {
+                       $this->teardownDatabase();
+               };
+
+               // Wipe some DB query result caches on setup and teardown
+               $reset = function () {
+                       LinkCache::singleton()->clear();
+
+                       // Clear the message cache
+                       MessageCache::singleton()->clear();
+               };
+               $reset();
+               $teardown[] = $reset;
+               return $this->createTeardownObject( $teardown, $nextTeardown );
+       }
+
+       /**
+        * Add data about uploads to the new test DB, and set up the upload
+        * directory. This should be called after either setDatabase() or
+        * setupDatabase().
+        *
+        * @param ScopedCallback|null $nextTeardown The next teardown object
+        * @return ScopedCallback The teardown object
+        */
+       public function setupUploads( $nextTeardown = null ) {
+               $teardown = [];
+
+               $this->checkSetupDone( 'setupDatabase', 'setDatabase' );
+               $teardown[] = $this->markSetupDone( 'setupUploads' );
+
+               // Create the files in the upload directory (or pretend to create them
+               // in a MockFileBackend). Append teardown callback.
+               $teardown[] = $this->setupUploadBackend();
+
+               // Create a user
+               $user = User::createNew( 'WikiSysop' );
+
+               // Register the uploads in the database
+
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
+               # note that the size/width/height/bits/etc of the file
+               # are actually set by inspecting the file itself; the arguments
+               # to recordUpload2 have no effect.  That said, we try to make things
+               # match up so it is less confusing to readers of the code & tests.
+               $image->recordUpload2( '', 'Upload of some lame file', 'Some lame file', [
+                       'size' => 7881,
+                       'width' => 1941,
+                       'height' => 220,
+                       'bits' => 8,
+                       'media_type' => MEDIATYPE_BITMAP,
+                       'mime' => 'image/jpeg',
+                       'metadata' => serialize( [] ),
+                       'sha1' => Wikimedia\base_convert( '1', 16, 36, 31 ),
+                       'fileExists' => true
+               ], $this->db->timestamp( '20010115123500' ), $user );
+
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Thumb.png' ) );
+               # again, note that size/width/height below are ignored; see above.
+               $image->recordUpload2( '', 'Upload of some lame thumbnail', 'Some lame thumbnail', [
+                       'size' => 22589,
+                       'width' => 135,
+                       'height' => 135,
+                       'bits' => 8,
+                       'media_type' => MEDIATYPE_BITMAP,
+                       'mime' => 'image/png',
+                       'metadata' => serialize( [] ),
+                       'sha1' => Wikimedia\base_convert( '2', 16, 36, 31 ),
+                       'fileExists' => true
+               ], $this->db->timestamp( '20130225203040' ), $user );
+
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.svg' ) );
+               $image->recordUpload2( '', 'Upload of some lame SVG', 'Some lame SVG', [
+                               'size'        => 12345,
+                               'width'       => 240,
+                               'height'      => 180,
+                               'bits'        => 0,
+                               'media_type'  => MEDIATYPE_DRAWING,
+                               'mime'        => 'image/svg+xml',
+                               'metadata'    => serialize( [] ),
+                               'sha1'        => Wikimedia\base_convert( '', 16, 36, 31 ),
+                               'fileExists'  => true
+               ], $this->db->timestamp( '20010115123500' ), $user );
+
+               # This image will be blacklisted in [[MediaWiki:Bad image list]]
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) );
+               $image->recordUpload2( '', 'zomgnotcensored', 'Borderline image', [
+                       'size' => 12345,
+                       'width' => 320,
+                       'height' => 240,
+                       'bits' => 24,
+                       'media_type' => MEDIATYPE_BITMAP,
+                       'mime' => 'image/jpeg',
+                       'metadata' => serialize( [] ),
+                       'sha1' => Wikimedia\base_convert( '3', 16, 36, 31 ),
+                       'fileExists' => true
+               ], $this->db->timestamp( '20010115123500' ), $user );
+
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Video.ogv' ) );
+               $image->recordUpload2( '', 'A pretty movie', 'Will it play', [
+                       'size' => 12345,
+                       'width' => 320,
+                       'height' => 240,
+                       'bits' => 0,
+                       'media_type' => MEDIATYPE_VIDEO,
+                       'mime' => 'application/ogg',
+                       'metadata' => serialize( [] ),
+                       'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
+                       'fileExists' => true
+               ], $this->db->timestamp( '20010115123500' ), $user );
+
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Audio.oga' ) );
+               $image->recordUpload2( '', 'An awesome hitsong', 'Will it play', [
+                       'size' => 12345,
+                       'width' => 0,
+                       'height' => 0,
+                       'bits' => 0,
+                       'media_type' => MEDIATYPE_AUDIO,
+                       'mime' => 'application/ogg',
+                       'metadata' => serialize( [] ),
+                       'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
+                       'fileExists' => true
+               ], $this->db->timestamp( '20010115123500' ), $user );
+
+               # A DjVu file
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'LoremIpsum.djvu' ) );
+               $image->recordUpload2( '', 'Upload a DjVu', 'A DjVu', [
+                       'size' => 3249,
+                       'width' => 2480,
+                       'height' => 3508,
+                       'bits' => 0,
+                       'media_type' => MEDIATYPE_BITMAP,
+                       'mime' => 'image/vnd.djvu',
+                       'metadata' => '<?xml version="1.0" ?>
+<!DOCTYPE DjVuXML PUBLIC "-//W3C//DTD DjVuXML 1.1//EN" "pubtext/DjVuXML-s.dtd">
+<DjVuXML>
+<HEAD></HEAD>
+<BODY><OBJECT height="3508" width="2480">
+<PARAM name="DPI" value="300" />
+<PARAM name="GAMMA" value="2.2" />
+</OBJECT>
+<OBJECT height="3508" width="2480">
+<PARAM name="DPI" value="300" />
+<PARAM name="GAMMA" value="2.2" />
+</OBJECT>
+<OBJECT height="3508" width="2480">
+<PARAM name="DPI" value="300" />
+<PARAM name="GAMMA" value="2.2" />
+</OBJECT>
+<OBJECT height="3508" width="2480">
+<PARAM name="DPI" value="300" />
+<PARAM name="GAMMA" value="2.2" />
+</OBJECT>
+<OBJECT height="3508" width="2480">
+<PARAM name="DPI" value="300" />
+<PARAM name="GAMMA" value="2.2" />
+</OBJECT>
+</BODY>
+</DjVuXML>',
+                       'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
+                       'fileExists' => true
+               ], $this->db->timestamp( '20010115123600' ), $user );
+
+               return $this->createTeardownObject( $teardown, $nextTeardown );
+       }
+
+       /**
+        * Helper for database teardown, called from the teardown closure. Destroy
+        * the database clone and fix up some things that CloneDatabase doesn't fix.
+        *
+        * @todo Move most things here to CloneDatabase
+        */
+       private function teardownDatabase() {
+               $this->checkSetupDone( 'setupDatabase' );
+
+               $this->dbClone->destroy();
+               $this->databaseSetupDone = false;
+
+               if ( $this->useTemporaryTables ) {
+                       if ( $this->db->getType() == 'sqlite' ) {
+                               # Under SQLite the searchindex table is virtual and need
+                               # to be explicitly destroyed. See bug 29912
+                               # See also MediaWikiTestCase::destroyDB()
+                               wfDebug( __METHOD__ . " explicitly destroying sqlite virtual table parsertest_searchindex\n" );
+                               $this->db->query( "DROP TABLE `parsertest_searchindex`" );
+                       }
+                       # Don't need to do anything
+                       return;
+               }
+
+               $tables = $this->listTables();
+
+               foreach ( $tables as $table ) {
+                       if ( $this->db->getType() == 'oracle' ) {
+                               $this->db->query( "DROP TABLE pt_$table DROP CONSTRAINTS" );
+                       } else {
+                               $this->db->query( "DROP TABLE `parsertest_$table`" );
+                       }
+               }
+
+               if ( $this->db->getType() == 'oracle' ) {
+                       $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
+               }
+       }
+
+       /**
+        * Upload test files to the backend created by createRepoGroup().
+        *
+        * @return callable The teardown callback
+        */
+       private function setupUploadBackend() {
+               global $IP;
+
+               $repo = RepoGroup::singleton()->getLocalRepo();
+               $base = $repo->getZonePath( 'public' );
+               $backend = $repo->getBackend();
+               $backend->prepare( [ 'dir' => "$base/3/3a" ] );
+               $backend->store( [
+                       'src' => "$IP/tests/phpunit/data/parser/headbg.jpg",
+                       'dst' => "$base/3/3a/Foobar.jpg"
+               ] );
+               $backend->prepare( [ 'dir' => "$base/e/ea" ] );
+               $backend->store( [
+                       'src' => "$IP/tests/phpunit/data/parser/wiki.png",
+                       'dst' => "$base/e/ea/Thumb.png"
+               ] );
+               $backend->prepare( [ 'dir' => "$base/0/09" ] );
+               $backend->store( [
+                       'src' => "$IP/tests/phpunit/data/parser/headbg.jpg",
+                       'dst' => "$base/0/09/Bad.jpg"
+               ] );
+               $backend->prepare( [ 'dir' => "$base/5/5f" ] );
+               $backend->store( [
+                       'src' => "$IP/tests/phpunit/data/parser/LoremIpsum.djvu",
+                       'dst' => "$base/5/5f/LoremIpsum.djvu"
+               ] );
+
+               // No helpful SVG file to copy, so make one ourselves
+               $data = '<?xml version="1.0" encoding="utf-8"?>' .
+                       '<svg xmlns="http://www.w3.org/2000/svg"' .
+                       ' version="1.1" width="240" height="180"/>';
+
+               $backend->prepare( [ 'dir' => "$base/f/ff" ] );
+               $backend->quickCreate( [
+                       'content' => $data, 'dst' => "$base/f/ff/Foobar.svg"
+               ] );
+
+               return function () use ( $backend ) {
+                       if ( $backend instanceof MockFileBackend ) {
+                               // In memory backend, so dont bother cleaning them up.
+                               return;
+                       }
+                       $this->teardownUploadBackend();
+               };
+       }
+
+       /**
+        * Remove the dummy uploads directory
+        */
+       private function teardownUploadBackend() {
+               if ( $this->keepUploads ) {
+                       return;
+               }
+
+               $repo = RepoGroup::singleton()->getLocalRepo();
+               $public = $repo->getZonePath( 'public' );
+
+               $this->deleteFiles(
+                       [
+                               "$public/3/3a/Foobar.jpg",
+                               "$public/e/ea/Thumb.png",
+                               "$public/0/09/Bad.jpg",
+                               "$public/5/5f/LoremIpsum.djvu",
+                               "$public/f/ff/Foobar.svg",
+                               "$public/0/00/Video.ogv",
+                               "$public/4/41/Audio.oga",
+                       ]
+               );
+       }
+
+       /**
+        * Delete the specified files and their parent directories
+        * @param array $files File backend URIs mwstore://...
+        */
+       private function deleteFiles( $files ) {
+               // Delete the files
+               $backend = RepoGroup::singleton()->getLocalRepo()->getBackend();
+               foreach ( $files as $file ) {
+                       $backend->delete( [ 'src' => $file ], [ 'force' => 1 ] );
+               }
+
+               // Delete the parent directories
+               foreach ( $files as $file ) {
+                       $tmp = FileBackend::parentStoragePath( $file );
+                       while ( $tmp ) {
+                               if ( !$backend->clean( [ 'dir' => $tmp ] )->isOK() ) {
+                                       break;
+                               }
+                               $tmp = FileBackend::parentStoragePath( $tmp );
+                       }
+               }
+       }
+
+       /**
+        * Add articles to the test DB.
+        *
+        * @param $articles Article info array from TestFileReader
+        */
+       public function addArticles( $articles ) {
+               global $wgContLang;
+               $setup = [];
+               $teardown = [];
+
+               // Be sure ParserTestRunner::addArticle has correct language set,
+               // so that system messages get into the right language cache
+               if ( $wgContLang->getCode() !== 'en' ) {
+                       $setup['wgLanguageCode'] = 'en';
+                       $setup['wgContLang'] = Language::factory( 'en' );
+               }
+
+               // Add special namespaces, in case that hasn't been done by staticSetup() yet
+               $this->appendNamespaceSetup( $setup, $teardown );
+
+               // wgCapitalLinks obviously needs initialisation
+               $setup['wgCapitalLinks'] = true;
+
+               $teardown[] = $this->executeSetupSnippets( $setup );
+
+               foreach ( $articles as $info ) {
+                       $this->addArticle( $info['name'], $info['text'], $info['file'], $info['line'] );
+               }
+
+               // Wipe WANObjectCache process cache, which is invalidated by article insertion
+               // due to T144706
+               ObjectCache::getMainWANInstance()->clearProcessCache();
+
+               $this->executeSetupSnippets( $teardown );
+       }
+
+       /**
+        * Insert a temporary test article
+        * @param string $name The title, including any prefix
+        * @param string $text The article text
+        * @param string $file The input file name
+        * @param int|string $line The input line number, for reporting errors
+        * @throws Exception
+        * @throws MWException
+        */
+       private function addArticle( $name, $text, $file, $line ) {
+               $text = self::chomp( $text );
+               $name = self::chomp( $name );
+
+               $title = Title::newFromText( $name );
+               wfDebug( __METHOD__ . ": adding $name" );
+
+               if ( is_null( $title ) ) {
+                       throw new MWException( "invalid title '$name' at $file:$line\n" );
+               }
+
+               $page = WikiPage::factory( $title );
+               $page->loadPageData( 'fromdbmaster' );
+
+               if ( $page->exists() ) {
+                       throw new MWException( "duplicate article '$name' at $file:$line\n" );
+               }
+
+               $page->doEditContent( ContentHandler::makeContent( $text, $title ), '', EDIT_NEW );
+
+               // The RepoGroup cache is invalidated by the creation of file redirects
+               if ( $title->getNamespace() === NS_IMAGE ) {
+                       RepoGroup::singleton()->clearCache( $title );
+               }
+       }
+
+       /**
+        * Check if a hook is installed
+        *
+        * @param string $name
+        * @return bool True if tag hook is present
+        */
+       public function requireHook( $name ) {
+               global $wgParser;
+
+               $wgParser->firstCallInit(); // make sure hooks are loaded.
+               if ( isset( $wgParser->mTagHooks[$name] ) ) {
+                       return true;
+               } else {
+                       $this->recorder->warning( "   This test suite requires the '$name' hook " .
+                               "extension, skipping." );
+                       return false;
+               }
+       }
+
+       /**
+        * Check if a function hook is installed
+        *
+        * @param string $name
+        * @return bool True if function hook is present
+        */
+       public function requireFunctionHook( $name ) {
+               global $wgParser;
+
+               $wgParser->firstCallInit(); // make sure hooks are loaded.
+
+               if ( isset( $wgParser->mFunctionHooks[$name] ) ) {
+                       return true;
+               } else {
+                       $this->recorder->warning( "   This test suite requires the '$name' function " .
+                               "hook extension, skipping." );
+                       return false;
+               }
+       }
+
+       /**
+        * Check if a transparent tag hook is installed
+        *
+        * @param string $name
+        * @return bool True if function hook is present
+        */
+       public function requireTransparentHook( $name ) {
+               global $wgParser;
+
+               $wgParser->firstCallInit(); // make sure hooks are loaded.
+
+               if ( isset( $wgParser->mTransparentTagHooks[$name] ) ) {
+                       return true;
+               } else {
+                       $this->recorder->warning( "   This test suite requires the '$name' transparent " .
+                               "hook extension, skipping.\n" );
+                       return false;
+               }
+       }
+
+       /**
+        * The ParserGetVariableValueTs hook, used to make sure time-related parser
+        * functions give a persistent value.
+        */
+       static function getFakeTimestamp( &$parser, &$ts ) {
+               $ts = 123; // parsed as '1970-01-01T00:02:03Z'
+               return true;
+       }
+}
diff --git a/tests/parser/PhpunitTestRecorder.php b/tests/parser/PhpunitTestRecorder.php
new file mode 100644 (file)
index 0000000..238d018
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+class PhpunitTestRecorder extends TestRecorder {
+       private $testCase;
+
+       public function setTestCase( PHPUnit_Framework_TestCase $testCase ) {
+               $this->testCase = $testCase;
+       }
+
+       /**
+        * Mark a test skipped
+        */
+       public function skipped( $test, $reason ) {
+               $this->testCase->markTestSkipped( "SKIPPED: $reason" );
+       }
+}
index 8b41337..f1a82ee 100644 (file)
@@ -1,8 +1,12 @@
-Parser tests are run using our PHPUnit test suite in tests/phpunit:
+Parser tests can be run either via PHPUnit or by using the standalone
+parserTests.php in this directory. The standalone version provides more
+options.
+
+To run parser tests via PHPUnit:
 
  $ cd tests/phpunit
- ./phpunit.php --group Parser
+ ./phpunit.php --testsuite parsertests
 
-You can optionally filter by title using --regex. I.e. :
+You can optionally filter by title using --filter, e.g.
 
- ./phpunit.php --group Parser --regex="Bug 6200"
+ ./phpunit.php --testsuite parsertests --filter="Bug 6200"
diff --git a/tests/parser/TestFileReader.php b/tests/parser/TestFileReader.php
new file mode 100644 (file)
index 0000000..a1a8d19
--- /dev/null
@@ -0,0 +1,289 @@
+<?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
+ * @ingroup Testing
+ */
+
+class TestFileReader {
+       private $file;
+       private $fh;
+       private $section = null;
+       /** String|null: current test section being analyzed */
+       private $sectionData = [];
+       private $lineNum = 0;
+       private $runDisabled;
+       private $runParsoid;
+       private $regex;
+
+       private $articles = [];
+       private $requirements = [];
+       private $tests = [];
+
+       public static function read( $file, array $options = [] ) {
+               $reader = new self( $file, $options );
+               $reader->execute();
+
+               $requirements = [];
+               foreach ( $reader->requirements as $type => $reqsOfType ) {
+                       foreach ( $reqsOfType as $name => $unused ) {
+                               $requirements[] = [
+                                       'type' => $type,
+                                       'name' => $name
+                               ];
+                       }
+               }
+
+               return [
+                       'requirements' => $requirements,
+                       'tests' => $reader->tests,
+                       'articles' => $reader->articles
+               ];
+       }
+
+       private function __construct( $file, $options ) {
+               $this->file = $file;
+               $this->fh = fopen( $this->file, "rt" );
+
+               if ( !$this->fh ) {
+                       throw new MWException( "Couldn't open file '$file'\n" );
+               }
+
+               $options = $options + [
+                       'runDisabled' => false,
+                       'runParsoid' => false,
+                       'regex' => '//',
+               ];
+               $this->runDisabled = $options['runDisabled'];
+               $this->runParsoid = $options['runParsoid'];
+               $this->regex = $options['regex'];
+       }
+
+       private function addCurrentTest() {
+               // "input" and "result" are old section names allowed
+               // for backwards-compatibility.
+               $input = $this->checkSection( [ 'wikitext', 'input' ], false );
+               $result = $this->checkSection( [ 'html/php', 'html/*', 'html', 'result' ], false );
+               // Some tests have "with tidy" and "without tidy" variants
+               $tidy = $this->checkSection( [ 'html/php+tidy', 'html+tidy' ], false );
+
+               if ( !isset( $this->sectionData['options'] ) ) {
+                       $this->sectionData['options'] = '';
+               }
+
+               if ( !isset( $this->sectionData['config'] ) ) {
+                       $this->sectionData['config'] = '';
+               }
+
+               $isDisabled = preg_match( '/\\bdisabled\\b/i', $this->sectionData['options'] ) &&
+                       !$this->runDisabled;
+               $isParsoidOnly = preg_match( '/\\bparsoid\\b/i', $this->sectionData['options'] ) &&
+                       $result == 'html' &&
+                       !$this->runParsoid;
+               $isFiltered = !preg_match( $this->regex, $this->sectionData['test'] );
+               if ( $input == false || $result == false || $isDisabled || $isParsoidOnly || $isFiltered ) {
+                       // Disabled test
+                       return;
+               }
+
+               $test = [
+                       'test' => ParserTestRunner::chomp( $this->sectionData['test'] ),
+                       'input' => ParserTestRunner::chomp( $this->sectionData[$input] ),
+                       'result' => ParserTestRunner::chomp( $this->sectionData[$result] ),
+                       'options' => ParserTestRunner::chomp( $this->sectionData['options'] ),
+                       'config' => ParserTestRunner::chomp( $this->sectionData['config'] ),
+               ];
+               $test['desc'] = $test['test'];
+               $this->tests[] = $test;
+
+               if ( $tidy !== false ) {
+                       $test['options'] .= " tidy";
+                       $test['desc'] .= ' (with tidy)';
+                       $test['result'] = ParserTestRunner::chomp( $this->sectionData[$tidy] );
+                       $this->tests[] = $test;
+               }
+       }
+
+       private function execute() {
+               while ( false !== ( $line = fgets( $this->fh ) ) ) {
+                       $this->lineNum++;
+                       $matches = [];
+
+                       if ( preg_match( '/^!!\s*(\S+)/', $line, $matches ) ) {
+                               $this->section = strtolower( $matches[1] );
+
+                               if ( $this->section == 'endarticle' ) {
+                                       $this->checkSection( 'text' );
+                                       $this->checkSection( 'article' );
+
+                                       $this->addArticle(
+                                               ParserTestRunner::chomp( $this->sectionData['article'] ),
+                                               $this->sectionData['text'], $this->lineNum );
+
+                                       $this->clearSection();
+
+                                       continue;
+                               }
+
+                               if ( $this->section == 'endhooks' ) {
+                                       $this->checkSection( 'hooks' );
+
+                                       foreach ( explode( "\n", $this->sectionData['hooks'] ) as $line ) {
+                                               $line = trim( $line );
+
+                                               if ( $line ) {
+                                                       $this->addRequirement( 'hook', $line );
+                                               }
+                                       }
+
+                                       $this->clearSection();
+
+                                       continue;
+                               }
+
+                               if ( $this->section == 'endfunctionhooks' ) {
+                                       $this->checkSection( 'functionhooks' );
+
+                                       foreach ( explode( "\n", $this->sectionData['functionhooks'] ) as $line ) {
+                                               $line = trim( $line );
+
+                                               if ( $line ) {
+                                                       $this->addRequirement( 'functionHook', $line );
+                                               }
+                                       }
+
+                                       $this->clearSection();
+
+                                       continue;
+                               }
+
+                               if ( $this->section == 'endtransparenthooks' ) {
+                                       $this->checkSection( 'transparenthooks' );
+
+                                       foreach ( explode( "\n", $this->sectionData['transparenthooks'] ) as $line ) {
+                                               $line = trim( $line );
+
+                                               if ( $line ) {
+                                                       $this->addRequirement( 'transparentHook', $line );
+                                               }
+                                       }
+
+                                       $this->clearSection();
+
+                                       continue;
+                               }
+
+                               if ( $this->section == 'end' ) {
+                                       $this->checkSection( 'test' );
+                                       $this->addCurrentTest();
+                                       $this->clearSection();
+                                       continue;
+                               }
+
+                               if ( isset( $this->sectionData[$this->section] ) ) {
+                                       throw new MWException( "duplicate section '$this->section' "
+                                               . "at line {$this->lineNum} of $this->file\n" );
+                               }
+
+                               $this->sectionData[$this->section] = '';
+
+                               continue;
+                       }
+
+                       if ( $this->section ) {
+                               $this->sectionData[$this->section] .= $line;
+                       }
+               }
+       }
+
+       /**
+        * Clear section name and its data
+        */
+       private function clearSection() {
+               $this->sectionData = [];
+               $this->section = null;
+
+       }
+
+       /**
+        * Verify the current section data has some value for the given token
+        * name(s) (first parameter).
+        * Throw an exception if it is not set, referencing current section
+        * and adding the current file name and line number
+        *
+        * @param string|array $tokens Expected token(s) that should have been
+        * mentioned before closing this section
+        * @param bool $fatal True iff an exception should be thrown if
+        * the section is not found.
+        * @return bool|string
+        * @throws MWException
+        */
+       private function checkSection( $tokens, $fatal = true ) {
+               if ( is_null( $this->section ) ) {
+                       throw new MWException( __METHOD__ . " can not verify a null section!\n" );
+               }
+               if ( !is_array( $tokens ) ) {
+                       $tokens = [ $tokens ];
+               }
+               if ( count( $tokens ) == 0 ) {
+                       throw new MWException( __METHOD__ . " can not verify zero sections!\n" );
+               }
+
+               $data = $this->sectionData;
+               $tokens = array_filter( $tokens, function ( $token ) use ( $data ) {
+                       return isset( $data[$token] );
+               } );
+
+               if ( count( $tokens ) == 0 ) {
+                       if ( !$fatal ) {
+                               return false;
+                       }
+                       throw new MWException( sprintf(
+                               "'%s' without '%s' at line %s of %s\n",
+                               $this->section,
+                               implode( ',', $tokens ),
+                               $this->lineNum,
+                               $this->file
+                       ) );
+               }
+               if ( count( $tokens ) > 1 ) {
+                       throw new MWException( sprintf(
+                               "'%s' with unexpected tokens '%s' at line %s of %s\n",
+                               $this->section,
+                               implode( ',', $tokens ),
+                               $this->lineNum,
+                               $this->file
+                       ) );
+               }
+
+               return array_values( $tokens )[0];
+       }
+
+       private function addArticle( $name, $text, $line ) {
+               $this->articles[] = [
+                       'name' => $name,
+                       'text' => $text,
+                       'line' => $line,
+                       'file' => $this->file
+               ];
+       }
+
+       private function addRequirement( $type, $name ) {
+               $this->requirements[$type][$name] = true;
+       }
+}
+
diff --git a/tests/parser/TestRecorder.php b/tests/parser/TestRecorder.php
new file mode 100644 (file)
index 0000000..70215b6
--- /dev/null
@@ -0,0 +1,94 @@
+<?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
+ * @ingroup Testing
+ */
+
+/**
+ * Interface to record parser test results.
+ *
+ * The TestRecorder is an class hierarchy to record the result of
+ * MediaWiki parser tests. One should call start() before running the
+ * full parser tests and end() once all the tests have been finished.
+ * After each test, you should use record() to keep track of your tests
+ * results. Finally, report() is used to generate a summary of your
+ * test run, one could dump it to the console for human consumption or
+ * register the result in a database for tracking purposes.
+ *
+ * @since 1.22
+ */
+abstract class TestRecorder {
+
+       /**
+        * Called at beginning of the parser test run
+        */
+       public function start() {
+       }
+
+       /**
+        * Called before starting a test
+        */
+       public function startTest( $test ) {
+       }
+
+       /**
+        * Called before starting an input file
+        */
+       public function startSuite( $path ) {
+       }
+
+       /**
+        * Called after ending an input file
+        */
+       public function endSuite( $path ) {
+       }
+
+       /**
+        * Called after each test
+        * @param array $test
+        * @param ParserTestResult $result
+        */
+       public function record( $test, ParserTestResult $result ) {
+       }
+
+       /**
+        * Show a warning to the user
+        */
+       public function warning( $message ) {
+       }
+
+       /**
+        * Mark a test skipped
+        */
+       public function skipped( $test, $subtest ) {
+       }
+
+       /**
+        * Called before finishing the test run
+        */
+       public function report() {
+       }
+
+       /**
+        * Called at the end of the parser test run
+        */
+       public function end() {
+       }
+
+}
+
diff --git a/tests/parser/TidySupport.php b/tests/parser/TidySupport.php
new file mode 100644 (file)
index 0000000..6b5fb48
--- /dev/null
@@ -0,0 +1,95 @@
+<?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
+ * @ingroup Testing
+ */
+
+/**
+ * Initialize and detect the tidy support
+ */
+class TidySupport {
+       private $enabled;
+       private $config;
+
+       /**
+        * Determine if there is a usable tidy.
+        */
+       public function __construct( $useConfiguration = false ) {
+               global $IP, $wgUseTidy, $wgTidyBin, $wgTidyInternal, $wgTidyConfig,
+                       $wgTidyConf, $wgTidyOpts;
+
+               $this->enabled = true;
+               if ( $useConfiguration ) {
+                       if ( $wgTidyConfig !== null ) {
+                               $this->config = $wgTidyConfig;
+                       } elseif ( $wgUseTidy ) {
+                               $this->config = [
+                                       'tidyConfigFile' => $wgTidyConf,
+                                       'debugComment' => false,
+                                       'tidyBin' => $wgTidyBin,
+                                       'tidyCommandLine' => $wgTidyOpts
+                               ];
+                               if ( $wgTidyInternal ) {
+                                       $this->config['driver'] = wfIsHHVM() ? 'RaggettInternalHHVM' : 'RaggettInternalPHP';
+                               } else {
+                                       $this->config['driver'] = 'RaggettExternal';
+                               }
+                       } else {
+                               $this->enabled = false;
+                       }
+               } else {
+                       $this->config = [
+                               'tidyConfigFile' => "$IP/includes/tidy/tidy.conf",
+                               'tidyCommandLine' => '',
+                       ];
+                       if ( extension_loaded( 'tidy' ) && class_exists( 'tidy' ) ) {
+                               $this->config['driver'] = wfIsHHVM() ? 'RaggettInternalHHVM' : 'RaggettInternalPHP';
+                       } else {
+                               if ( is_executable( $wgTidyBin ) ) {
+                                       $this->config['driver'] = 'RaggettExternal';
+                                       $this->config['tidyBin'] = $wgTidyBin;
+                               } else {
+                                       $path = Installer::locateExecutableInDefaultPaths( $wgTidyBin );
+                                       if ( $path !== false ) {
+                                               $this->config['driver'] = 'RaggettExternal';
+                                               $this->config['tidyBin'] = $wgTidyBin;
+                                       } else {
+                                               $this->enabled = false;
+                                       }
+                               }
+                       }
+               }
+               if ( !$this->enabled ) {
+                       $this->config = [ 'driver' => 'disabled' ];
+               }
+       }
+
+       /**
+        * Returns true if tidy is usable
+        *
+        * @return bool
+        */
+       public function isEnabled() {
+               return $this->enabled;
+       }
+
+       public function getConfig() {
+               return $this->config;
+       }
+}
diff --git a/tests/parser/fuzzTest.php b/tests/parser/fuzzTest.php
new file mode 100644 (file)
index 0000000..7437053
--- /dev/null
@@ -0,0 +1,200 @@
+<?php
+
+require __DIR__ . '/../../maintenance/Maintenance.php';
+
+// Make RequestContext::resetMain() happy
+define( 'MW_PARSER_TEST', 1 );
+
+class ParserFuzzTest extends Maintenance {
+       private $parserTest;
+       private $maxFuzzTestLength = 300;
+       private $memoryLimit = 100;
+       private $seed;
+
+       function __construct() {
+               parent::__construct();
+               $this->addDescription( 'Run a fuzz test on the parser, until it segfaults ' .
+                       'or throws an exception' );
+               $this->addOption( 'file', 'Use the specified file as a dictionary, ' .
+                       ' or leave blank to use parserTests.txt', false, true, true );
+
+               $this->addOption( 'seed', 'Start the fuzz test from the specified seed', false, true );
+       }
+
+       function finalSetup() {
+               self::requireTestsAutoloader();
+               TestSetup::applyInitialConfig();
+       }
+
+       function execute() {
+               $files = $this->getOption( 'file', [ __DIR__ . '/parserTests.txt' ] );
+               $this->seed = intval( $this->getOption( 'seed', 1 ) ) - 1;
+               $this->parserTest = new ParserTestRunner(
+                       new MultiTestRecorder,
+                       [] );
+               $this->fuzzTest( $files );
+       }
+
+       /**
+        * Run a fuzz test series
+        * Draw input from a set of test files
+        * @param array $filenames
+        */
+       function fuzzTest( $filenames ) {
+               $dict = $this->getFuzzInput( $filenames );
+               $dictSize = strlen( $dict );
+               $logMaxLength = log( $this->maxFuzzTestLength );
+
+               $teardown = $this->parserTest->staticSetup();
+               $teardown = $this->parserTest->setupDatabase( $teardown );
+               $teardown = $this->parserTest->setupUploads( $teardown );
+
+               $fakeTest = [
+                       'test' => '',
+                       'desc' => '',
+                       'input' => '',
+                       'result' => '',
+                       'options' => '',
+                       'config' => ''
+               ];
+
+               ini_set( 'memory_limit', $this->memoryLimit * 1048576 * 2 );
+
+               $numTotal = 0;
+               $numSuccess = 0;
+               $user = new User;
+               $opts = ParserOptions::newFromUser( $user );
+               $title = Title::makeTitle( NS_MAIN, 'Parser_test' );
+
+               while ( true ) {
+                       // Generate test input
+                       mt_srand( ++$this->seed );
+                       $totalLength = mt_rand( 1, $this->maxFuzzTestLength );
+                       $input = '';
+
+                       while ( strlen( $input ) < $totalLength ) {
+                               $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength;
+                               $hairLength = min( intval( exp( $logHairLength ) ), $dictSize );
+                               $offset = mt_rand( 0, $dictSize - $hairLength );
+                               $input .= substr( $dict, $offset, $hairLength );
+                       }
+
+                       $perTestTeardown = $this->parserTest->perTestSetup( $fakeTest );
+                       $parser = $this->parserTest->getParser();
+
+                       // Run the test
+                       try {
+                               $parser->parse( $input, $title, $opts );
+                               $fail = false;
+                       } catch ( Exception $exception ) {
+                               $fail = true;
+                       }
+
+                       if ( $fail ) {
+                               echo "Test failed with seed {$this->seed}\n";
+                               echo "Input:\n";
+                               printf( "string(%d) \"%s\"\n\n", strlen( $input ), $input );
+                               echo "$exception\n";
+                       } else {
+                               $numSuccess++;
+                       }
+
+                       $numTotal++;
+                       ScopedCallback::consume( $perTestTeardown );
+
+                       if ( $numTotal % 100 == 0 ) {
+                               $usage = intval( memory_get_usage( true ) / $this->memoryLimit / 1048576 * 100 );
+                               echo "{$this->seed}: $numSuccess/$numTotal (mem: $usage%)\n";
+                               if ( $usage >= 100 ) {
+                                       echo "Out of memory:\n";
+                                       $memStats = $this->getMemoryBreakdown();
+
+                                       foreach ( $memStats as $name => $usage ) {
+                                               echo "$name: $usage\n";
+                                       }
+                                       if ( function_exists( 'hphpd_break' ) ) {
+                                               hphpd_break();
+                                       }
+                                       return;
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Get a memory usage breakdown
+        * @return array
+        */
+       function getMemoryBreakdown() {
+               $memStats = [];
+
+               foreach ( $GLOBALS as $name => $value ) {
+                       $memStats['$' . $name] = $this->guessVarSize( $value );
+               }
+
+               $classes = get_declared_classes();
+
+               foreach ( $classes as $class ) {
+                       $rc = new ReflectionClass( $class );
+                       $props = $rc->getStaticProperties();
+                       $memStats[$class] = $this->guessVarSize( $props );
+                       $methods = $rc->getMethods();
+
+                       foreach ( $methods as $method ) {
+                               $memStats[$class] += $this->guessVarSize( $method->getStaticVariables() );
+                       }
+               }
+
+               $functions = get_defined_functions();
+
+               foreach ( $functions['user'] as $function ) {
+                       $rf = new ReflectionFunction( $function );
+                       $memStats["$function()"] = $this->guessVarSize( $rf->getStaticVariables() );
+               }
+
+               asort( $memStats );
+
+               return $memStats;
+       }
+
+       /**
+        * Estimate the size of the input variable
+        */
+       function guessVarSize( $var ) {
+               $length = 0;
+               try {
+                       MediaWiki\suppressWarnings();
+                       $length = strlen( serialize( $var ) );
+                       MediaWiki\restoreWarnings();
+               } catch ( Exception $e ) {
+               }
+               return $length;
+       }
+
+       /**
+        * Get an input dictionary from a set of parser test files
+        * @param array $filenames
+        * @return string
+        */
+       function getFuzzInput( $filenames ) {
+               $dict = '';
+
+               foreach ( $filenames as $filename ) {
+                       $contents = file_get_contents( $filename );
+                       preg_match_all(
+                               '/!!\s*(input|wikitext)\n(.*?)\n!!\s*(result|html|html\/\*|html\/php)/s',
+                               $contents,
+                               $matches
+                       );
+
+                       foreach ( $matches[1] as $match ) {
+                               $dict .= $match . "\n";
+                       }
+               }
+
+               return $dict;
+       }
+}
+
+$maintClass = 'ParserFuzzTest';
+require RUN_MAINTENANCE_IF_MAIN;
diff --git a/tests/parser/parserTest.inc b/tests/parser/parserTest.inc
deleted file mode 100644 (file)
index e519f59..0000000
+++ /dev/null
@@ -1,1701 +0,0 @@
-<?php
-/**
- * Helper code for the MediaWiki parser test suite. Some code is duplicated
- * in PHPUnit's NewParserTests.php, so you'll probably want to update both
- * at the same time.
- *
- * Copyright © 2004, 2010 Brion Vibber <brion@pobox.com>
- * https://www.mediawiki.org/
- *
- * 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
- *
- * @todo Make this more independent of the configuration (and if possible the database)
- * @todo document
- * @file
- * @ingroup Testing
- */
-use MediaWiki\MediaWikiServices;
-
-/**
- * @ingroup Testing
- */
-class ParserTest {
-       /**
-        * @var bool $color whereas output should be colorized
-        */
-       private $color;
-
-       /**
-        * @var bool $showOutput Show test output
-        */
-       private $showOutput;
-
-       /**
-        * @var bool $useTemporaryTables Use temporary tables for the temporary database
-        */
-       private $useTemporaryTables = true;
-
-       /**
-        * @var bool $databaseSetupDone True if the database has been set up
-        */
-       private $databaseSetupDone = false;
-
-       /**
-        * Our connection to the database
-        * @var DatabaseBase
-        */
-       private $db;
-
-       /**
-        * Database clone helper
-        * @var CloneDatabase
-        */
-       private $dbClone;
-
-       /**
-        * @var DjVuSupport
-        */
-       private $djVuSupport;
-
-       /**
-        * @var TidySupport
-        */
-       private $tidySupport;
-
-       private $maxFuzzTestLength = 300;
-       private $fuzzSeed = 0;
-       private $memoryLimit = 50;
-       private $uploadDir = null;
-
-       public $regex = "";
-       private $savedGlobals = [];
-
-       /**
-        * Sets terminal colorization and diff/quick modes depending on OS and
-        * command-line options (--color and --quick).
-        * @param array $options
-        */
-       public function __construct( $options = [] ) {
-               # Only colorize output if stdout is a terminal.
-               $this->color = !wfIsWindows() && Maintenance::posix_isatty( 1 );
-
-               if ( isset( $options['color'] ) ) {
-                       switch ( $options['color'] ) {
-                               case 'no':
-                                       $this->color = false;
-                                       break;
-                               case 'yes':
-                               default:
-                                       $this->color = true;
-                                       break;
-                       }
-               }
-
-               $this->term = $this->color
-                       ? new AnsiTermColorer()
-                       : new DummyTermColorer();
-
-               $this->showDiffs = !isset( $options['quick'] );
-               $this->showProgress = !isset( $options['quiet'] );
-               $this->showFailure = !(
-                       isset( $options['quiet'] )
-                               && ( isset( $options['record'] )
-                               || isset( $options['compare'] ) ) ); // redundant output
-
-               $this->showOutput = isset( $options['show-output'] );
-
-               if ( isset( $options['filter'] ) ) {
-                       $options['regex'] = $options['filter'];
-               }
-
-               if ( isset( $options['regex'] ) ) {
-                       if ( isset( $options['record'] ) ) {
-                               echo "Warning: --record cannot be used with --regex, disabling --record\n";
-                               unset( $options['record'] );
-                       }
-                       $this->regex = $options['regex'];
-               } else {
-                       # Matches anything
-                       $this->regex = '';
-               }
-
-               $this->setupRecorder( $options );
-               $this->keepUploads = isset( $options['keep-uploads'] );
-
-               if ( $this->keepUploads ) {
-                       $this->uploadDir = wfTempDir() . '/mwParser-images';
-               } else {
-                       $this->uploadDir = wfTempDir() . "/mwParser-" . mt_rand() . "-images";
-               }
-
-               if ( isset( $options['seed'] ) ) {
-                       $this->fuzzSeed = intval( $options['seed'] ) - 1;
-               }
-
-               $this->runDisabled = isset( $options['run-disabled'] );
-               $this->runParsoid = isset( $options['run-parsoid'] );
-
-               $this->djVuSupport = new DjVuSupport();
-               $this->tidySupport = new TidySupport();
-               if ( !$this->tidySupport->isEnabled() ) {
-                       echo "Warning: tidy is not installed, skipping some tests\n";
-               }
-
-               if ( !extension_loaded( 'gd' ) ) {
-                       echo "Warning: GD extension is not present, thumbnailing tests will probably fail\n";
-               }
-
-               $this->hooks = [];
-               $this->functionHooks = [];
-               $this->transparentHooks = [];
-               $this->setUp();
-       }
-
-       function setUp() {
-               global $wgParser, $wgParserConf, $IP, $messageMemc, $wgMemc,
-                       $wgUser, $wgLang, $wgOut, $wgRequest, $wgStyleDirectory,
-                       $wgExtraNamespaces, $wgNamespaceAliases, $wgNamespaceProtection, $wgLocalFileRepo,
-                       $wgExtraInterlanguageLinkPrefixes, $wgLocalInterwikis,
-                       $parserMemc, $wgThumbnailScriptPath, $wgScriptPath, $wgResourceBasePath,
-                       $wgArticlePath, $wgScript, $wgStylePath, $wgExtensionAssetsPath,
-                       $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType, $wgLockManagers;
-
-               $wgScriptPath = '';
-               $wgScript = '/index.php';
-               $wgStylePath = '/skins';
-               $wgResourceBasePath = '';
-               $wgExtensionAssetsPath = '/extensions';
-               $wgArticlePath = '/wiki/$1';
-               $wgThumbnailScriptPath = false;
-               $wgLockManagers = [ [
-                       'name' => 'fsLockManager',
-                       'class' => 'FSLockManager',
-                       'lockDirectory' => $this->uploadDir . '/lockdir',
-               ], [
-                       'name' => 'nullLockManager',
-                       'class' => 'NullLockManager',
-               ] ];
-               $wgLocalFileRepo = [
-                       'class' => 'LocalRepo',
-                       'name' => 'local',
-                       'url' => 'http://example.com/images',
-                       'hashLevels' => 2,
-                       'transformVia404' => false,
-                       'backend' => new FSFileBackend( [
-                               'name' => 'local-backend',
-                               'wikiId' => wfWikiID(),
-                               'containerPaths' => [
-                                       'local-public' => $this->uploadDir . '/public',
-                                       'local-thumb' => $this->uploadDir . '/thumb',
-                                       'local-temp' => $this->uploadDir . '/temp',
-                                       'local-deleted' => $this->uploadDir . '/deleted',
-                               ]
-                       ] )
-               ];
-               $wgNamespaceProtection[NS_MEDIAWIKI] = 'editinterface';
-               $wgNamespaceAliases['Image'] = NS_FILE;
-               $wgNamespaceAliases['Image_talk'] = NS_FILE_TALK;
-               # add a namespace shadowing a interwiki link, to test
-               # proper precedence when resolving links. (bug 51680)
-               $wgExtraNamespaces[100] = 'MemoryAlpha';
-               $wgExtraNamespaces[101] = 'MemoryAlpha talk';
-
-               // XXX: tests won't run without this (for CACHE_DB)
-               if ( $wgMainCacheType === CACHE_DB ) {
-                       $wgMainCacheType = CACHE_NONE;
-               }
-               if ( $wgMessageCacheType === CACHE_DB ) {
-                       $wgMessageCacheType = CACHE_NONE;
-               }
-               if ( $wgParserCacheType === CACHE_DB ) {
-                       $wgParserCacheType = CACHE_NONE;
-               }
-
-               DeferredUpdates::clearPendingUpdates();
-               $wgMemc = wfGetMainCache(); // checks $wgMainCacheType
-               $messageMemc = wfGetMessageCacheStorage();
-               $parserMemc = wfGetParserCacheStorage();
-
-               RequestContext::resetMain();
-               $context = new RequestContext;
-               $wgUser = new User;
-               $wgLang = $context->getLanguage();
-               $wgOut = $context->getOutput();
-               $wgRequest = $context->getRequest();
-               $wgParser = new StubObject( 'wgParser', $wgParserConf['class'], [ $wgParserConf ] );
-
-               if ( $wgStyleDirectory === false ) {
-                       $wgStyleDirectory = "$IP/skins";
-               }
-
-               self::setupInterwikis();
-               $wgLocalInterwikis = [ 'local', 'mi' ];
-               // "extra language links"
-               // see https://gerrit.wikimedia.org/r/111390
-               array_push( $wgExtraInterlanguageLinkPrefixes, 'mul' );
-
-               // Reset namespace cache
-               MWNamespace::getCanonicalNamespaces( true );
-               Language::factory( 'en' )->resetNamespaces();
-       }
-
-       /**
-        * Insert hardcoded interwiki in the lookup table.
-        *
-        * This function insert a set of well known interwikis that are used in
-        * the parser tests. They can be considered has fixtures are injected in
-        * the interwiki cache by using the 'InterwikiLoadPrefix' hook.
-        * Since we are not interested in looking up interwikis in the database,
-        * the hook completely replace the existing mechanism (hook returns false).
-        */
-       public static function setupInterwikis() {
-               # Hack: insert a few Wikipedia in-project interwiki prefixes,
-               # for testing inter-language links
-               Hooks::register( 'InterwikiLoadPrefix', function ( $prefix, &$iwData ) {
-                       static $testInterwikis = [
-                               'local' => [
-                                       'iw_url' => 'http://doesnt.matter.org/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 0 ],
-                               'wikipedia' => [
-                                       'iw_url' => 'http://en.wikipedia.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 0 ],
-                               'meatball' => [
-                                       'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 0 ],
-                               'memoryalpha' => [
-                                       'iw_url' => 'http://www.memory-alpha.org/en/index.php/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 0 ],
-                               'zh' => [
-                                       'iw_url' => 'http://zh.wikipedia.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 1 ],
-                               'es' => [
-                                       'iw_url' => 'http://es.wikipedia.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 1 ],
-                               'fr' => [
-                                       'iw_url' => 'http://fr.wikipedia.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 1 ],
-                               'ru' => [
-                                       'iw_url' => 'http://ru.wikipedia.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 1 ],
-                               'mi' => [
-                                       'iw_url' => 'http://mi.wikipedia.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 1 ],
-                               'mul' => [
-                                       'iw_url' => 'http://wikisource.org/wiki/$1',
-                                       'iw_api' => '',
-                                       'iw_wikiid' => '',
-                                       'iw_local' => 1 ],
-                       ];
-                       if ( array_key_exists( $prefix, $testInterwikis ) ) {
-                               $iwData = $testInterwikis[$prefix];
-                       }
-
-                       // We only want to rely on the above fixtures
-                       return false;
-               } );// hooks::register
-       }
-
-       /**
-        * Remove the hardcoded interwiki lookup table.
-        */
-       public static function tearDownInterwikis() {
-               Hooks::clear( 'InterwikiLoadPrefix' );
-       }
-
-       /**
-        * Reset the Title-related services that need resetting
-        * for each test
-        */
-       public static function resetTitleServices() {
-               $services = MediaWikiServices::getInstance();
-               $services->resetServiceForTesting( 'TitleFormatter' );
-               $services->resetServiceForTesting( 'TitleParser' );
-               $services->resetServiceForTesting( '_MediaWikiTitleCodec' );
-               $services->resetServiceForTesting( 'LinkRenderer' );
-               $services->resetServiceForTesting( 'LinkRendererFactory' );
-       }
-
-       public function setupRecorder( $options ) {
-               if ( isset( $options['record'] ) ) {
-                       $this->recorder = new DbTestRecorder( $this );
-                       $this->recorder->version = isset( $options['setversion'] ) ?
-                               $options['setversion'] : SpecialVersion::getVersion();
-               } elseif ( isset( $options['compare'] ) ) {
-                       $this->recorder = new DbTestPreviewer( $this );
-               } else {
-                       $this->recorder = new TestRecorder( $this );
-               }
-       }
-
-       /**
-        * Remove last character if it is a newline
-        * @group utility
-        * @param string $s
-        * @return string
-        */
-       public static function chomp( $s ) {
-               if ( substr( $s, -1 ) === "\n" ) {
-                       return substr( $s, 0, -1 );
-               } else {
-                       return $s;
-               }
-       }
-
-       /**
-        * Run a fuzz test series
-        * Draw input from a set of test files
-        * @param array $filenames
-        */
-       function fuzzTest( $filenames ) {
-               $GLOBALS['wgContLang'] = Language::factory( 'en' );
-               $dict = $this->getFuzzInput( $filenames );
-               $dictSize = strlen( $dict );
-               $logMaxLength = log( $this->maxFuzzTestLength );
-               $this->setupDatabase();
-               ini_set( 'memory_limit', $this->memoryLimit * 1048576 );
-
-               $numTotal = 0;
-               $numSuccess = 0;
-               $user = new User;
-               $opts = ParserOptions::newFromUser( $user );
-               $title = Title::makeTitle( NS_MAIN, 'Parser_test' );
-
-               while ( true ) {
-                       // Generate test input
-                       mt_srand( ++$this->fuzzSeed );
-                       $totalLength = mt_rand( 1, $this->maxFuzzTestLength );
-                       $input = '';
-
-                       while ( strlen( $input ) < $totalLength ) {
-                               $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength;
-                               $hairLength = min( intval( exp( $logHairLength ) ), $dictSize );
-                               $offset = mt_rand( 0, $dictSize - $hairLength );
-                               $input .= substr( $dict, $offset, $hairLength );
-                       }
-
-                       $this->setupGlobals();
-                       $parser = $this->getParser();
-
-                       // Run the test
-                       try {
-                               $parser->parse( $input, $title, $opts );
-                               $fail = false;
-                       } catch ( Exception $exception ) {
-                               $fail = true;
-                       }
-
-                       if ( $fail ) {
-                               echo "Test failed with seed {$this->fuzzSeed}\n";
-                               echo "Input:\n";
-                               printf( "string(%d) \"%s\"\n\n", strlen( $input ), $input );
-                               echo "$exception\n";
-                       } else {
-                               $numSuccess++;
-                       }
-
-                       $numTotal++;
-                       $this->teardownGlobals();
-                       $parser->__destruct();
-
-                       if ( $numTotal % 100 == 0 ) {
-                               $usage = intval( memory_get_usage( true ) / $this->memoryLimit / 1048576 * 100 );
-                               echo "{$this->fuzzSeed}: $numSuccess/$numTotal (mem: $usage%)\n";
-                               if ( $usage > 90 ) {
-                                       echo "Out of memory:\n";
-                                       $memStats = $this->getMemoryBreakdown();
-
-                                       foreach ( $memStats as $name => $usage ) {
-                                               echo "$name: $usage\n";
-                                       }
-                                       $this->abort();
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Get an input dictionary from a set of parser test files
-        * @param array $filenames
-        * @return string
-        */
-       function getFuzzInput( $filenames ) {
-               $dict = '';
-
-               foreach ( $filenames as $filename ) {
-                       $contents = file_get_contents( $filename );
-                       preg_match_all(
-                               '/!!\s*(input|wikitext)\n(.*?)\n!!\s*(result|html|html\/\*|html\/php)/s',
-                               $contents,
-                               $matches
-                       );
-
-                       foreach ( $matches[1] as $match ) {
-                               $dict .= $match . "\n";
-                       }
-               }
-
-               return $dict;
-       }
-
-       /**
-        * Get a memory usage breakdown
-        * @return array
-        */
-       function getMemoryBreakdown() {
-               $memStats = [];
-
-               foreach ( $GLOBALS as $name => $value ) {
-                       $memStats['$' . $name] = strlen( serialize( $value ) );
-               }
-
-               $classes = get_declared_classes();
-
-               foreach ( $classes as $class ) {
-                       $rc = new ReflectionClass( $class );
-                       $props = $rc->getStaticProperties();
-                       $memStats[$class] = strlen( serialize( $props ) );
-                       $methods = $rc->getMethods();
-
-                       foreach ( $methods as $method ) {
-                               $memStats[$class] += strlen( serialize( $method->getStaticVariables() ) );
-                       }
-               }
-
-               $functions = get_defined_functions();
-
-               foreach ( $functions['user'] as $function ) {
-                       $rf = new ReflectionFunction( $function );
-                       $memStats["$function()"] = strlen( serialize( $rf->getStaticVariables() ) );
-               }
-
-               asort( $memStats );
-
-               return $memStats;
-       }
-
-       function abort() {
-               $this->abort();
-       }
-
-       /**
-        * Run a series of tests listed in the given text files.
-        * Each test consists of a brief description, wikitext input,
-        * and the expected HTML output.
-        *
-        * Prints status updates on stdout and counts up the total
-        * number and percentage of passed tests.
-        *
-        * @param array $filenames Array of strings
-        * @return bool True if passed all tests, false if any tests failed.
-        */
-       public function runTestsFromFiles( $filenames ) {
-               $ok = false;
-
-               // be sure, ParserTest::addArticle has correct language set,
-               // so that system messages gets into the right language cache
-               $GLOBALS['wgLanguageCode'] = 'en';
-               $GLOBALS['wgContLang'] = Language::factory( 'en' );
-
-               $this->recorder->start();
-               try {
-                       $this->setupDatabase();
-                       $ok = true;
-
-                       foreach ( $filenames as $filename ) {
-                               echo "Running parser tests from: $filename\n";
-                               $tests = new TestFileIterator( $filename, $this );
-                               $ok = $this->runTests( $tests ) && $ok;
-                       }
-
-                       $this->teardownDatabase();
-                       $this->recorder->report();
-               } catch ( DBError $e ) {
-                       echo $e->getMessage();
-               }
-               $this->recorder->end();
-
-               return $ok;
-       }
-
-       function runTests( $tests ) {
-               $ok = true;
-
-               foreach ( $tests as $t ) {
-                       $result =
-                               $this->runTest( $t['test'], $t['input'], $t['result'], $t['options'], $t['config'] );
-                       $ok = $ok && $result;
-                       $this->recorder->record( $t['test'], $t['subtest'], $result );
-               }
-
-               if ( $this->showProgress ) {
-                       print "\n";
-               }
-
-               return $ok;
-       }
-
-       /**
-        * Get a Parser object
-        *
-        * @param string $preprocessor
-        * @return Parser
-        */
-       function getParser( $preprocessor = null ) {
-               global $wgParserConf;
-
-               $class = $wgParserConf['class'];
-               $parser = new $class( [ 'preprocessorClass' => $preprocessor ] + $wgParserConf );
-
-               foreach ( $this->hooks as $tag => $callback ) {
-                       $parser->setHook( $tag, $callback );
-               }
-
-               foreach ( $this->functionHooks as $tag => $bits ) {
-                       list( $callback, $flags ) = $bits;
-                       $parser->setFunctionHook( $tag, $callback, $flags );
-               }
-
-               foreach ( $this->transparentHooks as $tag => $callback ) {
-                       $parser->setTransparentTagHook( $tag, $callback );
-               }
-
-               Hooks::run( 'ParserTestParser', [ &$parser ] );
-
-               return $parser;
-       }
-
-       /**
-        * Run a given wikitext input through a freshly-constructed wiki parser,
-        * and compare the output against the expected results.
-        * Prints status and explanatory messages to stdout.
-        *
-        * @param string $desc Test's description
-        * @param string $input Wikitext to try rendering
-        * @param string $result Result to output
-        * @param array $opts Test's options
-        * @param string $config Overrides for global variables, one per line
-        * @return bool
-        */
-       public function runTest( $desc, $input, $result, $opts, $config ) {
-               if ( $this->showProgress ) {
-                       $this->showTesting( $desc );
-               }
-
-               $opts = $this->parseOptions( $opts );
-               $context = $this->setupGlobals( $opts, $config );
-
-               $user = $context->getUser();
-               $options = ParserOptions::newFromContext( $context );
-
-               if ( isset( $opts['djvu'] ) ) {
-                       if ( !$this->djVuSupport->isEnabled() ) {
-                               return $this->showSkipped();
-                       }
-               }
-
-               if ( isset( $opts['tidy'] ) ) {
-                       if ( !$this->tidySupport->isEnabled() ) {
-                               return $this->showSkipped();
-                       } else {
-                               $options->setTidy( true );
-                       }
-               }
-
-               if ( isset( $opts['title'] ) ) {
-                       $titleText = $opts['title'];
-               } else {
-                       $titleText = 'Parser test';
-               }
-
-               ObjectCache::getMainWANInstance()->clearProcessCache();
-               $local = isset( $opts['local'] );
-               $preprocessor = isset( $opts['preprocessor'] ) ? $opts['preprocessor'] : null;
-               $parser = $this->getParser( $preprocessor );
-               $title = Title::newFromText( $titleText );
-
-               if ( isset( $opts['pst'] ) ) {
-                       $out = $parser->preSaveTransform( $input, $title, $user, $options );
-               } elseif ( isset( $opts['msg'] ) ) {
-                       $out = $parser->transformMsg( $input, $options, $title );
-               } elseif ( isset( $opts['section'] ) ) {
-                       $section = $opts['section'];
-                       $out = $parser->getSection( $input, $section );
-               } elseif ( isset( $opts['replace'] ) ) {
-                       $section = $opts['replace'][0];
-                       $replace = $opts['replace'][1];
-                       $out = $parser->replaceSection( $input, $section, $replace );
-               } elseif ( isset( $opts['comment'] ) ) {
-                       $out = Linker::formatComment( $input, $title, $local );
-               } elseif ( isset( $opts['preload'] ) ) {
-                       $out = $parser->getPreloadText( $input, $title, $options );
-               } else {
-                       $output = $parser->parse( $input, $title, $options, true, true, 1337 );
-                       $output->setTOCEnabled( !isset( $opts['notoc'] ) );
-                       $out = $output->getText();
-                       if ( isset( $opts['tidy'] ) ) {
-                               $out = preg_replace( '/\s+$/', '', $out );
-                       }
-
-                       if ( isset( $opts['showtitle'] ) ) {
-                               if ( $output->getTitleText() ) {
-                                       $title = $output->getTitleText();
-                               }
-
-                               $out = "$title\n$out";
-                       }
-
-                       if ( isset( $opts['showindicators'] ) ) {
-                               $indicators = '';
-                               foreach ( $output->getIndicators() as $id => $content ) {
-                                       $indicators .= "$id=$content\n";
-                               }
-                               $out = $indicators . $out;
-                       }
-
-                       if ( isset( $opts['ill'] ) ) {
-                               $out = implode( ' ', $output->getLanguageLinks() );
-                       } elseif ( isset( $opts['cat'] ) ) {
-                               $outputPage = $context->getOutput();
-                               $outputPage->addCategoryLinks( $output->getCategories() );
-                               $cats = $outputPage->getCategoryLinks();
-
-                               if ( isset( $cats['normal'] ) ) {
-                                       $out = implode( ' ', $cats['normal'] );
-                               } else {
-                                       $out = '';
-                               }
-                       }
-               }
-
-               $this->teardownGlobals();
-
-               $testResult = new ParserTestResult( $desc );
-               $testResult->expected = $result;
-               $testResult->actual = $out;
-
-               return $this->showTestResult( $testResult );
-       }
-
-       /**
-        * Refactored in 1.22 to use ParserTestResult
-        * @param ParserTestResult $testResult
-        * @return bool
-        */
-       function showTestResult( ParserTestResult $testResult ) {
-               if ( $testResult->isSuccess() ) {
-                       $this->showSuccess( $testResult );
-                       return true;
-               } else {
-                       $this->showFailure( $testResult );
-                       return false;
-               }
-       }
-
-       /**
-        * Use a regex to find out the value of an option
-        * @param string $key Name of option val to retrieve
-        * @param array $opts Options array to look in
-        * @param mixed $default Default value returned if not found
-        * @return mixed
-        */
-       private static function getOptionValue( $key, $opts, $default ) {
-               $key = strtolower( $key );
-
-               if ( isset( $opts[$key] ) ) {
-                       return $opts[$key];
-               } else {
-                       return $default;
-               }
-       }
-
-       private function parseOptions( $instring ) {
-               $opts = [];
-               // foo
-               // foo=bar
-               // foo="bar baz"
-               // foo=[[bar baz]]
-               // foo=bar,"baz quux"
-               // foo={...json...}
-               $defs = '(?(DEFINE)
-                       (?<qstr>                                        # Quoted string
-                               "
-                               (?:[^\\\\"] | \\\\.)*
-                               "
-                       )
-                       (?<json>
-                               \{              # Open bracket
-                               (?:
-                                       [^"{}] |                                # Not a quoted string or object, or
-                                       (?&qstr) |                              # A quoted string, or
-                                       (?&json)                                # A json object (recursively)
-                               )*
-                               \}              # Close bracket
-                       )
-                       (?<value>
-                               (?:
-                                       (?&qstr)                        # Quoted val
-                               |
-                                       \[\[
-                                               [^]]*                   # Link target
-                                       \]\]
-                               |
-                                       [\w-]+                          # Plain word
-                               |
-                                       (?&json)                        # JSON object
-                               )
-                       )
-               )';
-               $regex = '/' . $defs . '\b
-                       (?<k>[\w-]+)                            # Key
-                       \b
-                       (?:\s*
-                               =                                               # First sub-value
-                               \s*
-                               (?<v>
-                                       (?&value)
-                                       (?:\s*
-                                               ,                               # Sub-vals 1..N
-                                               \s*
-                                               (?&value)
-                                       )*
-                               )
-                       )?
-                       /x';
-               $valueregex = '/' . $defs . '(?&value)/x';
-
-               if ( preg_match_all( $regex, $instring, $matches, PREG_SET_ORDER ) ) {
-                       foreach ( $matches as $bits ) {
-                               $key = strtolower( $bits['k'] );
-                               if ( !isset( $bits['v'] ) ) {
-                                       $opts[$key] = true;
-                               } else {
-                                       preg_match_all( $valueregex, $bits['v'], $vmatches );
-                                       $opts[$key] = array_map( [ $this, 'cleanupOption' ], $vmatches[0] );
-                                       if ( count( $opts[$key] ) == 1 ) {
-                                               $opts[$key] = $opts[$key][0];
-                                       }
-                               }
-                       }
-               }
-               return $opts;
-       }
-
-       private function cleanupOption( $opt ) {
-               if ( substr( $opt, 0, 1 ) == '"' ) {
-                       return stripcslashes( substr( $opt, 1, -1 ) );
-               }
-
-               if ( substr( $opt, 0, 2 ) == '[[' ) {
-                       return substr( $opt, 2, -2 );
-               }
-
-               if ( substr( $opt, 0, 1 ) == '{' ) {
-                       return FormatJson::decode( $opt, true );
-               }
-               return $opt;
-       }
-
-       /**
-        * Set up the global variables for a consistent environment for each test.
-        * Ideally this should replace the global configuration entirely.
-        * @param string $opts
-        * @param string $config
-        * @return RequestContext
-        */
-       private function setupGlobals( $opts = '', $config = '' ) {
-               global $IP;
-
-               # Find out values for some special options.
-               $lang =
-                       self::getOptionValue( 'language', $opts, 'en' );
-               $variant =
-                       self::getOptionValue( 'variant', $opts, false );
-               $maxtoclevel =
-                       self::getOptionValue( 'wgMaxTocLevel', $opts, 999 );
-               $linkHolderBatchSize =
-                       self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
-
-               $settings = [
-                       'wgServer' => 'http://example.org',
-                       'wgServerName' => 'example.org',
-                       'wgScript' => '/index.php',
-                       'wgScriptPath' => '',
-                       'wgArticlePath' => '/wiki/$1',
-                       'wgActionPaths' => [],
-                       'wgLockManagers' => [ [
-                               'name' => 'fsLockManager',
-                               'class' => 'FSLockManager',
-                               'lockDirectory' => $this->uploadDir . '/lockdir',
-                       ], [
-                               'name' => 'nullLockManager',
-                               'class' => 'NullLockManager',
-                       ] ],
-                       'wgLocalFileRepo' => [
-                               'class' => 'LocalRepo',
-                               'name' => 'local',
-                               'url' => 'http://example.com/images',
-                               'hashLevels' => 2,
-                               'transformVia404' => false,
-                               'backend' => new FSFileBackend( [
-                                       'name' => 'local-backend',
-                                       'wikiId' => wfWikiID(),
-                                       'containerPaths' => [
-                                               'local-public' => $this->uploadDir,
-                                               'local-thumb' => $this->uploadDir . '/thumb',
-                                               'local-temp' => $this->uploadDir . '/temp',
-                                               'local-deleted' => $this->uploadDir . '/delete',
-                                       ]
-                               ] )
-                       ],
-                       'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ),
-                       'wgUploadNavigationUrl' => false,
-                       'wgStylePath' => '/skins',
-                       'wgSitename' => 'MediaWiki',
-                       'wgLanguageCode' => $lang,
-                       'wgDBprefix' => $this->db->getType() != 'oracle' ? 'parsertest_' : 'pt_',
-                       'wgRawHtml' => self::getOptionValue( 'wgRawHtml', $opts, false ),
-                       'wgLang' => null,
-                       'wgContLang' => null,
-                       'wgNamespacesWithSubpages' => [ 0 => isset( $opts['subpage'] ) ],
-                       'wgMaxTocLevel' => $maxtoclevel,
-                       'wgCapitalLinks' => true,
-                       'wgNoFollowLinks' => true,
-                       'wgNoFollowDomainExceptions' => [ 'no-nofollow.org' ],
-                       'wgThumbnailScriptPath' => false,
-                       'wgUseImageResize' => true,
-                       'wgSVGConverter' => 'null',
-                       'wgSVGConverters' => [ 'null' => 'echo "1">$output' ],
-                       'wgLocaltimezone' => 'UTC',
-                       'wgAllowExternalImages' => self::getOptionValue( 'wgAllowExternalImages', $opts, true ),
-                       'wgThumbLimits' => [ self::getOptionValue( 'thumbsize', $opts, 180 ) ],
-                       'wgDefaultLanguageVariant' => $variant,
-                       'wgVariantArticlePath' => false,
-                       'wgGroupPermissions' => [ '*' => [
-                               'createaccount' => true,
-                               'read' => true,
-                               'edit' => true,
-                               'createpage' => true,
-                               'createtalk' => true,
-                       ] ],
-                       'wgNamespaceProtection' => [ NS_MEDIAWIKI => 'editinterface' ],
-                       'wgDefaultExternalStore' => [],
-                       'wgForeignFileRepos' => [],
-                       'wgLinkHolderBatchSize' => $linkHolderBatchSize,
-                       'wgExperimentalHtmlIds' => false,
-                       'wgExternalLinkTarget' => false,
-                       'wgHtml5' => true,
-                       'wgAdaptiveMessageCache' => true,
-                       'wgDisableLangConversion' => false,
-                       'wgDisableTitleConversion' => false,
-                       // Tidy options.
-                       'wgUseTidy' => isset( $opts['tidy'] ),
-                       'wgTidyConfig' => null,
-                       'wgDebugTidy' => false,
-                       'wgTidyConf' => $IP . '/includes/tidy/tidy.conf',
-                       'wgTidyOpts' => '',
-                       'wgTidyInternal' => $this->tidySupport->isInternal(),
-               ];
-
-               if ( $config ) {
-                       $configLines = explode( "\n", $config );
-
-                       foreach ( $configLines as $line ) {
-                               list( $var, $value ) = explode( '=', $line, 2 );
-
-                               $settings[$var] = eval( "return $value;" );
-                       }
-               }
-
-               $this->savedGlobals = [];
-
-               /** @since 1.20 */
-               Hooks::run( 'ParserTestGlobals', [ &$settings ] );
-
-               foreach ( $settings as $var => $val ) {
-                       if ( array_key_exists( $var, $GLOBALS ) ) {
-                               $this->savedGlobals[$var] = $GLOBALS[$var];
-                       }
-
-                       $GLOBALS[$var] = $val;
-               }
-
-               // Must be set before $context as user language defaults to $wgContLang
-               $GLOBALS['wgContLang'] = Language::factory( $lang );
-               $GLOBALS['wgMemc'] = new EmptyBagOStuff;
-
-               RequestContext::resetMain();
-               $context = RequestContext::getMain();
-               $GLOBALS['wgLang'] = $context->getLanguage();
-               $GLOBALS['wgOut'] = $context->getOutput();
-               $GLOBALS['wgUser'] = $context->getUser();
-
-               // We (re)set $wgThumbLimits to a single-element array above.
-               $context->getUser()->setOption( 'thumbsize', 0 );
-
-               global $wgHooks;
-
-               $wgHooks['ParserTestParser'][] = 'ParserTestParserHook::setup';
-               $wgHooks['ParserGetVariableValueTs'][] = 'ParserTest::getFakeTimestamp';
-
-               MagicWord::clearCache();
-               MWTidy::destroySingleton();
-               RepoGroup::destroySingleton();
-
-               self::resetTitleServices();
-
-               return $context;
-       }
-
-       /**
-        * List of temporary tables to create, without prefix.
-        * Some of these probably aren't necessary.
-        * @return array
-        */
-       private function listTables() {
-               $tables = [ 'user', 'user_properties', 'user_former_groups', 'page', 'page_restrictions',
-                       'protected_titles', 'revision', 'text', 'pagelinks', 'imagelinks',
-                       'categorylinks', 'templatelinks', 'externallinks', 'langlinks', 'iwlinks',
-                       'site_stats', 'ipblocks', 'image', 'oldimage',
-                       'recentchanges', 'watchlist', 'interwiki', 'logging', 'log_search',
-                       'querycache', 'objectcache', 'job', 'l10n_cache', 'redirect', 'querycachetwo',
-                       'archive', 'user_groups', 'page_props', 'category'
-               ];
-
-               if ( in_array( $this->db->getType(), [ 'mysql', 'sqlite', 'oracle' ] ) ) {
-                       array_push( $tables, 'searchindex' );
-               }
-
-               // Allow extensions to add to the list of tables to duplicate;
-               // may be necessary if they hook into page save or other code
-               // which will require them while running tests.
-               Hooks::run( 'ParserTestTables', [ &$tables ] );
-
-               return $tables;
-       }
-
-       /**
-        * Set up a temporary set of wiki tables to work with for the tests.
-        * Currently this will only be done once per run, and any changes to
-        * the db will be visible to later tests in the run.
-        */
-       public function setupDatabase() {
-               global $wgDBprefix;
-
-               if ( $this->databaseSetupDone ) {
-                       return;
-               }
-
-               $this->db = wfGetDB( DB_MASTER );
-               $dbType = $this->db->getType();
-
-               if ( $wgDBprefix === 'parsertest_' || ( $dbType == 'oracle' && $wgDBprefix === 'pt_' ) ) {
-                       throw new MWException( 'setupDatabase should be called before setupGlobals' );
-               }
-
-               $this->databaseSetupDone = true;
-
-               # SqlBagOStuff broke when using temporary tables on r40209 (bug 15892).
-               # It seems to have been fixed since (r55079?), but regressed at some point before r85701.
-               # This works around it for now...
-               ObjectCache::$instances[CACHE_DB] = new HashBagOStuff;
-
-               # CREATE TEMPORARY TABLE breaks if there is more than one server
-               if ( wfGetLB()->getServerCount() != 1 ) {
-                       $this->useTemporaryTables = false;
-               }
-
-               $temporary = $this->useTemporaryTables || $dbType == 'postgres';
-               $prefix = $dbType != 'oracle' ? 'parsertest_' : 'pt_';
-
-               $this->dbClone = new CloneDatabase( $this->db, $this->listTables(), $prefix );
-               $this->dbClone->useTemporaryTables( $temporary );
-               $this->dbClone->cloneTableStructure();
-
-               if ( $dbType == 'oracle' ) {
-                       $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
-                       # Insert 0 user to prevent FK violations
-
-                       # Anonymous user
-                       $this->db->insert( 'user', [
-                               'user_id' => 0,
-                               'user_name' => 'Anonymous' ] );
-               }
-
-               # Update certain things in site_stats
-               $this->db->insert( 'site_stats',
-                       [ 'ss_row_id' => 1, 'ss_images' => 2, 'ss_good_articles' => 1 ] );
-
-               # Reinitialise the LocalisationCache to match the database state
-               Language::getLocalisationCache()->unloadAll();
-
-               # Clear the message cache
-               MessageCache::singleton()->clear();
-
-               // Remember to update newParserTests.php after changing the below
-               // (and it uses a slightly different syntax just for teh lulz)
-               $this->setupUploadDir();
-               $user = User::createNew( 'WikiSysop' );
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
-               # note that the size/width/height/bits/etc of the file
-               # are actually set by inspecting the file itself; the arguments
-               # to recordUpload2 have no effect.  That said, we try to make things
-               # match up so it is less confusing to readers of the code & tests.
-               $image->recordUpload2( '', 'Upload of some lame file', 'Some lame file', [
-                       'size' => 7881,
-                       'width' => 1941,
-                       'height' => 220,
-                       'bits' => 8,
-                       'media_type' => MEDIATYPE_BITMAP,
-                       'mime' => 'image/jpeg',
-                       'metadata' => serialize( [] ),
-                       'sha1' => Wikimedia\base_convert( '1', 16, 36, 31 ),
-                       'fileExists' => true
-               ], $this->db->timestamp( '20010115123500' ), $user );
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Thumb.png' ) );
-               # again, note that size/width/height below are ignored; see above.
-               $image->recordUpload2( '', 'Upload of some lame thumbnail', 'Some lame thumbnail', [
-                       'size' => 22589,
-                       'width' => 135,
-                       'height' => 135,
-                       'bits' => 8,
-                       'media_type' => MEDIATYPE_BITMAP,
-                       'mime' => 'image/png',
-                       'metadata' => serialize( [] ),
-                       'sha1' => Wikimedia\base_convert( '2', 16, 36, 31 ),
-                       'fileExists' => true
-               ], $this->db->timestamp( '20130225203040' ), $user );
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.svg' ) );
-               $image->recordUpload2( '', 'Upload of some lame SVG', 'Some lame SVG', [
-                               'size'        => 12345,
-                               'width'       => 240,
-                               'height'      => 180,
-                               'bits'        => 0,
-                               'media_type'  => MEDIATYPE_DRAWING,
-                               'mime'        => 'image/svg+xml',
-                               'metadata'    => serialize( [] ),
-                               'sha1'        => Wikimedia\base_convert( '', 16, 36, 31 ),
-                               'fileExists'  => true
-               ], $this->db->timestamp( '20010115123500' ), $user );
-
-               # This image will be blacklisted in [[MediaWiki:Bad image list]]
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) );
-               $image->recordUpload2( '', 'zomgnotcensored', 'Borderline image', [
-                       'size' => 12345,
-                       'width' => 320,
-                       'height' => 240,
-                       'bits' => 24,
-                       'media_type' => MEDIATYPE_BITMAP,
-                       'mime' => 'image/jpeg',
-                       'metadata' => serialize( [] ),
-                       'sha1' => Wikimedia\base_convert( '3', 16, 36, 31 ),
-                       'fileExists' => true
-               ], $this->db->timestamp( '20010115123500' ), $user );
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Video.ogv' ) );
-               $image->recordUpload2( '', 'A pretty movie', 'Will it play', [
-                       'size' => 12345,
-                       'width' => 320,
-                       'height' => 240,
-                       'bits' => 0,
-                       'media_type' => MEDIATYPE_VIDEO,
-                       'mime' => 'application/ogg',
-                       'metadata' => serialize( [] ),
-                       'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
-                       'fileExists' => true
-               ], $this->db->timestamp( '20010115123500' ), $user );
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Audio.oga' ) );
-               $image->recordUpload2( '', 'An awesome hitsong', 'Will it play', [
-                       'size' => 12345,
-                       'width' => 0,
-                       'height' => 0,
-                       'bits' => 0,
-                       'media_type' => MEDIATYPE_AUDIO,
-                       'mime' => 'application/ogg',
-                       'metadata' => serialize( [] ),
-                       'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
-                       'fileExists' => true
-               ], $this->db->timestamp( '20010115123500' ), $user );
-
-               # A DjVu file
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'LoremIpsum.djvu' ) );
-               $image->recordUpload2( '', 'Upload a DjVu', 'A DjVu', [
-                       'size' => 3249,
-                       'width' => 2480,
-                       'height' => 3508,
-                       'bits' => 0,
-                       'media_type' => MEDIATYPE_BITMAP,
-                       'mime' => 'image/vnd.djvu',
-                       'metadata' => '<?xml version="1.0" ?>
-<!DOCTYPE DjVuXML PUBLIC "-//W3C//DTD DjVuXML 1.1//EN" "pubtext/DjVuXML-s.dtd">
-<DjVuXML>
-<HEAD></HEAD>
-<BODY><OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-</BODY>
-</DjVuXML>',
-                       'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
-                       'fileExists' => true
-               ], $this->db->timestamp( '20010115123600' ), $user );
-       }
-
-       public function teardownDatabase() {
-               if ( !$this->databaseSetupDone ) {
-                       $this->teardownGlobals();
-                       return;
-               }
-               $this->teardownUploadDir( $this->uploadDir );
-
-               $this->dbClone->destroy();
-               $this->databaseSetupDone = false;
-
-               if ( $this->useTemporaryTables ) {
-                       if ( $this->db->getType() == 'sqlite' ) {
-                               # Under SQLite the searchindex table is virtual and need
-                               # to be explicitly destroyed. See bug 29912
-                               # See also MediaWikiTestCase::destroyDB()
-                               wfDebug( __METHOD__ . " explicitly destroying sqlite virtual table parsertest_searchindex\n" );
-                               $this->db->query( "DROP TABLE `parsertest_searchindex`" );
-                       }
-                       # Don't need to do anything
-                       $this->teardownGlobals();
-                       return;
-               }
-
-               $tables = $this->listTables();
-
-               foreach ( $tables as $table ) {
-                       if ( $this->db->getType() == 'oracle' ) {
-                               $this->db->query( "DROP TABLE pt_$table DROP CONSTRAINTS" );
-                       } else {
-                               $this->db->query( "DROP TABLE `parsertest_$table`" );
-                       }
-               }
-
-               if ( $this->db->getType() == 'oracle' ) {
-                       $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
-               }
-
-               $this->teardownGlobals();
-       }
-
-       /**
-        * Create a dummy uploads directory which will contain a couple
-        * of files in order to pass existence tests.
-        *
-        * @return string The directory
-        */
-       private function setupUploadDir() {
-               global $IP;
-
-               $dir = $this->uploadDir;
-               if ( $this->keepUploads && is_dir( $dir ) ) {
-                       return;
-               }
-
-               // wfDebug( "Creating upload directory $dir\n" );
-               if ( file_exists( $dir ) ) {
-                       wfDebug( "Already exists!\n" );
-                       return;
-               }
-
-               wfMkdirParents( $dir . '/3/3a', null, __METHOD__ );
-               copy( "$IP/tests/phpunit/data/parser/headbg.jpg", "$dir/3/3a/Foobar.jpg" );
-               wfMkdirParents( $dir . '/e/ea', null, __METHOD__ );
-               copy( "$IP/tests/phpunit/data/parser/wiki.png", "$dir/e/ea/Thumb.png" );
-               wfMkdirParents( $dir . '/0/09', null, __METHOD__ );
-               copy( "$IP/tests/phpunit/data/parser/headbg.jpg", "$dir/0/09/Bad.jpg" );
-               wfMkdirParents( $dir . '/f/ff', null, __METHOD__ );
-               file_put_contents( "$dir/f/ff/Foobar.svg",
-                       '<?xml version="1.0" encoding="utf-8"?>' .
-                       '<svg xmlns="http://www.w3.org/2000/svg"' .
-                       ' version="1.1" width="240" height="180"/>' );
-               wfMkdirParents( $dir . '/5/5f', null, __METHOD__ );
-               copy( "$IP/tests/phpunit/data/parser/LoremIpsum.djvu", "$dir/5/5f/LoremIpsum.djvu" );
-               wfMkdirParents( $dir . '/0/00', null, __METHOD__ );
-               copy( "$IP/tests/phpunit/data/parser/320x240.ogv", "$dir/0/00/Video.ogv" );
-               wfMkdirParents( $dir . '/4/41', null, __METHOD__ );
-               copy( "$IP/tests/phpunit/data/media/say-test.ogg", "$dir/4/41/Audio.oga" );
-
-               return;
-       }
-
-       /**
-        * Restore default values and perform any necessary clean-up
-        * after each test runs.
-        */
-       private function teardownGlobals() {
-               RepoGroup::destroySingleton();
-               FileBackendGroup::destroySingleton();
-               LockManagerGroup::destroySingletons();
-               LinkCache::singleton()->clear();
-               MWTidy::destroySingleton();
-
-               foreach ( $this->savedGlobals as $var => $val ) {
-                       $GLOBALS[$var] = $val;
-               }
-       }
-
-       /**
-        * Remove the dummy uploads directory
-        * @param string $dir
-        */
-       private function teardownUploadDir( $dir ) {
-               if ( $this->keepUploads ) {
-                       return;
-               }
-
-               // delete the files first, then the dirs.
-               self::deleteFiles(
-                       [
-                               "$dir/3/3a/Foobar.jpg",
-                               "$dir/thumb/3/3a/Foobar.jpg/*.jpg",
-                               "$dir/e/ea/Thumb.png",
-                               "$dir/0/09/Bad.jpg",
-                               "$dir/5/5f/LoremIpsum.djvu",
-                               "$dir/thumb/5/5f/LoremIpsum.djvu/*-LoremIpsum.djvu.jpg",
-                               "$dir/f/ff/Foobar.svg",
-                               "$dir/thumb/f/ff/Foobar.svg/*-Foobar.svg.png",
-                               "$dir/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
-                               "$dir/0/00/Video.ogv",
-                               "$dir/thumb/0/00/Video.ogv/120px--Video.ogv.jpg",
-                               "$dir/thumb/0/00/Video.ogv/180px--Video.ogv.jpg",
-                               "$dir/thumb/0/00/Video.ogv/240px--Video.ogv.jpg",
-                               "$dir/thumb/0/00/Video.ogv/320px--Video.ogv.jpg",
-                               "$dir/thumb/0/00/Video.ogv/270px--Video.ogv.jpg",
-                               "$dir/thumb/0/00/Video.ogv/320px-seek=2-Video.ogv.jpg",
-                               "$dir/thumb/0/00/Video.ogv/320px-seek=3.3666666666667-Video.ogv.jpg",
-                               "$dir/4/41/Audio.oga",
-                       ]
-               );
-
-               self::deleteDirs(
-                       [
-                               "$dir/3/3a",
-                               "$dir/3",
-                               "$dir/thumb/3/3a/Foobar.jpg",
-                               "$dir/thumb/3/3a",
-                               "$dir/thumb/3",
-                               "$dir/e/ea",
-                               "$dir/e",
-                               "$dir/f/ff/",
-                               "$dir/f/",
-                               "$dir/thumb/f/ff/Foobar.svg",
-                               "$dir/thumb/f/ff/",
-                               "$dir/thumb/f/",
-                               "$dir/0/00/",
-                               "$dir/0/09/",
-                               "$dir/0/",
-                               "$dir/5/5f",
-                               "$dir/5",
-                               "$dir/thumb/0/00/Video.ogv",
-                               "$dir/thumb/0/00",
-                               "$dir/thumb/0",
-                               "$dir/thumb/5/5f/LoremIpsum.djvu",
-                               "$dir/thumb/5/5f",
-                               "$dir/thumb/5",
-                               "$dir/thumb",
-                               "$dir/4/41",
-                               "$dir/4",
-                               "$dir/math/f/a/5",
-                               "$dir/math/f/a",
-                               "$dir/math/f",
-                               "$dir/math",
-                               "$dir/lockdir",
-                               "$dir",
-                       ]
-               );
-       }
-
-       /**
-        * Delete the specified files, if they exist.
-        * @param array $files Full paths to files to delete.
-        */
-       private static function deleteFiles( $files ) {
-               foreach ( $files as $pattern ) {
-                       foreach ( glob( $pattern ) as $file ) {
-                               if ( file_exists( $file ) ) {
-                                       unlink( $file );
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Delete the specified directories, if they exist. Must be empty.
-        * @param array $dirs Full paths to directories to delete.
-        */
-       private static function deleteDirs( $dirs ) {
-               foreach ( $dirs as $dir ) {
-                       if ( is_dir( $dir ) ) {
-                               rmdir( $dir );
-                       }
-               }
-       }
-
-       /**
-        * "Running test $desc..."
-        * @param string $desc
-        */
-       protected function showTesting( $desc ) {
-               print "Running test $desc... ";
-       }
-
-       /**
-        * Print a happy success message.
-        *
-        * Refactored in 1.22 to use ParserTestResult
-        *
-        * @param ParserTestResult $testResult
-        * @return bool
-        */
-       protected function showSuccess( ParserTestResult $testResult ) {
-               if ( $this->showProgress ) {
-                       print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n";
-               }
-
-               return true;
-       }
-
-       /**
-        * Print a failure message and provide some explanatory output
-        * about what went wrong if so configured.
-        *
-        * Refactored in 1.22 to use ParserTestResult
-        *
-        * @param ParserTestResult $testResult
-        * @return bool
-        */
-       protected function showFailure( ParserTestResult $testResult ) {
-               if ( $this->showFailure ) {
-                       if ( !$this->showProgress ) {
-                               # In quiet mode we didn't show the 'Testing' message before the
-                               # test, in case it succeeded. Show it now:
-                               $this->showTesting( $testResult->description );
-                       }
-
-                       print $this->term->color( '31' ) . 'FAILED!' . $this->term->reset() . "\n";
-
-                       if ( $this->showOutput ) {
-                               print "--- Expected ---\n{$testResult->expected}\n";
-                               print "--- Actual ---\n{$testResult->actual}\n";
-                       }
-
-                       if ( $this->showDiffs ) {
-                               print $this->quickDiff( $testResult->expected, $testResult->actual );
-                               if ( !$this->wellFormed( $testResult->actual ) ) {
-                                       print "XML error: $this->mXmlError\n";
-                               }
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Print a skipped message.
-        *
-        * @return bool
-        */
-       protected function showSkipped() {
-               if ( $this->showProgress ) {
-                       print $this->term->color( '1;33' ) . 'SKIPPED' . $this->term->reset() . "\n";
-               }
-
-               return true;
-       }
-
-       /**
-        * Run given strings through a diff and return the (colorized) output.
-        * Requires writable /tmp directory and a 'diff' command in the PATH.
-        *
-        * @param string $input
-        * @param string $output
-        * @param string $inFileTail Tailing for the input file name
-        * @param string $outFileTail Tailing for the output file name
-        * @return string
-        */
-       protected function quickDiff( $input, $output,
-               $inFileTail = 'expected', $outFileTail = 'actual'
-       ) {
-               # Windows, or at least the fc utility, is retarded
-               $slash = wfIsWindows() ? '\\' : '/';
-               $prefix = wfTempDir() . "{$slash}mwParser-" . mt_rand();
-
-               $infile = "$prefix-$inFileTail";
-               $this->dumpToFile( $input, $infile );
-
-               $outfile = "$prefix-$outFileTail";
-               $this->dumpToFile( $output, $outfile );
-
-               $shellInfile = wfEscapeShellArg( $infile );
-               $shellOutfile = wfEscapeShellArg( $outfile );
-
-               global $wgDiff3;
-               // we assume that people with diff3 also have usual diff
-               $shellCommand = ( wfIsWindows() && !$wgDiff3 ) ? 'fc' : 'diff -au';
-
-               $diff = wfShellExec( "$shellCommand $shellInfile $shellOutfile" );
-
-               unlink( $infile );
-               unlink( $outfile );
-
-               return $this->colorDiff( $diff );
-       }
-
-       /**
-        * Write the given string to a file, adding a final newline.
-        *
-        * @param string $data
-        * @param string $filename
-        */
-       private function dumpToFile( $data, $filename ) {
-               $file = fopen( $filename, "wt" );
-               fwrite( $file, $data . "\n" );
-               fclose( $file );
-       }
-
-       /**
-        * Colorize unified diff output if set for ANSI color output.
-        * Subtractions are colored blue, additions red.
-        *
-        * @param string $text
-        * @return string
-        */
-       protected function colorDiff( $text ) {
-               return preg_replace(
-                       [ '/^(-.*)$/m', '/^(\+.*)$/m' ],
-                       [ $this->term->color( 34 ) . '$1' . $this->term->reset(),
-                               $this->term->color( 31 ) . '$1' . $this->term->reset() ],
-                       $text );
-       }
-
-       /**
-        * Show "Reading tests from ..."
-        *
-        * @param string $path
-        */
-       public function showRunFile( $path ) {
-               print $this->term->color( 1 ) .
-                       "Reading tests from \"$path\"..." .
-                       $this->term->reset() .
-                       "\n";
-       }
-
-       /**
-        * Insert a temporary test article
-        * @param string $name The title, including any prefix
-        * @param string $text The article text
-        * @param int|string $line The input line number, for reporting errors
-        * @param bool|string $ignoreDuplicate Whether to silently ignore duplicate pages
-        * @throws Exception
-        * @throws MWException
-        */
-       public static function addArticle( $name, $text, $line = 'unknown', $ignoreDuplicate = '' ) {
-               global $wgCapitalLinks;
-
-               $oldCapitalLinks = $wgCapitalLinks;
-               $wgCapitalLinks = true; // We only need this from SetupGlobals() See r70917#c8637
-
-               $text = self::chomp( $text );
-               $name = self::chomp( $name );
-
-               $title = Title::newFromText( $name );
-
-               if ( is_null( $title ) ) {
-                       throw new MWException( "invalid title '$name' at line $line\n" );
-               }
-
-               $page = WikiPage::factory( $title );
-               $page->loadPageData( 'fromdbmaster' );
-
-               if ( $page->exists() ) {
-                       if ( $ignoreDuplicate == 'ignoreduplicate' ) {
-                               return;
-                       } else {
-                               throw new MWException( "duplicate article '$name' at line $line\n" );
-                       }
-               }
-
-               $page->doEditContent( ContentHandler::makeContent( $text, $title ), '', EDIT_NEW );
-
-               $wgCapitalLinks = $oldCapitalLinks;
-       }
-
-       /**
-        * Steal a callback function from the primary parser, save it for
-        * application to our scary parser. If the hook is not installed,
-        * abort processing of this file.
-        *
-        * @param string $name
-        * @return bool True if tag hook is present
-        */
-       public function requireHook( $name ) {
-               global $wgParser;
-
-               $wgParser->firstCallInit(); // make sure hooks are loaded.
-
-               if ( isset( $wgParser->mTagHooks[$name] ) ) {
-                       $this->hooks[$name] = $wgParser->mTagHooks[$name];
-               } else {
-                       echo "   This test suite requires the '$name' hook extension, skipping.\n";
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * Steal a callback function from the primary parser, save it for
-        * application to our scary parser. If the hook is not installed,
-        * abort processing of this file.
-        *
-        * @param string $name
-        * @return bool True if function hook is present
-        */
-       public function requireFunctionHook( $name ) {
-               global $wgParser;
-
-               $wgParser->firstCallInit(); // make sure hooks are loaded.
-
-               if ( isset( $wgParser->mFunctionHooks[$name] ) ) {
-                       $this->functionHooks[$name] = $wgParser->mFunctionHooks[$name];
-               } else {
-                       echo "   This test suite requires the '$name' function hook extension, skipping.\n";
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * Steal a callback function from the primary parser, save it for
-        * application to our scary parser. If the hook is not installed,
-        * abort processing of this file.
-        *
-        * @param string $name
-        * @return bool True if function hook is present
-        */
-       public function requireTransparentHook( $name ) {
-               global $wgParser;
-
-               $wgParser->firstCallInit(); // make sure hooks are loaded.
-
-               if ( isset( $wgParser->mTransparentTagHooks[$name] ) ) {
-                       $this->transparentHooks[$name] = $wgParser->mTransparentTagHooks[$name];
-               } else {
-                       echo "   This test suite requires the '$name' transparent hook extension, skipping.\n";
-                       return false;
-               }
-
-               return true;
-       }
-
-       private function wellFormed( $text ) {
-               $html =
-                       Sanitizer::hackDocType() .
-                               '<html>' .
-                               $text .
-                               '</html>';
-
-               $parser = xml_parser_create( "UTF-8" );
-
-               # case folding violates XML standard, turn it off
-               xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
-
-               if ( !xml_parse( $parser, $html, true ) ) {
-                       $err = xml_error_string( xml_get_error_code( $parser ) );
-                       $position = xml_get_current_byte_index( $parser );
-                       $fragment = $this->extractFragment( $html, $position );
-                       $this->mXmlError = "$err at byte $position:\n$fragment";
-                       xml_parser_free( $parser );
-
-                       return false;
-               }
-
-               xml_parser_free( $parser );
-
-               return true;
-       }
-
-       private function extractFragment( $text, $position ) {
-               $start = max( 0, $position - 10 );
-               $before = $position - $start;
-               $fragment = '...' .
-                       $this->term->color( 34 ) .
-                       substr( $text, $start, $before ) .
-                       $this->term->color( 0 ) .
-                       $this->term->color( 31 ) .
-                       $this->term->color( 1 ) .
-                       substr( $text, $position, 1 ) .
-                       $this->term->color( 0 ) .
-                       $this->term->color( 34 ) .
-                       substr( $text, $position + 1, 9 ) .
-                       $this->term->color( 0 ) .
-                       '...';
-               $display = str_replace( "\n", ' ', $fragment );
-               $caret = '   ' .
-                       str_repeat( ' ', $before ) .
-                       $this->term->color( 31 ) .
-                       '^' .
-                       $this->term->color( 0 );
-
-               return "$display\n$caret";
-       }
-
-       static function getFakeTimestamp( &$parser, &$ts ) {
-               $ts = 123; // parsed as '1970-01-01T00:02:03Z'
-               return true;
-       }
-}
diff --git a/tests/parser/parserTests.php b/tests/parser/parserTests.php
new file mode 100644 (file)
index 0000000..38923f0
--- /dev/null
@@ -0,0 +1,195 @@
+<?php
+/**
+ * MediaWiki parser test suite
+ *
+ * Copyright © 2004 Brion Vibber <brion@pobox.com>
+ * https://www.mediawiki.org/
+ *
+ * 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 Testing
+ */
+
+// Some methods which are discouraged for normal code throw exceptions unless
+// we declare this is just a test.
+define( 'MW_PARSER_TEST', true );
+
+require __DIR__ . '/../../maintenance/Maintenance.php';
+
+class ParserTestsMaintenance extends Maintenance {
+       function __construct() {
+               parent::__construct();
+               $this->addDescription( 'Run parser tests' );
+
+               $this->addOption( 'quick', 'Suppress diff output of failed tests' );
+               $this->addOption( 'quiet', 'Suppress notification of passed tests (shows only failed tests)' );
+               $this->addOption( 'show-output', 'Show expected and actual output' );
+               $this->addOption( 'color', '[=yes|no] Override terminal detection and force ' .
+                       'color output on or off. Use wgCommandLineDarkBg = true; if your term is dark',
+                       false, true );
+               $this->addOption( 'regex', 'Only run tests whose descriptions which match given regex',
+                       false, true );
+               $this->addOption( 'filter', 'Alias for --regex', false, true );
+               $this->addOption( 'file', 'Run test cases from a custom file instead of parserTests.txt',
+                       false, true, false, true );
+               $this->addOption( 'record', 'Record tests in database' );
+               $this->addOption( 'compare', 'Compare with recorded results, without updating the database.' );
+               $this->addOption( 'setversion', 'When using --record, set the version string to use (useful' .
+                       'with "git rev-parse HEAD" to get the exact revision)',
+                       false, true );
+               $this->addOption( 'keep-uploads', 'Re-use the same upload directory for each ' .
+                       'test, don\'t delete it' );
+               $this->addOption( 'file-backend', 'Use the file backend with the given name,' .
+                       'and upload files to it, instead of creating a mock file backend.', false, true );
+               $this->addOption( 'upload-dir', 'Specify the upload directory to use. Useful in ' .
+                       'conjunction with --keep-uploads. Causes a real (non-mock) file backend to ' .
+                       'be used.', false, true );
+               $this->addOption( 'run-disabled', 'run disabled tests' );
+               $this->addOption( 'run-parsoid', 'run parsoid tests (normally disabled)' );
+               $this->addOption( 'dwdiff', 'Use dwdiff to display diff output' );
+               $this->addOption( 'mark-ws', 'Mark whitespace in diffs by replacing it with symbols' );
+               $this->addOption( 'norm', 'Apply a comma-separated list of normalization functions to ' .
+                       'both the expected and actual output in order to resolve ' .
+                       'irrelevant differences. The accepted normalization functions ' .
+                       'are: removeTbody to remove <tbody> tags; and trimWhitespace ' .
+                       'to trim whitespace from the start and end of text nodes.',
+                       false, true );
+               $this->addOption( 'use-tidy-config', 'Use the wiki\'s Tidy configuration instead of known-good' .
+                       'defaults.' );
+       }
+
+       public function finalSetup() {
+               parent::finalSetup();
+               self::requireTestsAutoloader();
+               TestSetup::applyInitialConfig();
+       }
+
+       public function execute() {
+               global $wgParserTestFiles, $wgDBtype;
+
+               // Cases of weird db corruption were encountered when running tests on earlyish
+               // versions of SQLite
+               if ( $wgDBtype == 'sqlite' ) {
+                       $db = wfGetDB( DB_MASTER );
+                       $version = $db->getServerVersion();
+                       if ( version_compare( $version, '3.6' ) < 0 ) {
+                               die( "Parser tests require SQLite version 3.6 or later, you have $version\n" );
+                       }
+               }
+
+               // Print out software version to assist with locating regressions
+               $version = SpecialVersion::getVersion( 'nodb' );
+               echo "This is MediaWiki version {$version}.\n\n";
+
+               // Only colorize output if stdout is a terminal.
+               $color = !wfIsWindows() && Maintenance::posix_isatty( 1 );
+
+               if ( $this->hasOption( 'color' ) ) {
+                       switch ( $this->getOption( 'color' ) ) {
+                               case 'no':
+                                       $color = false;
+                                       break;
+                               case 'yes':
+                               default:
+                                       $color = true;
+                                       break;
+                       }
+               }
+
+               $record = $this->hasOption( 'record' );
+               $compare = $this->hasOption( 'compare' );
+
+               $regex = $this->getOption( 'filter', $this->getOption( 'regex', false ) );
+               if ( $regex !== false ) {
+                       $regex = "/$regex/i";
+
+                       if ( $record ) {
+                               echo "Warning: --record cannot be used with --regex, disabling --record\n";
+                               $record = false;
+                       }
+               }
+
+               $term = $color
+                       ? new AnsiTermColorer()
+                       : new DummyTermColorer();
+
+               $recorder = new MultiTestRecorder;
+
+               $recorder->addRecorder( new ParserTestPrinter(
+                       $term,
+                       [
+                               'showDiffs' => !$this->hasOption( 'quick' ),
+                               'showProgress' => !$this->hasOption( 'quiet' ),
+                               'showFailure' => !$this->hasOption( 'quiet' )
+                                               || ( !$record && !$compare ), // redundant output
+                               'showOutput' => $this->hasOption( 'show-output' ),
+                               'useDwdiff' => $this->hasOption( 'dwdiff' ),
+                               'markWhitespace' => $this->hasOption( 'mark-ws' ),
+                       ]
+               ) );
+
+               $recorderLB = false;
+               if ( $record || $compare ) {
+                       $recorderLB = wfGetLBFactory()->newMainLB();
+                       // This connection will have the wiki's table prefix, not parsertest_
+                       $recorderDB = $recorderLB->getConnection( DB_MASTER );
+
+                       // Add recorder before previewer because recorder will create the
+                       // DB table if it doesn't exist
+                       if ( $record ) {
+                               $recorder->addRecorder( new DbTestRecorder( $recorderDB ) );
+                       }
+                       $recorder->addRecorder( new DbTestPreviewer(
+                               $recorderDB,
+                               function ( $name ) use ( $regex ) {
+                                       // Filter reports of old tests by the filter regex
+                                       if ( $regex === false ) {
+                                               return true;
+                                       } else {
+                                               return (bool)preg_match( $regex, $name );
+                                       }
+                               } ) );
+               }
+
+               // Default parser tests and any set from extensions or local config
+               $files = $this->getOption( 'file', $wgParserTestFiles );
+
+               $norm = $this->hasOption( 'norm' ) ? explode( ',', $this->getOption( 'norm' ) ) : [];
+
+               $tester = new ParserTestRunner( $recorder, [
+                       'norm' => $norm,
+                       'regex' => $regex,
+                       'keep-uploads' => $this->hasOption( 'keep-uploads' ),
+                       'run-disabled' => $this->hasOption( 'run-disabled' ),
+                       'run-parsoid' => $this->hasOption( 'run-parsoid' ),
+                       'use-tidy-config' => $this->hasOption( 'use-tidy-config' ),
+                       'file-backend' => $this->getOption( 'file-backend' ),
+                       'upload-dir' => $this->getOption( 'upload-dir' ),
+               ] );
+
+               $ok = $tester->runTestsFromFiles( $files );
+               if ( $recorderLB ) {
+                       $recorderLB->closeAll();
+               }
+               if ( !$ok ) {
+                       exit( 1 );
+               }
+       }
+}
+
+$maintClass = 'ParserTestsMaintenance';
+require_once RUN_MAINTENANCE_IF_MAIN;
index f45c666..c1c421b 100644 (file)
@@ -35,7 +35,7 @@
 #
 # You can also set the following parser properties via test options:
 #  wgEnableUploads, wgAllowExternalImages, wgMaxTocLevel,
-#  wgLinkHolderBatchSize, wgRawHtml
+#  wgLinkHolderBatchSize, wgRawHtml, wgInterwikiMagic
 #
 # For testing purposes, temporary articles can created:
 # !!article / NAMESPACE:TITLE / !!text / ARTICLE TEXT / !!endarticle
@@ -2824,7 +2824,7 @@ parsoid
 !! wikitext
 {{echo|[{{fullurl:{{FULLPAGENAME}}|action=edit}} bar]}}
 !! html
-<p typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"[{{fullurl:{{FULLPAGENAME}}|action=edit}} bar]"}},"i":0}}]}'>[Main_Page bar]</p>
+<p typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"[{{fullurl:{{FULLPAGENAME}}|action=edit}} bar]"}},"i":0}}]}'>[Main Page bar]</p>
 !! end
 
 !! test
@@ -3173,58 +3173,19 @@ foo
 
 !!end
 
-!!test
+!! test
 4. Indent-Pre and extension tags
 !! wikitext
- a <gallery>
-File:foobar.jpg
-</gallery>
-!! html
- a <ul class="gallery mw-gallery-traditional">
-               <li class="gallerybox" style="width: 155px"><div style="width: 155px">
-                       <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" width="120" height="14" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/240px-Foobar.jpg 2x" /></a></div></div>
-                       <div class="gallerytext">
-                       </div>
-               </div></li>
-</ul>
-
-!! html+tidy
-<p>a</p>
-<ul class="gallery mw-gallery-traditional">
-<li class="gallerybox" style="width: 155px">
-<div style="width: 155px">
-<div class="thumb" style="width: 150px;">
-<div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" width="120" height="14" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/240px-Foobar.jpg 2x" /></a></div>
-</div>
-<div class="gallerytext"></div>
-</div>
-</li>
-</ul>
-!!end
+ a <tag />
+!! html/php
+ a <pre>
+NULL
+array (
+)
+</pre>
 
-!! test
-Table wikitext syntax outside wiki-tables
-!! wikitext
-a
-! not a table heading
-|- not a table row
-| not a table cell
-| class="foo bar" | baz
-b
-|}
-|-
-c
-!! html
-<p>a
-! not a table heading
-|- not a table row
-| not a table cell
-| class="foo bar" | baz
-b
-|}
-|-
-c
-</p>
+!! html/parsoid
+ a <pre typeof="mw:Extension/tag" about="#mwt2" data-parsoid='{}' data-mw='{"name":"tag","attrs":{},"body":null}'></pre>
 !! end
 
 !!test
@@ -4865,13 +4826,20 @@ And again with mixed protocols: [ftp://example.com?url=http://example.com link]
 </p>
 !!end
 
+# Since Parsoid is starting to emit canonical wikitext for links,
+# [http://example.com http://example.com] will not RT back to that
+# form anymore.
 !! test
 External links: URL in text
+!! options
+parsoid=wt2html
 !! wikitext
 URL in text: [http://example.com http://example.com]
-!! html
+!! html/php
 <p>URL in text: <a rel="nofollow" class="external free" href="http://example.com">http://example.com</a>
 </p>
+!! html/parsoid
+<p>URL in text: <a rel="mw:ExtLink" href="http://example.com">http://example.com</a></p>
 !! end
 
 !! test
@@ -7619,11 +7587,14 @@ Just a test of an article title containing a percent.
 Link containing % (not as a hex sequence)
 !! wikitext
 [[7% Solution]]
+[[7% Solution|7%25 Solution]]
 !! html/php
 <p><a href="/wiki/7%25_Solution" title="7% Solution">7% Solution</a>
+<a href="/wiki/7%25_Solution" title="7% Solution">7%25 Solution</a>
 </p>
 !! html/parsoid
-<p><a rel="mw:WikiLink" href="./7%25_Solution" title="7% Solution">7% Solution</a></p>
+<p><a rel="mw:WikiLink" href="./7%25_Solution" title="7% Solution">7% Solution</a>
+<a rel="mw:WikiLink" href="./7%25_Solution" title="7% Solution">7%25 Solution</a></p>
 !! end
 
 # note that the parsoid HTML is identical to the previous test output,
@@ -7635,11 +7606,14 @@ Link containing % as a single hex sequence interpreted to char
 parsoid=wt2wt,wt2html,html2html
 !! wikitext
 [[7%25 Solution]]
+[[7%25 Solution|7%25 Solution]]
 !! html/php
 <p><a href="/wiki/7%25_Solution" title="7% Solution">7% Solution</a>
+<a href="/wiki/7%25_Solution" title="7% Solution">7%25 Solution</a>
 </p>
 !! html/parsoid
-<p><a rel="mw:WikiLink" href="./7%25_Solution" title="7% Solution">7% Solution</a></p>
+<p><a rel="mw:WikiLink" href="./7%25_Solution" title="7% Solution">7% Solution</a>
+<a rel="mw:WikiLink" href="./7%25_Solution" title="7% Solution">7%25 Solution</a></p>
 !!end
 
 !! test
@@ -8004,6 +7978,28 @@ Link with multiple ":" in a subpage-supporting namespace (bug 63636)
 <p><a rel="mw:WikiLink" href="./User:Foo/Test/63636:Bar" title="User:Foo/Test/63636:Bar">Test</a></p>
 !! end
 
+## Mainly a sanity check for Parsoid
+!! test
+Handle title parsing for subpages
+!! options
+title=[[/123123]]
+!! wikitext
+123
+!! html/parsoid
+<p>123</p>
+!! end
+
+## FIXME: Add a working php section here
+!! test
+Link to a subpage from a namespace other than main
+!! options
+title=[[User:test]]
+!! wikitext
+[[/123]]
+!! html/parsoid
+<p><a rel="mw:WikiLink" href="./User:Test/123" title="User:Test/123" data-parsoid='{"stx":"simple","a":{"href":"./User:Test/123"},"sa":{"href":"/123"}}'>/123</a></p>
+!! end
+
 !! test
 Purely hash wikilink
 !! options
@@ -8361,6 +8357,7 @@ Blah blah blah
 <link rel="mw:PageProp/Language" href="http://zh.wikipedia.org/wiki/Chinese"/>
 !! end
 
+## parsoid html2wt will lose the space variations
 !! test
 Interlanguage link with spacing
 !! options
@@ -8391,6 +8388,7 @@ Blah blah blah
 <link rel="mw:PageProp/Language" href="http://zh.wikipedia.org/wiki/Chinese"/>
 !! end
 
+## parsoid html2wt will lose the space variations
 !! test
 Interlanguage link variations
 !! options
@@ -8410,6 +8408,7 @@ Blah blah blah
 <link rel="mw:PageProp/Language" href="http://es.wikipedia.org/wiki/Foo_bar" />
 !! end
 
+## parsoid html2wt will normalize the space to _
 !! test
 Space and question mark encoding in interlanguage links (T95473)
 !! options
@@ -8470,6 +8469,34 @@ Blah blah blah
 <link rel="mw:PageProp/Language" title="Multilingual" href="http://wikisource.org/wiki/Article"/>
 !! end
 
+## PHP parser tests script needs an update
+## Parsoid html2wt will normalize output to [[:zh:Chinese]]
+!! test
+Language links render as inline links if $wgInterwikiMagic=false
+!! options
+wgInterwikiMagic=false
+parsoid=wt2html,wt2wt,html2html
+!! wikitext
+Blah blah blah
+[[zh:Chinese]]
+!! html/parsoid
+<p>Blah blah blah <a rel="mw:ExtLink" href="http://zh.wikipedia.org/wiki/Chinese" title="zh:Chinese">zh:Chinese</a></p>
+!! end
+
+## PHP parser tests script needs an update
+## Parsoid html2wt will normalize output to [[:zh:Chinese]]
+!! test
+Language links render as inline links in the Talk namespace
+!! options
+title=Talk:Foo
+parsoid=wt2html,wt2wt,html2html
+!! wikitext
+Blah blah blah
+[[zh:Chinese]]
+!! html/parsoid
+<p>Blah blah blah <a rel="mw:ExtLink" href="http://zh.wikipedia.org/wiki/Chinese" title="zh:Chinese">zh:Chinese</a></p>
+!! end
+
 !! test
 Parsoid-specific test: Wikilinks with &nbsp; should RT properly
 !! options
@@ -8484,18 +8511,25 @@ language=ln
 !! test
 Parsoid bug 53221: Wikilinks should be properly entity-escaped
 !! options
-parsoid=html2wt
+parsoid={ "modes": ["html2wt"], "suppressErrors": true }
 !! html/parsoid
 <p>He&amp;nbsp;llo <a href="Foo" rel="mw:WikiLink">He&amp;nbsp;llo</a></p>
 <p>He&amp;nbsp;llo <a href="He&amp;nbsp;llo" rel="mw:WikiLink">He&amp;nbsp;llo</a></p>
 !! wikitext
 He&amp;nbsp;llo [[Foo|He&amp;nbsp;llo]]
 
-He&amp;nbsp;llo [[He&amp;nbsp;llo]]
+He&amp;nbsp;llo He&amp;nbsp;llo
+!! html/php
+<p>He&amp;nbsp;llo <a href="/wiki/Foo" title="Foo">He&amp;nbsp;llo</a>
+</p><p>He&amp;nbsp;llo He&amp;nbsp;llo
+</p>
 !! end
 
+# html2wt will fail because of title normalization without data-parsoid
 !! test
 Parsoid: handle constructor well
+!! options
+parsoid=wt2html,wt2wt
 !! wikitext
 [[constructor]]
 
@@ -8505,9 +8539,9 @@ Parsoid: handle constructor well
 </p><p><a href="/index.php?title=Constructor:foo&amp;action=edit&amp;redlink=1" class="new" title="Constructor:foo (page does not exist)">constructor:foo</a>
 </p>
 !! html/parsoid
-<p><a rel="mw:WikiLink" href="./Constructor" title="Constructor" data-parsoid="{&quot;stx&quot;:&quot;simple&quot;,&quot;a&quot;:{&quot;href&quot;:&quot;./Constructor&quot;},&quot;sa&quot;:{&quot;href&quot;:&quot;constructor&quot;}}">constructor</a></p>
+<p><a rel="mw:WikiLink" href="./Constructor" title="Constructor" data-parsoid='{"stx":"simple","a":{"href":"./Constructor"},"sa":{"href":"constructor"}}'>constructor</a></p>
 
-<p><a rel="mw:WikiLink" href="./Foo" title="Foo" data-parsoid="{&quot;stx&quot;:&quot;simple&quot;,&quot;a&quot;:{&quot;href&quot;:&quot;./Foo&quot;},&quot;sa&quot;:{&quot;href&quot;:&quot;constructor:foo&quot;}}">constructor:foo</a></p>
+<p><a rel="mw:WikiLink" href="./Constructor:foo" title="Constructor:foo" data-parsoid='{"stx":"simple","a":{"href":"./Constructor:foo"},"sa":{"href":"constructor:foo"}}'>constructor:foo</a></p>
 !! end
 
 !! article
@@ -10109,7 +10143,7 @@ Parsoid: Page property magic word with magic word contents
 !! wikitext
 {{DISPLAYTITLE:''{{PAGENAME}}''}}
 !! html/parsoid
-<meta property="mw:PageProp/displaytitle" content="Main_Page" about="#mwt2" typeof="mw:ExpandedAttrs" data-parsoid='{"src":"{{DISPLAYTITLE:&#39;&#39;{{PAGENAME}}&#39;&#39;}}"}' data-mw='{"attribs":[[{"txt":"content"},{"html":"&lt;i data-parsoid=&#39;{\"dsr\":[15,31,2,2]}&#39;>&lt;span about=\"#mwt1\" typeof=\"mw:Transclusion\" data-parsoid=&#39;{\"pi\":[[]],\"dsr\":[17,29,null,null]}&#39; data-mw=&#39;{\"parts\":[{\"template\":{\"target\":{\"wt\":\"PAGENAME\",\"function\":\"pagename\"},\"params\":{},\"i\":0}}]}&#39;>Main_Page&lt;/span>&lt;/i>"}]]}'/>
+<meta property="mw:PageProp/displaytitle" content="Main Page" about="#mwt2" typeof="mw:ExpandedAttrs" data-parsoid='{"src":"{{DISPLAYTITLE:&#39;&#39;{{PAGENAME}}&#39;&#39;}}"}' data-mw='{"attribs":[[{"txt":"content"},{"html":"&lt;i data-parsoid=&#39;{\"dsr\":[15,31,2,2]}&#39;>&lt;span about=\"#mwt1\" typeof=\"mw:Transclusion\" data-parsoid=&#39;{\"pi\":[[]],\"dsr\":[17,29,null,null]}&#39; data-mw=&#39;{\"parts\":[{\"template\":{\"target\":{\"wt\":\"PAGENAME\",\"function\":\"pagename\"},\"params\":{},\"i\":0}}]}&#39;>Main Page&lt;/span>&lt;/i>"}]]}'/>
 !! end
 
 !! test
@@ -10939,7 +10973,7 @@ foo {{''}} baz
 <p>foo bar baz
 </p>
 !! html/parsoid
-<p>foo <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"&#39;&#39;"},"params":{},"i":0}}]}'>bar</span> baz
+<p>foo <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"&#39;&#39;","href":"./Template:&#39;&#39;"},"params":{},"i":0}}]}'>bar</span> baz
 </p>
 !! end
 
@@ -11413,6 +11447,33 @@ parsoid=wt2html
 </tbody></table>
 !!end
 
+!! test
+Table wikitext syntax outside wiki-tables
+!! wikitext
+a
+|+ not a caption
+! not a table heading
+|- not a table row
+| not a table cell
+| class="foo bar" | baz
+b
+|}
+|-
+c
+!! html
+<p>a
+|+ not a caption
+! not a table heading
+|- not a table row
+| not a table cell
+| class="foo bar" | baz
+b
+|}
+|-
+c
+</p>
+!! end
+
 ###
 ### Testing parsing of templates where a template arg
 ### has the same name as the template itself.
@@ -13924,6 +13985,17 @@ Escape HTML special chars in image alt text
 <p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"caption","ak":"&amp; &lt; > \""}]}' data-mw='{"caption":"&amp;amp; &amp;lt; > \""}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p>
 !! end
 
+!! test
+Entities in file name and attributes
+!! wikitext
+[[File:7%25 solution.gif|manualthumb=7%25 solution.gif|link=7%25 solution|[[7%25 solution]]]]
+!! html/php
+<p><a href="/index.php?title=Special:Upload&amp;wpDestFile=7%25_solution.gif" class="new" title="File:7% solution.gif">7% solution</a>
+</p>
+!! html/parsoid
+<p><span class="mw-default-size" typeof="mw:Error mw:Image" data-parsoid='{"optList":[{"ck":"bogus","ak":"manualthumb=7%25 solution.gif"},{"ck":"link","ak":"link=7%25 solution"},{"ck":"caption","ak":"[[7%25 solution]]"}]}' data-mw='{"errors":[{"key":"missing-image","message":"This image does not exist."}],"caption":"&lt;a rel=\"mw:WikiLink\" href=\"./7%25_solution\" title=\"7% solution\" data-parsoid=&#39;{\"stx\":\"simple\",\"a\":{\"href\":\"./7%25_solution\"},\"sa\":{\"href\":\"7%25 solution\"},\"dsr\":[74,91,2,2]}&#39;>7% solution&lt;/a>"}'><a href="./7%25_solution" data-parsoid='{"a":{"href":"./7%25_solution"},"sa":{"href":"link=7%25 solution"}}'><img resource="./File:7%25_solution.gif" src="./Special:FilePath/7%25_solution.gif" height="220" width="220" data-parsoid='{"a":{"resource":"./File:7%25_solution.gif","height":"220","width":"220"},"sa":{"resource":"File:7%25 solution.gif"}}'/></a></span></p>
+!! end
+
 !! test
 BUG 499: Alt text should have &#1234;, not &amp;1234;
 !! wikitext
@@ -14574,7 +14646,7 @@ cat
 !! wikitext
 [[Category:MediaWiki User's Guide]]
 !! html
-<a href="/wiki/Category:MediaWiki_User%27s_Guide" title="Category:MediaWiki User's Guide">MediaWiki User's Guide</a>
+cat=MediaWiki_User's_Guide sort=
 !! end
 
 !! test
@@ -14593,7 +14665,7 @@ cat
 !! wikitext
 [[Category:MediaWiki User's Guide|Foo]]
 !! html
-<a href="/wiki/Category:MediaWiki_User%27s_Guide" title="Category:MediaWiki User's Guide">MediaWiki User's Guide</a>
+cat=MediaWiki_User's_Guide sort=Foo
 !! end
 
 !! test
@@ -14603,7 +14675,7 @@ cat
 !! wikitext
 [[Category:MediaWiki User's Guide|MediaWiki User's Guide]]
 !! html
-<a href="/wiki/Category:MediaWiki_User%27s_Guide" title="Category:MediaWiki User's Guide">MediaWiki User's Guide</a>
+cat=MediaWiki_User's_Guide sort=MediaWiki User's Guide
 !! end
 
 !! test
@@ -14889,6 +14961,14 @@ parsoid=wt2html
 <link rel="mw:PageProp/Category" href="./Category:Baz" data-parsoid='{"stx":"simple","a":{"href":"./Category:Baz"},"sa":{"href":"Category:Baz"}}'/>
 !! end
 
+!! test
+Category links with multiple namespaces
+!! wikitext
+[[Category:Project:Foo]]
+!! html/parsoid
+<link rel="mw:PageProp/Category" href="./Category:Project:Foo" />
+!! end
+
 !! test
 Parsoid: Serialize link to category page with colon escape
 !! options
@@ -18827,7 +18907,7 @@ File:Foobar.jpg
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
                        <div class="thumb" style="height: 150px;">Nonexistent.jpg</div>
                        <div class="gallerytext">
-<p><a href="/wiki/File:Nonexistent.jpg" title="File:Nonexistent.jpg">Nonexistent.jpg</a><br />
+<p><a href="/wiki/File:Nonexistent.jpg" class="galleryfilename galleryfilename-truncate" title="File:Nonexistent.jpg">Nonexistent.jpg</a>
 caption
 </p>
                        </div>
@@ -18835,14 +18915,14 @@ caption
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
                        <div class="thumb" style="height: 150px;">Nonexistent.jpg</div>
                        <div class="gallerytext">
-<p><a href="/wiki/File:Nonexistent.jpg" title="File:Nonexistent.jpg">Nonexistent.jpg</a><br />
+<p><a href="/wiki/File:Nonexistent.jpg" class="galleryfilename galleryfilename-truncate" title="File:Nonexistent.jpg">Nonexistent.jpg</a>
 </p>
                        </div>
                </div></li>
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
                        <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" width="120" height="14" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/240px-Foobar.jpg 2x" /></a></div></div>
                        <div class="gallerytext">
-<p><a href="/wiki/File:Foobar.jpg" title="File:Foobar.jpg">Foobar.jpg</a><br />
+<p><a href="/wiki/File:Foobar.jpg" class="galleryfilename galleryfilename-truncate" title="File:Foobar.jpg">Foobar.jpg</a>
 some <b>caption</b> <a href="/wiki/Main_Page" title="Main Page">Main Page</a>
 </p>
                        </div>
@@ -18850,7 +18930,7 @@ some <b>caption</b> <a href="/wiki/Main_Page" title="Main Page">Main Page</a>
                <li class="gallerybox" style="width: 155px"><div style="width: 155px">
                        <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" width="120" height="14" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/240px-Foobar.jpg 2x" /></a></div></div>
                        <div class="gallerytext">
-<p><a href="/wiki/File:Foobar.jpg" title="File:Foobar.jpg">Foobar.jpg</a><br />
+<p><a href="/wiki/File:Foobar.jpg" class="galleryfilename galleryfilename-truncate" title="File:Foobar.jpg">Foobar.jpg</a>
 </p>
                        </div>
                </div></li>
@@ -19298,10 +19378,14 @@ subpage title=[[Subpage test/L1/L2]]
 </p>
 !! end
 
+# This is wt2html only in Parsoid because we add <nowiki>
+# because of {{..}} and we don't expect to fix that to
+# eliminate the nowikis selective for {{..}} markup.
 !! test
 Non-transclusion because of too many up levels
 !! options
 subpage title=[[Subpage test/L1/L2/L3]]
+parsoid=wt2html
 !! wikitext
 {{../../../../More than parent}}
 !! html
@@ -19701,7 +19785,7 @@ language=sr cat
 !! wikitext
 [[Category:МедиаWики Усер'с Гуиде]]
 !! html
-<a href="/wiki/%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%98%D0%B0:MediaWiki_User%27s_Guide" title="Категорија:MediaWiki User's Guide">MediaWiki User's Guide</a>
+cat=МедиаWики_Усер'с_Гуиде sort=
 !! end
 
 
@@ -19730,7 +19814,7 @@ parsoid=wt2html
 !! wikitext
 [[A]][[Category:分类]]
 !! html/php
-<a href="/wiki/Category:%E5%88%86%E7%B1%BB" title="Category:分类">分类</a>
+cat=分类 sort=
 !! html/parsoid
 <p><a rel="mw:WikiLink" href="A" title="A">A</a></p>
 <link rel="mw:PageProp/Category" href="Category:分类"/>
@@ -20005,10 +20089,14 @@ Nested: -{zh-hans:Hi -{zh-cn:China;zh-sg:Singapore;}-;zh-hant:Hello -{zh-tw:Taiw
 </p>
 !! end
 
+# Since Parsoid is starting to emit canonical wikitext for links,
+# [http://example.com http://example.com] will not RT back to that
+# form anymore.
 !! test
 Proper conversion of text in external links
 !! options
 language=sr variant=sr-ec
+parsoid=wt2html
 !! wikitext
 http://www.google.com
 gopher://www.google.com
@@ -20017,7 +20105,7 @@ gopher://www.google.com
 [https://www.google.com irc://www.google.com]
 [ftp://www.google.com www.google.com/ftp://dir]
 [//www.google.com www.google.com]
-!! html
+!! html/php
 <p><a rel="nofollow" class="external free" href="http://www.google.com">http://www.google.com</a>
 <a rel="nofollow" class="external free" href="gopher://www.google.com">gopher://www.google.com</a>
 <a rel="nofollow" class="external free" href="http://www.google.com">http://www.google.com</a>
@@ -20026,6 +20114,14 @@ gopher://www.google.com
 <a rel="nofollow" class="external text" href="ftp://www.google.com">www.гоогле.цом/фтп://дир</a>
 <a rel="nofollow" class="external text" href="//www.google.com">www.гоогле.цом</a>
 </p>
+!! html/parsoid
+<p><a rel="mw:ExtLink" href="http://www.google.com">http://www.google.com</a>
+<a rel="mw:ExtLink" href="gopher://www.google.com">gopher://www.google.com</a>
+<a rel="mw:ExtLink" href="http://www.google.com">http://www.google.com</a>
+<a rel="mw:ExtLink" href="gopher://www.google.com">gopher://www.google.com</a>
+<a rel="mw:ExtLink" href="https://www.google.com">irc://www.google.com</a>
+<a rel="mw:ExtLink" href="ftp://www.google.com">www.гоогле.цом/фтп://дир</a>
+<a rel="mw:ExtLink" href="//www.google.com">www.гоогле.цом</a></p>
 !! end
 
 !! test
@@ -24923,11 +25019,25 @@ Don't block XML namespace declaration
 
 # -----------------------------------------------------------------
 # The following section of tests are primarily to spec requirements
-# around serialization of new/edited content.
+# around Parsoid's serialization (old, new, edited content)
 #
 # All these tests are marked Parsoid html2wt and html2html only
 # ----------------------------------------------------------------
 
+!! test
+Ignore rel attribute in a-tags during serialization to url-links
+!! options
+parsoid=html2wt
+!! html/parsoid
+<a href='http://en.wikipedia.org/wiki/Foobar'>http://en.wikipedia.org/wiki/Foobar</a>
+<a href='http://en.wikipedia.org/wiki/Foobar' rel='mw:ExtLink'>http://en.wikipedia.org/wiki/Foobar</a>
+<a href='http://en.wikipedia.org/wiki/Foobar' rel='mw:WikiLink'>http://en.wikipedia.org/wiki/Foobar</a>
+!! wikitext
+http://en.wikipedia.org/wiki/Foobar
+http://en.wikipedia.org/wiki/Foobar
+http://en.wikipedia.org/wiki/Foobar
+!! end
+
 # 'mi' is a localinterwiki prefix as well as a language
 !! test
 Serialize interwiki links pointing to the current wiki as plain wiki links (bug 65869)
@@ -24947,9 +25057,15 @@ parsoid=html2wt
 !! html/parsoid
 <a rel="mw:WikiLink" href="./Foo" title="Foo" data-parsoid='{}'>Foo</a>
 <a rel="mw:WikiLink" href="./Foo" title="Foo">Foo</a>
+<a href="//en.wikipedia.org/wiki/Foo">//en.wikipedia.org/wiki/Foo</a>
+<a href="http://en.wikipedia.org/wiki/Foo">http://en.wikipedia.org/wiki/Foo</a>
+<a href="//en.wikipedia.org/wiki/Foo_bar">//en.wikipedia.org/wiki/Foo bar</a>
 !! wikitext
 [[Foo]]
 [[Foo]]
+[[:en:Foo|//en.wikipedia.org/wiki/Foo]]
+http://en.wikipedia.org/wiki/Foo
+[[:en:Foo_bar|//en.wikipedia.org/wiki/Foo bar]]
 !! end
 
 !! test
@@ -25260,6 +25376,17 @@ parsoid=html2wt
 [[File:Foobar.jpg|link=]]
 !! end
 
+!! test
+Image: Invalid title as link
+!! wikitext
+[[File:Foobar.jpg|link=<]]
+!! html/php
+<p><a href="/wiki/File:Foobar.jpg" class="image" title="link=&lt;"><img alt="link=&lt;" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! html/parsoid
+<p><span class="mw-default-size" typeof="mw:Image" data-parsoid='{"optList":[{"ck":"link","ak":"link=&lt;"}]}' data-mw='{"caption":"link=&amp;lt;"}'><a href="./File:Foobar.jpg" data-parsoid='{"a":{"href":"./File:Foobar.jpg"},"sa":{}}'><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="220" width="1941" data-parsoid='{"a":{"resource":"./File:Foobar.jpg","height":"220","width":"1941"},"sa":{"resource":"File:Foobar.jpg"}}'/></a></span></p>
+!! end
+
 !! test
 Lists: Serialize correctly even when list content is wrapped in p-tags (like VE does)
 !! options
@@ -27065,6 +27192,21 @@ B <ref group="X" name="b" />
 </references>
 !! end
 
+!! test
+DOMDiff: Edits to content nested in elements with templated attributes should not be lost (T139388)
+!! options
+parsoid={
+  "modes": ["selser"],
+  "changes": [
+    [ "div:first-child", "text", "bar" ]
+  ]
+}
+!! wikitext
+<div style="{{1x|color:red;}}%">foo</div>
+!! wikitext/edited
+<div style="{{1x|color:red;}}%">bar</div>
+!! end
+
 !! test
 Empty LI (T49673)
 !! wikitext
@@ -27075,8 +27217,31 @@ Empty LI (T49673)
 !! html/php+tidy
 <ul>
 <li>a</li>
-<li class="mw-empty-li"></li>
-<li class="mw-empty-li"></li>
+<li class="mw-empty-elt"></li>
+<li class="mw-empty-elt"></li>
 <li>b</li>
 </ul>
 !! end
+
+!! test
+Thumbnail output
+!! wikitext
+[[File:Thumb.png|thumb]]
+!! html/php+tidy
+<div class="thumb tright">
+<div class="thumbinner" style="width:137px;"><a href="/wiki/File:Thumb.png" class="image"><img alt="Thumb.png" src="http://example.com/images/e/ea/Thumb.png" width="135" height="135" class="thumbimage" /></a>
+<div class="thumbcaption">
+<div class="magnify"><a href="/wiki/File:Thumb.png" class="internal" title="Enlarge"></a></div>
+</div>
+</div>
+</div>
+!! end
+
+!! test
+unclosed internal link XSS (T137264)
+!! wikitext
+[[#%3Cscript%3Ealert(1)%3C/script%3E|
+!! html
+<p>[[#&lt;script&gt;alert(1)&lt;/script&gt;|
+</p>
+!! end
diff --git a/tests/parser/parserTestsParserHook.php b/tests/parser/parserTestsParserHook.php
deleted file mode 100644 (file)
index 5bf50ea..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-/**
- * A basic extension that's used by the parser tests to test whether input and
- * arguments are passed to extensions properly.
- *
- * Copyright © 2005, 2006 Ævar Arnfjörð Bjarmason
- *
- * 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 Testing
- * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
- */
-
-class ParserTestParserHook {
-
-       static function setup( &$parser ) {
-               $parser->setHook( 'tag', [ __CLASS__, 'dumpHook' ] );
-               $parser->setHook( 'tåg', [ __CLASS__, 'dumpHook' ] );
-               $parser->setHook( 'statictag', [ __CLASS__, 'staticTagHook' ] );
-               return true;
-       }
-
-       static function dumpHook( $in, $argv ) {
-               return "<pre>\n" .
-                       var_export( $in, true ) . "\n" .
-                       var_export( $argv, true ) . "\n" .
-                       "</pre>";
-       }
-
-       static function staticTagHook( $in, $argv, $parser ) {
-               if ( !count( $argv ) ) {
-                       $parser->static_tag_buf = $in;
-                       return '';
-               } elseif ( count( $argv ) === 1 && isset( $argv['action'] )
-                       && $argv['action'] === 'flush' && $in === null
-               ) {
-                       // Clear the buffer, we probably don't need to
-                       if ( isset( $parser->static_tag_buf ) ) {
-                               $tmp = $parser->static_tag_buf;
-                       } else {
-                               $tmp = '';
-                       }
-                       $parser->static_tag_buf = null;
-                       return $tmp;
-               } else { // wtf?
-                       return
-                               "\nCall this extension as <statictag>string</statictag> or as" .
-                               " <statictag action=flush/>, not in any other way.\n" .
-                               "text: " . var_export( $in, true ) . "\n" .
-                               "argv: " . var_export( $argv, true ) . "\n";
-               }
-       }
-}
diff --git a/tests/parserTests.php b/tests/parserTests.php
deleted file mode 100644 (file)
index b3cb89a..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-/**
- * MediaWiki parser test suite
- *
- * Copyright © 2004 Brion Vibber <brion@pobox.com>
- * https://www.mediawiki.org/
- *
- * 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 Testing
- */
-
-define( 'MW_PARSER_TEST', true );
-
-$options = [ 'quick', 'color', 'quiet', 'help', 'show-output',
-       'record', 'run-disabled', 'run-parsoid' ];
-$optionsWithArgs = [ 'regex', 'filter', 'seed', 'setversion', 'file' ];
-
-require_once __DIR__ . '/../maintenance/commandLine.inc';
-require_once __DIR__ . '/TestsAutoLoader.php';
-
-if ( isset( $options['help'] ) ) {
-       echo <<<ENDS
-MediaWiki $wgVersion parser test suite
-Usage: php parserTests.php [options...]
-
-Options:
-  --quick          Suppress diff output of failed tests
-  --quiet          Suppress notification of passed tests (shows only failed tests)
-  --show-output    Show expected and actual output
-  --color[=yes|no] Override terminal detection and force color output on or off
-                   use wgCommandLineDarkBg = true; if your term is dark
-  --regex          Only run tests whose descriptions which match given regex
-  --filter         Alias for --regex
-  --file=<testfile> Run test cases from a custom file instead of parserTests.txt
-  --record         Record tests in database
-  --compare        Compare with recorded results, without updating the database.
-  --setversion     When using --record, set the version string to use (useful
-                   with git-svn so that you can get the exact revision)
-  --keep-uploads   Re-use the same upload directory for each test, don't delete it
-  --fuzz           Do a fuzz test instead of a normal test
-  --seed <n>       Start the fuzz test from the specified seed
-  --help           Show this help message
-  --run-disabled   run disabled tests
-  --run-parsoid    run parsoid tests (normally disabled)
-
-ENDS;
-       exit( 0 );
-}
-
-# Cases of weird db corruption were encountered when running tests on earlyish
-# versions of SQLite
-if ( $wgDBtype == 'sqlite' ) {
-       $db = wfGetDB( DB_MASTER );
-       $version = $db->getServerVersion();
-       if ( version_compare( $version, '3.6' ) < 0 ) {
-               die( "Parser tests require SQLite version 3.6 or later, you have $version\n" );
-       }
-}
-
-$tester = new ParserTest( $options );
-
-if ( isset( $options['file'] ) ) {
-       $files = [ $options['file'] ];
-} else {
-       // Default parser tests and any set from extensions or local config
-       $files = $wgParserTestFiles;
-}
-
-# Print out software version to assist with locating regressions
-$version = SpecialVersion::getVersion( 'nodb' );
-echo "This is MediaWiki version {$version}.\n\n";
-
-if ( isset( $options['fuzz'] ) ) {
-       $tester->fuzzTest( $files );
-} else {
-       $ok = $tester->runTestsFromFiles( $files );
-       exit( $ok ? 0 : 1 );
-}
index e1537bf..d34e183 100644 (file)
@@ -43,26 +43,17 @@ coverage:
 
 parser:
        ${PU} --group Parser
-parserfuzz:
-       @echo "******************************************************************"
-       @echo "* This WILL kill your computer by eating all memory AND all swap *"
-       @echo "*                                                                *"
-       @echo "* If you are on a production machine. ABORT NOW!!                *"
-       @echo "*  Press control+C to stop                                       *"
-       @echo "*                                                                *"
-       @echo "******************************************************************"
-       ${PU} --group Parser,ParserFuzz
 noparser:
-       ${PU} --exclude-group Parser,Broken,ParserFuzz,Stub
+       ${PU} --exclude-group Parser,Broken,Stub
 
 safe:
-       ${PU} --exclude-group Broken,ParserFuzz,Destructive,Stub
+       ${PU} --exclude-group Broken,Destructive,Stub
 
 databaseless:
-       ${PU} --exclude-group Broken,ParserFuzz,Destructive,Database,Stub
+       ${PU} --exclude-group Broken,Destructive,Database,Stub
 
 database:
-       ${PU} --exclude-group Broken,ParserFuzz,Destructive,Stub --group Database
+       ${PU} --exclude-group Broken,Destructive,Stub --group Database
 
 list-groups:
        ${PU} --list-groups
@@ -76,7 +67,7 @@ help:
        #   tap                 Run the tests individually through Test::Harness's prove(1)
        #   help                You're looking at it!
        #   coverage            Run the tests and generates an HTML code coverage report
-       #                       You will need the Xdebug PHP extension for the later.
+       #                       You will need the Xdebug PHP extension for the latter.
        #   [no]parser          Skip or only run Parser tests
        #
        #   list-groups         List available Tests groups.
index 8dfe628..920dbb3 100644 (file)
@@ -122,9 +122,8 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
        public static function setUpBeforeClass() {
                parent::setUpBeforeClass();
 
-               // NOTE: Usually, PHPUnitMaintClass::finalSetup already called this,
-               // but let's make doubly sure.
-               self::prepareServices( new GlobalVarConfig() );
+               // Get the service locator, and reset services if it's not done already
+               self::$serviceLocator = self::prepareServices( new GlobalVarConfig() );
        }
 
        /**
@@ -180,28 +179,26 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
         *
         * @param Config $bootstrapConfig The bootstrap config to use with the new
         *        MediaWikiServices. Only used for the first call to this method.
+        * @return MediaWikiServices
         */
        public static function prepareServices( Config $bootstrapConfig ) {
-               static $servicesPrepared = false;
+               static $services = null;
 
-               if ( $servicesPrepared ) {
-                       return;
-               } else {
-                       $servicesPrepared = true;
+               if ( !$services ) {
+                       $services = self::resetGlobalServices( $bootstrapConfig );
                }
-
-               self::resetGlobalServices( $bootstrapConfig );
+               return $services;
        }
 
        /**
         * Reset global services, and install testing environment.
         * This is the testing equivalent of MediaWikiServices::resetGlobalInstance().
         * This should only be used to set up the testing environment, not when
-        * running unit tests. Use overrideMwServices() for that.
+        * running unit tests. Use MediaWikiTestCase::overrideMwServices() for that.
         *
         * @see MediaWikiServices::resetGlobalInstance()
         * @see prepareServices()
-        * @see overrideMwServices()
+        * @see MediaWikiTestCase::overrideMwServices()
         *
         * @param Config|null $bootstrapConfig The bootstrap config to use with the new
         *        MediaWikiServices.
@@ -214,11 +211,12 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
 
                MediaWikiServices::resetGlobalInstance( $testConfig );
 
-               self::$serviceLocator = MediaWikiServices::getInstance();
+               $serviceLocator = MediaWikiServices::getInstance();
                self::installTestServices(
                        $oldConfigFactory,
-                       self::$serviceLocator
+                       $serviceLocator
                );
+               return $serviceLocator;
        }
 
        /**
@@ -256,6 +254,7 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
 
                $defaultOverrides->set( 'ObjectCaches', $objectCaches );
                $defaultOverrides->set( 'MainCacheType', CACHE_NONE );
+               $defaultOverrides->set( 'JobTypeConf', [ 'default' => [ 'class' => 'JobQueueMemory' ] ] );
 
                // Use a fast hash algorithm to hash passwords.
                $defaultOverrides->set( 'PasswordDefault', 'A' );
@@ -341,7 +340,6 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
 
                // TODO: move global state into MediaWikiServices
                RequestContext::resetMain();
-               MediaHandler::resetCache();
                if ( session_id() !== '' ) {
                        session_write_close();
                        session_id( '' );
@@ -478,11 +476,20 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                        while ( $this->db->trxLevel() > 0 ) {
                                $this->db->rollback( __METHOD__, 'flush' );
                        }
+                       // Check for unsafe queries
+                       if ( $this->db->getType() === 'mysql' ) {
+                               $this->db->query( "SET sql_mode = 'STRICT_ALL_TABLES'" );
+                       }
                }
 
                DeferredUpdates::clearPendingUpdates();
                ObjectCache::getMainWANInstance()->clearProcessCache();
 
+               // XXX: reset maintenance triggers
+               // Hook into period lag checks which often happen in long-running scripts
+               $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+               Maintenance::setLBFactoryTriggers( $lbFactory );
+
                ob_start( 'MediaWikiTestCase::wfResetOutputBuffersBarrier' );
        }
 
@@ -491,7 +498,7 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
        }
 
        protected function tearDown() {
-               global $wgRequest;
+               global $wgRequest, $wgSQLMode;
 
                $status = ob_get_status();
                if ( isset( $status['name'] ) &&
@@ -515,6 +522,9 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                        while ( $this->db->trxLevel() > 0 ) {
                                $this->db->rollback( __METHOD__, 'flush' );
                        }
+                       if ( $this->db->getType() === 'mysql' ) {
+                               $this->db->query( "SET sql_mode = " . $this->db->addQuotes( $wgSQLMode ) );
+                       }
                }
 
                // Restore mw globals
@@ -530,7 +540,6 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
 
                // TODO: move global state into MediaWikiServices
                RequestContext::resetMain();
-               MediaHandler::resetCache();
                if ( session_id() !== '' ) {
                        session_write_close();
                        session_id( '' );
@@ -1111,15 +1120,15 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
         * @throws MWException If the database table prefix is already $prefix
         */
        public static function setupTestDB( DatabaseBase $db, $prefix ) {
+               if ( self::$dbSetup ) {
+                       return;
+               }
+
                if ( $db->tablePrefix() === $prefix ) {
                        throw new MWException(
                                'Cannot run unit tests, the database prefix is already "' . $prefix . '"' );
                }
 
-               if ( self::$dbSetup ) {
-                       return;
-               }
-
                // TODO: the below should be re-written as soon as LBFactory, LoadBalancer,
                // and DatabaseBase no longer use global state.
 
@@ -1775,4 +1784,15 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                return $buffer;
        }
 
+       /**
+        * Create a temporary hook handler which will be reset by tearDown.
+        * This replaces other handlers for the same hook.
+        * @param string $hookName Hook name
+        * @param mixed $handler Value suitable for a hook handler
+        * @since 1.28
+        */
+       protected function setTemporaryHook( $hookName, $handler ) {
+               $this->mergeMwGlobalArrayValue( 'wgHooks', [ $hookName => [ $handler ] ] );
+       }
+
 }
index 85bf954..18a49f6 100644 (file)
@@ -1,5 +1,8 @@
 <?php
 
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
 abstract class ResourceLoaderTestCase extends MediaWikiTestCase {
        /**
         * @param string $lang
@@ -64,10 +67,13 @@ class ResourceLoaderTestModule extends ResourceLoaderModule {
        protected $dependencies = [];
        protected $group = null;
        protected $source = 'local';
+       protected $position = 'bottom';
        protected $script = '';
        protected $styles = '';
        protected $skipFunction = null;
        protected $isRaw = false;
+       protected $isKnownEmpty = false;
+       protected $type = ResourceLoaderModule::LOAD_GENERAL;
        protected $targets = [ 'phpunit' ];
 
        public function __construct( $options = [] ) {
@@ -99,6 +105,13 @@ class ResourceLoaderTestModule extends ResourceLoaderModule {
        public function getSource() {
                return $this->source;
        }
+       public function getPosition() {
+               return $this->position;
+       }
+
+       public function getType() {
+               return $this->type;
+       }
 
        public function getSkipFunction() {
                return $this->skipFunction;
@@ -107,6 +120,9 @@ class ResourceLoaderTestModule extends ResourceLoaderModule {
        public function isRaw() {
                return $this->isRaw;
        }
+       public function isKnownEmpty( ResourceLoaderContext $context ) {
+               return $this->isKnownEmpty;
+       }
 
        public function enableModuleContentVersion() {
                return true;
@@ -115,3 +131,13 @@ class ResourceLoaderTestModule extends ResourceLoaderModule {
 
 class ResourceLoaderFileModuleTestModule extends ResourceLoaderFileModule {
 }
+
+class EmptyResourceLoader extends ResourceLoader {
+       // TODO: This won't be needed once ResourceLoader is empty by default
+       // and default registrations are done from ServiceWiring instead.
+       public function __construct( Config $config = null, LoggerInterface $logger = null ) {
+               $this->setLogger( $logger ?: new NullLogger() );
+               $this->config = $config ?: ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
+               $this->setMessageBlobStore( new MessageBlobStore( $this, $this->getLogger() ) );
+       }
+}
diff --git a/tests/phpunit/data/templates/conds.mustache b/tests/phpunit/data/templates/conds.mustache
deleted file mode 100644 (file)
index 5ebd2ea..0000000
+++ /dev/null
@@ -1 +0,0 @@
-{{#list}}oh no{{/list}}{{#foo}}none of this should render{{/foo}}
\ No newline at end of file
index c259a51..5a01dc0 100644 (file)
@@ -348,6 +348,8 @@ class EditPageTest extends MediaWikiLangTestCase {
 
                wfGetDB( DB_MASTER )->commit( __METHOD__ );
 
+               $this->assertEquals( 0, DeferredUpdates::pendingUpdatesCount(), 'No deferred updates' );
+
                if ( $expectedCode != EditPage::AS_BLANK_ARTICLE ) {
                        $latest = $page->getLatest();
                        $page->doDeleteArticleReal( $pageTitle );
index a2bb97a..a5d3570 100644 (file)
@@ -30,14 +30,14 @@ class ExportTest extends MediaWikiLangTestCase {
 
                $title = Title::newFromText( $pageTitle );
 
-               ob_start();
+               $sink = new DumpStringOutput;
+               $exporter->setOutputSink( $sink );
                $exporter->openStream();
                $exporter->pageByTitle( $title );
                $exporter->closeStream();
-               $xmlString = ob_get_clean();
 
                // This throws error if invalid xml output
-               $xmlObject = simplexml_load_string( $xmlString );
+               $xmlObject = simplexml_load_string( $sink );
 
                /**
                 * Check namespaces match xml
index 4622a38..e3713ab 100644 (file)
@@ -1,8 +1,162 @@
 <?php
 
-class FauxRequestTest extends MediaWikiTestCase {
+class FauxRequestTest extends PHPUnit_Framework_TestCase {
+       /**
+        * @covers FauxRequest::__construct
+        */
+       public function testConstructInvalidData() {
+               $this->setExpectedException( MWException::class, 'bogus data' );
+               $req = new FauxRequest( 'x' );
+       }
+
+       /**
+        * @covers FauxRequest::__construct
+        */
+       public function testConstructInvalidSession() {
+               $this->setExpectedException( MWException::class, 'bogus session' );
+               $req = new FauxRequest( [], false, 'x' );
+       }
+
+       /**
+        * @covers FauxRequest::getText
+        */
+       public function testGetText() {
+               $req = new FauxRequest( [ 'x' => 'Value' ] );
+               $this->assertEquals( 'Value', $req->getText( 'x' ) );
+               $this->assertEquals( '', $req->getText( 'z' ) );
+       }
+
+       // Integration test for parent method.
+       public function testGetVal() {
+               $req = new FauxRequest( [ 'crlf' => "A\r\nb" ] );
+               $this->assertSame( "A\r\nb", $req->getVal( 'crlf' ), 'CRLF' );
+       }
+
+       // Integration test for parent method.
+       public function testGetRawVal() {
+               $req = new FauxRequest( [
+                       'x' => 'Value',
+                       'y' => [ 'a' ],
+                       'crlf' => "A\r\nb"
+               ] );
+               $this->assertSame( 'Value', $req->getRawVal( 'x' ) );
+               $this->assertSame( null, $req->getRawVal( 'z' ), 'Not found' );
+               $this->assertSame( null, $req->getRawVal( 'y' ), 'Array is ignored' );
+               $this->assertSame( "A\r\nb", $req->getRawVal( 'crlf' ), 'CRLF' );
+       }
+
+       /**
+        * @covers FauxRequest::getValues
+        */
+       public function testGetValues() {
+               $values = [ 'x' => 'Value', 'y' => '' ];
+               $req = new FauxRequest( $values );
+               $this->assertEquals( $values, $req->getValues() );
+       }
+
+       /**
+        * @covers FauxRequest::getQueryValues
+        */
+       public function testGetQueryValues() {
+               $values = [ 'x' => 'Value', 'y' => '' ];
+
+               $req = new FauxRequest( $values );
+               $this->assertEquals( $values, $req->getQueryValues() );
+               $req = new FauxRequest( $values, /*wasPosted*/ true );
+               $this->assertEquals( [], $req->getQueryValues() );
+       }
+
+       /**
+        * @covers FauxRequest::getMethod
+        */
+       public function testGetMethod() {
+               $req = new FauxRequest( [] );
+               $this->assertEquals( 'GET', $req->getMethod() );
+               $req = new FauxRequest( [], /*wasPosted*/ true );
+               $this->assertEquals( 'POST', $req->getMethod() );
+       }
+
+       /**
+        * @covers FauxRequest::wasPosted
+        */
+       public function testWasPosted() {
+               $req = new FauxRequest( [] );
+               $this->assertFalse( $req->wasPosted() );
+               $req = new FauxRequest( [], /*wasPosted*/ true );
+               $this->assertTrue( $req->wasPosted() );
+       }
+
+       /**
+        * @covers FauxRequest::getCookie
+        * @covers FauxRequest::setCookie
+        * @covers FauxRequest::setCookies
+        */
+       public function testCookies() {
+               $req = new FauxRequest();
+               $this->assertSame( null, $req->getCookie( 'z', '' ) );
+
+               $req->setCookie( 'x', 'Value', '' );
+               $this->assertEquals( 'Value', $req->getCookie( 'x', '' ) );
+
+               $req->setCookies( [ 'x' => 'One', 'y' => 'Two' ], '' );
+               $this->assertEquals( 'One', $req->getCookie( 'x', '' ) );
+               $this->assertEquals( 'Two', $req->getCookie( 'y', '' ) );
+       }
+
+       /**
+        * @covers FauxRequest::getCookie
+        * @covers FauxRequest::setCookie
+        * @covers FauxRequest::setCookies
+        */
+       public function testCookiesDefaultPrefix() {
+               global $wgCookiePrefix;
+               $oldPrefix = $wgCookiePrefix;
+               $wgCookiePrefix = '_';
+
+               $req = new FauxRequest();
+               $this->assertSame( null, $req->getCookie( 'z' ) );
+
+               $req->setCookie( 'x', 'Value' );
+               $this->assertEquals( 'Value', $req->getCookie( 'x' ) );
+
+               $wgCookiePrefix = $oldPrefix;
+       }
+
+       /**
+        * @covers FauxRequest::getRequestURL
+        */
+       public function testGetRequestURL() {
+               $req = new FauxRequest();
+               $this->setExpectedException( MWException::class );
+               $req->getRequestURL();
+       }
+
+       /**
+        * @covers FauxRequest::setRequestURL
+        * @covers FauxRequest::getRequestURL
+        */
+       public function testSetRequestURL() {
+               $req = new FauxRequest();
+               $req->setRequestURL( 'https://example.org' );
+               $this->assertEquals( 'https://example.org', $req->getRequestURL() );
+       }
+
+       /**
+        * @covers FauxRequest::__construct
+        * @covers FauxRequest::getProtocol
+        */
+       public function testProtocol() {
+               $req = new FauxRequest();
+               $this->assertEquals( 'http', $req->getProtocol() );
+               $req = new FauxRequest( [], false, null, 'http' );
+               $this->assertEquals( 'http', $req->getProtocol() );
+               $req = new FauxRequest( [], false, null, 'https' );
+               $this->assertEquals( 'https', $req->getProtocol() );
+       }
+
        /**
         * @covers FauxRequest::setHeader
+        * @covers FauxRequest::setHeaders
         * @covers FauxRequest::getHeader
         */
        public function testGetSetHeader() {
@@ -22,7 +176,7 @@ class FauxRequestTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers FauxRequest::getAllHeaders
+        * @covers FauxRequest::initHeaders
         */
        public function testGetAllHeaders() {
                $_SERVER['HTTP_TEST'] = 'Example';
@@ -33,19 +187,36 @@ class FauxRequestTest extends MediaWikiTestCase {
                        [],
                        $request->getAllHeaders()
                );
+
+               $this->assertEquals(
+                       false,
+                       $request->getHeader( 'test' )
+               );
        }
 
        /**
-        * @covers FauxRequest::getHeader
+        * @covers FauxRequest::__construct
+        * @covers FauxRequest::getSessionArray
         */
-       public function testGetHeader() {
-               $_SERVER['HTTP_TEST'] = 'Example';
+       public function testSessionData() {
+               $values = [ 'x' => 'Value', 'y' => '' ];
 
-               $request = new FauxRequest();
+               $req = new FauxRequest( [], false, /*session*/ $values );
+               $this->assertEquals( $values, $req->getSessionArray() );
 
-               $this->assertEquals(
-                       false,
-                       $request->getHeader( 'test' )
-               );
+               $req = new FauxRequest();
+               $this->assertSame( null, $req->getSessionArray() );
+       }
+
+       /**
+        * @covers FauxRequest::getRawQueryString
+        * @covers FauxRequest::getRawPostString
+        * @covers FauxRequest::getRawInput
+        */
+       public function testDummies() {
+               $req = new FauxRequest();
+               $this->assertEquals( '', $req->getRawQueryString() );
+               $this->assertEquals( '', $req->getRawPostString() );
+               $this->assertEquals( '', $req->getRawInput() );
        }
 }
index 9f2fdfe..1d48d08 100644 (file)
@@ -413,8 +413,8 @@ class GlobalTest extends MediaWikiTestCase {
                );
        }
 
-       /** array( shorthand, expected integer ) */
        public static function provideShorthand() {
+               // Syntax: [ shorthand, expected integer ]
                return [
                        # Null, empty ...
                        [ '', -1 ],
index 082fe22..c76666d 100644 (file)
@@ -97,7 +97,7 @@ class WfBCP47Test extends MediaWikiTestCase {
                         *  az-Arab-x-AZE-derbend
                         * AZE being private, it should be lower case, hence the test above
                         * should probably be:
-                        *  array( 'az-arab-x-aze-derbend', 'az-Arab-x-AZE-derbend' ),
+                        * [ 'az-arab-x-aze-derbend', 'az-Arab-x-AZE-derbend' ],
                         */
 
                        # Private use registry values:
index a61b328..9d9815b 100644 (file)
@@ -18,9 +18,6 @@ class WfThumbIsStandardTest extends MediaWikiTestCase {
                                [ 300, 225 ],
                                [ 800, 600 ],
                        ],
-                       'wgMediaHandlers' => [
-                               'unknown/unknown' => 'MockBitmapHandler',
-                       ],
                ] );
        }
 
@@ -95,6 +92,7 @@ class WfThumbIsStandardTest extends MediaWikiTestCase {
         * @dataProvider provideThumbParams
         */
        public function testIsStandard( $message, $expected, $params ) {
+               $this->setService( 'MediaHandlerFactory', new MockMediaHandlerFactory() );
                $this->assertSame(
                        $expected,
                        wfThumbIsStandard( new FakeDimensionFile( [ 2000, 1800 ] ), $params ),
index 1f1c9be..cbe2e2f 100644 (file)
@@ -80,12 +80,12 @@ class WfUrlencodeTest extends MediaWikiTestCase {
 
        /**
         * Format is either:
-        *   array( 'input', 'expected' );
+        *   [ 'input', 'expected' ];
         * Or:
-        *   array( 'input',
-        *       array( 'Apache', 'expected' ),
-        *       array( 'Microsoft-IIS/7', 'expected' ),
-        *    ),
+        *   [ 'input',
+        *       [ 'Apache', 'expected' ],
+        *       [ 'Microsoft-IIS/7', 'expected' ],
+        *   ],
         * If you want to add other HTTP server name, you will have to add a new
         * testing method much like the testEncodingUrlWith() method above.
         */
index 4721793..85c95e4 100644 (file)
@@ -271,7 +271,7 @@ class HtmlTest extends MediaWikiTestCase {
        /**
         * How do we handle duplicate keys in HTML attributes expansion?
         * We could pass a "class" the values: 'GREEN' and array( 'GREEN' => false )
-        * The later will take precedence.
+        * The latter will take precedence.
         *
         * Feature added by r96188
         * @covers Html::expandAttributes
@@ -738,6 +738,16 @@ class HtmlTest extends MediaWikiTestCase {
                                '1x.png 1x, 1_5x.png 1.5x, 2x.png 2x',
                                'pixel depth keys may omit a trailing "x"'
                        ],
+                       [
+                               [ '1'  => 'small.png', '1.5' => 'large.png', '2'  => 'large.png' ],
+                               'small.png 1x, large.png 1.5x',
+                               'omit larger duplicates'
+                       ],
+                       [
+                               [ '1'  => 'small.png', '2'  => 'large.png', '1.5' => 'large.png' ],
+                               'small.png 1x, large.png 1.5x',
+                               'omit larger duplicates in irregular order'
+                       ],
                ];
        }
 
index 4c2e02b..ae0b4be 100644 (file)
@@ -106,8 +106,8 @@ class HttpTest extends MediaWikiTestCase {
                        # (\S+) - host part is made of anything not whitespaces
                        // commented these out in order to remove @group Broken
                        // @todo are these valid tests? if so, fix Http::isValidURI so it can handle them
-                       // array( false, 'http://!"èèè¿¿¿~~\'', 'hostname is made of any non whitespace' ),
-                       // array( false, 'http://exam:ple.org/', 'hostname can not use colons!' ),
+                       // [ false, 'http://!"èèè¿¿¿~~\'', 'hostname is made of any non whitespace' ],
+                       // [ false, 'http://exam:ple.org/', 'hostname can not use colons!' ],
 
                        # (:[0-9]+)? - port number
                        [ true, 'http://example.org:80/' ],
@@ -212,11 +212,11 @@ class HttpTest extends MediaWikiTestCase {
                        [ 'CURLAUTH_DIGEST' ],
                        [ 'CURLAUTH_GSSNEGOTIATE' ],
                        [ 'CURLAUTH_NTLM' ],
-                       // array( 'CURLCLOSEPOLICY_CALLBACK' ), // removed in PHP 5.6.0
-                       // array( 'CURLCLOSEPOLICY_LEAST_RECENTLY_USED' ), // removed in PHP 5.6.0
-                       // array( 'CURLCLOSEPOLICY_LEAST_TRAFFIC' ), // removed in PHP 5.6.0
-                       // array( 'CURLCLOSEPOLICY_OLDEST' ), // removed in PHP 5.6.0
-                       // array( 'CURLCLOSEPOLICY_SLOWEST' ), // removed in PHP 5.6.0
+                       // [ 'CURLCLOSEPOLICY_CALLBACK' ], // removed in PHP 5.6.0
+                       // [ 'CURLCLOSEPOLICY_LEAST_RECENTLY_USED' ], // removed in PHP 5.6.0
+                       // [ 'CURLCLOSEPOLICY_LEAST_TRAFFIC' ], // removed in PHP 5.6.0
+                       // [ 'CURLCLOSEPOLICY_OLDEST' ], // removed in PHP 5.6.0
+                       // [ 'CURLCLOSEPOLICY_SLOWEST' ], // removed in PHP 5.6.0
                        [ 'CURLE_ABORTED_BY_CALLBACK' ],
                        [ 'CURLE_BAD_CALLING_ORDER' ],
                        [ 'CURLE_BAD_CONTENT_ENCODING' ],
@@ -268,7 +268,7 @@ class HttpTest extends MediaWikiTestCase {
                        [ 'CURLE_RECV_ERROR' ],
                        [ 'CURLE_SEND_ERROR' ],
                        [ 'CURLE_SHARE_IN_USE' ],
-                       // array( 'CURLE_SSH' ), // not present in HHVM 3.3.0-dev
+                       // [ 'CURLE_SSH' ], // not present in HHVM 3.3.0-dev
                        [ 'CURLE_SSL_CACERT' ],
                        [ 'CURLE_SSL_CERTPROBLEM' ],
                        [ 'CURLE_SSL_CIPHER' ],
@@ -286,14 +286,14 @@ class HttpTest extends MediaWikiTestCase {
                        [ 'CURLFTPAUTH_DEFAULT' ],
                        [ 'CURLFTPAUTH_SSL' ],
                        [ 'CURLFTPAUTH_TLS' ],
-                       // array( 'CURLFTPMETHOD_MULTICWD' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLFTPMETHOD_NOCWD' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLFTPMETHOD_SINGLECWD' ), // not present in HHVM 3.3.0-dev
+                       // [ 'CURLFTPMETHOD_MULTICWD' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLFTPMETHOD_NOCWD' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLFTPMETHOD_SINGLECWD' ], // not present in HHVM 3.3.0-dev
                        [ 'CURLFTPSSL_ALL' ],
                        [ 'CURLFTPSSL_CONTROL' ],
                        [ 'CURLFTPSSL_NONE' ],
                        [ 'CURLFTPSSL_TRY' ],
-                       // array( 'CURLINFO_CERTINFO' ), // not present in HHVM 3.3.0-dev
+                       // [ 'CURLINFO_CERTINFO' ], // not present in HHVM 3.3.0-dev
                        [ 'CURLINFO_CONNECT_TIME' ],
                        [ 'CURLINFO_CONTENT_LENGTH_DOWNLOAD' ],
                        [ 'CURLINFO_CONTENT_LENGTH_UPLOAD' ],
@@ -308,7 +308,7 @@ class HttpTest extends MediaWikiTestCase {
                        [ 'CURLINFO_PRIVATE' ],
                        [ 'CURLINFO_REDIRECT_COUNT' ],
                        [ 'CURLINFO_REDIRECT_TIME' ],
-                       // array( 'CURLINFO_REDIRECT_URL' ), // not present in HHVM 3.3.0-dev
+                       // [ 'CURLINFO_REDIRECT_URL' ], // not present in HHVM 3.3.0-dev
                        [ 'CURLINFO_REQUEST_SIZE' ],
                        [ 'CURLINFO_SIZE_DOWNLOAD' ],
                        [ 'CURLINFO_SIZE_UPLOAD' ],
@@ -329,8 +329,8 @@ class HttpTest extends MediaWikiTestCase {
                        [ 'CURLOPT_BUFFERSIZE' ],
                        [ 'CURLOPT_CAINFO' ],
                        [ 'CURLOPT_CAPATH' ],
-                       // array( 'CURLOPT_CERTINFO' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLOPT_CLOSEPOLICY' ), // removed in PHP 5.6.0
+                       // [ 'CURLOPT_CERTINFO' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLOPT_CLOSEPOLICY' ], // removed in PHP 5.6.0
                        [ 'CURLOPT_CONNECTTIMEOUT' ],
                        [ 'CURLOPT_CONNECTTIMEOUT_MS' ],
                        [ 'CURLOPT_COOKIE' ],
@@ -354,8 +354,8 @@ class HttpTest extends MediaWikiTestCase {
                        [ 'CURLOPT_FTPPORT' ],
                        [ 'CURLOPT_FTPSSLAUTH' ],
                        [ 'CURLOPT_FTP_CREATE_MISSING_DIRS' ],
-                       // array( 'CURLOPT_FTP_FILEMETHOD' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLOPT_FTP_SKIP_PASV_IP' ), // not present in HHVM 3.3.0-dev
+                       // [ 'CURLOPT_FTP_FILEMETHOD' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLOPT_FTP_SKIP_PASV_IP' ], // not present in HHVM 3.3.0-dev
                        [ 'CURLOPT_FTP_SSL' ],
                        [ 'CURLOPT_FTP_USE_EPRT' ],
                        [ 'CURLOPT_FTP_USE_EPSV' ],
@@ -371,14 +371,14 @@ class HttpTest extends MediaWikiTestCase {
                        [ 'CURLOPT_INFILESIZE' ],
                        [ 'CURLOPT_INTERFACE' ],
                        [ 'CURLOPT_IPRESOLVE' ],
-                       // array( 'CURLOPT_KEYPASSWD' ), // not present in HHVM 3.3.0-dev
+                       // [ 'CURLOPT_KEYPASSWD' ], // not present in HHVM 3.3.0-dev
                        [ 'CURLOPT_KRB4LEVEL' ],
                        [ 'CURLOPT_LOW_SPEED_LIMIT' ],
                        [ 'CURLOPT_LOW_SPEED_TIME' ],
                        [ 'CURLOPT_MAXCONNECTS' ],
                        [ 'CURLOPT_MAXREDIRS' ],
-                       // array( 'CURLOPT_MAX_RECV_SPEED_LARGE' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLOPT_MAX_SEND_SPEED_LARGE' ), // not present in HHVM 3.3.0-dev
+                       // [ 'CURLOPT_MAX_RECV_SPEED_LARGE' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLOPT_MAX_SEND_SPEED_LARGE' ], // not present in HHVM 3.3.0-dev
                        [ 'CURLOPT_NETRC' ],
                        [ 'CURLOPT_NOBODY' ],
                        [ 'CURLOPT_NOPROGRESS' ],
@@ -390,7 +390,7 @@ class HttpTest extends MediaWikiTestCase {
                        [ 'CURLOPT_POSTREDIR' ],
                        [ 'CURLOPT_PRIVATE' ],
                        [ 'CURLOPT_PROGRESSFUNCTION' ],
-                       // array( 'CURLOPT_PROTOCOLS' ), // not present in HHVM 3.3.0-dev
+                       // [ 'CURLOPT_PROTOCOLS' ], // not present in HHVM 3.3.0-dev
                        [ 'CURLOPT_PROXY' ],
                        [ 'CURLOPT_PROXYAUTH' ],
                        [ 'CURLOPT_PROXYPORT' ],
@@ -402,14 +402,14 @@ class HttpTest extends MediaWikiTestCase {
                        [ 'CURLOPT_RANGE' ],
                        [ 'CURLOPT_READDATA' ],
                        [ 'CURLOPT_READFUNCTION' ],
-                       // array( 'CURLOPT_REDIR_PROTOCOLS' ), // not present in HHVM 3.3.0-dev
+                       // [ 'CURLOPT_REDIR_PROTOCOLS' ], // not present in HHVM 3.3.0-dev
                        [ 'CURLOPT_REFERER' ],
                        [ 'CURLOPT_RESUME_FROM' ],
                        [ 'CURLOPT_RETURNTRANSFER' ],
-                       // array( 'CURLOPT_SSH_AUTH_TYPES' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLOPT_SSH_HOST_PUBLIC_KEY_MD5' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLOPT_SSH_PRIVATE_KEYFILE' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLOPT_SSH_PUBLIC_KEYFILE' ), // not present in HHVM 3.3.0-dev
+                       // [ 'CURLOPT_SSH_AUTH_TYPES' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLOPT_SSH_HOST_PUBLIC_KEY_MD5' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLOPT_SSH_PRIVATE_KEYFILE' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLOPT_SSH_PUBLIC_KEYFILE' ], // not present in HHVM 3.3.0-dev
                        [ 'CURLOPT_SSLCERT' ],
                        [ 'CURLOPT_SSLCERTPASSWD' ],
                        [ 'CURLOPT_SSLCERTTYPE' ],
@@ -437,28 +437,28 @@ class HttpTest extends MediaWikiTestCase {
                        [ 'CURLOPT_VERBOSE' ],
                        [ 'CURLOPT_WRITEFUNCTION' ],
                        [ 'CURLOPT_WRITEHEADER' ],
-                       // array( 'CURLPROTO_ALL' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLPROTO_DICT' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLPROTO_FILE' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLPROTO_FTP' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLPROTO_FTPS' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLPROTO_HTTP' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLPROTO_HTTPS' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLPROTO_LDAP' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLPROTO_LDAPS' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLPROTO_SCP' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLPROTO_SFTP' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLPROTO_TELNET' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLPROTO_TFTP' ), // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROTO_ALL' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROTO_DICT' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROTO_FILE' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROTO_FTP' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROTO_FTPS' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROTO_HTTP' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROTO_HTTPS' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROTO_LDAP' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROTO_LDAPS' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROTO_SCP' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROTO_SFTP' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROTO_TELNET' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROTO_TFTP' ], // not present in HHVM 3.3.0-dev
                        [ 'CURLPROXY_HTTP' ],
-                       // array( 'CURLPROXY_SOCKS4' ), // not present in HHVM 3.3.0-dev
+                       // [ 'CURLPROXY_SOCKS4' ], // not present in HHVM 3.3.0-dev
                        [ 'CURLPROXY_SOCKS5' ],
-                       // array( 'CURLSSH_AUTH_DEFAULT' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLSSH_AUTH_HOST' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLSSH_AUTH_KEYBOARD' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLSSH_AUTH_NONE' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLSSH_AUTH_PASSWORD' ), // not present in HHVM 3.3.0-dev
-                       // array( 'CURLSSH_AUTH_PUBLICKEY' ), // not present in HHVM 3.3.0-dev
+                       // [ 'CURLSSH_AUTH_DEFAULT' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLSSH_AUTH_HOST' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLSSH_AUTH_KEYBOARD' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLSSH_AUTH_NONE' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLSSH_AUTH_PASSWORD' ], // not present in HHVM 3.3.0-dev
+                       // [ 'CURLSSH_AUTH_PUBLICKEY' ], // not present in HHVM 3.3.0-dev
                        [ 'CURLVERSION_NOW' ],
                        [ 'CURL_HTTP_VERSION_1_0' ],
                        [ 'CURL_HTTP_VERSION_1_1' ],
index b7f7880..3edf99f 100644 (file)
@@ -37,34 +37,34 @@ class LinkerTest extends MediaWikiLangTestCase {
                        [
                                '<a href="/wiki/Special:Contributions/JohnDoe" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/JohnDoe">JohnDoe</a>',
+                                       . 'title="Special:Contributions/JohnDoe"><bdi>JohnDoe</bdi></a>',
                                0, 'JohnDoe', false,
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/::1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/::1">::1</a>',
+                                       . 'title="Special:Contributions/::1"><bdi>::1</bdi></a>',
                                0, '::1', false,
                                'Anonymous with pretty IPv6'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/0:0:0:0:0:0:0:1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/0:0:0:0:0:0:0:1">::1</a>',
+                                       . 'title="Special:Contributions/0:0:0:0:0:0:0:1"><bdi>::1</bdi></a>',
                                0, '0:0:0:0:0:0:0:1', false,
                                'Anonymous with almost pretty IPv6'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/0000:0000:0000:0000:0000:0000:0000:0001" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/0000:0000:0000:0000:0000:0000:0000:0001">::1</a>',
+                                       . 'title="Special:Contributions/0000:0000:0000:0000:0000:0000:0000:0001"><bdi>::1</bdi></a>',
                                0, '0000:0000:0000:0000:0000:0000:0000:0001', false,
                                'Anonymous with full IPv6'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/::1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/::1">AlternativeUsername</a>',
+                                       . 'title="Special:Contributions/::1"><bdi>AlternativeUsername</bdi></a>',
                                0, '::1', 'AlternativeUsername',
                                'Anonymous with pretty IPv6 and an alternative username'
                        ],
@@ -73,14 +73,14 @@ class LinkerTest extends MediaWikiLangTestCase {
                        [
                                '<a href="/wiki/Special:Contributions/127.0.0.1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/127.0.0.1">127.0.0.1</a>',
+                                       . 'title="Special:Contributions/127.0.0.1"><bdi>127.0.0.1</bdi></a>',
                                0, '127.0.0.1', false,
                                'Anonymous with IPv4'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/127.0.0.1" '
                                        . 'class="mw-userlink mw-anonuserlink" '
-                                       . 'title="Special:Contributions/127.0.0.1">AlternativeUsername</a>',
+                                       . 'title="Special:Contributions/127.0.0.1"><bdi>AlternativeUsername</bdi></a>',
                                0, '127.0.0.1', 'AlternativeUsername',
                                'Anonymous with IPv4 and an alternative username'
                        ],
@@ -429,6 +429,7 @@ class LinkerTest extends MediaWikiLangTestCase {
         * @covers Linker::getLinkColour
         */
        public function testGetLinkColour() {
+               $this->hideDeprecated( 'Linker::getLinkColour' );
                $linkCache = MediaWikiServices::getInstance()->getLinkCache();
                $foobarTitle = Title::makeTitle( NS_MAIN, 'FooBar' );
                $redirectTitle = Title::makeTitle( NS_MAIN, 'Redirect' );
index ca01aef..24db445 100644 (file)
@@ -387,7 +387,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
                $wgContentNamespaces = 5;
                $this->assertEquals( [ NS_MAIN ], MWNamespace::getContentNamespaces() );
 
-               # test $wgContentNamespaces === array()
+               # test $wgContentNamespaces === []
                $wgContentNamespaces = [];
                $this->assertEquals( [ NS_MAIN ], MWNamespace::getContentNamespaces() );
 
@@ -474,7 +474,7 @@ class MWNamespaceTest extends MediaWikiTestCase {
         * global $wgCapitalLink setting to have extended coverage.
         *
         * MWNamespace::isCapitalized() rely on two global settings:
-        *   $wgCapitalLinkOverrides = array(); by default
+        *   $wgCapitalLinkOverrides = []; by default
         *   $wgCapitalLinks = true; by default
         * This function test $wgCapitalLinks
         *
index bca3982..70e4aea 100644 (file)
@@ -102,7 +102,7 @@ class MWTimestampTest extends MediaWikiLangTestCase {
 
        /**
         * Returns a list of valid timestamps in the format:
-        * array( type, timestamp_of_type, timestamp_in_MW )
+        * [ type, timestamp_of_type, timestamp_in_MW ]
         */
        public static function provideValidTimestamps() {
                return [
@@ -124,7 +124,7 @@ class MWTimestampTest extends MediaWikiLangTestCase {
 
        /**
         * Returns a list of out of range timestamps in the format:
-        * array( type, timestamp_of_type )
+        * [ type, timestamp_of_type ]
         */
        public static function provideOutOfRangeTimestamps() {
                return [
index d20344d..8c2b143 100644 (file)
@@ -316,6 +316,7 @@ class MediaWikiServicesTest extends MediaWikiTestCase {
                        'DBLoadBalancer' => [ 'DBLoadBalancer', 'LoadBalancer' ],
                        'WatchedItemStore' => [ 'WatchedItemStore', WatchedItemStore::class ],
                        'WatchedItemQueryService' => [ 'WatchedItemQueryService', WatchedItemQueryService::class ],
+                       'MediaHandlerFactory' => [ 'MediaHandlerFactory', MediaHandlerFactory::class ],
                        'GenderCache' => [ 'GenderCache', GenderCache::class ],
                        'LinkCache' => [ 'LinkCache', LinkCache::class ],
                        'LinkRenderer' => [ 'LinkRenderer', LinkRenderer::class ],
@@ -323,6 +324,7 @@ class MediaWikiServicesTest extends MediaWikiTestCase {
                        '_MediaWikiTitleCodec' => [ '_MediaWikiTitleCodec', MediaWikiTitleCodec::class ],
                        'TitleFormatter' => [ 'TitleFormatter', TitleFormatter::class ],
                        'TitleParser' => [ 'TitleParser', TitleParser::class ],
+                       'VirtualRESTServiceClient' => [ 'VirtualRESTServiceClient', VirtualRESTServiceClient::class ]
                ];
        }
 
index 9934749..c637d34 100644 (file)
@@ -139,72 +139,35 @@ class OutputPageTest extends MediaWikiTestCase {
        public static function provideMakeResourceLoaderLink() {
                // @codingStandardsIgnoreStart Generic.Files.LineLength
                return [
-                       // Load module script only
+                       // Single only=scripts load
                        [
                                [ 'test.foo', ResourceLoaderModule::TYPE_SCRIPTS ],
                                "<script>(window.RLQ=window.RLQ||[]).push(function(){"
                                        . 'mw.loader.load("http://127.0.0.1:8080/w/load.php?debug=false\u0026lang=en\u0026modules=test.foo\u0026only=scripts\u0026skin=fallback");'
                                        . "});</script>"
                        ],
-                       [
-                               // Don't condition wrap raw modules (like the startup module)
-                               [ 'test.raw', ResourceLoaderModule::TYPE_SCRIPTS ],
-                               '<script async="" src="http://127.0.0.1:8080/w/load.php?debug=false&amp;lang=en&amp;modules=test.raw&amp;only=scripts&amp;skin=fallback"></script>'
-                       ],
-                       // Load module styles only
-                       // This also tests the order the modules are put into the url
+                       // Multiple only=styles load
                        [
                                [ [ 'test.baz', 'test.foo', 'test.bar' ], ResourceLoaderModule::TYPE_STYLES ],
 
                                '<link rel="stylesheet" href="http://127.0.0.1:8080/w/load.php?debug=false&amp;lang=en&amp;modules=test.bar%2Cbaz%2Cfoo&amp;only=styles&amp;skin=fallback"/>'
                        ],
-                       // Load private module (only=scripts)
+                       // Private embed (only=scripts)
                        [
                                [ 'test.quux', ResourceLoaderModule::TYPE_SCRIPTS ],
                                "<script>(window.RLQ=window.RLQ||[]).push(function(){"
                                        . "mw.test.baz({token:123});mw.loader.state({\"test.quux\":\"ready\"});"
                                        . "});</script>"
                        ],
-                       // Load private module (combined)
-                       [
-                               [ 'test.quux', ResourceLoaderModule::TYPE_COMBINED ],
-                               "<script>(window.RLQ=window.RLQ||[]).push(function(){"
-                                       . "mw.loader.implement(\"test.quux\",function($,jQuery,require,module){"
-                                       . "mw.test.baz({token:123});},{\"css\":[\".mw-icon{transition:none}"
-                                       . "\"]});});</script>"
-                       ],
-                       // Load no modules
-                       [
-                               [ [], ResourceLoaderModule::TYPE_COMBINED ],
-                               '',
-                       ],
-                       // noscript group
-                       [
-                               [ 'test.noscript', ResourceLoaderModule::TYPE_STYLES ],
-                               '<noscript><link rel="stylesheet" href="http://127.0.0.1:8080/w/load.php?debug=false&amp;lang=en&amp;modules=test.noscript&amp;only=styles&amp;skin=fallback"/></noscript>'
-                       ],
-                       // Load two modules in separate groups
-                       [
-                               [ [ 'test.group.foo', 'test.group.bar' ], ResourceLoaderModule::TYPE_COMBINED ],
-                               "<script>(window.RLQ=window.RLQ||[]).push(function(){"
-                                       . 'mw.loader.load("http://127.0.0.1:8080/w/load.php?debug=false\u0026lang=en\u0026modules=test.group.bar\u0026skin=fallback");'
-                                       . "});</script>\n"
-                                       . "<script>(window.RLQ=window.RLQ||[]).push(function(){"
-                                       . 'mw.loader.load("http://127.0.0.1:8080/w/load.php?debug=false\u0026lang=en\u0026modules=test.group.foo\u0026skin=fallback");'
-                                       . "});</script>"
-                       ],
                ];
                // @codingStandardsIgnoreEnd
        }
 
        /**
+        * See ResourceLoaderClientHtmlTest for full coverage.
+        *
         * @dataProvider provideMakeResourceLoaderLink
         * @covers OutputPage::makeResourceLoaderLink
-        * @covers ResourceLoader::makeLoaderImplementScript
-        * @covers ResourceLoader::makeModuleResponse
-        * @covers ResourceLoader::makeInlineScript
-        * @covers ResourceLoader::makeLoaderStateScript
-        * @covers ResourceLoader::createLoaderURL
         */
        public function testMakeResourceLoaderLink( $args, $expectedHtml ) {
                $this->setMwGlobals( [
@@ -238,25 +201,9 @@ class OutputPageTest extends MediaWikiTestCase {
                                'styles' => '/* pref-animate=off */ .mw-icon { transition: none; }',
                                'group' => 'private',
                        ] ),
-                       'test.raw' => new ResourceLoaderTestModule( [
-                               'script' => 'mw.test.baz( { token: 123 } );',
-                               'isRaw' => true,
-                       ] ),
-                       'test.noscript' => new ResourceLoaderTestModule( [
-                               'styles' => '.mw-test-noscript { content: "style"; }',
-                               'group' => 'noscript',
-                       ] ),
-                       'test.group.bar' => new ResourceLoaderTestModule( [
-                               'styles' => '.mw-group-bar { content: "style"; }',
-                               'group' => 'bar',
-                       ] ),
-                       'test.group.foo' => new ResourceLoaderTestModule( [
-                               'styles' => '.mw-group-foo { content: "style"; }',
-                               'group' => 'foo',
-                       ] ),
                ] );
                $links = $method->invokeArgs( $out, $args );
-               $actualHtml = implode( "\n", $links['html'] );
+               $actualHtml = strval( $links );
                $this->assertEquals( $expectedHtml, $actualHtml );
        }
 
index 72d7166..26529e8 100644 (file)
@@ -248,7 +248,7 @@ class SanitizerTest extends MediaWikiTestCase {
        }
 
        public static function provideDeprecatedAttributes() {
-               /** array( <attribute>, <element>, [message] ) */
+               /** [ <attribute>, <element>, [message] ] */
                return [
                        [ 'clear="left"', 'br' ],
                        [ 'clear="all"', 'br' ],
@@ -276,7 +276,7 @@ class SanitizerTest extends MediaWikiTestCase {
        }
 
        public static function provideCssCommentsFixtures() {
-               /** array( <expected>, <css>, [message] ) */
+               /** [ <expected>, <css>, [message] ] */
                return [
                        // Valid comments spanning entire input
                        [ '/**/', '/**/' ],
@@ -353,7 +353,7 @@ class SanitizerTest extends MediaWikiTestCase {
        }
 
        public static function provideEscapeIdReferenceList() {
-               /** array( <reference list>, <individual id 1>, <individual id 2> ) */
+               /** [ <reference list>, <individual id 1>, <individual id 2> ] */
                return [
                        [ 'foo bar', 'foo', 'bar' ],
                        [ '#1 #2', '#1', '#2' ],
index 7850f24..7925c6f 100644 (file)
@@ -90,6 +90,8 @@ class TitleTest extends MediaWikiTestCase {
                        [ 'A < B', 'title-invalid-characters' ],
                        [ 'A > B', 'title-invalid-characters' ],
                        [ 'A | B', 'title-invalid-characters' ],
+                       [ "A \t B", 'title-invalid-characters' ],
+                       [ "A \n B", 'title-invalid-characters' ],
                        // URL encoding
                        [ 'A%20B', 'title-invalid-characters' ],
                        [ 'A%23B', 'title-invalid-characters' ],
index be22260..65a8c86 100644 (file)
@@ -1,4 +1,5 @@
 <?php
+use MediaWiki\MediaWikiServices;
 
 /**
  * @author Addshore
@@ -18,7 +19,6 @@ class WatchedItemIntegrationTest extends MediaWikiTestCase {
                $this->hideDeprecated( 'WatchedItem::addWatch' );
                $this->hideDeprecated( 'WatchedItem::removeWatch' );
                $this->hideDeprecated( 'WatchedItem::isWatched' );
-               $this->hideDeprecated( 'WatchedItem::resetNotificationTimestamp' );
                $this->hideDeprecated( 'WatchedItem::duplicateEntries' );
                $this->hideDeprecated( 'WatchedItem::batchAddWatch' );
        }
@@ -63,7 +63,9 @@ class WatchedItemIntegrationTest extends MediaWikiTestCase {
                        WatchedItem::fromUserTitle( $user, $title )->getNotificationTimestamp()
                );
 
-               WatchedItem::fromUserTitle( $user, $title )->resetNotificationTimestamp();
+               MediaWikiServices::getInstance()->getWatchedItemStore()->resetNotificationTimestamp(
+                       $user, $title
+               );
                $this->assertNull( WatchedItem::fromUserTitle( $user, $title )->getNotificationTimestamp() );
        }
 
@@ -107,7 +109,9 @@ class WatchedItemIntegrationTest extends MediaWikiTestCase {
                $user = $this->getUser();
                $title = Title::newFromText( 'WatchedItemIntegrationTestPage' );
                WatchedItem::fromUserTitle( $user, $title )->addWatch();
-               WatchedItem::fromUserTitle( $user, $title )->resetNotificationTimestamp();
+               MediaWikiServices::getInstance()->getWatchedItemStore()->resetNotificationTimestamp(
+                       $user, $title
+               );
 
                $this->assertEquals(
                        null,
index b63a1f4..1d232fe 100644 (file)
@@ -141,6 +141,14 @@ class WatchedItemQueryServiceUnitTest extends PHPUnit_Framework_TestCase {
                return $mock;
        }
 
+       private function getMockAnonUser() {
+               $mock = $this->getMock( User::class );
+               $mock->expects( $this->any() )
+                       ->method( 'isAnon' )
+                       ->will( $this->returnValue( true ) );
+               return $mock;
+       }
+
        private function getFakeRow( array $rowValues ) {
                $fakeRow = new stdClass();
                foreach ( $rowValues as $valueName => $value ) {
@@ -1010,4 +1018,295 @@ class WatchedItemQueryServiceUnitTest extends PHPUnit_Framework_TestCase {
                );
        }
 
+       public function testGetWatchedItemsForUser() {
+               $mockDb = $this->getMockDb();
+               $mockDb->expects( $this->once() )
+                       ->method( 'select' )
+                       ->with(
+                               'watchlist',
+                               [ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
+                               [ 'wl_user' => 1 ]
+                       )
+                       ->will( $this->returnValue( [
+                               $this->getFakeRow( [
+                                       'wl_namespace' => 0,
+                                       'wl_title' => 'Foo1',
+                                       'wl_notificationtimestamp' => '20151212010101',
+                               ] ),
+                               $this->getFakeRow( [
+                                       'wl_namespace' => 1,
+                                       'wl_title' => 'Foo2',
+                                       'wl_notificationtimestamp' => null,
+                               ] ),
+                       ] ) );
+
+               $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
+               $user = $this->getMockNonAnonUserWithId( 1 );
+
+               $items = $queryService->getWatchedItemsForUser( $user );
+
+               $this->assertInternalType( 'array', $items );
+               $this->assertCount( 2, $items );
+               $this->assertContainsOnlyInstancesOf( WatchedItem::class, $items );
+               $this->assertEquals(
+                       new WatchedItem( $user, new TitleValue( 0, 'Foo1' ), '20151212010101' ),
+                       $items[0]
+               );
+               $this->assertEquals(
+                       new WatchedItem( $user, new TitleValue( 1, 'Foo2' ), null ),
+                       $items[1]
+               );
+       }
+
+       public function provideGetWatchedItemsForUserOptions() {
+               return [
+                       [
+                               [ 'namespaceIds' => [ 0, 1 ], ],
+                               [ 'wl_namespace' => [ 0, 1 ], ],
+                               []
+                       ],
+                       [
+                               [ 'sort' => WatchedItemQueryService::SORT_ASC, ],
+                               [],
+                               [ 'ORDER BY' => [ 'wl_namespace ASC', 'wl_title ASC' ] ]
+                       ],
+                       [
+                               [
+                                       'namespaceIds' => [ 0 ],
+                                       'sort' => WatchedItemQueryService::SORT_ASC,
+                               ],
+                               [ 'wl_namespace' => [ 0 ], ],
+                               [ 'ORDER BY' => 'wl_title ASC' ]
+                       ],
+                       [
+                               [ 'limit' => 10 ],
+                               [],
+                               [ 'LIMIT' => 10 ]
+                       ],
+                       [
+                               [
+                                       'namespaceIds' => [ 0, "1; DROP TABLE watchlist;\n--" ],
+                                       'limit' => "10; DROP TABLE watchlist;\n--",
+                               ],
+                               [ 'wl_namespace' => [ 0, 1 ], ],
+                               [ 'LIMIT' => 10 ]
+                       ],
+                       [
+                               [ 'filter' => WatchedItemQueryService::FILTER_CHANGED ],
+                               [ 'wl_notificationtimestamp IS NOT NULL' ],
+                               []
+                       ],
+                       [
+                               [ 'filter' => WatchedItemQueryService::FILTER_NOT_CHANGED ],
+                               [ 'wl_notificationtimestamp IS NULL' ],
+                               []
+                       ],
+                       [
+                               [ 'sort' => WatchedItemQueryService::SORT_DESC, ],
+                               [],
+                               [ 'ORDER BY' => [ 'wl_namespace DESC', 'wl_title DESC' ] ]
+                       ],
+                       [
+                               [
+                                       'namespaceIds' => [ 0 ],
+                                       'sort' => WatchedItemQueryService::SORT_DESC,
+                               ],
+                               [ 'wl_namespace' => [ 0 ], ],
+                               [ 'ORDER BY' => 'wl_title DESC' ]
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideGetWatchedItemsForUserOptions
+        */
+       public function testGetWatchedItemsForUser_optionsAndEmptyResult(
+               array $options,
+               array $expectedConds,
+               array $expectedDbOptions
+       ) {
+               $mockDb = $this->getMockDb();
+               $user = $this->getMockNonAnonUserWithId( 1 );
+
+               $expectedConds = array_merge( [ 'wl_user' => 1 ], $expectedConds );
+               $mockDb->expects( $this->once() )
+                       ->method( 'select' )
+                       ->with(
+                               'watchlist',
+                               [ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
+                               $expectedConds,
+                               $this->isType( 'string' ),
+                               $expectedDbOptions
+                       )
+                       ->will( $this->returnValue( [] ) );
+
+               $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
+
+               $items = $queryService->getWatchedItemsForUser( $user, $options );
+               $this->assertEmpty( $items );
+       }
+
+       public function provideGetWatchedItemsForUser_fromUntilStartFromOptions() {
+               return [
+                       [
+                               [
+                                       'from' => new TitleValue( 0, 'SomeDbKey' ),
+                                       'sort' => WatchedItemQueryService::SORT_ASC
+                               ],
+                               [ "(wl_namespace > 0) OR ((wl_namespace = 0) AND (wl_title >= 'SomeDbKey'))", ],
+                               [ 'ORDER BY' => [ 'wl_namespace ASC', 'wl_title ASC' ] ]
+                       ],
+                       [
+                               [
+                                       'from' => new TitleValue( 0, 'SomeDbKey' ),
+                                       'sort' => WatchedItemQueryService::SORT_DESC,
+                               ],
+                               [ "(wl_namespace < 0) OR ((wl_namespace = 0) AND (wl_title <= 'SomeDbKey'))", ],
+                               [ 'ORDER BY' => [ 'wl_namespace DESC', 'wl_title DESC' ] ]
+                       ],
+                       [
+                               [
+                                       'until' => new TitleValue( 0, 'SomeDbKey' ),
+                                       'sort' => WatchedItemQueryService::SORT_ASC
+                               ],
+                               [ "(wl_namespace < 0) OR ((wl_namespace = 0) AND (wl_title <= 'SomeDbKey'))", ],
+                               [ 'ORDER BY' => [ 'wl_namespace ASC', 'wl_title ASC' ] ]
+                       ],
+                       [
+                               [
+                                       'until' => new TitleValue( 0, 'SomeDbKey' ),
+                                       'sort' => WatchedItemQueryService::SORT_DESC
+                               ],
+                               [ "(wl_namespace > 0) OR ((wl_namespace = 0) AND (wl_title >= 'SomeDbKey'))", ],
+                               [ 'ORDER BY' => [ 'wl_namespace DESC', 'wl_title DESC' ] ]
+                       ],
+                       [
+                               [
+                                       'from' => new TitleValue( 0, 'AnotherDbKey' ),
+                                       'until' => new TitleValue( 0, 'SomeOtherDbKey' ),
+                                       'startFrom' => new TitleValue( 0, 'SomeDbKey' ),
+                                       'sort' => WatchedItemQueryService::SORT_ASC
+                               ],
+                               [
+                                       "(wl_namespace > 0) OR ((wl_namespace = 0) AND (wl_title >= 'AnotherDbKey'))",
+                                       "(wl_namespace < 0) OR ((wl_namespace = 0) AND (wl_title <= 'SomeOtherDbKey'))",
+                                       "(wl_namespace > 0) OR ((wl_namespace = 0) AND (wl_title >= 'SomeDbKey'))",
+                               ],
+                               [ 'ORDER BY' => [ 'wl_namespace ASC', 'wl_title ASC' ] ]
+                       ],
+                       [
+                               [
+                                       'from' => new TitleValue( 0, 'SomeOtherDbKey' ),
+                                       'until' => new TitleValue( 0, 'AnotherDbKey' ),
+                                       'startFrom' => new TitleValue( 0, 'SomeDbKey' ),
+                                       'sort' => WatchedItemQueryService::SORT_DESC
+                               ],
+                               [
+                                       "(wl_namespace < 0) OR ((wl_namespace = 0) AND (wl_title <= 'SomeOtherDbKey'))",
+                                       "(wl_namespace > 0) OR ((wl_namespace = 0) AND (wl_title >= 'AnotherDbKey'))",
+                                       "(wl_namespace < 0) OR ((wl_namespace = 0) AND (wl_title <= 'SomeDbKey'))",
+                               ],
+                               [ 'ORDER BY' => [ 'wl_namespace DESC', 'wl_title DESC' ] ]
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideGetWatchedItemsForUser_fromUntilStartFromOptions
+        */
+       public function testGetWatchedItemsForUser_fromUntilStartFromOptions(
+               array $options,
+               array $expectedConds,
+               array $expectedDbOptions
+       ) {
+               $user = $this->getMockNonAnonUserWithId( 1 );
+
+               $expectedConds = array_merge( [ 'wl_user' => 1 ], $expectedConds );
+
+               $mockDb = $this->getMockDb();
+               $mockDb->expects( $this->any() )
+                       ->method( 'addQuotes' )
+                       ->will( $this->returnCallback( function( $value ) {
+                               return "'$value'";
+                       } ) );
+               $mockDb->expects( $this->any() )
+                       ->method( 'makeList' )
+                       ->with(
+                               $this->isType( 'array' ),
+                               $this->isType( 'int' )
+                       )
+                       ->will( $this->returnCallback( function( $a, $conj ) {
+                               $sqlConj = $conj === LIST_AND ? ' AND ' : ' OR ';
+                               return join( $sqlConj, array_map( function( $s ) {
+                                       return '(' . $s . ')';
+                               }, $a
+                               ) );
+                       } ) );
+               $mockDb->expects( $this->once() )
+                       ->method( 'select' )
+                       ->with(
+                               'watchlist',
+                               [ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
+                               $expectedConds,
+                               $this->isType( 'string' ),
+                               $expectedDbOptions
+                       )
+                       ->will( $this->returnValue( [] ) );
+
+               $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
+
+               $items = $queryService->getWatchedItemsForUser( $user, $options );
+               $this->assertEmpty( $items );
+       }
+
+       public function getWatchedItemsForUserInvalidOptionsProvider() {
+               return [
+                       [
+                               [ 'sort' => 'foo' ],
+                               'Bad value for parameter $options[\'sort\']'
+                       ],
+                       [
+                               [ 'filter' => 'foo' ],
+                               'Bad value for parameter $options[\'filter\']'
+                       ],
+                       [
+                               [ 'from' => new TitleValue( 0, 'SomeDbKey' ), ],
+                               'Bad value for parameter $options[\'sort\']: must be provided'
+                       ],
+                       [
+                               [ 'until' => new TitleValue( 0, 'SomeDbKey' ), ],
+                               'Bad value for parameter $options[\'sort\']: must be provided'
+                       ],
+                       [
+                               [ 'startFrom' => new TitleValue( 0, 'SomeDbKey' ), ],
+                               'Bad value for parameter $options[\'sort\']: must be provided'
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider getWatchedItemsForUserInvalidOptionsProvider
+        */
+       public function testGetWatchedItemsForUser_invalidOptionThrowsException(
+               array $options,
+               $expectedInExceptionMessage
+       ) {
+               $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $this->getMockDb() ) );
+
+               $this->setExpectedException( InvalidArgumentException::class, $expectedInExceptionMessage );
+               $queryService->getWatchedItemsForUser( $this->getMockNonAnonUserWithId( 1 ), $options );
+       }
+
+       public function testGetWatchedItemsForUser_userNotAllowedToViewWatchlist() {
+               $mockDb = $this->getMockDb();
+
+               $mockDb->expects( $this->never() )
+                       ->method( $this->anything() );
+
+               $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
+
+               $items = $queryService->getWatchedItemsForUser( $this->getMockAnonUser() );
+               $this->assertEmpty( $items );
+       }
+
 }
index 2d2e726..030d9d5 100644 (file)
@@ -2444,10 +2444,10 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        public function testUpdateNotificationTimestamp_watchersExist() {
                $mockDb = $this->getMockDb();
                $mockDb->expects( $this->once() )
-                       ->method( 'select' )
+                       ->method( 'selectFieldValues' )
                        ->with(
-                               [ 'watchlist' ],
-                               [ 'wl_user' ],
+                               'watchlist',
+                               'wl_user',
                                [
                                        'wl_user != 1',
                                        'wl_namespace' => 0,
@@ -2455,18 +2455,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                                        'wl_notificationtimestamp IS NULL'
                                ]
                        )
-                       ->will(
-                               $this->returnValue( [
-                                       $this->getFakeRow( [ 'wl_user' => '2' ] ),
-                                       $this->getFakeRow( [ 'wl_user' => '3' ] )
-                               ] )
-                       );
-               $mockDb->expects( $this->once() )
-                       ->method( 'onTransactionIdle' )
-                       ->with( $this->isType( 'callable' ) )
-                       ->will( $this->returnCallback( function( $callable ) {
-                               $callable();
-                       } ) );
+                       ->will( $this->returnValue( [ '2', '3' ] ) );
                $mockDb->expects( $this->once() )
                        ->method( 'update' )
                        ->with(
@@ -2502,10 +2491,10 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
        public function testUpdateNotificationTimestamp_noWatchers() {
                $mockDb = $this->getMockDb();
                $mockDb->expects( $this->once() )
-                       ->method( 'select' )
+                       ->method( 'selectFieldValues' )
                        ->with(
-                               [ 'watchlist' ],
-                               [ 'wl_user' ],
+                               'watchlist',
+                               'wl_user',
                                [
                                        'wl_user != 1',
                                        'wl_namespace' => 0,
@@ -2516,8 +2505,6 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                        ->will(
                                $this->returnValue( [] )
                        );
-               $mockDb->expects( $this->never() )
-                       ->method( 'onTransactionIdle' );
                $mockDb->expects( $this->never() )
                        ->method( 'update' );
 
@@ -2551,19 +2538,10 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
                                $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
                        ) );
                $mockDb->expects( $this->once() )
-                       ->method( 'select' )
+                       ->method( 'selectFieldValues' )
                        ->will(
-                               $this->returnValue( [
-                                       $this->getFakeRow( [ 'wl_user' => '2' ] ),
-                                       $this->getFakeRow( [ 'wl_user' => '3' ] )
-                               ] )
+                               $this->returnValue( [ '2', '3' ] )
                        );
-               $mockDb->expects( $this->once() )
-                       ->method( 'onTransactionIdle' )
-                       ->with( $this->isType( 'callable' ) )
-                       ->will( $this->returnCallback( function( $callable ) {
-                               $callable();
-                       } ) );
                $mockDb->expects( $this->once() )
                        ->method( 'update' );
 
index 0182eb7..7e1ff3d 100644 (file)
@@ -78,35 +78,6 @@ class WatchedItemUnitTest extends MediaWikiTestCase {
                $this->assertEquals( $timestamp, $item->getNotificationTimestamp() );
        }
 
-       /**
-        * @dataProvider provideUserTitleTimestamp
-        */
-       public function testResetNotificationTimestamp( $user, $linkTarget, $timestamp ) {
-               $force = 'XXX';
-               $oldid = 999;
-
-               $store = $this->getMockWatchedItemStore();
-               $store->expects( $this->once() )
-                       ->method( 'resetNotificationTimestamp' )
-                       ->with( $user, $this->isInstanceOf( Title::class ), $force, $oldid )
-                       ->will( $this->returnCallback(
-                               function ( $user, Title $title, $force, $oldid ) use ( $linkTarget ) {
-                                       /** @var LinkTarget $linkTarget */
-                                       $this->assertInstanceOf( 'Title', $title );
-                                       $this->assertSame( $linkTarget->getDBkey(), $title->getDBkey() );
-                                       $this->assertSame( $linkTarget->getFragment(), $title->getFragment() );
-                                       $this->assertSame( $linkTarget->getNamespace(), $title->getNamespace() );
-                                       $this->assertSame( $linkTarget->getText(), $title->getText() );
-
-                                       return true;
-                               }
-                       ) );
-               $this->setService( 'WatchedItemStore', $store );
-
-               $item = new WatchedItem( $user, $linkTarget, $timestamp );
-               $item->resetNotificationTimestamp( $force, $oldid );
-       }
-
        public function testAddWatch() {
                $title = Title::newFromText( 'SomeTitle' );
                $timestamp = null;
@@ -176,38 +147,4 @@ class WatchedItemUnitTest extends MediaWikiTestCase {
                WatchedItem::duplicateEntries( $oldTitle, $newTitle );
        }
 
-       public function testBatchAddWatch() {
-               $itemOne = new WatchedItem( $this->getMockUser( 1 ), new TitleValue( 0, 'Title1' ), null );
-               $itemTwo = new WatchedItem(
-                       $this->getMockUser( 3 ),
-                       Title::newFromText( 'Title2' ),
-                       '20150101010101'
-               );
-
-               $store = $this->getMockWatchedItemStore();
-               $store->expects( $this->exactly( 2 ) )
-                       ->method( 'addWatchBatchForUser' );
-               $store->expects( $this->at( 0 ) )
-                       ->method( 'addWatchBatchForUser' )
-                       ->with(
-                               $itemOne->getUser(),
-                               [
-                                       $itemOne->getTitle()->getSubjectPage(),
-                                       $itemOne->getTitle()->getTalkPage(),
-                               ]
-                       );
-               $store->expects( $this->at( 1 ) )
-                       ->method( 'addWatchBatchForUser' )
-                       ->with(
-                               $itemTwo->getUser(),
-                               [
-                                       $itemTwo->getTitle()->getSubjectPage(),
-                                       $itemTwo->getTitle()->getTalkPage(),
-                               ]
-                       );
-               $this->setService( 'WatchedItemStore', $store );
-
-               WatchedItem::batchAddWatch( [ $itemOne, $itemTwo ] );
-       }
-
 }
index c946689..aade490 100644 (file)
@@ -23,8 +23,11 @@ class WebRequestTest extends MediaWikiTestCase {
        /**
         * @dataProvider provideDetectServer
         * @covers WebRequest::detectServer
+        * @covers WebRequest::detectProtocol
         */
        public function testDetectServer( $expected, $input, $description ) {
+               $this->setMwGlobals( 'wgAssumeProxiesUseDefaultProtocolPorts', true );
+
                $_SERVER = $input;
                $result = WebRequest::detectServer();
                $this->assertEquals( $expected, $result, $description );
@@ -63,6 +66,24 @@ class WebRequestTest extends MediaWikiTestCase {
                                ],
                                'Secure off'
                        ],
+                       [
+                               'https://x',
+                               [
+                                       'HTTP_HOST' => 'x',
+                                       'HTTP_X_FORWARDED_PROTO' => 'https',
+                               ],
+                               'Forwarded HTTPS'
+                       ],
+                       [
+                               'https://x',
+                               [
+                                       'HTTP_HOST' => 'x',
+                                       'HTTPS' => 'off',
+                                       'SERVER_PORT' => '81',
+                                       'HTTP_X_FORWARDED_PROTO' => 'https',
+                               ],
+                               'Forwarded HTTPS'
+                       ],
                        [
                                'http://y',
                                [
@@ -104,6 +125,241 @@ class WebRequestTest extends MediaWikiTestCase {
                ];
        }
 
+       protected function mockWebRequest( $data = [] ) {
+               // Cannot use PHPUnit getMockBuilder() as it does not support
+               // overriding protected properties afterwards
+               $reflection = new ReflectionClass( 'WebRequest' );
+               $req = $reflection->newInstanceWithoutConstructor();
+
+               $prop = $reflection->getProperty( 'data' );
+               $prop->setAccessible( true );
+               $prop->setValue( $req, $data );
+
+               $prop = $reflection->getProperty( 'requestTime' );
+               $prop->setAccessible( true );
+               $prop->setValue( $req, microtime( true ) );
+
+               return $req;
+       }
+
+       /**
+        * @covers WebRequest::getElapsedTime
+        */
+       public function testGetElapsedTime() {
+               $req = $this->mockWebRequest();
+               $this->assertGreaterThanOrEqual( 0.0, $req->getElapsedTime() );
+               $this->assertEquals( 0.0, $req->getElapsedTime(), '', /*delta*/ 0.2 );
+       }
+
+       /**
+        * @covers WebRequest::getVal
+        * @covers WebRequest::getGPCVal
+        * @covers WebRequest::normalizeUnicode
+        */
+       public function testGetValNormal() {
+               // Assert that WebRequest normalises GPC data using UtfNormal\Validator
+               $input = "a \x00 null";
+               $normal = "a \xef\xbf\xbd null";
+               $req = $this->mockWebRequest( [ 'x' => $input, 'y' => [ $input, $input ] ] );
+               $this->assertSame( $normal, $req->getVal( 'x' ) );
+               $this->assertNotSame( $input, $req->getVal( 'x' ) );
+               $this->assertSame( [ $normal, $normal ], $req->getArray( 'y' ) );
+       }
+
+       /**
+        * @covers WebRequest::getVal
+        * @covers WebRequest::getGPCVal
+        */
+       public function testGetVal() {
+               $req = $this->mockWebRequest( [ 'x' => 'Value', 'y' => [ 'a' ], 'crlf' => "A\r\nb" ] );
+               $this->assertSame( 'Value', $req->getVal( 'x' ), 'Simple value' );
+               $this->assertSame( null, $req->getVal( 'z' ), 'Not found' );
+               $this->assertSame( null, $req->getVal( 'y' ), 'Array is ignored' );
+               $this->assertSame( "A\r\nb", $req->getVal( 'crlf' ), 'CRLF' );
+       }
+
+       /**
+        * @covers WebRequest::getRawVal
+        */
+       public function testGetRawVal() {
+               $req = $this->mockWebRequest( [
+                       'x' => 'Value',
+                       'y' => [ 'a' ],
+                       'crlf' => "A\r\nb"
+               ] );
+               $this->assertSame( 'Value', $req->getRawVal( 'x' ) );
+               $this->assertSame( null, $req->getRawVal( 'z' ), 'Not found' );
+               $this->assertSame( null, $req->getRawVal( 'y' ), 'Array is ignored' );
+               $this->assertSame( "A\r\nb", $req->getRawVal( 'crlf' ), 'CRLF' );
+       }
+
+       /**
+        * @covers WebRequest::getArray
+        */
+       public function testGetArray() {
+               $req = $this->mockWebRequest( [ 'x' => 'Value', 'y' => [ 'a', 'b' ] ] );
+               $this->assertSame( [ 'Value' ], $req->getArray( 'x' ), 'Value becomes array' );
+               $this->assertSame( null, $req->getArray( 'z' ), 'Not found' );
+               $this->assertSame( [ 'a', 'b' ], $req->getArray( 'y' ) );
+       }
+
+       /**
+        * @covers WebRequest::getIntArray
+        */
+       public function testGetIntArray() {
+               $req = $this->mockWebRequest( [ 'x' => [ 'Value' ], 'y' => [ '0', '4.2', '-2' ] ] );
+               $this->assertSame( [ 0 ], $req->getIntArray( 'x' ), 'Text becomes 0' );
+               $this->assertSame( null, $req->getIntArray( 'z' ), 'Not found' );
+               $this->assertSame( [ 0, 4, -2 ], $req->getIntArray( 'y' ) );
+       }
+
+       /**
+        * @covers WebRequest::getInt
+        */
+       public function testGetInt() {
+               $req = $this->mockWebRequest( [
+                       'x' => 'Value',
+                       'y' => [ 'a' ],
+                       'zero' => '0',
+                       'answer' => '4.2',
+                       'neg' => '-2',
+               ] );
+               $this->assertSame( 0, $req->getInt( 'x' ), 'Text' );
+               $this->assertSame( 0, $req->getInt( 'y' ), 'Array' );
+               $this->assertSame( 0, $req->getInt( 'z' ), 'Not found' );
+               $this->assertSame( 0, $req->getInt( 'zero' ) );
+               $this->assertSame( 4, $req->getInt( 'answer' ) );
+               $this->assertSame( -2, $req->getInt( 'neg' ) );
+       }
+
+       /**
+        * @covers WebRequest::getIntOrNull
+        */
+       public function testGetIntOrNull() {
+               $req = $this->mockWebRequest( [
+                       'x' => 'Value',
+                       'y' => [ 'a' ],
+                       'zero' => '0',
+                       'answer' => '4.2',
+                       'neg' => '-2',
+               ] );
+               $this->assertSame( null, $req->getIntOrNull( 'x' ), 'Text' );
+               $this->assertSame( null, $req->getIntOrNull( 'y' ), 'Array' );
+               $this->assertSame( null, $req->getIntOrNull( 'z' ), 'Not found' );
+               $this->assertSame( 0, $req->getIntOrNull( 'zero' ) );
+               $this->assertSame( 4, $req->getIntOrNull( 'answer' ) );
+               $this->assertSame( -2, $req->getIntOrNull( 'neg' ) );
+       }
+
+       /**
+        * @covers WebRequest::getFloat
+        */
+       public function testGetFloat() {
+               $req = $this->mockWebRequest( [
+                       'x' => 'Value',
+                       'y' => [ 'a' ],
+                       'zero' => '0',
+                       'answer' => '4.2',
+                       'neg' => '-2',
+               ] );
+               $this->assertSame( 0.0, $req->getFloat( 'x' ), 'Text' );
+               $this->assertSame( 0.0, $req->getFloat( 'y' ), 'Array' );
+               $this->assertSame( 0.0, $req->getFloat( 'z' ), 'Not found' );
+               $this->assertSame( 0.0, $req->getFloat( 'zero' ) );
+               $this->assertSame( 4.2, $req->getFloat( 'answer' ) );
+               $this->assertSame( -2.0, $req->getFloat( 'neg' ) );
+       }
+
+       /**
+        * @covers WebRequest::getBool
+        */
+       public function testGetBool() {
+               $req = $this->mockWebRequest( [
+                       'x' => 'Value',
+                       'y' => [ 'a' ],
+                       'zero' => '0',
+                       'f' => 'false',
+                       't' => 'true',
+               ] );
+               $this->assertSame( true, $req->getBool( 'x' ), 'Text' );
+               $this->assertSame( false, $req->getBool( 'y' ), 'Array' );
+               $this->assertSame( false, $req->getBool( 'z' ), 'Not found' );
+               $this->assertSame( false, $req->getBool( 'zero' ) );
+               $this->assertSame( true, $req->getBool( 'f' ) );
+               $this->assertSame( true, $req->getBool( 't' ) );
+       }
+
+       public static function provideFuzzyBool() {
+               return [
+                       [ 'Text', true ],
+                       [ '', false, '(empty string)' ],
+                       [ '0', false ],
+                       [ '1', true ],
+                       [ 'false', false ],
+                       [ 'true', true ],
+                       [ 'False', false ],
+                       [ 'True', true ],
+                       [ 'FALSE', false ],
+                       [ 'TRUE', true ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideFuzzyBool
+        * @covers WebRequest::getFuzzyBool
+        */
+       public function testGetFuzzyBool( $value, $expected, $message = null ) {
+               $req = $this->mockWebRequest( [ 'x' => $value ] );
+               $this->assertSame( $expected, $req->getFuzzyBool( 'x' ), $message ?: "Value: '$value'" );
+       }
+
+       /**
+        * @covers WebRequest::getFuzzyBool
+        */
+       public function testGetFuzzyBoolDefault() {
+               $req = $this->mockWebRequest();
+               $this->assertSame( false, $req->getFuzzyBool( 'z' ), 'Not found' );
+       }
+
+       /**
+        * @covers WebRequest::getCheck
+        */
+       public function testGetCheck() {
+               $req = $this->mockWebRequest( [ 'x' => 'Value', 'zero' => '0' ] );
+               $this->assertSame( false, $req->getCheck( 'z' ), 'Not found' );
+               $this->assertSame( true, $req->getCheck( 'x' ), 'Text' );
+               $this->assertSame( true, $req->getCheck( 'zero' ) );
+       }
+
+       /**
+        * @covers WebRequest::getText
+        */
+       public function testGetText() {
+               // Avoid FauxRequest (overrides getText)
+               $req = $this->mockWebRequest( [ 'crlf' => "Va\r\nlue" ] );
+               $this->assertSame( "Va\nlue", $req->getText( 'crlf' ), 'CR stripped' );
+       }
+
+       /**
+        * @covers WebRequest::getValues
+        */
+       public function testGetValues() {
+               $values = [ 'x' => 'Value', 'y' => '' ];
+               // Avoid FauxRequest (overrides getValues)
+               $req = $this->mockWebRequest( $values );
+               $this->assertSame( $values, $req->getValues() );
+               $this->assertSame( [ 'x' => 'Value' ], $req->getValues( 'x' ), 'Specific keys' );
+       }
+
+       /**
+        * @covers WebRequest::getValueNames
+        */
+       public function testGetValueNames() {
+               $req = $this->mockWebRequest( [ 'x' => 'Value', 'y' => '' ] );
+               $this->assertSame( [ 'x', 'y' ], $req->getValueNames() );
+               $this->assertSame( [ 'x' ], $req->getValueNames( [ 'y' ] ), 'Exclude keys' );
+       }
+
        /**
         * @dataProvider provideGetIP
         * @covers WebRequest::getIP
@@ -343,6 +599,7 @@ class WebRequestTest extends MediaWikiTestCase {
                                [ 'en-gb' => 1, 'en-us' => '1' ],
                                'Two equally prefered English variants'
                        ],
+                       [ '_', [], 'Invalid input' ],
                ];
        }
 
index 4e22e3c..12878b3 100644 (file)
@@ -15,6 +15,7 @@ class WikiMapTest extends MediaWikiLangTestCase {
                        'wgServer' => [
                                'enwiki' => 'http://en.example.org',
                                'ruwiki' => '//ru.example.org',
+                               'nopathwiki' => '//nopath.example.org',
                        ],
                        'wgArticlePath' => [
                                'enwiki' => '/w/$1',
@@ -46,6 +47,8 @@ class WikiMapTest extends MediaWikiLangTestCase {
                        'nlwiki (sites)' => [ $nlwiki, 'nlwiki', false ],
                        'enwiktionary (sites)' => [ $enwiktionary, 'enwiktionary', false ],
                        'non MediaWiki site' => [ null, 'spam', false ],
+                       'boguswiki' => [ null, 'boguswiki' ],
+                       'nopathwiki' => [ null, 'nopathwiki' ],
                ];
        }
 
index 5d1ead0..8b75d56 100644 (file)
@@ -43,4 +43,87 @@ class ApiBaseTest extends ApiTestCase {
                );
        }
 
+       /**
+        * @dataProvider provideGetParameterFromSettings
+        * @param string|null $input
+        * @param array $paramSettings
+        * @param mixed $expected
+        * @param string[] $warnings
+        */
+       public function testGetParameterFromSettings( $input, $paramSettings, $expected, $warnings ) {
+               $mock = new MockApi();
+               $wrapper = TestingAccessWrapper::newFromObject( $mock );
+
+               $context = new DerivativeContext( $mock );
+               $context->setRequest( new FauxRequest( $input !== null ? [ 'foo' => $input ] : [] ) );
+               $wrapper->mMainModule = new ApiMain( $context );
+
+               if ( $expected instanceof UsageException ) {
+                       try {
+                               $wrapper->getParameterFromSettings( 'foo', $paramSettings, true );
+                       } catch ( UsageException $ex ) {
+                               $this->assertEquals( $expected, $ex );
+                       }
+               } else {
+                       $result = $wrapper->getParameterFromSettings( 'foo', $paramSettings, true );
+                       $this->assertSame( $expected, $result );
+                       $this->assertSame( $warnings, $mock->warnings );
+               }
+       }
+
+       public static function provideGetParameterFromSettings() {
+               $warnings = [
+                       'The value passed for \'foo\' contains invalid or non-normalized data. Textual data should ' .
+                       'be valid, NFC-normalized Unicode without C0 control characters other than ' .
+                       'HT (\\t), LF (\\n), and CR (\\r).'
+               ];
+
+               $c0 = '';
+               $enc = '';
+               for ( $i = 0; $i < 32; $i++ ) {
+                       $c0 .= chr( $i );
+                       $enc .= ( $i === 9 || $i === 10 || $i === 13 )
+                               ? chr( $i )
+                               : '�';
+               }
+
+               return [
+                       'Basic param' => [ 'bar', null, 'bar', [] ],
+                       'Basic param, C0 controls' => [ $c0, null, $enc, $warnings ],
+                       'String param' => [ 'bar', '', 'bar', [] ],
+                       'String param, defaulted' => [ null, '', '', [] ],
+                       'String param, empty' => [ '', 'default', '', [] ],
+                       'String param, required, empty' => [
+                               '',
+                               [ ApiBase::PARAM_DFLT => 'default', ApiBase::PARAM_REQUIRED => true ],
+                               new UsageException( 'The foo parameter must be set', 'nofoo' ),
+                               []
+                       ],
+                       'Multi-valued parameter' => [
+                               'a|b|c',
+                               [ ApiBase::PARAM_ISMULTI => true ],
+                               [ 'a', 'b', 'c' ],
+                               []
+                       ],
+                       'Multi-valued parameter, alternative separator' => [
+                               "\x1fa|b\x1fc|d",
+                               [ ApiBase::PARAM_ISMULTI => true ],
+                               [ 'a|b', 'c|d' ],
+                               []
+                       ],
+                       'Multi-valued parameter, other C0 controls' => [
+                               $c0,
+                               [ ApiBase::PARAM_ISMULTI => true ],
+                               [ $enc ],
+                               $warnings
+                       ],
+                       'Multi-valued parameter, other C0 controls (2)' => [
+                               "\x1f" . $c0,
+                               [ ApiBase::PARAM_ISMULTI => true ],
+                               [ substr( $enc, 0, -3 ), '' ],
+                               $warnings
+                       ],
+               ];
+       }
+
 }
index 7a8e208..02d0a0d 100644 (file)
@@ -513,4 +513,57 @@ class ApiEditPageTest extends ApiTestCase {
                $this->assertEquals( "testing-nontext", $page->getContentModel() );
                $this->assertEquals( $data, $page->getContent()->serialize() );
        }
+
+       /**
+        * This test verifies that after changing the content model
+        * of a page, undoing that edit via the API will also
+        * undo the content model change.
+        */
+       public function testUndoAfterContentModelChange() {
+               $name = 'Help:' . __FUNCTION__;
+               $uploader = self::$users['uploader']->getUser();
+               $sysop = self::$users['sysop']->getUser();
+               $apiResult = $this->doApiRequestWithToken( [
+                       'action' => 'edit',
+                       'title' => $name,
+                       'text' => 'some text',
+               ], null, $sysop )[0];
+
+               // Check success
+               $this->assertArrayHasKey( 'edit', $apiResult );
+               $this->assertArrayHasKey( 'result', $apiResult['edit'] );
+               $this->assertEquals( 'Success', $apiResult['edit']['result'] );
+               $this->assertArrayHasKey( 'contentmodel', $apiResult['edit'] );
+               // Content model is wikitext
+               $this->assertEquals( 'wikitext', $apiResult['edit']['contentmodel'] );
+
+               // Convert the page to JSON
+               $apiResult = $this->doApiRequestWithToken( [
+                       'action' => 'edit',
+                       'title' => $name,
+                       'text' => '{}',
+                       'contentmodel' => 'json',
+               ], null, $uploader )[0];
+
+               // Check success
+               $this->assertArrayHasKey( 'edit', $apiResult );
+               $this->assertArrayHasKey( 'result', $apiResult['edit'] );
+               $this->assertEquals( 'Success', $apiResult['edit']['result'] );
+               $this->assertArrayHasKey( 'contentmodel', $apiResult['edit'] );
+               $this->assertEquals( 'json', $apiResult['edit']['contentmodel'] );
+
+               $apiResult = $this->doApiRequestWithToken( [
+                       'action' => 'edit',
+                       'title' => $name,
+                       'undo' => $apiResult['edit']['newrevid']
+               ], null, $sysop )[0];
+
+               // Check success
+               $this->assertArrayHasKey( 'edit', $apiResult );
+               $this->assertArrayHasKey( 'result', $apiResult['edit'] );
+               $this->assertEquals( 'Success', $apiResult['edit']['result'] );
+               $this->assertArrayHasKey( 'contentmodel', $apiResult['edit'] );
+               // Check that the contentmodel is back to wikitext now.
+               $this->assertEquals( 'wikitext', $apiResult['edit']['contentmodel'] );
+       }
 }
index 18da5af..d13b00b 100644 (file)
@@ -132,7 +132,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'err' => [
                                                        [
                                                                'code' => 'mainpage',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ]
                                                        ],
                                                        $I => 'error',
@@ -142,7 +142,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'string' => [
                                                        [
                                                                'code' => 'mainpage',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ]
                                                        ],
                                                        $I => 'warning',
@@ -154,7 +154,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'errWithData' => [
                                                        [
                                                                'code' => 'overriddenCode',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ],
                                                                'overriddenData' => true
                                                        ],
@@ -165,7 +165,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'messageWithData' => [
                                                        [
                                                                'code' => 'overriddenCode',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ],
                                                                'overriddenData' => true
                                                        ],
@@ -174,7 +174,7 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'message' => [
                                                        [
                                                                'code' => 'mainpage',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ]
                                                        ],
                                                        $I => 'warning',
@@ -182,12 +182,12 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'foo' => [
                                                        [
                                                                'code' => 'mainpage',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ]
                                                        ],
                                                        [
                                                                'code' => 'parentheses',
-                                                               'message' => 'parentheses',
+                                                               'key' => 'parentheses',
                                                                'params' => [ 'foobar', $I => 'param' ]
                                                        ],
                                                        $I => 'warning',
@@ -199,12 +199,12 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'status' => [
                                                        [
                                                                'code' => 'mainpage',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ]
                                                        ],
                                                        [
                                                                'code' => 'parentheses',
-                                                               'message' => 'parentheses',
+                                                               'key' => 'parentheses',
                                                                'params' => [ 'foobar', $I => 'param' ]
                                                        ],
                                                        $I => 'error',
@@ -214,17 +214,17 @@ class ApiErrorFormatterTest extends MediaWikiLangTestCase {
                                                'status' => [
                                                        [
                                                                'code' => 'mainpage',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ]
                                                        ],
                                                        [
                                                                'code' => 'parentheses',
-                                                               'message' => 'parentheses',
+                                                               'key' => 'parentheses',
                                                                'params' => [ 'foobar', $I => 'param' ]
                                                        ],
                                                        [
                                                                'code' => 'overriddenCode',
-                                                               'message' => 'mainpage',
+                                                               'key' => 'mainpage',
                                                                'params' => [ $I => 'param' ],
                                                                'overriddenData' => true
                                                        ],
index 155a9dd..487ab84 100644 (file)
@@ -13,8 +13,6 @@ class ApiLoginTest extends ApiTestCase {
         * Test result of attempted login with an empty username
         */
        public function testApiLoginNoName() {
-               global $wgDisableAuthManager;
-
                $session = [
                        'wsTokenSecrets' => [ 'login' => 'foobar' ],
                ];
@@ -22,11 +20,11 @@ class ApiLoginTest extends ApiTestCase {
                        'lgname' => '', 'lgpassword' => self::$users['sysop']->getPassword(),
                        'lgtoken' => (string)( new MediaWiki\Session\Token( 'foobar', '' ) )
                ], $session );
-               $this->assertEquals( $wgDisableAuthManager ? 'NoName' : 'Failed', $data[0]['login']['result'] );
+               $this->assertEquals( 'Failed', $data[0]['login']['result'] );
        }
 
        public function testApiLoginBadPass() {
-               global $wgServer, $wgDisableAuthManager;
+               global $wgServer;
 
                $user = self::$users['sysop'];
                $userName = $user->getUser()->getName();
@@ -64,7 +62,7 @@ class ApiLoginTest extends ApiTestCase {
                $this->assertNotInternalType( "bool", $result );
                $a = $result["login"]["result"];
 
-               $this->assertEquals( $wgDisableAuthManager ? 'WrongPass' : 'Failed', $a );
+               $this->assertEquals( 'Failed', $a );
        }
 
        public function testApiLoginGoodPass() {
diff --git a/tests/phpunit/includes/api/ApiOpenSearchTest.php b/tests/phpunit/includes/api/ApiOpenSearchTest.php
new file mode 100644 (file)
index 0000000..39e90c2
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+use MediaWiki\MediaWikiServices;
+
+class ApiOpenSearchTest extends MediaWikiTestCase {
+       public function testGetAllowedParams() {
+               $config = $this->replaceSearchEngineConfig();
+               $config->expects( $this->any() )
+                       ->method( 'getSearchTypes' )
+                       ->will( $this->returnValue( [ 'the one ring' ] ) );
+
+               $engine = $this->replaceSearchEngine();
+               $engine->expects( $this->any() )
+                       ->method( 'getProfiles' )
+                       ->will( $this->returnValueMap( [
+                               [ SearchEngine::COMPLETION_PROFILE_TYPE, [
+                                       [
+                                               'name' => 'normal',
+                                               'desc-message' => 'normal-message',
+                                               'default' => true,
+                                       ],
+                                       [
+                                               'name' => 'strict',
+                                               'desc-message' => 'strict-message',
+                                       ],
+                               ] ],
+                       ] ) );
+
+               $api = $this->createApi();
+               $params = $api->getAllowedParams();
+
+               $this->assertArrayNotHasKey( 'offset', $params );
+               $this->assertArrayHasKey( 'profile', $params, print_r( $params, true ) );
+               $this->assertEquals( 'normal', $params['profile'][ApiBase::PARAM_DFLT] );
+       }
+
+       private function replaceSearchEngineConfig() {
+               $config = $this->getMockBuilder( 'SearchEngineConfig' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $this->setService( 'SearchEngineConfig', $config );
+
+               return $config;
+       }
+
+       private function replaceSearchEngine() {
+               $engine = $this->getMockBuilder( 'SearchEngine' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $engineFactory = $this->getMockBuilder( 'SearchEngineFactory' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $engineFactory->expects( $this->any() )
+                       ->method( 'create' )
+                       ->will( $this->returnValue( $engine ) );
+               $this->setService( 'SearchEngineFactory', $engineFactory );
+
+               return $engine;
+       }
+
+       private function createApi() {
+               $ctx = new RequestContext();
+               $apiMain = new ApiMain( $ctx );
+               return new ApiOpenSearch( $apiMain, 'opensearch', '' );
+       }
+}
index 367210a..ad1deee 100644 (file)
@@ -75,4 +75,25 @@ class ApiPageSetTest extends ApiTestCase {
 
                return [ $target, $pageSet ];
        }
+
+       public function testHandleNormalization() {
+               $context = new RequestContext();
+               $context->setRequest( new FauxRequest( [ 'titles' => "a|B|a\xcc\x8a" ] ) );
+               $main = new ApiMain( $context );
+               $pageSet = new ApiPageSet( $main );
+               $pageSet->execute();
+
+               $this->assertSame(
+                       [ 0 => [ 'A' => -1, 'B' => -2, 'Å' => -3 ] ],
+                       $pageSet->getAllTitlesByNamespace()
+               );
+               $this->assertSame(
+                       [
+                               [ 'fromencoded' => true, 'from' => 'a%CC%8A', 'to' => 'å' ],
+                               [ 'fromencoded' => false, 'from' => 'a', 'to' => 'A' ],
+                               [ 'fromencoded' => false, 'from' => 'å', 'to' => 'Å' ],
+                       ],
+                       $pageSet->getNormalizedTitlesAsResult()
+               );
+       }
 }
index 85bcf5f..582c076 100644 (file)
@@ -138,10 +138,10 @@ class ApiQueryWatchlistRawIntegrationTest extends ApiTestCase {
                );
 
                $resultChanged = $this->doListWatchlistRawRequest(
-                       [ 'wrprop' => 'changed', 'wrshow' => 'changed' ]
+                       [ 'wrprop' => 'changed', 'wrshow' => WatchedItemQueryService::FILTER_CHANGED ]
                );
                $resultNotChanged = $this->doListWatchlistRawRequest(
-                       [ 'wrprop' => 'changed', 'wrshow' => '!changed' ]
+                       [ 'wrprop' => 'changed', 'wrshow' => WatchedItemQueryService::FILTER_NOT_CHANGED ]
                );
 
                $this->assertEquals(
index ac4d2c9..7e1f9d8 100644 (file)
@@ -9,7 +9,7 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
        protected $apiContext;
 
        protected function setUp() {
-               global $wgServer, $wgDisableAuthManager;
+               global $wgServer;
 
                parent::setUp();
                self::$apiUrl = $wgServer . wfScript( 'api' );
@@ -22,7 +22,7 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
                ];
 
                $this->setMwGlobals( [
-                       'wgAuth' => $wgDisableAuthManager ? new AuthPlugin : new MediaWiki\Auth\AuthManagerAuthPlugin,
+                       'wgAuth' => new MediaWiki\Auth\AuthManagerAuthPlugin,
                        'wgRequest' => new FauxRequest( [] ),
                        'wgUser' => self::$users['sysop']->getUser(),
                ] );
index 9a64d08..d7db538 100644 (file)
@@ -1,12 +1,18 @@
 <?php
 
 class MockApi extends ApiBase {
+       public $warnings = [];
+
        public function execute() {
        }
 
        public function __construct() {
        }
 
+       public function setWarning( $warning ) {
+               $this->warnings[] = $warning;
+       }
+
        public function getAllowedParams() {
                return [
                        'filename' => null,
index 504b16a..8cb2327 100644 (file)
@@ -43,6 +43,7 @@ class ApiQueryTest extends ApiTestCase {
 
                $this->assertEquals(
                        [
+                               'fromencoded' => false,
                                'from' => 'Project:articleA',
                                'to' => $to->getPrefixedText(),
                        ],
@@ -51,6 +52,7 @@ class ApiQueryTest extends ApiTestCase {
 
                $this->assertEquals(
                        [
+                               'fromencoded' => false,
                                'from' => 'article_B',
                                'to' => 'Article B'
                        ],
index 1ded0df..89e48f7 100644 (file)
@@ -7,15 +7,6 @@ namespace MediaWiki\Auth;
  * @covers MediaWiki\Auth\AbstractAuthenticationProvider
  */
 class AbstractAuthenticationProviderTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        public function testAbstractAuthenticationProvider() {
                $provider = $this->getMockForAbstractClass( AbstractAuthenticationProvider::class );
                $providerPriv = \TestingAccessWrapper::newFromObject( $provider );
index ecce932..a1cdf7e 100644 (file)
@@ -7,15 +7,6 @@ namespace MediaWiki\Auth;
  * @covers MediaWiki\Auth\AbstractPasswordPrimaryAuthenticationProvider
  */
 class AbstractPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        public function testConstructor() {
                $provider = $this->getMockForAbstractClass(
                        AbstractPasswordPrimaryAuthenticationProvider::class
index c35430e..9638451 100644 (file)
@@ -7,15 +7,6 @@ namespace MediaWiki\Auth;
  * @covers MediaWiki\Auth\AbstractPreAuthenticationProvider
  */
 class AbstractPreAuthenticationProviderTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        public function testAbstractPreAuthenticationProvider() {
                $user = \User::newFromName( 'UTSysop' );
 
index 420a330..d8588d5 100644 (file)
@@ -7,15 +7,6 @@ namespace MediaWiki\Auth;
  * @covers MediaWiki\Auth\AbstractPrimaryAuthenticationProvider
  */
 class AbstractPrimaryAuthenticationProviderTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        public function testAbstractPrimaryAuthenticationProvider() {
                $user = \User::newFromName( 'UTSysop' );
 
index 9cdc051..bb90dd9 100644 (file)
@@ -7,15 +7,6 @@ namespace MediaWiki\Auth;
  * @covers MediaWiki\Auth\AbstractSecondaryAuthenticationProvider
  */
 class AbstractSecondaryAuthenticationProviderTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        public function testAbstractSecondaryAuthenticationProvider() {
                $user = \User::newFromName( 'UTSysop' );
 
index 82608b0..f679f63 100644 (file)
@@ -30,12 +30,7 @@ class AuthManagerTest extends \MediaWikiTestCase {
        protected $managerPriv;
 
        protected function setUp() {
-               global $wgDisableAuthManager;
-
                parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
 
                $this->setMwGlobals( [ 'wgAuth' => null ] );
                $this->stashMwGlobals( [ 'wgHooks' ] );
@@ -200,14 +195,6 @@ class AuthManagerTest extends \MediaWikiTestCase {
                        \RequestContext::getMain()->getConfig(),
                        \TestingAccessWrapper::newFromObject( $singleton )->config
                );
-
-               $this->setMwGlobals( [ 'wgDisableAuthManager' => true ] );
-               try {
-                       AuthManager::singleton();
-                       $this->fail( 'Expected exception not thrown' );
-               } catch ( \BadMethodCallException $ex ) {
-                       $this->assertSame( '$wgDisableAuthManager is set', $ex->getMessage() );
-               }
        }
 
        public function testCanAuthenticateNow() {
@@ -2722,9 +2709,11 @@ class AuthManagerTest extends \MediaWikiTestCase {
                $session->clear();
                $user = $this->getMock( 'User', [ 'addToDatabase' ] );
                $user->expects( $this->once() )->method( 'addToDatabase' )
-                       ->will( $this->returnCallback( function () use ( $username ) {
-                               $status = \User::newFromName( $username )->addToDatabase();
+                       ->will( $this->returnCallback( function () use ( $username, &$user ) {
+                               $oldUser = \User::newFromName( $username );
+                               $status = $oldUser->addToDatabase();
                                $this->assertTrue( $status->isOK(), 'sanity check' );
+                               $user->setId( $oldUser->getId() );
                                return \Status::newFatal( 'userexists' );
                        } ) );
                $user->setName( $username );
@@ -3100,7 +3089,7 @@ class AuthManagerTest extends \MediaWikiTestCase {
                $actual = $this->manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN );
                $expected = [
                        $rememberReq,
-                       $makeReq( "primary-shared", AuthenticationRequest::REQUIRED ),
+                       $makeReq( "primary-shared", AuthenticationRequest::PRIMARY_REQUIRED ),
                        $makeReq( "required", AuthenticationRequest::PRIMARY_REQUIRED ),
                        $makeReq( "required2", AuthenticationRequest::PRIMARY_REQUIRED ),
                        $makeReq( "optional", AuthenticationRequest::OPTIONAL ),
@@ -3120,10 +3109,10 @@ class AuthManagerTest extends \MediaWikiTestCase {
                $actual = $this->manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN );
                $expected = [
                        $rememberReq,
-                       $makeReq( "primary-shared", AuthenticationRequest::REQUIRED ),
-                       $makeReq( "required", AuthenticationRequest::REQUIRED ),
+                       $makeReq( "primary-shared", AuthenticationRequest::PRIMARY_REQUIRED ),
+                       $makeReq( "required", AuthenticationRequest::PRIMARY_REQUIRED ),
                        $makeReq( "optional", AuthenticationRequest::OPTIONAL ),
-                       $makeReq( "foo", AuthenticationRequest::REQUIRED ),
+                       $makeReq( "foo", AuthenticationRequest::PRIMARY_REQUIRED ),
                        $makeReq( "bar", AuthenticationRequest::REQUIRED ),
                        $makeReq( "baz", AuthenticationRequest::REQUIRED ),
                ];
index 44f2743..96e50e0 100644 (file)
@@ -7,15 +7,6 @@ namespace MediaWiki\Auth;
  * @covers MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider
  */
 class AuthPluginPrimaryAuthenticationProviderTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        public function testConstruction() {
                $plugin = new AuthManagerAuthPlugin();
                try {
index cac031c..7d2ba8d 100644 (file)
@@ -7,15 +7,6 @@ namespace MediaWiki\Auth;
  * @covers MediaWiki\Auth\AuthenticationRequest
  */
 class AuthenticationRequestTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        public function testBasics() {
                $mock = $this->getMockForAbstractClass( AuthenticationRequest::class );
 
@@ -181,6 +172,7 @@ class AuthenticationRequestTest extends \MediaWikiTestCase {
                                'type' => 'string',
                                'label' => $msg,
                                'help' => $msg,
+                               'sensitive' => true,
                        ],
                        'string3' => [
                                'type' => 'string',
@@ -215,6 +207,7 @@ class AuthenticationRequestTest extends \MediaWikiTestCase {
                $expect = $req1->getFieldInfo();
                foreach ( $expect as $name => &$options ) {
                        $options['optional'] = !empty( $options['optional'] );
+                       $options['sensitive'] = !empty( $options['sensitive'] );
                }
                unset( $options );
                $this->assertEquals( $expect, $fields );
@@ -234,8 +227,10 @@ class AuthenticationRequestTest extends \MediaWikiTestCase {
 
                $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] );
                $expect += $req2->getFieldInfo();
+               $expect['string1']['sensitive'] = true;
                $expect['string2']['optional'] = false;
                $expect['string3']['optional'] = false;
+               $expect['string3']['sensitive'] = false;
                $expect['select']['options']['bar'] = $msg;
                $this->assertEquals( $expect, $fields );
 
@@ -246,6 +241,7 @@ class AuthenticationRequestTest extends \MediaWikiTestCase {
                $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] );
                $expect += $req2->getFieldInfo();
                $expect['string1']['optional'] = false;
+               $expect['string1']['sensitive'] = true;
                $expect['string3']['optional'] = false;
                $expect['select']['optional'] = false;
                $expect['select']['options']['bar'] = $msg;
@@ -255,7 +251,11 @@ class AuthenticationRequestTest extends \MediaWikiTestCase {
 
                $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] );
                $expect = $req1->getFieldInfo() + $req2->getFieldInfo();
+               foreach ( $expect as $name => &$options ) {
+                       $options['sensitive'] = !empty( $options['sensitive'] );
+               }
                $expect['string1']['optional'] = false;
+               $expect['string1']['sensitive'] = true;
                $expect['string2']['optional'] = true;
                $expect['string3']['optional'] = true;
                $expect['select']['optional'] = false;
index aafcd09..b5c8a36 100644 (file)
@@ -6,15 +6,6 @@ namespace MediaWiki\Auth;
  * @group AuthManager
  */
 abstract class AuthenticationRequestTestCase extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        abstract protected function getInstance( array $args = [] );
 
        /**
@@ -41,6 +32,13 @@ abstract class AuthenticationRequestTestCase extends \MediaWikiTestCase {
                        if ( isset( $data['image'] ) ) {
                                $this->assertType( 'string', $data['image'], "Field $field, image" );
                        }
+                       if ( isset( $data['sensitive'] ) ) {
+                               $this->assertType( 'bool', $data['sensitive'], "Field $field, sensitive" );
+                       }
+                       if ( $data['type'] === 'password' ) {
+                               $this->assertTrue( !empty( $data['sensitive'] ),
+                                       "Field $field, password field must be sensitive" );
+                       }
 
                        switch ( $data['type'] ) {
                                case 'string':
index 58ff8b6..477b161 100644 (file)
@@ -7,15 +7,6 @@ namespace MediaWiki\Auth;
  * @covers MediaWiki\Auth\AuthenticationResponse
  */
 class AuthenticationResponseTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        /**
         * @dataProvider provideConstructors
         * @param string $constructor
index f2341bc..e6d3ecf 100644 (file)
@@ -8,15 +8,6 @@ namespace MediaWiki\Auth;
  * @covers MediaWiki\Auth\CheckBlocksSecondaryAuthenticationProvider
  */
 class CheckBlocksSecondaryAuthenticationProviderTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        public function testConstructor() {
                $provider = new CheckBlocksSecondaryAuthenticationProvider();
                $providerPriv = \TestingAccessWrapper::newFromObject( $provider );
index 580ef6c..3fc45a4 100644 (file)
@@ -7,15 +7,6 @@ namespace MediaWiki\Auth;
  * @covers MediaWiki\Auth\ConfirmLinkSecondaryAuthenticationProvider
  */
 class ConfirmLinkSecondaryAuthenticationProviderTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        /**
         * @dataProvider provideGetAuthenticationRequests
         * @param string $action
index 18c46f7..c52c3e9 100644 (file)
@@ -60,19 +60,25 @@ class EmailNotificationSecondaryAuthenticationProviderTest extends \PHPUnit_Fram
                $creator = $this->getMock( 'User' );
                $userWithoutEmail = $this->getMock( 'User' );
                $userWithoutEmail->expects( $this->any() )->method( 'getEmail' )->willReturn( '' );
+               $userWithoutEmail->expects( $this->any() )->method( 'getInstanceForUpdate' )->willReturnSelf();
                $userWithoutEmail->expects( $this->never() )->method( 'sendConfirmationMail' );
                $userWithEmailError = $this->getMock( 'User' );
                $userWithEmailError->expects( $this->any() )->method( 'getEmail' )->willReturn( 'foo@bar.baz' );
+               $userWithEmailError->expects( $this->any() )->method( 'getInstanceForUpdate' )->willReturnSelf();
                $userWithEmailError->expects( $this->any() )->method( 'sendConfirmationMail' )
                        ->willReturn( \Status::newFatal( 'fail' ) );
                $userExpectsConfirmation = $this->getMock( 'User' );
                $userExpectsConfirmation->expects( $this->any() )->method( 'getEmail' )
                        ->willReturn( 'foo@bar.baz' );
+               $userExpectsConfirmation->expects( $this->any() )->method( 'getInstanceForUpdate' )
+                       ->willReturnSelf();
                $userExpectsConfirmation->expects( $this->once() )->method( 'sendConfirmationMail' )
                        ->willReturn( \Status::newGood() );
                $userNotExpectsConfirmation = $this->getMock( 'User' );
                $userNotExpectsConfirmation->expects( $this->any() )->method( 'getEmail' )
                        ->willReturn( 'foo@bar.baz' );
+               $userNotExpectsConfirmation->expects( $this->any() )->method( 'getInstanceForUpdate' )
+                       ->willReturnSelf();
                $userNotExpectsConfirmation->expects( $this->never() )->method( 'sendConfirmationMail' );
 
                $provider = new EmailNotificationSecondaryAuthenticationProvider( [
index 3548002..d482453 100644 (file)
@@ -8,15 +8,6 @@ namespace MediaWiki\Auth;
  * @covers MediaWiki\Auth\LegacyHookPreAuthenticationProvider
  */
 class LegacyHookPreAuthenticationProviderTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        /**
         * Get an instance of the provider
         * @return LegacyHookPreAuthenticationProvider
index 713c27e..088dd00 100644 (file)
@@ -13,15 +13,6 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
        private $config = null;
        private $validity = null;
 
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        /**
         * Get an instance of the provider
         *
index 79c138b..90ed542 100644 (file)
@@ -7,15 +7,6 @@ namespace MediaWiki\Auth;
  * @covers MediaWiki\Auth\ResetPasswordSecondaryAuthenticationProvider
  */
 class ResetPasswordSecondaryAuthenticationProviderTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        /**
         * @dataProvider provideGetAuthenticationRequests
         * @param string $action
index 8d0bf96..515a5b3 100644 (file)
@@ -13,15 +13,6 @@ class TemporaryPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestC
        private $config = null;
        private $validity = null;
 
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        /**
         * Get an instance of the provider
         *
index 8b273b5..aa6f0e8 100644 (file)
@@ -8,15 +8,6 @@ namespace MediaWiki\Auth;
  * @covers MediaWiki\Auth\ThrottlePreAuthenticationProvider
  */
 class ThrottlePreAuthenticationProviderTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        public function testConstructor() {
                $provider = new ThrottlePreAuthenticationProvider();
                $providerPriv = \TestingAccessWrapper::newFromObject( $provider );
index dba748b..5806003 100644 (file)
@@ -14,15 +14,6 @@ use Psr\Log\NullLogger;
  * @covers MediaWiki\Auth\Throttler
  */
 class ThrottlerTest extends \MediaWikiTestCase {
-       protected function setUp() {
-               global $wgDisableAuthManager;
-
-               parent::setUp();
-               if ( $wgDisableAuthManager ) {
-                       $this->markTestSkipped( '$wgDisableAuthManager is set' );
-               }
-       }
-
        public function testConstructor() {
                $cache = new \HashBagOStuff();
                $logger = $this->getMockBuilder( AbstractLogger::class )
index 16f210b..ccabab6 100644 (file)
@@ -35,9 +35,7 @@ class RCCacheEntryFactoryTest extends MediaWikiLangTestCase {
                        'wgArticlePath' => '/wiki/$1'
                ] );
 
-               $this->linkRenderer = new LinkRenderer(
-                       MediaWikiServices::getInstance()->getTitleFormatter()
-               );
+               $this->linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
        }
 
        public function testNewFromRecentChange() {
index cac3d43..ae51a6c 100644 (file)
@@ -103,9 +103,7 @@ class TestRecentChangesHelper {
                $rcCacheFactory = new RCCacheEntryFactory(
                        new RequestContext(),
                        [ 'diff' => 'diff', 'cur' => 'cur', 'last' => 'last' ],
-                       new LinkRenderer(
-                               MediaWikiServices::getInstance()->getTitleFormatter()
-                       )
+                       MediaWikiServices::getInstance()->getLinkRenderer()
                );
                return $rcCacheFactory->newFromRecentChange( $recentChange, false );
        }
index 545b964..39948ca 100644 (file)
@@ -3,6 +3,7 @@ use MediaWiki\MediaWikiServices;
 
 /**
  * @group ContentHandler
+ * @group Database
  */
 class ContentHandlerTest extends MediaWikiTestCase {
 
@@ -52,6 +53,11 @@ class ContentHandlerTest extends MediaWikiTestCase {
                parent::tearDown();
        }
 
+       public function addDBDataOnce() {
+               $this->insertPage( 'Not_Main_Page', 'This is not a main page' );
+               $this->insertPage( 'Smithee', 'A smithee is one who smiths. See also [[Alan Smithee]]' );
+       }
+
        public static function dataGetDefaultModelFor() {
                return [
                        [ 'Help:Foo', CONTENT_MODEL_WIKITEXT ],
@@ -370,8 +376,7 @@ class ContentHandlerTest extends MediaWikiTestCase {
                $content = new WikitextContent( 'test text' );
                $ok = ContentHandler::runLegacyHooks(
                        'testRunLegacyHooks',
-                       [ 'foo', &$content, 'bar' ],
-                       false
+                       [ 'foo', &$content, 'bar' ]
                );
 
                $this->assertTrue( $ok, "runLegacyHooks should have returned true" );
@@ -409,4 +414,65 @@ class ContentHandlerTest extends MediaWikiTestCase {
                $this->assertInstanceOf( $handlerClass, $handler );
        }
 
+       public function testGetFieldsForSearchIndex() {
+               $searchEngine = $this->newSearchEngine();
+
+               $handler = ContentHandler::getForModelID( CONTENT_MODEL_WIKITEXT );
+
+               $fields = $handler->getFieldsForSearchIndex( $searchEngine );
+
+               $this->assertArrayHasKey( 'category', $fields );
+               $this->assertArrayHasKey( 'external_link', $fields );
+               $this->assertArrayHasKey( 'outgoing_link', $fields );
+               $this->assertArrayHasKey( 'template', $fields );
+       }
+
+       private function newSearchEngine() {
+               $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+                       ->getMock();
+
+               $searchEngine->expects( $this->any() )
+                       ->method( 'makeSearchFieldMapping' )
+                       ->will( $this->returnCallback( function( $name, $type ) {
+                                       return new DummySearchIndexFieldDefinition( $name, $type );
+                       } ) );
+
+               return $searchEngine;
+       }
+
+       /**
+        * @covers ContentHandler::getDataForSearchIndex
+        */
+       public function testDataIndexFields() {
+               $mockEngine = $this->getMock( 'SearchEngine' );
+               $title = Title::newFromText( 'Not_Main_Page', NS_MAIN );
+               $page = new WikiPage( $title );
+
+               $this->setTemporaryHook( 'SearchDataForIndex',
+                       function ( &$fields, ContentHandler $handler, WikiPage $page, ParserOutput $output,
+                                          SearchEngine $engine ) {
+                               $fields['testDataField'] = 'test content';
+                       } );
+
+               $output = $page->getContent()->getParserOutput( $title );
+               $data = $page->getContentHandler()->getDataForSearchIndex( $page, $output, $mockEngine );
+               $this->assertArrayHasKey( 'text', $data );
+               $this->assertArrayHasKey( 'text_bytes', $data );
+               $this->assertArrayHasKey( 'language', $data );
+               $this->assertArrayHasKey( 'testDataField', $data );
+               $this->assertEquals( 'test content', $data['testDataField'] );
+       }
+
+       /**
+        * @covers ContentHandler::getParserOutputForIndexing
+        */
+       public function testParserOutputForIndexing() {
+               $title = Title::newFromText( 'Smithee', NS_MAIN );
+               $page = new WikiPage( $title );
+
+               $out = $page->getContentHandler()->getParserOutputForIndexing( $page );
+               $this->assertInstanceOf( ParserOutput::class, $out );
+               $this->assertContains( 'one who smiths', $out->getRawText() );
+       }
+
 }
index a2aef35..d2078d7 100644 (file)
@@ -101,15 +101,15 @@ class CssContentTest extends JavaScriptContentTest {
         */
        public static function provideGetRedirectTarget() {
                // @codingStandardsIgnoreStart Generic.Files.LineLength
-               return array(
-                       array( 'MediaWiki:MonoBook.css', "/* #REDIRECT */@import url(//example.org/w/index.php?title=MediaWiki:MonoBook.css&action=raw&ctype=text/css);" ),
-                       array( 'User:FooBar/common.css', "/* #REDIRECT */@import url(//example.org/w/index.php?title=User:FooBar/common.css&action=raw&ctype=text/css);" ),
-                       array( 'Gadget:FooBaz.css', "/* #REDIRECT */@import url(//example.org/w/index.php?title=Gadget:FooBaz.css&action=raw&ctype=text/css);" ),
+               return [
+                       [ 'MediaWiki:MonoBook.css', "/* #REDIRECT */@import url(//example.org/w/index.php?title=MediaWiki:MonoBook.css&action=raw&ctype=text/css);" ],
+                       [ 'User:FooBar/common.css', "/* #REDIRECT */@import url(//example.org/w/index.php?title=User:FooBar/common.css&action=raw&ctype=text/css);" ],
+                       [ 'Gadget:FooBaz.css', "/* #REDIRECT */@import url(//example.org/w/index.php?title=Gadget:FooBaz.css&action=raw&ctype=text/css);" ],
                        # No #REDIRECT comment
-                       array( null, "@import url(//example.org/w/index.php?title=Gadget:FooBaz.css&action=raw&ctype=text/css);" ),
+                       [ null, "@import url(//example.org/w/index.php?title=Gadget:FooBaz.css&action=raw&ctype=text/css);" ],
                        # Wrong domain
-                       array( null, "/* #REDIRECT */@import url(//example.com/w/index.php?title=Gadget:FooBaz.css&action=raw&ctype=text/css);" ),
-               );
+                       [ null, "/* #REDIRECT */@import url(//example.com/w/index.php?title=Gadget:FooBaz.css&action=raw&ctype=text/css);" ],
+               ];
                // @codingStandardsIgnoreEnd
        }
 
diff --git a/tests/phpunit/includes/content/JsonContentHandlerTest.php b/tests/phpunit/includes/content/JsonContentHandlerTest.php
new file mode 100644 (file)
index 0000000..abfb673
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+
+class JsonContentHandlerTest extends MediaWikiTestCase {
+
+       /**
+        * @covers JsonContentHandler::makeEmptyContent
+        */
+       public function testMakeEmptyContent() {
+               $handler = new JsonContentHandler();
+               $content = $handler->makeEmptyContent();
+               $this->assertInstanceOf( JsonContent::class, $content );
+               $this->assertTrue( $content->isValid() );
+       }
+}
index 492fec6..918815c 100644 (file)
@@ -9,4 +9,44 @@ class TextContentHandlerTest extends MediaWikiLangTestCase {
                $this->assertTrue( $handler->supportsDirectEditing(), 'direct editing is supported' );
        }
 
+       /**
+        * @covers SearchEngine::makeSearchFieldMapping
+        * @covers ContentHandler::getFieldsForSearchIndex
+        */
+       public function testFieldsForIndex() {
+               $handler = new TextContentHandler();
+
+               $mockEngine = $this->getMock( 'SearchEngine' );
+
+               $mockEngine->expects( $this->atLeastOnce() )
+                       ->method( 'makeSearchFieldMapping' )
+                       ->willReturnCallback( function ( $name, $type ) {
+                               $mockField =
+                                       $this->getMockBuilder( 'SearchIndexFieldDefinition' )
+                                               ->setConstructorArgs( [ $name, $type ] )
+                                               ->getMock();
+                               $mockField->expects( $this->atLeastOnce() )->method( 'getMapping' )->willReturn( [
+                                               'testData' => 'test',
+                                               'name' => $name,
+                                               'type' => $type,
+                                       ] );
+                               return $mockField;
+                       } );
+
+               /**
+                * @var $mockEngine SearchEngine
+                */
+               $fields = $handler->getFieldsForSearchIndex( $mockEngine );
+               $mappedFields = [];
+               foreach ( $fields as $name => $field ) {
+                       $this->assertInstanceOf( 'SearchIndexField', $field );
+                       /**
+                        * @var $field SearchIndexField
+                        */
+                       $mappedFields[$name] = $field->getMapping( $mockEngine );
+               }
+               $this->assertArrayHasKey( 'language', $mappedFields );
+               $this->assertEquals( 'test', $mappedFields['language']['testData'] );
+               $this->assertEquals( 'language', $mappedFields['language']['name'] );
+       }
 }
index ac83428..b290f8f 100644 (file)
@@ -102,6 +102,11 @@ class TextContentTest extends MediaWikiLangTestCase {
                                " Foo \n ",
                                ' Foo',
                        ],
+                       [
+                               # 2: newline normalization
+                               "LF\n\nCRLF\r\n\r\nCR\r\rEND",
+                               "LF\n\nCRLF\n\nCR\n\nEND",
+                       ],
                ];
        }
 
@@ -454,4 +459,30 @@ class TextContentTest extends MediaWikiLangTestCase {
                        $this->assertEquals( $expectedNative, $converted->getNativeData() );
                }
        }
+
+       /**
+        * @covers TextContent::normalizeLineEndings
+        * @dataProvider provideNormalizeLineEndings
+        */
+       public function testNormalizeLineEndings( $input, $expected ) {
+               $this->assertEquals( $expected, TextContent::normalizeLineEndings( $input ) );
+       }
+
+       public static function provideNormalizeLineEndings() {
+               return [
+                       [
+                               "Foo\r\nbar",
+                               "Foo\nbar"
+                       ],
+                       [
+                               "Foo\rbar",
+                               "Foo\nbar"
+                       ],
+                       [
+                               "Foobar\n  ",
+                               "Foobar"
+                       ]
+               ];
+       }
+
 }
index f632882..9d4abe8 100644 (file)
@@ -243,4 +243,20 @@ class WikitextContentHandlerTest extends MediaWikiLangTestCase {
        ) {
        }
        */
+
+       public function testDataIndexFieldsFile() {
+               $mockEngine = $this->getMock( 'SearchEngine' );
+               $title = Title::newFromText( 'Somefile.jpg', NS_FILE );
+               $page = new WikiPage( $title );
+
+               $handler = $this->getMockBuilder( WikitextContentHandler::class )
+                       ->disableOriginalConstructor()
+                       ->setMethods( [ 'getFileText' ] )
+                       ->getMock();
+               $handler->method( 'getFileText' )->will( $this->returnValue( 'This is file content' ) );
+
+               $data = $handler->getDataForSearchIndex( $page, new ParserOutput(), $mockEngine );
+               $this->assertArrayHasKey( 'file_text', $data );
+               $this->assertEquals( 'This is file content', $data['file_text'] );
+       }
 }
diff --git a/tests/phpunit/includes/content/WikitextStructureTest.php b/tests/phpunit/includes/content/WikitextStructureTest.php
new file mode 100644 (file)
index 0000000..49907c8
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+
+class WikitextStructureTest extends MediaWikiLangTestCase {
+
+       private function getMockTitle() {
+               return Title::newFromText( "TestTitle" );
+       }
+
+       /**
+        * Get parser output for Wiki text
+        * @param $text
+        * @return ParserOutput
+        */
+       private function getParserOutput( $text ) {
+               $content = new WikitextContent( $text );
+               return $content->getParserOutput( $this->getMockTitle() );
+       }
+
+       /**
+        * Get WikitextStructure for given text
+        * @param $text
+        * @return WikiTextStructure
+        */
+       private function getStructure( $text ) {
+               return new WikiTextStructure( $this->getParserOutput( $text ) );
+       }
+
+       public function testHeadings() {
+               $text = <<<END
+Some text here
+== Heading one ==
+Some text
+==== heading two ====
+More text
+=== Applicability of the strict mass-energy equivalence formula, ''E'' = ''mc''<sup>2</sup> ===
+and more text
+== Wikitext '''in''' [[Heading]] and also <b>html</b> ==
+more text
+==== See also ====
+* Also things to see!
+END;
+               $struct = $this->getStructure( $text );
+               $headings = $struct->headings();
+               $this->assertCount( 4, $headings );
+               $this->assertContains( "Heading one", $headings );
+               $this->assertContains( "heading two", $headings );
+               $this->assertContains( "Applicability of the strict mass-energy equivalence formula, E = mc2",
+                       $headings );
+               $this->assertContains( "Wikitext in Heading and also html", $headings );
+       }
+
+       public function testDefaultSort() {
+               $text = <<<END
+Louise Michel
+== Heading one ==
+Some text
+==== See also ====
+* Also things to see!
+{{DEFAULTSORT:Michel, Louise}}
+END;
+               $struct = $this->getStructure( $text );
+               $this->assertEquals( "Michel, Louise", $struct->getDefaultSort() );
+       }
+
+       public function testHeadingsFirst() {
+               $text = <<<END
+== Heading one ==
+Some text
+==== heading two ====
+END;
+               $struct = $this->getStructure( $text );
+               $headings = $struct->headings();
+               $this->assertCount( 2, $headings );
+               $this->assertContains( "Heading one", $headings );
+               $this->assertContains( "heading two", $headings );
+       }
+
+       public function testHeadingsNone() {
+               $text = "This text is completely devoid of headings.";
+               $struct = $this->getStructure( $text );
+               $headings = $struct->headings();
+               $this->assertArrayEquals( [], $headings );
+       }
+
+       public function testTexts() {
+               $text = <<<END
+Opening text is opening.
+== Then comes header ==
+Then we got more<br>text
+=== And more headers ===
+{| class="wikitable"
+|-
+! Header table
+|-
+| row in table
+|-
+| another row in table
+|}
+END;
+               $struct = $this->getStructure( $text );
+               $this->assertEquals( "Opening text is opening.", $struct->getOpeningText() );
+               $this->assertEquals( "Opening text is opening.   Then we got more text",
+                       $struct->getMainText() );
+               $this->assertEquals( [ "Header table  row in table  another row in table" ],
+                       $struct->getAuxiliaryText() );
+       }
+}
index bb747c7..607f25c 100644 (file)
@@ -31,6 +31,8 @@
 class FakeDatabaseMysqlBase extends DatabaseMysqlBase {
        // From DatabaseBase
        function __construct() {
+               $this->profiler = new ProfilerStub( [] );
+               $this->trxProfiler = new TransactionProfiler();
        }
 
        protected function closeConnection() {
@@ -76,9 +78,6 @@ class FakeDatabaseMysqlBase extends DatabaseMysqlBase {
        protected function mysqlFetchField( $res, $n ) {
        }
 
-       protected function mysqlPing() {
-       }
-
        protected function mysqlRealEscapeString( $s ) {
 
        }
@@ -250,26 +249,71 @@ class DatabaseMysqlBaseTest extends MediaWikiTestCase {
        /**
         * @dataProvider provideComparePositions
         */
-       function testHasReached( MySQLMasterPos $lowerPos, MySQLMasterPos $higherPos ) {
-               $this->assertTrue( $higherPos->hasReached( $lowerPos ) );
-               $this->assertTrue( $higherPos->hasReached( $higherPos ) );
-               $this->assertTrue( $lowerPos->hasReached( $lowerPos ) );
-               $this->assertFalse( $lowerPos->hasReached( $higherPos ) );
+       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' )
+                               new MySQLMasterPos( 'db1034-bin.000976', '843431248' ),
+                               true
                        ],
                        [
                                new MySQLMasterPos( 'db1034-bin.000976', '999' ),
-                               new MySQLMasterPos( 'db1034-bin.000976', '1000' )
+                               new MySQLMasterPos( 'db1034-bin.000976', '1000' ),
+                               true
                        ],
                        [
                                new MySQLMasterPos( 'db1034-bin.000976', '999' ),
-                               new MySQLMasterPos( 'db1035-bin.000976', '1000' )
+                               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
                        ],
                ];
        }
index 5d5521c..e7eeff9 100644 (file)
@@ -5,15 +5,12 @@
  * This is a non DBMS depending test.
  */
 class DatabaseSQLTest extends MediaWikiTestCase {
-
-       /**
-        * @var DatabaseTestHelper
-        */
+       /** @var DatabaseTestHelper */
        private $database;
 
        protected function setUp() {
                parent::setUp();
-               $this->database = new DatabaseTestHelper( __CLASS__ );
+               $this->database = new DatabaseTestHelper( __CLASS__, [ 'cliMode' => true ] );
        }
 
        protected function assertLastSql( $sqlText ) {
@@ -23,6 +20,10 @@ class DatabaseSQLTest extends MediaWikiTestCase {
                );
        }
 
+       protected function assertLastSqlDb( $sqlText, $db ) {
+               $this->assertEquals( $db->getLastSqls(), $sqlText );
+       }
+
        /**
         * @dataProvider provideSelect
         * @covers DatabaseBase::select
@@ -354,7 +355,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
         * @dataProvider provideInsertSelect
         * @covers DatabaseBase::insertSelect
         */
-       public function testInsertSelect( $sql, $sqlText ) {
+       public function testInsertSelect( $sql, $sqlTextNative, $sqlSelect, $sqlInsert ) {
                $this->database->insertSelect(
                        $sql['destTable'],
                        $sql['srcTable'],
@@ -364,7 +365,22 @@ class DatabaseSQLTest extends MediaWikiTestCase {
                        isset( $sql['insertOptions'] ) ? $sql['insertOptions'] : [],
                        isset( $sql['selectOptions'] ) ? $sql['selectOptions'] : []
                );
-               $this->assertLastSql( $sqlText );
+               $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'] : []
+               );
+               $this->assertLastSqlDb( implode( '; ', [ $sqlSelect, $sqlInsert ] ), $dbWeb );
        }
 
        public static function provideInsertSelect() {
@@ -379,7 +395,10 @@ class DatabaseSQLTest extends MediaWikiTestCase {
                                "INSERT INTO insert_table " .
                                        "(field_insert,field) " .
                                        "SELECT field_select,field2 " .
-                                       "FROM select_table"
+                                       "FROM select_table",
+                               "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')"
                        ],
                        [
                                [
@@ -392,7 +411,10 @@ class DatabaseSQLTest extends MediaWikiTestCase {
                                        "(field_insert,field) " .
                                        "SELECT field_select,field2 " .
                                        "FROM select_table " .
-                                       "WHERE field = '2'"
+                                       "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')"
                        ],
                        [
                                [
@@ -408,7 +430,10 @@ class DatabaseSQLTest extends MediaWikiTestCase {
                                        "SELECT field_select,field2 " .
                                        "FROM select_table " .
                                        "WHERE field = '2' " .
-                                       "ORDER BY field"
+                                       "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')"
                        ],
                ];
        }
index 0730529..0f9a401 100644 (file)
@@ -23,6 +23,7 @@ class DatabaseTest extends MediaWikiTestCase {
                        $this->dropFunctions();
                        $this->functionTest = false;
                }
+               $this->db->restoreFlags( IDatabase::RESTORE_INITIAL );
        }
        /**
         * @covers DatabaseBase::dropTable
@@ -239,12 +240,15 @@ class DatabaseTest extends MediaWikiTestCase {
                $db = $this->db;
 
                $db->setFlag( DBO_TRX );
+               $called = false;
                $flagSet = null;
-               $db->onTransactionIdle( function() use ( $db, &$flagSet ) {
+               $db->onTransactionIdle( function() use ( $db, &$flagSet, &$called ) {
+                       $called = true;
                        $flagSet = $db->getFlag( DBO_TRX );
                } );
                $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;
@@ -260,4 +264,152 @@ class DatabaseTest extends MediaWikiTestCase {
                } );
                $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 DatabaseBase::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 DatabaseBase::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 DatabaseBase::getFlag(
+        * @covers DatabaseBase::setFlag()
+        * @covers DatabaseBase::restoreFlags()
+        */
+       public function testFlagSetting() {
+               $db = $this->db;
+               $origTrx = $db->getFlag( DBO_TRX );
+               $origSsl = $db->getFlag( DBO_SSL );
+
+               if ( $origTrx ) {
+                       $db->clearFlag( DBO_TRX, $db::REMEMBER_PRIOR );
+               } else {
+                       $db->setFlag( DBO_TRX, $db::REMEMBER_PRIOR );
+               }
+               $this->assertEquals( !$origTrx, $db->getFlag( DBO_TRX ) );
+
+               if ( $origSsl ) {
+                       $db->clearFlag( DBO_SSL, $db::REMEMBER_PRIOR );
+               } else {
+                       $db->setFlag( DBO_SSL, $db::REMEMBER_PRIOR );
+               }
+               $this->assertEquals( !$origSsl, $db->getFlag( DBO_SSL ) );
+
+               $db2 = clone $db;
+               $db2->restoreFlags( $db::RESTORE_INITIAL );
+               $this->assertEquals( $origTrx, $db2->getFlag( DBO_TRX ) );
+               $this->assertEquals( $origSsl, $db2->getFlag( DBO_SSL ) );
+
+               $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 ) );
+       }
 }
index aa8b8e8..63322cc 100644 (file)
@@ -19,14 +19,21 @@ class DatabaseTestHelper extends DatabaseBase {
         */
        protected $lastSqls = [];
 
+       /** @var array List of row arrays */
+       protected $nextResult = [];
+
        /**
         * Array of tables to be considered as existing by tableExist()
         * Use setExistingTables() to alter.
         */
        protected $tablesExists;
 
-       public function __construct( $testName ) {
+       public function __construct( $testName, array $opts = [] ) {
                $this->testName = $testName;
+
+               $this->profiler = new ProfilerStub( [] );
+               $this->trxProfiler = new TransactionProfiler();
+               $this->cliMode = isset( $opts['cliMode'] ) ? $opts['cliMode'] : true;
        }
 
        /**
@@ -44,6 +51,13 @@ class DatabaseTestHelper extends DatabaseBase {
                $this->tablesExists = (array)$tablesExists;
        }
 
+       /**
+        * @param mixed $res Use an array of row arrays to set row result
+        */
+       public function forceNextResult( $res ) {
+               $this->nextResult = $res;
+       }
+
        protected function addSql( $sql ) {
                // clean up spaces before and after some words and the whole string
                $this->lastSqls[] = trim( preg_replace(
@@ -160,11 +174,19 @@ class DatabaseTestHelper extends DatabaseBase {
                return true;
        }
 
+       function ping( &$rtt = null ) {
+               $rtt = 0.0;
+               return true;
+       }
+
        protected function closeConnection() {
                return false;
        }
 
        protected function doQuery( $sql ) {
-               return [];
+               $res = $this->nextResult;
+               $this->nextResult = [];
+
+               return new FakeResultWrapper( $res );
        }
 }
index 3562ed8..bf78d13 100644 (file)
@@ -107,7 +107,7 @@ class LBFactoryTest extends MediaWikiTestCase {
                        $wgDBserver, $dbw->getLBInfo( 'clusterMasterHost' ), 'cluster master set' );
 
                $dbr = $lb->getConnection( DB_SLAVE );
-               $this->assertTrue( $dbr->getLBInfo( 'slave' ), 'slave shows as slave' );
+               $this->assertTrue( $dbr->getLBInfo( 'replica' ), 'slave shows as slave' );
                $this->assertEquals(
                        $wgDBserver, $dbr->getLBInfo( 'clusterMasterHost' ), 'cluster master set' );
 
@@ -145,7 +145,7 @@ class LBFactoryTest extends MediaWikiTestCase {
                $this->assertTrue( $dbw->getLBInfo( 'master' ), 'master shows as master' );
 
                $dbr = $lb->getConnection( DB_SLAVE );
-               $this->assertTrue( $dbr->getLBInfo( 'slave' ), 'slave shows as slave' );
+               $this->assertTrue( $dbr->getLBInfo( 'replica' ), 'slave shows as slave' );
 
                $factory->shutdown();
                $lb->closeAll();
@@ -155,11 +155,14 @@ class LBFactoryTest extends MediaWikiTestCase {
                // (a) First HTTP request
                $mPos = new MySQLMasterPos( 'db1034-bin.000976', '843431247' );
 
+               $now = microtime( true );
                $mockDB = $this->getMockBuilder( 'DatabaseMysql' )
                        ->disableOriginalConstructor()
                        ->getMock();
                $mockDB->expects( $this->any() )
-                       ->method( 'doneWrites' )->will( $this->returnValue( true ) );
+                       ->method( 'writesOrCallbacksPending' )->will( $this->returnValue( true ) );
+               $mockDB->expects( $this->any() )
+                       ->method( 'lastDoneWrites' )->will( $this->returnValue( $now ) );
                $mockDB->expects( $this->any() )
                        ->method( 'getMasterPos' )->will( $this->returnValue( $mPos ) );
 
@@ -174,6 +177,18 @@ class LBFactoryTest extends MediaWikiTestCase {
                        ->method( 'parentInfo' )->will( $this->returnValue( [ 'id' => "main-DEFAULT" ] ) );
                $lb->expects( $this->any() )
                        ->method( 'getAnyOpenConnection' )->will( $this->returnValue( $mockDB ) );
+               $lb->expects( $this->any() )
+                       ->method( 'hasOrMadeRecentMasterChanges' )->will( $this->returnCallback(
+                               function () use ( $mockDB ) {
+                                       $p = 0;
+                                       $p |= call_user_func( [ $mockDB, 'writesOrCallbacksPending' ] );
+                                       $p |= call_user_func( [ $mockDB, 'lastDoneWrites' ] );
+
+                                       return (bool)$p;
+                               }
+                       ) );
+               $lb->expects( $this->any() )
+                       ->method( 'getMasterPos' )->will( $this->returnValue( $mPos ) );
 
                $bag = new HashBagOStuff();
                $cp = new ChronologyProtector(
@@ -184,7 +199,8 @@ class LBFactoryTest extends MediaWikiTestCase {
                        ]
                );
 
-               $mockDB->expects( $this->exactly( 2 ) )->method( 'doneWrites' );
+               $mockDB->expects( $this->exactly( 2 ) )->method( 'writesOrCallbacksPending' );
+               $mockDB->expects( $this->exactly( 2 ) )->method( 'lastDoneWrites' );
 
                // Nothing to wait for
                $cp->initLB( $lb );
index ecc67b7..4227693 100644 (file)
@@ -5,10 +5,15 @@ class DeferredUpdatesTest extends MediaWikiTestCase {
                $this->setMwGlobals( 'wgCommandLineMode', false );
 
                $updates = [
-                       '1' => 'deferred update 1',
-                       '2' => 'deferred update 2',
-                       '3' => 'deferred update 3',
-                       '2-1' => 'deferred update 1 within deferred update 2',
+                       '1' => "deferred update 1;\n",
+                       '2' => "deferred update 2;\n",
+                       '2-1' => "deferred update 1 within deferred update 2;\n",
+                       '2-2' => "deferred update 2 within deferred update 2;\n",
+                       '3' => "deferred update 3;\n",
+                       '3-1' => "deferred update 1 within deferred update 3;\n",
+                       '3-2' => "deferred update 2 within deferred update 3;\n",
+                       '3-1-1' => "deferred update 1 within deferred update 1 within deferred update 3;\n",
+                       '3-2-1' => "deferred update 1 within deferred update 2 with deferred update 3;\n",
                ];
                DeferredUpdates::addCallableUpdate(
                        function () use ( $updates ) {
@@ -23,14 +28,41 @@ class DeferredUpdatesTest extends MediaWikiTestCase {
                                                echo $updates['2-1'];
                                        }
                                );
+                               DeferredUpdates::addCallableUpdate(
+                                       function () use ( $updates ) {
+                                               echo $updates['2-2'];
+                                       }
+                               );
                        }
                );
                DeferredUpdates::addCallableUpdate(
                        function () use ( $updates ) {
-                               echo $updates[3];
+                               echo $updates['3'];
+                               DeferredUpdates::addCallableUpdate(
+                                       function () use ( $updates ) {
+                                               echo $updates['3-1'];
+                                               DeferredUpdates::addCallableUpdate(
+                                                       function () use ( $updates ) {
+                                                               echo $updates['3-1-1'];
+                                                       }
+                                               );
+                                       }
+                               );
+                               DeferredUpdates::addCallableUpdate(
+                                       function () use ( $updates ) {
+                                               echo $updates['3-2'];
+                                               DeferredUpdates::addCallableUpdate(
+                                                       function () use ( $updates ) {
+                                                               echo $updates['3-2-1'];
+                                                       }
+                                               );
+                                       }
+                               );
                        }
                );
 
+               $this->assertEquals( 3, DeferredUpdates::pendingUpdatesCount() );
+
                $this->expectOutputString( implode( '', $updates ) );
 
                DeferredUpdates::doUpdates();
@@ -63,13 +95,20 @@ class DeferredUpdatesTest extends MediaWikiTestCase {
 
        public function testDoUpdatesCLI() {
                $this->setMwGlobals( 'wgCommandLineMode', true );
-
                $updates = [
-                       '1' => 'deferred update 1',
-                       '2' => 'deferred update 2',
-                       '2-1' => 'deferred update 1 within deferred update 2',
-                       '3' => 'deferred update 3',
+                       '1' => "deferred update 1;\n",
+                       '2' => "deferred update 2;\n",
+                       '2-1' => "deferred update 1 within deferred update 2;\n",
+                       '2-2' => "deferred update 2 within deferred update 2;\n",
+                       '3' => "deferred update 3;\n",
+                       '3-1' => "deferred update 1 within deferred update 3;\n",
+                       '3-2' => "deferred update 2 within deferred update 3;\n",
+                       '3-1-1' => "deferred update 1 within deferred update 1 within deferred update 3;\n",
+                       '3-2-1' => "deferred update 1 within deferred update 2 with deferred update 3;\n",
                ];
+
+               wfGetLBFactory()->commitMasterChanges( __METHOD__ ); // clear anything
+
                DeferredUpdates::addCallableUpdate(
                        function () use ( $updates ) {
                                echo $updates['1'];
@@ -83,11 +122,36 @@ class DeferredUpdatesTest extends MediaWikiTestCase {
                                                echo $updates['2-1'];
                                        }
                                );
+                               DeferredUpdates::addCallableUpdate(
+                                       function () use ( $updates ) {
+                                               echo $updates['2-2'];
+                                       }
+                               );
                        }
                );
                DeferredUpdates::addCallableUpdate(
                        function () use ( $updates ) {
-                               echo $updates[3];
+                               echo $updates['3'];
+                               DeferredUpdates::addCallableUpdate(
+                                       function () use ( $updates ) {
+                                               echo $updates['3-1'];
+                                               DeferredUpdates::addCallableUpdate(
+                                                       function () use ( $updates ) {
+                                                               echo $updates['3-1-1'];
+                                                       }
+                                               );
+                                       }
+                               );
+                               DeferredUpdates::addCallableUpdate(
+                                       function () use ( $updates ) {
+                                               echo $updates['3-2'];
+                                               DeferredUpdates::addCallableUpdate(
+                                                       function () use ( $updates ) {
+                                                               echo $updates['3-2-1'];
+                                                       }
+                                               );
+                                       }
+                               );
                        }
                );
 
index 3309352..9cc3ffd 100644 (file)
@@ -369,10 +369,7 @@ class LinksUpdateTest extends MediaWikiLangTestCase {
        ) {
                $update = new LinksUpdate( $title, $parserOutput );
 
-               // NOTE: make sure LinksUpdate does not generate warnings when called inside a transaction.
-               $update->beginTransaction();
                $update->doUpdate();
-               $update->commitTransaction();
 
                $this->assertSelect( $table, $fields, $condition, $expectedRows );
                return $update;
index 4aeddc6..254cfbd 100644 (file)
@@ -1139,16 +1139,16 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->tearDownFiles();
                $this->doTestStreamFile( $path, $content, $alreadyExists );
                $this->tearDownFiles();
+
+               $this->backend = $this->multiBackend;
+               $this->tearDownFiles();
+               $this->doTestStreamFile( $path, $content, $alreadyExists );
+               $this->tearDownFiles();
        }
 
        private function doTestStreamFile( $path, $content ) {
                $backendName = $this->backendClass();
 
-               // Test doStreamFile() directly to avoid header madness
-               $class = new ReflectionClass( $this->backend );
-               $method = $class->getMethod( 'doStreamFile' );
-               $method->setAccessible( true );
-
                if ( $content !== null ) {
                        $this->prepare( [ 'dir' => dirname( $path ) ] );
                        $status = $this->create( [ 'dst' => $path, 'content' => $content ] );
@@ -1156,18 +1156,19 @@ class FileBackendTest extends MediaWikiTestCase {
                                "Creation of file at $path succeeded ($backendName)." );
 
                        ob_start();
-                       $method->invokeArgs( $this->backend, [ [ 'src' => $path ] ] );
+                       $this->backend->streamFile( [ 'src' => $path, 'headless' => 1, 'allowOB' => 1 ] );
                        $data = ob_get_contents();
                        ob_end_clean();
 
                        $this->assertEquals( $content, $data, "Correct content streamed from '$path'" );
                } else { // 404 case
                        ob_start();
-                       $method->invokeArgs( $this->backend, [ [ 'src' => $path ] ] );
+                       $this->backend->streamFile( [ 'src' => $path, 'headless' => 1, 'allowOB' => 1 ] );
                        $data = ob_get_contents();
                        ob_end_clean();
 
-                       $this->assertEquals( '', $data, "Correct content streamed from '$path' ($backendName)" );
+                       $this->assertRegExp( '#<h1>File not found</h1>#', $data,
+                               "Correct content streamed from '$path' ($backendName)" );
                }
        }
 
@@ -1181,6 +1182,53 @@ class FileBackendTest extends MediaWikiTestCase {
                return $cases;
        }
 
+       public function testStreamFileRange() {
+               $this->backend = $this->singleBackend;
+               $this->tearDownFiles();
+               $this->doTestStreamFileRange();
+               $this->tearDownFiles();
+
+               $this->backend = $this->multiBackend;
+               $this->tearDownFiles();
+               $this->doTestStreamFileRange();
+               $this->tearDownFiles();
+       }
+
+       private function doTestStreamFileRange() {
+               $backendName = $this->backendClass();
+
+               $base = self::baseStorePath();
+               $path = "$base/unittest-cont1/e/b/z/range_file.txt";
+               $content = "0123456789ABCDEF";
+
+               $this->prepare( [ 'dir' => dirname( $path ) ] );
+               $status = $this->create( [ 'dst' => $path, 'content' => $content ] );
+               $this->assertGoodStatus( $status,
+                       "Creation of file at $path succeeded ($backendName)." );
+
+               static $ranges = [
+                       'bytes=0-0'   => '0',
+                       'bytes=0-3'   => '0123',
+                       'bytes=4-8'   => '45678',
+                       'bytes=15-15' => 'F',
+                       'bytes=14-15' => 'EF',
+                       'bytes=-5'    => 'BCDEF',
+                       'bytes=-1'    => 'F',
+                       'bytes=10-16' => 'ABCDEF',
+                       'bytes=10-99' => 'ABCDEF',
+               ];
+
+               foreach ( $ranges as $range => $chunk ) {
+                       ob_start();
+                       $this->backend->streamFile( [ 'src' => $path, 'headless' => 1, 'allowOB' => 1,
+                               'options' => [ 'range' => $range ] ] );
+                       $data = ob_get_contents();
+                       ob_end_clean();
+
+                       $this->assertEquals( $chunk, $data, "Correct chunk streamed from '$path' for '$range'" );
+               }
+       }
+
        /**
         * @dataProvider provider_testGetFileContents
         * @covers FileBackend::getFileContents
@@ -1516,7 +1564,7 @@ class FileBackendTest extends MediaWikiTestCase {
                        [ "$base/unittest-cont1/e/a/z/some_file1.txt", true ],
                        [ "$base/unittest-cont2/a/z/some_file2.txt", true ],
                        # Specific to FS backend with no basePath field set
-                       # array( "$base/unittest-cont3/a/z/some_file3.txt", false ),
+                       # [ "$base/unittest-cont3/a/z/some_file3.txt", false ],
                ];
        }
 
index c5fd369..6520610 100644 (file)
@@ -206,7 +206,7 @@ class FileTest extends MediaWikiMediaTestCase {
                        ] ],
                        [ [
                                'supportsBucketing' => true,
-                               'tmpBucketedThumbCache' => [ 1024 => '/tmp/shouldnotexist' + rand() ],
+                               'tmpBucketedThumbCache' => [ 1024 => '/tmp/shouldnotexist' . rand() ],
                                'thumbnailBucket' => 1024,
                                'physicalWidth' => 2048,
                                'expectedPath' => 'fsFilePath',
index 5e5e921..81c9faf 100644 (file)
@@ -23,10 +23,10 @@ class FakeDatabase extends DatabaseBase {
        function __construct() {
        }
 
-       function clearFlag( $arg ) {
+       function clearFlag( $arg, $remember = self::REMEMBER_NOTHING ) {
        }
 
-       function setFlag( $arg ) {
+       function setFlag( $arg, $remember = self::REMEMBER_NOTHING ) {
        }
 
        public function insert( $table, $a, $fname = __METHOD__, $options = [] ) {
@@ -124,7 +124,7 @@ class FakeDatabase extends DatabaseBase {
         *
         * Example:
         * $id = $dbw->nextSequenceValue( 'page_page_id_seq' );
-        * $dbw->insert( 'page', array( 'page_id' => $id ) );
+        * $dbw->insert( 'page', [ 'page_id' => $id ] );
         * $id = $dbw->insertId();
         *
         * @return int
index a4871a6..f8dda6f 100644 (file)
@@ -44,6 +44,7 @@ class ObjectFactoryTest extends PHPUnit_Framework_TestCase {
 
        /**
         * @covers ObjectFactory::getObjectFromSpec
+        * @covers ObjectFactory::expandClosures
         */
        public function testClosureExpansionEnabled() {
                $obj = ObjectFactory::getObjectFromSpec( [
@@ -80,6 +81,44 @@ class ObjectFactoryTest extends PHPUnit_Framework_TestCase {
                $this->assertSame( 'unwrapped', $obj->setterArgs[0] );
        }
 
+       /**
+        * @covers ObjectFactory::getObjectFromSpec
+        */
+       public function testGetObjectFromFactory() {
+               $args = [ 'a', 'b' ];
+               $obj = ObjectFactory::getObjectFromSpec( [
+                       'factory' => function ( $a, $b ) {
+                               return new ObjectFactoryTestFixture( $a, $b );
+                       },
+                       'args' => $args,
+               ] );
+               $this->assertSame( $args, $obj->args );
+       }
+
+       /**
+        * @covers ObjectFactory::getObjectFromSpec
+        * @expectedException InvalidArgumentException
+        */
+       public function testGetObjectFromInvalid() {
+               $args = [ 'a', 'b' ];
+               $obj = ObjectFactory::getObjectFromSpec( [
+                       // Missing 'class' or 'factory'
+                       'args' => $args,
+               ] );
+       }
+
+       /**
+        * @covers ObjectFactory::getObjectFromSpec
+        * @dataProvider provideConstructClassInstance
+        */
+       public function testGetObjectFromClass( $args ) {
+               $obj = ObjectFactory::getObjectFromSpec( [
+                       'class' => 'ObjectFactoryTestFixture',
+                       'args' => $args,
+               ] );
+               $this->assertSame( $args, $obj->args );
+       }
+
        /**
         * @covers ObjectFactory::constructClassInstance
         * @dataProvider provideConstructClassInstance
@@ -91,7 +130,7 @@ class ObjectFactoryTest extends PHPUnit_Framework_TestCase {
                $this->assertSame( $args, $obj->args );
        }
 
-       public function provideConstructClassInstance() {
+       public static function provideConstructClassInstance() {
                // These args go to 11. I thought about making 10 one louder, but 11!
                return [
                        '0 args' => [ [] ],
@@ -110,6 +149,7 @@ class ObjectFactoryTest extends PHPUnit_Framework_TestCase {
        }
 
        /**
+        * @covers ObjectFactory::constructClassInstance
         * @expectedException InvalidArgumentException
         */
        public function testNamedArgs() {
index 1ebe551..9a48930 100644 (file)
@@ -32,12 +32,35 @@ class SamplingStatsdClientTest extends PHPUnit_Framework_TestCase {
 
                return [
                        // $data, $sampleRate, $seed, $expectWrite
-                       [ $unsampled, 1, 0 /*0.44*/, $unsampled ],
-                       [ $sampled, 1, 0 /*0.44*/, null ],
-                       [ $sampled, 1, 4 /*0.03*/, $sampled ],
-                       [ $unsampled, 0.1, 4 /*0.03*/, $sampled ],
-                       [ $sampled, 0.5, 0 /*0.44*/, null ],
-                       [ $sampled, 0.5, 4 /*0.03*/, $sampled ],
+                       [ $unsampled, 1, 0 /*0.44*/, true ],
+                       [ $sampled, 1, 0 /*0.44*/, false ],
+                       [ $sampled, 1, 4 /*0.03*/, true ],
+                       [ $unsampled, 0.1, 0 /*0.44*/, false ],
+                       [ $sampled, 0.5, 0 /*0.44*/, false ],
+                       [ $sampled, 0.5, 4 /*0.03*/, false ],
                ];
        }
+
+       public function testSetSamplingRates() {
+               $matching = new StatsdData();
+               $matching->setKey( 'foo.bar' );
+               $matching->setValue( 1 );
+
+               $nonMatching = new StatsdData();
+               $nonMatching->setKey( 'oof.bar' );
+               $nonMatching->setValue( 1 );
+
+               $sender = $this->getMock( 'Liuggio\StatsdClient\Sender\SenderInterface' );
+               $sender->expects( $this->any() )->method( 'open' )->will( $this->returnValue( true ) );
+               $sender->expects( $this->once() )->method( 'write' )->with( $this->anything(),
+                       $this->equalTo( $nonMatching ) );
+
+               $client = new SamplingStatsdClient( $sender );
+               $client->setSamplingRates( [ 'foo.*' => 0.2 ] );
+
+               mt_srand( 0 ); // next random is 0.44
+               $client->send( $matching );
+               mt_srand( 0 );
+               $client->send( $nonMatching );
+       }
 }
diff --git a/tests/phpunit/includes/libs/WaitConditionLoopTest.php b/tests/phpunit/includes/libs/WaitConditionLoopTest.php
new file mode 100644 (file)
index 0000000..9ce93d6
--- /dev/null
@@ -0,0 +1,179 @@
+<?php
+
+class WaitConditionLoopFakeTime extends WaitConditionLoop {
+       protected $wallClock = 1;
+
+       function __construct( callable $condition, $timeout, array $busyCallbacks ) {
+               parent::__construct( $condition, $timeout, $busyCallbacks );
+       }
+
+       function usleep( $microseconds ) {
+               $this->wallClock += $microseconds / 1e6;
+       }
+
+       function getCpuTime() {
+               return 0.0;
+       }
+
+       function getWallTime() {
+               return $this->wallClock;
+       }
+
+       public function setWallClock( &$timestamp ) {
+               $this->wallClock =& $timestamp;
+       }
+}
+
+class WaitConditionLoopTest extends PHPUnit_Framework_TestCase {
+       public function testCallbackReached() {
+               $wallClock = microtime( true );
+
+               $count = 0;
+               $status = new StatusValue();
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$count, $status ) {
+                               ++$count;
+                               $status->value = 'cookie';
+
+                               return WaitConditionLoop::CONDITION_REACHED;
+                       },
+                       10.0,
+                       $this->newBusyWork( $x, $y, $z )
+               );
+               $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke() );
+               $this->assertEquals( 1, $count );
+               $this->assertEquals( 'cookie', $status->value );
+               $this->assertEquals( [ 0, 0, 0 ], [ $x, $y, $z ], "No busy work done" );
+
+               $count = 0;
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$count, &$wallClock ) {
+                               $wallClock += 1;
+                               ++$count;
+
+                               return $count >= 2 ? WaitConditionLoop::CONDITION_REACHED : false;
+                       },
+                       7.0,
+                       $this->newBusyWork( $x, $y, $z, $wallClock )
+               );
+               $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke(),
+                       "Busy work did not cause timeout" );
+               $this->assertEquals( [ 1, 0, 0 ], [ $x, $y, $z ] );
+
+               $count = 0;
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$count, &$wallClock ) {
+                               $wallClock += .1;
+                               ++$count;
+
+                               return $count > 80 ? true : false;
+                       },
+                       50.0,
+                       $this->newBusyWork( $x, $y, $z, $wallClock, $dontCallMe, $badCalls )
+               );
+               $this->assertEquals( 0, $badCalls, "Callback exception not yet called" );
+               $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke() );
+               $this->assertEquals( [ 1, 1, 1 ], [ $x, $y, $z ], "Busy work done" );
+               $this->assertEquals( 1, $badCalls, "Bad callback ran and was exception caught" );
+
+               try {
+                       $e = null;
+                       $dontCallMe();
+               } catch ( Exception $e ) {
+               }
+
+               $this->assertInstanceOf( 'RunTimeException', $e );
+               $this->assertEquals( 1, $badCalls, "Callback exception cached" );
+       }
+
+       public function testCallbackTimeout() {
+               $count = 0;
+               $wallClock = microtime( true );
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$count, &$wallClock ) {
+                               $wallClock += 3;
+                               ++$count;
+
+                               return $count > 300 ? true : false;
+                       },
+                       50.0,
+                       $this->newBusyWork( $x, $y, $z, $wallClock )
+               );
+               $loop->setWallClock( $wallClock );
+               $this->assertEquals( $loop::CONDITION_TIMED_OUT, $loop->invoke() );
+               $this->assertEquals( [ 1, 1, 1 ], [ $x, $y, $z ], "Busy work done" );
+
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$count, &$wallClock ) {
+                               $wallClock += 3;
+                               ++$count;
+
+                               return true;
+                       },
+                       0.0,
+                       $this->newBusyWork( $x, $y, $z, $wallClock )
+               );
+               $this->assertEquals( $loop::CONDITION_REACHED, $loop->invoke() );
+
+               $count = 0;
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$count, &$wallClock ) {
+                               $wallClock += 3;
+                               ++$count;
+
+                               return $count > 10 ? true : false;
+                       },
+                       0,
+                       $this->newBusyWork( $x, $y, $z, $wallClock )
+               );
+               $this->assertEquals( $loop::CONDITION_FAILED, $loop->invoke() );
+       }
+
+       public function testCallbackAborted() {
+               $x = 0;
+               $wallClock = microtime( true );
+               $loop = new WaitConditionLoopFakeTime(
+                       function () use ( &$x, &$wallClock ) {
+                               $wallClock += 2;
+                               ++$x;
+
+                               return $x > 2 ? WaitConditionLoop::CONDITION_ABORTED : false;
+                       },
+                       10.0,
+                       $this->newBusyWork( $x, $y, $z, $wallClock )
+               );
+               $loop->setWallClock( $wallClock );
+               $this->assertEquals( $loop::CONDITION_ABORTED, $loop->invoke() );
+       }
+
+       private function newBusyWork(
+               &$x, &$y, &$z, &$wallClock = 1, &$dontCallMe = null, &$badCalls = 0
+       ) {
+               $x = $y = $z = 0;
+               $badCalls = 0;
+
+               $list = [];
+               $list[] = function () use ( &$x, &$wallClock ) {
+                       $wallClock += 1;
+
+                       return ++$x;
+               };
+               $dontCallMe = function () use ( &$badCalls ) {
+                       ++$badCalls;
+                       throw new RuntimeException( "TrollyMcTrollFace" );
+               };
+               $list[] =& $dontCallMe;
+               $list[] = function () use ( &$y, &$wallClock ) {
+                       $wallClock += 15;
+
+                       return ++$y;
+               };
+               $list[] = function () use ( &$z, &$wallClock ) {
+                       $wallClock += 0.1;
+
+                       return ++$z;
+               };
+
+               return $list;
+       }
+}
index 80efcb3..7f9a772 100644 (file)
@@ -30,6 +30,32 @@ class XmlTypeCheckTest extends PHPUnit_Framework_TestCase {
                $this->assertFalse( $testXML->wellFormed );
        }
 
+       /**
+        * Verify we check for recursive entity DOS
+        *
+        * (If the DOS isn't properly handled, the test runner will probably go OOM...)
+        */
+       public function testRecursiveEntity() {
+               $xml = <<<'XML'
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE foo [
+       <!ENTITY test "&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;">
+       <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
+       <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
+       <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
+       <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
+       <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
+       <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
+       <!ENTITY g "-00000000000000000000000000000000000000000000000000000000000000000000000-">
+]>
+<foo>
+<bar>&test;</bar>
+</foo>
+XML;
+               $check = XmlTypeCheck::newFromString( $xml );
+               $this->assertFalse( $check->wellFormed );
+       }
+
        /**
         * @covers XMLTypeCheck::processingInstructionHandler
         */
index a8beb91..92fb954 100644 (file)
@@ -138,6 +138,20 @@ class BagOStuffTest extends MediaWikiTestCase {
                }
        }
 
+       /**
+        * @covers BagOStuff::changeTTL
+        */
+       public function testChangeTTL() {
+               $key = wfMemcKey( 'test' );
+               $value = 'meow';
+
+               $this->cache->add( $key, $value );
+               $this->assertTrue( $this->cache->changeTTL( $key, 5 ) );
+               $this->assertEquals( $this->cache->get( $key ), $value );
+               $this->cache->delete( $key );
+               $this->assertFalse( $this->cache->changeTTL( $key, 5 ) );
+       }
+
        /**
         * @covers BagOStuff::add
         */
index 7fe8055..a01cc6b 100644 (file)
@@ -5,6 +5,10 @@
  */
 class CachedBagOStuffTest extends PHPUnit_Framework_TestCase {
 
+       /**
+        * @covers CachedBagOStuff::__construct
+        * @covers CachedBagOStuff::doGet
+        */
        public function testGetFromBackend() {
                $backend = new HashBagOStuff;
                $cache = new CachedBagOStuff( $backend );
@@ -16,6 +20,10 @@ class CachedBagOStuffTest extends PHPUnit_Framework_TestCase {
                $this->assertEquals( 'bar', $cache->get( 'foo' ), 'cached' );
        }
 
+       /**
+        * @covers CachedBagOStuff::set
+        * @covers CachedBagOStuff::delete
+        */
        public function testSetAndDelete() {
                $backend = new HashBagOStuff;
                $cache = new CachedBagOStuff( $backend );
@@ -30,6 +38,10 @@ class CachedBagOStuffTest extends PHPUnit_Framework_TestCase {
                }
        }
 
+       /**
+        * @covers CachedBagOStuff::set
+        * @covers CachedBagOStuff::delete
+        */
        public function testWriteCacheOnly() {
                $backend = new HashBagOStuff;
                $cache = new CachedBagOStuff( $backend );
@@ -50,6 +62,9 @@ class CachedBagOStuffTest extends PHPUnit_Framework_TestCase {
                $this->assertEquals( 'old', $cache->get( 'foo' ) ); // Reloaded from backend
        }
 
+       /**
+        * @covers CachedBagOStuff::doGet
+        */
        public function testCacheBackendMisses() {
                $backend = new HashBagOStuff;
                $cache = new CachedBagOStuff( $backend );
index fce09ae..c4db0cf 100644 (file)
@@ -5,6 +5,9 @@
  */
 class HashBagOStuffTest extends PHPUnit_Framework_TestCase {
 
+       /**
+        * @covers HashBagOStuff::delete
+        */
        public function testDelete() {
                $cache = new HashBagOStuff();
                for ( $i = 0; $i < 10; $i++ ) {
@@ -15,6 +18,9 @@ class HashBagOStuffTest extends PHPUnit_Framework_TestCase {
                }
        }
 
+       /**
+        * @covers HashBagOStuff::clear
+        */
        public function testClear() {
                $cache = new HashBagOStuff();
                for ( $i = 0; $i < 10; $i++ ) {
@@ -27,6 +33,10 @@ class HashBagOStuffTest extends PHPUnit_Framework_TestCase {
                }
        }
 
+       /**
+        * @covers HashBagOStuff::doGet
+        * @covers HashBagOStuff::expire
+        */
        public function testExpire() {
                $cache = new HashBagOStuff();
                $cacheInternal = TestingAccessWrapper::newFromObject( $cache );
@@ -45,6 +55,9 @@ class HashBagOStuffTest extends PHPUnit_Framework_TestCase {
 
        /**
         * Ensure maxKeys eviction prefers keeping new keys.
+        *
+        * @covers HashBagOStuff::__construct
+        * @covers HashBagOStuff::set
         */
        public function testEvictionAdd() {
                $cache = new HashBagOStuff( [ 'maxKeys' => 10 ] );
@@ -62,6 +75,9 @@ class HashBagOStuffTest extends PHPUnit_Framework_TestCase {
        /**
         * Ensure maxKeys eviction prefers recently set keys
         * even if the keys pre-exist.
+        *
+        * @covers HashBagOStuff::__construct
+        * @covers HashBagOStuff::set
         */
        public function testEvictionSet() {
                $cache = new HashBagOStuff( [ 'maxKeys' => 3 ] );
@@ -85,6 +101,10 @@ class HashBagOStuffTest extends PHPUnit_Framework_TestCase {
 
        /**
         * Ensure maxKeys eviction prefers recently retrieved keys (LRU).
+        *
+        * @covers HashBagOStuff::__construct
+        * @covers HashBagOStuff::doGet
+        * @covers HashBagOStuff::hasKey
         */
        public function testEvictionGet() {
                $cache = new HashBagOStuff( [ 'maxKeys' => 3 ] );
index 6df74d6..38d63e3 100644 (file)
@@ -23,6 +23,10 @@ class MultiWriteBagOStuffTest extends MediaWikiTestCase {
                ] );
        }
 
+       /**
+        * @covers MultiWriteBagOStuff::set
+        * @covers MultiWriteBagOStuff::doWrite
+        */
        public function testSetImmediate() {
                $key = wfRandomString();
                $value = wfRandomString();
@@ -34,6 +38,9 @@ class MultiWriteBagOStuffTest extends MediaWikiTestCase {
                $this->assertEquals( $value, $this->cache2->get( $key ), 'Written to tier 2' );
        }
 
+       /**
+        * @covers MultiWriteBagOStuff
+        */
        public function testSyncMerge() {
                $key = wfRandomString();
                $value = wfRandomString();
@@ -69,6 +76,9 @@ class MultiWriteBagOStuffTest extends MediaWikiTestCase {
                $dbw->commit();
        }
 
+       /**
+        * @covers MultiWriteBagOStuff::set
+        */
        public function testSetDelayed() {
                $key = wfRandomString();
                $value = wfRandomString();
index 5bc1c8d..99b959b 100644 (file)
@@ -105,6 +105,47 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $this->assertFalse( $this->cache->get( $key ), "Stale set() value ignored" );
        }
 
+       public function testProcessCache() {
+               $hit = 0;
+               $callback = function () use ( &$hit ) {
+                       ++$hit;
+                       return 42;
+               };
+               $keys = [ wfRandomString(), wfRandomString(), wfRandomString() ];
+               $groups = [ 'thiscache:1', 'thatcache:1', 'somecache:1' ];
+
+               foreach ( $keys as $i => $key ) {
+                       $this->cache->getWithSetCallback(
+                               $key, 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               }
+               $this->assertEquals( 3, $hit );
+
+               foreach ( $keys as $i => $key ) {
+                       $this->cache->getWithSetCallback(
+                               $key, 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               }
+               $this->assertEquals( 3, $hit, "Values cached" );
+
+               foreach ( $keys as $i => $key ) {
+                       $this->cache->getWithSetCallback(
+                               "$key-2", 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               }
+               $this->assertEquals( 6, $hit );
+
+               foreach ( $keys as $i => $key ) {
+                       $this->cache->getWithSetCallback(
+                               "$key-2", 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               }
+               $this->assertEquals( 6, $hit, "New values cached" );
+
+               foreach ( $keys as $i => $key ) {
+                       $this->cache->delete( $key );
+                       $this->cache->getWithSetCallback(
+                               $key, 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               }
+               $this->assertEquals( 9, $hit, "Values evicted" );
+       }
+
        /**
         * @dataProvider getWithSetCallback_provider
         * @covers WANObjectCache::getWithSetCallback()
@@ -120,9 +161,15 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $cKey1 = wfRandomString();
                $cKey2 = wfRandomString();
 
+               $priorValue = null;
+               $priorAsOf = null;
                $wasSet = 0;
-               $func = function( $old, &$ttl ) use ( &$wasSet, $value ) {
+               $func = function( $old, &$ttl, &$opts, $asOf )
+                       use ( &$wasSet, &$priorValue, &$priorAsOf, $value )
+               {
                        ++$wasSet;
+                       $priorValue = $old;
+                       $priorAsOf = $asOf;
                        $ttl = 20; // override with another value
                        return $value;
                };
@@ -131,6 +178,8 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $v = $cache->getWithSetCallback( $key, 30, $func, [ 'lockTSE' => 5 ] + $extOpts );
                $this->assertEquals( $value, $v, "Value returned" );
                $this->assertEquals( 1, $wasSet, "Value regenerated" );
+               $this->assertFalse( $priorValue, "No prior value" );
+               $this->assertNull( $priorAsOf, "No prior value" );
 
                $curTTL = null;
                $cache->get( $key, $curTTL );
@@ -153,6 +202,8 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                );
                $this->assertEquals( $value, $v, "Value returned" );
                $this->assertEquals( 1, $wasSet, "Value regenerated due to check keys" );
+               $this->assertEquals( $value, $priorValue, "Has prior value" );
+               $this->assertType( 'float', $priorAsOf, "Has prior value" );
                $t1 = $cache->getCheckKeyTime( $cKey1 );
                $this->assertGreaterThanOrEqual( $priorTime, $t1, 'Check keys generated on miss' );
                $t2 = $cache->getCheckKeyTime( $cKey2 );
@@ -206,8 +257,10 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $value = wfRandomString();
 
                $calls = 0;
-               $func = function() use ( &$calls, $value ) {
+               $func = function() use ( &$calls, $value, $cache, $key ) {
                        ++$calls;
+                       // Immediately kill any mutex rather than waiting a second
+                       $cache->delete( $cache::MUTEX_KEY_PREFIX . $key );
                        return $value;
                };
 
@@ -216,23 +269,23 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $this->assertEquals( 1, $calls, 'Value was populated' );
 
                // Acquire a lock to verify that getWithSetCallback uses lockTSE properly
-               $this->internalCache->lock( $key, 0 );
+               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
 
                $checkKeys = [ wfRandomString() ]; // new check keys => force misses
                $ret = $cache->getWithSetCallback( $key, 30, $func,
                        [ 'lockTSE' => 5, 'checkKeys' => $checkKeys ] );
-               $this->assertEquals( $value, $ret );
+               $this->assertEquals( $value, $ret, 'Old value used' );
                $this->assertEquals( 1, $calls, 'Callback was not used' );
 
                $cache->delete( $key );
                $ret = $cache->getWithSetCallback( $key, 30, $func,
-                       [ 'lockTSE' => 5, 'checkKeys' => $checkKeys ] ); // should use interim value
-               $this->assertEquals( $value, $ret );
-               $this->assertEquals( 2, $calls, 'Callback was used' );
+                       [ 'lockTSE' => 5, 'checkKeys' => $checkKeys ] );
+               $this->assertEquals( $value, $ret, 'Callback was used; interim saved' );
+               $this->assertEquals( 2, $calls, 'Callback was used; interim saved' );
 
                $ret = $cache->getWithSetCallback( $key, 30, $func,
                        [ 'lockTSE' => 5, 'checkKeys' => $checkKeys ] );
-               $this->assertEquals( $value, $ret );
+               $this->assertEquals( $value, $ret, 'Callback was not used; used interim' );
                $this->assertEquals( 2, $calls, 'Callback was not used; used interim' );
        }
 
@@ -246,9 +299,11 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $value = wfRandomString();
 
                $calls = 0;
-               $func = function( $oldValue, &$ttl, &$setOpts ) use ( &$calls, $value ) {
+               $func = function( $oldValue, &$ttl, &$setOpts ) use ( &$calls, $value, $cache, $key ) {
                        ++$calls;
                        $setOpts['since'] = microtime( true ) - 10;
+                       // Immediately kill any mutex rather than waiting a second
+                       $cache->delete( $cache::MUTEX_KEY_PREFIX . $key );
                        return $value;
                };
 
@@ -261,12 +316,67 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $this->assertEquals( 1, $calls, 'Value was generated' );
 
                // Acquire a lock to verify that getWithSetCallback uses lockTSE properly
-               $this->internalCache->lock( $key, 0 );
+               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
                $ret = $cache->getWithSetCallback( $key, 30, $func, [ 'lockTSE' => 5 ] );
                $this->assertEquals( $value, $ret );
                $this->assertEquals( 1, $calls, 'Callback was not used' );
        }
 
+       /**
+        * @covers WANObjectCache::getWithSetCallback()
+        * @covers WANObjectCache::doGetWithSetCallback()
+        */
+       public function testBusyValue() {
+               $cache = $this->cache;
+               $key = wfRandomString();
+               $value = wfRandomString();
+               $busyValue = wfRandomString();
+
+               $calls = 0;
+               $func = function() use ( &$calls, $value, $cache, $key ) {
+                       ++$calls;
+                       // Immediately kill any mutex rather than waiting a second
+                       $cache->delete( $cache::MUTEX_KEY_PREFIX . $key );
+                       return $value;
+               };
+
+               $ret = $cache->getWithSetCallback( $key, 30, $func, [ 'busyValue' => $busyValue ] );
+               $this->assertEquals( $value, $ret );
+               $this->assertEquals( 1, $calls, 'Value was populated' );
+
+               // Acquire a lock to verify that getWithSetCallback uses busyValue properly
+               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+
+               $checkKeys = [ wfRandomString() ]; // new check keys => force misses
+               $ret = $cache->getWithSetCallback( $key, 30, $func,
+                       [ 'busyValue' => $busyValue, 'checkKeys' => $checkKeys ] );
+               $this->assertEquals( $value, $ret, 'Callback used' );
+               $this->assertEquals( 2, $calls, 'Callback used' );
+
+               $ret = $cache->getWithSetCallback( $key, 30, $func,
+                       [ 'lockTSE' => 30, 'busyValue' => $busyValue, 'checkKeys' => $checkKeys ] );
+               $this->assertEquals( $value, $ret, 'Old value used' );
+               $this->assertEquals( 2, $calls, 'Callback was not used' );
+
+               $cache->delete( $key ); // no value at all anymore and still locked
+               $ret = $cache->getWithSetCallback( $key, 30, $func,
+                       [ 'busyValue' => $busyValue, 'checkKeys' => $checkKeys ] );
+               $this->assertEquals( $busyValue, $ret, 'Callback was not used; used busy value' );
+               $this->assertEquals( 2, $calls, 'Callback was not used; used busy value' );
+
+               $this->internalCache->delete( $cache::MUTEX_KEY_PREFIX . $key );
+               $ret = $cache->getWithSetCallback( $key, 30, $func,
+                       [ 'lockTSE' => 30, 'busyValue' => $busyValue, 'checkKeys' => $checkKeys ] );
+               $this->assertEquals( $value, $ret, 'Callback was used; saved interim' );
+               $this->assertEquals( 3, $calls, 'Callback was used; saved interim' );
+
+               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $ret = $cache->getWithSetCallback( $key, 30, $func,
+                       [ 'busyValue' => $busyValue, 'checkKeys' => $checkKeys ] );
+               $this->assertEquals( $value, $ret, 'Callback was not used; used interim' );
+               $this->assertEquals( 3, $calls, 'Callback was not used; used interim' );
+       }
+
        /**
         * @covers WANObjectCache::getMulti()
         */
@@ -641,4 +751,54 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $this->cache->set( $key, $value, 30, $opts );
                $this->assertEquals( false, $this->cache->get( $key ), "Pending value not written." );
        }
+
+       public function testMcRouterSupport() {
+               $localBag = $this->getMock( 'EmptyBagOStuff', [ 'set', 'delete' ] );
+               $localBag->expects( $this->never() )->method( 'set' );
+               $localBag->expects( $this->never() )->method( 'delete' );
+               $wanCache = new WANObjectCache( [
+                       'cache' => $localBag,
+                       'pool' => 'testcache-hash',
+                       'relayer' => new EventRelayerNull( [] )
+               ] );
+               $valFunc = function () {
+                       return 1;
+               };
+
+               // None of these should use broadcasting commands (e.g. SET, DELETE)
+               $wanCache->get( 'x' );
+               $wanCache->get( 'x', $ctl, [ 'check1' ] );
+               $wanCache->getMulti( [ 'x', 'y' ] );
+               $wanCache->getMulti( [ 'x', 'y' ], $ctls, [ 'check2' ] );
+               $wanCache->getWithSetCallback( 'p', 30, $valFunc );
+               $wanCache->getCheckKeyTime( 'zzz' );
+       }
+
+       /**
+        * @dataProvider provideAdaptiveTTL
+        * @covers WANObjectCache::adaptiveTTL()
+        */
+       public function testAdaptiveTTL( $ago, $maxTTL, $minTTL, $factor, $adaptiveTTL ) {
+               $mtime = is_int( $ago ) ? time() - $ago : $ago;
+               $margin = 5;
+               $ttl = $this->cache->adaptiveTTL( $mtime, $maxTTL, $minTTL, $factor );
+
+               $this->assertGreaterThanOrEqual( $adaptiveTTL - $margin, $ttl );
+               $this->assertLessThanOrEqual( $adaptiveTTL + $margin, $ttl );
+
+               $ttl = $this->cache->adaptiveTTL( (string)$mtime, $maxTTL, $minTTL, $factor );
+
+               $this->assertGreaterThanOrEqual( $adaptiveTTL - $margin, $ttl );
+               $this->assertLessThanOrEqual( $adaptiveTTL + $margin, $ttl );
+       }
+
+       public static function provideAdaptiveTTL() {
+               return [
+                       [ 3600, 900, 30, .2, 720 ],
+                       [ 3600, 500, 30, .2, 500 ],
+                       [ 3600, 86400, 800, .2, 800 ],
+                       [ false, 86400, 800, .2, 800 ],
+                       [ null, 86400, 800, .2, 800 ]
+               ];
+       }
 }
index ed28b79..bf12f80 100644 (file)
@@ -14,9 +14,15 @@ class LinkRendererFactoryTest extends MediaWikiLangTestCase {
         */
        private $titleFormatter;
 
+       /**
+        * @var LinkCache
+        */
+       private $linkCache;
+
        public function setUp() {
                parent::setUp();
                $this->titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
+               $this->linkCache = MediaWikiServices::getInstance()->getLinkCache();
        }
 
        public static function provideCreateFromLegacyOptions() {
@@ -48,7 +54,7 @@ class LinkRendererFactoryTest extends MediaWikiLangTestCase {
         * @dataProvider provideCreateFromLegacyOptions
         */
        public function testCreateFromLegacyOptions( $options, $func, $val ) {
-               $factory = new LinkRendererFactory( $this->titleFormatter );
+               $factory = new LinkRendererFactory( $this->titleFormatter, $this->linkCache );
                $linkRenderer = $factory->createFromLegacyOptions(
                        $options
                );
@@ -57,16 +63,17 @@ class LinkRendererFactoryTest extends MediaWikiLangTestCase {
        }
 
        public function testCreate() {
-               $factory = new LinkRendererFactory( $this->titleFormatter );
+               $factory = new LinkRendererFactory( $this->titleFormatter, $this->linkCache );
                $this->assertInstanceOf( LinkRenderer::class, $factory->create() );
        }
 
        public function testCreateForUser() {
+               /** @var PHPUnit_Framework_MockObject_MockObject|User $user */
                $user = $this->getMock( User::class, [ 'getStubThreshold' ] );
                $user->expects( $this->once() )
                        ->method( 'getStubThreshold' )
                        ->willReturn( 15 );
-               $factory = new LinkRendererFactory( $this->titleFormatter );
+               $factory = new LinkRendererFactory( $this->titleFormatter, $this->linkCache );
                $linkRenderer = $factory->createForUser( $user );
                $this->assertInstanceOf( LinkRenderer::class, $linkRenderer );
                $this->assertEquals( 15, $linkRenderer->getStubThreshold() );
index b78592b..70c0ece 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 
 use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\Linker\LinkRendererFactory;
 use MediaWiki\MediaWikiServices;
 
 /**
@@ -9,9 +10,9 @@ use MediaWiki\MediaWikiServices;
 class LinkRendererTest extends MediaWikiLangTestCase {
 
        /**
-        * @var TitleFormatter
+        * @var LinkRendererFactory
         */
-       private $titleFormatter;
+       private $factory;
 
        public function setUp() {
                parent::setUp();
@@ -22,12 +23,13 @@ class LinkRendererTest extends MediaWikiLangTestCase {
                        'wgScriptPath' => '/w',
                        'wgScript' => '/w/index.php',
                ] );
-               $this->titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
+               $this->factory = MediaWikiServices::getInstance()->getLinkRendererFactory();
+
        }
 
        public function testMergeAttribs() {
                $target = new TitleValue( NS_SPECIAL, 'Blankpage' );
-               $linkRenderer = new LinkRenderer( $this->titleFormatter );
+               $linkRenderer = $this->factory->create();
                $link = $linkRenderer->makeBrokenLink( $target, null, [
                        // Appended to class
                        'class' => 'foobar',
@@ -46,7 +48,7 @@ class LinkRendererTest extends MediaWikiLangTestCase {
 
        public function testMakeKnownLink() {
                $target = new TitleValue( NS_MAIN, 'Foobar' );
-               $linkRenderer = new LinkRenderer( $this->titleFormatter );
+               $linkRenderer = $this->factory->create();
 
                // Query added
                $this->assertEquals(
@@ -73,7 +75,7 @@ class LinkRendererTest extends MediaWikiLangTestCase {
        public function testMakeBrokenLink() {
                $target = new TitleValue( NS_MAIN, 'Foobar' );
                $special = new TitleValue( NS_SPECIAL, 'Foobar' );
-               $linkRenderer = new LinkRenderer( $this->titleFormatter );
+               $linkRenderer = $this->factory->create();
 
                // action=edit&redlink=1 added
                $this->assertEquals(
@@ -105,7 +107,7 @@ class LinkRendererTest extends MediaWikiLangTestCase {
        }
 
        public function testMakeLink() {
-               $linkRenderer = new LinkRenderer( $this->titleFormatter );
+               $linkRenderer = $this->factory->create();
                $foobar = new TitleValue( NS_SPECIAL, 'Foobar' );
                $blankpage = new TitleValue( NS_SPECIAL, 'Blankpage' );
                $this->assertEquals(
@@ -131,4 +133,58 @@ class LinkRendererTest extends MediaWikiLangTestCase {
                        $linkRenderer->makeLink( $foobar, new HtmlArmor( '<script>evil()</script>' ) )
                );
        }
+
+       public function testGetLinkClasses() {
+               $wanCache = ObjectCache::getMainWANInstance();
+               $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
+               $linkCache = new LinkCache( $titleFormatter, $wanCache );
+               $foobarTitle = new TitleValue( NS_MAIN, 'FooBar' );
+               $redirectTitle = new TitleValue( NS_MAIN, 'Redirect' );
+               $userTitle = new TitleValue( NS_USER, 'Someuser' );
+               $linkCache->addGoodLinkObj(
+                       1, // id
+                       $foobarTitle,
+                       10, // len
+                       0 // redir
+               );
+               $linkCache->addGoodLinkObj(
+                       2, // id
+                       $redirectTitle,
+                       10, // len
+                       1 // redir
+               );
+
+               $linkCache->addGoodLinkObj(
+                       3, // id
+                       $userTitle,
+                       10, // len
+                       0 // redir
+               );
+
+               $linkRenderer = new LinkRenderer( $titleFormatter, $linkCache );
+               $linkRenderer->setStubThreshold( 0 );
+               $this->assertEquals(
+                       '',
+                       $linkRenderer->getLinkClasses( $foobarTitle )
+               );
+
+               $linkRenderer->setStubThreshold( 20 );
+               $this->assertEquals(
+                       'stub',
+                       $linkRenderer->getLinkClasses( $foobarTitle )
+               );
+
+               $linkRenderer->setStubThreshold( 0 );
+               $this->assertEquals(
+                       'mw-redirect',
+                       $linkRenderer->getLinkClasses( $redirectTitle )
+               );
+
+               $linkRenderer->setStubThreshold( 20 );
+               $this->assertEquals(
+                       '',
+                       $linkRenderer->getLinkClasses( $userTitle )
+               );
+       }
+
 }
index b09e5b1..c289839 100644 (file)
@@ -50,7 +50,7 @@ abstract class LogFormatterTestCase extends MediaWikiLangTestCase {
        private static function removeSomeHtml( $html ) {
                $html = str_replace( '&quot;', '"', $html );
                $html = preg_replace( '/\xE2\x80[\x8E\x8F]/', '', $html ); // Strip lrm/rlm
-               return trim( preg_replace( '/<(a|span)[^>]*>([^<]*)<\/\1>/', '$2', $html ) );
+               return trim( strip_tags( $html ) );
        }
 
        private static function removeApiMetaData( $val ) {
diff --git a/tests/phpunit/includes/objectcache/RESTBagOStuffTest.php b/tests/phpunit/includes/objectcache/RESTBagOStuffTest.php
new file mode 100644 (file)
index 0000000..ebeb109
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * @group BagOStuff
+ */
+class RESTBagOStuffTest extends MediaWikiTestCase {
+
+       /**
+        * @var MultiHttpClient
+        */
+       private $client;
+       /**
+        * @var RESTBagOStuff
+        */
+       private $bag;
+
+       public function setUp() {
+               parent::setUp();
+               $this->client =
+                       $this->getMockBuilder( 'MultiHttpClient' )
+                               ->setConstructorArgs( [ [] ] )
+                               ->setMethods( [ 'run' ] )
+                               ->getMock();
+               $this->bag = new RESTBagOStuff( [ 'client' => $this->client, 'url' => 'http://test/rest/' ] );
+       }
+
+       public function testGet() {
+               $this->client->expects( $this->once() )->method( 'run' )->with( [
+                       'method' => 'GET',
+                       'url' => 'http://test/rest/42xyz42'
+                   // list( $rcode, $rdesc, $rhdrs, $rbody, $rerr )
+               ] )->willReturn( [ 200, 'OK', [], 's:8:"somedata";', 0 ] );
+               $result = $this->bag->get( '42xyz42' );
+               $this->assertEquals( 'somedata', $result );
+       }
+
+       public function testGetNotExist() {
+               $this->client->expects( $this->once() )->method( 'run' )->with( [
+                       'method' => 'GET',
+                       'url' => 'http://test/rest/42xyz42'
+                       // list( $rcode, $rdesc, $rhdrs, $rbody, $rerr )
+               ] )->willReturn( [ 404, 'Not found', [], 'Nothing to see here', 0 ] );
+               $result = $this->bag->get( '42xyz42' );
+               $this->assertFalse( $result );
+       }
+
+       public function testGetBadClient() {
+               $this->client->expects( $this->once() )->method( 'run' )->with( [
+                       'method' => 'GET',
+                       'url' => 'http://test/rest/42xyz42'
+                       // list( $rcode, $rdesc, $rhdrs, $rbody, $rerr )
+               ] )->willReturn( [ 0, '', [], '', 'cURL has failed you today' ] );
+               $result = $this->bag->get( '42xyz42' );
+               $this->assertFalse( $result );
+               $this->assertEquals( BagOStuff::ERR_UNREACHABLE, $this->bag->getLastError() );
+       }
+
+       public function testGetBadServer() {
+               $this->client->expects( $this->once() )->method( 'run' )->with( [
+                       'method' => 'GET',
+                       'url' => 'http://test/rest/42xyz42'
+                       // list( $rcode, $rdesc, $rhdrs, $rbody, $rerr )
+               ] )->willReturn( [ 500, 'Too busy', [], 'Server is too busy', '' ] );
+               $result = $this->bag->get( '42xyz42' );
+               $this->assertFalse( $result );
+               $this->assertEquals( BagOStuff::ERR_UNEXPECTED, $this->bag->getLastError() );
+       }
+
+       public function testPut() {
+               $this->client->expects( $this->once() )->method( 'run' )->with( [
+                       'method' => 'PUT',
+                       'url' => 'http://test/rest/42xyz42',
+                   'body' => 's:8:"postdata";'
+                       // list( $rcode, $rdesc, $rhdrs, $rbody, $rerr )
+               ] )->willReturn( [ 200, 'OK', [], 'Done', 0 ] );
+               $result = $this->bag->set( '42xyz42', 'postdata' );
+               $this->assertTrue( $result );
+       }
+
+       public function testDelete() {
+               $this->client->expects( $this->once() )->method( 'run' )->with( [
+                       'method' => 'DELETE',
+                       'url' => 'http://test/rest/42xyz42',
+                       // list( $rcode, $rdesc, $rhdrs, $rbody, $rerr )
+               ] )->willReturn( [ 200, 'OK', [], 'Done', 0 ] );
+               $result = $this->bag->delete( '42xyz42' );
+               $this->assertTrue( $result );
+       }
+}
index cf87a98..705a34a 100644 (file)
@@ -2,13 +2,16 @@
 /**
  * @group BagOStuff
  */
-class RedisBagOStuffTest extends MediaWikiTestCase {
+class RedisBagOStuffTest extends PHPUnit_Framework_TestCase {
        /** @var RedisBagOStuff */
        private $cache;
 
        protected function setUp() {
                parent::setUp();
-               $this->cache = TestingAccessWrapper::newFromObject( new RedisBagOStuff( [ 'servers' => [] ] ) );
+               $cache = $this->getMockBuilder( 'RedisBagOStuff' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $this->cache = TestingAccessWrapper::newFromObject( $cache );
        }
 
        /**
diff --git a/tests/phpunit/includes/parser/MediaWikiParserTest.php b/tests/phpunit/includes/parser/MediaWikiParserTest.php
deleted file mode 100644 (file)
index 173447f..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-require_once __DIR__ . '/NewParserTest.php';
-
-/**
- * The UnitTest must be either a class that inherits from MediaWikiTestCase
- * or a class that provides a public static suite() method which returns
- * an PHPUnit_Framework_Test object
- *
- * @group Parser
- * @group ParserTests
- * @group Database
- */
-class MediaWikiParserTest {
-
-       /**
-        * @defgroup filtering_constants Filtering constants
-        *
-        * Limit inclusion of parser tests files coming from MediaWiki core
-        * @{
-        */
-
-       /** Include files shipped with MediaWiki core */
-       const CORE_ONLY = 1;
-       /** Include non core files as set in $wgParserTestFiles */
-       const NO_CORE = 2;
-       /** Include anything set via $wgParserTestFiles */
-       const WITH_ALL = 3; # CORE_ONLY | NO_CORE
-
-       /** @} */
-
-       /**
-        * Get a PHPUnit test suite of parser tests. Optionally filtered with
-        * $flags.
-        *
-        * @par Examples:
-        * Get a suite of parser tests shipped by MediaWiki core:
-        * @code
-        * MediaWikiParserTest::suite( MediaWikiParserTest::CORE_ONLY );
-        * @endcode
-        * Get a suite of various parser tests, like extensions:
-        * @code
-        * MediaWikiParserTest::suite( MediaWikiParserTest::NO_CORE );
-        * @endcode
-        * Get any test defined via $wgParserTestFiles:
-        * @code
-        * MediaWikiParserTest::suite( MediaWikiParserTest::WITH_ALL );
-        * @endcode
-        *
-        * @param int $flags Bitwise flag to filter out the $wgParserTestFiles that
-        * will be included.  Default: MediaWikiParserTest::CORE_ONLY
-        *
-        * @return PHPUnit_Framework_TestSuite
-        */
-       public static function suite( $flags = self::CORE_ONLY ) {
-               if ( is_string( $flags ) ) {
-                       $flags = self::CORE_ONLY;
-               }
-               global $wgParserTestFiles, $IP;
-
-               $mwTestDir = $IP . '/tests/';
-
-               # Human friendly helpers
-               $wantsCore = ( $flags & self::CORE_ONLY );
-               $wantsRest = ( $flags & self::NO_CORE );
-
-               # Will hold the .txt parser test files we will include
-               $filesToTest = [];
-
-               # Filter out .txt files
-               foreach ( $wgParserTestFiles as $parserTestFile ) {
-                       $isCore = ( 0 === strpos( $parserTestFile, $mwTestDir ) );
-
-                       if ( $isCore && $wantsCore ) {
-                               self::debug( "included core parser tests: $parserTestFile" );
-                               $filesToTest[] = $parserTestFile;
-                       } elseif ( !$isCore && $wantsRest ) {
-                               self::debug( "included non core parser tests: $parserTestFile" );
-                               $filesToTest[] = $parserTestFile;
-                       } else {
-                               self::debug( "skipped parser tests: $parserTestFile" );
-                       }
-               }
-               self::debug( 'parser tests files: '
-                       . implode( ' ', $filesToTest ) );
-
-               $suite = new PHPUnit_Framework_TestSuite;
-               $testList = [];
-               $counter = 0;
-               foreach ( $filesToTest as $fileName ) {
-                       // Call the highest level directory the extension name.
-                       // It may or may not actually be, but it should be close
-                       // enough to cause there to be separate names for different
-                       // things, which is good enough for our purposes.
-                       $extensionName = basename( dirname( $fileName ) );
-                       $testsName = $extensionName . '__' . basename( $fileName, '.txt' );
-                       $escapedFileName = strtr( $fileName, [ "'" => "\\'", '\\' => '\\\\' ] );
-                       $parserTestClassName = ucfirst( $testsName );
-
-                       // Official spec for class names: http://php.net/manual/en/language.oop5.basic.php
-                       // Prepend 'ParserTest_' to be paranoid about it not starting with a number
-                       $parserTestClassName = 'ParserTest_' .
-                               preg_replace( '/[^a-zA-Z0-9_\x7f-\xff]/', '_', $parserTestClassName );
-
-                       if ( isset( $testList[$parserTestClassName] ) ) {
-                               // If a conflict happens, gives a very unclear fatal.
-                               // So as a last ditch effort to prevent that eventuality, if there
-                               // is a conflict, append a number.
-                               $counter++;
-                               $parserTestClassName .= $counter;
-                       }
-                       $testList[$parserTestClassName] = true;
-                       $parserTestClassDefinition = <<<EOT
-/**
- * @group Database
- * @group Parser
- * @group ParserTests
- * @group ParserTests_$parserTestClassName
- */
-class $parserTestClassName extends NewParserTest {
-       protected \$file = '$escapedFileName';
-}
-EOT;
-
-                       eval( $parserTestClassDefinition );
-                       self::debug( "Adding test class $parserTestClassName" );
-                       $suite->addTestSuite( $parserTestClassName );
-               }
-               return $suite;
-       }
-
-       /**
-        * Write $msg under log group 'tests-parser'
-        * @param string $msg Message to log
-        */
-       protected static function debug( $msg ) {
-               return wfDebugLog( 'tests-parser', wfGetCaller() . ' ' . $msg );
-       }
-}
diff --git a/tests/phpunit/includes/parser/NewParserTest.php b/tests/phpunit/includes/parser/NewParserTest.php
deleted file mode 100644 (file)
index 8512572..0000000
+++ /dev/null
@@ -1,1140 +0,0 @@
-<?php
-/**
- * Although marked as a stub, can work independently.
- *
- * @group Database
- * @group Parser
- * @group Stub
- *
- * @todo covers tags
- */
-class NewParserTest extends MediaWikiTestCase {
-       static protected $articles = []; // Array of test articles defined by the tests
-       /* The data provider is run on a different instance than the test, so it must be static
-        * When running tests from several files, all tests will see all articles.
-        */
-       static protected $backendToUse;
-
-       public $keepUploads = false;
-       public $runDisabled = false;
-       public $runParsoid = false;
-       public $regex = '';
-       public $showProgress = true;
-       public $savedWeirdGlobals = [];
-       public $savedGlobals = [];
-       public $hooks = [];
-       public $functionHooks = [];
-       public $transparentHooks = [];
-
-       // Fuzz test
-       public $maxFuzzTestLength = 300;
-       public $fuzzSeed = 0;
-       public $memoryLimit = 50;
-
-       /**
-        * @var DjVuSupport
-        */
-       private $djVuSupport;
-       /**
-        * @var TidySupport
-        */
-       private $tidySupport;
-
-       protected $file = false;
-
-       public static function setUpBeforeClass() {
-               // Inject ParserTest well-known interwikis
-               ParserTest::setupInterwikis();
-       }
-
-       protected function setUp() {
-               global $wgNamespaceAliases, $wgContLang;
-               global $wgHooks, $IP;
-
-               parent::setUp();
-
-               // Setup CLI arguments
-               if ( $this->getCliArg( 'regex' ) ) {
-                       $this->regex = $this->getCliArg( 'regex' );
-               } else {
-                       # Matches anything
-                       $this->regex = '';
-               }
-
-               $this->keepUploads = $this->getCliArg( 'keep-uploads' );
-
-               $tmpGlobals = [];
-
-               $tmpGlobals['wgLanguageCode'] = 'en';
-               $tmpGlobals['wgContLang'] = Language::factory( 'en' );
-               $tmpGlobals['wgSitename'] = 'MediaWiki';
-               $tmpGlobals['wgServer'] = 'http://example.org';
-               $tmpGlobals['wgServerName'] = 'example.org';
-               $tmpGlobals['wgScriptPath'] = '';
-               $tmpGlobals['wgScript'] = '/index.php';
-               $tmpGlobals['wgResourceBasePath'] = '';
-               $tmpGlobals['wgStylePath'] = '/skins';
-               $tmpGlobals['wgExtensionAssetsPath'] = '/extensions';
-               $tmpGlobals['wgArticlePath'] = '/wiki/$1';
-               $tmpGlobals['wgActionPaths'] = [];
-               $tmpGlobals['wgVariantArticlePath'] = false;
-               $tmpGlobals['wgEnableUploads'] = true;
-               $tmpGlobals['wgUploadNavigationUrl'] = false;
-               $tmpGlobals['wgThumbnailScriptPath'] = false;
-               $tmpGlobals['wgLocalFileRepo'] = [
-                       'class' => 'LocalRepo',
-                       'name' => 'local',
-                       'url' => 'http://example.com/images',
-                       'hashLevels' => 2,
-                       'transformVia404' => false,
-                       'backend' => 'local-backend'
-               ];
-               $tmpGlobals['wgForeignFileRepos'] = [];
-               $tmpGlobals['wgDefaultExternalStore'] = [];
-               $tmpGlobals['wgParserCacheType'] = CACHE_NONE;
-               $tmpGlobals['wgCapitalLinks'] = true;
-               $tmpGlobals['wgNoFollowLinks'] = true;
-               $tmpGlobals['wgNoFollowDomainExceptions'] = [ 'no-nofollow.org' ];
-               $tmpGlobals['wgExternalLinkTarget'] = false;
-               $tmpGlobals['wgThumbnailScriptPath'] = false;
-               $tmpGlobals['wgUseImageResize'] = true;
-               $tmpGlobals['wgAllowExternalImages'] = true;
-               $tmpGlobals['wgRawHtml'] = false;
-               $tmpGlobals['wgExperimentalHtmlIds'] = false;
-               $tmpGlobals['wgAdaptiveMessageCache'] = true;
-               $tmpGlobals['wgUseDatabaseMessages'] = true;
-               $tmpGlobals['wgLocaltimezone'] = 'UTC';
-               $tmpGlobals['wgGroupPermissions'] = [
-                       '*' => [
-                               'createaccount' => true,
-                               'read' => true,
-                               'edit' => true,
-                               'createpage' => true,
-                               'createtalk' => true,
-               ] ];
-               $tmpGlobals['wgNamespaceProtection'] = [ NS_MEDIAWIKI => 'editinterface' ];
-
-               $tmpGlobals['wgParser'] = new StubObject(
-                       'wgParser', $GLOBALS['wgParserConf']['class'],
-                       [ $GLOBALS['wgParserConf'] ] );
-
-               $tmpGlobals['wgFileExtensions'][] = 'svg';
-               $tmpGlobals['wgSVGConverter'] = 'rsvg';
-               $tmpGlobals['wgSVGConverters']['rsvg'] =
-                       '$path/rsvg-convert -w $width -h $height -o $output $input';
-
-               if ( $GLOBALS['wgStyleDirectory'] === false ) {
-                       $tmpGlobals['wgStyleDirectory'] = "$IP/skins";
-               }
-
-               # Replace all media handlers with a mock. We do not need to generate
-               # actual thumbnails to do parser testing, we only care about receiving
-               # a ThumbnailImage properly initialized.
-               global $wgMediaHandlers;
-               foreach ( $wgMediaHandlers as $type => $handler ) {
-                       $tmpGlobals['wgMediaHandlers'][$type] = 'MockBitmapHandler';
-               }
-               // Vector images have to be handled slightly differently
-               $tmpGlobals['wgMediaHandlers']['image/svg+xml'] = 'MockSvgHandler';
-
-               // DjVu images have to be handled slightly differently
-               $tmpGlobals['wgMediaHandlers']['image/vnd.djvu'] = 'MockDjVuHandler';
-
-               // Ogg video/audio increasingly more differently
-               $tmpGlobals['wgMediaHandlers']['application/ogg'] = 'MockOggHandler';
-
-               $tmpHooks = $wgHooks;
-               $tmpHooks['ParserTestParser'][] = 'ParserTestParserHook::setup';
-               $tmpHooks['ParserGetVariableValueTs'][] = 'ParserTest::getFakeTimestamp';
-               $tmpGlobals['wgHooks'] = $tmpHooks;
-               # add a namespace shadowing a interwiki link, to test
-               # proper precedence when resolving links. (bug 51680)
-               $tmpGlobals['wgExtraNamespaces'] = [
-                       100 => 'MemoryAlpha',
-                       101 => 'MemoryAlpha_talk'
-               ];
-
-               $tmpGlobals['wgLocalInterwikis'] = [ 'local', 'mi' ];
-               # "extra language links"
-               # see https://gerrit.wikimedia.org/r/111390
-               $tmpGlobals['wgExtraInterlanguageLinkPrefixes'] = [ 'mul' ];
-
-               // DjVu support
-               $this->djVuSupport = new DjVuSupport();
-               // Tidy support
-               $this->tidySupport = new TidySupport();
-               $tmpGlobals['wgTidyConfig'] = null;
-               $tmpGlobals['wgUseTidy'] = false;
-               $tmpGlobals['wgDebugTidy'] = false;
-               $tmpGlobals['wgTidyConf'] = $IP . '/includes/tidy/tidy.conf';
-               $tmpGlobals['wgTidyOpts'] = '';
-               $tmpGlobals['wgTidyInternal'] = $this->tidySupport->isInternal();
-
-               $this->setMwGlobals( $tmpGlobals );
-
-               $this->savedWeirdGlobals['image_alias'] = $wgNamespaceAliases['Image'];
-               $this->savedWeirdGlobals['image_talk_alias'] = $wgNamespaceAliases['Image_talk'];
-
-               $wgNamespaceAliases['Image'] = NS_FILE;
-               $wgNamespaceAliases['Image_talk'] = NS_FILE_TALK;
-
-               MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
-               $wgContLang->resetNamespaces(); # reset namespace cache
-               ParserTest::resetTitleServices();
-       }
-
-       protected function tearDown() {
-               global $wgNamespaceAliases, $wgContLang;
-
-               $wgNamespaceAliases['Image'] = $this->savedWeirdGlobals['image_alias'];
-               $wgNamespaceAliases['Image_talk'] = $this->savedWeirdGlobals['image_talk_alias'];
-
-               MWTidy::destroySingleton();
-
-               // Restore backends
-               RepoGroup::destroySingleton();
-               FileBackendGroup::destroySingleton();
-
-               // Remove temporary pages from the link cache
-               LinkCache::singleton()->clear();
-
-               // Restore message cache (temporary pages and $wgUseDatabaseMessages)
-               MessageCache::destroyInstance();
-
-               parent::tearDown();
-
-               MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
-               $wgContLang->resetNamespaces(); # reset namespace cache
-       }
-
-       public static function tearDownAfterClass() {
-               ParserTest::tearDownInterwikis();
-               parent::tearDownAfterClass();
-       }
-
-       function addDBDataOnce() {
-               # disabled for performance
-               # $this->tablesUsed[] = 'image';
-
-               # Update certain things in site_stats
-               $this->db->insert( 'site_stats',
-                       [ 'ss_row_id' => 1, 'ss_images' => 2, 'ss_good_articles' => 1 ],
-                       __METHOD__,
-                       [ 'IGNORE' ]
-               );
-
-               $user = User::newFromId( 0 );
-               LinkCache::singleton()->clear(); # Avoids the odd failure at creating the nullRevision
-
-               # Upload DB table entries for files.
-               # We will upload the actual files later. Note that if anything causes LocalFile::load()
-               # to be triggered before then, it will break via maybeUpgrade() setting the fileExists
-               # member to false and storing it in cache.
-               # note that the size/width/height/bits/etc of the file
-               # are actually set by inspecting the file itself; the arguments
-               # to recordUpload2 have no effect.  That said, we try to make things
-               # match up so it is less confusing to readers of the code & tests.
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2(
-                               '', // archive name
-                               'Upload of some lame file',
-                               'Some lame file',
-                               [
-                                       'size' => 7881,
-                                       'width' => 1941,
-                                       'height' => 220,
-                                       'bits' => 8,
-                                       'media_type' => MEDIATYPE_BITMAP,
-                                       'mime' => 'image/jpeg',
-                                       'metadata' => serialize( [] ),
-                                       'sha1' => Wikimedia\base_convert( '1', 16, 36, 31 ),
-                                       'fileExists' => true ],
-                               $this->db->timestamp( '20010115123500' ), $user
-                       );
-               }
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Thumb.png' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2(
-                               '', // archive name
-                               'Upload of some lame thumbnail',
-                               'Some lame thumbnail',
-                               [
-                                       'size' => 22589,
-                                       'width' => 135,
-                                       'height' => 135,
-                                       'bits' => 8,
-                                       'media_type' => MEDIATYPE_BITMAP,
-                                       'mime' => 'image/png',
-                                       'metadata' => serialize( [] ),
-                                       'sha1' => Wikimedia\base_convert( '2', 16, 36, 31 ),
-                                       'fileExists' => true ],
-                               $this->db->timestamp( '20130225203040' ), $user
-                       );
-               }
-
-               # This image will be blacklisted in [[MediaWiki:Bad image list]]
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2(
-                               '', // archive name
-                               'zomgnotcensored',
-                               'Borderline image',
-                               [
-                                       'size' => 12345,
-                                       'width' => 320,
-                                       'height' => 240,
-                                       'bits' => 24,
-                                       'media_type' => MEDIATYPE_BITMAP,
-                                       'mime' => 'image/jpeg',
-                                       'metadata' => serialize( [] ),
-                                       'sha1' => Wikimedia\base_convert( '3', 16, 36, 31 ),
-                                       'fileExists' => true ],
-                               $this->db->timestamp( '20010115123500' ), $user
-                       );
-               }
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.svg' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2( '', 'Upload of some lame SVG', 'Some lame SVG', [
-                                       'size'        => 12345,
-                                       'width'       => 240,
-                                       'height'      => 180,
-                                       'bits'        => 0,
-                                       'media_type'  => MEDIATYPE_DRAWING,
-                                       'mime'        => 'image/svg+xml',
-                                       'metadata'    => serialize( [] ),
-                                       'sha1'        => Wikimedia\base_convert( '', 16, 36, 31 ),
-                                       'fileExists'  => true
-                       ], $this->db->timestamp( '20010115123500' ), $user );
-               }
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Video.ogv' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2( '', 'A pretty movie', 'Will it play', [
-                                       'size'        => 12345,
-                                       'width'       => 320,
-                                       'height'      => 240,
-                                       'bits'        => 0,
-                                       'media_type'  => MEDIATYPE_VIDEO,
-                                       'mime'        => 'application/ogg',
-                                       'metadata'    => serialize( [] ),
-                                       'sha1'        => Wikimedia\base_convert( '', 16, 36, 32 ),
-                                       'fileExists'  => true
-                       ], $this->db->timestamp( '20010115123500' ), $user );
-               }
-
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Audio.oga' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2( '', 'An awesome hitsong ', 'Will it play', [
-                                       'size'        => 12345,
-                                       'width'       => 0,
-                                       'height'      => 0,
-                                       'bits'        => 0,
-                                       'media_type'  => MEDIATYPE_AUDIO,
-                                       'mime'        => 'application/ogg',
-                                       'metadata'    => serialize( [] ),
-                                       'sha1'        => Wikimedia\base_convert( '', 16, 36, 32 ),
-                                       'fileExists'  => true
-                       ], $this->db->timestamp( '20010115123500' ), $user );
-               }
-
-               # A DjVu file
-               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'LoremIpsum.djvu' ) );
-               if ( !$this->db->selectField( 'image', '1', [ 'img_name' => $image->getName() ] ) ) {
-                       $image->recordUpload2( '', 'Upload a DjVu', 'A DjVu', [
-                               'size' => 3249,
-                               'width' => 2480,
-                               'height' => 3508,
-                               'bits' => 0,
-                               'media_type' => MEDIATYPE_BITMAP,
-                               'mime' => 'image/vnd.djvu',
-                               'metadata' => '<?xml version="1.0" ?>
-<!DOCTYPE DjVuXML PUBLIC "-//W3C//DTD DjVuXML 1.1//EN" "pubtext/DjVuXML-s.dtd">
-<DjVuXML>
-<HEAD></HEAD>
-<BODY><OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-<OBJECT height="3508" width="2480">
-<PARAM name="DPI" value="300" />
-<PARAM name="GAMMA" value="2.2" />
-</OBJECT>
-</BODY>
-</DjVuXML>',
-                               'sha1' => Wikimedia\base_convert( '', 16, 36, 31 ),
-                               'fileExists' => true
-                       ], $this->db->timestamp( '20140115123600' ), $user );
-               }
-       }
-
-       // ParserTest setup/teardown functions
-
-       /**
-        * Set up the global variables for a consistent environment for each test.
-        * Ideally this should replace the global configuration entirely.
-        * @param array $opts
-        * @param string $config
-        * @return RequestContext
-        */
-       protected function setupGlobals( $opts = [], $config = '' ) {
-               global $wgFileBackends;
-               # Find out values for some special options.
-               $lang =
-                       self::getOptionValue( 'language', $opts, 'en' );
-               $variant =
-                       self::getOptionValue( 'variant', $opts, false );
-               $maxtoclevel =
-                       self::getOptionValue( 'wgMaxTocLevel', $opts, 999 );
-               $linkHolderBatchSize =
-                       self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
-
-               $uploadDir = $this->getUploadDir();
-               if ( $this->getCliArg( 'use-filebackend' ) ) {
-                       if ( self::$backendToUse ) {
-                               $backend = self::$backendToUse;
-                       } else {
-                               $name = $this->getCliArg( 'use-filebackend' );
-                               $useConfig = [];
-                               foreach ( $wgFileBackends as $conf ) {
-                                       if ( $conf['name'] == $name ) {
-                                               $useConfig = $conf;
-                                       }
-                               }
-                               $useConfig['name'] = 'local-backend'; // swap name
-                               unset( $useConfig['lockManager'] );
-                               unset( $useConfig['fileJournal'] );
-                               $class = $useConfig['class'];
-                               self::$backendToUse = new $class( $useConfig );
-                               $backend = self::$backendToUse;
-                       }
-               } else {
-                       # Replace with a mock. We do not care about generating real
-                       # files on the filesystem, just need to expose the file
-                       # informations.
-                       $backend = new MockFileBackend( [
-                               'name' => 'local-backend',
-                               'wikiId' => wfWikiID()
-                       ] );
-               }
-
-               $settings = [
-                       'wgLocalFileRepo' => [
-                               'class' => 'LocalRepo',
-                               'name' => 'local',
-                               'url' => 'http://example.com/images',
-                               'hashLevels' => 2,
-                               'transformVia404' => false,
-                               'backend' => $backend
-                       ],
-                       'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ),
-                       'wgLanguageCode' => $lang,
-                       'wgDBprefix' => $this->db->getType() != 'oracle' ? 'unittest_' : 'ut_',
-                       'wgRawHtml' => self::getOptionValue( 'wgRawHtml', $opts, false ),
-                       'wgNamespacesWithSubpages' => [ NS_MAIN => isset( $opts['subpage'] ) ],
-                       'wgAllowExternalImages' => self::getOptionValue( 'wgAllowExternalImages', $opts, true ),
-                       'wgThumbLimits' => [ self::getOptionValue( 'thumbsize', $opts, 180 ) ],
-                       'wgMaxTocLevel' => $maxtoclevel,
-                       'wgUseTeX' => isset( $opts['math'] ) || isset( $opts['texvc'] ),
-                       'wgMathDirectory' => $uploadDir . '/math',
-                       'wgDefaultLanguageVariant' => $variant,
-                       'wgLinkHolderBatchSize' => $linkHolderBatchSize,
-                       'wgUseTidy' => isset( $opts['tidy'] ),
-               ];
-
-               if ( $config ) {
-                       $configLines = explode( "\n", $config );
-
-                       foreach ( $configLines as $line ) {
-                               list( $var, $value ) = explode( '=', $line, 2 );
-
-                               $settings[$var] = eval( "return $value;" ); // ???
-                       }
-               }
-
-               $this->savedGlobals = [];
-
-               /** @since 1.20 */
-               Hooks::run( 'ParserTestGlobals', [ &$settings ] );
-
-               $langObj = Language::factory( $lang );
-               $settings['wgContLang'] = $langObj;
-               $settings['wgLang'] = $langObj;
-
-               $context = new RequestContext();
-               $settings['wgOut'] = $context->getOutput();
-               $settings['wgUser'] = $context->getUser();
-               $settings['wgRequest'] = $context->getRequest();
-
-               // We (re)set $wgThumbLimits to a single-element array above.
-               $context->getUser()->setOption( 'thumbsize', 0 );
-
-               foreach ( $settings as $var => $val ) {
-                       if ( array_key_exists( $var, $GLOBALS ) ) {
-                               $this->savedGlobals[$var] = $GLOBALS[$var];
-                       }
-
-                       $GLOBALS[$var] = $val;
-               }
-
-               MWTidy::destroySingleton();
-               MagicWord::clearCache();
-
-               # The entries saved into RepoGroup cache with previous globals will be wrong.
-               RepoGroup::destroySingleton();
-               FileBackendGroup::destroySingleton();
-
-               # Create dummy files in storage
-               $this->setupUploads();
-
-               # Publish the articles after we have the final language set
-               $this->publishTestArticles();
-
-               MessageCache::destroyInstance();
-
-               return $context;
-       }
-
-       /**
-        * Get an FS upload directory (only applies to FSFileBackend)
-        *
-        * @return string The directory
-        */
-       protected function getUploadDir() {
-               if ( $this->keepUploads ) {
-                       // Don't use getNewTempDirectory() as this is meant to persist
-                       $dir = wfTempDir() . '/mwParser-images';
-
-                       if ( is_dir( $dir ) ) {
-                               return $dir;
-                       }
-               } else {
-                       $dir = $this->getNewTempDirectory();
-               }
-
-               if ( file_exists( $dir ) ) {
-                       wfDebug( "Already exists!\n" );
-
-                       return $dir;
-               }
-
-               return $dir;
-       }
-
-       /**
-        * Create a dummy uploads directory which will contain a couple
-        * of files in order to pass existence tests.
-        *
-        * @return string The directory
-        */
-       protected function setupUploads() {
-               global $IP;
-
-               $base = $this->getBaseDir();
-               $backend = RepoGroup::singleton()->getLocalRepo()->getBackend();
-               $backend->prepare( [ 'dir' => "$base/local-public/3/3a" ] );
-               $backend->store( [
-                       'src' => "$IP/tests/phpunit/data/parser/headbg.jpg",
-                       'dst' => "$base/local-public/3/3a/Foobar.jpg"
-               ] );
-               $backend->prepare( [ 'dir' => "$base/local-public/e/ea" ] );
-               $backend->store( [
-                       'src' => "$IP/tests/phpunit/data/parser/wiki.png",
-                       'dst' => "$base/local-public/e/ea/Thumb.png"
-               ] );
-               $backend->prepare( [ 'dir' => "$base/local-public/0/09" ] );
-               $backend->store( [
-                       'src' => "$IP/tests/phpunit/data/parser/headbg.jpg",
-                       'dst' => "$base/local-public/0/09/Bad.jpg"
-               ] );
-               $backend->prepare( [ 'dir' => "$base/local-public/5/5f" ] );
-               $backend->store( [
-                       'src' => "$IP/tests/phpunit/data/parser/LoremIpsum.djvu",
-                       'dst' => "$base/local-public/5/5f/LoremIpsum.djvu"
-               ] );
-
-               // No helpful SVG file to copy, so make one ourselves
-               $data = '<?xml version="1.0" encoding="utf-8"?>' .
-                       '<svg xmlns="http://www.w3.org/2000/svg"' .
-                       ' version="1.1" width="240" height="180"/>';
-
-               $backend->prepare( [ 'dir' => "$base/local-public/f/ff" ] );
-               $backend->quickCreate( [
-                       'content' => $data, 'dst' => "$base/local-public/f/ff/Foobar.svg"
-               ] );
-       }
-
-       /**
-        * Restore default values and perform any necessary clean-up
-        * after each test runs.
-        */
-       protected function teardownGlobals() {
-               $this->teardownUploads();
-
-               foreach ( $this->savedGlobals as $var => $val ) {
-                       $GLOBALS[$var] = $val;
-               }
-       }
-
-       /**
-        * Remove the dummy uploads directory
-        */
-       private function teardownUploads() {
-               if ( $this->keepUploads ) {
-                       return;
-               }
-
-               $backend = RepoGroup::singleton()->getLocalRepo()->getBackend();
-               if ( $backend instanceof MockFileBackend ) {
-                       # In memory backend, so dont bother cleaning them up.
-                       return;
-               }
-
-               $base = $this->getBaseDir();
-               // delete the files first, then the dirs.
-               self::deleteFiles(
-                       [
-                               "$base/local-public/3/3a/Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/1000px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/100px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/120px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/1280px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/137px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/1500px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/177px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/180px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/200px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/206px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/20px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/220px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/265px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/270px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/274px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/300px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/30px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/330px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/353px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/360px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/400px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/40px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/440px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/442px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/450px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/50px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/600px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/640px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/70px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/75px-Foobar.jpg",
-                               "$base/local-thumb/3/3a/Foobar.jpg/960px-Foobar.jpg",
-
-                               "$base/local-public/e/ea/Thumb.png",
-
-                               "$base/local-public/0/09/Bad.jpg",
-
-                               "$base/local-public/5/5f/LoremIpsum.djvu",
-                               "$base/local-thumb/5/5f/LoremIpsum.djvu/page2-2480px-LoremIpsum.djvu.jpg",
-                               "$base/local-thumb/5/5f/LoremIpsum.djvu/page2-3720px-LoremIpsum.djvu.jpg",
-                               "$base/local-thumb/5/5f/LoremIpsum.djvu/page2-4960px-LoremIpsum.djvu.jpg",
-
-                               "$base/local-public/f/ff/Foobar.svg",
-                               "$base/local-thumb/f/ff/Foobar.svg/180px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/2000px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/270px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/3000px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/360px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/4000px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/langde-180px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/langde-270px-Foobar.svg.png",
-                               "$base/local-thumb/f/ff/Foobar.svg/langde-360px-Foobar.svg.png",
-
-                               "$base/local-public/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
-                       ]
-               );
-       }
-
-       /**
-        * Delete the specified files, if they exist.
-        * @param array $files Full paths to files to delete.
-        */
-       private static function deleteFiles( $files ) {
-               $backend = RepoGroup::singleton()->getLocalRepo()->getBackend();
-               foreach ( $files as $file ) {
-                       $backend->delete( [ 'src' => $file ], [ 'force' => 1 ] );
-               }
-               foreach ( $files as $file ) {
-                       $tmp = FileBackend::parentStoragePath( $file );
-                       while ( $tmp ) {
-                               if ( !$backend->clean( [ 'dir' => $tmp ] )->isOK() ) {
-                                       break;
-                               }
-                               $tmp = FileBackend::parentStoragePath( $tmp );
-                       }
-               }
-       }
-
-       protected function getBaseDir() {
-               return 'mwstore://local-backend';
-       }
-
-       public function parserTestProvider() {
-               if ( $this->file === false ) {
-                       global $wgParserTestFiles;
-                       $this->file = $wgParserTestFiles[0];
-               }
-
-               return new TestFileDataProvider( $this->file, $this );
-       }
-
-       /**
-        * Set the file from whose tests will be run by this instance
-        * @param string $filename
-        */
-       public function setParserTestFile( $filename ) {
-               $this->file = $filename;
-       }
-
-       /**
-        * @group medium
-        * @group ParserTests
-        * @dataProvider parserTestProvider
-        * @param string $desc
-        * @param string $input
-        * @param string $result
-        * @param array $opts
-        * @param array $config
-        */
-       public function testParserTest( $desc, $input, $result, $opts, $config ) {
-               if ( $this->regex != '' && !preg_match( '/' . $this->regex . '/', $desc ) ) {
-                       $this->assertTrue( true ); // XXX: don't flood output with "test made no assertions"
-                       // $this->markTestSkipped( 'Filtered out by the user' );
-                       return;
-               }
-
-               if ( !$this->isWikitextNS( NS_MAIN ) ) {
-                       // parser tests frequently assume that the main namespace contains wikitext.
-                       // @todo When setting up pages, force the content model. Only skip if
-                       //        $wgtContentModelUseDB is false.
-                       $this->markTestSkipped( "Main namespace does not support wikitext,"
-                               . "skipping parser test: $desc" );
-               }
-
-               wfDebug( "Running parser test: $desc\n" );
-
-               $opts = $this->parseOptions( $opts );
-               $context = $this->setupGlobals( $opts, $config );
-
-               $user = $context->getUser();
-               $options = ParserOptions::newFromContext( $context );
-
-               if ( isset( $opts['title'] ) ) {
-                       $titleText = $opts['title'];
-               } else {
-                       $titleText = 'Parser test';
-               }
-
-               $local = isset( $opts['local'] );
-               $preprocessor = isset( $opts['preprocessor'] ) ? $opts['preprocessor'] : null;
-               $parser = $this->getParser( $preprocessor );
-
-               $title = Title::newFromText( $titleText );
-
-               # Parser test requiring math. Make sure texvc is executable
-               # or just skip such tests.
-               if ( isset( $opts['math'] ) || isset( $opts['texvc'] ) ) {
-                       global $wgTexvc;
-
-                       if ( !isset( $wgTexvc ) ) {
-                               $this->markTestSkipped( "SKIPPED: \$wgTexvc is not set" );
-                       } elseif ( !is_executable( $wgTexvc ) ) {
-                               $this->markTestSkipped( "SKIPPED: texvc binary does not exist"
-                                       . " or is not executable.\n"
-                                       . "Current configuration is:\n\$wgTexvc = '$wgTexvc'" );
-                       }
-               }
-
-               if ( isset( $opts['djvu'] ) ) {
-                       if ( !$this->djVuSupport->isEnabled() ) {
-                               $this->markTestSkipped( "SKIPPED: djvu binaries do not exist or are not executable.\n" );
-                       }
-               }
-
-               if ( isset( $opts['tidy'] ) ) {
-                       if ( !$this->tidySupport->isEnabled() ) {
-                               $this->markTestSkipped( "SKIPPED: tidy extension is not installed.\n" );
-                       } else {
-                               $options->setTidy( true );
-                       }
-               }
-
-               if ( isset( $opts['pst'] ) ) {
-                       $out = $parser->preSaveTransform( $input, $title, $user, $options );
-               } elseif ( isset( $opts['msg'] ) ) {
-                       $out = $parser->transformMsg( $input, $options, $title );
-               } elseif ( isset( $opts['section'] ) ) {
-                       $section = $opts['section'];
-                       $out = $parser->getSection( $input, $section );
-               } elseif ( isset( $opts['replace'] ) ) {
-                       $section = $opts['replace'][0];
-                       $replace = $opts['replace'][1];
-                       $out = $parser->replaceSection( $input, $section, $replace );
-               } elseif ( isset( $opts['comment'] ) ) {
-                       $out = Linker::formatComment( $input, $title, $local );
-               } elseif ( isset( $opts['preload'] ) ) {
-                       $out = $parser->getPreloadText( $input, $title, $options );
-               } else {
-                       $output = $parser->parse( $input, $title, $options, true, true, 1337 );
-                       $output->setTOCEnabled( !isset( $opts['notoc'] ) );
-                       $out = $output->getText();
-                       if ( isset( $opts['tidy'] ) ) {
-                               $out = preg_replace( '/\s+$/', '', $out );
-                       }
-
-                       if ( isset( $opts['showtitle'] ) ) {
-                               if ( $output->getTitleText() ) {
-                                       $title = $output->getTitleText();
-                               }
-
-                               $out = "$title\n$out";
-                       }
-
-                       if ( isset( $opts['showindicators'] ) ) {
-                               $indicators = '';
-                               foreach ( $output->getIndicators() as $id => $content ) {
-                                       $indicators .= "$id=$content\n";
-                               }
-                               $out = $indicators . $out;
-                       }
-
-                       if ( isset( $opts['ill'] ) ) {
-                               $out = implode( ' ', $output->getLanguageLinks() );
-                       } elseif ( isset( $opts['cat'] ) ) {
-                               $outputPage = $context->getOutput();
-                               $outputPage->addCategoryLinks( $output->getCategories() );
-                               $cats = $outputPage->getCategoryLinks();
-
-                               if ( isset( $cats['normal'] ) ) {
-                                       $out = implode( ' ', $cats['normal'] );
-                               } else {
-                                       $out = '';
-                               }
-                       }
-                       $parser->mPreprocessor = null;
-               }
-
-               $this->teardownGlobals();
-
-               $this->assertEquals( $result, $out, $desc );
-       }
-
-       /**
-        * Run a fuzz test series
-        * Draw input from a set of test files
-        *
-        * @todo fixme Needs some work to not eat memory until the world explodes
-        *
-        * @group ParserFuzz
-        */
-       public function testFuzzTests() {
-               global $wgParserTestFiles;
-
-               $files = $wgParserTestFiles;
-
-               if ( $this->getCliArg( 'file' ) ) {
-                       $files = [ $this->getCliArg( 'file' ) ];
-               }
-
-               $dict = $this->getFuzzInput( $files );
-               $dictSize = strlen( $dict );
-               $logMaxLength = log( $this->maxFuzzTestLength );
-
-               ini_set( 'memory_limit', $this->memoryLimit * 1048576 );
-
-               $user = new User;
-               $opts = ParserOptions::newFromUser( $user );
-               $title = Title::makeTitle( NS_MAIN, 'Parser_test' );
-
-               $id = 1;
-
-               while ( true ) {
-
-                       // Generate test input
-                       mt_srand( ++$this->fuzzSeed );
-                       $totalLength = mt_rand( 1, $this->maxFuzzTestLength );
-                       $input = '';
-
-                       while ( strlen( $input ) < $totalLength ) {
-                               $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength;
-                               $hairLength = min( intval( exp( $logHairLength ) ), $dictSize );
-                               $offset = mt_rand( 0, $dictSize - $hairLength );
-                               $input .= substr( $dict, $offset, $hairLength );
-                       }
-
-                       $this->setupGlobals();
-                       $parser = $this->getParser();
-
-                       // Run the test
-                       try {
-                               $parser->parse( $input, $title, $opts );
-                               $this->assertTrue( true, "Test $id, fuzz seed {$this->fuzzSeed}" );
-                       } catch ( Exception $exception ) {
-                               $input_dump = sprintf( "string(%d) \"%s\"\n", strlen( $input ), $input );
-
-                               $this->assertTrue( false, "Test $id, fuzz seed {$this->fuzzSeed}. \n\n" .
-                                       "Input: $input_dump\n\nError: {$exception->getMessage()}\n\n" .
-                                       "Backtrace: {$exception->getTraceAsString()}" );
-                       }
-
-                       $this->teardownGlobals();
-                       $parser->__destruct();
-
-                       if ( $id % 100 == 0 ) {
-                               $usage = intval( memory_get_usage( true ) / $this->memoryLimit / 1048576 * 100 );
-                               // echo "{$this->fuzzSeed}: $numSuccess/$numTotal (mem: $usage%)\n";
-                               if ( $usage > 90 ) {
-                                       $ret = "Out of memory:\n";
-                                       $memStats = $this->getMemoryBreakdown();
-
-                                       foreach ( $memStats as $name => $usage ) {
-                                               $ret .= "$name: $usage\n";
-                                       }
-
-                                       throw new MWException( $ret );
-                               }
-                       }
-
-                       $id++;
-               }
-       }
-
-       // Various getter functions
-
-       /**
-        * Get an input dictionary from a set of parser test files
-        * @param array $filenames
-        * @return string
-        */
-       function getFuzzInput( $filenames ) {
-               $dict = '';
-
-               foreach ( $filenames as $filename ) {
-                       $contents = file_get_contents( $filename );
-                       preg_match_all( '/!!\s*input\n(.*?)\n!!\s*result/s', $contents, $matches );
-
-                       foreach ( $matches[1] as $match ) {
-                               $dict .= $match . "\n";
-                       }
-               }
-
-               return $dict;
-       }
-
-       /**
-        * Get a memory usage breakdown
-        * @return array
-        */
-       function getMemoryBreakdown() {
-               $memStats = [];
-
-               foreach ( $GLOBALS as $name => $value ) {
-                       $memStats['$' . $name] = strlen( serialize( $value ) );
-               }
-
-               $classes = get_declared_classes();
-
-               foreach ( $classes as $class ) {
-                       $rc = new ReflectionClass( $class );
-                       $props = $rc->getStaticProperties();
-                       $memStats[$class] = strlen( serialize( $props ) );
-                       $methods = $rc->getMethods();
-
-                       foreach ( $methods as $method ) {
-                               $memStats[$class] += strlen( serialize( $method->getStaticVariables() ) );
-                       }
-               }
-
-               $functions = get_defined_functions();
-
-               foreach ( $functions['user'] as $function ) {
-                       $rf = new ReflectionFunction( $function );
-                       $memStats["$function()"] = strlen( serialize( $rf->getStaticVariables() ) );
-               }
-
-               asort( $memStats );
-
-               return $memStats;
-       }
-
-       /**
-        * Get a Parser object
-        * @param Preprocessor $preprocessor
-        * @return Parser
-        */
-       function getParser( $preprocessor = null ) {
-               global $wgParserConf;
-
-               $class = $wgParserConf['class'];
-               $parser = new $class( [ 'preprocessorClass' => $preprocessor ] + $wgParserConf );
-
-               Hooks::run( 'ParserTestParser', [ &$parser ] );
-
-               return $parser;
-       }
-
-       // Various action functions
-
-       public function addArticle( $name, $text, $line ) {
-               self::$articles[$name] = [ $text, $line ];
-       }
-
-       public function publishTestArticles() {
-               if ( empty( self::$articles ) ) {
-                       return;
-               }
-
-               foreach ( self::$articles as $name => $info ) {
-                       list( $text, $line ) = $info;
-                       ParserTest::addArticle( $name, $text, $line, 'ignoreduplicate' );
-               }
-       }
-
-       /**
-        * Steal a callback function from the primary parser, save it for
-        * application to our scary parser. If the hook is not installed,
-        * abort processing of this file.
-        *
-        * @param string $name
-        * @return bool True if tag hook is present
-        */
-       public function requireHook( $name ) {
-               global $wgParser;
-               $wgParser->firstCallInit(); // make sure hooks are loaded.
-               return isset( $wgParser->mTagHooks[$name] );
-       }
-
-       public function requireFunctionHook( $name ) {
-               global $wgParser;
-               $wgParser->firstCallInit(); // make sure hooks are loaded.
-               return isset( $wgParser->mFunctionHooks[$name] );
-       }
-
-       public function requireTransparentHook( $name ) {
-               global $wgParser;
-               $wgParser->firstCallInit(); // make sure hooks are loaded.
-               return isset( $wgParser->mTransparentTagHooks[$name] );
-       }
-
-       // Various "cleanup" functions
-
-       /**
-        * Remove last character if it is a newline
-        * @param string $s
-        * @return string
-        */
-       public function removeEndingNewline( $s ) {
-               if ( substr( $s, -1 ) === "\n" ) {
-                       return substr( $s, 0, -1 );
-               } else {
-                       return $s;
-               }
-       }
-
-       // Test options parser functions
-
-       protected function parseOptions( $instring ) {
-               $opts = [];
-               // foo
-               // foo=bar
-               // foo="bar baz"
-               // foo=[[bar baz]]
-               // foo=bar,"baz quux"
-               $regex = '/\b
-                       ([\w-]+)                                                # Key
-                       \b
-                       (?:\s*
-                               =                                               # First sub-value
-                               \s*
-                               (
-                                       "
-                                               [^"]*                   # Quoted val
-                                       "
-                               |
-                                       \[\[
-                                               [^]]*                   # Link target
-                                       \]\]
-                               |
-                                       [\w-]+                          # Plain word
-                               )
-                               (?:\s*
-                                       ,                                       # Sub-vals 1..N
-                                       \s*
-                                       (
-                                               "[^"]*"                 # Quoted val
-                                       |
-                                               \[\[[^]]*\]\]   # Link target
-                                       |
-                                               [\w-]+                  # Plain word
-                                       )
-                               )*
-                       )?
-                       /x';
-
-               if ( preg_match_all( $regex, $instring, $matches, PREG_SET_ORDER ) ) {
-                       foreach ( $matches as $bits ) {
-                               array_shift( $bits );
-                               $key = strtolower( array_shift( $bits ) );
-                               if ( count( $bits ) == 0 ) {
-                                       $opts[$key] = true;
-                               } elseif ( count( $bits ) == 1 ) {
-                                       $opts[$key] = $this->cleanupOption( array_shift( $bits ) );
-                               } else {
-                                       // Array!
-                                       $opts[$key] = array_map( [ $this, 'cleanupOption' ], $bits );
-                               }
-                       }
-               }
-
-               return $opts;
-       }
-
-       protected function cleanupOption( $opt ) {
-               if ( substr( $opt, 0, 1 ) == '"' ) {
-                       return substr( $opt, 1, -1 );
-               }
-
-               if ( substr( $opt, 0, 2 ) == '[[' ) {
-                       return substr( $opt, 2, -2 );
-               }
-
-               return $opt;
-       }
-
-       /**
-        * Use a regex to find out the value of an option
-        * @param string $key Name of option val to retrieve
-        * @param array $opts Options array to look in
-        * @param mixed $default Default value returned if not found
-        * @return mixed
-        */
-       protected static function getOptionValue( $key, $opts, $default ) {
-               $key = strtolower( $key );
-
-               if ( isset( $opts[$key] ) ) {
-                       return $opts[$key];
-               } else {
-                       return $default;
-               }
-       }
-}
diff --git a/tests/phpunit/includes/parser/ParserIntegrationTest.php b/tests/phpunit/includes/parser/ParserIntegrationTest.php
new file mode 100644 (file)
index 0000000..698bd0b
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * This is the TestCase subclass for running a single parser test via the
+ * ParserTestRunner integration test system.
+ *
+ * Note: the following groups are not used by PHPUnit.
+ * The list in ParserTestFileSuite::__construct() is used instead.
+ *
+ * @group Database
+ * @group Parser
+ *
+ * @todo covers tags
+ */
+class ParserIntegrationTest extends PHPUnit_Framework_TestCase {
+       /** @var array */
+       private $ptTest;
+
+       /** @var ParserTestRunner */
+       private $ptRunner;
+
+       /** @var ScopedCallback */
+       private $ptTeardownScope;
+
+       public function __construct( $runner, $fileName, $test ) {
+               parent::__construct( 'testParse', [ '[details omitted]' ],
+                       basename( $fileName ) . ': ' . $test['desc'] );
+               $this->ptTest = $test;
+               $this->ptRunner = $runner;
+       }
+
+       public function testParse() {
+               $this->ptRunner->getRecorder()->setTestCase( $this );
+               $result = $this->ptRunner->runTest( $this->ptTest );
+               $this->assertEquals( $result->expected, $result->actual );
+       }
+
+       public function setUp() {
+               $this->ptTeardownScope = $this->ptRunner->staticSetup();
+       }
+
+       public function tearDown() {
+               if ( $this->ptTeardownScope ) {
+                       ScopedCallback::consume( $this->ptTeardownScope );
+               }
+       }
+}
index a62503a..6710b19 100644 (file)
@@ -1,5 +1,29 @@
 <?php
 
+/**
+ * @covers Preprocessor
+ *
+ * @covers Preprocessor_DOM
+ * @covers PPDStack
+ * @covers PPDStackElement
+ * @covers PPDPart
+ * @covers PPFrame_DOM
+ * @covers PPTemplateFrame_DOM
+ * @covers PPCustomFrame_DOM
+ * @covers PPNode_DOM
+ *
+ * @covers Preprocessor_Hash
+ * @covers PPDStack_Hash
+ * @covers PPDStackElement_Hash
+ * @covers PPDPart_Hash
+ * @covers PPFrame_Hash
+ * @covers PPTemplateFrame_Hash
+ * @covers PPCustomFrame_Hash
+ * @covers PPNode_Hash_Tree
+ * @covers PPNode_Hash_Text
+ * @covers PPNode_Hash_Array
+ * @covers PPNode_Hash_Attr
+ */
 class PreprocessorTest extends MediaWikiTestCase {
        protected $mTitle = 'Page title';
        protected $mPPNodeCount = 0;
@@ -8,28 +32,44 @@ class PreprocessorTest extends MediaWikiTestCase {
         */
        protected $mOptions;
        /**
-        * @var Preprocessor
+        * @var array
         */
-       protected $mPreprocessor;
+       protected $mPreprocessors;
+
+       protected static $classNames = [
+               'Preprocessor_DOM',
+               'Preprocessor_Hash'
+       ];
 
        protected function setUp() {
-               global $wgParserConf, $wgContLang;
+               global $wgContLang;
                parent::setUp();
                $this->mOptions = ParserOptions::newFromUserAndLang( new User, $wgContLang );
-               $name = isset( $wgParserConf['preprocessorClass'] )
-                       ? $wgParserConf['preprocessorClass']
-                       : 'Preprocessor_DOM';
 
-               $this->mPreprocessor = new $name( $this );
+               $this->mPreprocessors = [];
+               foreach ( self::$classNames as $className ) {
+                       $this->mPreprocessors[$className] = new $className( $this );
+               }
        }
 
        function getStripList() {
                return [ 'gallery', 'display map' /* Used by Maps, see r80025 CR */, '/foo' ];
        }
 
+       protected static function addClassArg( $testCases ) {
+               $newTestCases = [];
+               foreach ( self::$classNames as $className ) {
+                       foreach ( $testCases as $testCase ) {
+                               array_unshift( $testCase, $className );
+                               $newTestCases[] = $testCase;
+                       }
+               }
+               return $newTestCases;
+       }
+
        public static function provideCases() {
                // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong
-               return [
+               return self::addClassArg( [
                        [ "Foo", "<root>Foo</root>" ],
                        [ "<!-- Foo -->", "<root><comment>&lt;!-- Foo --&gt;</comment></root>" ],
                        [ "<!-- Foo --><!-- Bar -->", "<root><comment>&lt;!-- Foo --&gt;</comment><comment>&lt;!-- Bar --&gt;</comment></root>" ],
@@ -115,7 +155,7 @@ class PreprocessorTest extends MediaWikiTestCase {
                        [ "{{Foo|} Bar=", "<root>{{Foo|} Bar=</root>" ],
                        [ "{{Foo|} Bar=}}", "<root><template><title>Foo</title><part><name>} Bar</name>=<value></value></part></template></root>" ],
                        /* [ file_get_contents( __DIR__ . '/QuoteQuran.txt' ], file_get_contents( __DIR__ . '/QuoteQuranExpanded.txt' ) ], */
-               ];
+               ] );
                // @codingStandardsIgnoreEnd
        }
 
@@ -123,15 +163,17 @@ class PreprocessorTest extends MediaWikiTestCase {
         * Get XML preprocessor tree from the preprocessor (which may not be the
         * native XML-based one).
         *
+        * @param string $className
         * @param string $wikiText
         * @return string
         */
-       protected function preprocessToXml( $wikiText ) {
-               if ( method_exists( $this->mPreprocessor, 'preprocessToXml' ) ) {
-                       return $this->normalizeXml( $this->mPreprocessor->preprocessToXml( $wikiText ) );
+       protected function preprocessToXml( $className, $wikiText ) {
+               $preprocessor = $this->mPreprocessors[$className];
+               if ( method_exists( $preprocessor, 'preprocessToXml' ) ) {
+                       return $this->normalizeXml( $preprocessor->preprocessToXml( $wikiText ) );
                }
 
-               $dom = $this->mPreprocessor->preprocessToObj( $wikiText );
+               $dom = $preprocessor->preprocessToObj( $wikiText );
                if ( is_callable( [ $dom, 'saveXML' ] ) ) {
                        return $dom->saveXML();
                } else {
@@ -146,15 +188,20 @@ class PreprocessorTest extends MediaWikiTestCase {
         * @return string
         */
        protected function normalizeXml( $xml ) {
-               return preg_replace( '!<([a-z]+)/>!', '<$1></$1>', str_replace( ' />', '/>', $xml ) );
+               // Normalize self-closing tags
+               $xml = preg_replace( '!<([a-z]+)/>!', '<$1></$1>', str_replace( ' />', '/>', $xml ) );
+               // Remove <equals> tags, which only occur in Preprocessor_Hash and
+               // have no semantic value
+               $xml = preg_replace( '!</?equals>!', '', $xml );
+               return $xml;
        }
 
        /**
         * @dataProvider provideCases
-        * @covers Preprocessor_DOM::preprocessToXml
         */
-       public function testPreprocessorOutput( $wikiText, $expectedXml ) {
-               $this->assertEquals( $this->normalizeXml( $expectedXml ), $this->preprocessToXml( $wikiText ) );
+       public function testPreprocessorOutput( $className, $wikiText, $expectedXml ) {
+               $this->assertEquals( $this->normalizeXml( $expectedXml ),
+                       $this->preprocessToXml( $className, $wikiText ) );
        }
 
        /**
@@ -162,24 +209,23 @@ class PreprocessorTest extends MediaWikiTestCase {
         */
        public static function provideFiles() {
                // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong
-               return [
+               return self::addClassArg( [
                        [ "QuoteQuran" ], # http://en.wikipedia.org/w/index.php?title=Template:QuoteQuran/sandbox&oldid=237348988 GFDL + CC BY-SA by Striver
                        [ "Factorial" ], # http://en.wikipedia.org/w/index.php?title=Template:Factorial&oldid=98548758 GFDL + CC BY-SA by Polonium
                        [ "All_system_messages" ], # http://tl.wiktionary.org/w/index.php?title=Suleras:All_system_messages&oldid=2765 GPL text generated by MediaWiki
                        [ "Fundraising" ], # http://tl.wiktionary.org/w/index.php?title=MediaWiki:Sitenotice&oldid=5716 GFDL + CC BY-SA, copied there by Sky Harbor.
                        [ "NestedTemplates" ], # bug 27936
-               ];
+               ] );
                // @codingStandardsIgnoreEnd
        }
 
        /**
         * @dataProvider provideFiles
-        * @covers Preprocessor_DOM::preprocessToXml
         */
-       public function testPreprocessorOutputFiles( $filename ) {
+       public function testPreprocessorOutputFiles( $className, $filename ) {
                $folder = __DIR__ . "/../../../parser/preprocess";
                $wikiText = file_get_contents( "$folder/$filename.txt" );
-               $output = $this->preprocessToXml( $wikiText );
+               $output = $this->preprocessToXml( $className, $wikiText );
 
                $expectedFilename = "$folder/$filename.expected";
                if ( file_exists( $expectedFilename ) ) {
@@ -197,7 +243,8 @@ class PreprocessorTest extends MediaWikiTestCase {
         */
        public static function provideHeadings() {
                // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong
-               return [ /* These should become headings: */
+               return self::addClassArg( [
+                       /* These should become headings: */
                        [ "== h ==<!--c1-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment></h></root>" ],
                        [ "== h ==      <!--c1-->", "<root><h level=\"2\" i=\"1\">== h ==       <comment>&lt;!--c1--&gt;</comment></h></root>" ],
                        [ "== h ==<!--c1-->     ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment>      </h></root>" ],
@@ -233,15 +280,15 @@ class PreprocessorTest extends MediaWikiTestCase {
                        [ "== h == x <!--c1--><!--c2--><!--c3-->  ", "<root>== h == x <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment>  </root>" ],
                        [ "== h ==<!--c1--> x <!--c2--><!--c3-->  ", "<root>== h ==<comment>&lt;!--c1--&gt;</comment> x <comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment>  </root>" ],
                        [ "== h ==<!--c1--><!--c2--><!--c3--> x ", "<root>== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> x </root>" ],
-               ];
+               ] );
                // @codingStandardsIgnoreEnd
        }
 
        /**
         * @dataProvider provideHeadings
-        * @covers Preprocessor_DOM::preprocessToXml
         */
-       public function testHeadings( $wikiText, $expectedXml ) {
-               $this->assertEquals( $this->normalizeXml( $expectedXml ), $this->preprocessToXml( $wikiText ) );
+       public function testHeadings( $className, $wikiText, $expectedXml ) {
+               $this->assertEquals( $this->normalizeXml( $expectedXml ),
+                       $this->preprocessToXml( $className, $wikiText ) );
        }
 }
index 4aa4f41..1dfcd82 100644 (file)
@@ -14,7 +14,7 @@ class CoreVersionCheckerTest extends PHPUnit_Framework_TestCase {
 
        public static function provideCheck() {
                return [
-                       // array( $wgVersion, constraint, expected )
+                       // [ $wgVersion, constraint, expected ]
                        [ '1.25alpha', '>= 1.26', false ],
                        [ '1.25.0', '>= 1.26', false ],
                        [ '1.26alpha', '>= 1.26', true ],
index 0120d79..11995de 100644 (file)
@@ -2,11 +2,12 @@
 
 class ExtensionProcessorTest extends MediaWikiTestCase {
 
-       private $dir;
+       private $dir, $dirname;
 
        public function setUp() {
                parent::setUp();
                $this->dir = __DIR__ . '/FooBar/extension.json';
+               $this->dirname = dirname( $this->dir );
        }
 
        /**
@@ -108,9 +109,9 @@ class ExtensionProcessorTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers ExtensionProcessor::extractConfig
+        * @covers ExtensionProcessor::extractConfig1
         */
-       public function testExtractConfig() {
+       public function testExtractConfig1() {
                $processor = new ExtensionProcessor;
                $info = [
                        'config' => [
@@ -136,6 +137,35 @@ class ExtensionProcessorTest extends MediaWikiTestCase {
                $this->assertEquals( 'somevalue', $extracted['globals']['egBar'] );
        }
 
+       /**
+        * @covers ExtensionProcessor::extractConfig2
+        */
+       public function testExtractConfig2() {
+               $processor = new ExtensionProcessor;
+               $info = [
+                       'config' => [
+                               'Bar' => [ 'value' => 'somevalue' ],
+                               'Foo' => [ 'value' => 10 ],
+                               'Path' => [ 'value' => 'foo.txt', 'path' => true ],
+                       ],
+               ] + self::$default;
+               $info2 = [
+                       'config' => [
+                               'Bar' => [ 'value' => 'somevalue' ],
+                       ],
+                       'config_prefix' => 'eg',
+                       'name' => 'FooBar2',
+               ];
+               $processor->extractInfo( $this->dir, $info, 2 );
+               $processor->extractInfo( $this->dir, $info2, 2 );
+               $extracted = $processor->getExtractedInfo();
+               $this->assertEquals( 'somevalue', $extracted['globals']['wgBar'] );
+               $this->assertEquals( 10, $extracted['globals']['wgFoo'] );
+               $this->assertEquals( "{$this->dirname}/foo.txt", $extracted['globals']['wgPath'] );
+               // Custom prefix:
+               $this->assertEquals( 'somevalue', $extracted['globals']['egBar'] );
+       }
+
        public static function provideExtractExtensionMessagesFiles() {
                $dir = __DIR__ . '/FooBar/';
                return [
@@ -414,6 +444,26 @@ class ExtensionProcessorTest extends MediaWikiTestCase {
                        ]
                ];
        }
+
+       public function testGlobalSettingsDocumentedInSchema() {
+               global $IP;
+               $globalSettings = TestingAccessWrapper::newFromClass(
+                       ExtensionProcessor::class )->globalSettings;
+
+               $schema = FormatJson::decode(
+                       file_get_contents( "$IP/docs/extension.schema.json" ),
+                       true
+               );
+               $missing = [];
+               foreach ( $globalSettings as $global ) {
+                       if ( !isset( $schema['properties'][$global] ) ) {
+                               $missing[] = $global;
+                       }
+               }
+
+               $this->assertEquals( [], $missing,
+                       "The following global settings are not documented in docs/extension.schema.json" );
+       }
 }
 
 /**
index 90c9f38..0be04ef 100644 (file)
@@ -2,11 +2,11 @@
 
 /**
  * @group ResourceLoader
+ * @covers DerivativeResourceLoaderContext
  */
 class DerivativeResourceLoaderContextTest extends PHPUnit_Framework_TestCase {
 
-       protected static function getResourceLoaderContext() {
-               $resourceLoader = new ResourceLoader();
+       protected static function getContext() {
                $request = new FauxRequest( [
                                'lang' => 'zh',
                                'modules' => 'test.context',
@@ -14,42 +14,76 @@ class DerivativeResourceLoaderContextTest extends PHPUnit_Framework_TestCase {
                                'skin' => 'fallback',
                                'target' => 'test',
                ] );
-               return new ResourceLoaderContext( $resourceLoader, $request );
+               return new ResourceLoaderContext( new ResourceLoader(), $request );
        }
 
-       public function testGet() {
-               $context = self::getResourceLoaderContext();
-               $derived = new DerivativeResourceLoaderContext( $context );
+       public function testGetInherited() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
 
+               // Request parameters
+               $this->assertEquals( $derived->getDebug(), false );
                $this->assertEquals( $derived->getLanguage(), 'zh' );
                $this->assertEquals( $derived->getModules(), [ 'test.context' ] );
                $this->assertEquals( $derived->getOnly(), 'scripts' );
                $this->assertEquals( $derived->getSkin(), 'fallback' );
+               $this->assertEquals( $derived->getUser(), null );
+
+               // Misc
+               $this->assertEquals( $derived->getDirection(), 'ltr' );
                $this->assertEquals( $derived->getHash(), 'zh|fallback|||scripts|||||' );
        }
 
-       public function testSetLanguage() {
-               $context = self::getResourceLoaderContext();
+       public function testModules() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
+
+               $derived->setModules( [ 'test.override' ] );
+               $this->assertEquals( $derived->getModules(), [ 'test.override' ] );
+       }
+
+       public function testLanguage() {
+               $context = self::getContext();
                $derived = new DerivativeResourceLoaderContext( $context );
 
                $derived->setLanguage( 'nl' );
                $this->assertEquals( $derived->getLanguage(), 'nl' );
+       }
+
+       public function testDirection() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
+
+               $derived->setLanguage( 'nl' );
+               $this->assertEquals( $derived->getDirection(), 'ltr' );
 
                $derived->setLanguage( 'he' );
                $this->assertEquals( $derived->getDirection(), 'rtl' );
+
+               $derived->setDirection( 'ltr' );
+               $this->assertEquals( $derived->getDirection(), 'ltr' );
        }
 
-       public function testSetModules() {
-               $context = self::getResourceLoaderContext();
-               $derived = new DerivativeResourceLoaderContext( $context );
+       public function testSkin() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
 
-               $derived->setModules( [ 'test.override' ] );
-               $this->assertEquals( $derived->getModules(), [ 'test.override' ] );
+               $derived->setSkin( 'override' );
+               $this->assertEquals( $derived->getSkin(), 'override' );
        }
 
-       public function testSetOnly() {
-               $context = self::getResourceLoaderContext();
-               $derived = new DerivativeResourceLoaderContext( $context );
+       public function testUser() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
+
+               $derived->setUser( 'Example' );
+               $this->assertEquals( $derived->getUser(), 'Example' );
+       }
+
+       public function testDebug() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
+
+               $derived->setDebug( true );
+               $this->assertEquals( $derived->getDebug(), true );
+       }
+
+       public function testOnly() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
 
                $derived->setOnly( 'styles' );
                $this->assertEquals( $derived->getOnly(), 'styles' );
@@ -58,21 +92,35 @@ class DerivativeResourceLoaderContextTest extends PHPUnit_Framework_TestCase {
                $this->assertEquals( $derived->getOnly(), null );
        }
 
-       public function testSetSkin() {
-               $context = self::getResourceLoaderContext();
-               $derived = new DerivativeResourceLoaderContext( $context );
+       public function testVersion() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
 
-               $derived->setSkin( 'override' );
-               $this->assertEquals( $derived->getSkin(), 'override' );
+               $derived->setVersion( 'hw1' );
+               $this->assertEquals( $derived->getVersion(), 'hw1' );
+       }
+
+       public function testRaw() {
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
+
+               $derived->setRaw( true );
+               $this->assertEquals( $derived->getRaw(), true );
        }
 
        public function testGetHash() {
-               $context = self::getResourceLoaderContext();
-               $derived = new DerivativeResourceLoaderContext( $context );
+               $derived = new DerivativeResourceLoaderContext( self::getContext() );
+
+               $this->assertEquals( $derived->getHash(), 'zh|fallback|||scripts|||||' );
 
                $derived->setLanguage( 'nl' );
+               $derived->setUser( 'Example' );
                // Assert that subclass is able to clear parent class "hash" member
-               $this->assertEquals( $derived->getHash(), 'nl|fallback|||scripts|||||' );
+               $this->assertEquals( $derived->getHash(), 'nl|fallback||Example|scripts|||||' );
        }
 
+       public function testAccessors() {
+               $context = self::getContext();
+               $derived = new DerivativeResourceLoaderContext( $context );
+               $this->assertSame( $derived->getRequest(), $context->getRequest() );
+               $this->assertSame( $derived->getResourceLoader(), $context->getResourceLoader() );
+       }
 }
diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderClientHtmlTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderClientHtmlTest.php
new file mode 100644 (file)
index 0000000..0965b9f
--- /dev/null
@@ -0,0 +1,278 @@
+<?php
+
+/**
+ * @group ResourceLoader
+ */
+class ResourceLoaderClientHtmlTest extends PHPUnit_Framework_TestCase {
+
+       protected static function makeContext( $extraQuery = [] ) {
+               $conf = new HashConfig( [
+                       'ResourceLoaderSources' => [],
+                       'ResourceModuleSkinStyles' => [],
+                       'ResourceModules' => [],
+                       'EnableJavaScriptTest' => false,
+                       'ResourceLoaderDebug' => false,
+                       'LoadScript' => '/w/load.php',
+               ] );
+               return new ResourceLoaderContext(
+                       new ResourceLoader( $conf ),
+                       new FauxRequest( array_merge( [
+                               'lang' => 'nl',
+                               'skin' => 'fallback',
+                               'user' => 'Example',
+                               'target' => 'phpunit',
+                       ], $extraQuery ) )
+               );
+       }
+
+       protected static function makeModule( array $options = [] ) {
+               return new ResourceLoaderTestModule( $options );
+       }
+
+       protected static function makeSampleModules() {
+               $modules = [
+                       'test' => [],
+                       'test.top' => [ 'position' => 'top' ],
+                       'test.private.top' => [ 'group' => 'private', 'position' => 'top' ],
+                       'test.private.bottom' => [ 'group' => 'private', 'position' => 'bottom' ],
+
+                       'test.styles.pure' => [ 'type' => ResourceLoaderModule::LOAD_STYLES ],
+                       'test.styles.mixed' => [],
+                       'test.styles.noscript' => [ 'group' => 'noscript', 'type' => ResourceLoaderModule::LOAD_STYLES ],
+                       'test.styles.mixed.user' => [ 'group' => 'user' ],
+                       'test.styles.mixed.user.empty' => [ 'group' => 'user', 'isKnownEmpty' => true ],
+                       'test.styles.private' => [ 'group' => 'private', 'styles' => '.private{}' ],
+
+                       'test.scripts' => [],
+                       'test.scripts.top' => [ 'position' => 'top' ],
+                       'test.scripts.mixed.user' => [ 'group' => 'user' ],
+                       'test.scripts.mixed.user.empty' => [ 'group' => 'user', 'isKnownEmpty' => true ],
+                       'test.scripts.raw' => [ 'isRaw' => true ],
+               ];
+               return array_map( function ( $options ) {
+                       return self::makeModule( $options );
+               }, $modules );
+       }
+
+       /**
+        * @covers ResourceLoaderClientHtml::getDocumentAttributes
+        */
+       public function testGetDocumentAttributes() {
+               $client = new ResourceLoaderClientHtml( self::makeContext() );
+               $this->assertInternalType( 'array', $client->getDocumentAttributes() );
+       }
+
+       /**
+        * @covers ResourceLoaderClientHtml::__construct
+        * @covers ResourceLoaderClientHtml::setModules
+        * @covers ResourceLoaderClientHtml::setModuleStyles
+        * @covers ResourceLoaderClientHtml::setModuleScripts
+        * @covers ResourceLoaderClientHtml::getData
+        * @covers ResourceLoaderClientHtml::getContext
+        */
+       public function testGetData() {
+               $context = self::makeContext();
+               $context->getResourceLoader()->register( self::makeSampleModules() );
+
+               $client = new ResourceLoaderClientHtml( $context );
+               $client->setModules( [
+                       'test',
+                       'test.private.bottom',
+                       'test.private.top',
+                       'test.top',
+                       'test.unregistered',
+               ] );
+               $client->setModuleStyles( [
+                       'test.styles.mixed',
+                       'test.styles.mixed.user.empty',
+                       'test.styles.private',
+                       'test.styles.pure',
+                       'test.unregistered.styles',
+               ] );
+               $client->setModuleScripts( [
+                       'test.scripts',
+                       'test.scripts.mixed.user.empty',
+                       'test.scripts.top',
+                       'test.unregistered.scripts',
+               ] );
+
+               $expected = [
+                       'states' => [
+                               'test.private.top' => 'loading',
+                               'test.private.bottom' => 'loading',
+                               'test.styles.pure' => 'ready',
+                               'test.styles.mixed.user.empty' => 'ready',
+                               'test.styles.private' => 'ready',
+                               'test.scripts' => 'loading',
+                               'test.scripts.top' => 'loading',
+                               'test.scripts.mixed.user.empty' => 'ready',
+                       ],
+                       'general' => [
+                               'top' => [ 'test.top' ],
+                               'bottom' => [ 'test' ],
+                       ],
+                       'styles' => [
+                               'test.styles.mixed',
+                               'test.styles.pure',
+                       ],
+                       'scripts' => [
+                               'top' => [ 'test.scripts.top' ],
+                               'bottom' => [ 'test.scripts' ],
+                       ],
+                       'embed' => [
+                               'styles' => [ 'test.styles.private' ],
+                               'general' => [
+                                       'top' => [ 'test.private.top' ],
+                                       'bottom' => [ 'test.private.bottom' ],
+                               ],
+                       ],
+               ];
+
+               $access = TestingAccessWrapper::newFromObject( $client );
+               $this->assertEquals( $expected, $access->getData() );
+       }
+
+       /**
+        * @covers ResourceLoaderClientHtml::setConfig
+        * @covers ResourceLoaderClientHtml::setExemptStates
+        * @covers ResourceLoaderClientHtml::getHeadHtml
+        * @covers ResourceLoaderClientHtml::getLoad
+        * @covers ResourceLoader::makeLoaderStateScript
+        */
+       public function testGetHeadHtml() {
+               $context = self::makeContext();
+               $context->getResourceLoader()->register( self::makeSampleModules() );
+
+               $client = new ResourceLoaderClientHtml( $context );
+               $client->setConfig( [ 'key' => 'value' ] );
+               $client->setModules( [
+                       'test.top',
+                       'test.private.top',
+               ] );
+               $client->setModuleStyles( [
+                       'test.styles.pure',
+                       'test.styles.private',
+               ] );
+               $client->setModuleScripts( [
+                       'test.scripts.top',
+               ] );
+               $client->setExemptStates( [
+                       'test.exempt' => 'ready',
+               ] );
+
+               // @codingStandardsIgnoreStart Generic.Files.LineLength
+               $expected = '<script>document.documentElement.className = document.documentElement.className.replace( /(^|\s)client-nojs(\s|$)/, "$1client-js$2" );</script>' . "\n"
+                       . '<script>(window.RLQ=window.RLQ||[]).push(function(){'
+                       . 'mw.config.set({"key":"value"});'
+                       . 'mw.loader.state({"test.exempt":"ready","test.private.top":"loading","test.styles.pure":"ready","test.styles.private":"ready","test.scripts.top":"loading"});'
+                       . 'mw.loader.implement("test.private.top",function($,jQuery,require,module){},{"css":[]});'
+                       . 'mw.loader.load(["test.top"]);'
+                       . 'mw.loader.load("/w/load.php?debug=false\u0026lang=nl\u0026modules=test.scripts.top\u0026only=scripts\u0026skin=fallback");'
+                       . '});</script>' . "\n"
+                       . '<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>.private{}</style>' . "\n"
+                       . '<script async="" src="/w/load.php?debug=false&amp;lang=nl&amp;modules=startup&amp;only=scripts&amp;skin=fallback"></script>';
+               // @codingStandardsIgnoreEnd
+
+               $this->assertEquals( $expected, $client->getHeadHtml() );
+       }
+
+       /**
+        * @covers ResourceLoaderClientHtml::getBodyHtml
+        * @covers ResourceLoaderClientHtml::getLoad
+        */
+       public function testGetBodyHtml() {
+               $context = self::makeContext();
+               $context->getResourceLoader()->register( self::makeSampleModules() );
+
+               $client = new ResourceLoaderClientHtml( $context );
+               $client->setConfig( [ 'key' => 'value' ] );
+               $client->setModules( [
+                       'test',
+                       'test.private.bottom',
+               ] );
+               $client->setModuleScripts( [
+                       'test.scripts',
+               ] );
+
+               // @codingStandardsIgnoreStart Generic.Files.LineLength
+               $expected = '<script>(window.RLQ=window.RLQ||[]).push(function(){'
+                       . 'mw.loader.implement("test.private.bottom",function($,jQuery,require,module){},{"css":[]});'
+                       . 'mw.loader.load("/w/load.php?debug=false\u0026lang=nl\u0026modules=test.scripts\u0026only=scripts\u0026skin=fallback");'
+                       . 'mw.loader.load(["test"]);'
+                       . '});</script>';
+               // @codingStandardsIgnoreEnd
+
+               $this->assertEquals( $expected, $client->getBodyHtml() );
+       }
+
+       public static function provideMakeLoad() {
+               return [
+                       // @codingStandardsIgnoreStart Generic.Files.LineLength
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.unknown' ],
+                               'only' => ResourceLoaderModule::TYPE_STYLES,
+                               'output' => '',
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.styles.private' ],
+                               'only' => ResourceLoaderModule::TYPE_STYLES,
+                               'output' => '<style>.private{}</style>',
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.private.top' ],
+                               'only' => ResourceLoaderModule::TYPE_COMBINED,
+                               'output' => '<script>(window.RLQ=window.RLQ||[]).push(function(){mw.loader.implement("test.private.top",function($,jQuery,require,module){},{"css":[]});});</script>',
+                       ],
+                       [
+                               'context' => [],
+                               // Eg. startup module
+                               'modules' => [ 'test.scripts.raw' ],
+                               'only' => ResourceLoaderModule::TYPE_SCRIPTS,
+                               'output' => '<script async="" src="/w/load.php?debug=false&amp;lang=nl&amp;modules=test.scripts.raw&amp;only=scripts&amp;skin=fallback"></script>',
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.scripts.mixed.user' ],
+                               'only' => ResourceLoaderModule::TYPE_SCRIPTS,
+                               'output' => '<script>(window.RLQ=window.RLQ||[]).push(function(){mw.loader.load("/w/load.php?debug=false\u0026lang=nl\u0026modules=test.scripts.mixed.user\u0026only=scripts\u0026skin=fallback\u0026user=Example\u0026version=0a56zyi");});</script>',
+                       ],
+                       [
+                               'context' => [ 'debug' => true ],
+                               'modules' => [ 'test.styles.pure', 'test.styles.mixed' ],
+                               'only' => ResourceLoaderModule::TYPE_STYLES,
+                               'output' => '<link rel="stylesheet" href="/w/load.php?debug=true&amp;lang=nl&amp;modules=test.styles.pure&amp;only=styles&amp;skin=fallback"/>' . "\n"
+                                       . '<link rel="stylesheet" href="/w/load.php?debug=true&amp;lang=nl&amp;modules=test.styles.mixed&amp;only=styles&amp;skin=fallback"/>',
+                       ],
+                       [
+                               'context' => [],
+                               'modules' => [ 'test.styles.noscript' ],
+                               '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>',
+                       ],
+                       // @codingStandardsIgnoreEnd
+               ];
+       }
+
+       /**
+        * @dataProvider provideMakeLoad
+        * @covers ResourceLoaderClientHtml::makeLoad
+        * @covers ResourceLoaderClientHtml::makeContext
+        * @covers ResourceLoader::makeModuleResponse
+        * @covers ResourceLoaderModule::getModuleContent
+        * @covers ResourceLoader::getCombinedVersion
+        * @covers ResourceLoader::createLoaderURL
+        * @covers ResourceLoader::createLoaderQuery
+        * @covers ResourceLoader::makeLoaderQuery
+        * @covers ResourceLoader::makeInlineScript
+        */
+       public function testMakeLoad( array $extraQuery, array $modules, $type, $expected ) {
+               $context = self::makeContext( $extraQuery );
+               $context->getResourceLoader()->register( self::makeSampleModules() );
+               $actual = ResourceLoaderClientHtml::makeLoad( $context, $modules, $type );
+               $this->assertEquals( $expected, (string)$actual );
+       }
+}
diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderContextTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderContextTest.php
new file mode 100644 (file)
index 0000000..1093039
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * See also:
+ * - ResourceLoaderTest::testExpandModuleNames
+ * - ResourceLoaderImageModuleTest::testContext
+ *
+ * @group Cache
+ * @covers ResourceLoaderContext
+ */
+class ResourceLoaderContextTest extends PHPUnit_Framework_TestCase {
+       protected static function getResourceLoader() {
+               return new EmptyResourceLoader( new HashConfig( [
+                       'ResourceLoaderDebug' => false,
+                       'DefaultSkin' => 'fallback',
+                       'LanguageCode' => 'nl',
+               ] ) );
+       }
+
+       public function testEmpty() {
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [] ) );
+
+               // Request parameters
+               $this->assertEquals( [], $ctx->getModules() );
+               $this->assertEquals( 'nl', $ctx->getLanguage() );
+               $this->assertEquals( false, $ctx->getDebug() );
+               $this->assertEquals( null, $ctx->getOnly() );
+               $this->assertEquals( 'fallback', $ctx->getSkin() );
+               $this->assertEquals( null, $ctx->getUser() );
+
+               // Misc
+               $this->assertEquals( 'ltr', $ctx->getDirection() );
+               $this->assertEquals( 'nl|fallback||||||||', $ctx->getHash() );
+               $this->assertInstanceOf( User::class, $ctx->getUserObj() );
+       }
+
+       public function testDummy() {
+               $this->assertInstanceOf(
+                       ResourceLoaderContext::class,
+                       ResourceLoaderContext::newDummyContext()
+               );
+       }
+
+       public function testAccessors() {
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [] ) );
+               $this->assertInstanceOf( WebRequest::class, $ctx->getRequest() );
+               $this->assertInstanceOf( \Psr\Log\LoggerInterface::class, $ctx->getLogger() );
+       }
+
+       public function testTypicalRequest() {
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [
+                       'debug' => 'false',
+                       'lang' => 'zh',
+                       'modules' => 'foo|foo.quux,baz,bar|baz.quux',
+                       'only' => 'styles',
+                       'skin' => 'fallback',
+               ] ) );
+
+               // Request parameters
+               $this->assertEquals(
+                       $ctx->getModules(),
+                       [ 'foo', 'foo.quux', 'foo.baz', 'foo.bar', 'baz.quux' ]
+               );
+               $this->assertEquals( false, $ctx->getDebug() );
+               $this->assertEquals( 'zh', $ctx->getLanguage() );
+               $this->assertEquals( 'styles', $ctx->getOnly() );
+               $this->assertEquals( 'fallback', $ctx->getSkin() );
+               $this->assertEquals( null, $ctx->getUser() );
+
+               // Misc
+               $this->assertEquals( 'ltr', $ctx->getDirection() );
+               $this->assertEquals( 'zh|fallback|||styles|||||', $ctx->getHash() );
+       }
+
+       public function testShouldInclude() {
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [] ) );
+               $this->assertTrue( $ctx->shouldIncludeScripts(), 'Scripts in combined' );
+               $this->assertTrue( $ctx->shouldIncludeStyles(), 'Styles in combined' );
+               $this->assertTrue( $ctx->shouldIncludeMessages(), 'Messages in combined' );
+
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [
+                       'only' => 'styles'
+               ] ) );
+               $this->assertFalse( $ctx->shouldIncludeScripts(), 'Scripts not in styles-only' );
+               $this->assertTrue( $ctx->shouldIncludeStyles(), 'Styles in styles-only' );
+               $this->assertFalse( $ctx->shouldIncludeMessages(), 'Messages not in styles-only' );
+
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [
+                       'only' => 'scripts'
+               ] ) );
+               $this->assertTrue( $ctx->shouldIncludeScripts(), 'Scripts in scripts-only' );
+               $this->assertFalse( $ctx->shouldIncludeStyles(), 'Styles not in scripts-only' );
+               $this->assertFalse( $ctx->shouldIncludeMessages(), 'Messages not in scripts-only' );
+       }
+
+       public function testGetUser() {
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [] ) );
+               $this->assertSame( null, $ctx->getUser() );
+               $this->assertTrue( $ctx->getUserObj()->isAnon() );
+
+               $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [
+                       'user' => 'Example'
+               ] ) );
+               $this->assertSame( 'Example', $ctx->getUser() );
+               $this->assertEquals( 'Example', $ctx->getUserObj()->getName() );
+       }
+}
index 2dec02b..8b29983 100644 (file)
@@ -27,6 +27,15 @@ class ResourceLoaderFileModuleTest extends ResourceLoaderTestCase {
                return [
                        'noTemplateModule' => [],
 
+                       'deprecatedModule' => $base + [
+                               'deprecated' => true,
+                       ],
+                       'deprecatedTomorrow' => $base + [
+                               'deprecated' => [
+                                       'message' => 'Will be removed tomorrow.'
+                               ],
+                       ],
+
                        'htmlTemplateModule' => $base + [
                                'templates' => [
                                        'templates/template.html',
@@ -93,9 +102,38 @@ class ResourceLoaderFileModuleTest extends ResourceLoaderTestCase {
         */
        public function testTemplateDependencies( $module, $expected ) {
                $rl = new ResourceLoaderFileModule( $module );
+               $rl->setName( 'testing' );
                $this->assertEquals( $rl->getDependencies(), $expected );
        }
 
+       public static function providerDeprecatedModules() {
+               return [
+                       [
+                               'deprecatedModule',
+                               'mw.log.warn("This page is using the deprecated ResourceLoader module \"deprecatedModule\".");',
+                       ],
+                       [
+                               'deprecatedTomorrow',
+                               'mw.log.warn(' .
+                                       '"This page is using the deprecated ResourceLoader module \"deprecatedTomorrow\".\\n' .
+                                       "Will be removed tomorrow." .
+                                       '");'
+                       ]
+               ];
+       }
+
+       /**
+        * @dataProvider providerDeprecatedModules
+        * @covers ResourceLoaderFileModule::getScript
+        */
+       public function testDeprecatedModules( $name, $expected ) {
+               $modules = self::getModules();
+               $rl = new ResourceLoaderFileModule( $modules[$name] );
+               $rl->setName( $name );
+               $ctx = $this->getResourceLoaderContext( 'en', 'ltr' );
+               $this->assertEquals( $rl->getScript( $ctx ), $expected );
+       }
+
        /**
         * @covers ResourceLoaderFileModule::getAllStyleFiles
         * @covers ResourceLoaderFileModule::getAllSkinStyleFiles
@@ -127,6 +165,7 @@ class ResourceLoaderFileModuleTest extends ResourceLoaderTestCase {
                ];
 
                $module = new ResourceLoaderFileModule( $baseParams );
+               $module->setName( 'testing' );
 
                $this->assertEquals(
                        [
@@ -164,10 +203,12 @@ class ResourceLoaderFileModuleTest extends ResourceLoaderTestCase {
                        'localBasePath' => $basePath,
                        'styles' => [ 'test.css' ],
                ] );
+               $testModule->setName( 'testing' );
                $expectedModule = new ResourceLoaderFileModule( [
                        'localBasePath' => $basePath,
                        'styles' => [ 'expected.css' ],
                ] );
+               $expectedModule->setName( 'testing' );
 
                $contextLtr = $this->getResourceLoaderContext( 'en', 'ltr' );
                $contextRtl = $this->getResourceLoaderContext( 'he', 'rtl' );
@@ -223,6 +264,7 @@ class ResourceLoaderFileModuleTest extends ResourceLoaderTestCase {
         */
        public function testGetTemplates( $module, $expected ) {
                $rl = new ResourceLoaderFileModule( $module );
+               $rl->setName( 'testing' );
 
                $this->assertEquals( $rl->getTemplates(), $expected );
        }
@@ -233,6 +275,7 @@ class ResourceLoaderFileModuleTest extends ResourceLoaderTestCase {
                        'localBasePath' => $basePath,
                        'styles' => [ 'bom.css' ],
                        ] );
+               $testModule->setName( 'testing' );
                $this->assertEquals(
                        substr( file_get_contents( "$basePath/bom.css" ), 0, 10 ),
                        "\xef\xbb\xbf.efbbbf",
index f61540d..aeb82d1 100644 (file)
@@ -154,6 +154,49 @@ class ResourceLoaderImageModuleTest extends ResourceLoaderTestCase {
                $styles = $module->getStyles( $this->getResourceLoaderContext() );
                $this->assertEquals( $expected, $styles['all'] );
        }
+
+       /**
+        * @covers ResourceLoaderContext::getImageObj
+        */
+       public function testContext() {
+               $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest() );
+               $this->assertFalse( $context->getImageObj(), 'Missing image parameter' );
+
+               $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest( [
+                       'image' => 'example',
+               ] ) );
+               $this->assertFalse( $context->getImageObj(), 'Missing module parameter' );
+
+               $context = new ResourceLoaderContext( new EmptyResourceLoader(), new FauxRequest( [
+                       'modules' => 'unknown',
+                       'image' => 'example',
+               ] ) );
+               $this->assertFalse( $context->getImageObj(), 'Not an image module' );
+
+               $rl = new EmptyResourceLoader();
+               $rl->register( 'test', [
+                       'class' => ResourceLoaderImageModule::class,
+                       'prefix' => 'test',
+                       'images' => [ 'example' => 'example.png' ],
+               ] );
+               $context = new ResourceLoaderContext( $rl, new FauxRequest( [
+                       'modules' => 'test',
+                       'image' => 'unknown',
+               ] ) );
+               $this->assertFalse( $context->getImageObj(), 'Unknown image' );
+
+               $rl = new EmptyResourceLoader();
+               $rl->register( 'test', [
+                       'class' => ResourceLoaderImageModule::class,
+                       'prefix' => 'test',
+                       'images' => [ 'example' => 'example.png' ],
+               ] );
+               $context = new ResourceLoaderContext( $rl, new FauxRequest( [
+                       'modules' => 'test',
+                       'image' => 'example',
+               ] ) );
+               $this->assertInstanceOf( ResourceLoaderImage::class, $context->getImageObj() );
+       }
 }
 
 class ResourceLoaderImageModuleTestable extends ResourceLoaderImageModule {
index 72ea495..ab1323e 100644 (file)
@@ -5,7 +5,7 @@ class ResourceLoaderStartUpModuleTest extends ResourceLoaderTestCase {
        // Version hash for a blank file module.
        // Result of ResourceLoader::makeHash(), ResourceLoaderTestModule
        // and ResourceLoaderFileModule::getDefinitionSummary().
-       protected static $blankVersion = 'GqV9IPpY';
+       protected static $blankVersion = '09p30q0';
 
        protected static function expandPlaceholders( $text ) {
                return strtr( $text, [
@@ -310,7 +310,6 @@ mw.loader.register( [
         * @dataProvider provideGetModuleRegistrations
         * @covers ResourceLoaderStartUpModule::compileUnresolvedDependencies
         * @covers ResourceLoaderStartUpModule::getModuleRegistrations
-        * @covers ResourceLoader::makeLoaderSourcesScript
         * @covers ResourceLoader::makeLoaderRegisterScript
         */
        public function testGetModuleRegistrations( $case ) {
index 65cd6ed..9a3e222 100644 (file)
@@ -17,18 +17,12 @@ class ResourceLoaderTest extends ResourceLoaderTestCase {
                ] );
        }
 
-       public static function provideValidModules() {
-               return [
-                       [ 'TEST.validModule1', new ResourceLoaderTestModule() ],
-               ];
-       }
-
        /**
-        * Ensures that the ResourceLoaderRegisterModules hook is called when a new
-        * ResourceLoader object is constructed.
+        * Ensure the ResourceLoaderRegisterModules hook is called.
+        *
         * @covers ResourceLoader::__construct
         */
-       public function testCreatingNewResourceLoaderCallsRegistrationHook() {
+       public function testConstructRegistrationHook() {
                $resourceLoaderRegisterModulesHook = false;
 
                $this->setMwGlobals( 'wgHooks', [
@@ -39,66 +33,112 @@ class ResourceLoaderTest extends ResourceLoaderTestCase {
                        ]
                ] );
 
-               $resourceLoader = new ResourceLoader();
+               $unused = new ResourceLoader();
                $this->assertTrue(
                        $resourceLoaderRegisterModulesHook,
                        'Hook ResourceLoaderRegisterModules called'
                );
-
-               return $resourceLoader;
        }
 
        /**
-        * @dataProvider provideValidModules
-        * @depends testCreatingNewResourceLoaderCallsRegistrationHook
         * @covers ResourceLoader::register
         * @covers ResourceLoader::getModule
         */
-       public function testRegisteredValidModulesAreAccessible(
-               $name, ResourceLoaderModule $module, ResourceLoader $resourceLoader
-       ) {
-               $resourceLoader->register( $name, $module );
-               $this->assertEquals( $module, $resourceLoader->getModule( $name ) );
+       public function testRegisterValid() {
+               $module = new ResourceLoaderTestModule();
+               $resourceLoader = new EmptyResourceLoader();
+               $resourceLoader->register( 'test', $module );
+               $this->assertEquals( $module, $resourceLoader->getModule( 'test' ) );
        }
 
        /**
-        * @covers ResourceLoaderFileModule::compileLessFile
+        * @covers ResourceLoader::register
         */
-       public function testLessFileCompilation() {
-               $context = $this->getResourceLoaderContext();
-               $basePath = __DIR__ . '/../../data/less/module';
-               $module = new ResourceLoaderFileModule( [
-                       'localBasePath' => $basePath,
-                       'styles' => [ 'styles.less' ],
-               ] );
-               $module->setName( 'test.less' );
-               $styles = $module->getStyles( $context );
-               $this->assertStringEqualsFile( $basePath . '/styles.css', $styles['all'] );
+       public function testRegisterInvalidName() {
+               $resourceLoader = new EmptyResourceLoader();
+               $this->setExpectedException( 'MWException', "name 'test!invalid' is invalid" );
+               $resourceLoader->register( 'test!invalid', new ResourceLoaderTestModule() );
        }
 
        /**
-        * Strip @noflip annotations from CSS code.
-        * @param string $css
-        * @return string
+        * @covers ResourceLoader::register
         */
-       private static function stripNoflip( $css ) {
-               return str_replace( '/*@noflip*/ ', '', $css );
+       public function testRegisterInvalidType() {
+               $resourceLoader = new EmptyResourceLoader();
+               $this->setExpectedException( 'MWException', 'ResourceLoader module info type error' );
+               $resourceLoader->register( 'test', new stdClass() );
        }
 
        /**
-        * @dataProvider providePackedModules
-        * @covers ResourceLoader::makePackedModulesString
+        * @covers ResourceLoader::getModuleNames
         */
-       public function testMakePackedModulesString( $desc, $modules, $packed ) {
-               $this->assertEquals( $packed, ResourceLoader::makePackedModulesString( $modules ), $desc );
+       public function testGetModuleNames() {
+               // Use an empty one so that core and extension modules don't get in.
+               $resourceLoader = new EmptyResourceLoader();
+               $resourceLoader->register( 'test.foo', new ResourceLoaderTestModule() );
+               $resourceLoader->register( 'test.bar', new ResourceLoaderTestModule() );
+               $this->assertEquals(
+                       [ 'test.foo', 'test.bar' ],
+                       $resourceLoader->getModuleNames()
+               );
        }
 
        /**
-        * @dataProvider providePackedModules
-        * @covers ResourceLoaderContext::expandModuleNames
+        * @covers ResourceLoader::isModuleRegistered
+        */
+       public function testIsModuleRegistered() {
+               $rl = new EmptyResourceLoader();
+               $rl->register( 'test', new ResourceLoaderTestModule() );
+               $this->assertTrue( $rl->isModuleRegistered( 'test' ) );
+               $this->assertFalse( $rl->isModuleRegistered( 'test.unknown' ) );
+       }
+
+       /**
+        * @covers ResourceLoader::getModule
+        */
+       public function testGetModuleUnknown() {
+               $rl = new EmptyResourceLoader();
+               $this->assertSame( null, $rl->getModule( 'test' ) );
+       }
+
+       /**
+        * @covers ResourceLoader::getModule
+        */
+       public function testGetModuleClass() {
+               $rl = new EmptyResourceLoader();
+               $rl->register( 'test', [ 'class' => ResourceLoaderTestModule::class ] );
+               $this->assertInstanceOf(
+                       ResourceLoaderTestModule::class,
+                       $rl->getModule( 'test' )
+               );
+       }
+
+       /**
+        * @covers ResourceLoader::getModule
+        */
+       public function testGetModuleClassDefault() {
+               $rl = new EmptyResourceLoader();
+               $rl->register( 'test', [] );
+               $this->assertInstanceOf(
+                       ResourceLoaderFileModule::class,
+                       $rl->getModule( 'test' ),
+                       'Array-style module registrations default to FileModule'
+               );
+       }
+
+       /**
+        * @covers ResourceLoaderFileModule::compileLessFile
         */
-       public function testexpandModuleNames( $desc, $modules, $packed ) {
-               $this->assertEquals( $modules, ResourceLoaderContext::expandModuleNames( $packed ), $desc );
+       public function testLessFileCompilation() {
+               $context = $this->getResourceLoaderContext();
+               $basePath = __DIR__ . '/../../data/less/module';
+               $module = new ResourceLoaderFileModule( [
+                       'localBasePath' => $basePath,
+                       'styles' => [ 'styles.less' ],
+               ] );
+               $module->setName( 'test.less' );
+               $styles = $module->getStyles( $context );
+               $this->assertStringEqualsFile( $basePath . '/styles.css', $styles['all'] );
        }
 
        public static function providePackedModules() {
@@ -123,23 +163,47 @@ class ResourceLoaderTest extends ResourceLoaderTestCase {
                                [ 'single.module', 'foobar', 'foobaz' ],
                                'single.module|foobar,foobaz',
                        ],
+                       [
+                               'Ordering',
+                               [ 'foo', 'foo.baz', 'baz.quux', 'foo.bar' ],
+                               'foo|foo.baz,bar|baz.quux',
+                               [ 'foo', 'foo.baz', 'foo.bar', 'baz.quux' ],
+                       ]
                ];
        }
 
+       /**
+        * @dataProvider providePackedModules
+        * @covers ResourceLoader::makePackedModulesString
+        */
+       public function testMakePackedModulesString( $desc, $modules, $packed ) {
+               $this->assertEquals( $packed, ResourceLoader::makePackedModulesString( $modules ), $desc );
+       }
+
+       /**
+        * @dataProvider providePackedModules
+        * @covers ResourceLoaderContext::expandModuleNames
+        */
+       public function testExpandModuleNames( $desc, $modules, $packed, $unpacked = null ) {
+               $this->assertEquals(
+                       $unpacked ?: $modules,
+                       ResourceLoaderContext::expandModuleNames( $packed ),
+                       $desc
+               );
+       }
+
        public static function provideAddSource() {
                return [
-                       [ 'examplewiki', '//example.org/w/load.php', 'examplewiki' ],
-                       [ 'example2wiki', [ 'loadScript' => '//example.com/w/load.php' ], 'example2wiki' ],
+                       [ 'foowiki', 'https://example.org/w/load.php', 'foowiki' ],
+                       [ 'foowiki', [ 'loadScript' => 'https://example.org/w/load.php' ], 'foowiki' ],
                        [
-                               [ 'foowiki' => '//foo.org/w/load.php', 'bazwiki' => '//baz.org/w/load.php' ],
+                               [
+                                       'foowiki' => 'https://example.org/w/load.php',
+                                       'bazwiki' => 'https://example.com/w/load.php',
+                               ],
                                null,
                                [ 'foowiki', 'bazwiki' ]
-                       ],
-                       [
-                               [ 'foowiki' => '//foo.org/w/load.php' ],
-                               null,
-                               false,
-                       ],
+                       ]
                ];
        }
 
@@ -150,10 +214,6 @@ class ResourceLoaderTest extends ResourceLoaderTestCase {
         */
        public function testAddSource( $name, $info, $expected ) {
                $rl = new ResourceLoader;
-               if ( $expected === false ) {
-                       $this->setExpectedException( 'MWException', 'ResourceLoader duplicate source addition error' );
-                       $rl->addSource( $name, $info );
-               }
                $rl->addSource( $name, $info );
                if ( is_array( $expected ) ) {
                        foreach ( $expected as $source ) {
@@ -164,17 +224,23 @@ class ResourceLoaderTest extends ResourceLoaderTestCase {
                }
        }
 
-       public static function fakeSources() {
-               return [
-                       'examplewiki' => [
-                               'loadScript' => '//example.org/w/load.php',
-                               'apiScript' => '//example.org/w/api.php',
-                       ],
-                       'example2wiki' => [
-                               'loadScript' => '//example.com/w/load.php',
-                               'apiScript' => '//example.com/w/api.php',
-                       ],
-               ];
+       /**
+        * @covers ResourceLoader::addSource
+        */
+       public function testAddSourceDupe() {
+               $rl = new ResourceLoader;
+               $this->setExpectedException( 'MWException', 'ResourceLoader duplicate source addition error' );
+               $rl->addSource( 'foo', 'https://example.org/w/load.php' );
+               $rl->addSource( 'foo', 'https://example.com/w/load.php' );
+       }
+
+       /**
+        * @covers ResourceLoader::addSource
+        */
+       public function testAddSourceInvalid() {
+               $rl = new ResourceLoader;
+               $this->setExpectedException( 'MWException', 'with no "loadScript" key' );
+               $rl->addSource( 'foo',  [ 'x' => 'https://example.org/w/load.php' ] );
        }
 
        public static function provideLoaderImplement() {
@@ -204,8 +270,6 @@ mw.example();
                                'name' => 'test.example',
                                'scripts' => 'mw.example();',
                                'styles' => [],
-                               'messages' => new XmlJsCode( '{}' ),
-                               'templates' => [],
 
                                'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
 mw.example();
@@ -218,7 +282,6 @@ mw.example();
                                'scripts' => [],
                                'styles' => [ 'css' => [ '.mw-example {}' ] ],
                                'messages' => new XmlJsCode( '{}' ),
-                               'templates' => [],
 
                                'expected' => 'mw.loader.implement( "test.example", [], {
     "css": [
@@ -231,9 +294,7 @@ mw.example();
 
                                'name' => 'test.example',
                                'scripts' => 'mw.example();',
-                               'styles' => [],
                                'messages' => [ 'example' => '' ],
-                               'templates' => [],
 
                                'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
 mw.example();
@@ -246,8 +307,6 @@ mw.example();
 
                                'name' => 'test.example',
                                'scripts' => 'mw.example();',
-                               'styles' => [],
-                               'messages' => new XmlJsCode( '{}' ),
                                'templates' => [ 'example.html' => '' ],
 
                                'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
@@ -256,14 +315,39 @@ mw.example();
     "example.html": ""
 } );',
                        ] ],
+                       [ [
+                               'title' => 'Implement unwrapped user script',
+
+                               'name' => 'user',
+                               'scripts' => 'mw.example( 1 );',
+
+                               'expected' => 'mw.loader.implement( "user", "mw.example( 1 );" );',
+                       ] ],
+                       [ [
+                               'title' => 'Implement unwrapped user script',
+                               'debug' => false,
+
+                               'name' => 'user',
+                               'scripts' => 'mw.example( 1 );',
+
+                               'expected' => 'mw.loader.implement("user","mw.example(1);");',
+                       ] ],
                ];
        }
 
        /**
         * @dataProvider provideLoaderImplement
         * @covers ResourceLoader::makeLoaderImplementScript
+        * @covers ResourceLoader::trimArray
         */
        public function testMakeLoaderImplementScript( $case ) {
+               $case += [
+                       'styles' => [], 'templates' => [], 'messages' => new XmlJsCode( '{}' ),
+                       'debug' => true
+               ];
+               ResourceLoader::clearCache();
+               $this->setMwGlobals( 'wgResourceLoaderDebug', $case['debug'] );
+
                $this->assertEquals(
                        $case['expected'],
                        ResourceLoader::makeLoaderImplementScript(
@@ -276,6 +360,63 @@ mw.example();
                );
        }
 
+       /**
+        * @covers ResourceLoader::makeLoaderImplementScript
+        */
+       public function testMakeLoaderImplementScriptInvalid() {
+               $this->setExpectedException( 'MWException', 'Invalid scripts error' );
+               ResourceLoader::makeLoaderImplementScript(
+                       'test', // name
+                       123, // scripts
+                       null, // styles
+                       null, // messages
+                       null // templates
+               );
+       }
+
+       /**
+        * @covers ResourceLoader::makeLoaderSourcesScript
+        */
+       public function testMakeLoaderSourcesScript() {
+               $this->assertEquals(
+                       'mw.loader.addSource( "local", "/w/load.php" );',
+                       ResourceLoader::makeLoaderSourcesScript( 'local', '/w/load.php' )
+               );
+               $this->assertEquals(
+                       'mw.loader.addSource( {
+    "local": "/w/load.php"
+} );',
+                       ResourceLoader::makeLoaderSourcesScript( [ 'local' => '/w/load.php' ] )
+               );
+               $this->assertEquals(
+                       'mw.loader.addSource( {
+    "local": "/w/load.php",
+    "example": "https://example.org/w/load.php"
+} );',
+                       ResourceLoader::makeLoaderSourcesScript( [
+                               'local' => '/w/load.php',
+                               'example' => 'https://example.org/w/load.php'
+                       ] )
+               );
+               $this->assertEquals(
+                       'mw.loader.addSource( [] );',
+                       ResourceLoader::makeLoaderSourcesScript( [] )
+               );
+       }
+
+       private static function fakeSources() {
+               return [
+                       'examplewiki' => [
+                               'loadScript' => '//example.org/w/load.php',
+                               'apiScript' => '//example.org/w/api.php',
+                       ],
+                       'example2wiki' => [
+                               'loadScript' => '//example.com/w/load.php',
+                               'apiScript' => '//example.com/w/api.php',
+                       ],
+               ];
+       }
+
        /**
         * @covers ResourceLoader::getLoadScript
         */
@@ -295,14 +436,4 @@ mw.example();
                        $this->assertTrue( true );
                }
        }
-
-       /**
-        * @covers ResourceLoader::isModuleRegistered
-        */
-       public function testIsModuleRegistered() {
-               $rl = new ResourceLoader();
-               $rl->register( 'test.module', new ResourceLoaderTestModule() );
-               $this->assertTrue( $rl->isModuleRegistered( 'test.module' ) );
-               $this->assertFalse( $rl->isModuleRegistered( 'test.modulenotregistered' ) );
-       }
 }
index 85834d7..404fd97 100644 (file)
@@ -114,25 +114,25 @@ class ResourceLoaderWikiModuleTest extends ResourceLoaderTestCase {
                        [ [], 'test1', true ],
                        // 'site' module with a non-empty page
                        [
-                               [ 'MediaWiki:Common.js' => [ 'rev_sha1' => 'dmh6qn', 'rev_len' => 1234 ] ],
+                               [ 'MediaWiki:Common.js' => [ 'page_len' => 1234 ] ],
                                'site',
                                false,
                        ],
                        // 'site' module with an empty page
                        [
-                               [ 'MediaWiki:Foo.js' => [ 'rev_sha1' => 'phoi', 'rev_len' => 0 ] ],
+                               [ 'MediaWiki:Foo.js' => [ 'page_len' => 0 ] ],
                                'site',
                                false,
                        ],
                        // 'user' module with a non-empty page
                        [
-                               [ 'User:Example/common.js' => [ 'rev_sha1' => 'j7ssba', 'rev_len' => 25 ] ],
+                               [ 'User:Example/common.js' => [ 'page_len' => 25 ] ],
                                'user',
                                false,
                        ],
                        // 'user' module with an empty page
                        [
-                               [ 'User:Example/foo.js' => [ 'rev_sha1' => 'phoi', 'rev_len' => 0 ] ],
+                               [ 'User:Example/foo.js' => [ 'page_len' => 0 ] ],
                                'user',
                                true,
                        ],
diff --git a/tests/phpunit/includes/search/ParserOutputSearchDataExtractorTest.php b/tests/phpunit/includes/search/ParserOutputSearchDataExtractorTest.php
new file mode 100644 (file)
index 0000000..69d0b76
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+
+use MediaWiki\Search\ParserOutputSearchDataExtractor;
+
+/**
+ * @group Search
+ * @covers MediaWiki\Search\ParserOutputSearchDataExtractor
+ */
+class ParserOutputSearchDataExtractorTest extends MediaWikiLangTestCase {
+
+       public function testGetCategories() {
+               $categories = [
+                       'Foo_bar' => 'Bar',
+                       'New_page' => ''
+               ];
+
+               $parserOutput = new ParserOutput( '', [], $categories );
+
+               $searchDataExtractor = new ParserOutputSearchDataExtractor();
+
+               $this->assertEquals(
+                       [ 'Foo bar', 'New page' ],
+                       $searchDataExtractor->getCategories( $parserOutput )
+               );
+       }
+
+       public function testGetExternalLinks() {
+               $parserOutput = new ParserOutput();
+
+               $parserOutput->addExternalLink( 'https://foo' );
+               $parserOutput->addExternalLink( 'https://bar' );
+
+               $searchDataExtractor = new ParserOutputSearchDataExtractor();
+
+               $this->assertEquals(
+                       [ 'https://foo', 'https://bar' ],
+                       $searchDataExtractor->getExternalLinks( $parserOutput )
+               );
+       }
+
+       public function testGetOutgoingLinks() {
+               $parserOutput = new ParserOutput();
+
+               $parserOutput->addLink( Title::makeTitle( NS_MAIN, 'Foo_bar' ), 1 );
+               $parserOutput->addLink( Title::makeTitle( NS_HELP, 'Contents' ), 2 );
+
+               $searchDataExtractor = new ParserOutputSearchDataExtractor();
+
+               // this indexes links with db key
+               $this->assertEquals(
+                       [ 'Foo_bar', 'Help:Contents' ],
+                       $searchDataExtractor->getOutgoingLinks( $parserOutput )
+               );
+       }
+
+       public function testGetTemplates() {
+               $title = Title::makeTitle( NS_TEMPLATE, 'Cite_news' );
+
+               $parserOutput = new ParserOutput();
+               $parserOutput->addTemplate( $title, 10, 100 );
+
+               $searchDataExtractor = new ParserOutputSearchDataExtractor();
+
+               $this->assertEquals(
+                       [ 'Template:Cite news' ],
+                       $searchDataExtractor->getTemplates( $parserOutput )
+               );
+       }
+
+}
index 40a33d9..0b34b6f 100644 (file)
@@ -157,4 +157,54 @@ class SearchEngineTest extends MediaWikiLangTestCase {
                        "Title power search failed" );
        }
 
+       /**
+        * @covers SearchEngine::getSearchIndexFields
+        */
+       public function testSearchIndexFields() {
+               /**
+                * @var $mockEngine SearchEngine
+                */
+               $mockEngine = $this->getMock( 'SearchEngine', [ 'makeSearchFieldMapping' ] );
+
+               $mockFieldBuilder = function ( $name, $type ) {
+                       $mockField =
+                               $this->getMockBuilder( 'SearchIndexFieldDefinition' )->setConstructorArgs( [
+                                       $name,
+                                       $type
+                               ] )->getMock();
+
+                       $mockField->expects( $this->any() )->method( 'getMapping' )->willReturn( [
+                               'testData' => 'test',
+                               'name' => $name,
+                               'type' => $type,
+                       ] );
+
+                       $mockField->expects( $this->any() )
+                               ->method( 'merge' )
+                               ->willReturn( $mockField );
+
+                       return $mockField;
+               };
+
+               $mockEngine->expects( $this->atLeastOnce() )
+                       ->method( 'makeSearchFieldMapping' )
+                       ->willReturnCallback( $mockFieldBuilder );
+
+               // Not using mock since PHPUnit mocks do not work properly with references in params
+               $this->setTemporaryHook( 'SearchIndexFields',
+                       function ( &$fields, SearchEngine $engine ) use ( $mockFieldBuilder ) {
+                               $fields['testField'] =
+                                       $mockFieldBuilder( "testField", SearchIndexField::INDEX_TYPE_TEXT );
+                               return true;
+                       } );
+
+               $fields = $mockEngine->getSearchIndexFields();
+               $this->assertArrayHasKey( 'language', $fields );
+               $this->assertArrayHasKey( 'category', $fields );
+               $this->assertInstanceOf( 'SearchIndexField', $fields['testField'] );
+
+               $mapping = $fields['testField']->getMapping( $mockEngine );
+               $this->assertArrayHasKey( 'testData', $mapping );
+               $this->assertEquals( 'test', $mapping['testData'] );
+       }
 }
diff --git a/tests/phpunit/includes/search/SearchIndexFieldTest.php b/tests/phpunit/includes/search/SearchIndexFieldTest.php
new file mode 100644 (file)
index 0000000..ec046a7
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @group Search
+ * @covers SearchIndexFieldDefinition
+ */
+class SearchIndexFieldTest extends MediaWikiTestCase {
+
+       public function getMergeCases() {
+               return [
+                       [ 0, 'test', 0, 'test', true ],
+                       [ SearchIndexField::INDEX_TYPE_NESTED, 'test',
+                         SearchIndexField::INDEX_TYPE_NESTED, 'test', false ],
+                       [ 0, 'test', 0, 'test2', true ],
+                       [ 0, 'test', 1, 'test', false ],
+               ];
+       }
+
+       /**
+        * @dataProvider getMergeCases
+        */
+       public function testMerge( $t1, $n1, $t2, $n2, $result ) {
+               $field1 = $this->getMockBuilder( 'SearchIndexFieldDefinition' )
+                       ->setMethods( [ 'getMapping' ] )
+                       ->setConstructorArgs( [ $n1, $t1 ] )->getMock();
+               $field2 = $this->getMockBuilder( 'SearchIndexFieldDefinition' )
+                       ->setMethods( [ 'getMapping' ] )
+                       ->setConstructorArgs( [ $n2, $t2 ] )->getMock();
+
+               if ( $result ) {
+                       $this->assertNotFalse( $field1->merge( $field2 ) );
+               } else {
+                       $this->assertFalse( $field1->merge( $field2 ) );
+               }
+
+               $field1->setFlag( 0xFF );
+               $this->assertFalse( $field1->merge( $field2 ) );
+       }
+}
index e725fee..1ebb07c 100644 (file)
@@ -867,303 +867,6 @@ class SessionManagerTest extends MediaWikiTestCase {
                $this->assertTrue( SessionManager::validateSessionId( $id ), "Generated ID: $id" );
        }
 
-       public function testAutoCreateUser() {
-               global $wgGroupPermissions, $wgDisableAuthManager;
-
-               if ( !$wgDisableAuthManager ) {
-                       $this->markTestSkipped( 'AuthManager is not disabled' );
-               }
-
-               \ObjectCache::$instances[__METHOD__] = new TestBagOStuff();
-               $this->setMwGlobals( [ 'wgMainCacheType' => __METHOD__ ] );
-               $this->setMwGlobals( [
-                       'wgAuth' => new AuthPlugin,
-               ] );
-
-               $this->stashMwGlobals( [ 'wgGroupPermissions' ] );
-               $wgGroupPermissions['*']['createaccount'] = true;
-               $wgGroupPermissions['*']['autocreateaccount'] = false;
-
-               // Replace the global singleton with one configured for testing
-               $manager = $this->getManager();
-               $reset = TestUtils::setSessionManagerSingleton( $manager );
-
-               $logger = new \TestLogger( true, function ( $m ) {
-                       if ( substr( $m, 0, 15 ) === 'SessionBackend ' ) {
-                               // Don't care.
-                               return null;
-                       }
-                       $m = str_replace( 'MediaWiki\Session\SessionManager::autoCreateUser: ', '', $m );
-                       return $m;
-               } );
-               $manager->setLogger( $logger );
-
-               $session = SessionManager::getGlobalSession();
-
-               // Can't create an already-existing user
-               $user = User::newFromName( 'UTSysop' );
-               $id = $user->getId();
-               $this->assertFalse( $manager->autoCreateUser( $user ) );
-               $this->assertSame( $id, $user->getId() );
-               $this->assertSame( 'UTSysop', $user->getName() );
-               $this->assertSame( [], $logger->getBuffer() );
-               $logger->clearBuffer();
-
-               // Sanity check that creation works at all
-               $user = User::newFromName( 'UTSessionAutoCreate1' );
-               $this->assertSame( 0, $user->getId(), 'sanity check' );
-               $this->assertTrue( $manager->autoCreateUser( $user ) );
-               $this->assertNotEquals( 0, $user->getId() );
-               $this->assertSame( 'UTSessionAutoCreate1', $user->getName() );
-               $this->assertEquals(
-                       $user->getId(), User::idFromName( 'UTSessionAutoCreate1', User::READ_LATEST )
-               );
-               $this->assertSame( [
-                       [ LogLevel::INFO, 'creating new user ({username}) - from: {url}' ],
-               ], $logger->getBuffer() );
-               $logger->clearBuffer();
-
-               // Check lack of permissions
-               $wgGroupPermissions['*']['createaccount'] = false;
-               $wgGroupPermissions['*']['autocreateaccount'] = false;
-               $user = User::newFromName( 'UTDoesNotExist' );
-               $this->assertFalse( $manager->autoCreateUser( $user ) );
-               $this->assertSame( 0, $user->getId() );
-               $this->assertNotSame( 'UTDoesNotExist', $user->getName() );
-               $this->assertEquals( 0, User::idFromName( 'UTDoesNotExist', User::READ_LATEST ) );
-               $session->clear();
-               $this->assertSame( [
-                       [
-                               LogLevel::DEBUG,
-                               'user is blocked from this wiki, blacklisting',
-                       ],
-               ], $logger->getBuffer() );
-               $logger->clearBuffer();
-
-               // Check other permission
-               $wgGroupPermissions['*']['createaccount'] = false;
-               $wgGroupPermissions['*']['autocreateaccount'] = true;
-               $user = User::newFromName( 'UTSessionAutoCreate2' );
-               $this->assertSame( 0, $user->getId(), 'sanity check' );
-               $this->assertTrue( $manager->autoCreateUser( $user ) );
-               $this->assertNotEquals( 0, $user->getId() );
-               $this->assertSame( 'UTSessionAutoCreate2', $user->getName() );
-               $this->assertEquals(
-                       $user->getId(), User::idFromName( 'UTSessionAutoCreate2', User::READ_LATEST )
-               );
-               $this->assertSame( [
-                       [ LogLevel::INFO, 'creating new user ({username}) - from: {url}' ],
-               ], $logger->getBuffer() );
-               $logger->clearBuffer();
-
-               // Test account-creation block
-               $anon = new User;
-               $block = new \Block( [
-                       'address' => $anon->getName(),
-                       'user' => $id,
-                       'reason' => __METHOD__,
-                       'expiry' => time() + 100500,
-                       'createAccount' => true,
-               ] );
-               $block->insert();
-               $this->assertInstanceOf( 'Block', $anon->isBlockedFromCreateAccount(), 'sanity check' );
-               $reset2 = new \ScopedCallback( [ $block, 'delete' ] );
-               $user = User::newFromName( 'UTDoesNotExist' );
-               $this->assertFalse( $manager->autoCreateUser( $user ) );
-               $this->assertSame( 0, $user->getId() );
-               $this->assertNotSame( 'UTDoesNotExist', $user->getName() );
-               $this->assertEquals( 0, User::idFromName( 'UTDoesNotExist', User::READ_LATEST ) );
-               \ScopedCallback::consume( $reset2 );
-               $session->clear();
-               $this->assertSame( [
-                       [ LogLevel::DEBUG, 'user is blocked from this wiki, blacklisting' ],
-               ], $logger->getBuffer() );
-               $logger->clearBuffer();
-
-               // Sanity check that creation still works
-               $user = User::newFromName( 'UTSessionAutoCreate3' );
-               $this->assertSame( 0, $user->getId(), 'sanity check' );
-               $this->assertTrue( $manager->autoCreateUser( $user ) );
-               $this->assertNotEquals( 0, $user->getId() );
-               $this->assertSame( 'UTSessionAutoCreate3', $user->getName() );
-               $this->assertEquals(
-                       $user->getId(), User::idFromName( 'UTSessionAutoCreate3', User::READ_LATEST )
-               );
-               $this->assertSame( [
-                       [ LogLevel::INFO, 'creating new user ({username}) - from: {url}' ],
-               ], $logger->getBuffer() );
-               $logger->clearBuffer();
-
-               // Test prevention by AuthPlugin
-               global $wgAuth;
-               $oldWgAuth = $wgAuth;
-               $mockWgAuth = $this->getMock( 'AuthPlugin', [ 'autoCreate' ] );
-               $mockWgAuth->expects( $this->once() )->method( 'autoCreate' )
-                       ->will( $this->returnValue( false ) );
-               $this->setMwGlobals( [
-                       'wgAuth' => $mockWgAuth,
-               ] );
-               $user = User::newFromName( 'UTDoesNotExist' );
-               $this->assertFalse( $manager->autoCreateUser( $user ) );
-               $this->assertSame( 0, $user->getId() );
-               $this->assertNotSame( 'UTDoesNotExist', $user->getName() );
-               $this->assertEquals( 0, User::idFromName( 'UTDoesNotExist', User::READ_LATEST ) );
-               $this->setMwGlobals( [
-                       'wgAuth' => $oldWgAuth,
-               ] );
-               $session->clear();
-               $this->assertSame( [
-                       [ LogLevel::DEBUG, 'denied by AuthPlugin' ],
-               ], $logger->getBuffer() );
-               $logger->clearBuffer();
-
-               // Test prevention by wfReadOnly()
-               $this->setMwGlobals( [
-                       'wgReadOnly' => 'Because',
-               ] );
-               $user = User::newFromName( 'UTDoesNotExist' );
-               $this->assertFalse( $manager->autoCreateUser( $user ) );
-               $this->assertSame( 0, $user->getId() );
-               $this->assertNotSame( 'UTDoesNotExist', $user->getName() );
-               $this->assertEquals( 0, User::idFromName( 'UTDoesNotExist', User::READ_LATEST ) );
-               $this->setMwGlobals( [
-                       'wgReadOnly' => false,
-               ] );
-               $session->clear();
-               $this->assertSame( [
-                       [ LogLevel::DEBUG, 'denied by wfReadOnly()' ],
-               ], $logger->getBuffer() );
-               $logger->clearBuffer();
-
-               // Test prevention by a previous session
-               $session->set( 'MWSession::AutoCreateBlacklist', 'test' );
-               $user = User::newFromName( 'UTDoesNotExist' );
-               $this->assertFalse( $manager->autoCreateUser( $user ) );
-               $this->assertSame( 0, $user->getId() );
-               $this->assertNotSame( 'UTDoesNotExist', $user->getName() );
-               $this->assertEquals( 0, User::idFromName( 'UTDoesNotExist', User::READ_LATEST ) );
-               $session->clear();
-               $this->assertSame( [
-                       [ LogLevel::DEBUG, 'blacklisted in session (test)' ],
-               ], $logger->getBuffer() );
-               $logger->clearBuffer();
-
-               // Test uncreatable name
-               $user = User::newFromName( 'UTDoesNotExist@' );
-               $this->assertFalse( $manager->autoCreateUser( $user ) );
-               $this->assertSame( 0, $user->getId() );
-               $this->assertNotSame( 'UTDoesNotExist@', $user->getName() );
-               $this->assertEquals( 0, User::idFromName( 'UTDoesNotExist', User::READ_LATEST ) );
-               $session->clear();
-               $this->assertSame( [
-                       [ LogLevel::DEBUG, 'Invalid username, blacklisting' ],
-               ], $logger->getBuffer() );
-               $logger->clearBuffer();
-
-               // Test AbortAutoAccount hook
-               $mock = $this->getMock( __CLASS__, [ 'onAbortAutoAccount' ] );
-               $mock->expects( $this->once() )->method( 'onAbortAutoAccount' )
-                       ->will( $this->returnCallback( function ( User $user, &$msg ) {
-                               $msg = 'No way!';
-                               return false;
-                       } ) );
-               $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'AbortAutoAccount' => [ $mock ] ] );
-               $user = User::newFromName( 'UTDoesNotExist' );
-               $this->assertFalse( $manager->autoCreateUser( $user ) );
-               $this->assertSame( 0, $user->getId() );
-               $this->assertNotSame( 'UTDoesNotExist', $user->getName() );
-               $this->assertEquals( 0, User::idFromName( 'UTDoesNotExist', User::READ_LATEST ) );
-               $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'AbortAutoAccount' => [] ] );
-               $session->clear();
-               $this->assertSame( [
-                       [ LogLevel::DEBUG, 'denied by hook: No way!' ],
-               ], $logger->getBuffer() );
-               $logger->clearBuffer();
-
-               // Test AbortAutoAccount hook screwing up the name
-               $mock = $this->getMock( 'stdClass', [ 'onAbortAutoAccount' ] );
-               $mock->expects( $this->once() )->method( 'onAbortAutoAccount' )
-                       ->will( $this->returnCallback( function ( User $user ) {
-                               $user->setName( 'UTDoesNotExistEither' );
-                       } ) );
-               $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'AbortAutoAccount' => [ $mock ] ] );
-               try {
-                       $user = User::newFromName( 'UTDoesNotExist' );
-                       $manager->autoCreateUser( $user );
-                       $this->fail( 'Expected exception not thrown' );
-               } catch ( \UnexpectedValueException $ex ) {
-                       $this->assertSame(
-                               'AbortAutoAccount hook tried to change the user name',
-                               $ex->getMessage()
-                       );
-               }
-               $this->assertSame( 0, $user->getId() );
-               $this->assertNotSame( 'UTDoesNotExist', $user->getName() );
-               $this->assertNotSame( 'UTDoesNotExistEither', $user->getName() );
-               $this->assertEquals( 0, User::idFromName( 'UTDoesNotExist', User::READ_LATEST ) );
-               $this->assertEquals( 0, User::idFromName( 'UTDoesNotExistEither', User::READ_LATEST ) );
-               $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'AbortAutoAccount' => [] ] );
-               $session->clear();
-               $this->assertSame( [], $logger->getBuffer() );
-               $logger->clearBuffer();
-
-               // Test for "exception backoff"
-               $user = User::newFromName( 'UTDoesNotExist' );
-               $cache = \ObjectCache::getLocalClusterInstance();
-               $backoffKey = wfMemcKey( 'MWSession', 'autocreate-failed', md5( $user->getName() ) );
-               $cache->set( $backoffKey, 1, 60 * 10 );
-               $this->assertFalse( $manager->autoCreateUser( $user ) );
-               $this->assertSame( 0, $user->getId() );
-               $this->assertNotSame( 'UTDoesNotExist', $user->getName() );
-               $this->assertEquals( 0, User::idFromName( 'UTDoesNotExist', User::READ_LATEST ) );
-               $cache->delete( $backoffKey );
-               $session->clear();
-               $this->assertSame( [
-                       [ LogLevel::DEBUG, 'denied by prior creation attempt failures' ],
-               ], $logger->getBuffer() );
-               $logger->clearBuffer();
-
-               // Sanity check that creation still works, and test completion hook
-               $cb = $this->callback( function ( User $user ) {
-                       $this->assertNotEquals( 0, $user->getId() );
-                       $this->assertSame( 'UTSessionAutoCreate4', $user->getName() );
-                       $this->assertEquals(
-                               $user->getId(), User::idFromName( 'UTSessionAutoCreate4', User::READ_LATEST )
-                       );
-                       return true;
-               } );
-               $mock = $this->getMock( 'stdClass',
-                       [ 'onAuthPluginAutoCreate', 'onLocalUserCreated' ] );
-               $mock->expects( $this->once() )->method( 'onAuthPluginAutoCreate' )
-                       ->with( $cb );
-               $mock->expects( $this->once() )->method( 'onLocalUserCreated' )
-                       ->with( $cb, $this->identicalTo( true ) );
-               $this->mergeMwGlobalArrayValue( 'wgHooks', [
-                       'AuthPluginAutoCreate' => [ $mock ],
-                       'LocalUserCreated' => [ $mock ],
-               ] );
-               $user = User::newFromName( 'UTSessionAutoCreate4' );
-               $this->assertSame( 0, $user->getId(), 'sanity check' );
-               $this->assertTrue( $manager->autoCreateUser( $user ) );
-               $this->assertNotEquals( 0, $user->getId() );
-               $this->assertSame( 'UTSessionAutoCreate4', $user->getName() );
-               $this->assertEquals(
-                       $user->getId(),
-                       User::idFromName( 'UTSessionAutoCreate4', User::READ_LATEST )
-               );
-               $this->mergeMwGlobalArrayValue( 'wgHooks', [
-                       'AuthPluginAutoCreate' => [],
-                       'LocalUserCreated' => [],
-               ] );
-               $this->assertSame( [
-                       [ LogLevel::INFO, 'creating new user ({username}) - from: {url}' ],
-               ], $logger->getBuffer() );
-               $logger->clearBuffer();
-       }
-
-       public function onAbortAutoAccount( User $user, &$msg ) {
-       }
-
        public function testPreventSessionsForUser() {
                $manager = $this->getManager();
 
index 9e3a620..ff544cd 100644 (file)
@@ -35,7 +35,7 @@ class SkinTemplateTest extends MediaWikiTestCase {
                                        'text' => 'text'
                                ],
                                [],
-                               'Test makteListItem with normal values'
+                               'Test makeListItem with normal values'
                        ]
                ];
        }
index 4ea9686..3fa8a9f 100644 (file)
@@ -29,10 +29,10 @@ class SpecialSearchTest extends MediaWikiTestCase {
                        $this->newUserWithSearchNS( $userOptions )
                );
                /*
-               $context->setRequest( new FauxRequest( array(
+               $context->setRequest( new FauxRequest( [
                        'ns5'=>true,
                        'ns6'=>true,
-               ) ));
+               ] ));
                 */
                $context->setRequest( new FauxRequest( $requested ) );
                $search = new SpecialSearch();
diff --git a/tests/phpunit/includes/tidy/BalancerTest.php b/tests/phpunit/includes/tidy/BalancerTest.php
new file mode 100644 (file)
index 0000000..f69ecaf
--- /dev/null
@@ -0,0 +1,155 @@
+<?php
+
+class BalancerTest extends MediaWikiTestCase {
+       private $balancer;
+
+       /**
+        * Anything that needs to happen before your tests should go here.
+        */
+       protected function setUp() {
+               // Be sure to do call the parent setup and teardown functions.
+               // This makes sure that all the various cleanup and restorations
+               // happen as they should (including the restoration for setMwGlobals).
+               parent::setUp();
+               $this->balancer = new MediaWiki\Tidy\Balancer( [
+                       'strict' => false, /* not strict */
+                       'allowedHtmlElements' => null, /* no sanitization */
+                       'tidyCompat' => false, /* standard parser */
+                       'allowComments' => true, /* comment parsing */
+               ] );
+       }
+
+       /**
+        * @covers MediaWiki\Tidy\Balancer::balance
+        * @dataProvider provideBalancerTests
+        */
+       public function testBalancer( $description, $input, $expected ) {
+               $output = $this->balancer->balance( $input );
+
+               // Ignore self-closing tags
+               $output = preg_replace( '/\s*\/>/', '>', $output );
+
+               $this->assertEquals( $expected, $output, $description );
+       }
+
+       public static function provideBalancerTests() {
+               // Get the tests from html5lib-tests.json
+               $json = json_decode( file_get_contents(
+                       __DIR__ . '/html5lib-tests.json'
+               ), true );
+               // Munge this slightly into the format phpunit expects
+               // for providers, and filter out HTML constructs which
+               // the balancer doesn't support.
+               $tests = [];
+               $okre = "~ \A
+                       (?i:<!DOCTYPE\ html>)?
+                       <html><head></head><body>
+                       .*
+                       </body></html>
+               \z ~xs";
+               foreach ( $json as $filename => $cases ) {
+                       foreach ( $cases as $case ) {
+                               $html = $case['document']['html'];
+                               if ( !preg_match( $okre, $html ) ) {
+                                       // Skip tests which involve stuff in the <head> or
+                                       // weird doctypes.
+                                       continue;
+                               }
+                               // We used to do this:
+                               //   $html = substr( $html, strlen( $start ), -strlen( $end ) );
+                               // But now we use a different field in the test case,
+                               // which reports how domino would parse this case in a
+                               // no-quirks <body> context.  (The original test case may
+                               // have had a different context, or relied on quirks mode.)
+                               $html = $case['document']['noQuirksBodyHtml'];
+                               // Normalize case of SVG attributes.
+                               $html = str_replace( 'foreignObject', 'foreignobject', $html );
+                               // Normalize case of MathML attributes.
+                               $html = str_replace( 'definitionURL', 'definitionurl', $html );
+
+                               if (
+                                       isset( $case['document']['props']['comment'] ) &&
+                                       preg_match( ',<!--[^>]*<,', $html )
+                               ) {
+                                       // Skip tests which include HTML comments containing
+                                       // the < character, which we don't support.
+                                       continue;
+                               }
+                               if ( strpos( $case['data'], '<![CDATA[' ) !== false ) {
+                                       // Skip tests involving <![CDATA[ ]]> quoting.
+                                       continue;
+                               }
+                               if (
+                                       stripos( $case['data'], '<!DOCTYPE' ) !== false &&
+                                       stripos( $case['data'], '<!DOCTYPE html>' ) === false
+                               ) {
+                                       // Skip tests involving unusual doctypes.
+                                       continue;
+                               }
+                               $literalre = "~ <rdar: | <isindex | < /? (
+                                       html | head | body | frame | frameset | plaintext
+                               ) > ~xi";
+                               if ( preg_match( $literalre, $case['data'] ) ) {
+                                       // Skip tests involving some literal tags, which are
+                                       // unsupported but don't show up in the expected output.
+                                       continue;
+                               }
+                               if (
+                                       isset( $case['document']['props']['tags']['iframe'] ) ||
+                                       isset( $case['document']['props']['tags']['noembed'] ) ||
+                                       isset( $case['document']['props']['tags']['noscript'] ) ||
+                                       isset( $case['document']['props']['tags']['script'] ) ||
+                                       isset( $case['document']['props']['tags']['svg script'] ) ||
+                                       isset( $case['document']['props']['tags']['svg title'] ) ||
+                                       isset( $case['document']['props']['tags']['title'] ) ||
+                                       isset( $case['document']['props']['tags']['xmp'] )
+                               ) {
+                                       // Skip tests with unsupported tags which *do* show
+                                       // up in the expected output.
+                                       continue;
+                               }
+                               if (
+                                       $filename === 'entities01.dat' ||
+                                       $filename === 'entities02.dat' ||
+                                       preg_match( '/&([a-z]+|#x[0-9A-F]+);/i', $case['data'] ) ||
+                                       preg_match( '/^(&|&#|&#X|&#x|&#45|&x-test|&AMP)$/', $case['data'] )
+                               ) {
+                                       // Skip tests involving entity encoding.
+                                       continue;
+                               }
+                               if (
+                                       isset( $case['document']['props']['tagWithLt'] ) ||
+                                       isset( $case['document']['props']['attrWithFunnyChar'] ) ||
+                                       preg_match( ':^(</b test|<di|<foo bar=qux/>)$:', $case['data'] ) ||
+                                       preg_match( ':</p<p>:', $case['data'] ) ||
+                                       preg_match( ':<b &=&amp>|<p/x/y/z>:', $case['data'] )
+                               ) {
+                                       // Skip tests with funny tag or attribute names,
+                                       // which are really tests of the HTML tokenizer, not
+                                       // the tree builder.
+                                       continue;
+                               }
+                               if (
+                                       preg_match( ':encoding=" text/html "|type=" hidden":', $case['data'] )
+                               ) {
+                                       // The Sanitizer normalizes whitespace in attribute
+                                       // values, which makes this test case invalid.
+                                       continue;
+                               }
+                               if ( $filename === 'plain-text-unsafe.dat' ) {
+                                       // Skip tests with ASCII null, etc.
+                                       continue;
+                               }
+                               $data = preg_replace(
+                                       '~<!DOCTYPE html>~i', '', $case['data']
+                               );
+                               $tests[] = [
+                                       $filename, # use better description?
+                                       $data,
+                                       $html
+                               ];
+                       }
+               }
+               return $tests;
+       }
+}
diff --git a/tests/phpunit/includes/tidy/html5lib-tests.json b/tests/phpunit/includes/tidy/html5lib-tests.json
new file mode 100644 (file)
index 0000000..beb3659
--- /dev/null
@@ -0,0 +1,78905 @@
+{
+  "adoption01.dat": [
+    {
+      "data": "<a><p></a></p>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,10): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "a"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a></a><p><a></a></p></body></html>",
+        "noQuirksBodyHtml": "<a></a><p><a></a></p>"
+      }
+    },
+    {
+      "data": "<a>1<p>2</a>3</p>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,12): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "text": "1"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "text": "2"
+                          }
+                        ]
+                      },
+                      {
+                        "text": "3"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a>1</a><p><a>2</a>3</p></body></html>",
+        "noQuirksBodyHtml": "<a>1</a><p><a>2</a>3</p>"
+      }
+    },
+    {
+      "data": "<a>1<button>2</a>3</button>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,17): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "button": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "text": "1"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "button",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "text": "2"
+                          }
+                        ]
+                      },
+                      {
+                        "text": "3"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a>1</a><button><a>2</a>3</button></body></html>",
+        "noQuirksBodyHtml": "<a>1</a><button><a>2</a>3</button>"
+      }
+    },
+    {
+      "data": "<a>1<b>2</a>3</b>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,12): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "b": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "text": "1"
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "2"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "3"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a>1<b>2</b></a><b>3</b></body></html>",
+        "noQuirksBodyHtml": "<a>1<b>2</b></a><b>3</b>"
+      }
+    },
+    {
+      "data": "<a>1<div>2<div>3</a>4</div>5</div>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,20): adoption-agency-1.3",
+        "(1,20): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "text": "1"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "text": "2"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "tag": "a",
+                            "children": [
+                              {
+                                "text": "3"
+                              }
+                            ]
+                          },
+                          {
+                            "text": "4"
+                          }
+                        ]
+                      },
+                      {
+                        "text": "5"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a>1</a><div><a>2</a><div><a>3</a>4</div>5</div></body></html>",
+        "noQuirksBodyHtml": "<a>1</a><div><a>2</a><div><a>3</a>4</div>5</div>"
+      }
+    },
+    {
+      "data": "<table><a>1<p>2</a>3</p>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,10): unexpected-start-tag-implies-table-voodoo",
+        "(1,11): unexpected-character-implies-table-voodoo",
+        "(1,14): unexpected-start-tag-implies-table-voodoo",
+        "(1,15): unexpected-character-implies-table-voodoo",
+        "(1,19): unexpected-end-tag-implies-table-voodoo",
+        "(1,19): adoption-agency-1.3",
+        "(1,20): unexpected-character-implies-table-voodoo",
+        "(1,24): unexpected-end-tag-implies-table-voodoo",
+        "(1,24): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "p": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "text": "1"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "text": "2"
+                          }
+                        ]
+                      },
+                      {
+                        "text": "3"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a>1</a><p><a>2</a>3</p><table></table></body></html>",
+        "noQuirksBodyHtml": "<a>1</a><p><a>2</a>3</p><table></table>"
+      }
+    },
+    {
+      "data": "<b><b><a><p></a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,16): adoption-agency-1.3",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "a": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "a"
+                          },
+                          {
+                            "tag": "p",
+                            "children": [
+                              {
+                                "tag": "a"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b><b><a></a><p><a></a></p></b></b></body></html>",
+        "noQuirksBodyHtml": "<b><b><a></a><p><a></a></p></b></b>"
+      }
+    },
+    {
+      "data": "<b><a><b><p></a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,16): adoption-agency-1.3",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "a": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "tag": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "p",
+                            "children": [
+                              {
+                                "tag": "a"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b><a><b></b></a><b><p><a></a></p></b></b></body></html>",
+        "noQuirksBodyHtml": "<b><a><b></b></a><b><p><a></a></p></b></b>"
+      }
+    },
+    {
+      "data": "<a><b><b><p></a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,16): adoption-agency-1.3",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "b": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "b"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "p",
+                            "children": [
+                              {
+                                "tag": "a"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a><b><b></b></b></a><b><b><p><a></a></p></b></b></body></html>",
+        "noQuirksBodyHtml": "<a><b><b></b></b></a><b><b><p><a></a></p></b></b>"
+      }
+    },
+    {
+      "data": "<p>1<s id=\"A\">2<b id=\"B\">3</p>4</s>5</b>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,30): unexpected-end-tag",
+        "(1,35): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "s": true,
+            "b": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "1"
+                      },
+                      {
+                        "tag": "s",
+                        "attrs": [
+                          {
+                            "name": "id",
+                            "value": "A"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "text": "2"
+                          },
+                          {
+                            "tag": "b",
+                            "attrs": [
+                              {
+                                "name": "id",
+                                "value": "B"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "text": "3"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "s",
+                    "attrs": [
+                      {
+                        "name": "id",
+                        "value": "A"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "b",
+                        "attrs": [
+                          {
+                            "name": "id",
+                            "value": "B"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "text": "4"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b",
+                    "attrs": [
+                      {
+                        "name": "id",
+                        "value": "B"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "5"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p>1<s id=\"A\">2<b id=\"B\">3</b></s></p><s id=\"A\"><b id=\"B\">4</b></s><b id=\"B\">5</b></body></html>",
+        "noQuirksBodyHtml": "<p>1<s id=\"A\">2<b id=\"B\">3</b></s></p><s id=\"A\"><b id=\"B\">4</b></s><b id=\"B\">5</b>"
+      }
+    },
+    {
+      "data": "<table><a>1<td>2</td>3</table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,10): unexpected-start-tag-implies-table-voodoo",
+        "(1,11): unexpected-character-implies-table-voodoo",
+        "(1,15): unexpected-cell-in-table-body",
+        "(1,30): unexpected-implied-end-tag-in-table-view"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "text": "1"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "text": "3"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "text": "2"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a>1</a><a>3</a><table><tbody><tr><td>2</td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<a>1</a><a>3</a><table><tbody><tr><td>2</td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table>A<td>B</td>C</table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,8): unexpected-character-implies-table-voodoo",
+        "(1,12): unexpected-cell-in-table-body",
+        "(1,22): unexpected-character-implies-table-voodoo"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "AC"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "text": "B"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>AC<table><tbody><tr><td>B</td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "AC<table><tbody><tr><td>B</td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<a><svg><tr><input></a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,23): unexpected-end-tag",
+        "(1,23): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "svg svg": true,
+            "svg tr": true,
+            "svg input": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "tag": "svg",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "input",
+                                "ns": "http://www.w3.org/2000/svg"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a><svg><tr><input></input></tr></svg></a></body></html>",
+        "noQuirksBodyHtml": "<a><svg><tr><input></input></tr></svg></a>"
+      }
+    },
+    {
+      "data": "<div><a><b><div><div><div><div><div><div><div><div><div><div></a>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,65): adoption-agency-1.3",
+        "(1,65): adoption-agency-1.3",
+        "(1,65): adoption-agency-1.3",
+        "(1,65): adoption-agency-1.3",
+        "(1,65): adoption-agency-1.3",
+        "(1,65): adoption-agency-1.3",
+        "(1,65): adoption-agency-1.3",
+        "(1,65): adoption-agency-1.3",
+        "(1,65): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "a": true,
+            "b": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "tag": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "div",
+                            "children": [
+                              {
+                                "tag": "a"
+                              },
+                              {
+                                "tag": "div",
+                                "children": [
+                                  {
+                                    "tag": "a"
+                                  },
+                                  {
+                                    "tag": "div",
+                                    "children": [
+                                      {
+                                        "tag": "a"
+                                      },
+                                      {
+                                        "tag": "div",
+                                        "children": [
+                                          {
+                                            "tag": "a"
+                                          },
+                                          {
+                                            "tag": "div",
+                                            "children": [
+                                              {
+                                                "tag": "a"
+                                              },
+                                              {
+                                                "tag": "div",
+                                                "children": [
+                                                  {
+                                                    "tag": "a"
+                                                  },
+                                                  {
+                                                    "tag": "div",
+                                                    "children": [
+                                                      {
+                                                        "tag": "a"
+                                                      },
+                                                      {
+                                                        "tag": "div",
+                                                        "children": [
+                                                          {
+                                                            "tag": "a",
+                                                            "children": [
+                                                              {
+                                                                "tag": "div",
+                                                                "children": [
+                                                                  {
+                                                                    "tag": "div"
+                                                                  }
+                                                                ]
+                                                              }
+                                                            ]
+                                                          }
+                                                        ]
+                                                      }
+                                                    ]
+                                                  }
+                                                ]
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><a><b></b></a><b><div><a></a><div><a></a><div><a></a><div><a></a><div><a></a><div><a></a><div><a></a><div><a><div><div></div></div></a></div></div></div></div></div></div></div></div></b></div></body></html>",
+        "noQuirksBodyHtml": "<div><a><b></b></a><b><div><a></a><div><a></a><div><a></a><div><a></a><div><a></a><div><a></a><div><a></a><div><a><div><div></div></div></a></div></div></div></div></div></div></div></div></b></div>"
+      }
+    },
+    {
+      "data": "<div><a><b><u><i><code><div></a>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,32): adoption-agency-1.3",
+        "(1,32): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "a": true,
+            "b": true,
+            "u": true,
+            "i": true,
+            "code": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "tag": "b",
+                            "children": [
+                              {
+                                "tag": "u",
+                                "children": [
+                                  {
+                                    "tag": "i",
+                                    "children": [
+                                      {
+                                        "tag": "code"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "u",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "tag": "code",
+                                "children": [
+                                  {
+                                    "tag": "div",
+                                    "children": [
+                                      {
+                                        "tag": "a"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><a><b><u><i><code></code></i></u></b></a><u><i><code><div><a></a></div></code></i></u></div></body></html>",
+        "noQuirksBodyHtml": "<div><a><b><u><i><code></code></i></u></b></a><u><i><code><div><a></a></div></code></i></u></div>"
+      }
+    },
+    {
+      "data": "<b><b><b><b>x</b></b></b></b>y",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "b",
+                            "children": [
+                              {
+                                "tag": "b",
+                                "children": [
+                                  {
+                                    "text": "x"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "text": "y"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b><b><b><b>x</b></b></b></b>y</body></html>",
+        "noQuirksBodyHtml": "<b><b><b><b>x</b></b></b></b>y"
+      }
+    },
+    {
+      "data": "<p><b><b><b><b><p>x",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,18): unexpected-end-tag",
+        "(1,19): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "b": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "b",
+                            "children": [
+                              {
+                                "tag": "b",
+                                "children": [
+                                  {
+                                    "tag": "b"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "b",
+                            "children": [
+                              {
+                                "tag": "b",
+                                "children": [
+                                  {
+                                    "text": "x"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><b><b><b><b></b></b></b></b></p><p><b><b><b>x</b></b></b></p></body></html>",
+        "noQuirksBodyHtml": "<p><b><b><b><b></b></b></b></b></p><p><b><b><b>x</b></b></b></p>"
+      }
+    }
+  ],
+  "adoption02.dat": [
+    {
+      "data": "<b>1<i>2<p>3</b>4",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,16): adoption-agency-1.3",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "i": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "1"
+                      },
+                      {
+                        "tag": "i",
+                        "children": [
+                          {
+                            "text": "2"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "tag": "b",
+                            "children": [
+                              {
+                                "text": "3"
+                              }
+                            ]
+                          },
+                          {
+                            "text": "4"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b>1<i>2</i></b><i><p><b>3</b>4</p></i></body></html>",
+        "noQuirksBodyHtml": "<b>1<i>2</i></b><i><p><b>3</b>4</p></i>"
+      }
+    },
+    {
+      "data": "<a><div><style></style><address><a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,35): unexpected-start-tag-implies-end-tag",
+        "(1,35): adoption-agency-1.3",
+        "(1,35): adoption-agency-1.3",
+        "(1,35): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "div": true,
+            "style": true,
+            "address": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a"
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "tag": "style"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "address",
+                        "children": [
+                          {
+                            "tag": "a"
+                          },
+                          {
+                            "tag": "a"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a></a><div><a><style></style></a><address><a></a><a></a></address></div></body></html>",
+        "noQuirksBodyHtml": "<a></a><div><a><style></style></a><address><a></a><a></a></address></div>"
+      }
+    }
+  ],
+  "comments01.dat": [
+    {
+      "data": "FOO<!-- BAR -->BAZ",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "comment": " BAR "
+                  },
+                  {
+                    "text": "BAZ"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<!-- BAR -->BAZ</body></html>",
+        "noQuirksBodyHtml": "FOO<!-- BAR -->BAZ"
+      }
+    },
+    {
+      "data": "FOO<!-- BAR --!>BAZ",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,15): unexpected-bang-after-double-dash-in-comment"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "comment": " BAR "
+                  },
+                  {
+                    "text": "BAZ"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<!-- BAR -->BAZ</body></html>",
+        "noQuirksBodyHtml": "FOO<!-- BAR -->BAZ"
+      }
+    },
+    {
+      "data": "FOO<!-- BAR --   >BAZ",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,15): unexpected-char-in-comment",
+        "(1,21): eof-in-comment"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "comment": " BAR --   >BAZ"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<!-- BAR --   >BAZ--></body></html>",
+        "noQuirksBodyHtml": "FOO<!-- BAR --   >BAZ-->"
+      }
+    },
+    {
+      "data": "FOO<!-- BAR -- <QUX> -- MUX -->BAZ",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,15): unexpected-char-in-comment",
+        "(1,24): unexpected-char-in-comment"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "comment": " BAR -- <QUX> -- MUX "
+                  },
+                  {
+                    "text": "BAZ"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<!-- BAR -- <QUX> -- MUX -->BAZ</body></html>",
+        "noQuirksBodyHtml": "FOO<!-- BAR -- <QUX> -- MUX -->BAZ"
+      }
+    },
+    {
+      "data": "FOO<!-- BAR -- <QUX> -- MUX --!>BAZ",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,15): unexpected-char-in-comment",
+        "(1,24): unexpected-char-in-comment",
+        "(1,31): unexpected-bang-after-double-dash-in-comment"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "comment": " BAR -- <QUX> -- MUX "
+                  },
+                  {
+                    "text": "BAZ"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<!-- BAR -- <QUX> -- MUX -->BAZ</body></html>",
+        "noQuirksBodyHtml": "FOO<!-- BAR -- <QUX> -- MUX -->BAZ"
+      }
+    },
+    {
+      "data": "FOO<!-- BAR -- <QUX> -- MUX -- >BAZ",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,15): unexpected-char-in-comment",
+        "(1,24): unexpected-char-in-comment",
+        "(1,31): unexpected-char-in-comment",
+        "(1,35): eof-in-comment"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "comment": " BAR -- <QUX> -- MUX -- >BAZ"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<!-- BAR -- <QUX> -- MUX -- >BAZ--></body></html>",
+        "noQuirksBodyHtml": "FOO<!-- BAR -- <QUX> -- MUX -- >BAZ-->"
+      }
+    },
+    {
+      "data": "FOO<!---->BAZ",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "comment": ""
+                  },
+                  {
+                    "text": "BAZ"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<!---->BAZ</body></html>",
+        "noQuirksBodyHtml": "FOO<!---->BAZ"
+      }
+    },
+    {
+      "data": "FOO<!--->BAZ",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,9): incorrect-comment"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "comment": ""
+                  },
+                  {
+                    "text": "BAZ"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<!---->BAZ</body></html>",
+        "noQuirksBodyHtml": "FOO<!---->BAZ"
+      }
+    },
+    {
+      "data": "FOO<!-->BAZ",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,8): incorrect-comment"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "comment": ""
+                  },
+                  {
+                    "text": "BAZ"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<!---->BAZ</body></html>",
+        "noQuirksBodyHtml": "FOO<!---->BAZ"
+      }
+    },
+    {
+      "data": "<?xml version=\"1.0\">Hi",
+      "errors": [
+        "(1,1): expected-tag-name-but-got-question-mark",
+        "(1,22): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "?xml version=\"1.0\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hi"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!--?xml version=\"1.0\"--><html><head></head><body>Hi</body></html>",
+        "noQuirksBodyHtml": "<!--?xml version=\"1.0\"-->Hi"
+      }
+    },
+    {
+      "data": "<?xml version=\"1.0\">",
+      "errors": [
+        "(1,1): expected-tag-name-but-got-question-mark",
+        "(1,20): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "?xml version=\"1.0\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!--?xml version=\"1.0\"--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--?xml version=\"1.0\"-->"
+      }
+    },
+    {
+      "data": "<?xml version",
+      "errors": [
+        "(1,1): expected-tag-name-but-got-question-mark",
+        "(1,13): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "?xml version"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!--?xml version--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--?xml version-->"
+      }
+    },
+    {
+      "data": "FOO<!----->BAZ",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,10): unexpected-dash-after-double-dash-in-comment"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "comment": "-"
+                  },
+                  {
+                    "text": "BAZ"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<!----->BAZ</body></html>",
+        "noQuirksBodyHtml": "FOO<!----->BAZ"
+      }
+    },
+    {
+      "data": "<html><!-- comment --><title>Comment before head</title>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "comment": " comment "
+              },
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "Comment before head"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><!-- comment --><head><title>Comment before head</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<!-- comment --><title>Comment before head</title>"
+      }
+    }
+  ],
+  "doctype01.dat": [
+    {
+      "data": "<!DOCTYPE html>Hello",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!dOctYpE HtMl>Hello",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPEhtml>Hello",
+      "errors": [
+        "(1,9): need-space-after-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE>Hello",
+      "errors": [
+        "(1,9): need-space-after-doctype",
+        "(1,10): expected-doctype-name-but-got-right-bracket",
+        "(1,10): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": ""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE ><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE >Hello",
+      "errors": [
+        "(1,11): expected-doctype-name-but-got-right-bracket",
+        "(1,11): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": ""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE ><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato>Hello",
+      "errors": [
+        "(1,17): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato >Hello",
+      "errors": [
+        "(1,18): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato taco>Hello",
+      "errors": [
+        "(1,17): expected-space-or-right-bracket-in-doctype",
+        "(1,22): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato taco \"ddd>Hello",
+      "errors": [
+        "(1,17): expected-space-or-right-bracket-in-doctype",
+        "(1,27): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato sYstEM>Hello",
+      "errors": [
+        "(1,24): unexpected-char-in-doctype",
+        "(1,24): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato sYstEM    >Hello",
+      "errors": [
+        "(1,28): unexpected-char-in-doctype",
+        "(1,28): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE   potato       sYstEM  ggg>Hello",
+      "errors": [
+        "(1,34): unexpected-char-in-doctype",
+        "(1,37): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato SYSTEM taco  >Hello",
+      "errors": [
+        "(1,25): unexpected-char-in-doctype",
+        "(1,31): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato SYSTEM 'taco\"'>Hello",
+      "errors": [
+        "(1,32): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato \"\" \"taco\"\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato SYSTEM \"taco\">Hello",
+      "errors": [
+        "(1,31): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato \"\" \"taco\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato SYSTEM \"tai'co\">Hello",
+      "errors": [
+        "(1,33): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato \"\" \"tai'co\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato SYSTEMtaco \"ddd\">Hello",
+      "errors": [
+        "(1,24): unexpected-char-in-doctype",
+        "(1,34): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato grass SYSTEM taco>Hello",
+      "errors": [
+        "(1,17): expected-space-or-right-bracket-in-doctype",
+        "(1,35): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato pUbLIc>Hello",
+      "errors": [
+        "(1,24): unexpected-end-of-doctype",
+        "(1,24): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato pUbLIc >Hello",
+      "errors": [
+        "(1,25): unexpected-end-of-doctype",
+        "(1,25): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato pUbLIcgoof>Hello",
+      "errors": [
+        "(1,24): unexpected-char-in-doctype",
+        "(1,28): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato PUBLIC goof>Hello",
+      "errors": [
+        "(1,25): unexpected-char-in-doctype",
+        "(1,29): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato PUBLIC \"go'of\">Hello",
+      "errors": [
+        "(1,32): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato \"go'of\" \"\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato PUBLIC 'go'of'>Hello",
+      "errors": [
+        "(1,29): unexpected-char-in-doctype",
+        "(1,32): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato \"go\" \"\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato PUBLIC 'go:hh   of' >Hello",
+      "errors": [
+        "(1,38): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato \"go:hh   of\" \"\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE potato PUBLIC \"W3C-//dfdf\" SYSTEM ggg>Hello",
+      "errors": [
+        "(1,38): unexpected-char-in-doctype",
+        "(1,48): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "potato \"W3C-//dfdf\" \"\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE potato><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n   \"http://www.w3.org/TR/html4/strict.dtd\">Hello",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE ...>Hello",
+      "errors": [
+        "(1,14): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "..."
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Hello"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE ...><html><head></head><body>Hello</body></html>",
+        "noQuirksBodyHtml": "Hello"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">",
+      "errors": [
+        "(2,58): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\"\n\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">",
+      "errors": [
+        "(2,54): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html \"-//W3C//DTD XHTML 1.0 Frameset//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE root-element [SYSTEM OR PUBLIC FPI] \"uri\" [ \n<!-- internal declarations -->\n]>",
+      "errors": [
+        "(1,23): expected-space-or-right-bracket-in-doctype",
+        "(2,30): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "root-element"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "]>",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE root-element><html><head></head><body>]&gt;</body></html>",
+        "noQuirksBodyHtml": "\n]&gt;"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html PUBLIC\n  \"-//WAPFORUM//DTD XHTML Mobile 1.0//EN\"\n    \"http://www.wapforum.org/DTD/xhtml-mobile10.dtd\">",
+      "errors": [
+        "(3,53): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html \"-//WAPFORUM//DTD XHTML Mobile 1.0//EN\" \"http://www.wapforum.org/DTD/xhtml-mobile10.dtd\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE HTML SYSTEM \"http://www.w3.org/DTD/HTML4-strict.dtd\"><body><b>Mine!</b></body>",
+      "errors": [
+        "(1,63): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html \"\" \"http://www.w3.org/DTD/HTML4-strict.dtd\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "Mine!"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><b>Mine!</b></body></html>",
+        "noQuirksBodyHtml": "<b>Mine!</b>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\"http://www.w3.org/TR/html4/strict.dtd\">",
+      "errors": [
+        "(1,50): unexpected-char-in-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"'http://www.w3.org/TR/html4/strict.dtd'>",
+      "errors": [
+        "(1,50): unexpected-char-in-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE HTML PUBLIC\"-//W3C//DTD HTML 4.01//EN\"'http://www.w3.org/TR/html4/strict.dtd'>",
+      "errors": [
+        "(1,21): unexpected-char-in-doctype",
+        "(1,49): unexpected-char-in-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE HTML PUBLIC'-//W3C//DTD HTML 4.01//EN''http://www.w3.org/TR/html4/strict.dtd'>",
+      "errors": [
+        "(1,21): unexpected-char-in-doctype",
+        "(1,49): unexpected-char-in-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    }
+  ],
+  "domjs-unsafe.dat": [
+    {
+      "data": "<svg><![CDATA[foo\nbar]]>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(2,6): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "foo\nbar"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>foo\nbar</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>foo\nbar</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[foo\rbar]]>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(2,6): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "foo\nbar"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>foo\nbar</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>foo\nbar</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[foo\r\nbar]]>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(2,6): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "foo\nbar"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>foo\nbar</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>foo\nbar</svg>"
+      }
+    },
+    {
+      "data": "<script>a='\u0000'</script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,12): invalid-codepoint"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "a='�'",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script>a='�'</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script>a='�'</script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--\u0000</script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag",
+        "(1,25): invalid-codepoint"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--�",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--�</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--�</script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--foo\u0000</script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag",
+        "(1,28): invalid-codepoint"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--foo�",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--foo�</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--foo�</script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!-- foo-\u0000</script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag",
+        "(1,30): invalid-codepoint"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!-- foo-�",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!-- foo-�</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!-- foo-�</script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!-- foo--\u0000</script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag",
+        "(1,31): invalid-codepoint"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!-- foo--�",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!-- foo--�</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!-- foo--�</script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!-- foo-",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag",
+        "(1,29): expected-script-data-but-got-eof",
+        "(1,29): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!-- foo-",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!-- foo-</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!-- foo-</script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!-- foo-<</script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!-- foo-<",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!-- foo-<</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!-- foo-<</script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!-- foo-<S",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag",
+        "(1,31): expected-script-data-but-got-eof",
+        "(1,31): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!-- foo-<S",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!-- foo-<S</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!-- foo-<S</script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!-- foo-</SCRIPT>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!-- foo-",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!-- foo-</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!-- foo-</script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--<p></script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--<p>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--<p></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--<p></script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--<script></script></script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--<script></script>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--<script></script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--<script></script></script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--<script>\u0000</script></script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag",
+        "(1,33): invalid-codepoint"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--<script>�</script>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--<script>�</script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--<script>�</script></script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--<script>-\u0000</script></script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag",
+        "(1,34): invalid-codepoint"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--<script>-�</script>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--<script>-�</script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--<script>-�</script></script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--<script>--\u0000</script></script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag",
+        "(1,35): invalid-codepoint"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--<script>--�</script>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--<script>--�</script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--<script>--�</script></script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--<script>---</script></script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--<script>---</script>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--<script>---</script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--<script>---</script></script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--<script></scrip></SCRIPT></script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--<script></scrip></SCRIPT>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--<script></scrip></SCRIPT></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--<script></scrip></SCRIPT></script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--<script></scrip </SCRIPT></script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--<script></scrip </SCRIPT>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--<script></scrip </SCRIPT></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--<script></scrip </SCRIPT></script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--<script></scrip/</SCRIPT></script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--<script></scrip/</SCRIPT>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--<script></scrip/</SCRIPT></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--<script></scrip/</SCRIPT></script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"></scrip/></script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "</scrip/>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"></scrip/></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"></scrip/></script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"></scrip ></script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "</scrip >",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"></scrip ></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"></scrip ></script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--</scrip></script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--</scrip>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--</scrip></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--</scrip></script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--</scrip </script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--</scrip ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--</scrip </script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--</scrip </script>"
+      }
+    },
+    {
+      "data": "<script type=\"data\"><!--</scrip/</script>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "data"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "<!--</scrip/",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script type=\"data\"><!--</scrip/</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"data\"><!--</scrip/</script>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><!DOCTYPE html>",
+      "errors": [
+        "(1,30): unexpected-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html><!DOCTYPE html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,21): unexpected-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html><head><!DOCTYPE html></head>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,27): unexpected-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html><head></head><!DOCTYPE html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,34): unexpected-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<body></body><!DOCTYPE html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,28): unexpected-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<table><!DOCTYPE html></table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,22): unexpected-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table></table></body></html>",
+        "noQuirksBodyHtml": "<table></table>"
+      }
+    },
+    {
+      "data": "<select><!DOCTYPE html></select>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,23): unexpected-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select></select></body></html>",
+        "noQuirksBodyHtml": "<select></select>"
+      }
+    },
+    {
+      "data": "<table><colgroup><!DOCTYPE html></colgroup></table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,32): unexpected-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "colgroup": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><colgroup></colgroup></table></body></html>",
+        "noQuirksBodyHtml": "<table><colgroup></colgroup></table>"
+      }
+    },
+    {
+      "data": "<table><colgroup><!--test--></colgroup></table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "colgroup": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup",
+                        "children": [
+                          {
+                            "comment": "test"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><colgroup><!--test--></colgroup></table></body></html>",
+        "noQuirksBodyHtml": "<table><colgroup><!--test--></colgroup></table>"
+      }
+    },
+    {
+      "data": "<table><colgroup><html></colgroup></table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,23): non-html-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "colgroup": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><colgroup></colgroup></table></body></html>",
+        "noQuirksBodyHtml": "<table><colgroup></colgroup></table>"
+      }
+    },
+    {
+      "data": "<table><colgroup> foo</colgroup></table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,32): foster-parenting-character-in-table",
+        "(1,32): foster-parenting-character-in-table",
+        "(1,32): foster-parenting-character-in-table",
+        "(1,32): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "colgroup": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "foo"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup",
+                        "children": [
+                          {
+                            "text": " "
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>foo<table><colgroup> </colgroup></table></body></html>",
+        "noQuirksBodyHtml": "foo<table><colgroup> </colgroup></table>"
+      }
+    },
+    {
+      "data": "<select><!--test--></select>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "comment": "test"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select><!--test--></select></body></html>",
+        "noQuirksBodyHtml": "<select><!--test--></select>"
+      }
+    },
+    {
+      "data": "<select><html></select>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,14): non-html-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select></select></body></html>",
+        "noQuirksBodyHtml": "<select></select>"
+      }
+    },
+    {
+      "data": "<frameset><html></frameset>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,16): non-html-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<frameset></frameset><html>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,27): non-html-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<frameset></frameset><!DOCTYPE html>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,36): unexpected-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html><body></body></html><!DOCTYPE html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,41): unexpected-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<svg><!DOCTYPE html></svg>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,20): unexpected-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg></svg></body></html>",
+        "noQuirksBodyHtml": "<svg></svg>"
+      }
+    },
+    {
+      "data": "<svg><font></font></svg>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg font": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "ns": "http://www.w3.org/2000/svg"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg><font></font></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><font></font></svg>"
+      }
+    },
+    {
+      "data": "<svg><font id=foo></font></svg>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg font": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "attrs": [
+                          {
+                            "name": "id",
+                            "value": "foo"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg><font id=\"foo\"></font></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><font id=\"foo\"></font></svg>"
+      }
+    },
+    {
+      "data": "<svg><font size=4></font></svg>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,18): unexpected-html-element-in-foreign-content",
+        "(1,31): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "font": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  },
+                  {
+                    "tag": "font",
+                    "attrs": [
+                      {
+                        "name": "size",
+                        "value": "4"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg></svg><font size=\"4\"></font></body></html>",
+        "noQuirksBodyHtml": "<svg><font size=\"4\"></font></svg>"
+      }
+    },
+    {
+      "data": "<svg><font color=red></font></svg>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,21): unexpected-html-element-in-foreign-content",
+        "(1,34): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "font": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  },
+                  {
+                    "tag": "font",
+                    "attrs": [
+                      {
+                        "name": "color",
+                        "value": "red"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg></svg><font color=\"red\"></font></body></html>",
+        "noQuirksBodyHtml": "<svg><font color=\"red\"></font></svg>"
+      }
+    },
+    {
+      "data": "<svg><font font=sans></font></svg>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg font": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "attrs": [
+                          {
+                            "name": "font",
+                            "value": "sans"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg><font font=\"sans\"></font></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><font font=\"sans\"></font></svg>"
+      }
+    }
+  ],
+  "entities01.dat": [
+    {
+      "data": "FOO&gt;BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO>BAR",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO&gt;BAR</body></html>",
+        "noQuirksBodyHtml": "FOO&gt;BAR"
+      }
+    },
+    {
+      "data": "FOO&gtBAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,6): named-entity-without-semicolon"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO>BAR",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO&gt;BAR</body></html>",
+        "noQuirksBodyHtml": "FOO&gt;BAR"
+      }
+    },
+    {
+      "data": "FOO&gt BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,6): named-entity-without-semicolon"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO> BAR",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO&gt; BAR</body></html>",
+        "noQuirksBodyHtml": "FOO&gt; BAR"
+      }
+    },
+    {
+      "data": "FOO&gt;;;BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO>;;BAR",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO&gt;;;BAR</body></html>",
+        "noQuirksBodyHtml": "FOO&gt;;;BAR"
+      }
+    },
+    {
+      "data": "I'm &notit; I tell you",
+      "errors": [
+        "(1,4): expected-doctype-but-got-chars",
+        "(1,9): named-entity-without-semicolon"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "I'm ¬it; I tell you"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>I'm ¬it; I tell you</body></html>",
+        "noQuirksBodyHtml": "I'm ¬it; I tell you"
+      }
+    },
+    {
+      "data": "I'm &notin; I tell you",
+      "errors": [
+        "(1,4): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "I'm ∉ I tell you"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>I'm ∉ I tell you</body></html>",
+        "noQuirksBodyHtml": "I'm ∉ I tell you"
+      }
+    },
+    {
+      "data": "FOO& BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO& BAR",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO&amp; BAR</body></html>",
+        "noQuirksBodyHtml": "FOO&amp; BAR"
+      }
+    },
+    {
+      "data": "FOO&<BAR>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,9): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "bar": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO&",
+                    "escaped": true
+                  },
+                  {
+                    "tag": "bar"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO&amp;<bar></bar></body></html>",
+        "noQuirksBodyHtml": "FOO&amp;<bar></bar>"
+      }
+    },
+    {
+      "data": "FOO&&&&gt;BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO&&&>BAR",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO&amp;&amp;&amp;&gt;BAR</body></html>",
+        "noQuirksBodyHtml": "FOO&amp;&amp;&amp;&gt;BAR"
+      }
+    },
+    {
+      "data": "FOO&#41;BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO)BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO)BAR</body></html>",
+        "noQuirksBodyHtml": "FOO)BAR"
+      }
+    },
+    {
+      "data": "FOO&#x41;BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOABAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOABAR</body></html>",
+        "noQuirksBodyHtml": "FOOABAR"
+      }
+    },
+    {
+      "data": "FOO&#X41;BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOABAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOABAR</body></html>",
+        "noQuirksBodyHtml": "FOOABAR"
+      }
+    },
+    {
+      "data": "FOO&#BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,5): expected-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO&#BAR",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO&amp;#BAR</body></html>",
+        "noQuirksBodyHtml": "FOO&amp;#BAR"
+      }
+    },
+    {
+      "data": "FOO&#ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,5): expected-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO&#ZOO",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO&amp;#ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO&amp;#ZOO"
+      }
+    },
+    {
+      "data": "FOO&#xBAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,7): expected-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOºR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOºR</body></html>",
+        "noQuirksBodyHtml": "FOOºR"
+      }
+    },
+    {
+      "data": "FOO&#xZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,6): expected-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO&#xZOO",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO&amp;#xZOO</body></html>",
+        "noQuirksBodyHtml": "FOO&amp;#xZOO"
+      }
+    },
+    {
+      "data": "FOO&#XZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,6): expected-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO&#XZOO",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO&amp;#XZOO</body></html>",
+        "noQuirksBodyHtml": "FOO&amp;#XZOO"
+      }
+    },
+    {
+      "data": "FOO&#41BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,7): numeric-entity-without-semicolon"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO)BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO)BAR</body></html>",
+        "noQuirksBodyHtml": "FOO)BAR"
+      }
+    },
+    {
+      "data": "FOO&#x41BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,10): numeric-entity-without-semicolon"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO䆺R"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO䆺R</body></html>",
+        "noQuirksBodyHtml": "FOO䆺R"
+      }
+    },
+    {
+      "data": "FOO&#x41ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,8): numeric-entity-without-semicolon"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOAZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOAZOO</body></html>",
+        "noQuirksBodyHtml": "FOOAZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0000;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO�ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO�ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO�ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0078;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOxZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOxZOO</body></html>",
+        "noQuirksBodyHtml": "FOOxZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0079;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOyZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOyZOO</body></html>",
+        "noQuirksBodyHtml": "FOOyZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0080;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO€ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO€ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO€ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0081;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO\81ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO\81ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO\81ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0082;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO‚ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO‚ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO‚ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0083;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOƒZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOƒZOO</body></html>",
+        "noQuirksBodyHtml": "FOOƒZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0084;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO„ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO„ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO„ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0085;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO…ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO…ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO…ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0086;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO†ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO†ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO†ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0087;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO‡ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO‡ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO‡ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0088;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOˆZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOˆZOO</body></html>",
+        "noQuirksBodyHtml": "FOOˆZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0089;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO‰ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO‰ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO‰ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x008A;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOŠZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOŠZOO</body></html>",
+        "noQuirksBodyHtml": "FOOŠZOO"
+      }
+    },
+    {
+      "data": "FOO&#x008B;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO‹ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO‹ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO‹ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x008C;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOŒZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOŒZOO</body></html>",
+        "noQuirksBodyHtml": "FOOŒZOO"
+      }
+    },
+    {
+      "data": "FOO&#x008D;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO\8dZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO\8dZOO</body></html>",
+        "noQuirksBodyHtml": "FOO\8dZOO"
+      }
+    },
+    {
+      "data": "FOO&#x008E;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOŽZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOŽZOO</body></html>",
+        "noQuirksBodyHtml": "FOOŽZOO"
+      }
+    },
+    {
+      "data": "FOO&#x008F;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO\8fZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO\8fZOO</body></html>",
+        "noQuirksBodyHtml": "FOO\8fZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0090;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO\90ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO\90ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO\90ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0091;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO‘ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO‘ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO‘ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0092;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO’ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO’ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO’ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0093;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO“ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO“ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO“ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0094;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO”ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO”ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO”ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0095;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO•ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO•ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO•ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0096;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO–ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO–ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO–ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0097;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO—ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO—ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO—ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0098;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO˜ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO˜ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO˜ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x0099;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO™ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO™ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO™ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x009A;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOšZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOšZOO</body></html>",
+        "noQuirksBodyHtml": "FOOšZOO"
+      }
+    },
+    {
+      "data": "FOO&#x009B;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO›ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO›ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO›ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x009C;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOœZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOœZOO</body></html>",
+        "noQuirksBodyHtml": "FOOœZOO"
+      }
+    },
+    {
+      "data": "FOO&#x009D;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO\9dZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO\9dZOO</body></html>",
+        "noQuirksBodyHtml": "FOO\9dZOO"
+      }
+    },
+    {
+      "data": "FOO&#x009E;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOžZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOžZOO</body></html>",
+        "noQuirksBodyHtml": "FOOžZOO"
+      }
+    },
+    {
+      "data": "FOO&#x009F;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOŸZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOŸZOO</body></html>",
+        "noQuirksBodyHtml": "FOOŸZOO"
+      }
+    },
+    {
+      "data": "FOO&#x00A0;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO ZOO",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO&nbsp;ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO&nbsp;ZOO"
+      }
+    },
+    {
+      "data": "FOO&#xD7FF;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO퟿ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO퟿ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO퟿ZOO"
+      }
+    },
+    {
+      "data": "FOO&#xD800;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO�ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO�ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO�ZOO"
+      }
+    },
+    {
+      "data": "FOO&#xD801;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO�ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO�ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO�ZOO"
+      }
+    },
+    {
+      "data": "FOO&#xDFFE;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO�ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO�ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO�ZOO"
+      }
+    },
+    {
+      "data": "FOO&#xDFFF;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO�ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO�ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO�ZOO"
+      }
+    },
+    {
+      "data": "FOO&#xE000;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOOZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOOZOO</body></html>",
+        "noQuirksBodyHtml": "FOOZOO"
+      }
+    },
+    {
+      "data": "FOO&#x10FFFE;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,13): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO􏿾ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO􏿾ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO􏿾ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x1087D4;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO􈟔ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO􈟔ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO􈟔ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x10FFFF;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,13): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO􏿿ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO􏿿ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO􏿿ZOO"
+      }
+    },
+    {
+      "data": "FOO&#x110000;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,13): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO�ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO�ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO�ZOO"
+      }
+    },
+    {
+      "data": "FOO&#xFFFFFF;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,13): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO�ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO�ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO�ZOO"
+      }
+    },
+    {
+      "data": "FOO&#11111111111",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,13): illegal-codepoint-for-numeric-entity",
+        "(1,13): eof-in-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO�"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO�</body></html>",
+        "noQuirksBodyHtml": "FOO�"
+      }
+    },
+    {
+      "data": "FOO&#1111111111",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,13): illegal-codepoint-for-numeric-entity",
+        "(1,13): eof-in-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO�"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO�</body></html>",
+        "noQuirksBodyHtml": "FOO�"
+      }
+    },
+    {
+      "data": "FOO&#111111111111",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,13): illegal-codepoint-for-numeric-entity",
+        "(1,13): eof-in-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO�"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO�</body></html>",
+        "noQuirksBodyHtml": "FOO�"
+      }
+    },
+    {
+      "data": "FOO&#11111111111ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,13): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO�ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO�ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO�ZOO"
+      }
+    },
+    {
+      "data": "FOO&#1111111111ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,13): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO�ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO�ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO�ZOO"
+      }
+    },
+    {
+      "data": "FOO&#111111111111ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,13): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO�ZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO�ZOO</body></html>",
+        "noQuirksBodyHtml": "FOO�ZOO"
+      }
+    }
+  ],
+  "entities02.dat": [
+    {
+      "data": "<div bar=\"ZZ&gt;YY\"></div>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ>YY"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ>YY\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ>YY\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&\"></div>",
+      "errors": [
+        "(1,15): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ&",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ&amp;\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ&amp;\"></div>"
+      }
+    },
+    {
+      "data": "<div bar='ZZ&'></div>",
+      "errors": [
+        "(1,15): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ&",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ&amp;\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ&amp;\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=ZZ&></div>",
+      "errors": [
+        "(1,13): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ&",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ&amp;\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ&amp;\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&gt=YY\"></div>",
+      "errors": [
+        "(1,15): named-entity-without-semicolon",
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ&gt=YY",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ&amp;gt=YY\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ&amp;gt=YY\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&gt0YY\"></div>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ&gt0YY",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ&amp;gt0YY\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ&amp;gt0YY\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&gt9YY\"></div>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ&gt9YY",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ&amp;gt9YY\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ&amp;gt9YY\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&gtaYY\"></div>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ&gtaYY",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ&amp;gtaYY\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ&amp;gtaYY\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&gtZYY\"></div>",
+      "errors": [
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ&gtZYY",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ&amp;gtZYY\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ&amp;gtZYY\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&gt YY\"></div>",
+      "errors": [
+        "(1,15): named-entity-without-semicolon",
+        "(1,20): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ> YY"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ> YY\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ> YY\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&gt\"></div>",
+      "errors": [
+        "(1,15): named-entity-without-semicolon",
+        "(1,17): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ>"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ>\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ>\"></div>"
+      }
+    },
+    {
+      "data": "<div bar='ZZ&gt'></div>",
+      "errors": [
+        "(1,15): named-entity-without-semicolon",
+        "(1,17): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ>"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ>\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ>\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=ZZ&gt></div>",
+      "errors": [
+        "(1,14): named-entity-without-semicolon",
+        "(1,15): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ>"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ>\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ>\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&pound_id=23\"></div>",
+      "errors": [
+        "(1,18): named-entity-without-semicolon",
+        "(1,26): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ£_id=23"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ£_id=23\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ£_id=23\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&prod_id=23\"></div>",
+      "errors": [
+        "(1,25): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ&prod_id=23",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ&amp;prod_id=23\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ&amp;prod_id=23\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&pound;_id=23\"></div>",
+      "errors": [
+        "(1,27): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ£_id=23"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ£_id=23\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ£_id=23\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&prod;_id=23\"></div>",
+      "errors": [
+        "(1,26): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ∏_id=23"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ∏_id=23\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ∏_id=23\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&pound=23\"></div>",
+      "errors": [
+        "(1,18): named-entity-without-semicolon",
+        "(1,23): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ&pound=23",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ&amp;pound=23\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ&amp;pound=23\"></div>"
+      }
+    },
+    {
+      "data": "<div bar=\"ZZ&prod=23\"></div>",
+      "errors": [
+        "(1,22): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "ZZ&prod=23",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div bar=\"ZZ&amp;prod=23\"></div></body></html>",
+        "noQuirksBodyHtml": "<div bar=\"ZZ&amp;prod=23\"></div>"
+      }
+    },
+    {
+      "data": "<div>ZZ&pound_id=23</div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,13): named-entity-without-semicolon"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "ZZ£_id=23"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>ZZ£_id=23</div></body></html>",
+        "noQuirksBodyHtml": "<div>ZZ£_id=23</div>"
+      }
+    },
+    {
+      "data": "<div>ZZ&prod_id=23</div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "ZZ&prod_id=23",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>ZZ&amp;prod_id=23</div></body></html>",
+        "noQuirksBodyHtml": "<div>ZZ&amp;prod_id=23</div>"
+      }
+    },
+    {
+      "data": "<div>ZZ&pound;_id=23</div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "ZZ£_id=23"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>ZZ£_id=23</div></body></html>",
+        "noQuirksBodyHtml": "<div>ZZ£_id=23</div>"
+      }
+    },
+    {
+      "data": "<div>ZZ&prod;_id=23</div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "ZZ∏_id=23"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>ZZ∏_id=23</div></body></html>",
+        "noQuirksBodyHtml": "<div>ZZ∏_id=23</div>"
+      }
+    },
+    {
+      "data": "<div>ZZ&pound=23</div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,13): named-entity-without-semicolon"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "ZZ£=23"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>ZZ£=23</div></body></html>",
+        "noQuirksBodyHtml": "<div>ZZ£=23</div>"
+      }
+    },
+    {
+      "data": "<div>ZZ&prod=23</div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "ZZ&prod=23",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>ZZ&amp;prod=23</div></body></html>",
+        "noQuirksBodyHtml": "<div>ZZ&amp;prod=23</div>"
+      }
+    },
+    {
+      "data": "<div>ZZ&AElig=</div>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "ZZÆ="
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>ZZÆ=</div></body></html>",
+        "noQuirksBodyHtml": "<div>ZZÆ=</div>"
+      }
+    }
+  ],
+  "foreign-fragment.dat": [
+    {
+      "data": "<nobr>X",
+      "errors": [
+        "6: HTML start tag “nobr” in a foreign namespace context.",
+        "7: End of file seen and there were open elements.",
+        "6: Unclosed element “nobr”."
+      ],
+      "fragment": {
+        "name": "path",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "svg nobr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "nobr",
+            "ns": "http://www.w3.org/2000/svg",
+            "children": [
+              {
+                "text": "X"
+              }
+            ]
+          }
+        ],
+        "html": "<nobr>X</nobr>",
+        "noQuirksBodyHtml": "<nobr>X</nobr>"
+      }
+    },
+    {
+      "data": "<font color></font>X",
+      "errors": [
+        "12: HTML start tag “font” in a foreign namespace context."
+      ],
+      "fragment": {
+        "name": "path",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "svg font": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "font",
+            "ns": "http://www.w3.org/2000/svg",
+            "attrs": [
+              {
+                "name": "color",
+                "value": ""
+              }
+            ]
+          },
+          {
+            "text": "X"
+          }
+        ],
+        "html": "<font color=\"\"></font>X",
+        "noQuirksBodyHtml": "<font color=\"\"></font>X"
+      }
+    },
+    {
+      "data": "<font></font>X",
+      "errors": [],
+      "fragment": {
+        "name": "path",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "svg font": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "font",
+            "ns": "http://www.w3.org/2000/svg"
+          },
+          {
+            "text": "X"
+          }
+        ],
+        "html": "<font></font>X",
+        "noQuirksBodyHtml": "<font></font>X"
+      }
+    },
+    {
+      "data": "<g></path>X",
+      "errors": [
+        "10: End tag “path” did not match the name of the current open element (“g”).",
+        "11: End of file seen and there were open elements.",
+        "3: Unclosed element “g”."
+      ],
+      "fragment": {
+        "name": "path",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "svg g": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "g",
+            "ns": "http://www.w3.org/2000/svg",
+            "children": [
+              {
+                "text": "X"
+              }
+            ]
+          }
+        ],
+        "html": "<g>X</g>",
+        "noQuirksBodyHtml": "<g>X</g>"
+      }
+    },
+    {
+      "data": "</path>X",
+      "errors": [
+        "5: Stray end tag “path”."
+      ],
+      "fragment": {
+        "name": "path",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "</foreignObject>X",
+      "errors": [
+        "5: Stray end tag “foreignobject”."
+      ],
+      "fragment": {
+        "name": "foreignObject",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "</desc>X",
+      "errors": [
+        "5: Stray end tag “desc”."
+      ],
+      "fragment": {
+        "name": "desc",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "</title>X",
+      "errors": [
+        "5: Stray end tag “title”."
+      ],
+      "fragment": {
+        "name": "title",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "</svg>X",
+      "errors": [
+        "5: Stray end tag “svg”."
+      ],
+      "fragment": {
+        "name": "svg",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "</mfenced>X",
+      "errors": [
+        "5: Stray end tag “mfenced”."
+      ],
+      "fragment": {
+        "name": "mfenced",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "</malignmark>X",
+      "errors": [
+        "5: Stray end tag “malignmark”."
+      ],
+      "fragment": {
+        "name": "malignmark",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "</math>X",
+      "errors": [
+        "5: Stray end tag “math”."
+      ],
+      "fragment": {
+        "name": "math",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "</annotation-xml>X",
+      "errors": [
+        "5: Stray end tag “annotation-xml”."
+      ],
+      "fragment": {
+        "name": "annotation-xml",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "</mtext>X",
+      "errors": [
+        "5: Stray end tag “mtext”."
+      ],
+      "fragment": {
+        "name": "mtext",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "</mi>X",
+      "errors": [
+        "5: Stray end tag “mi”."
+      ],
+      "fragment": {
+        "name": "mi",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "</mo>X",
+      "errors": [
+        "5: Stray end tag “mo”."
+      ],
+      "fragment": {
+        "name": "mo",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "</mn>X",
+      "errors": [
+        "5: Stray end tag “mn”."
+      ],
+      "fragment": {
+        "name": "mn",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "</ms>X",
+      "errors": [
+        "5: Stray end tag “ms”."
+      ],
+      "fragment": {
+        "name": "ms",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "<b></b><mglyph/><i></i><malignmark/><u></u><ms/>X",
+      "errors": [
+        "51: Self-closing syntax (“/>”) used on a non-void HTML element. Ignoring the slash and treating as a start tag.",
+        "52: End of file seen and there were open elements.",
+        "51: Unclosed element “ms”."
+      ],
+      "fragment": {
+        "name": "ms",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "b": true,
+            "math mglyph": true,
+            "i": true,
+            "math malignmark": true,
+            "u": true,
+            "ms": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "b"
+          },
+          {
+            "tag": "mglyph",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          },
+          {
+            "tag": "i"
+          },
+          {
+            "tag": "malignmark",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          },
+          {
+            "tag": "u"
+          },
+          {
+            "tag": "ms",
+            "children": [
+              {
+                "text": "X"
+              }
+            ]
+          }
+        ],
+        "html": "<b></b><mglyph></mglyph><i></i><malignmark></malignmark><u></u><ms>X</ms>",
+        "noQuirksBodyHtml": "<b></b><mglyph><i></i><malignmark><u></u><ms>X</ms></malignmark></mglyph>"
+      }
+    },
+    {
+      "data": "<malignmark></malignmark>",
+      "errors": [],
+      "fragment": {
+        "name": "ms",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math malignmark": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "malignmark",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          }
+        ],
+        "html": "<malignmark></malignmark>",
+        "noQuirksBodyHtml": "<malignmark></malignmark>"
+      }
+    },
+    {
+      "data": "<div></div>",
+      "errors": [],
+      "fragment": {
+        "name": "ms",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<figure></figure>",
+      "errors": [],
+      "fragment": {
+        "name": "ms",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "figure": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "figure"
+          }
+        ],
+        "html": "<figure></figure>",
+        "noQuirksBodyHtml": "<figure></figure>"
+      }
+    },
+    {
+      "data": "<b></b><mglyph/><i></i><malignmark/><u></u><mn/>X",
+      "errors": [
+        "51: Self-closing syntax (“/>”) used on a non-void HTML element. Ignoring the slash and treating as a start tag.",
+        "52: End of file seen and there were open elements.",
+        "51: Unclosed element “mn”."
+      ],
+      "fragment": {
+        "name": "mn",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "b": true,
+            "math mglyph": true,
+            "i": true,
+            "math malignmark": true,
+            "u": true,
+            "mn": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "b"
+          },
+          {
+            "tag": "mglyph",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          },
+          {
+            "tag": "i"
+          },
+          {
+            "tag": "malignmark",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          },
+          {
+            "tag": "u"
+          },
+          {
+            "tag": "mn",
+            "children": [
+              {
+                "text": "X"
+              }
+            ]
+          }
+        ],
+        "html": "<b></b><mglyph></mglyph><i></i><malignmark></malignmark><u></u><mn>X</mn>",
+        "noQuirksBodyHtml": "<b></b><mglyph><i></i><malignmark><u></u><mn>X</mn></malignmark></mglyph>"
+      }
+    },
+    {
+      "data": "<malignmark></malignmark>",
+      "errors": [],
+      "fragment": {
+        "name": "mn",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math malignmark": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "malignmark",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          }
+        ],
+        "html": "<malignmark></malignmark>",
+        "noQuirksBodyHtml": "<malignmark></malignmark>"
+      }
+    },
+    {
+      "data": "<div></div>",
+      "errors": [],
+      "fragment": {
+        "name": "mn",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<figure></figure>",
+      "errors": [],
+      "fragment": {
+        "name": "mn",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "figure": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "figure"
+          }
+        ],
+        "html": "<figure></figure>",
+        "noQuirksBodyHtml": "<figure></figure>"
+      }
+    },
+    {
+      "data": "<b></b><mglyph/><i></i><malignmark/><u></u><mo/>X",
+      "errors": [
+        "51: Self-closing syntax (“/>”) used on a non-void HTML element. Ignoring the slash and treating as a start tag.",
+        "52: End of file seen and there were open elements.",
+        "51: Unclosed element “mo”."
+      ],
+      "fragment": {
+        "name": "mo",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "b": true,
+            "math mglyph": true,
+            "i": true,
+            "math malignmark": true,
+            "u": true,
+            "mo": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "b"
+          },
+          {
+            "tag": "mglyph",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          },
+          {
+            "tag": "i"
+          },
+          {
+            "tag": "malignmark",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          },
+          {
+            "tag": "u"
+          },
+          {
+            "tag": "mo",
+            "children": [
+              {
+                "text": "X"
+              }
+            ]
+          }
+        ],
+        "html": "<b></b><mglyph></mglyph><i></i><malignmark></malignmark><u></u><mo>X</mo>",
+        "noQuirksBodyHtml": "<b></b><mglyph><i></i><malignmark><u></u><mo>X</mo></malignmark></mglyph>"
+      }
+    },
+    {
+      "data": "<malignmark></malignmark>",
+      "errors": [],
+      "fragment": {
+        "name": "mo",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math malignmark": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "malignmark",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          }
+        ],
+        "html": "<malignmark></malignmark>",
+        "noQuirksBodyHtml": "<malignmark></malignmark>"
+      }
+    },
+    {
+      "data": "<div></div>",
+      "errors": [],
+      "fragment": {
+        "name": "mo",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<figure></figure>",
+      "errors": [],
+      "fragment": {
+        "name": "mo",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "figure": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "figure"
+          }
+        ],
+        "html": "<figure></figure>",
+        "noQuirksBodyHtml": "<figure></figure>"
+      }
+    },
+    {
+      "data": "<b></b><mglyph/><i></i><malignmark/><u></u><mi/>X",
+      "errors": [
+        "51: Self-closing syntax (“/>”) used on a non-void HTML element. Ignoring the slash and treating as a start tag.",
+        "52: End of file seen and there were open elements.",
+        "51: Unclosed element “mi”."
+      ],
+      "fragment": {
+        "name": "mi",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "b": true,
+            "math mglyph": true,
+            "i": true,
+            "math malignmark": true,
+            "u": true,
+            "mi": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "b"
+          },
+          {
+            "tag": "mglyph",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          },
+          {
+            "tag": "i"
+          },
+          {
+            "tag": "malignmark",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          },
+          {
+            "tag": "u"
+          },
+          {
+            "tag": "mi",
+            "children": [
+              {
+                "text": "X"
+              }
+            ]
+          }
+        ],
+        "html": "<b></b><mglyph></mglyph><i></i><malignmark></malignmark><u></u><mi>X</mi>",
+        "noQuirksBodyHtml": "<b></b><mglyph><i></i><malignmark><u></u><mi>X</mi></malignmark></mglyph>"
+      }
+    },
+    {
+      "data": "<malignmark></malignmark>",
+      "errors": [],
+      "fragment": {
+        "name": "mi",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math malignmark": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "malignmark",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          }
+        ],
+        "html": "<malignmark></malignmark>",
+        "noQuirksBodyHtml": "<malignmark></malignmark>"
+      }
+    },
+    {
+      "data": "<div></div>",
+      "errors": [],
+      "fragment": {
+        "name": "mi",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<figure></figure>",
+      "errors": [],
+      "fragment": {
+        "name": "mi",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "figure": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "figure"
+          }
+        ],
+        "html": "<figure></figure>",
+        "noQuirksBodyHtml": "<figure></figure>"
+      }
+    },
+    {
+      "data": "<b></b><mglyph/><i></i><malignmark/><u></u><mtext/>X",
+      "errors": [
+        "51: Self-closing syntax (“/>”) used on a non-void HTML element. Ignoring the slash and treating as a start tag.",
+        "52: End of file seen and there were open elements.",
+        "51: Unclosed element “mtext”."
+      ],
+      "fragment": {
+        "name": "mtext",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "b": true,
+            "math mglyph": true,
+            "i": true,
+            "math malignmark": true,
+            "u": true,
+            "mtext": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "b"
+          },
+          {
+            "tag": "mglyph",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          },
+          {
+            "tag": "i"
+          },
+          {
+            "tag": "malignmark",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          },
+          {
+            "tag": "u"
+          },
+          {
+            "tag": "mtext",
+            "children": [
+              {
+                "text": "X"
+              }
+            ]
+          }
+        ],
+        "html": "<b></b><mglyph></mglyph><i></i><malignmark></malignmark><u></u><mtext>X</mtext>",
+        "noQuirksBodyHtml": "<b></b><mglyph><i></i><malignmark><u></u><mtext>X</mtext></malignmark></mglyph>"
+      }
+    },
+    {
+      "data": "<malignmark></malignmark>",
+      "errors": [],
+      "fragment": {
+        "name": "mtext",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math malignmark": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "malignmark",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          }
+        ],
+        "html": "<malignmark></malignmark>",
+        "noQuirksBodyHtml": "<malignmark></malignmark>"
+      }
+    },
+    {
+      "data": "<div></div>",
+      "errors": [],
+      "fragment": {
+        "name": "mtext",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<figure></figure>",
+      "errors": [],
+      "fragment": {
+        "name": "mtext",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "figure": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "figure"
+          }
+        ],
+        "html": "<figure></figure>",
+        "noQuirksBodyHtml": "<figure></figure>"
+      }
+    },
+    {
+      "data": "<div></div>",
+      "errors": [
+        "5: HTML start tag “div” in a foreign namespace context."
+      ],
+      "fragment": {
+        "name": "annotation-xml",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<figure></figure>",
+      "errors": [],
+      "fragment": {
+        "name": "annotation-xml",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math figure": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "figure",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          }
+        ],
+        "html": "<figure></figure>",
+        "noQuirksBodyHtml": "<figure></figure>"
+      }
+    },
+    {
+      "data": "<div></div>",
+      "errors": [
+        "5: HTML start tag “div” in a foreign namespace context."
+      ],
+      "fragment": {
+        "name": "math",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<figure></figure>",
+      "errors": [],
+      "fragment": {
+        "name": "math",
+        "ns": "http://www.w3.org/1998/Math/MathML"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math figure": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "figure",
+            "ns": "http://www.w3.org/1998/Math/MathML"
+          }
+        ],
+        "html": "<figure></figure>",
+        "noQuirksBodyHtml": "<figure></figure>"
+      }
+    },
+    {
+      "data": "<div></div>",
+      "errors": [],
+      "fragment": {
+        "name": "foreignObject",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<figure></figure>",
+      "errors": [],
+      "fragment": {
+        "name": "foreignObject",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "figure": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "figure"
+          }
+        ],
+        "html": "<figure></figure>",
+        "noQuirksBodyHtml": "<figure></figure>"
+      }
+    },
+    {
+      "data": "<div></div>",
+      "errors": [],
+      "fragment": {
+        "name": "title",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<figure></figure>",
+      "errors": [],
+      "fragment": {
+        "name": "title",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "figure": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "figure"
+          }
+        ],
+        "html": "<figure></figure>",
+        "noQuirksBodyHtml": "<figure></figure>"
+      }
+    },
+    {
+      "data": "<figure></figure>",
+      "errors": [],
+      "fragment": {
+        "name": "desc",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "figure": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "figure"
+          }
+        ],
+        "html": "<figure></figure>",
+        "noQuirksBodyHtml": "<figure></figure>"
+      }
+    },
+    {
+      "data": "<div><h1>X</h1></div>",
+      "errors": [
+        "5: HTML start tag “div” in a foreign namespace context.",
+        "9: HTML start tag “h1” in a foreign namespace context."
+      ],
+      "fragment": {
+        "name": "svg",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "svg div": true,
+            "svg h1": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div",
+            "ns": "http://www.w3.org/2000/svg",
+            "children": [
+              {
+                "tag": "h1",
+                "ns": "http://www.w3.org/2000/svg",
+                "children": [
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<div><h1>X</h1></div>",
+        "noQuirksBodyHtml": "<div><h1>X</h1></div>"
+      }
+    },
+    {
+      "data": "<div></div>",
+      "errors": [
+        "5: HTML start tag “div” in a foreign namespace context."
+      ],
+      "fragment": {
+        "name": "svg",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "svg div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div",
+            "ns": "http://www.w3.org/2000/svg"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<div></div>",
+      "errors": [],
+      "fragment": {
+        "name": "desc",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<figure></figure>",
+      "errors": [],
+      "fragment": {
+        "name": "desc",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "figure": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "figure"
+          }
+        ],
+        "html": "<figure></figure>",
+        "noQuirksBodyHtml": "<figure></figure>"
+      }
+    },
+    {
+      "data": "<plaintext><foo>",
+      "errors": [
+        "16: End of file seen and there were open elements.",
+        "11: Unclosed element “plaintext”."
+      ],
+      "fragment": {
+        "name": "desc",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "plaintext": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "plaintext",
+            "children": [
+              {
+                "text": "<foo>",
+                "no_escape": true
+              }
+            ]
+          }
+        ],
+        "html": "<plaintext><foo></plaintext>",
+        "noQuirksBodyHtml": "<plaintext><foo></plaintext>"
+      }
+    },
+    {
+      "data": "<frameset>X",
+      "errors": [
+        "6: Stray start tag “frameset”."
+      ],
+      "fragment": {
+        "name": "desc",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "<head>X",
+      "errors": [
+        "6: Stray start tag “head”."
+      ],
+      "fragment": {
+        "name": "desc",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "<body>X",
+      "errors": [
+        "6: Stray start tag “body”."
+      ],
+      "fragment": {
+        "name": "desc",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "<html>X",
+      "errors": [
+        "6: Stray start tag “html”."
+      ],
+      "fragment": {
+        "name": "desc",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "<html class=\"foo\">X",
+      "errors": [
+        "6: Stray start tag “html”."
+      ],
+      "fragment": {
+        "name": "desc",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "<body class=\"foo\">X",
+      "errors": [
+        "6: Stray start tag “body”."
+      ],
+      "fragment": {
+        "name": "desc",
+        "ns": "http://www.w3.org/2000/svg"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "X"
+          }
+        ],
+        "html": "X",
+        "noQuirksBodyHtml": "X"
+      }
+    }
+  ],
+  "html5test-com.dat": [
+    {
+      "data": "<div<div>",
+      "errors": [
+        "(1,9): expected-doctype-but-got-start-tag",
+        "(1,9): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div<div": true
+          },
+          "tagWithLt": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div<div"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div<div></div<div></body></html>",
+        "noQuirksBodyHtml": "<div<div></div<div>"
+      }
+    },
+    {
+      "data": "<div foo<bar=''>",
+      "errors": [
+        "(1,9): invalid-character-in-attribute-name",
+        "(1,16): expected-doctype-but-got-start-tag",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "attrWithFunnyChar": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "foo<bar",
+                        "value": ""
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div foo<bar=\"\"></div></body></html>",
+        "noQuirksBodyHtml": "<div foo<bar=\"\"></div>"
+      }
+    },
+    {
+      "data": "<div foo=`bar`>",
+      "errors": [
+        "(1,10): equals-in-unquoted-attribute-value",
+        "(1,14): unexpected-character-in-unquoted-attribute-value",
+        "(1,15): expected-doctype-but-got-start-tag",
+        "(1,15): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "foo",
+                        "value": "`bar`"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div foo=\"`bar`\"></div></body></html>",
+        "noQuirksBodyHtml": "<div foo=\"`bar`\"></div>"
+      }
+    },
+    {
+      "data": "<div \\\"foo=''>",
+      "errors": [
+        "(1,7): invalid-character-in-attribute-name",
+        "(1,14): expected-doctype-but-got-start-tag",
+        "(1,14): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "attrWithFunnyChar": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "\\\"foo",
+                        "value": ""
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div \\\"foo=\"\"></div></body></html>",
+        "noQuirksBodyHtml": "<div \\\"foo=\"\"></div>"
+      }
+    },
+    {
+      "data": "<a href='\\nbar'></a>",
+      "errors": [
+        "(1,16): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "attrs": [
+                      {
+                        "name": "href",
+                        "value": "\\nbar"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a href=\"\\nbar\"></a></body></html>",
+        "noQuirksBodyHtml": "<a href=\"\\nbar\"></a>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "&lang;&rang;",
+      "errors": [
+        "(1,6): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "⟨⟩"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>⟨⟩</body></html>",
+        "noQuirksBodyHtml": "⟨⟩"
+      }
+    },
+    {
+      "data": "&apos;",
+      "errors": [
+        "(1,6): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "'"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>'</body></html>",
+        "noQuirksBodyHtml": "'"
+      }
+    },
+    {
+      "data": "&ImaginaryI;",
+      "errors": [
+        "(1,12): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "ⅈ"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>ⅈ</body></html>",
+        "noQuirksBodyHtml": "ⅈ"
+      }
+    },
+    {
+      "data": "&Kopf;",
+      "errors": [
+        "(1,6): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "𝕂"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>𝕂</body></html>",
+        "noQuirksBodyHtml": "𝕂"
+      }
+    },
+    {
+      "data": "&notinva;",
+      "errors": [
+        "(1,9): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "∉"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>∉</body></html>",
+        "noQuirksBodyHtml": "∉"
+      }
+    },
+    {
+      "data": "<?import namespace=\"foo\" implementation=\"#bar\">",
+      "errors": [
+        "(1,1): expected-tag-name-but-got-question-mark",
+        "(1,47): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "?import namespace=\"foo\" implementation=\"#bar\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!--?import namespace=\"foo\" implementation=\"#bar\"--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--?import namespace=\"foo\" implementation=\"#bar\"-->"
+      }
+    },
+    {
+      "data": "<!--foo--bar-->",
+      "errors": [
+        "(1,10): unexpected-char-in-comment",
+        "(1,15): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "foo--bar"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!--foo--bar--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--foo--bar-->"
+      }
+    },
+    {
+      "data": "<![CDATA[x]]>",
+      "errors": [
+        "(1,2): expected-dashes-or-doctype",
+        "(1,13): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "[CDATA[x]]"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!--[CDATA[x]]--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--[CDATA[x]]-->"
+      }
+    },
+    {
+      "data": "<textarea><!--</textarea>--></textarea>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,39): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "escaped": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><textarea>&lt;!--</textarea>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<textarea>&lt;!--</textarea>--&gt;"
+      }
+    },
+    {
+      "data": "<textarea><!--</textarea>-->",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "escaped": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><textarea>&lt;!--</textarea>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<textarea>&lt;!--</textarea>--&gt;"
+      }
+    },
+    {
+      "data": "<style><!--</style>--></style>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,30): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style><!--</style></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<style><!--</style>--&gt;"
+      }
+    },
+    {
+      "data": "<style><!--</style>-->",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style><!--</style></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<style><!--</style>--&gt;"
+      }
+    },
+    {
+      "data": "<ul><li>A </li> <li>B</li></ul>",
+      "errors": [
+        "(1,4): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ul": true,
+            "li": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ul",
+                    "children": [
+                      {
+                        "tag": "li",
+                        "children": [
+                          {
+                            "text": "A "
+                          }
+                        ]
+                      },
+                      {
+                        "text": " "
+                      },
+                      {
+                        "tag": "li",
+                        "children": [
+                          {
+                            "text": "B"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ul><li>A </li> <li>B</li></ul></body></html>",
+        "noQuirksBodyHtml": "<ul><li>A </li> <li>B</li></ul>"
+      }
+    },
+    {
+      "data": "<table><form><input type=hidden><input></form><div></div></table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,13): unexpected-form-in-table",
+        "(1,32): unexpected-hidden-input-in-table",
+        "(1,39): unexpected-start-tag-implies-table-voodoo",
+        "(1,46): unexpected-end-tag-implies-table-voodoo",
+        "(1,46): unexpected-end-tag",
+        "(1,51): unexpected-start-tag-implies-table-voodoo",
+        "(1,57): unexpected-end-tag-implies-table-voodoo"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "input": true,
+            "div": true,
+            "table": true,
+            "form": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "input"
+                  },
+                  {
+                    "tag": "div"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "form"
+                      },
+                      {
+                        "tag": "input",
+                        "attrs": [
+                          {
+                            "name": "type",
+                            "value": "hidden"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><input><div></div><table><form></form><input type=\"hidden\"></table></body></html>",
+        "noQuirksBodyHtml": "<input><div></div><table><form></form><input type=\"hidden\"></table>"
+      }
+    },
+    {
+      "data": "<i>A<b>B<p></i>C</b>D",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,15): adoption-agency-1.3",
+        "(1,20): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "i": true,
+            "b": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "text": "A"
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "B"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "i"
+                          },
+                          {
+                            "text": "C"
+                          }
+                        ]
+                      },
+                      {
+                        "text": "D"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><i>A<b>B</b></i><b></b><p><b><i></i>C</b>D</p></body></html>",
+        "noQuirksBodyHtml": "<i>A<b>B</b></i><b></b><p><b><i></i>C</b>D</p>"
+      }
+    },
+    {
+      "data": "<div></div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div></div></body></html>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<svg></svg>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg></svg></body></html>",
+        "noQuirksBodyHtml": "<svg></svg>"
+      }
+    },
+    {
+      "data": "<math></math>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math></math></body></html>",
+        "noQuirksBodyHtml": "<math></math>"
+      }
+    }
+  ],
+  "inbody01.dat": [
+    {
+      "data": "<button>1</foo>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,15): unexpected-end-tag",
+        "(1,15): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "button": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "button",
+                    "children": [
+                      {
+                        "text": "1"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><button>1</button></body></html>",
+        "noQuirksBodyHtml": "<button>1</button>"
+      }
+    },
+    {
+      "data": "<foo>1<p>2</foo>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,16): unexpected-end-tag",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "foo": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "foo",
+                    "children": [
+                      {
+                        "text": "1"
+                      },
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "text": "2"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><foo>1<p>2</p></foo></body></html>",
+        "noQuirksBodyHtml": "<foo>1<p>2</p></foo>"
+      }
+    },
+    {
+      "data": "<dd>1</foo>",
+      "errors": [
+        "(1,4): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "dd": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "dd",
+                    "children": [
+                      {
+                        "text": "1"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><dd>1</dd></body></html>",
+        "noQuirksBodyHtml": "<dd>1</dd>"
+      }
+    },
+    {
+      "data": "<foo>1<dd>2</foo>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,17): unexpected-end-tag",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "foo": true,
+            "dd": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "foo",
+                    "children": [
+                      {
+                        "text": "1"
+                      },
+                      {
+                        "tag": "dd",
+                        "children": [
+                          {
+                            "text": "2"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><foo>1<dd>2</dd></foo></body></html>",
+        "noQuirksBodyHtml": "<foo>1<dd>2</dd></foo>"
+      }
+    }
+  ],
+  "isindex.dat": [
+    {
+      "data": "<isindex>",
+      "errors": [
+        "(1,9): expected-doctype-but-got-start-tag",
+        "(1,9): deprecated-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "form": true,
+            "hr": true,
+            "label": true,
+            "input": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "form",
+                    "children": [
+                      {
+                        "tag": "hr"
+                      },
+                      {
+                        "tag": "label",
+                        "children": [
+                          {
+                            "text": "This is a searchable index. Enter search keywords: "
+                          },
+                          {
+                            "tag": "input",
+                            "attrs": [
+                              {
+                                "name": "name",
+                                "value": "isindex"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "hr"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><form><hr><label>This is a searchable index. Enter search keywords: <input name=\"isindex\"></label><hr></form></body></html>",
+        "noQuirksBodyHtml": "<form><hr><label>This is a searchable index. Enter search keywords: <input name=\"isindex\"></label><hr></form>"
+      }
+    },
+    {
+      "data": "<isindex name=\"A\" action=\"B\" prompt=\"C\" foo=\"D\">",
+      "errors": [
+        "(1,48): expected-doctype-but-got-start-tag",
+        "(1,48): deprecated-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "form": true,
+            "hr": true,
+            "label": true,
+            "input": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "form",
+                    "attrs": [
+                      {
+                        "name": "action",
+                        "value": "B"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "hr"
+                      },
+                      {
+                        "tag": "label",
+                        "children": [
+                          {
+                            "text": "C"
+                          },
+                          {
+                            "tag": "input",
+                            "attrs": [
+                              {
+                                "name": "foo",
+                                "value": "D"
+                              },
+                              {
+                                "name": "name",
+                                "value": "isindex"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "hr"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><form action=\"B\"><hr><label>C<input name=\"isindex\" foo=\"D\"></label><hr></form></body></html>",
+        "noQuirksBodyHtml": "<form action=\"B\"><hr><label>C<input name=\"isindex\" foo=\"D\"></label><hr></form>"
+      }
+    },
+    {
+      "data": "<form><isindex>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,15): deprecated-tag",
+        "(1,15): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "form": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "form"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><form></form></body></html>",
+        "noQuirksBodyHtml": "<form></form>"
+      }
+    }
+  ],
+  "main-element.dat": [
+    {
+      "data": "<!doctype html><p>foo<main>bar<p>baz",
+      "errors": [
+        "(1,36): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "main": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "foo"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "main",
+                    "children": [
+                      {
+                        "text": "bar"
+                      },
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "text": "baz"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p>foo</p><main>bar<p>baz</p></main></body></html>",
+        "noQuirksBodyHtml": "<p>foo</p><main>bar<p>baz</p></main>"
+      }
+    },
+    {
+      "data": "<!doctype html><main><p>foo</main>bar",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "main": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "main",
+                    "children": [
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "text": "bar"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><main><p>foo</p></main>bar</body></html>",
+        "noQuirksBodyHtml": "<main><p>foo</p></main>bar"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>xxx<svg><x><g><a><main><b>",
+      "errors": [
+        " * (1,42) unexpected HTML-like start tag token in foreign content",
+        " * (1,42) unexpected end of file"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg x": true,
+            "svg g": true,
+            "svg a": true,
+            "svg main": true,
+            "b": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "xxx"
+                  },
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "x",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "g",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "a",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "tag": "main",
+                                    "ns": "http://www.w3.org/2000/svg"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>xxx<svg><x><g><a><main></main></a></g></x></svg><b></b></body></html>",
+        "noQuirksBodyHtml": "xxx<svg><x><g><a><main><b></b></main></a></g></x></svg>"
+      }
+    }
+  ],
+  "math.dat": [
+    {
+      "data": "<math><tr><td><mo><tr>",
+      "errors": [],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math math": true,
+            "math tr": true,
+            "math td": true,
+            "math mo": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "math",
+            "ns": "http://www.w3.org/1998/Math/MathML",
+            "children": [
+              {
+                "tag": "tr",
+                "ns": "http://www.w3.org/1998/Math/MathML",
+                "children": [
+                  {
+                    "tag": "td",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mo",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<math><tr><td><mo></mo></td></tr></math>",
+        "noQuirksBodyHtml": "<math><tr><td><mo></mo></td></tr></math>"
+      }
+    },
+    {
+      "data": "<math><tr><td><mo><tr>",
+      "errors": [],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math math": true,
+            "math tr": true,
+            "math td": true,
+            "math mo": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "math",
+            "ns": "http://www.w3.org/1998/Math/MathML",
+            "children": [
+              {
+                "tag": "tr",
+                "ns": "http://www.w3.org/1998/Math/MathML",
+                "children": [
+                  {
+                    "tag": "td",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mo",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<math><tr><td><mo></mo></td></tr></math>",
+        "noQuirksBodyHtml": "<math><tr><td><mo></mo></td></tr></math>"
+      }
+    },
+    {
+      "data": "<math><thead><mo><tbody>",
+      "errors": [],
+      "fragment": {
+        "name": "thead"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math math": true,
+            "math thead": true,
+            "math mo": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "math",
+            "ns": "http://www.w3.org/1998/Math/MathML",
+            "children": [
+              {
+                "tag": "thead",
+                "ns": "http://www.w3.org/1998/Math/MathML",
+                "children": [
+                  {
+                    "tag": "mo",
+                    "ns": "http://www.w3.org/1998/Math/MathML"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<math><thead><mo></mo></thead></math>",
+        "noQuirksBodyHtml": "<math><thead><mo></mo></thead></math>"
+      }
+    },
+    {
+      "data": "<math><tfoot><mo><tbody>",
+      "errors": [],
+      "fragment": {
+        "name": "tfoot"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math math": true,
+            "math tfoot": true,
+            "math mo": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "math",
+            "ns": "http://www.w3.org/1998/Math/MathML",
+            "children": [
+              {
+                "tag": "tfoot",
+                "ns": "http://www.w3.org/1998/Math/MathML",
+                "children": [
+                  {
+                    "tag": "mo",
+                    "ns": "http://www.w3.org/1998/Math/MathML"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<math><tfoot><mo></mo></tfoot></math>",
+        "noQuirksBodyHtml": "<math><tfoot><mo></mo></tfoot></math>"
+      }
+    },
+    {
+      "data": "<math><tbody><mo><tfoot>",
+      "errors": [],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math math": true,
+            "math tbody": true,
+            "math mo": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "math",
+            "ns": "http://www.w3.org/1998/Math/MathML",
+            "children": [
+              {
+                "tag": "tbody",
+                "ns": "http://www.w3.org/1998/Math/MathML",
+                "children": [
+                  {
+                    "tag": "mo",
+                    "ns": "http://www.w3.org/1998/Math/MathML"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<math><tbody><mo></mo></tbody></math>",
+        "noQuirksBodyHtml": "<math><tbody><mo></mo></tbody></math>"
+      }
+    },
+    {
+      "data": "<math><tbody><mo></table>",
+      "errors": [],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math math": true,
+            "math tbody": true,
+            "math mo": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "math",
+            "ns": "http://www.w3.org/1998/Math/MathML",
+            "children": [
+              {
+                "tag": "tbody",
+                "ns": "http://www.w3.org/1998/Math/MathML",
+                "children": [
+                  {
+                    "tag": "mo",
+                    "ns": "http://www.w3.org/1998/Math/MathML"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<math><tbody><mo></mo></tbody></math>",
+        "noQuirksBodyHtml": "<math><tbody><mo></mo></tbody></math>"
+      }
+    },
+    {
+      "data": "<math><thead><mo></table>",
+      "errors": [],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math math": true,
+            "math thead": true,
+            "math mo": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "math",
+            "ns": "http://www.w3.org/1998/Math/MathML",
+            "children": [
+              {
+                "tag": "thead",
+                "ns": "http://www.w3.org/1998/Math/MathML",
+                "children": [
+                  {
+                    "tag": "mo",
+                    "ns": "http://www.w3.org/1998/Math/MathML"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<math><thead><mo></mo></thead></math>",
+        "noQuirksBodyHtml": "<math><thead><mo></mo></thead></math>"
+      }
+    },
+    {
+      "data": "<math><tfoot><mo></table>",
+      "errors": [],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "math math": true,
+            "math tfoot": true,
+            "math mo": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "math",
+            "ns": "http://www.w3.org/1998/Math/MathML",
+            "children": [
+              {
+                "tag": "tfoot",
+                "ns": "http://www.w3.org/1998/Math/MathML",
+                "children": [
+                  {
+                    "tag": "mo",
+                    "ns": "http://www.w3.org/1998/Math/MathML"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<math><tfoot><mo></mo></tfoot></math>",
+        "noQuirksBodyHtml": "<math><tfoot><mo></mo></tfoot></math>"
+      }
+    }
+  ],
+  "namespace-sensitivity.dat": [
+    {
+      "data": "<body><table><tr><td><svg><td><foreignObject><span></td>Foo",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "svg svg": true,
+            "svg td": true,
+            "svg foreignObject": true,
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Foo"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "svg",
+                                    "ns": "http://www.w3.org/2000/svg",
+                                    "children": [
+                                      {
+                                        "tag": "td",
+                                        "ns": "http://www.w3.org/2000/svg",
+                                        "children": [
+                                          {
+                                            "tag": "foreignObject",
+                                            "ns": "http://www.w3.org/2000/svg",
+                                            "children": [
+                                              {
+                                                "tag": "span"
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>Foo<table><tbody><tr><td><svg><td><foreignObject><span></span></foreignObject></td></svg></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "Foo<table><tbody><tr><td><svg><td><foreignObject><span></span></foreignObject></td></svg></td></tr></tbody></table>"
+      }
+    }
+  ],
+  "pending-spec-changes-plain-text-unsafe.dat": [
+    {
+      "data": "<body><table>\u0000filler\u0000text\u0000",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,14): invalid-codepoint",
+        "(1,14): invalid-codepoint-in-table-text",
+        "(1,21): invalid-codepoint",
+        "(1,21): invalid-codepoint-in-table-text",
+        "(1,26): invalid-codepoint",
+        "(1,26): invalid-codepoint-in-table-text",
+        "(1,26): foster-parenting-character-in-table",
+        "(1,26): foster-parenting-character-in-table",
+        "(1,26): foster-parenting-character-in-table",
+        "(1,26): foster-parenting-character-in-table",
+        "(1,26): foster-parenting-character-in-table",
+        "(1,26): foster-parenting-character-in-table",
+        "(1,26): foster-parenting-character-in-table",
+        "(1,26): foster-parenting-character-in-table",
+        "(1,26): foster-parenting-character-in-table",
+        "(1,26): foster-parenting-character-in-table",
+        "(1,26): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "fillertext"
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>fillertext<table></table></body></html>",
+        "noQuirksBodyHtml": "fillertext<table></table>"
+      }
+    }
+  ],
+  "pending-spec-changes.dat": [
+    {
+      "data": "<input type=\"hidden\"><frameset>",
+      "errors": [
+        "(1,21): expected-doctype-but-got-start-tag",
+        "(1,31): unexpected-start-tag",
+        "(1,31): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<input type=\"hidden\">"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><table><caption><svg>foo</table>bar",
+      "errors": [
+        "(1,47): unexpected-end-tag",
+        "(1,47): end-table-tag-in-caption"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "svg svg": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "text": "foo"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "text": "bar"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><caption><svg>foo</svg></caption></table>bar</body></html>",
+        "noQuirksBodyHtml": "<table><caption><svg>foo</svg></caption></table>bar"
+      }
+    },
+    {
+      "data": "<table><tr><td><svg><desc><td></desc><circle>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,30): unexpected-cell-end-tag",
+        "(1,37): unexpected-end-tag",
+        "(1,45): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "svg svg": true,
+            "svg desc": true,
+            "circle": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "svg",
+                                    "ns": "http://www.w3.org/2000/svg",
+                                    "children": [
+                                      {
+                                        "tag": "desc",
+                                        "ns": "http://www.w3.org/2000/svg"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "circle"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td><svg><desc></desc></svg></td><td><circle></circle></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><svg><desc></desc></svg></td><td><circle></circle></td></tr></tbody></table>"
+      }
+    }
+  ],
+  "plain-text-unsafe.dat": [
+    {
+      "data": "FOO&#x000D;ZOO",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,11): illegal-codepoint-for-numeric-entity"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO\rZOO"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO\rZOO</body></html>",
+        "noQuirksBodyHtml": "FOO\rZOO"
+      }
+    },
+    {
+      "data": "<html>\u0000<frameset></frameset>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,7): invalid-codepoint",
+        "(1,7): invalid-codepoint-in-body",
+        "(1,17): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html> \u0000 <frameset></frameset>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,8): invalid-codepoint",
+        "(1,8): invalid-codepoint-in-body",
+        "(1,19): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "  "
+      }
+    },
+    {
+      "data": "<html>a\u0000a<frameset></frameset>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,8): invalid-codepoint",
+        "(1,8): invalid-codepoint-in-body",
+        "(1,19): unexpected-start-tag",
+        "(1,30): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "aa"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>aa</body></html>",
+        "noQuirksBodyHtml": "aa"
+      }
+    },
+    {
+      "data": "<html>\u0000\u0000<frameset></frameset>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,7): invalid-codepoint",
+        "(1,7): invalid-codepoint-in-body",
+        "(1,8): invalid-codepoint",
+        "(1,8): invalid-codepoint-in-body",
+        "(1,18): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html>\u0000\n <frameset></frameset>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,7): invalid-codepoint",
+        "(1,7): invalid-codepoint-in-body",
+        "(2,11): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "\n "
+      }
+    },
+    {
+      "data": "<html><select>\u0000",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,15): invalid-codepoint",
+        "(1,15): invalid-codepoint-in-select",
+        "(1,15): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select></select></body></html>",
+        "noQuirksBodyHtml": "<select></select>"
+      }
+    },
+    {
+      "data": "\u0000",
+      "errors": [
+        "(1,1): invalid-codepoint",
+        "(1,1): expected-doctype-but-got-chars",
+        "(1,1): invalid-codepoint-in-body"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<body>\u0000",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,7): invalid-codepoint",
+        "(1,7): invalid-codepoint-in-body"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<plaintext>\u0000filler\u0000text\u0000",
+      "errors": [
+        "(1,11): expected-doctype-but-got-start-tag",
+        "(1,12): invalid-codepoint",
+        "(1,19): invalid-codepoint",
+        "(1,24): invalid-codepoint",
+        "(1,24): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "plaintext": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "plaintext",
+                    "children": [
+                      {
+                        "text": "�filler�text�",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><plaintext>�filler�text�</plaintext></body></html>",
+        "noQuirksBodyHtml": "<plaintext>�filler�text�</plaintext>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[\u0000filler\u0000text\u0000]]>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,30): invalid-codepoint",
+        "(1,30): invalid-codepoint",
+        "(1,30): invalid-codepoint",
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "�filler�text�"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>�filler�text�</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>�filler�text�</svg>"
+      }
+    },
+    {
+      "data": "<body><!\u0000>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,8): expected-dashes-or-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "comment": "�"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><!--�--></body></html>",
+        "noQuirksBodyHtml": "<!--�-->"
+      }
+    },
+    {
+      "data": "<body><!\u0000filler\u0000text>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,8): expected-dashes-or-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "comment": "�filler�text"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><!--�filler�text--></body></html>",
+        "noQuirksBodyHtml": "<!--�filler�text-->"
+      }
+    },
+    {
+      "data": "<body><svg><foreignObject>\u0000filler\u0000text",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,27): invalid-codepoint",
+        "(1,27): invalid-codepoint-in-body",
+        "(1,34): invalid-codepoint",
+        "(1,34): invalid-codepoint-in-body",
+        "(1,38): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg foreignObject": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "foreignObject",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "fillertext"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg><foreignObject>fillertext</foreignObject></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><foreignObject>fillertext</foreignObject></svg>"
+      }
+    },
+    {
+      "data": "<svg>\u0000filler\u0000text",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,6): invalid-codepoint",
+        "(1,6): invalid-codepoint-in-foreign-content",
+        "(1,13): invalid-codepoint",
+        "(1,13): invalid-codepoint-in-foreign-content",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "�filler�text"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>�filler�text</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>�filler�text</svg>"
+      }
+    },
+    {
+      "data": "<svg>\u0000<frameset>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,6): invalid-codepoint",
+        "(1,6): invalid-codepoint-in-foreign-content",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "�"
+                      },
+                      {
+                        "tag": "frameset",
+                        "ns": "http://www.w3.org/2000/svg"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>�<frameset></frameset></svg></body></html>",
+        "noQuirksBodyHtml": "<svg>�<frameset></frameset></svg>"
+      }
+    },
+    {
+      "data": "<svg>\u0000 <frameset>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,6): invalid-codepoint",
+        "(1,6): invalid-codepoint-in-foreign-content",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "� "
+                      },
+                      {
+                        "tag": "frameset",
+                        "ns": "http://www.w3.org/2000/svg"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>� <frameset></frameset></svg></body></html>",
+        "noQuirksBodyHtml": "<svg>� <frameset></frameset></svg>"
+      }
+    },
+    {
+      "data": "<svg>\u0000a<frameset>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,6): invalid-codepoint",
+        "(1,6): invalid-codepoint-in-foreign-content",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "�a"
+                      },
+                      {
+                        "tag": "frameset",
+                        "ns": "http://www.w3.org/2000/svg"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>�a<frameset></frameset></svg></body></html>",
+        "noQuirksBodyHtml": "<svg>�a<frameset></frameset></svg>"
+      }
+    },
+    {
+      "data": "<svg>\u0000</svg><frameset>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,6): invalid-codepoint",
+        "(1,6): invalid-codepoint-in-foreign-content",
+        "(1,22): unexpected-start-tag",
+        "(1,22): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<svg>�</svg>"
+      }
+    },
+    {
+      "data": "<svg>\u0000 </svg><frameset>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,6): invalid-codepoint",
+        "(1,6): invalid-codepoint-in-foreign-content",
+        "(1,23): unexpected-start-tag",
+        "(1,23): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<svg>� </svg>"
+      }
+    },
+    {
+      "data": "<svg>\u0000a</svg><frameset>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,6): invalid-codepoint",
+        "(1,6): invalid-codepoint-in-foreign-content",
+        "(1,23): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "�a"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>�a</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>�a</svg>"
+      }
+    },
+    {
+      "data": "<svg><path></path></svg><frameset>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,34): unexpected-start-tag",
+        "(1,34): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<svg><path></path></svg>"
+      }
+    },
+    {
+      "data": "<svg><p><frameset>",
+      "errors": [
+        "(1, 5) expected-doctype-but-got-start-tag",
+        "(1, 8) unexpected-html-element-in-foreign-content",
+        "(1, 18) unexpected-start-tag",
+        "(1, 18) eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<svg><p><frameset></frameset></p></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><pre>\r\n\r\nA</pre>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "pre": true
+          },
+          "doctype": true,
+          "extraNL": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "pre",
+                    "children": [
+                      {
+                        "text": "\nA",
+                        "extraNL": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><pre>\n\nA</pre></body></html>",
+        "noQuirksBodyHtml": "<pre>\n\nA</pre>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><pre>\r\rA</pre>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "pre": true
+          },
+          "doctype": true,
+          "extraNL": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "pre",
+                    "children": [
+                      {
+                        "text": "\nA",
+                        "extraNL": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><pre>\n\nA</pre></body></html>",
+        "noQuirksBodyHtml": "<pre>\n\nA</pre>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><pre>\rA</pre>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "pre": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "pre",
+                    "children": [
+                      {
+                        "text": "A"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><pre>A</pre></body></html>",
+        "noQuirksBodyHtml": "<pre>A</pre>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><table><tr><td><math><mtext>\u0000a",
+      "errors": [
+        "(1,44): invalid-codepoint",
+        "(1,44): invalid-codepoint-in-body",
+        "(1,45): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "math math": true,
+            "math mtext": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "math",
+                                    "ns": "http://www.w3.org/1998/Math/MathML",
+                                    "children": [
+                                      {
+                                        "tag": "mtext",
+                                        "ns": "http://www.w3.org/1998/Math/MathML",
+                                        "children": [
+                                          {
+                                            "text": "a"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><math><mtext>a</mtext></math></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><math><mtext>a</mtext></math></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><table><tr><td><svg><foreignObject>\u0000a",
+      "errors": [
+        "(1,51): invalid-codepoint",
+        "(1,51): invalid-codepoint-in-body",
+        "(1,52): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "svg svg": true,
+            "svg foreignObject": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "svg",
+                                    "ns": "http://www.w3.org/2000/svg",
+                                    "children": [
+                                      {
+                                        "tag": "foreignObject",
+                                        "ns": "http://www.w3.org/2000/svg",
+                                        "children": [
+                                          {
+                                            "text": "a"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><svg><foreignObject>a</foreignObject></svg></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><svg><foreignObject>a</foreignObject></svg></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><math><mi>a\u0000b",
+      "errors": [
+        "(1,27): invalid-codepoint",
+        "(1,27): invalid-codepoint-in-body",
+        "(1,28): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "ab"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mi>ab</mi></math></body></html>",
+        "noQuirksBodyHtml": "<math><mi>ab</mi></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><math><mo>a\u0000b",
+      "errors": [
+        "(1,27): invalid-codepoint",
+        "(1,27): invalid-codepoint-in-body",
+        "(1,28): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mo": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mo",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "ab"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mo>ab</mo></math></body></html>",
+        "noQuirksBodyHtml": "<math><mo>ab</mo></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><math><mn>a\u0000b",
+      "errors": [
+        "(1,27): invalid-codepoint",
+        "(1,27): invalid-codepoint-in-body",
+        "(1,28): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mn": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mn",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "ab"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mn>ab</mn></math></body></html>",
+        "noQuirksBodyHtml": "<math><mn>ab</mn></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><math><ms>a\u0000b",
+      "errors": [
+        "(1,27): invalid-codepoint",
+        "(1,27): invalid-codepoint-in-body",
+        "(1,28): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math ms": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "ms",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "ab"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><ms>ab</ms></math></body></html>",
+        "noQuirksBodyHtml": "<math><ms>ab</ms></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><math><mtext>a\u0000b",
+      "errors": [
+        "(1,30): invalid-codepoint",
+        "(1,30): invalid-codepoint-in-body",
+        "(1,31): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mtext": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mtext",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "ab"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mtext>ab</mtext></math></body></html>",
+        "noQuirksBodyHtml": "<math><mtext>ab</mtext></math>"
+      }
+    }
+  ],
+  "ruby.dat": [
+    {
+      "data": "<html><ruby>a<rb>b<rb></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rb": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rb",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rb"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rb>b</rb><rb></rb></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rb>b</rb><rb></rb></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rb>b<rt></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rb": true,
+            "rt": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rb",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rt"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rb>b</rb><rt></rt></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rb>b</rb><rt></rt></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rb>b<rtc></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rb": true,
+            "rtc": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rb",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rtc"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rb>b</rb><rtc></rtc></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rb>b</rb><rtc></rtc></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rb>b<rp></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rb": true,
+            "rp": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rb",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rp"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rb>b</rb><rp></rp></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rb>b</rb><rp></rp></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rb>b<span></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rb": true,
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rb",
+                        "children": [
+                          {
+                            "text": "b"
+                          },
+                          {
+                            "tag": "span"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rb>b<span></span></rb></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rb>b<span></span></rb></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rt>b<rb></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rt": true,
+            "rb": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rt",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rb"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rt>b</rt><rb></rb></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rt>b</rt><rb></rb></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rt>b<rt></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rt": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rt",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rt"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rt>b</rt><rt></rt></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rt>b</rt><rt></rt></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rt>b<rtc></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rt": true,
+            "rtc": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rt",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rtc"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rt>b</rt><rtc></rtc></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rt>b</rt><rtc></rtc></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rt>b<rp></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rt": true,
+            "rp": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rt",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rp"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rt>b</rt><rp></rp></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rt>b</rt><rp></rp></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rt>b<span></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rt": true,
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rt",
+                        "children": [
+                          {
+                            "text": "b"
+                          },
+                          {
+                            "tag": "span"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rt>b<span></span></rt></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rt>b<span></span></rt></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rtc>b<rb></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rtc": true,
+            "rb": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rtc",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rb"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rtc>b</rtc><rb></rb></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rtc>b</rtc><rb></rb></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rtc>b<rt>c<rt>d</ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rtc": true,
+            "rt": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rtc",
+                        "children": [
+                          {
+                            "text": "b"
+                          },
+                          {
+                            "tag": "rt",
+                            "children": [
+                              {
+                                "text": "c"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "rt",
+                            "children": [
+                              {
+                                "text": "d"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rtc>b<rt>c</rt><rt>d</rt></rtc></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rtc>b<rt>c</rt><rt>d</rt></rtc></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rtc>b<rtc></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rtc": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rtc",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rtc"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rtc>b</rtc><rtc></rtc></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rtc>b</rtc><rtc></rtc></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rtc>b<rp></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rtc": true,
+            "rp": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rtc",
+                        "children": [
+                          {
+                            "text": "b"
+                          },
+                          {
+                            "tag": "rp"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rtc>b<rp></rp></rtc></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rtc>b<rp></rp></rtc></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rtc>b<span></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rtc": true,
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rtc",
+                        "children": [
+                          {
+                            "text": "b"
+                          },
+                          {
+                            "tag": "span"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rtc>b<span></span></rtc></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rtc>b<span></span></rtc></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rp>b<rb></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rp": true,
+            "rb": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rp",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rb"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rp>b</rp><rb></rb></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rp>b</rp><rb></rb></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rp>b<rt></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rp": true,
+            "rt": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rp",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rt"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rp>b</rp><rt></rt></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rp>b</rp><rt></rt></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rp>b<rtc></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rp": true,
+            "rtc": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rp",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rtc"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rp>b</rp><rtc></rtc></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rp>b</rp><rtc></rtc></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rp>b<rp></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rp": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rp",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rp"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rp>b</rp><rp></rp></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rp>b</rp><rp></rp></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rp>b<span></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rp": true,
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rp",
+                        "children": [
+                          {
+                            "text": "b"
+                          },
+                          {
+                            "tag": "span"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rp>b<span></span></rp></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rp>b<span></span></rp></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby><rtc><ruby>a<rb>b<rt></ruby></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rtc": true,
+            "rb": true,
+            "rt": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "tag": "rtc",
+                        "children": [
+                          {
+                            "tag": "ruby",
+                            "children": [
+                              {
+                                "text": "a"
+                              },
+                              {
+                                "tag": "rb",
+                                "children": [
+                                  {
+                                    "text": "b"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "rt"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby><rtc><ruby>a<rb>b</rb><rt></rt></ruby></rtc></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby><rtc><ruby>a<rb>b</rb><rt></rt></ruby></rtc></ruby>"
+      }
+    }
+  ],
+  "scriptdata01.dat": [
+    {
+      "data": "FOO<script>'Hello'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "'Hello'",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script>'Hello'</script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script>'Hello'</script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script></script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script"
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script></script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script></script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script></script >BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script"
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script></script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script></script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script></script/>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,21): self-closing-flag-on-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script"
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script></script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script></script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script></script/ >BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,20): unexpected-character-after-solidus-in-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script"
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script></script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script></script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script type=\"text/plain\"></scriptx>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,42): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "text/plain"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "</scriptx>BAR",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script type=\"text/plain\"></scriptx>BAR</script></body></html>",
+        "noQuirksBodyHtml": "FOO<script type=\"text/plain\"></scriptx>BAR</script>"
+      }
+    },
+    {
+      "data": "FOO<script></script foo=\">\" dd>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,31): attributes-in-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script"
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script></script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script></script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script>'<'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "'<'",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script>'<'</script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script>'<'</script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script>'<!'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "'<!'",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script>'<!'</script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script>'<!'</script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script>'<!-'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "'<!-'",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script>'<!-'</script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script>'<!-'</script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script>'<!--'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "'<!--'",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script>'<!--'</script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script>'<!--'</script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script>'<!---'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "'<!---'",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script>'<!---'</script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script>'<!---'</script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script>'<!-->'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "'<!-->'",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script>'<!-->'</script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script>'<!-->'</script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script>'<!-->'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "'<!-->'",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script>'<!-->'</script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script>'<!-->'</script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script>'<!-- potato'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "'<!-- potato'",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script>'<!-- potato'</script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script>'<!-- potato'</script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script>'<!-- <sCrIpt'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "'<!-- <sCrIpt'",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script>'<!-- <sCrIpt'</script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script>'<!-- <sCrIpt'</script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script type=\"text/plain\">'<!-- <sCrIpt>'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,56): expected-script-data-but-got-eof",
+        "(1,56): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "text/plain"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "'<!-- <sCrIpt>'</script>BAR",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script type=\"text/plain\">'<!-- <sCrIpt>'</script>BAR</script></body></html>",
+        "noQuirksBodyHtml": "FOO<script type=\"text/plain\">'<!-- <sCrIpt>'</script>BAR</script>"
+      }
+    },
+    {
+      "data": "FOO<script type=\"text/plain\">'<!-- <sCrIpt> -'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,58): expected-script-data-but-got-eof",
+        "(1,58): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "text/plain"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "'<!-- <sCrIpt> -'</script>BAR",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script type=\"text/plain\">'<!-- <sCrIpt> -'</script>BAR</script></body></html>",
+        "noQuirksBodyHtml": "FOO<script type=\"text/plain\">'<!-- <sCrIpt> -'</script>BAR</script>"
+      }
+    },
+    {
+      "data": "FOO<script type=\"text/plain\">'<!-- <sCrIpt> --'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,59): expected-script-data-but-got-eof",
+        "(1,59): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "text/plain"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "'<!-- <sCrIpt> --'</script>BAR",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script type=\"text/plain\">'<!-- <sCrIpt> --'</script>BAR</script></body></html>",
+        "noQuirksBodyHtml": "FOO<script type=\"text/plain\">'<!-- <sCrIpt> --'</script>BAR</script>"
+      }
+    },
+    {
+      "data": "FOO<script>'<!-- <sCrIpt> -->'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "'<!-- <sCrIpt> -->'",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script>'<!-- <sCrIpt> -->'</script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script>'<!-- <sCrIpt> -->'</script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script type=\"text/plain\">'<!-- <sCrIpt> --!>'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,61): expected-script-data-but-got-eof",
+        "(1,61): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "text/plain"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "'<!-- <sCrIpt> --!>'</script>BAR",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script type=\"text/plain\">'<!-- <sCrIpt> --!>'</script>BAR</script></body></html>",
+        "noQuirksBodyHtml": "FOO<script type=\"text/plain\">'<!-- <sCrIpt> --!>'</script>BAR</script>"
+      }
+    },
+    {
+      "data": "FOO<script type=\"text/plain\">'<!-- <sCrIpt> -- >'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,61): expected-script-data-but-got-eof",
+        "(1,61): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "text/plain"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "'<!-- <sCrIpt> -- >'</script>BAR",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script type=\"text/plain\">'<!-- <sCrIpt> -- >'</script>BAR</script></body></html>",
+        "noQuirksBodyHtml": "FOO<script type=\"text/plain\">'<!-- <sCrIpt> -- >'</script>BAR</script>"
+      }
+    },
+    {
+      "data": "FOO<script type=\"text/plain\">'<!-- <sCrIpt '</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,56): expected-script-data-but-got-eof",
+        "(1,56): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "text/plain"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "'<!-- <sCrIpt '</script>BAR",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script type=\"text/plain\">'<!-- <sCrIpt '</script>BAR</script></body></html>",
+        "noQuirksBodyHtml": "FOO<script type=\"text/plain\">'<!-- <sCrIpt '</script>BAR</script>"
+      }
+    },
+    {
+      "data": "FOO<script type=\"text/plain\">'<!-- <sCrIpt/'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars",
+        "(1,56): expected-script-data-but-got-eof",
+        "(1,56): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "text/plain"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "'<!-- <sCrIpt/'</script>BAR",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script type=\"text/plain\">'<!-- <sCrIpt/'</script>BAR</script></body></html>",
+        "noQuirksBodyHtml": "FOO<script type=\"text/plain\">'<!-- <sCrIpt/'</script>BAR</script>"
+      }
+    },
+    {
+      "data": "FOO<script type=\"text/plain\">'<!-- <sCrIpt\\'</script>BAR",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "text/plain"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "'<!-- <sCrIpt\\'",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "BAR"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script type=\"text/plain\">'<!-- <sCrIpt\\'</script>BAR</body></html>",
+        "noQuirksBodyHtml": "FOO<script type=\"text/plain\">'<!-- <sCrIpt\\'</script>BAR"
+      }
+    },
+    {
+      "data": "FOO<script type=\"text/plain\">'<!-- <sCrIpt/'</script>BAR</script>QUX",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "text/plain"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "'<!-- <sCrIpt/'</script>BAR",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "QUX"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script type=\"text/plain\">'<!-- <sCrIpt/'</script>BAR</script>QUX</body></html>",
+        "noQuirksBodyHtml": "FOO<script type=\"text/plain\">'<!-- <sCrIpt/'</script>BAR</script>QUX"
+      }
+    },
+    {
+      "data": "FOO<script><!--<script>-></script>--></script>QUX",
+      "errors": [
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "FOO"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script>-></script>-->",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "QUX"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>FOO<script><!--<script>-></script>--></script>QUX</body></html>",
+        "noQuirksBodyHtml": "FOO<script><!--<script>-></script>--></script>QUX"
+      }
+    }
+  ],
+  "tables01.dat": [
+    {
+      "data": "<table><th>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-cell-in-table-body",
+        "(1,11): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "th": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "th"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><th></th></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><th></th></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><td>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-cell-in-table-body",
+        "(1,11): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><col foo='bar'>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,22): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "colgroup": true,
+            "col": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup",
+                        "children": [
+                          {
+                            "tag": "col",
+                            "attrs": [
+                              {
+                                "name": "foo",
+                                "value": "bar"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><colgroup><col foo=\"bar\"></colgroup></table></body></html>",
+        "noQuirksBodyHtml": "<table><colgroup><col foo=\"bar\"></colgroup></table>"
+      }
+    },
+    {
+      "data": "<table><colgroup></html>foo",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,24): unexpected-end-tag",
+        "(1,27): foster-parenting-character-in-table",
+        "(1,27): foster-parenting-character-in-table",
+        "(1,27): foster-parenting-character-in-table",
+        "(1,27): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "colgroup": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "foo"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>foo<table><colgroup></colgroup></table></body></html>",
+        "noQuirksBodyHtml": "foo<table><colgroup></colgroup></table>"
+      }
+    },
+    {
+      "data": "<table></table><p>foo",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table></table><p>foo</p></body></html>",
+        "noQuirksBodyHtml": "<table></table><p>foo</p>"
+      }
+    },
+    {
+      "data": "<table></body></caption></col></colgroup></html></tbody></td></tfoot></th></thead></tr><td>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,14): unexpected-end-tag",
+        "(1,24): unexpected-end-tag",
+        "(1,30): unexpected-end-tag",
+        "(1,41): unexpected-end-tag",
+        "(1,48): unexpected-end-tag",
+        "(1,56): unexpected-end-tag",
+        "(1,61): unexpected-end-tag",
+        "(1,69): unexpected-end-tag",
+        "(1,74): unexpected-end-tag",
+        "(1,82): unexpected-end-tag",
+        "(1,87): unexpected-end-tag",
+        "(1,91): unexpected-cell-in-table-body",
+        "(1,91): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><select><option>3</select></table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,15): unexpected-start-tag-implies-table-voodoo"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option",
+                        "children": [
+                          {
+                            "text": "3"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select><option>3</option></select><table></table></body></html>",
+        "noQuirksBodyHtml": "<select><option>3</option></select><table></table>"
+      }
+    },
+    {
+      "data": "<table><select><table></table></select></table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,15): unexpected-start-tag-implies-table-voodoo",
+        "(1,22): unexpected-table-element-start-tag-in-select-in-table",
+        "(1,22): unexpected-start-tag-implies-end-tag",
+        "(1,39): unexpected-end-tag",
+        "(1,47): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  },
+                  {
+                    "tag": "table"
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select></select><table></table><table></table></body></html>",
+        "noQuirksBodyHtml": "<select></select><table></table><table></table>"
+      }
+    },
+    {
+      "data": "<table><select></table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,15): unexpected-start-tag-implies-table-voodoo",
+        "(1,23): unexpected-table-element-end-tag-in-select-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select></select><table></table></body></html>",
+        "noQuirksBodyHtml": "<select></select><table></table>"
+      }
+    },
+    {
+      "data": "<table><select><option>A<tr><td>B</td></tr></table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,15): unexpected-start-tag-implies-table-voodoo",
+        "(1,28): unexpected-table-element-start-tag-in-select-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option",
+                        "children": [
+                          {
+                            "text": "A"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "text": "B"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select><option>A</option></select><table><tbody><tr><td>B</td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<select><option>A</option></select><table><tbody><tr><td>B</td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><td></body></caption></col></colgroup></html>foo",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-cell-in-table-body",
+        "(1,18): unexpected-end-tag",
+        "(1,28): unexpected-end-tag",
+        "(1,34): unexpected-end-tag",
+        "(1,45): unexpected-end-tag",
+        "(1,52): unexpected-end-tag",
+        "(1,55): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "text": "foo"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td>foo</td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td>foo</td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><td>A</table>B",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-cell-in-table-body"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "text": "A"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "text": "B"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td>A</td></tr></tbody></table>B</body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td>A</td></tr></tbody></table>B"
+      }
+    },
+    {
+      "data": "<table><tr><caption>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,20): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "caption": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "caption"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr></tr></tbody><caption></caption></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr></tr></tbody><caption></caption></table>"
+      }
+    },
+    {
+      "data": "<table><tr></body></caption></col></colgroup></html></td></th><td>foo",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,18): unexpected-end-tag-in-table-row",
+        "(1,28): unexpected-end-tag-in-table-row",
+        "(1,34): unexpected-end-tag-in-table-row",
+        "(1,45): unexpected-end-tag-in-table-row",
+        "(1,52): unexpected-end-tag-in-table-row",
+        "(1,57): unexpected-end-tag-in-table-row",
+        "(1,62): unexpected-end-tag-in-table-row",
+        "(1,69): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "text": "foo"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td>foo</td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td>foo</td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><td><tr>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-cell-in-table-body",
+        "(1,15): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td></td></tr><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td></td></tr><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><td><button><td>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-cell-in-table-body",
+        "(1,23): unexpected-cell-end-tag",
+        "(1,23): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "button": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "button"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td><button></button></td><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><button></button></td><td></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><tr><td><svg><desc><td>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,30): unexpected-cell-end-tag",
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "svg svg": true,
+            "svg desc": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "svg",
+                                    "ns": "http://www.w3.org/2000/svg",
+                                    "children": [
+                                      {
+                                        "tag": "desc",
+                                        "ns": "http://www.w3.org/2000/svg"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td><svg><desc></desc></svg></td><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><svg><desc></desc></svg></td><td></td></tr></tbody></table>"
+      }
+    }
+  ],
+  "template.dat": [
+    {
+      "data": "<body><template>Hello</template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "text": "Hello"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template>Hello</template></body></html>",
+        "noQuirksBodyHtml": "<template>Hello</template>"
+      }
+    },
+    {
+      "data": "<template>Hello</template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "text": "Hello"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template>Hello</template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template>Hello</template>"
+      }
+    },
+    {
+      "data": "<template></template><div></div>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "body": true,
+            "div": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template></template></head><body><div></div></body></html>",
+        "noQuirksBodyHtml": "<template></template><div></div>"
+      }
+    },
+    {
+      "data": "<html><template>Hello</template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "text": "Hello"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template>Hello</template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template>Hello</template>"
+      }
+    },
+    {
+      "data": "<head><template><div></div></template></head>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "div": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><div></div></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><div></div></template>"
+      }
+    },
+    {
+      "data": "<div><template><div><span></template><b>",
+      "errors": [
+        " * (1,6) missing DOCTYPE",
+        " * (1,38) mismatched template end tag",
+        " * (1,41) unexpected end of file"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "template": true,
+            "span": true,
+            "b": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true,
+                            "children": [
+                              {
+                                "tag": "div",
+                                "children": [
+                                  {
+                                    "tag": "span"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><template><div><span></span></div></template><b></b></div></body></html>",
+        "noQuirksBodyHtml": "<div><template><div><span></span></div></template><b></b></div>"
+      }
+    },
+    {
+      "data": "<div><template></div>Hello",
+      "errors": [
+        " * (1,6) missing DOCTYPE",
+        " * (1,22) unexpected token in template",
+        " * (1,27) unexpected end of file in template",
+        " * (1,27) unexpected end of file"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true,
+                            "children": [
+                              {
+                                "text": "Hello"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><template>Hello</template></div></body></html>",
+        "noQuirksBodyHtml": "<div><template>Hello</template></div>"
+      }
+    },
+    {
+      "data": "<div></template></div>",
+      "errors": [
+        " * (1,6) missing DOCTYPE",
+        " * (1,17) unexpected template end tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div></div></body></html>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<table><template></template></table>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><template></template></table></body></html>",
+        "noQuirksBodyHtml": "<table><template></template></table>"
+      }
+    },
+    {
+      "data": "<table><template></template></div>",
+      "errors": [
+        " * (1,8) missing DOCTYPE",
+        " * (1,35) unexpected token in table - foster parenting",
+        " * (1,35) unexpected end tag",
+        " * (1,35) unexpected end of file"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><template></template></table></body></html>",
+        "noQuirksBodyHtml": "<table><template></template></table>"
+      }
+    },
+    {
+      "data": "<table><div><template></template></div>",
+      "errors": [
+        " * (1,8) missing DOCTYPE",
+        " * (1,13) unexpected token in table - foster parenting",
+        " * (1,40) unexpected token in table - foster parenting",
+        " * (1,40) unexpected end of file"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "template": true,
+            "table": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><template></template></div><table></table></body></html>",
+        "noQuirksBodyHtml": "<div><template></template></div><table></table>"
+      }
+    },
+    {
+      "data": "<table><template></template><div></div>",
+      "errors": [
+        "no doctype",
+        "bad div in table",
+        "bad /div in table",
+        "eof in table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "table": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div></div><table><template></template></table></body></html>",
+        "noQuirksBodyHtml": "<div></div><table><template></template></table>"
+      }
+    },
+    {
+      "data": "<table>   <template></template></table>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "text": "   "
+                      },
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table>   <template></template></table></body></html>",
+        "noQuirksBodyHtml": "<table>   <template></template></table>"
+      }
+    },
+    {
+      "data": "<table><tbody><template></template></tbody>",
+      "errors": [
+        "no doctype",
+        "eof in table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><template></template></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><template></template></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><tbody><template></tbody></template>",
+      "errors": [
+        "no doctype",
+        "bad /tbody",
+        "eof in table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><template></template></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><template></template></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><tbody><template></template></tbody></table>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><template></template></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><template></template></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><thead><template></template></thead>",
+      "errors": [
+        "no doctype",
+        "eof in table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "thead": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "thead",
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><thead><template></template></thead></table></body></html>",
+        "noQuirksBodyHtml": "<table><thead><template></template></thead></table>"
+      }
+    },
+    {
+      "data": "<table><tfoot><template></template></tfoot>",
+      "errors": [
+        "no doctype",
+        "eof in table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tfoot": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tfoot",
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tfoot><template></template></tfoot></table></body></html>",
+        "noQuirksBodyHtml": "<table><tfoot><template></template></tfoot></table>"
+      }
+    },
+    {
+      "data": "<select><template></template></select>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select><template></template></select></body></html>",
+        "noQuirksBodyHtml": "<select><template></template></select>"
+      }
+    },
+    {
+      "data": "<select><template><option></option></template></select>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "template": true,
+            "option": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true,
+                            "children": [
+                              {
+                                "tag": "option"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select><template><option></option></template></select></body></html>",
+        "noQuirksBodyHtml": "<select><template><option></option></template></select>"
+      }
+    },
+    {
+      "data": "<template><option></option></select><option></option></template>",
+      "errors": [
+        "no doctype",
+        "bad /select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "option": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "option"
+                          },
+                          {
+                            "tag": "option"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><option></option><option></option></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><option></option><option></option></template>"
+      }
+    },
+    {
+      "data": "<select><template></template><option></select>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "template": true,
+            "option": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "option"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select><template></template><option></option></select></body></html>",
+        "noQuirksBodyHtml": "<select><template></template><option></option></select>"
+      }
+    },
+    {
+      "data": "<select><option><template></template></select>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option",
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select><option><template></template></option></select></body></html>",
+        "noQuirksBodyHtml": "<select><option><template></template></option></select>"
+      }
+    },
+    {
+      "data": "<select><template>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select><template></template></select></body></html>",
+        "noQuirksBodyHtml": "<select><template></template></select>"
+      }
+    },
+    {
+      "data": "<select><option></option><template>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option"
+                      },
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select><option></option><template></template></select></body></html>",
+        "noQuirksBodyHtml": "<select><option></option><template></template></select>"
+      }
+    },
+    {
+      "data": "<select><option></option><template><option>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option"
+                      },
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true,
+                            "children": [
+                              {
+                                "tag": "option"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select><option></option><template><option></option></template></select></body></html>",
+        "noQuirksBodyHtml": "<select><option></option><template><option></option></template></select>"
+      }
+    },
+    {
+      "data": "<table><thead><template><td></template></table>",
+      "errors": [
+        " * (1,8) missing DOCTYPE"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "thead": true,
+            "template": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "thead",
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "td"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><thead><template><td></td></template></thead></table></body></html>",
+        "noQuirksBodyHtml": "<table><thead><template><td></td></template></thead></table>"
+      }
+    },
+    {
+      "data": "<table><template><thead></template></table>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "template": true,
+            "thead": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true,
+                            "children": [
+                              {
+                                "tag": "thead"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><template><thead></thead></template></table></body></html>",
+        "noQuirksBodyHtml": "<table><template><thead></thead></template></table>"
+      }
+    },
+    {
+      "data": "<body><table><template><td></tr><div></template></table>",
+      "errors": [
+        "no doctype",
+        "bad </tr>",
+        "missing </div>"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "template": true,
+            "td": true,
+            "div": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true,
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "div"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><template><td><div></div></td></template></table></body></html>",
+        "noQuirksBodyHtml": "<table><template><td><div></div></td></template></table>"
+      }
+    },
+    {
+      "data": "<table><template><thead></template></thead></table>",
+      "errors": [
+        "no doctype",
+        "bad /thead after /template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "template": true,
+            "thead": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true,
+                            "children": [
+                              {
+                                "tag": "thead"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><template><thead></thead></template></table></body></html>",
+        "noQuirksBodyHtml": "<table><template><thead></thead></template></table>"
+      }
+    },
+    {
+      "data": "<table><thead><template><tr></template></table>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "thead": true,
+            "template": true,
+            "tr": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "thead",
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "tr"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><thead><template><tr></tr></template></thead></table></body></html>",
+        "noQuirksBodyHtml": "<table><thead><template><tr></tr></template></thead></table>"
+      }
+    },
+    {
+      "data": "<table><template><tr></template></table>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "template": true,
+            "tr": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true,
+                            "children": [
+                              {
+                                "tag": "tr"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><template><tr></tr></template></table></body></html>",
+        "noQuirksBodyHtml": "<table><template><tr></tr></template></table>"
+      }
+    },
+    {
+      "data": "<table><tr><template><td>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "template": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "template",
+                                "children": [
+                                  {
+                                    "content": true,
+                                    "children": [
+                                      {
+                                        "tag": "td"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><template><td></td></template></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><template><td></td></template></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><template><tr><template><td></template></tr></template></table>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "template": true,
+            "tr": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true,
+                            "children": [
+                              {
+                                "tag": "tr",
+                                "children": [
+                                  {
+                                    "tag": "template",
+                                    "children": [
+                                      {
+                                        "content": true,
+                                        "children": [
+                                          {
+                                            "tag": "td"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><template><tr><template><td></td></template></tr></template></table></body></html>",
+        "noQuirksBodyHtml": "<table><template><tr><template><td></td></template></tr></template></table>"
+      }
+    },
+    {
+      "data": "<table><template><tr><template><td></td></template></tr></template></table>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "template": true,
+            "tr": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true,
+                            "children": [
+                              {
+                                "tag": "tr",
+                                "children": [
+                                  {
+                                    "tag": "template",
+                                    "children": [
+                                      {
+                                        "content": true,
+                                        "children": [
+                                          {
+                                            "tag": "td"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><template><tr><template><td></td></template></tr></template></table></body></html>",
+        "noQuirksBodyHtml": "<table><template><tr><template><td></td></template></tr></template></table>"
+      }
+    },
+    {
+      "data": "<table><template><td></template>",
+      "errors": [
+        "no doctype",
+        "eof in table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "template": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true,
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><template><td></td></template></table></body></html>",
+        "noQuirksBodyHtml": "<table><template><td></td></template></table>"
+      }
+    },
+    {
+      "data": "<body><template><td></td></template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "td"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><td></td></template></body></html>",
+        "noQuirksBodyHtml": "<template><td></td></template>"
+      }
+    },
+    {
+      "data": "<body><template><template><tr></tr></template><td></td></template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "tr": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "tr"
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "td"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><template><tr></tr></template><td></td></template></body></html>",
+        "noQuirksBodyHtml": "<template><template><tr></tr></template><td></td></template>"
+      }
+    },
+    {
+      "data": "<table><colgroup><template><col>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "colgroup": true,
+            "template": true,
+            "col": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup",
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "col"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><colgroup><template><col></template></colgroup></table></body></html>",
+        "noQuirksBodyHtml": "<table><colgroup><template><col></template></colgroup></table>"
+      }
+    },
+    {
+      "data": "<frameset><template><frame></frame></template></frameset>",
+      "errors": [
+        " * (1,11) missing DOCTYPE",
+        " * (1,21) unexpected start tag token",
+        " * (1,36) unexpected end tag token",
+        " * (1,47) unexpected end tag token"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "frame": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset",
+                "children": [
+                  {
+                    "tag": "frame"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset><frame></frameset></html>",
+        "noQuirksBodyHtml": "<template></template>"
+      }
+    },
+    {
+      "data": "<template><frame></frame></frameset><frame></frame></template>",
+      "errors": [
+        " * (1,11) missing DOCTYPE",
+        " * (1,18) unexpected start tag",
+        " * (1,26) unexpected end tag",
+        " * (1,37) unexpected end tag",
+        " * (1,44) unexpected start tag",
+        " * (1,52) unexpected end tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template></template>"
+      }
+    },
+    {
+      "data": "<template><div><frameset><span></span></div><span></span></template>",
+      "errors": [
+        "no doctype",
+        "bad frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "div": true,
+            "span": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "div",
+                            "children": [
+                              {
+                                "tag": "span"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "span"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><div><span></span></div><span></span></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><div><span></span></div><span></span></template>"
+      }
+    },
+    {
+      "data": "<body><template><div><frameset><span></span></div><span></span></template></body>",
+      "errors": [
+        "no doctype",
+        "bad frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "div": true,
+            "span": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "div",
+                            "children": [
+                              {
+                                "tag": "span"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "span"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><div><span></span></div><span></span></template></body></html>",
+        "noQuirksBodyHtml": "<template><div><span></span></div><span></span></template>"
+      }
+    },
+    {
+      "data": "<body><template><script>var i = 1;</script><td></td></template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "script": true,
+            "td": true
+          },
+          "template": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "script",
+                            "children": [
+                              {
+                                "text": "var i = 1;",
+                                "no_escape": true
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "td"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><script>var i = 1;</script><td></td></template></body></html>",
+        "noQuirksBodyHtml": "<template><script>var i = 1;</script><td></td></template>"
+      }
+    },
+    {
+      "data": "<body><template><tr><div></div></tr></template>",
+      "errors": [
+        "no doctype",
+        "foster-parented div",
+        "foster-parented /div"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "tr": true,
+            "div": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "tr"
+                          },
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><tr></tr><div></div></template></body></html>",
+        "noQuirksBodyHtml": "<template><tr></tr><div></div></template>"
+      }
+    },
+    {
+      "data": "<body><template><tr></tr><td></td></template>",
+      "errors": [
+        "no doctype",
+        "unexpected <td>"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "tr": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "tr"
+                          },
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><tr></tr><tr><td></td></tr></template></body></html>",
+        "noQuirksBodyHtml": "<template><tr></tr><tr><td></td></tr></template>"
+      }
+    },
+    {
+      "data": "<body><template><td></td></tr><td></td></template>",
+      "errors": [
+        "no doctype",
+        "bad </tr>"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "td"
+                          },
+                          {
+                            "tag": "td"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><td></td><td></td></template></body></html>",
+        "noQuirksBodyHtml": "<template><td></td><td></td></template>"
+      }
+    },
+    {
+      "data": "<body><template><td></td><tbody><td></td></template>",
+      "errors": [
+        "no doctype",
+        "bad <tbody>"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "td"
+                          },
+                          {
+                            "tag": "td"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><td></td><td></td></template></body></html>",
+        "noQuirksBodyHtml": "<template><td></td><td></td></template>"
+      }
+    },
+    {
+      "data": "<body><template><td></td><caption></caption><td></td></template>",
+      "errors": [
+        " * (1,7) missing DOCTYPE",
+        " * (1,35) unexpected start tag in table row",
+        " * (1,45) unexpected end tag in table row"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "td"
+                          },
+                          {
+                            "tag": "td"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><td></td><td></td></template></body></html>",
+        "noQuirksBodyHtml": "<template><td></td><td></td></template>"
+      }
+    },
+    {
+      "data": "<body><template><td></td><colgroup></caption><td></td></template>",
+      "errors": [
+        " * (1,7) missing DOCTYPE",
+        " * (1,36) unexpected start tag in table row",
+        " * (1,46) unexpected end tag in table row"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "td"
+                          },
+                          {
+                            "tag": "td"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><td></td><td></td></template></body></html>",
+        "noQuirksBodyHtml": "<template><td></td><td></td></template>"
+      }
+    },
+    {
+      "data": "<body><template><td></td></table><td></td></template>",
+      "errors": [
+        "no doctype",
+        "bad </table>"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "td"
+                          },
+                          {
+                            "tag": "td"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><td></td><td></td></template></body></html>",
+        "noQuirksBodyHtml": "<template><td></td><td></td></template>"
+      }
+    },
+    {
+      "data": "<body><template><tr></tr><tbody><tr></tr></template>",
+      "errors": [
+        "no doctype",
+        "bad <tbody>"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "tr": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "tr"
+                          },
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><tr></tr><tr></tr></template></body></html>",
+        "noQuirksBodyHtml": "<template><tr></tr><tr></tr></template>"
+      }
+    },
+    {
+      "data": "<body><template><tr></tr><caption><tr></tr></template>",
+      "errors": [
+        "no doctype",
+        "bad <caption>"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "tr": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "tr"
+                          },
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><tr></tr><tr></tr></template></body></html>",
+        "noQuirksBodyHtml": "<template><tr></tr><tr></tr></template>"
+      }
+    },
+    {
+      "data": "<body><template><tr></tr></table><tr></tr></template>",
+      "errors": [
+        "no doctype",
+        "bad </table>"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "tr": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "tr"
+                          },
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><tr></tr><tr></tr></template></body></html>",
+        "noQuirksBodyHtml": "<template><tr></tr><tr></tr></template>"
+      }
+    },
+    {
+      "data": "<body><template><thead></thead><caption></caption><tbody></tbody></template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "thead": true,
+            "caption": true,
+            "tbody": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "thead"
+                          },
+                          {
+                            "tag": "caption"
+                          },
+                          {
+                            "tag": "tbody"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><thead></thead><caption></caption><tbody></tbody></template></body></html>",
+        "noQuirksBodyHtml": "<template><thead></thead><caption></caption><tbody></tbody></template>"
+      }
+    },
+    {
+      "data": "<body><template><thead></thead></table><tbody></tbody></template></body>",
+      "errors": [
+        "no doctype",
+        "bad </table>"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "thead": true,
+            "tbody": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "thead"
+                          },
+                          {
+                            "tag": "tbody"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><thead></thead><tbody></tbody></template></body></html>",
+        "noQuirksBodyHtml": "<template><thead></thead><tbody></tbody></template>"
+      }
+    },
+    {
+      "data": "<body><template><div><tr></tr></div></template>",
+      "errors": [
+        "no doctype",
+        "bad tr",
+        "bad /tr"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "div": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><div></div></template></body></html>",
+        "noQuirksBodyHtml": "<template><div></div></template>"
+      }
+    },
+    {
+      "data": "<body><template><em>Hello</em></template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "em": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "em",
+                            "children": [
+                              {
+                                "text": "Hello"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><em>Hello</em></template></body></html>",
+        "noQuirksBodyHtml": "<template><em>Hello</em></template>"
+      }
+    },
+    {
+      "data": "<body><template><!--comment--></template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true
+          },
+          "template": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "comment": "comment"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><!--comment--></template></body></html>",
+        "noQuirksBodyHtml": "<template><!--comment--></template>"
+      }
+    },
+    {
+      "data": "<body><template><style></style><td></td></template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "style": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "style"
+                          },
+                          {
+                            "tag": "td"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><style></style><td></td></template></body></html>",
+        "noQuirksBodyHtml": "<template><style></style><td></td></template>"
+      }
+    },
+    {
+      "data": "<body><template><meta><td></td></template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "meta": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "meta"
+                          },
+                          {
+                            "tag": "td"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><meta><td></td></template></body></html>",
+        "noQuirksBodyHtml": "<template><meta><td></td></template>"
+      }
+    },
+    {
+      "data": "<body><template><link><td></td></template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "link": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "link"
+                          },
+                          {
+                            "tag": "td"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><link><td></td></template></body></html>",
+        "noQuirksBodyHtml": "<template><link><td></td></template>"
+      }
+    },
+    {
+      "data": "<body><template><template><tr></tr></template><td></td></template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "tr": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "tr"
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "td"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><template><tr></tr></template><td></td></template></body></html>",
+        "noQuirksBodyHtml": "<template><template><tr></tr></template><td></td></template>"
+      }
+    },
+    {
+      "data": "<body><table><colgroup><template><col></col></template></colgroup></table></body>",
+      "errors": [
+        "no doctype",
+        "bad /col"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "colgroup": true,
+            "template": true,
+            "col": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup",
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "col"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><colgroup><template><col></template></colgroup></table></body></html>",
+        "noQuirksBodyHtml": "<table><colgroup><template><col></template></colgroup></table>"
+      }
+    },
+    {
+      "data": "<body a=b><template><div></div><body c=d><div></div></body></template></body>",
+      "errors": [
+        "no doctype",
+        "bad <body>",
+        "bad </body>"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "div": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "attrs": [
+                  {
+                    "name": "a",
+                    "value": "b"
+                  }
+                ],
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "div"
+                          },
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body a=\"b\"><template><div></div><div></div></template></body></html>",
+        "noQuirksBodyHtml": "<template><div></div><div></div></template>"
+      }
+    },
+    {
+      "data": "<html a=b><template><div><html b=c><span></template>",
+      "errors": [
+        "no doctype",
+        "bad <html>",
+        "missing end tags in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "div": true,
+            "span": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "attrs": [
+              {
+                "name": "a",
+                "value": "b"
+              }
+            ],
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "div",
+                            "children": [
+                              {
+                                "tag": "span"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html a=\"b\"><head><template><div><span></span></div></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><div><span></span></div></template>"
+      }
+    },
+    {
+      "data": "<html a=b><template><col></col><html b=c><col></col></template>",
+      "errors": [
+        "no doctype",
+        "bad /col",
+        "bad html",
+        "bad /col"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "col": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "attrs": [
+              {
+                "name": "a",
+                "value": "b"
+              }
+            ],
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "col"
+                          },
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html a=\"b\"><head><template><col><col></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><col><col></template>"
+      }
+    },
+    {
+      "data": "<html a=b><template><frame></frame><html b=c><frame></frame></template>",
+      "errors": [
+        "no doctype",
+        "bad frame",
+        "bad /frame",
+        "bad html",
+        "bad frame",
+        "bad /frame"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "attrs": [
+              {
+                "name": "a",
+                "value": "b"
+              }
+            ],
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html a=\"b\"><head><template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template></template>"
+      }
+    },
+    {
+      "data": "<body><template><tr></tr><template></template><td></td></template>",
+      "errors": [
+        "no doctype",
+        "unexpected <td>"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "tr": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "tr"
+                          },
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><tr></tr><template></template><tr><td></td></tr></template></body></html>",
+        "noQuirksBodyHtml": "<template><tr></tr><template></template><tr><td></td></tr></template>"
+      }
+    },
+    {
+      "data": "<body><template><thead></thead><template><tr></tr></template><tr></tr><tfoot></tfoot></template>",
+      "errors": [
+        "no doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "thead": true,
+            "tr": true,
+            "tbody": true,
+            "tfoot": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "thead"
+                          },
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "tr"
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "tbody",
+                            "children": [
+                              {
+                                "tag": "tr"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "tfoot"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><thead></thead><template><tr></tr></template><tbody><tr></tr></tbody><tfoot></tfoot></template></body></html>",
+        "noQuirksBodyHtml": "<template><thead></thead><template><tr></tr></template><tbody><tr></tr></tbody><tfoot></tfoot></template>"
+      }
+    },
+    {
+      "data": "<body><template><template><b><template></template></template>text</template>",
+      "errors": [
+        "no doctype",
+        "missing </b>"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "b": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "b",
+                                    "children": [
+                                      {
+                                        "tag": "template",
+                                        "children": [
+                                          {
+                                            "content": true
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "text": "text"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><template><b><template></template></b></template>text</template></body></html>",
+        "noQuirksBodyHtml": "<template><template><b><template></template></b></template>text</template>"
+      }
+    },
+    {
+      "data": "<body><template><col><colgroup>",
+      "errors": [
+        "no doctype",
+        "bad colgroup",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "col": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><col></template></body></html>",
+        "noQuirksBodyHtml": "<template><col></template>"
+      }
+    },
+    {
+      "data": "<body><template><col></colgroup>",
+      "errors": [
+        "no doctype",
+        "bogus /colgroup",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "col": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><col></template></body></html>",
+        "noQuirksBodyHtml": "<template><col></template>"
+      }
+    },
+    {
+      "data": "<body><template><col><colgroup></template></body>",
+      "errors": [
+        "no doctype",
+        "bad colgroup"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "col": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><col></template></body></html>",
+        "noQuirksBodyHtml": "<template><col></template>"
+      }
+    },
+    {
+      "data": "<body><template><col><div>",
+      "errors": [
+        " * (1,7) missing DOCTYPE",
+        " * (1,27) unexpected token",
+        " * (1,27) unexpected end of file in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "col": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><col></template></body></html>",
+        "noQuirksBodyHtml": "<template><col></template>"
+      }
+    },
+    {
+      "data": "<body><template><col></div>",
+      "errors": [
+        "no doctype",
+        "bad /div",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "col": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><col></template></body></html>",
+        "noQuirksBodyHtml": "<template><col></template>"
+      }
+    },
+    {
+      "data": "<body><template><col>Hello",
+      "errors": [
+        "no doctype",
+        "unexpected text",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "col": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><col></template></body></html>",
+        "noQuirksBodyHtml": "<template><col></template>"
+      }
+    },
+    {
+      "data": "<body><template><i><menu>Foo</i>",
+      "errors": [
+        "no doctype",
+        "mising /menu",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "i": true,
+            "menu": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "i"
+                          },
+                          {
+                            "tag": "menu",
+                            "children": [
+                              {
+                                "tag": "i",
+                                "children": [
+                                  {
+                                    "text": "Foo"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><i></i><menu><i>Foo</i></menu></template></body></html>",
+        "noQuirksBodyHtml": "<template><i></i><menu><i>Foo</i></menu></template>"
+      }
+    },
+    {
+      "data": "<body><template></div><div>Foo</div><template></template><tr></tr>",
+      "errors": [
+        "no doctype",
+        "bogus /div",
+        "bogus tr",
+        "bogus /tr",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true,
+            "div": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "div",
+                            "children": [
+                              {
+                                "text": "Foo"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template><div>Foo</div><template></template></template></body></html>",
+        "noQuirksBodyHtml": "<template><div>Foo</div><template></template></template>"
+      }
+    },
+    {
+      "data": "<body><div><template></div><tr><td>Foo</td></tr></template>",
+      "errors": [
+        " * (1,7) missing DOCTYPE",
+        " * (1,28) unexpected token in template",
+        " * (1,60) unexpected end of file"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "template": true,
+            "tr": true,
+            "td": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true,
+                            "children": [
+                              {
+                                "tag": "tr",
+                                "children": [
+                                  {
+                                    "tag": "td",
+                                    "children": [
+                                      {
+                                        "text": "Foo"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><template><tr><td>Foo</td></tr></template></div></body></html>",
+        "noQuirksBodyHtml": "<div><template><tr><td>Foo</td></tr></template></div>"
+      }
+    },
+    {
+      "data": "<template></figcaption><sub><table></table>",
+      "errors": [
+        "no doctype",
+        "bad /figcaption",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "sub": true,
+            "table": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "sub",
+                            "children": [
+                              {
+                                "tag": "table"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><sub><table></table></sub></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><sub><table></table></sub></template>"
+      }
+    },
+    {
+      "data": "<template><template>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template></template></template>"
+      }
+    },
+    {
+      "data": "<template><div>",
+      "errors": [
+        "no doctype",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "div": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><div></div></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><div></div></template>"
+      }
+    },
+    {
+      "data": "<template><template><div>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "div": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "div"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template><div></div></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template><div></div></template></template>"
+      }
+    },
+    {
+      "data": "<template><template><table>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "table": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "table"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template><table></table></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template><table></table></template></template>"
+      }
+    },
+    {
+      "data": "<template><template><tbody>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "tbody": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "tbody"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template><tbody></tbody></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template><tbody></tbody></template></template>"
+      }
+    },
+    {
+      "data": "<template><template><tr>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "tr": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "tr"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template><tr></tr></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template><tr></tr></template></template>"
+      }
+    },
+    {
+      "data": "<template><template><td>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "td": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "td"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template><td></td></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template><td></td></template></template>"
+      }
+    },
+    {
+      "data": "<template><template><caption>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "caption": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "caption"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template><caption></caption></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template><caption></caption></template></template>"
+      }
+    },
+    {
+      "data": "<template><template><colgroup>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "colgroup": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "colgroup"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template><colgroup></colgroup></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template><colgroup></colgroup></template></template>"
+      }
+    },
+    {
+      "data": "<template><template><col>",
+      "errors": [
+        "no doctype",
+        "eof in template",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "col": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "col"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template><col></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template><col></template></template>"
+      }
+    },
+    {
+      "data": "<template><template><tbody><select>",
+      "errors": [
+        " * (1,11) missing DOCTYPE",
+        " * (1,36) unexpected token in table - foster parenting",
+        " * (1,36) unexpected end of file in template",
+        " * (1,36) unexpected end of file in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "tbody": true,
+            "select": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "tbody"
+                                  },
+                                  {
+                                    "tag": "select"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template><tbody></tbody><select></select></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template><tbody></tbody><select></select></template></template>"
+      }
+    },
+    {
+      "data": "<template><template><table>Foo",
+      "errors": [
+        "no doctype",
+        "foster-parenting text F",
+        "foster-parenting text o",
+        "foster-parenting text o",
+        "eof",
+        "eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "table": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "text": "Foo"
+                                  },
+                                  {
+                                    "tag": "table"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template>Foo<table></table></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template>Foo<table></table></template></template>"
+      }
+    },
+    {
+      "data": "<template><template><frame>",
+      "errors": [
+        "no doctype",
+        "bad tag",
+        "eof",
+        "eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template></template></template>"
+      }
+    },
+    {
+      "data": "<template><template><script>var i",
+      "errors": [
+        "no doctype",
+        "eof in script",
+        "eof in template",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "script": true,
+            "body": true
+          },
+          "template": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "script",
+                                    "children": [
+                                      {
+                                        "text": "var i",
+                                        "no_escape": true
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template><script>var i</script></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template><script>var i</script></template></template>"
+      }
+    },
+    {
+      "data": "<template><template><style>var i",
+      "errors": [
+        "no doctype",
+        "eof in style",
+        "eof in template",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "style": true,
+            "body": true
+          },
+          "template": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "style",
+                                    "children": [
+                                      {
+                                        "text": "var i",
+                                        "no_escape": true
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><template><style>var i</style></template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><template><style>var i</style></template></template>"
+      }
+    },
+    {
+      "data": "<template><table></template><body><span>Foo",
+      "errors": [
+        "no doctype",
+        "missing /table",
+        "bad eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "table": true,
+            "body": true,
+            "span": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "table"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "span",
+                    "children": [
+                      {
+                        "text": "Foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><table></table></template></head><body><span>Foo</span></body></html>",
+        "noQuirksBodyHtml": "<template><table></table></template><span>Foo</span>"
+      }
+    },
+    {
+      "data": "<template><td></template><body><span>Foo",
+      "errors": [
+        "no doctype",
+        "bad eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "td": true,
+            "body": true,
+            "span": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "td"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "span",
+                    "children": [
+                      {
+                        "text": "Foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><td></td></template></head><body><span>Foo</span></body></html>",
+        "noQuirksBodyHtml": "<template><td></td></template><span>Foo</span>"
+      }
+    },
+    {
+      "data": "<template><object></template><body><span>Foo",
+      "errors": [
+        "no doctype",
+        "missing /object",
+        "bad eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "object": true,
+            "body": true,
+            "span": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "object"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "span",
+                    "children": [
+                      {
+                        "text": "Foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><object></object></template></head><body><span>Foo</span></body></html>",
+        "noQuirksBodyHtml": "<template><object></object></template><span>Foo</span>"
+      }
+    },
+    {
+      "data": "<template><svg><template>",
+      "errors": [
+        "no doctype",
+        "eof in template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "svg svg": true,
+            "svg template": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "template",
+                                "ns": "http://www.w3.org/2000/svg"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><svg><template></template></svg></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><svg><template></template></svg></template>"
+      }
+    },
+    {
+      "data": "<template><svg><foo><template><foreignObject><div></template><div>",
+      "errors": [
+        "no doctype",
+        "ugly template closure",
+        "bad eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "svg svg": true,
+            "svg foo": true,
+            "svg template": true,
+            "svg foreignObject": true,
+            "div": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "foo",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "tag": "template",
+                                    "ns": "http://www.w3.org/2000/svg",
+                                    "children": [
+                                      {
+                                        "tag": "foreignObject",
+                                        "ns": "http://www.w3.org/2000/svg",
+                                        "children": [
+                                          {
+                                            "tag": "div"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><svg><foo><template><foreignObject><div></div></foreignObject></template></foo></svg></template></head><body><div></div></body></html>",
+        "noQuirksBodyHtml": "<template><svg><foo><template><foreignObject><div></div></foreignObject></template></foo></svg></template><div></div>"
+      }
+    },
+    {
+      "data": "<dummy><template><span></dummy>",
+      "errors": [
+        "no doctype",
+        "bad end tag </dummy>",
+        "eof in template",
+        "eof in dummy"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "dummy": true,
+            "template": true,
+            "span": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "dummy",
+                    "children": [
+                      {
+                        "tag": "template",
+                        "children": [
+                          {
+                            "content": true,
+                            "children": [
+                              {
+                                "tag": "span"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><dummy><template><span></span></template></dummy></body></html>",
+        "noQuirksBodyHtml": "<dummy><template><span></span></template></dummy>"
+      }
+    },
+    {
+      "data": "<body><table><tr><td><select><template>Foo</template><caption>A</table>",
+      "errors": [
+        "no doctype",
+        "(1,62): unexpected-caption-in-select-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "select": true,
+            "template": true,
+            "caption": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "select",
+                                    "children": [
+                                      {
+                                        "tag": "template",
+                                        "children": [
+                                          {
+                                            "content": true,
+                                            "children": [
+                                              {
+                                                "text": "Foo"
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "text": "A"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td><select><template>Foo</template></select></td></tr></tbody><caption>A</caption></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><select><template>Foo</template></select></td></tr></tbody><caption>A</caption></table>"
+      }
+    },
+    {
+      "data": "<body></body><template>",
+      "errors": [
+        "no doctype",
+        "(1,23): template-after-body",
+        "(1,24): eof-in-template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "template": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><template></template></body></html>",
+        "noQuirksBodyHtml": "<template></template>"
+      }
+    },
+    {
+      "data": "<head></head><template>",
+      "errors": [
+        "no doctype",
+        "(1,23): template-after-head",
+        "(1,24): eof-in-template"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template></template>"
+      }
+    },
+    {
+      "data": "<head></head><template>Foo</template>",
+      "errors": [
+        "no doctype",
+        "(1,23): template-after-head"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "text": "Foo"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template>Foo</template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template>Foo</template>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE HTML><dummy><table><template><table><template><table><script>",
+      "errors": [
+        "eof script",
+        "eof template",
+        "eof template",
+        "eof table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "dummy": true,
+            "table": true,
+            "template": true,
+            "script": true
+          },
+          "doctype": true,
+          "template": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "dummy",
+                    "children": [
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "tag": "template",
+                            "children": [
+                              {
+                                "content": true,
+                                "children": [
+                                  {
+                                    "tag": "table",
+                                    "children": [
+                                      {
+                                        "tag": "template",
+                                        "children": [
+                                          {
+                                            "content": true,
+                                            "children": [
+                                              {
+                                                "tag": "table",
+                                                "children": [
+                                                  {
+                                                    "tag": "script"
+                                                  }
+                                                ]
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><dummy><table><template><table><template><table><script></script></table></template></table></template></table></dummy></body></html>",
+        "noQuirksBodyHtml": "<dummy><table><template><table><template><table><script></script></table></template></table></template></table></dummy>"
+      }
+    },
+    {
+      "data": "<template><a><table><a>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "template": true,
+            "a": true,
+            "table": true,
+            "body": true
+          },
+          "template": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "template",
+                    "children": [
+                      {
+                        "content": true,
+                        "children": [
+                          {
+                            "tag": "a",
+                            "children": [
+                              {
+                                "tag": "a"
+                              },
+                              {
+                                "tag": "table"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><template><a><a></a><table></table></a></template></head><body></body></html>",
+        "noQuirksBodyHtml": "<template><a><a></a><table></table></a></template>"
+      }
+    }
+  ],
+  "tests1.dat": [
+    {
+      "data": "Test",
+      "errors": [
+        "(1,0): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Test"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>Test</body></html>",
+        "noQuirksBodyHtml": "Test"
+      }
+    },
+    {
+      "data": "<p>One<p>Two",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "One"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "Two"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p>One</p><p>Two</p></body></html>",
+        "noQuirksBodyHtml": "<p>One</p><p>Two</p>"
+      }
+    },
+    {
+      "data": "Line1<br>Line2<br>Line3<br>Line4",
+      "errors": [
+        "(1,0): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "br": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Line1"
+                  },
+                  {
+                    "tag": "br"
+                  },
+                  {
+                    "text": "Line2"
+                  },
+                  {
+                    "tag": "br"
+                  },
+                  {
+                    "text": "Line3"
+                  },
+                  {
+                    "tag": "br"
+                  },
+                  {
+                    "text": "Line4"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>Line1<br>Line2<br>Line3<br>Line4</body></html>",
+        "noQuirksBodyHtml": "Line1<br>Line2<br>Line3<br>Line4"
+      }
+    },
+    {
+      "data": "<html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<head>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<body>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html><head>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html><head></head>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html><head></head><body>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html><head></head><body></body>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html><head><body></body></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html><head></body></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html><head><body></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html><body></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<body></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<head></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "</head>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "</body>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-end-tag element."
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "</html>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-end-tag element."
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<b><table><td><i></table>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,14): unexpected-cell-in-table-body",
+        "(1,25): unexpected-cell-end-tag",
+        "(1,25): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "i": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "tag": "tbody",
+                            "children": [
+                              {
+                                "tag": "tr",
+                                "children": [
+                                  {
+                                    "tag": "td",
+                                    "children": [
+                                      {
+                                        "tag": "i"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b><table><tbody><tr><td><i></i></td></tr></tbody></table></b></body></html>",
+        "noQuirksBodyHtml": "<b><table><tbody><tr><td><i></i></td></tr></tbody></table></b>"
+      }
+    },
+    {
+      "data": "<b><table><td></b><i></table>X",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,14): unexpected-cell-in-table-body",
+        "(1,18): unexpected-end-tag",
+        "(1,29): unexpected-cell-end-tag",
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "i": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "tag": "tbody",
+                            "children": [
+                              {
+                                "tag": "tr",
+                                "children": [
+                                  {
+                                    "tag": "td",
+                                    "children": [
+                                      {
+                                        "tag": "i"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "text": "X"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b><table><tbody><tr><td><i></i></td></tr></tbody></table>X</b></body></html>",
+        "noQuirksBodyHtml": "<b><table><tbody><tr><td><i></i></td></tr></tbody></table>X</b>"
+      }
+    },
+    {
+      "data": "<h1>Hello<h2>World",
+      "errors": [
+        "(1,4): expected-doctype-but-got-start-tag",
+        "(1,13): unexpected-start-tag",
+        "(1,18): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "h1": true,
+            "h2": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "h1",
+                    "children": [
+                      {
+                        "text": "Hello"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "h2",
+                    "children": [
+                      {
+                        "text": "World"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><h1>Hello</h1><h2>World</h2></body></html>",
+        "noQuirksBodyHtml": "<h1>Hello</h1><h2>World</h2>"
+      }
+    },
+    {
+      "data": "<a><p>X<a>Y</a>Z</p></a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,10): unexpected-start-tag-implies-end-tag",
+        "(1,10): adoption-agency-1.3",
+        "(1,24): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "text": "X"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "text": "Y"
+                          }
+                        ]
+                      },
+                      {
+                        "text": "Z"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a></a><p><a>X</a><a>Y</a>Z</p></body></html>",
+        "noQuirksBodyHtml": "<a></a><p><a>X</a><a>Y</a>Z</p>"
+      }
+    },
+    {
+      "data": "<b><button>foo</b>bar",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,18): adoption-agency-1.3",
+        "(1,21): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "button": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b"
+                  },
+                  {
+                    "tag": "button",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "text": "bar"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b></b><button><b>foo</b>bar</button></body></html>",
+        "noQuirksBodyHtml": "<b></b><button><b>foo</b>bar</button>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><span><button>foo</span>bar",
+      "errors": [
+        "(1,39): unexpected-end-tag",
+        "(1,42): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "span": true,
+            "button": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "span",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "text": "foobar"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><span><button>foobar</button></span></body></html>",
+        "noQuirksBodyHtml": "<span><button>foobar</button></span>"
+      }
+    },
+    {
+      "data": "<p><b><div><marquee></p></b></div>X",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-end-tag",
+        "(1,24): unexpected-end-tag",
+        "(1,28): unexpected-end-tag",
+        "(1,34): end-tag-too-early",
+        "(1,35): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "b": true,
+            "div": true,
+            "marquee": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "marquee",
+                            "children": [
+                              {
+                                "tag": "p"
+                              },
+                              {
+                                "text": "X"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><b></b></p><div><b><marquee><p></p>X</marquee></b></div></body></html>",
+        "noQuirksBodyHtml": "<p><b></b></p><div><b><marquee><p></p>X</marquee></b></div>"
+      }
+    },
+    {
+      "data": "<script><div></script></div><title><p></title><p><p>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,28): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "title": true,
+            "body": true,
+            "p": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<div>",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "<p>",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "p"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><div></script><title>&lt;p&gt;</title></head><body><p></p><p></p></body></html>",
+        "noQuirksBodyHtml": "<script><div></script><title>&lt;p&gt;</title><p></p><p></p>"
+      }
+    },
+    {
+      "data": "<!--><div>--<!-->",
+      "errors": [
+        "(1,5): incorrect-comment",
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,17): incorrect-comment",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": ""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "--"
+                      },
+                      {
+                        "comment": ""
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!----><html><head></head><body><div>--<!----></div></body></html>",
+        "noQuirksBodyHtml": "<!----><div>--<!----></div>"
+      }
+    },
+    {
+      "data": "<p><hr></p>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "hr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "hr"
+                  },
+                  {
+                    "tag": "p"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p></p><hr><p></p></body></html>",
+        "noQuirksBodyHtml": "<p></p><hr><p></p>"
+      }
+    },
+    {
+      "data": "<select><b><option><select><option></b></select>X",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-start-tag-in-select",
+        "(1,27): unexpected-select-in-select",
+        "(1,39): unexpected-end-tag",
+        "(1,48): unexpected-end-tag",
+        "(1,49): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "option",
+                    "children": [
+                      {
+                        "text": "X"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select><option></option></select><option>X</option></body></html>",
+        "noQuirksBodyHtml": "<select><option></option></select><option>X</option>"
+      }
+    },
+    {
+      "data": "<a><table><td><a><table></table><a></tr><a></table><b>X</b>C<a>Y",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,14): unexpected-cell-in-table-body",
+        "(1,35): unexpected-start-tag-implies-end-tag",
+        "(1,40): unexpected-cell-end-tag",
+        "(1,43): unexpected-start-tag-implies-table-voodoo",
+        "(1,43): unexpected-start-tag-implies-end-tag",
+        "(1,43): unexpected-end-tag",
+        "(1,63): unexpected-start-tag-implies-end-tag",
+        "(1,64): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "b": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "tag": "a"
+                      },
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "tag": "tbody",
+                            "children": [
+                              {
+                                "tag": "tr",
+                                "children": [
+                                  {
+                                    "tag": "td",
+                                    "children": [
+                                      {
+                                        "tag": "a",
+                                        "children": [
+                                          {
+                                            "tag": "table"
+                                          }
+                                        ]
+                                      },
+                                      {
+                                        "tag": "a"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "X"
+                          }
+                        ]
+                      },
+                      {
+                        "text": "C"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "text": "Y"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a><a></a><table><tbody><tr><td><a><table></table></a><a></a></td></tr></tbody></table></a><a><b>X</b>C</a><a>Y</a></body></html>",
+        "noQuirksBodyHtml": "<a><a></a><table><tbody><tr><td><a><table></table></a><a></a></td></tr></tbody></table></a><a><b>X</b>C</a><a>Y</a>"
+      }
+    },
+    {
+      "data": "<a X>0<b>1<a Y>2",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,15): unexpected-start-tag-implies-end-tag",
+        "(1,15): adoption-agency-1.3",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "b": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "attrs": [
+                      {
+                        "name": "x",
+                        "value": ""
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "0"
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "1"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "attrs": [
+                          {
+                            "name": "y",
+                            "value": ""
+                          }
+                        ],
+                        "children": [
+                          {
+                            "text": "2"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a x=\"\">0<b>1</b></a><b><a y=\"\">2</a></b></body></html>",
+        "noQuirksBodyHtml": "<a x=\"\">0<b>1</b></a><b><a y=\"\">2</a></b>"
+      }
+    },
+    {
+      "data": "<!-----><font><div>hello<table>excite!<b>me!<th><i>please!</tr><!--X-->",
+      "errors": [
+        "(1,7): unexpected-dash-after-double-dash-in-comment",
+        "(1,14): expected-doctype-but-got-start-tag",
+        "(1,41): unexpected-start-tag-implies-table-voodoo",
+        "(1,48): foster-parenting-character-in-table",
+        "(1,48): foster-parenting-character-in-table",
+        "(1,48): foster-parenting-character-in-table",
+        "(1,48): foster-parenting-character-in-table",
+        "(1,48): foster-parenting-character-in-table",
+        "(1,48): foster-parenting-character-in-table",
+        "(1,48): foster-parenting-character-in-table",
+        "(1,48): foster-parenting-character-in-table",
+        "(1,48): foster-parenting-character-in-table",
+        "(1,48): foster-parenting-character-in-table",
+        "(1,48): unexpected-cell-in-table-body",
+        "(1,63): unexpected-cell-end-tag",
+        "(1,71): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "font": true,
+            "div": true,
+            "b": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "th": true,
+            "i": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "-"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "font",
+                    "children": [
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "text": "helloexcite!"
+                          },
+                          {
+                            "tag": "b",
+                            "children": [
+                              {
+                                "text": "me!"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "table",
+                            "children": [
+                              {
+                                "tag": "tbody",
+                                "children": [
+                                  {
+                                    "tag": "tr",
+                                    "children": [
+                                      {
+                                        "tag": "th",
+                                        "children": [
+                                          {
+                                            "tag": "i",
+                                            "children": [
+                                              {
+                                                "text": "please!"
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  },
+                                  {
+                                    "comment": "X"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!-----><html><head></head><body><font><div>helloexcite!<b>me!</b><table><tbody><tr><th><i>please!</i></th></tr><!--X--></tbody></table></div></font></body></html>",
+        "noQuirksBodyHtml": "<!-----><font><div>helloexcite!<b>me!</b><table><tbody><tr><th><i>please!</i></th></tr><!--X--></tbody></table></div></font>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><li>hello<li>world<ul>how<li>do</ul>you</body><!--do-->",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "li": true,
+            "ul": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "li",
+                    "children": [
+                      {
+                        "text": "hello"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "li",
+                    "children": [
+                      {
+                        "text": "world"
+                      },
+                      {
+                        "tag": "ul",
+                        "children": [
+                          {
+                            "text": "how"
+                          },
+                          {
+                            "tag": "li",
+                            "children": [
+                              {
+                                "text": "do"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "text": "you"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "comment": "do"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><li>hello</li><li>world<ul>how<li>do</li></ul>you</li></body><!--do--></html>",
+        "noQuirksBodyHtml": "<li>hello</li><li>world<ul>how<li>do</li></ul>you<!--do--></li>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>A<option>B<optgroup>C<select>D</option>E",
+      "errors": [
+        "(1,54): unexpected-end-tag-in-select",
+        "(1,55): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "option": true,
+            "optgroup": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "A"
+                  },
+                  {
+                    "tag": "option",
+                    "children": [
+                      {
+                        "text": "B"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "optgroup",
+                    "children": [
+                      {
+                        "text": "C"
+                      },
+                      {
+                        "tag": "select",
+                        "children": [
+                          {
+                            "text": "DE"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>A<option>B</option><optgroup>C<select>DE</select></optgroup></body></html>",
+        "noQuirksBodyHtml": "A<option>B</option><optgroup>C<select>DE</select></optgroup>"
+      }
+    },
+    {
+      "data": "<",
+      "errors": [
+        "(1,1): expected-tag-name",
+        "(1,1): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "<",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>&lt;</body></html>",
+        "noQuirksBodyHtml": "&lt;"
+      }
+    },
+    {
+      "data": "<#",
+      "errors": [
+        "(1,1): expected-tag-name",
+        "(1,1): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "<#",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>&lt;#</body></html>",
+        "noQuirksBodyHtml": "&lt;#"
+      }
+    },
+    {
+      "data": "</",
+      "errors": [
+        "(1,2): expected-closing-tag-but-got-eof",
+        "(1,2): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "</",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>&lt;/</body></html>",
+        "noQuirksBodyHtml": "&lt;/"
+      }
+    },
+    {
+      "data": "</#",
+      "errors": [
+        "(1,2): expected-closing-tag-but-got-char",
+        "(1,3): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "#"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!--#--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--#-->"
+      }
+    },
+    {
+      "data": "<?",
+      "errors": [
+        "(1,1): expected-tag-name-but-got-question-mark",
+        "(1,2): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "?"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!--?--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--?-->"
+      }
+    },
+    {
+      "data": "<?#",
+      "errors": [
+        "(1,1): expected-tag-name-but-got-question-mark",
+        "(1,3): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "?#"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!--?#--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--?#-->"
+      }
+    },
+    {
+      "data": "<!",
+      "errors": [
+        "(1,2): expected-dashes-or-doctype",
+        "(1,2): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": ""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!----><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!---->"
+      }
+    },
+    {
+      "data": "<!#",
+      "errors": [
+        "(1,2): expected-dashes-or-doctype",
+        "(1,3): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "#"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!--#--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--#-->"
+      }
+    },
+    {
+      "data": "<?COMMENT?>",
+      "errors": [
+        "(1,1): expected-tag-name-but-got-question-mark",
+        "(1,11): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "?COMMENT?"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!--?COMMENT?--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--?COMMENT?-->"
+      }
+    },
+    {
+      "data": "<!COMMENT>",
+      "errors": [
+        "(1,2): expected-dashes-or-doctype",
+        "(1,10): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "COMMENT"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!--COMMENT--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--COMMENT-->"
+      }
+    },
+    {
+      "data": "</ COMMENT >",
+      "errors": [
+        "(1,2): expected-closing-tag-but-got-char",
+        "(1,12): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": " COMMENT "
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!-- COMMENT --><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!-- COMMENT -->"
+      }
+    },
+    {
+      "data": "<?COM--MENT?>",
+      "errors": [
+        "(1,1): expected-tag-name-but-got-question-mark",
+        "(1,13): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "?COM--MENT?"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!--?COM--MENT?--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--?COM--MENT?-->"
+      }
+    },
+    {
+      "data": "<!COM--MENT>",
+      "errors": [
+        "(1,2): expected-dashes-or-doctype",
+        "(1,12): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "COM--MENT"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!--COM--MENT--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--COM--MENT-->"
+      }
+    },
+    {
+      "data": "</ COM--MENT >",
+      "errors": [
+        "(1,2): expected-closing-tag-but-got-char",
+        "(1,14): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": " COM--MENT "
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!-- COM--MENT --><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!-- COM--MENT -->"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><style> EOF",
+      "errors": [
+        "(1,26): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": " EOF",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><style> EOF</style></head><body></body></html>",
+        "noQuirksBodyHtml": "<style> EOF</style>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><script> <!-- </script> --> </script> EOF",
+      "errors": [
+        "(1,52): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": " <!-- ",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": " "
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->  EOF",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script> <!-- </script> </head><body>--&gt;  EOF</body></html>",
+        "noQuirksBodyHtml": "<script> <!-- </script> --&gt;  EOF"
+      }
+    },
+    {
+      "data": "<b><p></b>TEST",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,10): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "b"
+                      },
+                      {
+                        "text": "TEST"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b></b><p><b></b>TEST</p></body></html>",
+        "noQuirksBodyHtml": "<b></b><p><b></b>TEST</p>"
+      }
+    },
+    {
+      "data": "<p id=a><b><p id=b></b>TEST",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,19): unexpected-end-tag",
+        "(1,23): adoption-agency-1.2"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "b": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "attrs": [
+                      {
+                        "name": "id",
+                        "value": "a"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "attrs": [
+                      {
+                        "name": "id",
+                        "value": "b"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "TEST"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p id=\"a\"><b></b></p><p id=\"b\">TEST</p></body></html>",
+        "noQuirksBodyHtml": "<p id=\"a\"><b></b></p><p id=\"b\">TEST</p>"
+      }
+    },
+    {
+      "data": "<b id=a><p><b id=b></p></b>TEST",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,23): unexpected-end-tag",
+        "(1,27): adoption-agency-1.2",
+        "(1,31): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "attrs": [
+                      {
+                        "name": "id",
+                        "value": "a"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "tag": "b",
+                            "attrs": [
+                              {
+                                "name": "id",
+                                "value": "b"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "text": "TEST"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b id=\"a\"><p><b id=\"b\"></b></p>TEST</b></body></html>",
+        "noQuirksBodyHtml": "<b id=\"a\"><p><b id=\"b\"></b></p>TEST</b>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><title>U-test</title><body><div><p>Test<u></p></div></body>",
+      "errors": [
+        "(1,61): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true,
+            "div": true,
+            "p": true,
+            "u": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "U-test"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "text": "Test"
+                          },
+                          {
+                            "tag": "u"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><title>U-test</title></head><body><div><p>Test<u></u></p></div></body></html>",
+        "noQuirksBodyHtml": "<title>U-test</title><div><p>Test<u></u></p></div>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><font><table></font></table></font>",
+      "errors": [
+        "(1,35): unexpected-end-tag-implies-table-voodoo",
+        "(1,35): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "font": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "font",
+                    "children": [
+                      {
+                        "tag": "table"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><font><table></table></font></body></html>",
+        "noQuirksBodyHtml": "<font><table></table></font>"
+      }
+    },
+    {
+      "data": "<font><p>hello<b>cruel</font>world",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,29): adoption-agency-1.3",
+        "(1,29): adoption-agency-1.3",
+        "(1,34): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "font": true,
+            "p": true,
+            "b": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "font"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "children": [
+                          {
+                            "text": "hello"
+                          },
+                          {
+                            "tag": "b",
+                            "children": [
+                              {
+                                "text": "cruel"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "world"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><font></font><p><font>hello<b>cruel</b></font><b>world</b></p></body></html>",
+        "noQuirksBodyHtml": "<font></font><p><font>hello<b>cruel</b></font><b>world</b></p>"
+      }
+    },
+    {
+      "data": "<b>Test</i>Test",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-end-tag",
+        "(1,15): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "TestTest"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b>TestTest</b></body></html>",
+        "noQuirksBodyHtml": "<b>TestTest</b>"
+      }
+    },
+    {
+      "data": "<b>A<cite>B<div>C",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "cite": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "A"
+                      },
+                      {
+                        "tag": "cite",
+                        "children": [
+                          {
+                            "text": "B"
+                          },
+                          {
+                            "tag": "div",
+                            "children": [
+                              {
+                                "text": "C"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b>A<cite>B<div>C</div></cite></b></body></html>",
+        "noQuirksBodyHtml": "<b>A<cite>B<div>C</div></cite></b>"
+      }
+    },
+    {
+      "data": "<b>A<cite>B<div>C</cite>D",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,24): unexpected-end-tag",
+        "(1,25): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "cite": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "A"
+                      },
+                      {
+                        "tag": "cite",
+                        "children": [
+                          {
+                            "text": "B"
+                          },
+                          {
+                            "tag": "div",
+                            "children": [
+                              {
+                                "text": "CD"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b>A<cite>B<div>CD</div></cite></b></body></html>",
+        "noQuirksBodyHtml": "<b>A<cite>B<div>CD</div></cite></b>"
+      }
+    },
+    {
+      "data": "<b>A<cite>B<div>C</b>D",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,21): adoption-agency-1.3",
+        "(1,22): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "cite": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "A"
+                      },
+                      {
+                        "tag": "cite",
+                        "children": [
+                          {
+                            "text": "B"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "C"
+                          }
+                        ]
+                      },
+                      {
+                        "text": "D"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b>A<cite>B</cite></b><div><b>C</b>D</div></body></html>",
+        "noQuirksBodyHtml": "<b>A<cite>B</cite></b><div><b>C</b>D</div>"
+      }
+    },
+    {
+      "data": "",
+      "errors": [
+        "(1,0): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<DIV>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,5): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div></div></body></html>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<DIV> abc",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,9): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": " abc"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div> abc</div></body></html>",
+        "noQuirksBodyHtml": "<div> abc</div>"
+      }
+    },
+    {
+      "data": "<DIV> abc <B>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,13): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "b": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": " abc "
+                      },
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div> abc <b></b></div></body></html>",
+        "noQuirksBodyHtml": "<div> abc <b></b></div>"
+      }
+    },
+    {
+      "data": "<DIV> abc <B> def",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "b": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": " abc "
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": " def"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div> abc <b> def</b></div></body></html>",
+        "noQuirksBodyHtml": "<div> abc <b> def</b></div>"
+      }
+    },
+    {
+      "data": "<DIV> abc <B> def <I>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,21): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "b": true,
+            "i": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": " abc "
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": " def "
+                          },
+                          {
+                            "tag": "i"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div> abc <b> def <i></i></b></div></body></html>",
+        "noQuirksBodyHtml": "<div> abc <b> def <i></i></b></div>"
+      }
+    },
+    {
+      "data": "<DIV> abc <B> def <I> ghi",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,25): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "b": true,
+            "i": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": " abc "
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": " def "
+                          },
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": " ghi"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div> abc <b> def <i> ghi</i></b></div></body></html>",
+        "noQuirksBodyHtml": "<div> abc <b> def <i> ghi</i></b></div>"
+      }
+    },
+    {
+      "data": "<DIV> abc <B> def <I> ghi <P>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,29): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "b": true,
+            "i": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": " abc "
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": " def "
+                          },
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": " ghi "
+                              },
+                              {
+                                "tag": "p"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div> abc <b> def <i> ghi <p></p></i></b></div></body></html>",
+        "noQuirksBodyHtml": "<div> abc <b> def <i> ghi <p></p></i></b></div>"
+      }
+    },
+    {
+      "data": "<DIV> abc <B> def <I> ghi <P> jkl",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,33): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "b": true,
+            "i": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": " abc "
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": " def "
+                          },
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": " ghi "
+                              },
+                              {
+                                "tag": "p",
+                                "children": [
+                                  {
+                                    "text": " jkl"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div> abc <b> def <i> ghi <p> jkl</p></i></b></div></body></html>",
+        "noQuirksBodyHtml": "<div> abc <b> def <i> ghi <p> jkl</p></i></b></div>"
+      }
+    },
+    {
+      "data": "<DIV> abc <B> def <I> ghi <P> jkl </B>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,38): adoption-agency-1.3",
+        "(1,38): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "b": true,
+            "i": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": " abc "
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": " def "
+                          },
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": " ghi "
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "i",
+                        "children": [
+                          {
+                            "tag": "p",
+                            "children": [
+                              {
+                                "tag": "b",
+                                "children": [
+                                  {
+                                    "text": " jkl "
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div> abc <b> def <i> ghi </i></b><i><p><b> jkl </b></p></i></div></body></html>",
+        "noQuirksBodyHtml": "<div> abc <b> def <i> ghi </i></b><i><p><b> jkl </b></p></i></div>"
+      }
+    },
+    {
+      "data": "<DIV> abc <B> def <I> ghi <P> jkl </B> mno",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,38): adoption-agency-1.3",
+        "(1,42): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "b": true,
+            "i": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": " abc "
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": " def "
+                          },
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": " ghi "
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "i",
+                        "children": [
+                          {
+                            "tag": "p",
+                            "children": [
+                              {
+                                "tag": "b",
+                                "children": [
+                                  {
+                                    "text": " jkl "
+                                  }
+                                ]
+                              },
+                              {
+                                "text": " mno"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div> abc <b> def <i> ghi </i></b><i><p><b> jkl </b> mno</p></i></div></body></html>",
+        "noQuirksBodyHtml": "<div> abc <b> def <i> ghi </i></b><i><p><b> jkl </b> mno</p></i></div>"
+      }
+    },
+    {
+      "data": "<DIV> abc <B> def <I> ghi <P> jkl </B> mno </I>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,38): adoption-agency-1.3",
+        "(1,47): adoption-agency-1.3",
+        "(1,47): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "b": true,
+            "i": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": " abc "
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": " def "
+                          },
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": " ghi "
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "i"
+                      },
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "tag": "b",
+                                "children": [
+                                  {
+                                    "text": " jkl "
+                                  }
+                                ]
+                              },
+                              {
+                                "text": " mno "
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div> abc <b> def <i> ghi </i></b><i></i><p><i><b> jkl </b> mno </i></p></div></body></html>",
+        "noQuirksBodyHtml": "<div> abc <b> def <i> ghi </i></b><i></i><p><i><b> jkl </b> mno </i></p></div>"
+      }
+    },
+    {
+      "data": "<DIV> abc <B> def <I> ghi <P> jkl </B> mno </I> pqr",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,38): adoption-agency-1.3",
+        "(1,47): adoption-agency-1.3",
+        "(1,51): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "b": true,
+            "i": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": " abc "
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": " def "
+                          },
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": " ghi "
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "i"
+                      },
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "tag": "b",
+                                "children": [
+                                  {
+                                    "text": " jkl "
+                                  }
+                                ]
+                              },
+                              {
+                                "text": " mno "
+                              }
+                            ]
+                          },
+                          {
+                            "text": " pqr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div> abc <b> def <i> ghi </i></b><i></i><p><i><b> jkl </b> mno </i> pqr</p></div></body></html>",
+        "noQuirksBodyHtml": "<div> abc <b> def <i> ghi </i></b><i></i><p><i><b> jkl </b> mno </i> pqr</p></div>"
+      }
+    },
+    {
+      "data": "<DIV> abc <B> def <I> ghi <P> jkl </B> mno </I> pqr </P>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,38): adoption-agency-1.3",
+        "(1,47): adoption-agency-1.3",
+        "(1,56): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "b": true,
+            "i": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": " abc "
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": " def "
+                          },
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": " ghi "
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "i"
+                      },
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "tag": "b",
+                                "children": [
+                                  {
+                                    "text": " jkl "
+                                  }
+                                ]
+                              },
+                              {
+                                "text": " mno "
+                              }
+                            ]
+                          },
+                          {
+                            "text": " pqr "
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div> abc <b> def <i> ghi </i></b><i></i><p><i><b> jkl </b> mno </i> pqr </p></div></body></html>",
+        "noQuirksBodyHtml": "<div> abc <b> def <i> ghi </i></b><i></i><p><i><b> jkl </b> mno </i> pqr </p></div>"
+      }
+    },
+    {
+      "data": "<DIV> abc <B> def <I> ghi <P> jkl </B> mno </I> pqr </P> stu",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,38): adoption-agency-1.3",
+        "(1,47): adoption-agency-1.3",
+        "(1,60): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "b": true,
+            "i": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": " abc "
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": " def "
+                          },
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": " ghi "
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "i"
+                      },
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "tag": "b",
+                                "children": [
+                                  {
+                                    "text": " jkl "
+                                  }
+                                ]
+                              },
+                              {
+                                "text": " mno "
+                              }
+                            ]
+                          },
+                          {
+                            "text": " pqr "
+                          }
+                        ]
+                      },
+                      {
+                        "text": " stu"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div> abc <b> def <i> ghi </i></b><i></i><p><i><b> jkl </b> mno </i> pqr </p> stu</div></body></html>",
+        "noQuirksBodyHtml": "<div> abc <b> def <i> ghi </i></b><i></i><p><i><b> jkl </b> mno </i> pqr </p> stu</div>"
+      }
+    },
+    {
+      "data": "<test attribute---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->",
+      "errors": [
+        "(1,1040): expected-doctype-but-got-start-tag",
+        "(1,1040): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "test": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "test",
+                    "attrs": [
+                      {
+                        "name": "attribute----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------",
+                        "value": ""
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><test attribute----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------=\"\"></test></body></html>",
+        "noQuirksBodyHtml": "<test attribute----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------=\"\"></test>"
+      }
+    },
+    {
+      "data": "<a href=\"blah\">aba<table><a href=\"foo\">br<tr><td></td></tr>x</table>aoe",
+      "errors": [
+        "(1,15): expected-doctype-but-got-start-tag",
+        "(1,39): unexpected-start-tag-implies-table-voodoo",
+        "(1,39): unexpected-start-tag-implies-end-tag",
+        "(1,39): unexpected-end-tag",
+        "(1,45): foster-parenting-character-in-table",
+        "(1,45): foster-parenting-character-in-table",
+        "(1,68): foster-parenting-character-in-table",
+        "(1,71): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "attrs": [
+                      {
+                        "name": "href",
+                        "value": "blah"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "aba"
+                      },
+                      {
+                        "tag": "a",
+                        "attrs": [
+                          {
+                            "name": "href",
+                            "value": "foo"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "text": "br"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "a",
+                        "attrs": [
+                          {
+                            "name": "href",
+                            "value": "foo"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "text": "x"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "tag": "tbody",
+                            "children": [
+                              {
+                                "tag": "tr",
+                                "children": [
+                                  {
+                                    "tag": "td"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "a",
+                    "attrs": [
+                      {
+                        "name": "href",
+                        "value": "foo"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "aoe"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a href=\"blah\">aba<a href=\"foo\">br</a><a href=\"foo\">x</a><table><tbody><tr><td></td></tr></tbody></table></a><a href=\"foo\">aoe</a></body></html>",
+        "noQuirksBodyHtml": "<a href=\"blah\">aba<a href=\"foo\">br</a><a href=\"foo\">x</a><table><tbody><tr><td></td></tr></tbody></table></a><a href=\"foo\">aoe</a>"
+      }
+    },
+    {
+      "data": "<a href=\"blah\">aba<table><tr><td><a href=\"foo\">br</td></tr>x</table>aoe",
+      "errors": [
+        "(1,15): expected-doctype-but-got-start-tag",
+        "(1,54): unexpected-cell-end-tag",
+        "(1,68): unexpected text in table",
+        "(1,71): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "attrs": [
+                      {
+                        "name": "href",
+                        "value": "blah"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "abax"
+                      },
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "tag": "tbody",
+                            "children": [
+                              {
+                                "tag": "tr",
+                                "children": [
+                                  {
+                                    "tag": "td",
+                                    "children": [
+                                      {
+                                        "tag": "a",
+                                        "attrs": [
+                                          {
+                                            "name": "href",
+                                            "value": "foo"
+                                          }
+                                        ],
+                                        "children": [
+                                          {
+                                            "text": "br"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "text": "aoe"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a href=\"blah\">abax<table><tbody><tr><td><a href=\"foo\">br</a></td></tr></tbody></table>aoe</a></body></html>",
+        "noQuirksBodyHtml": "<a href=\"blah\">abax<table><tbody><tr><td><a href=\"foo\">br</a></td></tr></tbody></table>aoe</a>"
+      }
+    },
+    {
+      "data": "<table><a href=\"blah\">aba<tr><td><a href=\"foo\">br</td></tr>x</table>aoe",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,22): unexpected-start-tag-implies-table-voodoo",
+        "(1,29): foster-parenting-character-in-table",
+        "(1,29): foster-parenting-character-in-table",
+        "(1,29): foster-parenting-character-in-table",
+        "(1,54): unexpected-cell-end-tag",
+        "(1,68): foster-parenting-character-in-table",
+        "(1,71): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "attrs": [
+                      {
+                        "name": "href",
+                        "value": "blah"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "aba"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "a",
+                    "attrs": [
+                      {
+                        "name": "href",
+                        "value": "blah"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "x"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "a",
+                                    "attrs": [
+                                      {
+                                        "name": "href",
+                                        "value": "foo"
+                                      }
+                                    ],
+                                    "children": [
+                                      {
+                                        "text": "br"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "a",
+                    "attrs": [
+                      {
+                        "name": "href",
+                        "value": "blah"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "aoe"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a href=\"blah\">aba</a><a href=\"blah\">x</a><table><tbody><tr><td><a href=\"foo\">br</a></td></tr></tbody></table><a href=\"blah\">aoe</a></body></html>",
+        "noQuirksBodyHtml": "<a href=\"blah\">aba</a><a href=\"blah\">x</a><table><tbody><tr><td><a href=\"foo\">br</a></td></tr></tbody></table><a href=\"blah\">aoe</a>"
+      }
+    },
+    {
+      "data": "<a href=a>aa<marquee>aa<a href=b>bb</marquee>aa",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,45): end-tag-too-early",
+        "(1,47): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "marquee": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "attrs": [
+                      {
+                        "name": "href",
+                        "value": "a"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "aa"
+                      },
+                      {
+                        "tag": "marquee",
+                        "children": [
+                          {
+                            "text": "aa"
+                          },
+                          {
+                            "tag": "a",
+                            "attrs": [
+                              {
+                                "name": "href",
+                                "value": "b"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "text": "bb"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "text": "aa"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a href=\"a\">aa<marquee>aa<a href=\"b\">bb</a></marquee>aa</a></body></html>",
+        "noQuirksBodyHtml": "<a href=\"a\">aa<marquee>aa<a href=\"b\">bb</a></marquee>aa</a>"
+      }
+    },
+    {
+      "data": "<wbr><strike><code></strike><code><strike></code>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,28): adoption-agency-1.3",
+        "(1,49): adoption-agency-1.3",
+        "(1,49): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "wbr": true,
+            "strike": true,
+            "code": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "wbr"
+                  },
+                  {
+                    "tag": "strike",
+                    "children": [
+                      {
+                        "tag": "code"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "code",
+                    "children": [
+                      {
+                        "tag": "code",
+                        "children": [
+                          {
+                            "tag": "strike"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><wbr><strike><code></code></strike><code><code><strike></strike></code></code></body></html>",
+        "noQuirksBodyHtml": "<wbr><strike><code></code></strike><code><code><strike></strike></code></code>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><spacer>foo",
+      "errors": [
+        "(1,26): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "spacer": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "spacer",
+                    "children": [
+                      {
+                        "text": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><spacer>foo</spacer></body></html>",
+        "noQuirksBodyHtml": "<spacer>foo</spacer>"
+      }
+    },
+    {
+      "data": "<title><meta></title><link><title><meta></title>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "link": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "<meta>",
+                        "escaped": true
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "link"
+                  },
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "<meta>",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><title>&lt;meta&gt;</title><link><title>&lt;meta&gt;</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title>&lt;meta&gt;</title><link><title>&lt;meta&gt;</title>"
+      }
+    },
+    {
+      "data": "<style><!--</style><meta><script>--><link></script>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "meta": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "meta"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "--><link>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style><!--</style><meta><script>--><link></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<style><!--</style><meta><script>--><link></script>"
+      }
+    },
+    {
+      "data": "<head><meta></head><link>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,25): unexpected-start-tag-out-of-my-head"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "meta": true,
+            "link": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "meta"
+                  },
+                  {
+                    "tag": "link"
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><meta><link></head><body></body></html>",
+        "noQuirksBodyHtml": "<meta><link>"
+      }
+    },
+    {
+      "data": "<table><tr><tr><td><td><span><th><span>X</table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,33): unexpected-cell-end-tag",
+        "(1,48): unexpected-cell-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "span": true,
+            "th": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          },
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              },
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "span"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "th",
+                                "children": [
+                                  {
+                                    "tag": "span",
+                                    "children": [
+                                      {
+                                        "text": "X"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr></tr><tr><td></td><td><span></span></td><th><span>X</span></th></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr></tr><tr><td></td><td><span></span></td><th><span>X</span></th></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<body><body><base><link><meta><title><p></title><body><p></body>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,12): unexpected-start-tag",
+        "(1,54): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "base": true,
+            "link": true,
+            "meta": true,
+            "title": true,
+            "p": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "base"
+                  },
+                  {
+                    "tag": "link"
+                  },
+                  {
+                    "tag": "meta"
+                  },
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "<p>",
+                        "escaped": true
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><base><link><meta><title>&lt;p&gt;</title><p></p></body></html>",
+        "noQuirksBodyHtml": "<base><link><meta><title>&lt;p&gt;</title><p></p>"
+      }
+    },
+    {
+      "data": "<textarea><p></textarea>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": "<p>",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><textarea>&lt;p&gt;</textarea></body></html>",
+        "noQuirksBodyHtml": "<textarea>&lt;p&gt;</textarea>"
+      }
+    },
+    {
+      "data": "<p><image></p>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,10): unexpected-start-tag-treated-as"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "img": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "img"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><img></p></body></html>",
+        "noQuirksBodyHtml": "<p><img></p>"
+      }
+    },
+    {
+      "data": "<a><table><a></table><p><a><div><a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,13): unexpected-start-tag-implies-table-voodoo",
+        "(1,13): unexpected-start-tag-implies-end-tag",
+        "(1,13): adoption-agency-1.3",
+        "(1,27): unexpected-start-tag-implies-end-tag",
+        "(1,27): adoption-agency-1.2",
+        "(1,32): unexpected-end-tag",
+        "(1,35): unexpected-start-tag-implies-end-tag",
+        "(1,35): adoption-agency-1.2",
+        "(1,35): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "table": true,
+            "p": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "tag": "a"
+                      },
+                      {
+                        "tag": "table"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "a"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "a"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a><a></a><table></table></a><p><a></a></p><div><a></a></div></body></html>",
+        "noQuirksBodyHtml": "<a><a></a><table></table></a><p><a></a></p><div><a></a></div>"
+      }
+    },
+    {
+      "data": "<head></p><meta><p>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,10): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "meta": true,
+            "body": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "meta"
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><meta></head><body><p></p></body></html>",
+        "noQuirksBodyHtml": "<p></p><meta><p></p>"
+      }
+    },
+    {
+      "data": "<head></html><meta><p>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,19): expected-eof-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "meta": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "meta"
+                  },
+                  {
+                    "tag": "p"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><meta><p></p></body></html>",
+        "noQuirksBodyHtml": "<meta><p></p>"
+      }
+    },
+    {
+      "data": "<b><table><td><i></table>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,14): unexpected-cell-in-table-body",
+        "(1,25): unexpected-cell-end-tag",
+        "(1,25): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "i": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "tag": "tbody",
+                            "children": [
+                              {
+                                "tag": "tr",
+                                "children": [
+                                  {
+                                    "tag": "td",
+                                    "children": [
+                                      {
+                                        "tag": "i"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b><table><tbody><tr><td><i></i></td></tr></tbody></table></b></body></html>",
+        "noQuirksBodyHtml": "<b><table><tbody><tr><td><i></i></td></tr></tbody></table></b>"
+      }
+    },
+    {
+      "data": "<b><table><td></b><i></table>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,14): unexpected-cell-in-table-body",
+        "(1,18): unexpected-end-tag",
+        "(1,29): unexpected-cell-end-tag",
+        "(1,29): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "i": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "tag": "tbody",
+                            "children": [
+                              {
+                                "tag": "tr",
+                                "children": [
+                                  {
+                                    "tag": "td",
+                                    "children": [
+                                      {
+                                        "tag": "i"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b><table><tbody><tr><td><i></i></td></tr></tbody></table></b></body></html>",
+        "noQuirksBodyHtml": "<b><table><tbody><tr><td><i></i></td></tr></tbody></table></b>"
+      }
+    },
+    {
+      "data": "<h1><h2>",
+      "errors": [
+        "(1,4): expected-doctype-but-got-start-tag",
+        "(1,8): unexpected-start-tag",
+        "(1,8): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "h1": true,
+            "h2": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "h1"
+                  },
+                  {
+                    "tag": "h2"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><h1></h1><h2></h2></body></html>",
+        "noQuirksBodyHtml": "<h1></h1><h2></h2>"
+      }
+    },
+    {
+      "data": "<a><p><a></a></p></a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,9): unexpected-start-tag-implies-end-tag",
+        "(1,9): adoption-agency-1.3",
+        "(1,21): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "a"
+                      },
+                      {
+                        "tag": "a"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a></a><p><a></a><a></a></p></body></html>",
+        "noQuirksBodyHtml": "<a></a><p><a></a><a></a></p>"
+      }
+    },
+    {
+      "data": "<b><button></b></button></b>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,15): adoption-agency-1.3",
+        "(1,28): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "button": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b"
+                  },
+                  {
+                    "tag": "button",
+                    "children": [
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b></b><button><b></b></button></body></html>",
+        "noQuirksBodyHtml": "<b></b><button><b></b></button>"
+      }
+    },
+    {
+      "data": "<p><b><div><marquee></p></b></div>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-end-tag",
+        "(1,24): unexpected-end-tag",
+        "(1,28): unexpected-end-tag",
+        "(1,34): end-tag-too-early",
+        "(1,34): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "b": true,
+            "div": true,
+            "marquee": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "marquee",
+                            "children": [
+                              {
+                                "tag": "p"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><b></b></p><div><b><marquee><p></p></marquee></b></div></body></html>",
+        "noQuirksBodyHtml": "<p><b></b></p><div><b><marquee><p></p></marquee></b></div>"
+      }
+    },
+    {
+      "data": "<script></script></div><title></title><p><p>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,23): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "title": true,
+            "body": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script"
+                  },
+                  {
+                    "tag": "title"
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "p"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></script><title></title></head><body><p></p><p></p></body></html>",
+        "noQuirksBodyHtml": "<script></script><title></title><p></p><p></p>"
+      }
+    },
+    {
+      "data": "<p><hr></p>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "hr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "hr"
+                  },
+                  {
+                    "tag": "p"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p></p><hr><p></p></body></html>",
+        "noQuirksBodyHtml": "<p></p><hr><p></p>"
+      }
+    },
+    {
+      "data": "<select><b><option><select><option></b></select>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-start-tag-in-select",
+        "(1,27): unexpected-select-in-select",
+        "(1,39): unexpected-end-tag",
+        "(1,48): unexpected-end-tag",
+        "(1,48): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "option"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select><option></option></select><option></option></body></html>",
+        "noQuirksBodyHtml": "<select><option></option></select><option></option>"
+      }
+    },
+    {
+      "data": "<html><head><title></title><body></body></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title"
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><title></title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title></title>"
+      }
+    },
+    {
+      "data": "<a><table><td><a><table></table><a></tr><a></table><a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,14): unexpected-cell-in-table-body",
+        "(1,35): unexpected-start-tag-implies-end-tag",
+        "(1,40): unexpected-cell-end-tag",
+        "(1,43): unexpected-start-tag-implies-table-voodoo",
+        "(1,43): unexpected-start-tag-implies-end-tag",
+        "(1,43): unexpected-end-tag",
+        "(1,54): unexpected-start-tag-implies-end-tag",
+        "(1,54): adoption-agency-1.2",
+        "(1,54): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "tag": "a"
+                      },
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "tag": "tbody",
+                            "children": [
+                              {
+                                "tag": "tr",
+                                "children": [
+                                  {
+                                    "tag": "td",
+                                    "children": [
+                                      {
+                                        "tag": "a",
+                                        "children": [
+                                          {
+                                            "tag": "table"
+                                          }
+                                        ]
+                                      },
+                                      {
+                                        "tag": "a"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "a"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a><a></a><table><tbody><tr><td><a><table></table></a><a></a></td></tr></tbody></table></a><a></a></body></html>",
+        "noQuirksBodyHtml": "<a><a></a><table><tbody><tr><td><a><table></table></a><a></a></td></tr></tbody></table></a><a></a>"
+      }
+    },
+    {
+      "data": "<ul><li></li><div><li></div><li><li><div><li><address><li><b><em></b><li></ul>",
+      "errors": [
+        "(1,4): expected-doctype-but-got-start-tag",
+        "(1,45): end-tag-too-early",
+        "(1,58): end-tag-too-early",
+        "(1,69): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ul": true,
+            "li": true,
+            "div": true,
+            "address": true,
+            "b": true,
+            "em": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ul",
+                    "children": [
+                      {
+                        "tag": "li"
+                      },
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "tag": "li"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "li"
+                      },
+                      {
+                        "tag": "li",
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "li",
+                        "children": [
+                          {
+                            "tag": "address"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "li",
+                        "children": [
+                          {
+                            "tag": "b",
+                            "children": [
+                              {
+                                "tag": "em"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "li"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ul><li></li><div><li></li></div><li></li><li><div></div></li><li><address></address></li><li><b><em></em></b></li><li></li></ul></body></html>",
+        "noQuirksBodyHtml": "<ul><li></li><div><li></li></div><li></li><li><div></div></li><li><address></address></li><li><b><em></em></b></li><li></li></ul>"
+      }
+    },
+    {
+      "data": "<ul><li><ul></li><li>a</li></ul></li></ul>",
+      "errors": [
+        "(1,4): expected-doctype-but-got-start-tag",
+        "(1,17): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ul": true,
+            "li": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ul",
+                    "children": [
+                      {
+                        "tag": "li",
+                        "children": [
+                          {
+                            "tag": "ul",
+                            "children": [
+                              {
+                                "tag": "li",
+                                "children": [
+                                  {
+                                    "text": "a"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ul><li><ul><li>a</li></ul></li></ul></body></html>",
+        "noQuirksBodyHtml": "<ul><li><ul><li>a</li></ul></li></ul>"
+      }
+    },
+    {
+      "data": "<frameset><frame><frameset><frame></frameset><noframes></noframes></frameset>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "frame": true,
+            "noframes": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset",
+                "children": [
+                  {
+                    "tag": "frame"
+                  },
+                  {
+                    "tag": "frameset",
+                    "children": [
+                      {
+                        "tag": "frame"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "noframes"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset><frame><frameset><frame></frameset><noframes></noframes></frameset></html>",
+        "noQuirksBodyHtml": "<noframes></noframes>"
+      }
+    },
+    {
+      "data": "<h1><table><td><h3></table><h3></h1>",
+      "errors": [
+        "(1,4): expected-doctype-but-got-start-tag",
+        "(1,15): unexpected-cell-in-table-body",
+        "(1,27): unexpected-cell-end-tag",
+        "(1,31): unexpected-start-tag",
+        "(1,36): end-tag-too-early"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "h1": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "h3": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "h1",
+                    "children": [
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "tag": "tbody",
+                            "children": [
+                              {
+                                "tag": "tr",
+                                "children": [
+                                  {
+                                    "tag": "td",
+                                    "children": [
+                                      {
+                                        "tag": "h3"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "h3"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><h1><table><tbody><tr><td><h3></h3></td></tr></tbody></table></h1><h3></h3></body></html>",
+        "noQuirksBodyHtml": "<h1><table><tbody><tr><td><h3></h3></td></tr></tbody></table></h1><h3></h3>"
+      }
+    },
+    {
+      "data": "<table><colgroup><col><colgroup><col><col><col><colgroup><col><col><thead><tr><td></table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "colgroup": true,
+            "col": true,
+            "thead": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup",
+                        "children": [
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "colgroup",
+                        "children": [
+                          {
+                            "tag": "col"
+                          },
+                          {
+                            "tag": "col"
+                          },
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "colgroup",
+                        "children": [
+                          {
+                            "tag": "col"
+                          },
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "thead",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><colgroup><col></colgroup><colgroup><col><col><col></colgroup><colgroup><col><col></colgroup><thead><tr><td></td></tr></thead></table></body></html>",
+        "noQuirksBodyHtml": "<table><colgroup><col></colgroup><colgroup><col><col><col></colgroup><colgroup><col><col></colgroup><thead><tr><td></td></tr></thead></table>"
+      }
+    },
+    {
+      "data": "<table><col><tbody><col><tr><col><td><col></table><col>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,37): unexpected-cell-in-table-body",
+        "(1,55): unexpected-start-tag-ignored"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "colgroup": true,
+            "col": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup",
+                        "children": [
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "tbody"
+                      },
+                      {
+                        "tag": "colgroup",
+                        "children": [
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "colgroup",
+                        "children": [
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "colgroup",
+                        "children": [
+                          {
+                            "tag": "col"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><colgroup><col></colgroup><tbody></tbody><colgroup><col></colgroup><tbody><tr></tr></tbody><colgroup><col></colgroup><tbody><tr><td></td></tr></tbody><colgroup><col></colgroup></table></body></html>",
+        "noQuirksBodyHtml": "<table><colgroup><col></colgroup><tbody></tbody><colgroup><col></colgroup><tbody><tr></tr></tbody><colgroup><col></colgroup><tbody><tr><td></td></tr></tbody><colgroup><col></colgroup></table>"
+      }
+    },
+    {
+      "data": "<table><colgroup><tbody><colgroup><tr><colgroup><td><colgroup></table><colgroup>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,52): unexpected-cell-in-table-body",
+        "(1,80): unexpected-start-tag-ignored"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "colgroup": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup"
+                      },
+                      {
+                        "tag": "tbody"
+                      },
+                      {
+                        "tag": "colgroup"
+                      },
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "colgroup"
+                      },
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "colgroup"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><colgroup></colgroup><tbody></tbody><colgroup></colgroup><tbody><tr></tr></tbody><colgroup></colgroup><tbody><tr><td></td></tr></tbody><colgroup></colgroup></table></body></html>",
+        "noQuirksBodyHtml": "<table><colgroup></colgroup><tbody></tbody><colgroup></colgroup><tbody><tr></tr></tbody><colgroup></colgroup><tbody><tr><td></td></tr></tbody><colgroup></colgroup></table>"
+      }
+    },
+    {
+      "data": "</strong></b></em></i></u></strike></s></blink></tt></pre></big></small></font></select></h1></h2></h3></h4></h5></h6></body></br></a></img></title></span></style></script></table></th></td></tr></frame></area></link></param></hr></input></col></base></meta></basefont></bgsound></embed></spacer></p></dd></dt></caption></colgroup></tbody></tfoot></thead></address></blockquote></center></dir></div></dl></fieldset></listing></menu></ol></ul></li></nobr></wbr></form></button></marquee></object></html></frameset></head></iframe></image></isindex></noembed></noframes></noscript></optgroup></option></plaintext></textarea>",
+      "errors": [
+        "(1,9): expected-doctype-but-got-end-tag",
+        "(1,9): unexpected-end-tag-before-html",
+        "(1,13): unexpected-end-tag-before-html",
+        "(1,18): unexpected-end-tag-before-html",
+        "(1,22): unexpected-end-tag-before-html",
+        "(1,26): unexpected-end-tag-before-html",
+        "(1,35): unexpected-end-tag-before-html",
+        "(1,39): unexpected-end-tag-before-html",
+        "(1,47): unexpected-end-tag-before-html",
+        "(1,52): unexpected-end-tag-before-html",
+        "(1,58): unexpected-end-tag-before-html",
+        "(1,64): unexpected-end-tag-before-html",
+        "(1,72): unexpected-end-tag-before-html",
+        "(1,79): unexpected-end-tag-before-html",
+        "(1,88): unexpected-end-tag-before-html",
+        "(1,93): unexpected-end-tag-before-html",
+        "(1,98): unexpected-end-tag-before-html",
+        "(1,103): unexpected-end-tag-before-html",
+        "(1,108): unexpected-end-tag-before-html",
+        "(1,113): unexpected-end-tag-before-html",
+        "(1,118): unexpected-end-tag-before-html",
+        "(1,130): unexpected-end-tag-after-body",
+        "(1,130): unexpected-end-tag-treated-as",
+        "(1,134): unexpected-end-tag",
+        "(1,140): unexpected-end-tag",
+        "(1,148): unexpected-end-tag",
+        "(1,155): unexpected-end-tag",
+        "(1,163): unexpected-end-tag",
+        "(1,172): unexpected-end-tag",
+        "(1,180): unexpected-end-tag",
+        "(1,185): unexpected-end-tag",
+        "(1,190): unexpected-end-tag",
+        "(1,195): unexpected-end-tag",
+        "(1,203): unexpected-end-tag",
+        "(1,210): unexpected-end-tag",
+        "(1,217): unexpected-end-tag",
+        "(1,225): unexpected-end-tag",
+        "(1,230): unexpected-end-tag",
+        "(1,238): unexpected-end-tag",
+        "(1,244): unexpected-end-tag",
+        "(1,251): unexpected-end-tag",
+        "(1,258): unexpected-end-tag",
+        "(1,269): unexpected-end-tag",
+        "(1,279): unexpected-end-tag",
+        "(1,287): unexpected-end-tag",
+        "(1,296): unexpected-end-tag",
+        "(1,300): unexpected-end-tag",
+        "(1,305): unexpected-end-tag",
+        "(1,310): unexpected-end-tag",
+        "(1,320): unexpected-end-tag",
+        "(1,331): unexpected-end-tag",
+        "(1,339): unexpected-end-tag",
+        "(1,347): unexpected-end-tag",
+        "(1,355): unexpected-end-tag",
+        "(1,365): end-tag-too-early",
+        "(1,378): end-tag-too-early",
+        "(1,387): end-tag-too-early",
+        "(1,393): end-tag-too-early",
+        "(1,399): end-tag-too-early",
+        "(1,404): end-tag-too-early",
+        "(1,415): end-tag-too-early",
+        "(1,425): end-tag-too-early",
+        "(1,432): end-tag-too-early",
+        "(1,437): end-tag-too-early",
+        "(1,442): end-tag-too-early",
+        "(1,447): unexpected-end-tag",
+        "(1,454): unexpected-end-tag",
+        "(1,460): unexpected-end-tag",
+        "(1,467): unexpected-end-tag",
+        "(1,476): end-tag-too-early",
+        "(1,486): end-tag-too-early",
+        "(1,495): end-tag-too-early",
+        "(1,513): expected-eof-but-got-end-tag",
+        "(1,513): unexpected-end-tag",
+        "(1,520): unexpected-end-tag",
+        "(1,529): unexpected-end-tag",
+        "(1,537): unexpected-end-tag",
+        "(1,547): unexpected-end-tag",
+        "(1,557): unexpected-end-tag",
+        "(1,568): unexpected-end-tag",
+        "(1,579): unexpected-end-tag",
+        "(1,590): unexpected-end-tag",
+        "(1,599): unexpected-end-tag",
+        "(1,611): unexpected-end-tag",
+        "(1,622): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "br": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "br"
+                  },
+                  {
+                    "tag": "p"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><br><p></p></body></html>",
+        "noQuirksBodyHtml": "<br><p></p>"
+      }
+    },
+    {
+      "data": "<table><tr></strong></b></em></i></u></strike></s></blink></tt></pre></big></small></font></select></h1></h2></h3></h4></h5></h6></body></br></a></img></title></span></style></script></table></th></td></tr></frame></area></link></param></hr></input></col></base></meta></basefont></bgsound></embed></spacer></p></dd></dt></caption></colgroup></tbody></tfoot></thead></address></blockquote></center></dir></div></dl></fieldset></listing></menu></ol></ul></li></nobr></wbr></form></button></marquee></object></html></frameset></head></iframe></image></isindex></noembed></noframes></noscript></optgroup></option></plaintext></textarea>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,20): unexpected-end-tag-implies-table-voodoo",
+        "(1,20): unexpected-end-tag",
+        "(1,24): unexpected-end-tag-implies-table-voodoo",
+        "(1,24): unexpected-end-tag",
+        "(1,29): unexpected-end-tag-implies-table-voodoo",
+        "(1,29): unexpected-end-tag",
+        "(1,33): unexpected-end-tag-implies-table-voodoo",
+        "(1,33): unexpected-end-tag",
+        "(1,37): unexpected-end-tag-implies-table-voodoo",
+        "(1,37): unexpected-end-tag",
+        "(1,46): unexpected-end-tag-implies-table-voodoo",
+        "(1,46): unexpected-end-tag",
+        "(1,50): unexpected-end-tag-implies-table-voodoo",
+        "(1,50): unexpected-end-tag",
+        "(1,58): unexpected-end-tag-implies-table-voodoo",
+        "(1,58): unexpected-end-tag",
+        "(1,63): unexpected-end-tag-implies-table-voodoo",
+        "(1,63): unexpected-end-tag",
+        "(1,69): unexpected-end-tag-implies-table-voodoo",
+        "(1,69): end-tag-too-early",
+        "(1,75): unexpected-end-tag-implies-table-voodoo",
+        "(1,75): unexpected-end-tag",
+        "(1,83): unexpected-end-tag-implies-table-voodoo",
+        "(1,83): unexpected-end-tag",
+        "(1,90): unexpected-end-tag-implies-table-voodoo",
+        "(1,90): unexpected-end-tag",
+        "(1,99): unexpected-end-tag-implies-table-voodoo",
+        "(1,99): unexpected-end-tag",
+        "(1,104): unexpected-end-tag-implies-table-voodoo",
+        "(1,104): end-tag-too-early",
+        "(1,109): unexpected-end-tag-implies-table-voodoo",
+        "(1,109): end-tag-too-early",
+        "(1,114): unexpected-end-tag-implies-table-voodoo",
+        "(1,114): end-tag-too-early",
+        "(1,119): unexpected-end-tag-implies-table-voodoo",
+        "(1,119): end-tag-too-early",
+        "(1,124): unexpected-end-tag-implies-table-voodoo",
+        "(1,124): end-tag-too-early",
+        "(1,129): unexpected-end-tag-implies-table-voodoo",
+        "(1,129): end-tag-too-early",
+        "(1,136): unexpected-end-tag-in-table-row",
+        "(1,141): unexpected-end-tag-implies-table-voodoo",
+        "(1,141): unexpected-end-tag-treated-as",
+        "(1,145): unexpected-end-tag-implies-table-voodoo",
+        "(1,145): unexpected-end-tag",
+        "(1,151): unexpected-end-tag-implies-table-voodoo",
+        "(1,151): unexpected-end-tag",
+        "(1,159): unexpected-end-tag-implies-table-voodoo",
+        "(1,159): unexpected-end-tag",
+        "(1,166): unexpected-end-tag-implies-table-voodoo",
+        "(1,166): unexpected-end-tag",
+        "(1,174): unexpected-end-tag-implies-table-voodoo",
+        "(1,174): unexpected-end-tag",
+        "(1,183): unexpected-end-tag-implies-table-voodoo",
+        "(1,183): unexpected-end-tag",
+        "(1,196): unexpected-end-tag",
+        "(1,201): unexpected-end-tag",
+        "(1,206): unexpected-end-tag",
+        "(1,214): unexpected-end-tag",
+        "(1,221): unexpected-end-tag",
+        "(1,228): unexpected-end-tag",
+        "(1,236): unexpected-end-tag",
+        "(1,241): unexpected-end-tag",
+        "(1,249): unexpected-end-tag",
+        "(1,255): unexpected-end-tag",
+        "(1,262): unexpected-end-tag",
+        "(1,269): unexpected-end-tag",
+        "(1,280): unexpected-end-tag",
+        "(1,290): unexpected-end-tag",
+        "(1,298): unexpected-end-tag",
+        "(1,307): unexpected-end-tag",
+        "(1,311): unexpected-end-tag",
+        "(1,316): unexpected-end-tag",
+        "(1,321): unexpected-end-tag",
+        "(1,331): unexpected-end-tag",
+        "(1,342): unexpected-end-tag",
+        "(1,350): unexpected-end-tag",
+        "(1,358): unexpected-end-tag",
+        "(1,366): unexpected-end-tag",
+        "(1,376): end-tag-too-early",
+        "(1,389): end-tag-too-early",
+        "(1,398): end-tag-too-early",
+        "(1,404): end-tag-too-early",
+        "(1,410): end-tag-too-early",
+        "(1,415): end-tag-too-early",
+        "(1,426): end-tag-too-early",
+        "(1,436): end-tag-too-early",
+        "(1,443): end-tag-too-early",
+        "(1,448): end-tag-too-early",
+        "(1,453): end-tag-too-early",
+        "(1,458): unexpected-end-tag",
+        "(1,465): unexpected-end-tag",
+        "(1,471): unexpected-end-tag",
+        "(1,478): unexpected-end-tag",
+        "(1,487): end-tag-too-early",
+        "(1,497): end-tag-too-early",
+        "(1,506): end-tag-too-early",
+        "(1,524): expected-eof-but-got-end-tag",
+        "(1,524): unexpected-end-tag",
+        "(1,531): unexpected-end-tag",
+        "(1,540): unexpected-end-tag",
+        "(1,548): unexpected-end-tag",
+        "(1,558): unexpected-end-tag",
+        "(1,568): unexpected-end-tag",
+        "(1,579): unexpected-end-tag",
+        "(1,590): unexpected-end-tag",
+        "(1,601): unexpected-end-tag",
+        "(1,610): unexpected-end-tag",
+        "(1,622): unexpected-end-tag",
+        "(1,633): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "br": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "br"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><br><table><tbody><tr></tr></tbody></table><p></p></body></html>",
+        "noQuirksBodyHtml": "<br><table><tbody><tr></tr></tbody></table><p></p>"
+      }
+    },
+    {
+      "data": "<frameset>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,10): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": ""
+      }
+    }
+  ],
+  "tests10.dat": [
+    {
+      "data": "<!DOCTYPE html><svg></svg>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg></svg></body></html>",
+        "noQuirksBodyHtml": "<svg></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><svg></svg><![CDATA[a]]>",
+      "errors": [
+        "(1,28) expected-dashes-or-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  },
+                  {
+                    "comment": "[CDATA[a]]"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg></svg><!--[CDATA[a]]--></body></html>",
+        "noQuirksBodyHtml": "<svg></svg><!--[CDATA[a]]-->"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><svg></svg>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg></svg></body></html>",
+        "noQuirksBodyHtml": "<svg></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><select><svg></svg></select>",
+      "errors": [
+        "(1,34) unexpected-start-tag-in-select",
+        "(1,40) unexpected-end-tag-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select></body></html>",
+        "noQuirksBodyHtml": "<select></select>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><select><option><svg></svg></option></select>",
+      "errors": [
+        "(1,42) unexpected-start-tag-in-select",
+        "(1,48) unexpected-end-tag-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select><option></option></select></body></html>",
+        "noQuirksBodyHtml": "<select><option></option></select>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><svg></svg></table>",
+      "errors": [
+        "(1,33) foster-parenting-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg></svg><table></table></body></html>",
+        "noQuirksBodyHtml": "<svg></svg><table></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><svg><g>foo</g></svg></table>",
+      "errors": [
+        "(1,33) foster-parenting-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg g": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><g>foo</g></svg><table></table></body></html>",
+        "noQuirksBodyHtml": "<svg><g>foo</g></svg><table></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><svg><g>foo</g><g>bar</g></svg></table>",
+      "errors": [
+        "(1,33) foster-parenting-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg g": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><g>foo</g><g>bar</g></svg><table></table></body></html>",
+        "noQuirksBodyHtml": "<svg><g>foo</g><g>bar</g></svg><table></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><tbody><svg><g>foo</g><g>bar</g></svg></tbody></table>",
+      "errors": [
+        "(1,40) foster-parenting-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg g": true,
+            "table": true,
+            "tbody": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><g>foo</g><g>bar</g></svg><table><tbody></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<svg><g>foo</g><g>bar</g></svg><table><tbody></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><tbody><tr><svg><g>foo</g><g>bar</g></svg></tr></tbody></table>",
+      "errors": [
+        "(1,44) foster-parenting-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg g": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><g>foo</g><g>bar</g></svg><table><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<svg><g>foo</g><g>bar</g></svg><table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><tbody><tr><td><svg><g>foo</g><g>bar</g></svg></td></tr></tbody></table>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "svg svg": true,
+            "svg g": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "svg",
+                                    "ns": "http://www.w3.org/2000/svg",
+                                    "children": [
+                                      {
+                                        "tag": "g",
+                                        "ns": "http://www.w3.org/2000/svg",
+                                        "children": [
+                                          {
+                                            "text": "foo"
+                                          }
+                                        ]
+                                      },
+                                      {
+                                        "tag": "g",
+                                        "ns": "http://www.w3.org/2000/svg",
+                                        "children": [
+                                          {
+                                            "text": "bar"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><svg><g>foo</g><g>bar</g></svg></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><svg><g>foo</g><g>bar</g></svg></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><tbody><tr><td><svg><g>foo</g><g>bar</g></svg><p>baz</td></tr></tbody></table>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "svg svg": true,
+            "svg g": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "svg",
+                                    "ns": "http://www.w3.org/2000/svg",
+                                    "children": [
+                                      {
+                                        "tag": "g",
+                                        "ns": "http://www.w3.org/2000/svg",
+                                        "children": [
+                                          {
+                                            "text": "foo"
+                                          }
+                                        ]
+                                      },
+                                      {
+                                        "tag": "g",
+                                        "ns": "http://www.w3.org/2000/svg",
+                                        "children": [
+                                          {
+                                            "text": "bar"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  },
+                                  {
+                                    "tag": "p",
+                                    "children": [
+                                      {
+                                        "text": "baz"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><svg><g>foo</g><g>bar</g></svg><p>baz</p></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><svg><g>foo</g><g>bar</g></svg><p>baz</p></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><caption><svg><g>foo</g><g>bar</g></svg><p>baz</caption></table>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "svg svg": true,
+            "svg g": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "g",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "text": "foo"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "g",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "text": "bar"
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "p",
+                            "children": [
+                              {
+                                "text": "baz"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><caption><svg><g>foo</g><g>bar</g></svg><p>baz</p></caption></table></body></html>",
+        "noQuirksBodyHtml": "<table><caption><svg><g>foo</g><g>bar</g></svg><p>baz</p></caption></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><caption><svg><g>foo</g><g>bar</g><p>baz</table><p>quux",
+      "errors": [
+        "(1,65) unexpected-html-element-in-foreign-content"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "svg svg": true,
+            "svg g": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "g",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "text": "foo"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "g",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "text": "bar"
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "p",
+                            "children": [
+                              {
+                                "text": "baz"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "quux"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><caption><svg><g>foo</g><g>bar</g></svg><p>baz</p></caption></table><p>quux</p></body></html>",
+        "noQuirksBodyHtml": "<table><caption><svg><g>foo</g><g>bar</g><p>baz</p></svg></caption></table><p>quux</p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><caption><svg><g>foo</g><g>bar</g>baz</table><p>quux",
+      "errors": [
+        "(1,73) unexpected-end-tag",
+        "(1,73) expected-one-end-tag-but-got-another"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "svg svg": true,
+            "svg g": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "g",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "text": "foo"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "g",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "text": "bar"
+                                  }
+                                ]
+                              },
+                              {
+                                "text": "baz"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "quux"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><caption><svg><g>foo</g><g>bar</g>baz</svg></caption></table><p>quux</p></body></html>",
+        "noQuirksBodyHtml": "<table><caption><svg><g>foo</g><g>bar</g>baz</svg></caption></table><p>quux</p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><colgroup><svg><g>foo</g><g>bar</g><p>baz</table><p>quux",
+      "errors": [
+        "(1,43) foster-parenting-start-tag svg",
+        "(1,66) unexpected HTML-like start tag token in foreign content",
+        "(1,66) foster-parenting-start-tag",
+        "(1,67) foster-parenting-character",
+        "(1,68) foster-parenting-character",
+        "(1,69) foster-parenting-character"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg g": true,
+            "p": true,
+            "table": true,
+            "colgroup": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "baz"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "quux"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><g>foo</g><g>bar</g></svg><p>baz</p><table><colgroup></colgroup></table><p>quux</p></body></html>",
+        "noQuirksBodyHtml": "<svg><g>foo</g><g>bar</g><p>baz</p></svg><table><colgroup></colgroup></table><p>quux</p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><tr><td><select><svg><g>foo</g><g>bar</g><p>baz</table><p>quux",
+      "errors": [
+        "(1,49) unexpected-start-tag-in-select",
+        "(1,52) unexpected-start-tag-in-select",
+        "(1,59) unexpected-end-tag-in-select",
+        "(1,62) unexpected-start-tag-in-select",
+        "(1,69) unexpected-end-tag-in-select",
+        "(1,72) unexpected-start-tag-in-select",
+        "(1,83) unexpected-table-element-end-tag-in-select-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "select": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "select",
+                                    "children": [
+                                      {
+                                        "text": "foobarbaz"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "quux"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><select>foobarbaz</select></td></tr></tbody></table><p>quux</p></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><select>foobarbaz</select></td></tr></tbody></table><p>quux</p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><select><svg><g>foo</g><g>bar</g><p>baz</table><p>quux",
+      "errors": [
+        "(1,36) unexpected-start-tag-implies-table-voodoo",
+        "(1,41) unexpected-start-tag-in-select",
+        "(1,44) unexpected-start-tag-in-select",
+        "(1,51) unexpected-end-tag-in-select",
+        "(1,54) unexpected-start-tag-in-select",
+        "(1,61) unexpected-end-tag-in-select",
+        "(1,64) unexpected-start-tag-in-select",
+        "(1,75) unexpected-table-element-end-tag-in-select-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "table": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "text": "foobarbaz"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "quux"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select>foobarbaz</select><table></table><p>quux</p></body></html>",
+        "noQuirksBodyHtml": "<select>foobarbaz</select><table></table><p>quux</p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body></body></html><svg><g>foo</g><g>bar</g><p>baz",
+      "errors": [
+        "(1,40) expected-eof-but-got-start-tag",
+        "(1,63) unexpected-html-element-in-foreign-content"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg g": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "baz"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><g>foo</g><g>bar</g></svg><p>baz</p></body></html>",
+        "noQuirksBodyHtml": "<svg><g>foo</g><g>bar</g><p>baz</p></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body></body><svg><g>foo</g><g>bar</g><p>baz",
+      "errors": [
+        "(1,33) unexpected-start-tag-after-body",
+        "(1,56) unexpected-html-element-in-foreign-content"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg g": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "baz"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><g>foo</g><g>bar</g></svg><p>baz</p></body></html>",
+        "noQuirksBodyHtml": "<svg><g>foo</g><g>bar</g><p>baz</p></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><frameset><svg><g></g><g></g><p><span>",
+      "errors": [
+        "(1,30) unexpected-start-tag-in-frameset",
+        "(1,33) unexpected-start-tag-in-frameset",
+        "(1,37) unexpected-end-tag-in-frameset",
+        "(1,40) unexpected-start-tag-in-frameset",
+        "(1,44) unexpected-end-tag-in-frameset",
+        "(1,47) unexpected-start-tag-in-frameset",
+        "(1,53) unexpected-start-tag-in-frameset",
+        "(1,53) eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<svg><g></g><g></g><p><span></span></p></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><frameset></frameset><svg><g></g><g></g><p><span>",
+      "errors": [
+        "(1,41) unexpected-start-tag-after-frameset",
+        "(1,44) unexpected-start-tag-after-frameset",
+        "(1,48) unexpected-end-tag-after-frameset",
+        "(1,51) unexpected-start-tag-after-frameset",
+        "(1,55) unexpected-end-tag-after-frameset",
+        "(1,58) unexpected-start-tag-after-frameset",
+        "(1,64) unexpected-start-tag-after-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<svg><g></g><g></g><p><span></span></p></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body xlink:href=foo><svg xlink:href=foo></svg>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "attrs": [
+                  {
+                    "name": "xlink:href",
+                    "value": "foo"
+                  }
+                ],
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "attrs": [
+                      {
+                        "name": "href",
+                        "ns": "http://www.w3.org/1999/xlink",
+                        "value": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body xlink:href=\"foo\"><svg xlink:href=\"foo\"></svg></body></html>",
+        "noQuirksBodyHtml": "<svg xlink:href=\"foo\"></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body xlink:href=foo xml:lang=en><svg><g xml:lang=en xlink:href=foo></g></svg>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg g": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "attrs": [
+                  {
+                    "name": "xlink:href",
+                    "value": "foo"
+                  },
+                  {
+                    "name": "xml:lang",
+                    "value": "en"
+                  }
+                ],
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "attrs": [
+                          {
+                            "name": "href",
+                            "ns": "http://www.w3.org/1999/xlink",
+                            "value": "foo"
+                          },
+                          {
+                            "name": "lang",
+                            "ns": "http://www.w3.org/XML/1998/namespace",
+                            "value": "en"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body xlink:href=\"foo\" xml:lang=\"en\"><svg><g xml:lang=\"en\" xlink:href=\"foo\"></g></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><g xml:lang=\"en\" xlink:href=\"foo\"></g></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body xlink:href=foo xml:lang=en><svg><g xml:lang=en xlink:href=foo /></svg>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg g": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "attrs": [
+                  {
+                    "name": "xlink:href",
+                    "value": "foo"
+                  },
+                  {
+                    "name": "xml:lang",
+                    "value": "en"
+                  }
+                ],
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "attrs": [
+                          {
+                            "name": "href",
+                            "ns": "http://www.w3.org/1999/xlink",
+                            "value": "foo"
+                          },
+                          {
+                            "name": "lang",
+                            "ns": "http://www.w3.org/XML/1998/namespace",
+                            "value": "en"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body xlink:href=\"foo\" xml:lang=\"en\"><svg><g xml:lang=\"en\" xlink:href=\"foo\"></g></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><g xml:lang=\"en\" xlink:href=\"foo\"></g></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body xlink:href=foo xml:lang=en><svg><g xml:lang=en xlink:href=foo />bar</svg>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg g": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "attrs": [
+                  {
+                    "name": "xlink:href",
+                    "value": "foo"
+                  },
+                  {
+                    "name": "xml:lang",
+                    "value": "en"
+                  }
+                ],
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "g",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "attrs": [
+                          {
+                            "name": "href",
+                            "ns": "http://www.w3.org/1999/xlink",
+                            "value": "foo"
+                          },
+                          {
+                            "name": "lang",
+                            "ns": "http://www.w3.org/XML/1998/namespace",
+                            "value": "en"
+                          }
+                        ]
+                      },
+                      {
+                        "text": "bar"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body xlink:href=\"foo\" xml:lang=\"en\"><svg><g xml:lang=\"en\" xlink:href=\"foo\"></g>bar</svg></body></html>",
+        "noQuirksBodyHtml": "<svg><g xml:lang=\"en\" xlink:href=\"foo\"></g>bar</svg>"
+      }
+    },
+    {
+      "data": "<svg></path>",
+      "errors": [
+        "(1,5) expected-doctype-but-got-start-tag",
+        "(1,12) unexpected-end-tag",
+        "(1,12) unexpected-end-tag",
+        "(1,12) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg></svg></body></html>",
+        "noQuirksBodyHtml": "<svg></svg>"
+      }
+    },
+    {
+      "data": "<div><svg></div>a",
+      "errors": [
+        "(1,5) expected-doctype-but-got-start-tag",
+        "(1,16) unexpected-end-tag",
+        "(1,16) end-tag-too-early"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "svg",
+                        "ns": "http://www.w3.org/2000/svg"
+                      }
+                    ]
+                  },
+                  {
+                    "text": "a"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><svg></svg></div>a</body></html>",
+        "noQuirksBodyHtml": "<div><svg></svg></div>a"
+      }
+    },
+    {
+      "data": "<div><svg><path></div>a",
+      "errors": [
+        "(1,5) expected-doctype-but-got-start-tag",
+        "(1,22) unexpected-end-tag",
+        "(1,22) end-tag-too-early"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "svg svg": true,
+            "svg path": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "svg",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "path",
+                            "ns": "http://www.w3.org/2000/svg"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "text": "a"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><svg><path></path></svg></div>a</body></html>",
+        "noQuirksBodyHtml": "<div><svg><path></path></svg></div>a"
+      }
+    },
+    {
+      "data": "<div><svg><path></svg><path>",
+      "errors": [
+        "(1,5) expected-doctype-but-got-start-tag",
+        "(1,22) unexpected-end-tag",
+        "(1,28) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "svg svg": true,
+            "svg path": true,
+            "path": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "svg",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "path",
+                            "ns": "http://www.w3.org/2000/svg"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "path"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><svg><path></path></svg><path></path></div></body></html>",
+        "noQuirksBodyHtml": "<div><svg><path></path></svg><path></path></div>"
+      }
+    },
+    {
+      "data": "<div><svg><path><foreignObject><math></div>a",
+      "errors": [
+        "(1,5) expected-doctype-but-got-start-tag",
+        "(1,43) unexpected-end-tag",
+        "(1,43) end-tag-too-early",
+        "(1,44) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "svg svg": true,
+            "svg path": true,
+            "svg foreignObject": true,
+            "math math": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "svg",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "path",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "foreignObject",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "tag": "math",
+                                    "ns": "http://www.w3.org/1998/Math/MathML",
+                                    "children": [
+                                      {
+                                        "text": "a"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><svg><path><foreignObject><math>a</math></foreignObject></path></svg></div></body></html>",
+        "noQuirksBodyHtml": "<div><svg><path><foreignObject><math>a</math></foreignObject></path></svg></div>"
+      }
+    },
+    {
+      "data": "<div><svg><path><foreignObject><p></div>a",
+      "errors": [
+        "(1,5) expected-doctype-but-got-start-tag",
+        "(1,40) end-tag-too-early",
+        "(1,41) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "svg svg": true,
+            "svg path": true,
+            "svg foreignObject": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "svg",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "path",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "foreignObject",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "tag": "p",
+                                    "children": [
+                                      {
+                                        "text": "a"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><svg><path><foreignObject><p>a</p></foreignObject></path></svg></div></body></html>",
+        "noQuirksBodyHtml": "<div><svg><path><foreignObject><p>a</p></foreignObject></path></svg></div>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><svg><desc><div><svg><ul>a",
+      "errors": [
+        "(1,40) unexpected-html-element-in-foreign-content",
+        "(1,41) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg desc": true,
+            "div": true,
+            "ul": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "desc",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "div",
+                            "children": [
+                              {
+                                "tag": "svg",
+                                "ns": "http://www.w3.org/2000/svg"
+                              },
+                              {
+                                "tag": "ul",
+                                "children": [
+                                  {
+                                    "text": "a"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><desc><div><svg></svg><ul>a</ul></div></desc></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><desc><div><svg><ul>a</ul></svg></div></desc></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><svg><desc><svg><ul>a",
+      "errors": [
+        "(1,35) unexpected-html-element-in-foreign-content",
+        "(1,36) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg desc": true,
+            "ul": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "desc",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg"
+                          },
+                          {
+                            "tag": "ul",
+                            "children": [
+                              {
+                                "text": "a"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><desc><svg></svg><ul>a</ul></desc></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><desc><svg><ul>a</ul></svg></desc></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><p><svg><desc><p>",
+      "errors": [
+        "(1,32) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "svg svg": true,
+            "svg desc": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "svg",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "desc",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "p"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><svg><desc><p></p></desc></svg></p></body></html>",
+        "noQuirksBodyHtml": "<p><svg><desc><p></p></desc></svg></p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><p><svg><title><p>",
+      "errors": [
+        "(1,33) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "svg svg": true,
+            "svg title": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "svg",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "title",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "p"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><svg><title><p></p></title></svg></p></body></html>",
+        "noQuirksBodyHtml": "<p><svg><title><p></p></title></svg></p>"
+      }
+    },
+    {
+      "data": "<div><svg><path><foreignObject><p></foreignObject><p>",
+      "errors": [
+        "(1,5) expected-doctype-but-got-start-tag",
+        "(1,50) unexpected-end-tag",
+        "(1,53) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "svg svg": true,
+            "svg path": true,
+            "svg foreignObject": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "svg",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "path",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "foreignObject",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "tag": "p"
+                                  },
+                                  {
+                                    "tag": "p"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><svg><path><foreignObject><p></p><p></p></foreignObject></path></svg></div></body></html>",
+        "noQuirksBodyHtml": "<div><svg><path><foreignObject><p></p><p></p></foreignObject></path></svg></div>"
+      }
+    },
+    {
+      "data": "<math><mi><div><object><div><span></span></div></object></div></mi><mi>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,71) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true,
+            "div": true,
+            "object": true,
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "div",
+                            "children": [
+                              {
+                                "tag": "object",
+                                "children": [
+                                  {
+                                    "tag": "div",
+                                    "children": [
+                                      {
+                                        "tag": "span"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><mi><div><object><div><span></span></div></object></div></mi><mi></mi></math></body></html>",
+        "noQuirksBodyHtml": "<math><mi><div><object><div><span></span></div></object></div></mi><mi></mi></math>"
+      }
+    },
+    {
+      "data": "<math><mi><svg><foreignObject><div><div></div></div></foreignObject></svg></mi><mi>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,83) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true,
+            "svg svg": true,
+            "svg foreignObject": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "foreignObject",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "tag": "div",
+                                    "children": [
+                                      {
+                                        "tag": "div"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><mi><svg><foreignObject><div><div></div></div></foreignObject></svg></mi><mi></mi></math></body></html>",
+        "noQuirksBodyHtml": "<math><mi><svg><foreignObject><div><div></div></div></foreignObject></svg></mi><mi></mi></math>"
+      }
+    },
+    {
+      "data": "<svg><script></script><path>",
+      "errors": [
+        "(1,5) expected-doctype-but-got-start-tag",
+        "(1,28) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg script": true,
+            "svg path": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "script",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "path",
+                        "ns": "http://www.w3.org/2000/svg"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg><script></script><path></path></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><script></script><path></path></svg>"
+      }
+    },
+    {
+      "data": "<table><svg></svg><tr>",
+      "errors": [
+        "(1,7) expected-doctype-but-got-start-tag",
+        "(1,12) unexpected-start-tag-implies-table-voodoo",
+        "(1,22) eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg></svg><table><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<svg></svg><table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<math><mi><mglyph>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,18) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true,
+            "math mglyph": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "mglyph",
+                            "ns": "http://www.w3.org/1998/Math/MathML"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><mi><mglyph></mglyph></mi></math></body></html>",
+        "noQuirksBodyHtml": "<math><mi><mglyph></mglyph></mi></math>"
+      }
+    },
+    {
+      "data": "<math><mi><malignmark>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,22) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true,
+            "math malignmark": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "malignmark",
+                            "ns": "http://www.w3.org/1998/Math/MathML"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><mi><malignmark></malignmark></mi></math></body></html>",
+        "noQuirksBodyHtml": "<math><mi><malignmark></malignmark></mi></math>"
+      }
+    },
+    {
+      "data": "<math><mo><mglyph>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,18) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mo": true,
+            "math mglyph": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mo",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "mglyph",
+                            "ns": "http://www.w3.org/1998/Math/MathML"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><mo><mglyph></mglyph></mo></math></body></html>",
+        "noQuirksBodyHtml": "<math><mo><mglyph></mglyph></mo></math>"
+      }
+    },
+    {
+      "data": "<math><mo><malignmark>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,22) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mo": true,
+            "math malignmark": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mo",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "malignmark",
+                            "ns": "http://www.w3.org/1998/Math/MathML"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><mo><malignmark></malignmark></mo></math></body></html>",
+        "noQuirksBodyHtml": "<math><mo><malignmark></malignmark></mo></math>"
+      }
+    },
+    {
+      "data": "<math><mn><mglyph>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,18) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mn": true,
+            "math mglyph": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mn",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "mglyph",
+                            "ns": "http://www.w3.org/1998/Math/MathML"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><mn><mglyph></mglyph></mn></math></body></html>",
+        "noQuirksBodyHtml": "<math><mn><mglyph></mglyph></mn></math>"
+      }
+    },
+    {
+      "data": "<math><mn><malignmark>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,22) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mn": true,
+            "math malignmark": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mn",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "malignmark",
+                            "ns": "http://www.w3.org/1998/Math/MathML"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><mn><malignmark></malignmark></mn></math></body></html>",
+        "noQuirksBodyHtml": "<math><mn><malignmark></malignmark></mn></math>"
+      }
+    },
+    {
+      "data": "<math><ms><mglyph>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,18) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math ms": true,
+            "math mglyph": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "ms",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "mglyph",
+                            "ns": "http://www.w3.org/1998/Math/MathML"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><ms><mglyph></mglyph></ms></math></body></html>",
+        "noQuirksBodyHtml": "<math><ms><mglyph></mglyph></ms></math>"
+      }
+    },
+    {
+      "data": "<math><ms><malignmark>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,22) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math ms": true,
+            "math malignmark": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "ms",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "malignmark",
+                            "ns": "http://www.w3.org/1998/Math/MathML"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><ms><malignmark></malignmark></ms></math></body></html>",
+        "noQuirksBodyHtml": "<math><ms><malignmark></malignmark></ms></math>"
+      }
+    },
+    {
+      "data": "<math><mtext><mglyph>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,21) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mtext": true,
+            "math mglyph": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mtext",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "mglyph",
+                            "ns": "http://www.w3.org/1998/Math/MathML"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><mtext><mglyph></mglyph></mtext></math></body></html>",
+        "noQuirksBodyHtml": "<math><mtext><mglyph></mglyph></mtext></math>"
+      }
+    },
+    {
+      "data": "<math><mtext><malignmark>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,25) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mtext": true,
+            "math malignmark": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mtext",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "malignmark",
+                            "ns": "http://www.w3.org/1998/Math/MathML"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><mtext><malignmark></malignmark></mtext></math></body></html>",
+        "noQuirksBodyHtml": "<math><mtext><malignmark></malignmark></mtext></math>"
+      }
+    },
+    {
+      "data": "<math><annotation-xml><svg></svg></annotation-xml><mi>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,54) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math annotation-xml": true,
+            "svg svg": true,
+            "math mi": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "annotation-xml",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><annotation-xml><svg></svg></annotation-xml><mi></mi></math></body></html>",
+        "noQuirksBodyHtml": "<math><annotation-xml><svg></svg></annotation-xml><mi></mi></math>"
+      }
+    },
+    {
+      "data": "<math><annotation-xml><svg><foreignObject><div><math><mi></mi></math><span></span></div></foreignObject><path></path></svg></annotation-xml><mi>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,144) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math annotation-xml": true,
+            "svg svg": true,
+            "svg foreignObject": true,
+            "div": true,
+            "math mi": true,
+            "span": true,
+            "svg path": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "annotation-xml",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "foreignObject",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "tag": "div",
+                                    "children": [
+                                      {
+                                        "tag": "math",
+                                        "ns": "http://www.w3.org/1998/Math/MathML",
+                                        "children": [
+                                          {
+                                            "tag": "mi",
+                                            "ns": "http://www.w3.org/1998/Math/MathML"
+                                          }
+                                        ]
+                                      },
+                                      {
+                                        "tag": "span"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "path",
+                                "ns": "http://www.w3.org/2000/svg"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><annotation-xml><svg><foreignObject><div><math><mi></mi></math><span></span></div></foreignObject><path></path></svg></annotation-xml><mi></mi></math></body></html>",
+        "noQuirksBodyHtml": "<math><annotation-xml><svg><foreignObject><div><math><mi></mi></math><span></span></div></foreignObject><path></path></svg></annotation-xml><mi></mi></math>"
+      }
+    },
+    {
+      "data": "<math><annotation-xml><svg><foreignObject><math><mi><svg></svg></mi><mo></mo></math><span></span></foreignObject><path></path></svg></annotation-xml><mi>",
+      "errors": [
+        "(1,6) expected-doctype-but-got-start-tag",
+        "(1,153) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math annotation-xml": true,
+            "svg svg": true,
+            "svg foreignObject": true,
+            "math mi": true,
+            "math mo": true,
+            "span": true,
+            "svg path": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "annotation-xml",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "foreignObject",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "tag": "math",
+                                    "ns": "http://www.w3.org/1998/Math/MathML",
+                                    "children": [
+                                      {
+                                        "tag": "mi",
+                                        "ns": "http://www.w3.org/1998/Math/MathML",
+                                        "children": [
+                                          {
+                                            "tag": "svg",
+                                            "ns": "http://www.w3.org/2000/svg"
+                                          }
+                                        ]
+                                      },
+                                      {
+                                        "tag": "mo",
+                                        "ns": "http://www.w3.org/1998/Math/MathML"
+                                      }
+                                    ]
+                                  },
+                                  {
+                                    "tag": "span"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "path",
+                                "ns": "http://www.w3.org/2000/svg"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><annotation-xml><svg><foreignObject><math><mi><svg></svg></mi><mo></mo></math><span></span></foreignObject><path></path></svg></annotation-xml><mi></mi></math></body></html>",
+        "noQuirksBodyHtml": "<math><annotation-xml><svg><foreignObject><math><mi><svg></svg></mi><mo></mo></math><span></span></foreignObject><path></path></svg></annotation-xml><mi></mi></math>"
+      }
+    }
+  ],
+  "tests11.dat": [
+    {
+      "data": "<!DOCTYPE html><body><svg attributeName='' attributeType='' baseFrequency='' baseProfile='' calcMode='' clipPathUnits='' diffuseConstant='' edgeMode='' filterUnits='' glyphRef='' gradientTransform='' gradientUnits='' kernelMatrix='' kernelUnitLength='' keyPoints='' keySplines='' keyTimes='' lengthAdjust='' limitingConeAngle='' markerHeight='' markerUnits='' markerWidth='' maskContentUnits='' maskUnits='' numOctaves='' pathLength='' patternContentUnits='' patternTransform='' patternUnits='' pointsAtX='' pointsAtY='' pointsAtZ='' preserveAlpha='' preserveAspectRatio='' primitiveUnits='' refX='' refY='' repeatCount='' repeatDur='' requiredExtensions='' requiredFeatures='' specularConstant='' specularExponent='' spreadMethod='' startOffset='' stdDeviation='' stitchTiles='' surfaceScale='' systemLanguage='' tableValues='' targetX='' targetY='' textLength='' viewBox='' viewTarget='' xChannelSelector='' yChannelSelector='' zoomAndPan=''></svg>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "attrs": [
+                      {
+                        "name": "attributeName",
+                        "value": ""
+                      },
+                      {
+                        "name": "attributeType",
+                        "value": ""
+                      },
+                      {
+                        "name": "baseFrequency",
+                        "value": ""
+                      },
+                      {
+                        "name": "baseProfile",
+                        "value": ""
+                      },
+                      {
+                        "name": "calcMode",
+                        "value": ""
+                      },
+                      {
+                        "name": "clipPathUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "diffuseConstant",
+                        "value": ""
+                      },
+                      {
+                        "name": "edgeMode",
+                        "value": ""
+                      },
+                      {
+                        "name": "filterUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "glyphRef",
+                        "value": ""
+                      },
+                      {
+                        "name": "gradientTransform",
+                        "value": ""
+                      },
+                      {
+                        "name": "gradientUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "kernelMatrix",
+                        "value": ""
+                      },
+                      {
+                        "name": "kernelUnitLength",
+                        "value": ""
+                      },
+                      {
+                        "name": "keyPoints",
+                        "value": ""
+                      },
+                      {
+                        "name": "keySplines",
+                        "value": ""
+                      },
+                      {
+                        "name": "keyTimes",
+                        "value": ""
+                      },
+                      {
+                        "name": "lengthAdjust",
+                        "value": ""
+                      },
+                      {
+                        "name": "limitingConeAngle",
+                        "value": ""
+                      },
+                      {
+                        "name": "markerHeight",
+                        "value": ""
+                      },
+                      {
+                        "name": "markerUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "markerWidth",
+                        "value": ""
+                      },
+                      {
+                        "name": "maskContentUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "maskUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "numOctaves",
+                        "value": ""
+                      },
+                      {
+                        "name": "pathLength",
+                        "value": ""
+                      },
+                      {
+                        "name": "patternContentUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "patternTransform",
+                        "value": ""
+                      },
+                      {
+                        "name": "patternUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "pointsAtX",
+                        "value": ""
+                      },
+                      {
+                        "name": "pointsAtY",
+                        "value": ""
+                      },
+                      {
+                        "name": "pointsAtZ",
+                        "value": ""
+                      },
+                      {
+                        "name": "preserveAlpha",
+                        "value": ""
+                      },
+                      {
+                        "name": "preserveAspectRatio",
+                        "value": ""
+                      },
+                      {
+                        "name": "primitiveUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "refX",
+                        "value": ""
+                      },
+                      {
+                        "name": "refY",
+                        "value": ""
+                      },
+                      {
+                        "name": "repeatCount",
+                        "value": ""
+                      },
+                      {
+                        "name": "repeatDur",
+                        "value": ""
+                      },
+                      {
+                        "name": "requiredExtensions",
+                        "value": ""
+                      },
+                      {
+                        "name": "requiredFeatures",
+                        "value": ""
+                      },
+                      {
+                        "name": "specularConstant",
+                        "value": ""
+                      },
+                      {
+                        "name": "specularExponent",
+                        "value": ""
+                      },
+                      {
+                        "name": "spreadMethod",
+                        "value": ""
+                      },
+                      {
+                        "name": "startOffset",
+                        "value": ""
+                      },
+                      {
+                        "name": "stdDeviation",
+                        "value": ""
+                      },
+                      {
+                        "name": "stitchTiles",
+                        "value": ""
+                      },
+                      {
+                        "name": "surfaceScale",
+                        "value": ""
+                      },
+                      {
+                        "name": "systemLanguage",
+                        "value": ""
+                      },
+                      {
+                        "name": "tableValues",
+                        "value": ""
+                      },
+                      {
+                        "name": "targetX",
+                        "value": ""
+                      },
+                      {
+                        "name": "targetY",
+                        "value": ""
+                      },
+                      {
+                        "name": "textLength",
+                        "value": ""
+                      },
+                      {
+                        "name": "viewBox",
+                        "value": ""
+                      },
+                      {
+                        "name": "viewTarget",
+                        "value": ""
+                      },
+                      {
+                        "name": "xChannelSelector",
+                        "value": ""
+                      },
+                      {
+                        "name": "yChannelSelector",
+                        "value": ""
+                      },
+                      {
+                        "name": "zoomAndPan",
+                        "value": ""
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg attributeName=\"\" attributeType=\"\" baseFrequency=\"\" baseProfile=\"\" calcMode=\"\" clipPathUnits=\"\" diffuseConstant=\"\" edgeMode=\"\" filterUnits=\"\" glyphRef=\"\" gradientTransform=\"\" gradientUnits=\"\" kernelMatrix=\"\" kernelUnitLength=\"\" keyPoints=\"\" keySplines=\"\" keyTimes=\"\" lengthAdjust=\"\" limitingConeAngle=\"\" markerHeight=\"\" markerUnits=\"\" markerWidth=\"\" maskContentUnits=\"\" maskUnits=\"\" numOctaves=\"\" pathLength=\"\" patternContentUnits=\"\" patternTransform=\"\" patternUnits=\"\" pointsAtX=\"\" pointsAtY=\"\" pointsAtZ=\"\" preserveAlpha=\"\" preserveAspectRatio=\"\" primitiveUnits=\"\" refX=\"\" refY=\"\" repeatCount=\"\" repeatDur=\"\" requiredExtensions=\"\" requiredFeatures=\"\" specularConstant=\"\" specularExponent=\"\" spreadMethod=\"\" startOffset=\"\" stdDeviation=\"\" stitchTiles=\"\" surfaceScale=\"\" systemLanguage=\"\" tableValues=\"\" targetX=\"\" targetY=\"\" textLength=\"\" viewBox=\"\" viewTarget=\"\" xChannelSelector=\"\" yChannelSelector=\"\" zoomAndPan=\"\"></svg></body></html>",
+        "noQuirksBodyHtml": "<svg attributeName=\"\" attributeType=\"\" baseFrequency=\"\" baseProfile=\"\" calcMode=\"\" clipPathUnits=\"\" diffuseConstant=\"\" edgeMode=\"\" filterUnits=\"\" glyphRef=\"\" gradientTransform=\"\" gradientUnits=\"\" kernelMatrix=\"\" kernelUnitLength=\"\" keyPoints=\"\" keySplines=\"\" keyTimes=\"\" lengthAdjust=\"\" limitingConeAngle=\"\" markerHeight=\"\" markerUnits=\"\" markerWidth=\"\" maskContentUnits=\"\" maskUnits=\"\" numOctaves=\"\" pathLength=\"\" patternContentUnits=\"\" patternTransform=\"\" patternUnits=\"\" pointsAtX=\"\" pointsAtY=\"\" pointsAtZ=\"\" preserveAlpha=\"\" preserveAspectRatio=\"\" primitiveUnits=\"\" refX=\"\" refY=\"\" repeatCount=\"\" repeatDur=\"\" requiredExtensions=\"\" requiredFeatures=\"\" specularConstant=\"\" specularExponent=\"\" spreadMethod=\"\" startOffset=\"\" stdDeviation=\"\" stitchTiles=\"\" surfaceScale=\"\" systemLanguage=\"\" tableValues=\"\" targetX=\"\" targetY=\"\" textLength=\"\" viewBox=\"\" viewTarget=\"\" xChannelSelector=\"\" yChannelSelector=\"\" zoomAndPan=\"\"></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><BODY><SVG ATTRIBUTENAME='' ATTRIBUTETYPE='' BASEFREQUENCY='' BASEPROFILE='' CALCMODE='' CLIPPATHUNITS='' DIFFUSECONSTANT='' EDGEMODE='' FILTERUNITS='' GLYPHREF='' GRADIENTTRANSFORM='' GRADIENTUNITS='' KERNELMATRIX='' KERNELUNITLENGTH='' KEYPOINTS='' KEYSPLINES='' KEYTIMES='' LENGTHADJUST='' LIMITINGCONEANGLE='' MARKERHEIGHT='' MARKERUNITS='' MARKERWIDTH='' MASKCONTENTUNITS='' MASKUNITS='' NUMOCTAVES='' PATHLENGTH='' PATTERNCONTENTUNITS='' PATTERNTRANSFORM='' PATTERNUNITS='' POINTSATX='' POINTSATY='' POINTSATZ='' PRESERVEALPHA='' PRESERVEASPECTRATIO='' PRIMITIVEUNITS='' REFX='' REFY='' REPEATCOUNT='' REPEATDUR='' REQUIREDEXTENSIONS='' REQUIREDFEATURES='' SPECULARCONSTANT='' SPECULAREXPONENT='' SPREADMETHOD='' STARTOFFSET='' STDDEVIATION='' STITCHTILES='' SURFACESCALE='' SYSTEMLANGUAGE='' TABLEVALUES='' TARGETX='' TARGETY='' TEXTLENGTH='' VIEWBOX='' VIEWTARGET='' XCHANNELSELECTOR='' YCHANNELSELECTOR='' ZOOMANDPAN=''></SVG>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "attrs": [
+                      {
+                        "name": "attributeName",
+                        "value": ""
+                      },
+                      {
+                        "name": "attributeType",
+                        "value": ""
+                      },
+                      {
+                        "name": "baseFrequency",
+                        "value": ""
+                      },
+                      {
+                        "name": "baseProfile",
+                        "value": ""
+                      },
+                      {
+                        "name": "calcMode",
+                        "value": ""
+                      },
+                      {
+                        "name": "clipPathUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "diffuseConstant",
+                        "value": ""
+                      },
+                      {
+                        "name": "edgeMode",
+                        "value": ""
+                      },
+                      {
+                        "name": "filterUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "glyphRef",
+                        "value": ""
+                      },
+                      {
+                        "name": "gradientTransform",
+                        "value": ""
+                      },
+                      {
+                        "name": "gradientUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "kernelMatrix",
+                        "value": ""
+                      },
+                      {
+                        "name": "kernelUnitLength",
+                        "value": ""
+                      },
+                      {
+                        "name": "keyPoints",
+                        "value": ""
+                      },
+                      {
+                        "name": "keySplines",
+                        "value": ""
+                      },
+                      {
+                        "name": "keyTimes",
+                        "value": ""
+                      },
+                      {
+                        "name": "lengthAdjust",
+                        "value": ""
+                      },
+                      {
+                        "name": "limitingConeAngle",
+                        "value": ""
+                      },
+                      {
+                        "name": "markerHeight",
+                        "value": ""
+                      },
+                      {
+                        "name": "markerUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "markerWidth",
+                        "value": ""
+                      },
+                      {
+                        "name": "maskContentUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "maskUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "numOctaves",
+                        "value": ""
+                      },
+                      {
+                        "name": "pathLength",
+                        "value": ""
+                      },
+                      {
+                        "name": "patternContentUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "patternTransform",
+                        "value": ""
+                      },
+                      {
+                        "name": "patternUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "pointsAtX",
+                        "value": ""
+                      },
+                      {
+                        "name": "pointsAtY",
+                        "value": ""
+                      },
+                      {
+                        "name": "pointsAtZ",
+                        "value": ""
+                      },
+                      {
+                        "name": "preserveAlpha",
+                        "value": ""
+                      },
+                      {
+                        "name": "preserveAspectRatio",
+                        "value": ""
+                      },
+                      {
+                        "name": "primitiveUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "refX",
+                        "value": ""
+                      },
+                      {
+                        "name": "refY",
+                        "value": ""
+                      },
+                      {
+                        "name": "repeatCount",
+                        "value": ""
+                      },
+                      {
+                        "name": "repeatDur",
+                        "value": ""
+                      },
+                      {
+                        "name": "requiredExtensions",
+                        "value": ""
+                      },
+                      {
+                        "name": "requiredFeatures",
+                        "value": ""
+                      },
+                      {
+                        "name": "specularConstant",
+                        "value": ""
+                      },
+                      {
+                        "name": "specularExponent",
+                        "value": ""
+                      },
+                      {
+                        "name": "spreadMethod",
+                        "value": ""
+                      },
+                      {
+                        "name": "startOffset",
+                        "value": ""
+                      },
+                      {
+                        "name": "stdDeviation",
+                        "value": ""
+                      },
+                      {
+                        "name": "stitchTiles",
+                        "value": ""
+                      },
+                      {
+                        "name": "surfaceScale",
+                        "value": ""
+                      },
+                      {
+                        "name": "systemLanguage",
+                        "value": ""
+                      },
+                      {
+                        "name": "tableValues",
+                        "value": ""
+                      },
+                      {
+                        "name": "targetX",
+                        "value": ""
+                      },
+                      {
+                        "name": "targetY",
+                        "value": ""
+                      },
+                      {
+                        "name": "textLength",
+                        "value": ""
+                      },
+                      {
+                        "name": "viewBox",
+                        "value": ""
+                      },
+                      {
+                        "name": "viewTarget",
+                        "value": ""
+                      },
+                      {
+                        "name": "xChannelSelector",
+                        "value": ""
+                      },
+                      {
+                        "name": "yChannelSelector",
+                        "value": ""
+                      },
+                      {
+                        "name": "zoomAndPan",
+                        "value": ""
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg attributeName=\"\" attributeType=\"\" baseFrequency=\"\" baseProfile=\"\" calcMode=\"\" clipPathUnits=\"\" diffuseConstant=\"\" edgeMode=\"\" filterUnits=\"\" glyphRef=\"\" gradientTransform=\"\" gradientUnits=\"\" kernelMatrix=\"\" kernelUnitLength=\"\" keyPoints=\"\" keySplines=\"\" keyTimes=\"\" lengthAdjust=\"\" limitingConeAngle=\"\" markerHeight=\"\" markerUnits=\"\" markerWidth=\"\" maskContentUnits=\"\" maskUnits=\"\" numOctaves=\"\" pathLength=\"\" patternContentUnits=\"\" patternTransform=\"\" patternUnits=\"\" pointsAtX=\"\" pointsAtY=\"\" pointsAtZ=\"\" preserveAlpha=\"\" preserveAspectRatio=\"\" primitiveUnits=\"\" refX=\"\" refY=\"\" repeatCount=\"\" repeatDur=\"\" requiredExtensions=\"\" requiredFeatures=\"\" specularConstant=\"\" specularExponent=\"\" spreadMethod=\"\" startOffset=\"\" stdDeviation=\"\" stitchTiles=\"\" surfaceScale=\"\" systemLanguage=\"\" tableValues=\"\" targetX=\"\" targetY=\"\" textLength=\"\" viewBox=\"\" viewTarget=\"\" xChannelSelector=\"\" yChannelSelector=\"\" zoomAndPan=\"\"></svg></body></html>",
+        "noQuirksBodyHtml": "<svg attributeName=\"\" attributeType=\"\" baseFrequency=\"\" baseProfile=\"\" calcMode=\"\" clipPathUnits=\"\" diffuseConstant=\"\" edgeMode=\"\" filterUnits=\"\" glyphRef=\"\" gradientTransform=\"\" gradientUnits=\"\" kernelMatrix=\"\" kernelUnitLength=\"\" keyPoints=\"\" keySplines=\"\" keyTimes=\"\" lengthAdjust=\"\" limitingConeAngle=\"\" markerHeight=\"\" markerUnits=\"\" markerWidth=\"\" maskContentUnits=\"\" maskUnits=\"\" numOctaves=\"\" pathLength=\"\" patternContentUnits=\"\" patternTransform=\"\" patternUnits=\"\" pointsAtX=\"\" pointsAtY=\"\" pointsAtZ=\"\" preserveAlpha=\"\" preserveAspectRatio=\"\" primitiveUnits=\"\" refX=\"\" refY=\"\" repeatCount=\"\" repeatDur=\"\" requiredExtensions=\"\" requiredFeatures=\"\" specularConstant=\"\" specularExponent=\"\" spreadMethod=\"\" startOffset=\"\" stdDeviation=\"\" stitchTiles=\"\" surfaceScale=\"\" systemLanguage=\"\" tableValues=\"\" targetX=\"\" targetY=\"\" textLength=\"\" viewBox=\"\" viewTarget=\"\" xChannelSelector=\"\" yChannelSelector=\"\" zoomAndPan=\"\"></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><svg attributename='' attributetype='' basefrequency='' baseprofile='' calcmode='' clippathunits='' diffuseconstant='' edgemode='' filterunits='' filterres='' glyphref='' gradienttransform='' gradientunits='' kernelmatrix='' kernelunitlength='' keypoints='' keysplines='' keytimes='' lengthadjust='' limitingconeangle='' markerheight='' markerunits='' markerwidth='' maskcontentunits='' maskunits='' numoctaves='' pathlength='' patterncontentunits='' patterntransform='' patternunits='' pointsatx='' pointsaty='' pointsatz='' preservealpha='' preserveaspectratio='' primitiveunits='' refx='' refy='' repeatcount='' repeatdur='' requiredextensions='' requiredfeatures='' specularconstant='' specularexponent='' spreadmethod='' startoffset='' stddeviation='' stitchtiles='' surfacescale='' systemlanguage='' tablevalues='' targetx='' targety='' textlength='' viewbox='' viewtarget='' xchannelselector='' ychannelselector='' zoomandpan=''></svg>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "attrs": [
+                      {
+                        "name": "attributeName",
+                        "value": ""
+                      },
+                      {
+                        "name": "attributeType",
+                        "value": ""
+                      },
+                      {
+                        "name": "baseFrequency",
+                        "value": ""
+                      },
+                      {
+                        "name": "baseProfile",
+                        "value": ""
+                      },
+                      {
+                        "name": "calcMode",
+                        "value": ""
+                      },
+                      {
+                        "name": "clipPathUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "diffuseConstant",
+                        "value": ""
+                      },
+                      {
+                        "name": "edgeMode",
+                        "value": ""
+                      },
+                      {
+                        "name": "filterUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "filterres",
+                        "value": ""
+                      },
+                      {
+                        "name": "glyphRef",
+                        "value": ""
+                      },
+                      {
+                        "name": "gradientTransform",
+                        "value": ""
+                      },
+                      {
+                        "name": "gradientUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "kernelMatrix",
+                        "value": ""
+                      },
+                      {
+                        "name": "kernelUnitLength",
+                        "value": ""
+                      },
+                      {
+                        "name": "keyPoints",
+                        "value": ""
+                      },
+                      {
+                        "name": "keySplines",
+                        "value": ""
+                      },
+                      {
+                        "name": "keyTimes",
+                        "value": ""
+                      },
+                      {
+                        "name": "lengthAdjust",
+                        "value": ""
+                      },
+                      {
+                        "name": "limitingConeAngle",
+                        "value": ""
+                      },
+                      {
+                        "name": "markerHeight",
+                        "value": ""
+                      },
+                      {
+                        "name": "markerUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "markerWidth",
+                        "value": ""
+                      },
+                      {
+                        "name": "maskContentUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "maskUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "numOctaves",
+                        "value": ""
+                      },
+                      {
+                        "name": "pathLength",
+                        "value": ""
+                      },
+                      {
+                        "name": "patternContentUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "patternTransform",
+                        "value": ""
+                      },
+                      {
+                        "name": "patternUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "pointsAtX",
+                        "value": ""
+                      },
+                      {
+                        "name": "pointsAtY",
+                        "value": ""
+                      },
+                      {
+                        "name": "pointsAtZ",
+                        "value": ""
+                      },
+                      {
+                        "name": "preserveAlpha",
+                        "value": ""
+                      },
+                      {
+                        "name": "preserveAspectRatio",
+                        "value": ""
+                      },
+                      {
+                        "name": "primitiveUnits",
+                        "value": ""
+                      },
+                      {
+                        "name": "refX",
+                        "value": ""
+                      },
+                      {
+                        "name": "refY",
+                        "value": ""
+                      },
+                      {
+                        "name": "repeatCount",
+                        "value": ""
+                      },
+                      {
+                        "name": "repeatDur",
+                        "value": ""
+                      },
+                      {
+                        "name": "requiredExtensions",
+                        "value": ""
+                      },
+                      {
+                        "name": "requiredFeatures",
+                        "value": ""
+                      },
+                      {
+                        "name": "specularConstant",
+                        "value": ""
+                      },
+                      {
+                        "name": "specularExponent",
+                        "value": ""
+                      },
+                      {
+                        "name": "spreadMethod",
+                        "value": ""
+                      },
+                      {
+                        "name": "startOffset",
+                        "value": ""
+                      },
+                      {
+                        "name": "stdDeviation",
+                        "value": ""
+                      },
+                      {
+                        "name": "stitchTiles",
+                        "value": ""
+                      },
+                      {
+                        "name": "surfaceScale",
+                        "value": ""
+                      },
+                      {
+                        "name": "systemLanguage",
+                        "value": ""
+                      },
+                      {
+                        "name": "tableValues",
+                        "value": ""
+                      },
+                      {
+                        "name": "targetX",
+                        "value": ""
+                      },
+                      {
+                        "name": "targetY",
+                        "value": ""
+                      },
+                      {
+                        "name": "textLength",
+                        "value": ""
+                      },
+                      {
+                        "name": "viewBox",
+                        "value": ""
+                      },
+                      {
+                        "name": "viewTarget",
+                        "value": ""
+                      },
+                      {
+                        "name": "xChannelSelector",
+                        "value": ""
+                      },
+                      {
+                        "name": "yChannelSelector",
+                        "value": ""
+                      },
+                      {
+                        "name": "zoomAndPan",
+                        "value": ""
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg attributeName=\"\" attributeType=\"\" baseFrequency=\"\" baseProfile=\"\" calcMode=\"\" clipPathUnits=\"\" diffuseConstant=\"\" edgeMode=\"\" filterUnits=\"\" filterres=\"\" glyphRef=\"\" gradientTransform=\"\" gradientUnits=\"\" kernelMatrix=\"\" kernelUnitLength=\"\" keyPoints=\"\" keySplines=\"\" keyTimes=\"\" lengthAdjust=\"\" limitingConeAngle=\"\" markerHeight=\"\" markerUnits=\"\" markerWidth=\"\" maskContentUnits=\"\" maskUnits=\"\" numOctaves=\"\" pathLength=\"\" patternContentUnits=\"\" patternTransform=\"\" patternUnits=\"\" pointsAtX=\"\" pointsAtY=\"\" pointsAtZ=\"\" preserveAlpha=\"\" preserveAspectRatio=\"\" primitiveUnits=\"\" refX=\"\" refY=\"\" repeatCount=\"\" repeatDur=\"\" requiredExtensions=\"\" requiredFeatures=\"\" specularConstant=\"\" specularExponent=\"\" spreadMethod=\"\" startOffset=\"\" stdDeviation=\"\" stitchTiles=\"\" surfaceScale=\"\" systemLanguage=\"\" tableValues=\"\" targetX=\"\" targetY=\"\" textLength=\"\" viewBox=\"\" viewTarget=\"\" xChannelSelector=\"\" yChannelSelector=\"\" zoomAndPan=\"\"></svg></body></html>",
+        "noQuirksBodyHtml": "<svg attributeName=\"\" attributeType=\"\" baseFrequency=\"\" baseProfile=\"\" calcMode=\"\" clipPathUnits=\"\" diffuseConstant=\"\" edgeMode=\"\" filterUnits=\"\" filterres=\"\" glyphRef=\"\" gradientTransform=\"\" gradientUnits=\"\" kernelMatrix=\"\" kernelUnitLength=\"\" keyPoints=\"\" keySplines=\"\" keyTimes=\"\" lengthAdjust=\"\" limitingConeAngle=\"\" markerHeight=\"\" markerUnits=\"\" markerWidth=\"\" maskContentUnits=\"\" maskUnits=\"\" numOctaves=\"\" pathLength=\"\" patternContentUnits=\"\" patternTransform=\"\" patternUnits=\"\" pointsAtX=\"\" pointsAtY=\"\" pointsAtZ=\"\" preserveAlpha=\"\" preserveAspectRatio=\"\" primitiveUnits=\"\" refX=\"\" refY=\"\" repeatCount=\"\" repeatDur=\"\" requiredExtensions=\"\" requiredFeatures=\"\" specularConstant=\"\" specularExponent=\"\" spreadMethod=\"\" startOffset=\"\" stdDeviation=\"\" stitchTiles=\"\" surfaceScale=\"\" systemLanguage=\"\" tableValues=\"\" targetX=\"\" targetY=\"\" textLength=\"\" viewBox=\"\" viewTarget=\"\" xChannelSelector=\"\" yChannelSelector=\"\" zoomAndPan=\"\"></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><math attributeName='' attributeType='' baseFrequency='' baseProfile='' calcMode='' clipPathUnits='' diffuseConstant='' edgeMode='' filterUnits='' glyphRef='' gradientTransform='' gradientUnits='' kernelMatrix='' kernelUnitLength='' keyPoints='' keySplines='' keyTimes='' lengthAdjust='' limitingConeAngle='' markerHeight='' markerUnits='' markerWidth='' maskContentUnits='' maskUnits='' numOctaves='' pathLength='' patternContentUnits='' patternTransform='' patternUnits='' pointsAtX='' pointsAtY='' pointsAtZ='' preserveAlpha='' preserveAspectRatio='' primitiveUnits='' refX='' refY='' repeatCount='' repeatDur='' requiredExtensions='' requiredFeatures='' specularConstant='' specularExponent='' spreadMethod='' startOffset='' stdDeviation='' stitchTiles='' surfaceScale='' systemLanguage='' tableValues='' targetX='' targetY='' textLength='' viewBox='' viewTarget='' xChannelSelector='' yChannelSelector='' zoomAndPan=''></math>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "attrs": [
+                      {
+                        "name": "attributename",
+                        "value": ""
+                      },
+                      {
+                        "name": "attributetype",
+                        "value": ""
+                      },
+                      {
+                        "name": "basefrequency",
+                        "value": ""
+                      },
+                      {
+                        "name": "baseprofile",
+                        "value": ""
+                      },
+                      {
+                        "name": "calcmode",
+                        "value": ""
+                      },
+                      {
+                        "name": "clippathunits",
+                        "value": ""
+                      },
+                      {
+                        "name": "diffuseconstant",
+                        "value": ""
+                      },
+                      {
+                        "name": "edgemode",
+                        "value": ""
+                      },
+                      {
+                        "name": "filterunits",
+                        "value": ""
+                      },
+                      {
+                        "name": "glyphref",
+                        "value": ""
+                      },
+                      {
+                        "name": "gradienttransform",
+                        "value": ""
+                      },
+                      {
+                        "name": "gradientunits",
+                        "value": ""
+                      },
+                      {
+                        "name": "kernelmatrix",
+                        "value": ""
+                      },
+                      {
+                        "name": "kernelunitlength",
+                        "value": ""
+                      },
+                      {
+                        "name": "keypoints",
+                        "value": ""
+                      },
+                      {
+                        "name": "keysplines",
+                        "value": ""
+                      },
+                      {
+                        "name": "keytimes",
+                        "value": ""
+                      },
+                      {
+                        "name": "lengthadjust",
+                        "value": ""
+                      },
+                      {
+                        "name": "limitingconeangle",
+                        "value": ""
+                      },
+                      {
+                        "name": "markerheight",
+                        "value": ""
+                      },
+                      {
+                        "name": "markerunits",
+                        "value": ""
+                      },
+                      {
+                        "name": "markerwidth",
+                        "value": ""
+                      },
+                      {
+                        "name": "maskcontentunits",
+                        "value": ""
+                      },
+                      {
+                        "name": "maskunits",
+                        "value": ""
+                      },
+                      {
+                        "name": "numoctaves",
+                        "value": ""
+                      },
+                      {
+                        "name": "pathlength",
+                        "value": ""
+                      },
+                      {
+                        "name": "patterncontentunits",
+                        "value": ""
+                      },
+                      {
+                        "name": "patterntransform",
+                        "value": ""
+                      },
+                      {
+                        "name": "patternunits",
+                        "value": ""
+                      },
+                      {
+                        "name": "pointsatx",
+                        "value": ""
+                      },
+                      {
+                        "name": "pointsaty",
+                        "value": ""
+                      },
+                      {
+                        "name": "pointsatz",
+                        "value": ""
+                      },
+                      {
+                        "name": "preservealpha",
+                        "value": ""
+                      },
+                      {
+                        "name": "preserveaspectratio",
+                        "value": ""
+                      },
+                      {
+                        "name": "primitiveunits",
+                        "value": ""
+                      },
+                      {
+                        "name": "refx",
+                        "value": ""
+                      },
+                      {
+                        "name": "refy",
+                        "value": ""
+                      },
+                      {
+                        "name": "repeatcount",
+                        "value": ""
+                      },
+                      {
+                        "name": "repeatdur",
+                        "value": ""
+                      },
+                      {
+                        "name": "requiredextensions",
+                        "value": ""
+                      },
+                      {
+                        "name": "requiredfeatures",
+                        "value": ""
+                      },
+                      {
+                        "name": "specularconstant",
+                        "value": ""
+                      },
+                      {
+                        "name": "specularexponent",
+                        "value": ""
+                      },
+                      {
+                        "name": "spreadmethod",
+                        "value": ""
+                      },
+                      {
+                        "name": "startoffset",
+                        "value": ""
+                      },
+                      {
+                        "name": "stddeviation",
+                        "value": ""
+                      },
+                      {
+                        "name": "stitchtiles",
+                        "value": ""
+                      },
+                      {
+                        "name": "surfacescale",
+                        "value": ""
+                      },
+                      {
+                        "name": "systemlanguage",
+                        "value": ""
+                      },
+                      {
+                        "name": "tablevalues",
+                        "value": ""
+                      },
+                      {
+                        "name": "targetx",
+                        "value": ""
+                      },
+                      {
+                        "name": "targety",
+                        "value": ""
+                      },
+                      {
+                        "name": "textlength",
+                        "value": ""
+                      },
+                      {
+                        "name": "viewbox",
+                        "value": ""
+                      },
+                      {
+                        "name": "viewtarget",
+                        "value": ""
+                      },
+                      {
+                        "name": "xchannelselector",
+                        "value": ""
+                      },
+                      {
+                        "name": "ychannelselector",
+                        "value": ""
+                      },
+                      {
+                        "name": "zoomandpan",
+                        "value": ""
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math attributename=\"\" attributetype=\"\" basefrequency=\"\" baseprofile=\"\" calcmode=\"\" clippathunits=\"\" diffuseconstant=\"\" edgemode=\"\" filterunits=\"\" glyphref=\"\" gradienttransform=\"\" gradientunits=\"\" kernelmatrix=\"\" kernelunitlength=\"\" keypoints=\"\" keysplines=\"\" keytimes=\"\" lengthadjust=\"\" limitingconeangle=\"\" markerheight=\"\" markerunits=\"\" markerwidth=\"\" maskcontentunits=\"\" maskunits=\"\" numoctaves=\"\" pathlength=\"\" patterncontentunits=\"\" patterntransform=\"\" patternunits=\"\" pointsatx=\"\" pointsaty=\"\" pointsatz=\"\" preservealpha=\"\" preserveaspectratio=\"\" primitiveunits=\"\" refx=\"\" refy=\"\" repeatcount=\"\" repeatdur=\"\" requiredextensions=\"\" requiredfeatures=\"\" specularconstant=\"\" specularexponent=\"\" spreadmethod=\"\" startoffset=\"\" stddeviation=\"\" stitchtiles=\"\" surfacescale=\"\" systemlanguage=\"\" tablevalues=\"\" targetx=\"\" targety=\"\" textlength=\"\" viewbox=\"\" viewtarget=\"\" xchannelselector=\"\" ychannelselector=\"\" zoomandpan=\"\"></math></body></html>",
+        "noQuirksBodyHtml": "<math attributename=\"\" attributetype=\"\" basefrequency=\"\" baseprofile=\"\" calcmode=\"\" clippathunits=\"\" diffuseconstant=\"\" edgemode=\"\" filterunits=\"\" glyphref=\"\" gradienttransform=\"\" gradientunits=\"\" kernelmatrix=\"\" kernelunitlength=\"\" keypoints=\"\" keysplines=\"\" keytimes=\"\" lengthadjust=\"\" limitingconeangle=\"\" markerheight=\"\" markerunits=\"\" markerwidth=\"\" maskcontentunits=\"\" maskunits=\"\" numoctaves=\"\" pathlength=\"\" patterncontentunits=\"\" patterntransform=\"\" patternunits=\"\" pointsatx=\"\" pointsaty=\"\" pointsatz=\"\" preservealpha=\"\" preserveaspectratio=\"\" primitiveunits=\"\" refx=\"\" refy=\"\" repeatcount=\"\" repeatdur=\"\" requiredextensions=\"\" requiredfeatures=\"\" specularconstant=\"\" specularexponent=\"\" spreadmethod=\"\" startoffset=\"\" stddeviation=\"\" stitchtiles=\"\" surfacescale=\"\" systemlanguage=\"\" tablevalues=\"\" targetx=\"\" targety=\"\" textlength=\"\" viewbox=\"\" viewtarget=\"\" xchannelselector=\"\" ychannelselector=\"\" zoomandpan=\"\"></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><svg><altGlyph /><altGlyphDef /><altGlyphItem /><animateColor /><animateMotion /><animateTransform /><clipPath /><feBlend /><feColorMatrix /><feComponentTransfer /><feComposite /><feConvolveMatrix /><feDiffuseLighting /><feDisplacementMap /><feDistantLight /><feFlood /><feFuncA /><feFuncB /><feFuncG /><feFuncR /><feGaussianBlur /><feImage /><feMerge /><feMergeNode /><feMorphology /><feOffset /><fePointLight /><feSpecularLighting /><feSpotLight /><feTile /><feTurbulence /><foreignObject /><glyphRef /><linearGradient /><radialGradient /><textPath /></svg>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg altGlyph": true,
+            "svg altGlyphDef": true,
+            "svg altGlyphItem": true,
+            "svg animateColor": true,
+            "svg animateMotion": true,
+            "svg animateTransform": true,
+            "svg clipPath": true,
+            "svg feBlend": true,
+            "svg feColorMatrix": true,
+            "svg feComponentTransfer": true,
+            "svg feComposite": true,
+            "svg feConvolveMatrix": true,
+            "svg feDiffuseLighting": true,
+            "svg feDisplacementMap": true,
+            "svg feDistantLight": true,
+            "svg feFlood": true,
+            "svg feFuncA": true,
+            "svg feFuncB": true,
+            "svg feFuncG": true,
+            "svg feFuncR": true,
+            "svg feGaussianBlur": true,
+            "svg feImage": true,
+            "svg feMerge": true,
+            "svg feMergeNode": true,
+            "svg feMorphology": true,
+            "svg feOffset": true,
+            "svg fePointLight": true,
+            "svg feSpecularLighting": true,
+            "svg feSpotLight": true,
+            "svg feTile": true,
+            "svg feTurbulence": true,
+            "svg foreignObject": true,
+            "svg glyphRef": true,
+            "svg linearGradient": true,
+            "svg radialGradient": true,
+            "svg textPath": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "altGlyph",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "altGlyphDef",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "altGlyphItem",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "animateColor",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "animateMotion",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "animateTransform",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "clipPath",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feBlend",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feColorMatrix",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feComponentTransfer",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feComposite",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feConvolveMatrix",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feDiffuseLighting",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feDisplacementMap",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feDistantLight",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFlood",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFuncA",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFuncB",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFuncG",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFuncR",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feGaussianBlur",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feImage",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feMerge",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feMergeNode",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feMorphology",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feOffset",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "fePointLight",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feSpecularLighting",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feSpotLight",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feTile",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feTurbulence",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "foreignObject",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "glyphRef",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "linearGradient",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "radialGradient",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "textPath",
+                        "ns": "http://www.w3.org/2000/svg"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><altGlyph></altGlyph><altGlyphDef></altGlyphDef><altGlyphItem></altGlyphItem><animateColor></animateColor><animateMotion></animateMotion><animateTransform></animateTransform><clipPath></clipPath><feBlend></feBlend><feColorMatrix></feColorMatrix><feComponentTransfer></feComponentTransfer><feComposite></feComposite><feConvolveMatrix></feConvolveMatrix><feDiffuseLighting></feDiffuseLighting><feDisplacementMap></feDisplacementMap><feDistantLight></feDistantLight><feFlood></feFlood><feFuncA></feFuncA><feFuncB></feFuncB><feFuncG></feFuncG><feFuncR></feFuncR><feGaussianBlur></feGaussianBlur><feImage></feImage><feMerge></feMerge><feMergeNode></feMergeNode><feMorphology></feMorphology><feOffset></feOffset><fePointLight></fePointLight><feSpecularLighting></feSpecularLighting><feSpotLight></feSpotLight><feTile></feTile><feTurbulence></feTurbulence><foreignObject></foreignObject><glyphRef></glyphRef><linearGradient></linearGradient><radialGradient></radialGradient><textPath></textPath></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><altGlyph></altGlyph><altGlyphDef></altGlyphDef><altGlyphItem></altGlyphItem><animateColor></animateColor><animateMotion></animateMotion><animateTransform></animateTransform><clipPath></clipPath><feBlend></feBlend><feColorMatrix></feColorMatrix><feComponentTransfer></feComponentTransfer><feComposite></feComposite><feConvolveMatrix></feConvolveMatrix><feDiffuseLighting></feDiffuseLighting><feDisplacementMap></feDisplacementMap><feDistantLight></feDistantLight><feFlood></feFlood><feFuncA></feFuncA><feFuncB></feFuncB><feFuncG></feFuncG><feFuncR></feFuncR><feGaussianBlur></feGaussianBlur><feImage></feImage><feMerge></feMerge><feMergeNode></feMergeNode><feMorphology></feMorphology><feOffset></feOffset><fePointLight></fePointLight><feSpecularLighting></feSpecularLighting><feSpotLight></feSpotLight><feTile></feTile><feTurbulence></feTurbulence><foreignObject></foreignObject><glyphRef></glyphRef><linearGradient></linearGradient><radialGradient></radialGradient><textPath></textPath></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><svg><altglyph /><altglyphdef /><altglyphitem /><animatecolor /><animatemotion /><animatetransform /><clippath /><feblend /><fecolormatrix /><fecomponenttransfer /><fecomposite /><feconvolvematrix /><fediffuselighting /><fedisplacementmap /><fedistantlight /><feflood /><fefunca /><fefuncb /><fefuncg /><fefuncr /><fegaussianblur /><feimage /><femerge /><femergenode /><femorphology /><feoffset /><fepointlight /><fespecularlighting /><fespotlight /><fetile /><feturbulence /><foreignobject /><glyphref /><lineargradient /><radialgradient /><textpath /></svg>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg altGlyph": true,
+            "svg altGlyphDef": true,
+            "svg altGlyphItem": true,
+            "svg animateColor": true,
+            "svg animateMotion": true,
+            "svg animateTransform": true,
+            "svg clipPath": true,
+            "svg feBlend": true,
+            "svg feColorMatrix": true,
+            "svg feComponentTransfer": true,
+            "svg feComposite": true,
+            "svg feConvolveMatrix": true,
+            "svg feDiffuseLighting": true,
+            "svg feDisplacementMap": true,
+            "svg feDistantLight": true,
+            "svg feFlood": true,
+            "svg feFuncA": true,
+            "svg feFuncB": true,
+            "svg feFuncG": true,
+            "svg feFuncR": true,
+            "svg feGaussianBlur": true,
+            "svg feImage": true,
+            "svg feMerge": true,
+            "svg feMergeNode": true,
+            "svg feMorphology": true,
+            "svg feOffset": true,
+            "svg fePointLight": true,
+            "svg feSpecularLighting": true,
+            "svg feSpotLight": true,
+            "svg feTile": true,
+            "svg feTurbulence": true,
+            "svg foreignObject": true,
+            "svg glyphRef": true,
+            "svg linearGradient": true,
+            "svg radialGradient": true,
+            "svg textPath": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "altGlyph",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "altGlyphDef",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "altGlyphItem",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "animateColor",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "animateMotion",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "animateTransform",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "clipPath",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feBlend",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feColorMatrix",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feComponentTransfer",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feComposite",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feConvolveMatrix",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feDiffuseLighting",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feDisplacementMap",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feDistantLight",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFlood",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFuncA",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFuncB",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFuncG",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFuncR",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feGaussianBlur",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feImage",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feMerge",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feMergeNode",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feMorphology",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feOffset",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "fePointLight",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feSpecularLighting",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feSpotLight",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feTile",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feTurbulence",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "foreignObject",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "glyphRef",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "linearGradient",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "radialGradient",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "textPath",
+                        "ns": "http://www.w3.org/2000/svg"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><altGlyph></altGlyph><altGlyphDef></altGlyphDef><altGlyphItem></altGlyphItem><animateColor></animateColor><animateMotion></animateMotion><animateTransform></animateTransform><clipPath></clipPath><feBlend></feBlend><feColorMatrix></feColorMatrix><feComponentTransfer></feComponentTransfer><feComposite></feComposite><feConvolveMatrix></feConvolveMatrix><feDiffuseLighting></feDiffuseLighting><feDisplacementMap></feDisplacementMap><feDistantLight></feDistantLight><feFlood></feFlood><feFuncA></feFuncA><feFuncB></feFuncB><feFuncG></feFuncG><feFuncR></feFuncR><feGaussianBlur></feGaussianBlur><feImage></feImage><feMerge></feMerge><feMergeNode></feMergeNode><feMorphology></feMorphology><feOffset></feOffset><fePointLight></fePointLight><feSpecularLighting></feSpecularLighting><feSpotLight></feSpotLight><feTile></feTile><feTurbulence></feTurbulence><foreignObject></foreignObject><glyphRef></glyphRef><linearGradient></linearGradient><radialGradient></radialGradient><textPath></textPath></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><altGlyph></altGlyph><altGlyphDef></altGlyphDef><altGlyphItem></altGlyphItem><animateColor></animateColor><animateMotion></animateMotion><animateTransform></animateTransform><clipPath></clipPath><feBlend></feBlend><feColorMatrix></feColorMatrix><feComponentTransfer></feComponentTransfer><feComposite></feComposite><feConvolveMatrix></feConvolveMatrix><feDiffuseLighting></feDiffuseLighting><feDisplacementMap></feDisplacementMap><feDistantLight></feDistantLight><feFlood></feFlood><feFuncA></feFuncA><feFuncB></feFuncB><feFuncG></feFuncG><feFuncR></feFuncR><feGaussianBlur></feGaussianBlur><feImage></feImage><feMerge></feMerge><feMergeNode></feMergeNode><feMorphology></feMorphology><feOffset></feOffset><fePointLight></fePointLight><feSpecularLighting></feSpecularLighting><feSpotLight></feSpotLight><feTile></feTile><feTurbulence></feTurbulence><foreignObject></foreignObject><glyphRef></glyphRef><linearGradient></linearGradient><radialGradient></radialGradient><textPath></textPath></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><BODY><SVG><ALTGLYPH /><ALTGLYPHDEF /><ALTGLYPHITEM /><ANIMATECOLOR /><ANIMATEMOTION /><ANIMATETRANSFORM /><CLIPPATH /><FEBLEND /><FECOLORMATRIX /><FECOMPONENTTRANSFER /><FECOMPOSITE /><FECONVOLVEMATRIX /><FEDIFFUSELIGHTING /><FEDISPLACEMENTMAP /><FEDISTANTLIGHT /><FEFLOOD /><FEFUNCA /><FEFUNCB /><FEFUNCG /><FEFUNCR /><FEGAUSSIANBLUR /><FEIMAGE /><FEMERGE /><FEMERGENODE /><FEMORPHOLOGY /><FEOFFSET /><FEPOINTLIGHT /><FESPECULARLIGHTING /><FESPOTLIGHT /><FETILE /><FETURBULENCE /><FOREIGNOBJECT /><GLYPHREF /><LINEARGRADIENT /><RADIALGRADIENT /><TEXTPATH /></SVG>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg altGlyph": true,
+            "svg altGlyphDef": true,
+            "svg altGlyphItem": true,
+            "svg animateColor": true,
+            "svg animateMotion": true,
+            "svg animateTransform": true,
+            "svg clipPath": true,
+            "svg feBlend": true,
+            "svg feColorMatrix": true,
+            "svg feComponentTransfer": true,
+            "svg feComposite": true,
+            "svg feConvolveMatrix": true,
+            "svg feDiffuseLighting": true,
+            "svg feDisplacementMap": true,
+            "svg feDistantLight": true,
+            "svg feFlood": true,
+            "svg feFuncA": true,
+            "svg feFuncB": true,
+            "svg feFuncG": true,
+            "svg feFuncR": true,
+            "svg feGaussianBlur": true,
+            "svg feImage": true,
+            "svg feMerge": true,
+            "svg feMergeNode": true,
+            "svg feMorphology": true,
+            "svg feOffset": true,
+            "svg fePointLight": true,
+            "svg feSpecularLighting": true,
+            "svg feSpotLight": true,
+            "svg feTile": true,
+            "svg feTurbulence": true,
+            "svg foreignObject": true,
+            "svg glyphRef": true,
+            "svg linearGradient": true,
+            "svg radialGradient": true,
+            "svg textPath": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "altGlyph",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "altGlyphDef",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "altGlyphItem",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "animateColor",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "animateMotion",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "animateTransform",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "clipPath",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feBlend",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feColorMatrix",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feComponentTransfer",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feComposite",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feConvolveMatrix",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feDiffuseLighting",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feDisplacementMap",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feDistantLight",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFlood",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFuncA",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFuncB",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFuncG",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feFuncR",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feGaussianBlur",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feImage",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feMerge",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feMergeNode",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feMorphology",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feOffset",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "fePointLight",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feSpecularLighting",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feSpotLight",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feTile",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "feTurbulence",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "foreignObject",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "glyphRef",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "linearGradient",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "radialGradient",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "textPath",
+                        "ns": "http://www.w3.org/2000/svg"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><altGlyph></altGlyph><altGlyphDef></altGlyphDef><altGlyphItem></altGlyphItem><animateColor></animateColor><animateMotion></animateMotion><animateTransform></animateTransform><clipPath></clipPath><feBlend></feBlend><feColorMatrix></feColorMatrix><feComponentTransfer></feComponentTransfer><feComposite></feComposite><feConvolveMatrix></feConvolveMatrix><feDiffuseLighting></feDiffuseLighting><feDisplacementMap></feDisplacementMap><feDistantLight></feDistantLight><feFlood></feFlood><feFuncA></feFuncA><feFuncB></feFuncB><feFuncG></feFuncG><feFuncR></feFuncR><feGaussianBlur></feGaussianBlur><feImage></feImage><feMerge></feMerge><feMergeNode></feMergeNode><feMorphology></feMorphology><feOffset></feOffset><fePointLight></fePointLight><feSpecularLighting></feSpecularLighting><feSpotLight></feSpotLight><feTile></feTile><feTurbulence></feTurbulence><foreignObject></foreignObject><glyphRef></glyphRef><linearGradient></linearGradient><radialGradient></radialGradient><textPath></textPath></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><altGlyph></altGlyph><altGlyphDef></altGlyphDef><altGlyphItem></altGlyphItem><animateColor></animateColor><animateMotion></animateMotion><animateTransform></animateTransform><clipPath></clipPath><feBlend></feBlend><feColorMatrix></feColorMatrix><feComponentTransfer></feComponentTransfer><feComposite></feComposite><feConvolveMatrix></feConvolveMatrix><feDiffuseLighting></feDiffuseLighting><feDisplacementMap></feDisplacementMap><feDistantLight></feDistantLight><feFlood></feFlood><feFuncA></feFuncA><feFuncB></feFuncB><feFuncG></feFuncG><feFuncR></feFuncR><feGaussianBlur></feGaussianBlur><feImage></feImage><feMerge></feMerge><feMergeNode></feMergeNode><feMorphology></feMorphology><feOffset></feOffset><fePointLight></fePointLight><feSpecularLighting></feSpecularLighting><feSpotLight></feSpotLight><feTile></feTile><feTurbulence></feTurbulence><foreignObject></foreignObject><glyphRef></glyphRef><linearGradient></linearGradient><radialGradient></radialGradient><textPath></textPath></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><math><altGlyph /><altGlyphDef /><altGlyphItem /><animateColor /><animateMotion /><animateTransform /><clipPath /><feBlend /><feColorMatrix /><feComponentTransfer /><feComposite /><feConvolveMatrix /><feDiffuseLighting /><feDisplacementMap /><feDistantLight /><feFlood /><feFuncA /><feFuncB /><feFuncG /><feFuncR /><feGaussianBlur /><feImage /><feMerge /><feMergeNode /><feMorphology /><feOffset /><fePointLight /><feSpecularLighting /><feSpotLight /><feTile /><feTurbulence /><foreignObject /><glyphRef /><linearGradient /><radialGradient /><textPath /></math>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math altglyph": true,
+            "math altglyphdef": true,
+            "math altglyphitem": true,
+            "math animatecolor": true,
+            "math animatemotion": true,
+            "math animatetransform": true,
+            "math clippath": true,
+            "math feblend": true,
+            "math fecolormatrix": true,
+            "math fecomponenttransfer": true,
+            "math fecomposite": true,
+            "math feconvolvematrix": true,
+            "math fediffuselighting": true,
+            "math fedisplacementmap": true,
+            "math fedistantlight": true,
+            "math feflood": true,
+            "math fefunca": true,
+            "math fefuncb": true,
+            "math fefuncg": true,
+            "math fefuncr": true,
+            "math fegaussianblur": true,
+            "math feimage": true,
+            "math femerge": true,
+            "math femergenode": true,
+            "math femorphology": true,
+            "math feoffset": true,
+            "math fepointlight": true,
+            "math fespecularlighting": true,
+            "math fespotlight": true,
+            "math fetile": true,
+            "math feturbulence": true,
+            "math foreignobject": true,
+            "math glyphref": true,
+            "math lineargradient": true,
+            "math radialgradient": true,
+            "math textpath": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "altglyph",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "altglyphdef",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "altglyphitem",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "animatecolor",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "animatemotion",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "animatetransform",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "clippath",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "feblend",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fecolormatrix",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fecomponenttransfer",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fecomposite",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "feconvolvematrix",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fediffuselighting",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fedisplacementmap",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fedistantlight",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "feflood",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fefunca",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fefuncb",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fefuncg",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fefuncr",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fegaussianblur",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "feimage",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "femerge",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "femergenode",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "femorphology",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "feoffset",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fepointlight",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fespecularlighting",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fespotlight",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "fetile",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "feturbulence",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "foreignobject",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "glyphref",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "lineargradient",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "radialgradient",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      },
+                      {
+                        "tag": "textpath",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><altglyph></altglyph><altglyphdef></altglyphdef><altglyphitem></altglyphitem><animatecolor></animatecolor><animatemotion></animatemotion><animatetransform></animatetransform><clippath></clippath><feblend></feblend><fecolormatrix></fecolormatrix><fecomponenttransfer></fecomponenttransfer><fecomposite></fecomposite><feconvolvematrix></feconvolvematrix><fediffuselighting></fediffuselighting><fedisplacementmap></fedisplacementmap><fedistantlight></fedistantlight><feflood></feflood><fefunca></fefunca><fefuncb></fefuncb><fefuncg></fefuncg><fefuncr></fefuncr><fegaussianblur></fegaussianblur><feimage></feimage><femerge></femerge><femergenode></femergenode><femorphology></femorphology><feoffset></feoffset><fepointlight></fepointlight><fespecularlighting></fespecularlighting><fespotlight></fespotlight><fetile></fetile><feturbulence></feturbulence><foreignobject></foreignobject><glyphref></glyphref><lineargradient></lineargradient><radialgradient></radialgradient><textpath></textpath></math></body></html>",
+        "noQuirksBodyHtml": "<math><altglyph></altglyph><altglyphdef></altglyphdef><altglyphitem></altglyphitem><animatecolor></animatecolor><animatemotion></animatemotion><animatetransform></animatetransform><clippath></clippath><feblend></feblend><fecolormatrix></fecolormatrix><fecomponenttransfer></fecomponenttransfer><fecomposite></fecomposite><feconvolvematrix></feconvolvematrix><fediffuselighting></fediffuselighting><fedisplacementmap></fedisplacementmap><fedistantlight></fedistantlight><feflood></feflood><fefunca></fefunca><fefuncb></fefuncb><fefuncg></fefuncg><fefuncr></fefuncr><fegaussianblur></fegaussianblur><feimage></feimage><femerge></femerge><femergenode></femergenode><femorphology></femorphology><feoffset></feoffset><fepointlight></fepointlight><fespecularlighting></fespecularlighting><fespotlight></fespotlight><fetile></fetile><feturbulence></feturbulence><foreignobject></foreignobject><glyphref></glyphref><lineargradient></lineargradient><radialgradient></radialgradient><textpath></textpath></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><svg><solidColor /></svg>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg solidcolor": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "solidcolor",
+                        "ns": "http://www.w3.org/2000/svg"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><solidcolor></solidcolor></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><solidcolor></solidcolor></svg>"
+      }
+    }
+  ],
+  "tests12.dat": [
+    {
+      "data": "<!DOCTYPE html><body><p>foo<math><mtext><i>baz</i></mtext><annotation-xml><svg><desc><b>eggs</b></desc><g><foreignObject><P>spam<TABLE><tr><td><img></td></table></foreignObject></g><g>quux</g></svg></annotation-xml></math>bar",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "math math": true,
+            "math mtext": true,
+            "i": true,
+            "math annotation-xml": true,
+            "svg svg": true,
+            "svg desc": true,
+            "b": true,
+            "svg g": true,
+            "svg foreignObject": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "img": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "foo"
+                      },
+                      {
+                        "tag": "math",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "mtext",
+                            "ns": "http://www.w3.org/1998/Math/MathML",
+                            "children": [
+                              {
+                                "tag": "i",
+                                "children": [
+                                  {
+                                    "text": "baz"
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "annotation-xml",
+                            "ns": "http://www.w3.org/1998/Math/MathML",
+                            "children": [
+                              {
+                                "tag": "svg",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "tag": "desc",
+                                    "ns": "http://www.w3.org/2000/svg",
+                                    "children": [
+                                      {
+                                        "tag": "b",
+                                        "children": [
+                                          {
+                                            "text": "eggs"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  },
+                                  {
+                                    "tag": "g",
+                                    "ns": "http://www.w3.org/2000/svg",
+                                    "children": [
+                                      {
+                                        "tag": "foreignObject",
+                                        "ns": "http://www.w3.org/2000/svg",
+                                        "children": [
+                                          {
+                                            "tag": "p",
+                                            "children": [
+                                              {
+                                                "text": "spam"
+                                              }
+                                            ]
+                                          },
+                                          {
+                                            "tag": "table",
+                                            "children": [
+                                              {
+                                                "tag": "tbody",
+                                                "children": [
+                                                  {
+                                                    "tag": "tr",
+                                                    "children": [
+                                                      {
+                                                        "tag": "td",
+                                                        "children": [
+                                                          {
+                                                            "tag": "img"
+                                                          }
+                                                        ]
+                                                      }
+                                                    ]
+                                                  }
+                                                ]
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  },
+                                  {
+                                    "tag": "g",
+                                    "ns": "http://www.w3.org/2000/svg",
+                                    "children": [
+                                      {
+                                        "text": "quux"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "text": "bar"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p>foo<math><mtext><i>baz</i></mtext><annotation-xml><svg><desc><b>eggs</b></desc><g><foreignObject><p>spam</p><table><tbody><tr><td><img></td></tr></tbody></table></foreignObject></g><g>quux</g></svg></annotation-xml></math>bar</p></body></html>",
+        "noQuirksBodyHtml": "<p>foo<math><mtext><i>baz</i></mtext><annotation-xml><svg><desc><b>eggs</b></desc><g><foreignObject><p>spam</p><table><tbody><tr><td><img></td></tr></tbody></table></foreignObject></g><g>quux</g></svg></annotation-xml></math>bar</p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body>foo<math><mtext><i>baz</i></mtext><annotation-xml><svg><desc><b>eggs</b></desc><g><foreignObject><P>spam<TABLE><tr><td><img></td></table></foreignObject></g><g>quux</g></svg></annotation-xml></math>bar",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mtext": true,
+            "i": true,
+            "math annotation-xml": true,
+            "svg svg": true,
+            "svg desc": true,
+            "b": true,
+            "svg g": true,
+            "svg foreignObject": true,
+            "p": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "img": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "foo"
+                  },
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mtext",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": "baz"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "annotation-xml",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg",
+                            "children": [
+                              {
+                                "tag": "desc",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "tag": "b",
+                                    "children": [
+                                      {
+                                        "text": "eggs"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "g",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "tag": "foreignObject",
+                                    "ns": "http://www.w3.org/2000/svg",
+                                    "children": [
+                                      {
+                                        "tag": "p",
+                                        "children": [
+                                          {
+                                            "text": "spam"
+                                          }
+                                        ]
+                                      },
+                                      {
+                                        "tag": "table",
+                                        "children": [
+                                          {
+                                            "tag": "tbody",
+                                            "children": [
+                                              {
+                                                "tag": "tr",
+                                                "children": [
+                                                  {
+                                                    "tag": "td",
+                                                    "children": [
+                                                      {
+                                                        "tag": "img"
+                                                      }
+                                                    ]
+                                                  }
+                                                ]
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "g",
+                                "ns": "http://www.w3.org/2000/svg",
+                                "children": [
+                                  {
+                                    "text": "quux"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "text": "bar"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>foo<math><mtext><i>baz</i></mtext><annotation-xml><svg><desc><b>eggs</b></desc><g><foreignObject><p>spam</p><table><tbody><tr><td><img></td></tr></tbody></table></foreignObject></g><g>quux</g></svg></annotation-xml></math>bar</body></html>",
+        "noQuirksBodyHtml": "foo<math><mtext><i>baz</i></mtext><annotation-xml><svg><desc><b>eggs</b></desc><g><foreignObject><p>spam</p><table><tbody><tr><td><img></td></tr></tbody></table></foreignObject></g><g>quux</g></svg></annotation-xml></math>bar"
+      }
+    }
+  ],
+  "tests14.dat": [
+    {
+      "data": "<!DOCTYPE html><html><body><xyz:abc></xyz:abc>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "xyz:abc": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "xyz:abc"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><xyz:abc></xyz:abc></body></html>",
+        "noQuirksBodyHtml": "<xyz:abc></xyz:abc>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html><body><xyz:abc></xyz:abc><span></span>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "xyz:abc": true,
+            "span": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "xyz:abc"
+                  },
+                  {
+                    "tag": "span"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><xyz:abc></xyz:abc><span></span></body></html>",
+        "noQuirksBodyHtml": "<xyz:abc></xyz:abc><span></span>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html><html abc:def=gh><xyz:abc></xyz:abc>",
+      "errors": [
+        "(1,38): non-html-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "xyz:abc": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "attrs": [
+              {
+                "name": "abc:def",
+                "value": "gh"
+              }
+            ],
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "xyz:abc"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html abc:def=\"gh\"><head></head><body><xyz:abc></xyz:abc></body></html>",
+        "noQuirksBodyHtml": "<xyz:abc></xyz:abc>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html xml:lang=bar><html xml:lang=foo>",
+      "errors": [
+        "(1,53): non-html-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "attrs": [
+              {
+                "name": "xml:lang",
+                "value": "bar"
+              }
+            ],
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html xml:lang=\"bar\"><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html 123=456>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "attrs": [
+              {
+                "name": "123",
+                "value": "456"
+              }
+            ],
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html 123=\"456\"><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html 123=456><html 789=012>",
+      "errors": [
+        "(1,43): non-html-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "attrs": [
+              {
+                "name": "123",
+                "value": "456"
+              },
+              {
+                "name": "789",
+                "value": "012"
+              }
+            ],
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html 123=\"456\" 789=\"012\"><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html><body 789=012>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "attrs": [
+                  {
+                    "name": "789",
+                    "value": "012"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body 789=\"012\"></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    }
+  ],
+  "tests15.dat": [
+    {
+      "data": "<!DOCTYPE html><p><b><i><u></p> <p>X",
+      "errors": [
+        "(1,31): unexpected-end-tag",
+        "(1,36): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "b": true,
+            "i": true,
+            "u": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "tag": "u"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "i",
+                        "children": [
+                          {
+                            "tag": "u",
+                            "children": [
+                              {
+                                "text": " "
+                              },
+                              {
+                                "tag": "p",
+                                "children": [
+                                  {
+                                    "text": "X"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><b><i><u></u></i></b></p><b><i><u> <p>X</p></u></i></b></body></html>",
+        "noQuirksBodyHtml": "<p><b><i><u></u></i></b></p><b><i><u> <p>X</p></u></i></b>"
+      }
+    },
+    {
+      "data": "<p><b><i><u></p>\n<p>X",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,16): unexpected-end-tag",
+        "(2,4): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "b": true,
+            "i": true,
+            "u": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "tag": "u"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "i",
+                        "children": [
+                          {
+                            "tag": "u",
+                            "children": [
+                              {
+                                "text": "\n"
+                              },
+                              {
+                                "tag": "p",
+                                "children": [
+                                  {
+                                    "text": "X"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><b><i><u></u></i></b></p><b><i><u>\n<p>X</p></u></i></b></body></html>",
+        "noQuirksBodyHtml": "<p><b><i><u></u></i></b></p><b><i><u>\n<p>X</p></u></i></b>"
+      }
+    },
+    {
+      "data": "<!doctype html></html> <head>",
+      "errors": [
+        "(1,29): expected-eof-but-got-start-tag",
+        "(1,29): unexpected-start-tag-ignored"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": " "
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body> </body></html>",
+        "noQuirksBodyHtml": " "
+      }
+    },
+    {
+      "data": "<!doctype html></body><meta>",
+      "errors": [
+        "(1,28): unexpected-start-tag-after-body"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "meta": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "meta"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><meta></body></html>",
+        "noQuirksBodyHtml": "<meta>"
+      }
+    },
+    {
+      "data": "<html></html><!-- foo -->",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          },
+          {
+            "comment": " foo "
+          }
+        ],
+        "html": "<html><head></head><body></body></html><!-- foo -->",
+        "noQuirksBodyHtml": "<!-- foo -->"
+      }
+    },
+    {
+      "data": "<!doctype html></body><title>X</title>",
+      "errors": [
+        "(1,29): unexpected-start-tag-after-body"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "title": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "X"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><title>X</title></body></html>",
+        "noQuirksBodyHtml": "<title>X</title>"
+      }
+    },
+    {
+      "data": "<!doctype html><table> X<meta></table>",
+      "errors": [
+        "(1,23): foster-parenting-character",
+        "(1,24): foster-parenting-character",
+        "(1,30): foster-parenting-start-character"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "meta": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": " X"
+                  },
+                  {
+                    "tag": "meta"
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body> X<meta><table></table></body></html>",
+        "noQuirksBodyHtml": " X<meta><table></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table> x</table>",
+      "errors": [
+        "(1,23): foster-parenting-character",
+        "(1,24): foster-parenting-character"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": " x"
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body> x<table></table></body></html>",
+        "noQuirksBodyHtml": " x<table></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table> x </table>",
+      "errors": [
+        "(1,23): foster-parenting-character",
+        "(1,24): foster-parenting-character",
+        "(1,25): foster-parenting-character"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": " x "
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body> x <table></table></body></html>",
+        "noQuirksBodyHtml": " x <table></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><tr> x</table>",
+      "errors": [
+        "(1,27): foster-parenting-character",
+        "(1,28): foster-parenting-character"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": " x"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body> x<table><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": " x<table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table>X<style> <tr>x </style> </table>",
+      "errors": [
+        "(1,23): foster-parenting-character"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "style": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "style",
+                        "children": [
+                          {
+                            "text": " <tr>x ",
+                            "no_escape": true
+                          }
+                        ]
+                      },
+                      {
+                        "text": " "
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>X<table><style> <tr>x </style> </table></body></html>",
+        "noQuirksBodyHtml": "X<table><style> <tr>x </style> </table>"
+      }
+    },
+    {
+      "data": "<!doctype html><div><table><a>foo</a> <tr><td>bar</td> </tr></table></div>",
+      "errors": [
+        "(1,30): foster-parenting-start-tag",
+        "(1,31): foster-parenting-character",
+        "(1,32): foster-parenting-character",
+        "(1,33): foster-parenting-character",
+        "(1,37): foster-parenting-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "a": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "text": " "
+                          },
+                          {
+                            "tag": "tbody",
+                            "children": [
+                              {
+                                "tag": "tr",
+                                "children": [
+                                  {
+                                    "tag": "td",
+                                    "children": [
+                                      {
+                                        "text": "bar"
+                                      }
+                                    ]
+                                  },
+                                  {
+                                    "text": " "
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><div><a>foo</a><table> <tbody><tr><td>bar</td> </tr></tbody></table></div></body></html>",
+        "noQuirksBodyHtml": "<div><a>foo</a><table> <tbody><tr><td>bar</td> </tr></tbody></table></div>"
+      }
+    },
+    {
+      "data": "<frame></frame></frame><frameset><frame><frameset><frame></frameset><noframes></frameset><noframes>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,7): unexpected-start-tag-ignored",
+        "(1,15): unexpected-end-tag",
+        "(1,23): unexpected-end-tag",
+        "(1,33): unexpected-start-tag",
+        "(1,99): expected-named-closing-tag-but-got-eof",
+        "(1,99): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "frame": true,
+            "noframes": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset",
+                "children": [
+                  {
+                    "tag": "frame"
+                  },
+                  {
+                    "tag": "frameset",
+                    "children": [
+                      {
+                        "tag": "frame"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "noframes",
+                    "children": [
+                      {
+                        "text": "</frameset><noframes>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset><frame><frameset><frame></frameset><noframes></frameset><noframes></noframes></frameset></html>",
+        "noQuirksBodyHtml": "<noframes></frameset><noframes></noframes>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><object></html>",
+      "errors": [
+        "(1,30): expected-body-in-scope",
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "object": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "object"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><object></object></body></html>",
+        "noQuirksBodyHtml": "<object></object>"
+      }
+    }
+  ],
+  "tests16.dat": [
+    {
+      "data": "<!doctype html><script>",
+      "errors": [
+        "(1,23): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script"
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script>a",
+      "errors": [
+        "(1,24): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script>a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script>a</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><",
+      "errors": [
+        "(1,24): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></",
+      "errors": [
+        "(1,25): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></S",
+      "errors": [
+        "(1,26): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</S",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></S</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></S</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></SC",
+      "errors": [
+        "(1,27): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</SC",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></SC</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></SC</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></SCR",
+      "errors": [
+        "(1,28): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</SCR",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></SCR</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></SCR</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></SCRI",
+      "errors": [
+        "(1,29): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</SCRI",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></SCRI</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></SCRI</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></SCRIP",
+      "errors": [
+        "(1,30): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</SCRIP",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></SCRIP</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></SCRIP</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></SCRIPT",
+      "errors": [
+        "(1,31): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</SCRIPT",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></SCRIPT</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></SCRIPT</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></SCRIPT ",
+      "errors": [
+        "(1,32): expected-attribute-name-but-got-eof",
+        "(1,32): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script"
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></s",
+      "errors": [
+        "(1,26): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</s",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></s</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></s</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></sc",
+      "errors": [
+        "(1,27): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</sc",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></sc</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></sc</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></scr",
+      "errors": [
+        "(1,28): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</scr",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></scr</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></scr</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></scri",
+      "errors": [
+        "(1,29): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</scri",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></scri</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></scri</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></scrip",
+      "errors": [
+        "(1,30): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</scrip",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></scrip</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></scrip</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></script",
+      "errors": [
+        "(1,31): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</script",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></script</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></script</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script></script ",
+      "errors": [
+        "(1,32): expected-attribute-name-but-got-eof",
+        "(1,32): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script"
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!",
+      "errors": [
+        "(1,25): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!a",
+      "errors": [
+        "(1,26): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!a</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!-",
+      "errors": [
+        "(1,26): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!-",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!-</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!-</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!-a",
+      "errors": [
+        "(1,27): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!-a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!-a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!-a</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--",
+      "errors": [
+        "(1,27): expected-named-closing-tag-but-got-eof",
+        "(1,27): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--a",
+      "errors": [
+        "(1,28): expected-named-closing-tag-but-got-eof",
+        "(1,28): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--a</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<",
+      "errors": [
+        "(1,28): expected-named-closing-tag-but-got-eof",
+        "(1,28): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<a",
+      "errors": [
+        "(1,29): expected-named-closing-tag-but-got-eof",
+        "(1,29): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<a</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--</",
+      "errors": [
+        "(1,29): expected-named-closing-tag-but-got-eof",
+        "(1,29): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--</",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--</</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--</</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--</script",
+      "errors": [
+        "(1,35): expected-named-closing-tag-but-got-eof",
+        "(1,35): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--</script",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--</script</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--</script</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--</script ",
+      "errors": [
+        "(1,36): expected-attribute-name-but-got-eof",
+        "(1,36): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<s",
+      "errors": [
+        "(1,29): expected-named-closing-tag-but-got-eof",
+        "(1,29): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<s",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<s</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<s</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script",
+      "errors": [
+        "(1,34): expected-named-closing-tag-but-got-eof",
+        "(1,34): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script ",
+      "errors": [
+        "(1,35): eof-in-script-in-script",
+        "(1,35): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script <",
+      "errors": [
+        "(1,36): eof-in-script-in-script",
+        "(1,36): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script <",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script <</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script <</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script <a",
+      "errors": [
+        "(1,37): eof-in-script-in-script",
+        "(1,37): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script <a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script <a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script <a</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </",
+      "errors": [
+        "(1,37): eof-in-script-in-script",
+        "(1,37): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </s",
+      "errors": [
+        "(1,38): eof-in-script-in-script",
+        "(1,38): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </s",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </s</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </s</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </script",
+      "errors": [
+        "(1,43): eof-in-script-in-script",
+        "(1,43): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </script</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </scripta",
+      "errors": [
+        "(1,44): eof-in-script-in-script",
+        "(1,44): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </scripta",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </scripta</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </scripta</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </script ",
+      "errors": [
+        "(1,44): expected-named-closing-tag-but-got-eof",
+        "(1,44): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </script </script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script </script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </script>",
+      "errors": [
+        "(1,44): expected-named-closing-tag-but-got-eof",
+        "(1,44): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </script/",
+      "errors": [
+        "(1,44): expected-named-closing-tag-but-got-eof",
+        "(1,44): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script/",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </script/</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script/</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </script <",
+      "errors": [
+        "(1,45): expected-named-closing-tag-but-got-eof",
+        "(1,45): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script <",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </script <</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script <</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </script <a",
+      "errors": [
+        "(1,46): expected-named-closing-tag-but-got-eof",
+        "(1,46): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script <a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </script <a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script <a</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </script </",
+      "errors": [
+        "(1,46): expected-named-closing-tag-but-got-eof",
+        "(1,46): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script </",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </script </</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script </</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </script </script",
+      "errors": [
+        "(1,52): expected-named-closing-tag-but-got-eof",
+        "(1,52): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script </script",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </script </script</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script </script</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </script </script ",
+      "errors": [
+        "(1,53): expected-attribute-name-but-got-eof",
+        "(1,53): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </script </script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script </script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </script </script/",
+      "errors": [
+        "(1,53): unexpected-EOF-after-solidus-in-tag",
+        "(1,53): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </script </script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script </script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script </script </script>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script </script </script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script </script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script -",
+      "errors": [
+        "(1,36): eof-in-script-in-script",
+        "(1,36): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script -",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script -</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script -</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script -a",
+      "errors": [
+        "(1,37): eof-in-script-in-script",
+        "(1,37): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script -a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script -a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script -a</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script -<",
+      "errors": [
+        "(1,37): eof-in-script-in-script",
+        "(1,37): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script -<",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script -<</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script -<</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script --",
+      "errors": [
+        "(1,37): eof-in-script-in-script",
+        "(1,37): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script --",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script --</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script --a",
+      "errors": [
+        "(1,38): eof-in-script-in-script",
+        "(1,38): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script --a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script --a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --a</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script --<",
+      "errors": [
+        "(1,38): eof-in-script-in-script",
+        "(1,38): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script --<",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script --<</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --<</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script -->",
+      "errors": [
+        "(1,38): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script -->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script --></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script --><",
+      "errors": [
+        "(1,39): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script --><",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script --><</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --><</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script --></",
+      "errors": [
+        "(1,40): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script --></",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script --></</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --></</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script --></script",
+      "errors": [
+        "(1,46): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script --></script",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script --></script</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --></script</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script --></script ",
+      "errors": [
+        "(1,47): expected-attribute-name-but-got-eof",
+        "(1,47): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script -->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script --></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script --></script/",
+      "errors": [
+        "(1,47): unexpected-EOF-after-solidus-in-tag",
+        "(1,47): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script -->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script --></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script --></script>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script -->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script --></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script><\\/script>--></script>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script><\\/script>-->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script><\\/script>--></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script><\\/script>--></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script></scr'+'ipt>--></script>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></scr'+'ipt>-->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script></scr'+'ipt>--></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></scr'+'ipt>--></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script></script><script></script></script>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></script><script></script>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script></script><script></script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></script><script></script></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script></script><script></script>--><!--</script>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></script><script></script>--><!--",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script></script><script></script>--><!--</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></script><script></script>--><!--</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script></script><script></script>-- ></script>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></script><script></script>-- >",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script></script><script></script>-- ></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></script><script></script>-- ></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script></script><script></script>- -></script>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></script><script></script>- ->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script></script><script></script>- -></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></script><script></script>- -></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script></script><script></script>- - ></script>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></script><script></script>- - >",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script></script><script></script>- - ></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></script><script></script>- - ></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script></script><script></script>-></script>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></script><script></script>->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script></script><script></script>-></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></script><script></script>-></script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script>--!></script>X",
+      "errors": [
+        "(1,49): expected-named-closing-tag-but-got-eof",
+        "(1,49): unexpected-EOF-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script>--!></script>X",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script>--!></script>X</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script>--!></script>X</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<scr'+'ipt></script>--></script>",
+      "errors": [
+        "(1,59): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<scr'+'ipt>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<scr'+'ipt></script></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<script><!--<scr'+'ipt></script>--&gt;"
+      }
+    },
+    {
+      "data": "<!doctype html><script><!--<script></scr'+'ipt></script>X",
+      "errors": [
+        "(1,57): expected-named-closing-tag-but-got-eof",
+        "(1,57): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></scr'+'ipt></script>X",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script><!--<script></scr'+'ipt></script>X</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></scr'+'ipt></script>X</script>"
+      }
+    },
+    {
+      "data": "<!doctype html><style><!--<style></style>--></style>",
+      "errors": [
+        "(1,52): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--<style>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><style><!--<style></style></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<style><!--<style></style>--&gt;"
+      }
+    },
+    {
+      "data": "<!doctype html><style><!--</style>X",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><style><!--</style></head><body>X</body></html>",
+        "noQuirksBodyHtml": "<style><!--</style>X"
+      }
+    },
+    {
+      "data": "<!doctype html><style><!--...</style>...--></style>",
+      "errors": [
+        "(1,51): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--...",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "...-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><style><!--...</style></head><body>...--&gt;</body></html>",
+        "noQuirksBodyHtml": "<style><!--...</style>...--&gt;"
+      }
+    },
+    {
+      "data": "<!doctype html><style><!--<br><html xmlns:v=\"urn:schemas-microsoft-com:vml\"><!--[if !mso]><style></style>X",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--<br><html xmlns:v=\"urn:schemas-microsoft-com:vml\"><!--[if !mso]><style>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><style><!--<br><html xmlns:v=\"urn:schemas-microsoft-com:vml\"><!--[if !mso]><style></style></head><body>X</body></html>",
+        "noQuirksBodyHtml": "<style><!--<br><html xmlns:v=\"urn:schemas-microsoft-com:vml\"><!--[if !mso]><style></style>X"
+      }
+    },
+    {
+      "data": "<!doctype html><style><!--...<style><!--...--!></style>--></style>",
+      "errors": [
+        "(1,66): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--...<style><!--...--!>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><style><!--...<style><!--...--!></style></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<style><!--...<style><!--...--!></style>--&gt;"
+      }
+    },
+    {
+      "data": "<!doctype html><style><!--...</style><!-- --><style>@import ...</style>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--...",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "comment": " "
+                  },
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "@import ...",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><style><!--...</style><!-- --><style>@import ...</style></head><body></body></html>",
+        "noQuirksBodyHtml": "<style><!--...</style><!-- --><style>@import ...</style>"
+      }
+    },
+    {
+      "data": "<!doctype html><style>...<style><!--...</style><!-- --></style>",
+      "errors": [
+        "(1,63): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "...<style><!--...",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "comment": " "
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><style>...<style><!--...</style><!-- --></head><body></body></html>",
+        "noQuirksBodyHtml": "<style>...<style><!--...</style><!-- -->"
+      }
+    },
+    {
+      "data": "<!doctype html><style>...<!--[if IE]><style>...</style>X",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "...<!--[if IE]><style>...",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><style>...<!--[if IE]><style>...</style></head><body>X</body></html>",
+        "noQuirksBodyHtml": "<style>...<!--[if IE]><style>...</style>X"
+      }
+    },
+    {
+      "data": "<!doctype html><title><!--<title></title>--></title>",
+      "errors": [
+        "(1,52): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "doctype": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "<!--<title>",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><title>&lt;!--&lt;title&gt;</title></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<title>&lt;!--&lt;title&gt;</title>--&gt;"
+      }
+    },
+    {
+      "data": "<!doctype html><title>&lt;/title></title>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "doctype": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "</title>",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><title>&lt;/title&gt;</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title>&lt;/title&gt;</title>"
+      }
+    },
+    {
+      "data": "<!doctype html><title>foo/title><link></head><body>X",
+      "errors": [
+        "(1,52): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "doctype": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "foo/title><link></head><body>X",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><title>foo/title&gt;&lt;link&gt;&lt;/head&gt;&lt;body&gt;X</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title>foo/title&gt;&lt;link&gt;&lt;/head&gt;&lt;body&gt;X</title>"
+      }
+    },
+    {
+      "data": "<!doctype html><noscript><!--<noscript></noscript>--></noscript>",
+      "errors": [
+        "(1,64): unexpected-end-tag"
+      ],
+      "script": "on",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "text": "<!--<noscript>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><noscript><!--<noscript></noscript></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;!--&lt;noscript&gt;</noscript>--&gt;"
+      }
+    },
+    {
+      "data": "<!doctype html><noscript><!--<noscript></noscript>--></noscript>",
+      "errors": [],
+      "script": "off",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "comment": "<noscript></noscript>"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><noscript><!--<noscript></noscript>--></noscript></head><body></body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;!--&lt;noscript&gt;</noscript>--&gt;"
+      }
+    },
+    {
+      "data": "<!doctype html><noscript><!--</noscript>X<noscript>--></noscript>",
+      "errors": [],
+      "script": "on",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  },
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "text": "-->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><noscript><!--</noscript></head><body>X<noscript>--></noscript></body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;!--</noscript>X<noscript>--&gt;</noscript>"
+      }
+    },
+    {
+      "data": "<!doctype html><noscript><!--</noscript>X<noscript>--></noscript>",
+      "errors": [],
+      "script": "off",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "comment": "</noscript>X<noscript>"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><noscript><!--</noscript>X<noscript>--></noscript></head><body></body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;!--</noscript>X<noscript>--&gt;</noscript>"
+      }
+    },
+    {
+      "data": "<!doctype html><noscript><iframe></noscript>X",
+      "errors": [],
+      "script": "on",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "text": "<iframe>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><noscript><iframe></noscript></head><body>X</body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;iframe&gt;</noscript>X"
+      }
+    },
+    {
+      "data": "<!doctype html><noscript><iframe></noscript>X",
+      "errors": [
+        " * (1,34) unexpected token in head noscript",
+        " * (1,46) unexpected EOF"
+      ],
+      "script": "off",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true,
+            "iframe": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript"
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "iframe",
+                    "children": [
+                      {
+                        "text": "</noscript>X",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><noscript></noscript></head><body><iframe></noscript>X</iframe></body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;iframe&gt;</noscript>X"
+      }
+    },
+    {
+      "data": "<!doctype html><noframes><!--<noframes></noframes>--></noframes>",
+      "errors": [
+        "(1,64): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noframes": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noframes",
+                    "children": [
+                      {
+                        "text": "<!--<noframes>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><noframes><!--<noframes></noframes></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<noframes><!--<noframes></noframes>--&gt;"
+      }
+    },
+    {
+      "data": "<!doctype html><noframes><body><script><!--...</script></body></noframes></html>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noframes": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noframes",
+                    "children": [
+                      {
+                        "text": "<body><script><!--...</script></body>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><noframes><body><script><!--...</script></body></noframes></head><body></body></html>",
+        "noQuirksBodyHtml": "<noframes><body><script><!--...</script></body></noframes>"
+      }
+    },
+    {
+      "data": "<!doctype html><textarea><!--<textarea></textarea>--></textarea>",
+      "errors": [
+        "(1,64): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "doctype": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": "<!--<textarea>",
+                        "escaped": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><textarea>&lt;!--&lt;textarea&gt;</textarea>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<textarea>&lt;!--&lt;textarea&gt;</textarea>--&gt;"
+      }
+    },
+    {
+      "data": "<!doctype html><textarea>&lt;/textarea></textarea>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "doctype": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": "</textarea>",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><textarea>&lt;/textarea&gt;</textarea></body></html>",
+        "noQuirksBodyHtml": "<textarea>&lt;/textarea&gt;</textarea>"
+      }
+    },
+    {
+      "data": "<!doctype html><textarea>&lt;</textarea>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "doctype": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": "<",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><textarea>&lt;</textarea></body></html>",
+        "noQuirksBodyHtml": "<textarea>&lt;</textarea>"
+      }
+    },
+    {
+      "data": "<!doctype html><textarea>a&lt;b</textarea>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "doctype": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": "a<b",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><textarea>a&lt;b</textarea></body></html>",
+        "noQuirksBodyHtml": "<textarea>a&lt;b</textarea>"
+      }
+    },
+    {
+      "data": "<!doctype html><iframe><!--<iframe></iframe>--></iframe>",
+      "errors": [
+        "(1,56): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "iframe": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "iframe",
+                    "children": [
+                      {
+                        "text": "<!--<iframe>",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><iframe><!--<iframe></iframe>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<iframe><!--<iframe></iframe>--&gt;"
+      }
+    },
+    {
+      "data": "<!doctype html><iframe>...<!--X->...<!--/X->...</iframe>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "iframe": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "iframe",
+                    "children": [
+                      {
+                        "text": "...<!--X->...<!--/X->...",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><iframe>...<!--X->...<!--/X->...</iframe></body></html>",
+        "noQuirksBodyHtml": "<iframe>...<!--X->...<!--/X->...</iframe>"
+      }
+    },
+    {
+      "data": "<!doctype html><xmp><!--<xmp></xmp>--></xmp>",
+      "errors": [
+        "(1,44): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "xmp": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "xmp",
+                    "children": [
+                      {
+                        "text": "<!--<xmp>",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><xmp><!--<xmp></xmp>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<xmp><!--<xmp></xmp>--&gt;"
+      }
+    },
+    {
+      "data": "<!doctype html><noembed><!--<noembed></noembed>--></noembed>",
+      "errors": [
+        "(1,60): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "noembed": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "noembed",
+                    "children": [
+                      {
+                        "text": "<!--<noembed>",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><noembed><!--<noembed></noembed>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<noembed><!--<noembed></noembed>--&gt;"
+      }
+    },
+    {
+      "data": "<script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,8): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script"
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></script>"
+      }
+    },
+    {
+      "data": "<script>a",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,9): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script>a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script>a</script>"
+      }
+    },
+    {
+      "data": "<script><",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,9): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><</script>"
+      }
+    },
+    {
+      "data": "<script></",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,10): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></</script>"
+      }
+    },
+    {
+      "data": "<script></S",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,11): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</S",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></S</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></S</script>"
+      }
+    },
+    {
+      "data": "<script></SC",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,12): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</SC",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></SC</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></SC</script>"
+      }
+    },
+    {
+      "data": "<script></SCR",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,13): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</SCR",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></SCR</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></SCR</script>"
+      }
+    },
+    {
+      "data": "<script></SCRI",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,14): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</SCRI",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></SCRI</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></SCRI</script>"
+      }
+    },
+    {
+      "data": "<script></SCRIP",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,15): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</SCRIP",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></SCRIP</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></SCRIP</script>"
+      }
+    },
+    {
+      "data": "<script></SCRIPT",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,16): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</SCRIPT",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></SCRIPT</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></SCRIPT</script>"
+      }
+    },
+    {
+      "data": "<script></SCRIPT ",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,17): expected-attribute-name-but-got-eof",
+        "(1,17): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script"
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></script>"
+      }
+    },
+    {
+      "data": "<script></s",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,11): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</s",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></s</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></s</script>"
+      }
+    },
+    {
+      "data": "<script></sc",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,12): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</sc",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></sc</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></sc</script>"
+      }
+    },
+    {
+      "data": "<script></scr",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,13): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</scr",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></scr</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></scr</script>"
+      }
+    },
+    {
+      "data": "<script></scri",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,14): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</scri",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></scri</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></scri</script>"
+      }
+    },
+    {
+      "data": "<script></scrip",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,15): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</scrip",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></scrip</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></scrip</script>"
+      }
+    },
+    {
+      "data": "<script></script",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,16): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</script",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></script</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></script</script>"
+      }
+    },
+    {
+      "data": "<script></script ",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,17): expected-attribute-name-but-got-eof",
+        "(1,17): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script"
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></script>"
+      }
+    },
+    {
+      "data": "<script><!",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,10): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!</script>"
+      }
+    },
+    {
+      "data": "<script><!a",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,11): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!a</script>"
+      }
+    },
+    {
+      "data": "<script><!-",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,11): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!-",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!-</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!-</script>"
+      }
+    },
+    {
+      "data": "<script><!-a",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,12): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!-a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!-a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!-a</script>"
+      }
+    },
+    {
+      "data": "<script><!--",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,12): expected-named-closing-tag-but-got-eof",
+        "(1,12): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--</script>"
+      }
+    },
+    {
+      "data": "<script><!--a",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,13): expected-named-closing-tag-but-got-eof",
+        "(1,13): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--a</script>"
+      }
+    },
+    {
+      "data": "<script><!--<",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,13): expected-named-closing-tag-but-got-eof",
+        "(1,13): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<</script>"
+      }
+    },
+    {
+      "data": "<script><!--<a",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,14): expected-named-closing-tag-but-got-eof",
+        "(1,14): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<a</script>"
+      }
+    },
+    {
+      "data": "<script><!--</",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,14): expected-named-closing-tag-but-got-eof",
+        "(1,14): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--</",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--</</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--</</script>"
+      }
+    },
+    {
+      "data": "<script><!--</script",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,20): expected-named-closing-tag-but-got-eof",
+        "(1,20): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--</script",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--</script</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--</script</script>"
+      }
+    },
+    {
+      "data": "<script><!--</script ",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,21): expected-attribute-name-but-got-eof",
+        "(1,21): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--</script>"
+      }
+    },
+    {
+      "data": "<script><!--<s",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,14): expected-named-closing-tag-but-got-eof",
+        "(1,14): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<s",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<s</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<s</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,19): expected-named-closing-tag-but-got-eof",
+        "(1,19): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script ",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,20): eof-in-script-in-script",
+        "(1,20): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script>"
+      }
+    },
+    {
+      "data": "<script><!--<script <",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,21): eof-in-script-in-script",
+        "(1,21): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script <",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script <</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script <</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script <a",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,22): eof-in-script-in-script",
+        "(1,22): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script <a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script <a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script <a</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,22): eof-in-script-in-script",
+        "(1,22): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </s",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,23): eof-in-script-in-script",
+        "(1,23): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </s",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </s</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </s</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </script",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,28): eof-in-script-in-script",
+        "(1,28): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </script</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </scripta",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,29): eof-in-script-in-script",
+        "(1,29): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </scripta",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </scripta</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </scripta</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </script ",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,29): expected-named-closing-tag-but-got-eof",
+        "(1,29): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </script </script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script </script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,29): expected-named-closing-tag-but-got-eof",
+        "(1,29): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script></script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </script/",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,29): expected-named-closing-tag-but-got-eof",
+        "(1,29): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script/",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </script/</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script/</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </script <",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,30): expected-named-closing-tag-but-got-eof",
+        "(1,30): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script <",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </script <</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script <</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </script <a",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,31): expected-named-closing-tag-but-got-eof",
+        "(1,31): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script <a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </script <a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script <a</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </script </",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,31): expected-named-closing-tag-but-got-eof",
+        "(1,31): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script </",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </script </</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script </</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </script </script",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,37): expected-named-closing-tag-but-got-eof",
+        "(1,37): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script </script",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </script </script</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script </script</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </script </script ",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,38): expected-attribute-name-but-got-eof",
+        "(1,38): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </script </script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script </script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </script </script/",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,38): unexpected-EOF-after-solidus-in-tag",
+        "(1,38): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </script </script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script </script>"
+      }
+    },
+    {
+      "data": "<script><!--<script </script </script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script </script ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script </script </script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script </script </script>"
+      }
+    },
+    {
+      "data": "<script><!--<script -",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,21): eof-in-script-in-script",
+        "(1,21): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script -",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script -</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script -</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script -a",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,22): eof-in-script-in-script",
+        "(1,22): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script -a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script -a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script -a</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script --",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,22): eof-in-script-in-script",
+        "(1,22): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script --",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script --</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script --a",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,23): eof-in-script-in-script",
+        "(1,23): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script --a",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script --a</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --a</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script -->",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,23): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script -->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script --></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --></script>"
+      }
+    },
+    {
+      "data": "<script><!--<script --><",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,24): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script --><",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script --><</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --><</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script --></",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,25): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script --></",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script --></</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --></</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script --></script",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,31): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script --></script",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script --></script</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --></script</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script --></script ",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,32): expected-attribute-name-but-got-eof",
+        "(1,32): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script -->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script --></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --></script>"
+      }
+    },
+    {
+      "data": "<script><!--<script --></script/",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,32): unexpected-EOF-after-solidus-in-tag",
+        "(1,32): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script -->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script --></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --></script>"
+      }
+    },
+    {
+      "data": "<script><!--<script --></script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script -->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script --></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script --></script>"
+      }
+    },
+    {
+      "data": "<script><!--<script><\\/script>--></script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script><\\/script>-->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script><\\/script>--></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script><\\/script>--></script>"
+      }
+    },
+    {
+      "data": "<script><!--<script></scr'+'ipt>--></script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></scr'+'ipt>-->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script></scr'+'ipt>--></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></scr'+'ipt>--></script>"
+      }
+    },
+    {
+      "data": "<script><!--<script></script><script></script></script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></script><script></script>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script></script><script></script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></script><script></script></script>"
+      }
+    },
+    {
+      "data": "<script><!--<script></script><script></script>--><!--</script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></script><script></script>--><!--",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script></script><script></script>--><!--</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></script><script></script>--><!--</script>"
+      }
+    },
+    {
+      "data": "<script><!--<script></script><script></script>-- ></script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></script><script></script>-- >",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script></script><script></script>-- ></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></script><script></script>-- ></script>"
+      }
+    },
+    {
+      "data": "<script><!--<script></script><script></script>- -></script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></script><script></script>- ->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script></script><script></script>- -></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></script><script></script>- -></script>"
+      }
+    },
+    {
+      "data": "<script><!--<script></script><script></script>- - ></script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></script><script></script>- - >",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script></script><script></script>- - ></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></script><script></script>- - ></script>"
+      }
+    },
+    {
+      "data": "<script><!--<script></script><script></script>-></script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></script><script></script>->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script></script><script></script>-></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></script><script></script>-></script>"
+      }
+    },
+    {
+      "data": "<script><!--<script>--!></script>X",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,34): expected-named-closing-tag-but-got-eof",
+        "(1,34): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script>--!></script>X",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script>--!></script>X</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script>--!></script>X</script>"
+      }
+    },
+    {
+      "data": "<script><!--<scr'+'ipt></script>--></script>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,44): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<scr'+'ipt>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<scr'+'ipt></script></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<script><!--<scr'+'ipt></script>--&gt;"
+      }
+    },
+    {
+      "data": "<script><!--<script></scr'+'ipt></script>X",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,42): expected-named-closing-tag-but-got-eof",
+        "(1,42): unexpected-eof-in-text-mode"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "<!--<script></scr'+'ipt></script>X",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script><!--<script></scr'+'ipt></script>X</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script><!--<script></scr'+'ipt></script>X</script>"
+      }
+    },
+    {
+      "data": "<style><!--<style></style>--></style>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,37): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--<style>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style><!--<style></style></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<style><!--<style></style>--&gt;"
+      }
+    },
+    {
+      "data": "<style><!--</style>X",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style><!--</style></head><body>X</body></html>",
+        "noQuirksBodyHtml": "<style><!--</style>X"
+      }
+    },
+    {
+      "data": "<style><!--...</style>...--></style>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,36): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--...",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "...-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style><!--...</style></head><body>...--&gt;</body></html>",
+        "noQuirksBodyHtml": "<style><!--...</style>...--&gt;"
+      }
+    },
+    {
+      "data": "<style><!--<br><html xmlns:v=\"urn:schemas-microsoft-com:vml\"><!--[if !mso]><style></style>X",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--<br><html xmlns:v=\"urn:schemas-microsoft-com:vml\"><!--[if !mso]><style>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style><!--<br><html xmlns:v=\"urn:schemas-microsoft-com:vml\"><!--[if !mso]><style></style></head><body>X</body></html>",
+        "noQuirksBodyHtml": "<style><!--<br><html xmlns:v=\"urn:schemas-microsoft-com:vml\"><!--[if !mso]><style></style>X"
+      }
+    },
+    {
+      "data": "<style><!--...<style><!--...--!></style>--></style>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,51): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--...<style><!--...--!>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style><!--...<style><!--...--!></style></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<style><!--...<style><!--...--!></style>--&gt;"
+      }
+    },
+    {
+      "data": "<style><!--...</style><!-- --><style>@import ...</style>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "<!--...",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "comment": " "
+                  },
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "@import ...",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style><!--...</style><!-- --><style>@import ...</style></head><body></body></html>",
+        "noQuirksBodyHtml": "<style><!--...</style><!-- --><style>@import ...</style>"
+      }
+    },
+    {
+      "data": "<style>...<style><!--...</style><!-- --></style>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,48): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "...<style><!--...",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "comment": " "
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style>...<style><!--...</style><!-- --></head><body></body></html>",
+        "noQuirksBodyHtml": "<style>...<style><!--...</style><!-- -->"
+      }
+    },
+    {
+      "data": "<style>...<!--[if IE]><style>...</style>X",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "...<!--[if IE]><style>...",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style>...<!--[if IE]><style>...</style></head><body>X</body></html>",
+        "noQuirksBodyHtml": "<style>...<!--[if IE]><style>...</style>X"
+      }
+    },
+    {
+      "data": "<title><!--<title></title>--></title>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,37): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "<!--<title>",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><title>&lt;!--&lt;title&gt;</title></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<title>&lt;!--&lt;title&gt;</title>--&gt;"
+      }
+    },
+    {
+      "data": "<title>&lt;/title></title>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "</title>",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><title>&lt;/title&gt;</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title>&lt;/title&gt;</title>"
+      }
+    },
+    {
+      "data": "<title>foo/title><link></head><body>X",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,37): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "foo/title><link></head><body>X",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><title>foo/title&gt;&lt;link&gt;&lt;/head&gt;&lt;body&gt;X</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title>foo/title&gt;&lt;link&gt;&lt;/head&gt;&lt;body&gt;X</title>"
+      }
+    },
+    {
+      "data": "<noscript><!--<noscript></noscript>--></noscript>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,49): unexpected-end-tag"
+      ],
+      "script": "on",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "text": "<!--<noscript>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><noscript><!--<noscript></noscript></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;!--&lt;noscript&gt;</noscript>--&gt;"
+      }
+    },
+    {
+      "data": "<noscript><!--<noscript></noscript>--></noscript>",
+      "errors": [
+        " * (1,11) missing DOCTYPE"
+      ],
+      "script": "off",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "comment": "<noscript></noscript>"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><noscript><!--<noscript></noscript>--></noscript></head><body></body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;!--&lt;noscript&gt;</noscript>--&gt;"
+      }
+    },
+    {
+      "data": "<noscript><!--</noscript>X<noscript>--></noscript>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag"
+      ],
+      "script": "on",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  },
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "text": "-->",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><noscript><!--</noscript></head><body>X<noscript>--></noscript></body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;!--</noscript>X<noscript>--&gt;</noscript>"
+      }
+    },
+    {
+      "data": "<noscript><!--</noscript>X<noscript>--></noscript>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag"
+      ],
+      "script": "off",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "comment": "</noscript>X<noscript>"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><noscript><!--</noscript>X<noscript>--></noscript></head><body></body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;!--</noscript>X<noscript>--&gt;</noscript>"
+      }
+    },
+    {
+      "data": "<noscript><iframe></noscript>X",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag"
+      ],
+      "script": "on",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "text": "<iframe>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><noscript><iframe></noscript></head><body>X</body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;iframe&gt;</noscript>X"
+      }
+    },
+    {
+      "data": "<noscript><iframe></noscript>X",
+      "errors": [
+        " * (1,11) missing DOCTYPE",
+        " * (1,19) unexpected token in head noscript",
+        " * (1,31) unexpected EOF"
+      ],
+      "script": "off",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true,
+            "iframe": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript"
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "iframe",
+                    "children": [
+                      {
+                        "text": "</noscript>X",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><noscript></noscript></head><body><iframe></noscript>X</iframe></body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;iframe&gt;</noscript>X"
+      }
+    },
+    {
+      "data": "<noframes><!--<noframes></noframes>--></noframes>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,49): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noframes": true,
+            "body": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noframes",
+                    "children": [
+                      {
+                        "text": "<!--<noframes>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><noframes><!--<noframes></noframes></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<noframes><!--<noframes></noframes>--&gt;"
+      }
+    },
+    {
+      "data": "<noframes><body><script><!--...</script></body></noframes></html>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noframes": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noframes",
+                    "children": [
+                      {
+                        "text": "<body><script><!--...</script></body>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><noframes><body><script><!--...</script></body></noframes></head><body></body></html>",
+        "noQuirksBodyHtml": "<noframes><body><script><!--...</script></body></noframes>"
+      }
+    },
+    {
+      "data": "<textarea><!--<textarea></textarea>--></textarea>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,49): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": "<!--<textarea>",
+                        "escaped": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><textarea>&lt;!--&lt;textarea&gt;</textarea>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<textarea>&lt;!--&lt;textarea&gt;</textarea>--&gt;"
+      }
+    },
+    {
+      "data": "<textarea>&lt;/textarea></textarea>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": "</textarea>",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><textarea>&lt;/textarea&gt;</textarea></body></html>",
+        "noQuirksBodyHtml": "<textarea>&lt;/textarea&gt;</textarea>"
+      }
+    },
+    {
+      "data": "<iframe><!--<iframe></iframe>--></iframe>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,41): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "iframe": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "iframe",
+                    "children": [
+                      {
+                        "text": "<!--<iframe>",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><iframe><!--<iframe></iframe>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<iframe><!--<iframe></iframe>--&gt;"
+      }
+    },
+    {
+      "data": "<iframe>...<!--X->...<!--/X->...</iframe>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "iframe": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "iframe",
+                    "children": [
+                      {
+                        "text": "...<!--X->...<!--/X->...",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><iframe>...<!--X->...<!--/X->...</iframe></body></html>",
+        "noQuirksBodyHtml": "<iframe>...<!--X->...<!--/X->...</iframe>"
+      }
+    },
+    {
+      "data": "<xmp><!--<xmp></xmp>--></xmp>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,29): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "xmp": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "xmp",
+                    "children": [
+                      {
+                        "text": "<!--<xmp>",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><xmp><!--<xmp></xmp>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<xmp><!--<xmp></xmp>--&gt;"
+      }
+    },
+    {
+      "data": "<noembed><!--<noembed></noembed>--></noembed>",
+      "errors": [
+        "(1,9): expected-doctype-but-got-start-tag",
+        "(1,45): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "noembed": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "noembed",
+                    "children": [
+                      {
+                        "text": "<!--<noembed>",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><noembed><!--<noembed></noembed>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<noembed><!--<noembed></noembed>--&gt;"
+      }
+    },
+    {
+      "data": "<!doctype html><table>\n",
+      "errors": [
+        "(2,0): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "text": "\n"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table>\n</table></body></html>",
+        "noQuirksBodyHtml": "<table>\n</table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><td><span><font></span><span>",
+      "errors": [
+        "(1,26): unexpected-cell-in-table-body",
+        "(1,45): unexpected-end-tag",
+        "(1,51): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "span": true,
+            "font": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "span",
+                                    "children": [
+                                      {
+                                        "tag": "font"
+                                      }
+                                    ]
+                                  },
+                                  {
+                                    "tag": "font",
+                                    "children": [
+                                      {
+                                        "tag": "span"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><span><font></font></span><font><span></span></font></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><span><font></font></span><font><span></span></font></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><form><table></form><form></table></form>",
+      "errors": [
+        "(1,35): unexpected-end-tag-implies-table-voodoo",
+        "(1,35): unexpected-end-tag",
+        "(1,41): unexpected-form-in-table",
+        "(1,56): unexpected-end-tag",
+        "(1,56): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "form": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "form",
+                    "children": [
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "tag": "form"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><form><table><form></form></table></form></body></html>",
+        "noQuirksBodyHtml": "<form><table><form></form></table></form>"
+      }
+    }
+  ],
+  "tests17.dat": [
+    {
+      "data": "<!doctype html><table><tbody><select><tr>",
+      "errors": [
+        "(1,37): unexpected-start-tag-implies-table-voodoo",
+        "(1,41): unexpected-table-element-start-tag-in-select-in-table",
+        "(1,41): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select><table><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<select></select><table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><tr><select><td>",
+      "errors": [
+        "(1,34): unexpected-start-tag-implies-table-voodoo",
+        "(1,38): unexpected-table-element-start-tag-in-select-in-table",
+        "(1,38): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select><table><tbody><tr><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<select></select><table><tbody><tr><td></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><tr><td><select><td>",
+      "errors": [
+        "(1,42): unexpected-table-element-start-tag-in-select-in-table",
+        "(1,42): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "select"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><select></select></td><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><select></select></td><td></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><tr><th><select><td>",
+      "errors": [
+        "(1,42): unexpected-table-element-start-tag-in-select-in-table",
+        "(1,42): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "th": true,
+            "select": true,
+            "td": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "th",
+                                "children": [
+                                  {
+                                    "tag": "select"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><th><select></select></th><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><th><select></select></th><td></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><caption><select><tr>",
+      "errors": [
+        "(1,43): unexpected-table-element-start-tag-in-select-in-table",
+        "(1,43): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "select": true,
+            "tbody": true,
+            "tr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "tag": "select"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><caption><select></select></caption><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><caption><select></select></caption><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><select><tr>",
+      "errors": [
+        "(1,27): unexpected-start-tag-in-select",
+        "(1,27): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select></body></html>",
+        "noQuirksBodyHtml": "<select></select>"
+      }
+    },
+    {
+      "data": "<!doctype html><select><td>",
+      "errors": [
+        "(1,27): unexpected-start-tag-in-select",
+        "(1,27): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select></body></html>",
+        "noQuirksBodyHtml": "<select></select>"
+      }
+    },
+    {
+      "data": "<!doctype html><select><th>",
+      "errors": [
+        "(1,27): unexpected-start-tag-in-select",
+        "(1,27): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select></body></html>",
+        "noQuirksBodyHtml": "<select></select>"
+      }
+    },
+    {
+      "data": "<!doctype html><select><tbody>",
+      "errors": [
+        "(1,30): unexpected-start-tag-in-select",
+        "(1,30): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select></body></html>",
+        "noQuirksBodyHtml": "<select></select>"
+      }
+    },
+    {
+      "data": "<!doctype html><select><thead>",
+      "errors": [
+        "(1,30): unexpected-start-tag-in-select",
+        "(1,30): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select></body></html>",
+        "noQuirksBodyHtml": "<select></select>"
+      }
+    },
+    {
+      "data": "<!doctype html><select><tfoot>",
+      "errors": [
+        "(1,30): unexpected-start-tag-in-select",
+        "(1,30): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select></body></html>",
+        "noQuirksBodyHtml": "<select></select>"
+      }
+    },
+    {
+      "data": "<!doctype html><select><caption>",
+      "errors": [
+        "(1,32): unexpected-start-tag-in-select",
+        "(1,32): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select></body></html>",
+        "noQuirksBodyHtml": "<select></select>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><tr></table>a",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "text": "a"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr></tr></tbody></table>a</body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr></tr></tbody></table>a"
+      }
+    }
+  ],
+  "tests18.dat": [
+    {
+      "data": "<!doctype html><plaintext></plaintext>",
+      "errors": [
+        "(1,38): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "plaintext": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "plaintext",
+                    "children": [
+                      {
+                        "text": "</plaintext>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><plaintext></plaintext></plaintext></body></html>",
+        "noQuirksBodyHtml": "<plaintext></plaintext></plaintext>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><plaintext></plaintext>",
+      "errors": [
+        "(1,33): foster-parenting-start-tag",
+        "(1,34): foster-parenting-character",
+        "(1,35): foster-parenting-character",
+        "(1,36): foster-parenting-character",
+        "(1,37): foster-parenting-character",
+        "(1,38): foster-parenting-character",
+        "(1,39): foster-parenting-character",
+        "(1,40): foster-parenting-character",
+        "(1,41): foster-parenting-character",
+        "(1,42): foster-parenting-character",
+        "(1,43): foster-parenting-character",
+        "(1,44): foster-parenting-character",
+        "(1,45): foster-parenting-character",
+        "(1,45): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "plaintext": true,
+            "table": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "plaintext",
+                    "children": [
+                      {
+                        "text": "</plaintext>",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><plaintext></plaintext></plaintext><table></table></body></html>",
+        "noQuirksBodyHtml": "<plaintext></plaintext></plaintext><table></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><tbody><plaintext></plaintext>",
+      "errors": [
+        "(1,40): foster-parenting-start-tag",
+        "(1,41): foster-parenting-character",
+        "(1,41): foster-parenting-character",
+        "(1,41): foster-parenting-character",
+        "(1,41): foster-parenting-character",
+        "(1,41): foster-parenting-character",
+        "(1,41): foster-parenting-character",
+        "(1,41): foster-parenting-character",
+        "(1,41): foster-parenting-character",
+        "(1,41): foster-parenting-character",
+        "(1,41): foster-parenting-character",
+        "(1,41): foster-parenting-character",
+        "(1,41): foster-parenting-character",
+        "(1,52): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "plaintext": true,
+            "table": true,
+            "tbody": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "plaintext",
+                    "children": [
+                      {
+                        "text": "</plaintext>",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><plaintext></plaintext></plaintext><table><tbody></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<plaintext></plaintext></plaintext><table><tbody></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><tbody><tr><plaintext></plaintext>",
+      "errors": [
+        "(1,44): foster-parenting-start-tag",
+        "(1,45): foster-parenting-character",
+        "(1,46): foster-parenting-character",
+        "(1,47): foster-parenting-character",
+        "(1,48): foster-parenting-character",
+        "(1,49): foster-parenting-character",
+        "(1,50): foster-parenting-character",
+        "(1,51): foster-parenting-character",
+        "(1,52): foster-parenting-character",
+        "(1,53): foster-parenting-character",
+        "(1,54): foster-parenting-character",
+        "(1,55): foster-parenting-character",
+        "(1,56): foster-parenting-character",
+        "(1,56): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "plaintext": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "plaintext",
+                    "children": [
+                      {
+                        "text": "</plaintext>",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><plaintext></plaintext></plaintext><table><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<plaintext></plaintext></plaintext><table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><td><plaintext></plaintext>",
+      "errors": [
+        "(1,26): unexpected-cell-in-table-body",
+        "(1,49): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "plaintext": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "plaintext",
+                                    "children": [
+                                      {
+                                        "text": "</plaintext>",
+                                        "no_escape": true
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><plaintext></plaintext></plaintext></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><plaintext></plaintext></plaintext></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><caption><plaintext></plaintext>",
+      "errors": [
+        "(1,54): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "plaintext": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "tag": "plaintext",
+                            "children": [
+                              {
+                                "text": "</plaintext>",
+                                "no_escape": true
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><caption><plaintext></plaintext></plaintext></caption></table></body></html>",
+        "noQuirksBodyHtml": "<table><caption><plaintext></plaintext></plaintext></caption></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><tr><style></script></style>abc",
+      "errors": [
+        "(1,51): foster-parenting-character",
+        "(1,52): foster-parenting-character",
+        "(1,53): foster-parenting-character",
+        "(1,53): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "style": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "abc"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "style",
+                                "children": [
+                                  {
+                                    "text": "</script>",
+                                    "no_escape": true
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>abc<table><tbody><tr><style></script></style></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "abc<table><tbody><tr><style></script></style></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><tr><script></style></script>abc",
+      "errors": [
+        "(1,52): foster-parenting-character",
+        "(1,53): foster-parenting-character",
+        "(1,54): foster-parenting-character",
+        "(1,54): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "script": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "abc"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "script",
+                                "children": [
+                                  {
+                                    "text": "</style>",
+                                    "no_escape": true
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>abc<table><tbody><tr><script></style></script></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "abc<table><tbody><tr><script></style></script></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><caption><style></script></style>abc",
+      "errors": [
+        "(1,58): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "style": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "tag": "style",
+                            "children": [
+                              {
+                                "text": "</script>",
+                                "no_escape": true
+                              }
+                            ]
+                          },
+                          {
+                            "text": "abc"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><caption><style></script></style>abc</caption></table></body></html>",
+        "noQuirksBodyHtml": "<table><caption><style></script></style>abc</caption></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><td><style></script></style>abc",
+      "errors": [
+        "(1,26): unexpected-cell-in-table-body",
+        "(1,53): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "style": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "style",
+                                    "children": [
+                                      {
+                                        "text": "</script>",
+                                        "no_escape": true
+                                      }
+                                    ]
+                                  },
+                                  {
+                                    "text": "abc"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><style></script></style>abc</td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><style></script></style>abc</td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><select><script></style></script>abc",
+      "errors": [
+        "(1,51): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "script": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "script",
+                        "children": [
+                          {
+                            "text": "</style>",
+                            "no_escape": true
+                          }
+                        ]
+                      },
+                      {
+                        "text": "abc"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select><script></style></script>abc</select></body></html>",
+        "noQuirksBodyHtml": "<select><script></style></script>abc</select>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><select><script></style></script>abc",
+      "errors": [
+        "(1,30): unexpected-start-tag-implies-table-voodoo",
+        "(1,58): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "script": true,
+            "table": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "script",
+                        "children": [
+                          {
+                            "text": "</style>",
+                            "no_escape": true
+                          }
+                        ]
+                      },
+                      {
+                        "text": "abc"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select><script></style></script>abc</select><table></table></body></html>",
+        "noQuirksBodyHtml": "<select><script></style></script>abc</select><table></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><tr><select><script></style></script>abc",
+      "errors": [
+        "(1,34): unexpected-start-tag-implies-table-voodoo",
+        "(1,62): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "script": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "script",
+                        "children": [
+                          {
+                            "text": "</style>",
+                            "no_escape": true
+                          }
+                        ]
+                      },
+                      {
+                        "text": "abc"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select><script></style></script>abc</select><table><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<select><script></style></script>abc</select><table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><frameset></frameset><noframes>abc",
+      "errors": [
+        "(1,49): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "noframes": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              },
+              {
+                "tag": "noframes",
+                "children": [
+                  {
+                    "text": "abc",
+                    "no_escape": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset><noframes>abc</noframes></html>",
+        "noQuirksBodyHtml": "<noframes>abc</noframes>"
+      }
+    },
+    {
+      "data": "<!doctype html><frameset></frameset><noframes>abc</noframes><!--abc-->",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "noframes": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              },
+              {
+                "tag": "noframes",
+                "children": [
+                  {
+                    "text": "abc",
+                    "no_escape": true
+                  }
+                ]
+              },
+              {
+                "comment": "abc"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset><noframes>abc</noframes><!--abc--></html>",
+        "noQuirksBodyHtml": "<noframes>abc</noframes><!--abc-->"
+      }
+    },
+    {
+      "data": "<!doctype html><frameset></frameset></html><noframes>abc",
+      "errors": [
+        "(1,56): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "noframes": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              },
+              {
+                "tag": "noframes",
+                "children": [
+                  {
+                    "text": "abc",
+                    "no_escape": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset><noframes>abc</noframes></html>",
+        "noQuirksBodyHtml": "<noframes>abc</noframes>"
+      }
+    },
+    {
+      "data": "<!doctype html><frameset></frameset></html><noframes>abc</noframes><!--abc-->",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "noframes": true
+          },
+          "doctype": true,
+          "no_escape": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              },
+              {
+                "tag": "noframes",
+                "children": [
+                  {
+                    "text": "abc",
+                    "no_escape": true
+                  }
+                ]
+              }
+            ]
+          },
+          {
+            "comment": "abc"
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset><noframes>abc</noframes></html><!--abc-->",
+        "noQuirksBodyHtml": "<noframes>abc</noframes><!--abc-->"
+      }
+    },
+    {
+      "data": "<!doctype html><table><tr></tbody><tfoot>",
+      "errors": [
+        "(1,41): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "tfoot": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "tfoot"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr></tr></tbody><tfoot></tfoot></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr></tr></tbody><tfoot></tfoot></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><td><svg></svg>abc<td>",
+      "errors": [
+        "(1,26): unexpected-cell-in-table-body",
+        "(1,44): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "svg svg": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "svg",
+                                    "ns": "http://www.w3.org/2000/svg"
+                                  },
+                                  {
+                                    "text": "abc"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><svg></svg>abc</td><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><svg></svg>abc</td><td></td></tr></tbody></table>"
+      }
+    }
+  ],
+  "tests19.dat": [
+    {
+      "data": "<!doctype html><math><mn DefinitionUrl=\"foo\">",
+      "errors": [
+        "(1,45): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mn": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mn",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "attrs": [
+                          {
+                            "name": "definitionURL",
+                            "value": "foo"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mn definitionURL=\"foo\"></mn></math></body></html>",
+        "noQuirksBodyHtml": "<math><mn definitionURL=\"foo\"></mn></math>"
+      }
+    },
+    {
+      "data": "<!doctype html><html></p><!--foo-->",
+      "errors": [
+        "(1,25): end-tag-after-implied-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "comment": "foo"
+              },
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><!--foo--><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<p></p><!--foo-->"
+      }
+    },
+    {
+      "data": "<!doctype html><head></head></p><!--foo-->",
+      "errors": [
+        "(1,32): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "comment": "foo"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><!--foo--><body></body></html>",
+        "noQuirksBodyHtml": "<p></p><!--foo-->"
+      }
+    },
+    {
+      "data": "<!doctype html><body><p><pre>",
+      "errors": [
+        "(1,29): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "pre": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "pre"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p></p><pre></pre></body></html>",
+        "noQuirksBodyHtml": "<p></p><pre></pre>"
+      }
+    },
+    {
+      "data": "<!doctype html><body><p><listing>",
+      "errors": [
+        "(1,33): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "listing": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "listing"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p></p><listing></listing></body></html>",
+        "noQuirksBodyHtml": "<p></p><listing></listing>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><plaintext>",
+      "errors": [
+        "(1,29): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "plaintext": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "plaintext"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p></p><plaintext></plaintext></body></html>",
+        "noQuirksBodyHtml": "<p></p><plaintext></plaintext>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><h1>",
+      "errors": [
+        "(1,22): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "h1": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "h1"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p></p><h1></h1></body></html>",
+        "noQuirksBodyHtml": "<p></p><h1></h1>"
+      }
+    },
+    {
+      "data": "<!doctype html><form><isindex>",
+      "errors": [
+        "(1,30): deprecated-tag",
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "form": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "form"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><form></form></body></html>",
+        "noQuirksBodyHtml": "<form></form>"
+      }
+    },
+    {
+      "data": "<!doctype html><isindex action=\"POST\">",
+      "errors": [
+        "(1,38): deprecated-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "form": true,
+            "hr": true,
+            "label": true,
+            "input": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "form",
+                    "attrs": [
+                      {
+                        "name": "action",
+                        "value": "POST"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "hr"
+                      },
+                      {
+                        "tag": "label",
+                        "children": [
+                          {
+                            "text": "This is a searchable index. Enter search keywords: "
+                          },
+                          {
+                            "tag": "input",
+                            "attrs": [
+                              {
+                                "name": "name",
+                                "value": "isindex"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "hr"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><form action=\"POST\"><hr><label>This is a searchable index. Enter search keywords: <input name=\"isindex\"></label><hr></form></body></html>",
+        "noQuirksBodyHtml": "<form action=\"POST\"><hr><label>This is a searchable index. Enter search keywords: <input name=\"isindex\"></label><hr></form>"
+      }
+    },
+    {
+      "data": "<!doctype html><isindex prompt=\"this is isindex\">",
+      "errors": [
+        "(1,49): deprecated-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "form": true,
+            "hr": true,
+            "label": true,
+            "input": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "form",
+                    "children": [
+                      {
+                        "tag": "hr"
+                      },
+                      {
+                        "tag": "label",
+                        "children": [
+                          {
+                            "text": "this is isindex"
+                          },
+                          {
+                            "tag": "input",
+                            "attrs": [
+                              {
+                                "name": "name",
+                                "value": "isindex"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "hr"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><form><hr><label>this is isindex<input name=\"isindex\"></label><hr></form></body></html>",
+        "noQuirksBodyHtml": "<form><hr><label>this is isindex<input name=\"isindex\"></label><hr></form>"
+      }
+    },
+    {
+      "data": "<!doctype html><isindex type=\"hidden\">",
+      "errors": [
+        "(1,38): deprecated-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "form": true,
+            "hr": true,
+            "label": true,
+            "input": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "form",
+                    "children": [
+                      {
+                        "tag": "hr"
+                      },
+                      {
+                        "tag": "label",
+                        "children": [
+                          {
+                            "text": "This is a searchable index. Enter search keywords: "
+                          },
+                          {
+                            "tag": "input",
+                            "attrs": [
+                              {
+                                "name": "name",
+                                "value": "isindex"
+                              },
+                              {
+                                "name": "type",
+                                "value": "hidden"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "hr"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><form><hr><label>This is a searchable index. Enter search keywords: <input name=\"isindex\" type=\"hidden\"></label><hr></form></body></html>",
+        "noQuirksBodyHtml": "<form><hr><label>This is a searchable index. Enter search keywords: <input name=\"isindex\" type=\"hidden\"></label><hr></form>"
+      }
+    },
+    {
+      "data": "<!doctype html><isindex name=\"foo\">",
+      "errors": [
+        "(1,35): deprecated-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "form": true,
+            "hr": true,
+            "label": true,
+            "input": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "form",
+                    "children": [
+                      {
+                        "tag": "hr"
+                      },
+                      {
+                        "tag": "label",
+                        "children": [
+                          {
+                            "text": "This is a searchable index. Enter search keywords: "
+                          },
+                          {
+                            "tag": "input",
+                            "attrs": [
+                              {
+                                "name": "name",
+                                "value": "isindex"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "hr"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><form><hr><label>This is a searchable index. Enter search keywords: <input name=\"isindex\"></label><hr></form></body></html>",
+        "noQuirksBodyHtml": "<form><hr><label>This is a searchable index. Enter search keywords: <input name=\"isindex\"></label><hr></form>"
+      }
+    },
+    {
+      "data": "<!doctype html><ruby><p><rp>",
+      "errors": [
+        "(1,28): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "p": true,
+            "rp": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "tag": "p"
+                      },
+                      {
+                        "tag": "rp"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><ruby><p></p><rp></rp></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby><p></p><rp></rp></ruby>"
+      }
+    },
+    {
+      "data": "<!doctype html><ruby><div><span><rp>",
+      "errors": [
+        "(1,36): XXX-undefined-error",
+        "(1,36): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "div": true,
+            "span": true,
+            "rp": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "tag": "span",
+                            "children": [
+                              {
+                                "tag": "rp"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><ruby><div><span><rp></rp></span></div></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby><div><span><rp></rp></span></div></ruby>"
+      }
+    },
+    {
+      "data": "<!doctype html><ruby><div><p><rp>",
+      "errors": [
+        "(1,33): XXX-undefined-error",
+        "(1,33): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "div": true,
+            "p": true,
+            "rp": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "tag": "p"
+                          },
+                          {
+                            "tag": "rp"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><ruby><div><p></p><rp></rp></div></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby><div><p></p><rp></rp></div></ruby>"
+      }
+    },
+    {
+      "data": "<!doctype html><ruby><p><rt>",
+      "errors": [
+        "(1,28): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "p": true,
+            "rt": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "tag": "p"
+                      },
+                      {
+                        "tag": "rt"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><ruby><p></p><rt></rt></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby><p></p><rt></rt></ruby>"
+      }
+    },
+    {
+      "data": "<!doctype html><ruby><div><span><rt>",
+      "errors": [
+        "(1,36): XXX-undefined-error",
+        "(1,36): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "div": true,
+            "span": true,
+            "rt": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "tag": "span",
+                            "children": [
+                              {
+                                "tag": "rt"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><ruby><div><span><rt></rt></span></div></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby><div><span><rt></rt></span></div></ruby>"
+      }
+    },
+    {
+      "data": "<!doctype html><ruby><div><p><rt>",
+      "errors": [
+        "(1,33): XXX-undefined-error",
+        "(1,33): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "div": true,
+            "p": true,
+            "rt": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "tag": "p"
+                          },
+                          {
+                            "tag": "rt"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><ruby><div><p></p><rt></rt></div></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby><div><p></p><rt></rt></div></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rb>b<rt></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rb": true,
+            "rt": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rb",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rt"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rb>b</rb><rt></rt></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rb>b</rb><rt></rt></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rp>b<rt></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rp": true,
+            "rt": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rp",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rt"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rp>b</rp><rt></rt></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rp>b</rp><rt></rt></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rt>b<rt></ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rt": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rt",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rt"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rt>b</rt><rt></rt></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rt>b</rt><rt></rt></ruby>"
+      }
+    },
+    {
+      "data": "<html><ruby>a<rtc>b<rt>c<rb>d</ruby></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "rtc": true,
+            "rt": true,
+            "rb": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "rtc",
+                        "children": [
+                          {
+                            "text": "b"
+                          },
+                          {
+                            "tag": "rt",
+                            "children": [
+                              {
+                                "text": "c"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "rb",
+                        "children": [
+                          {
+                            "text": "d"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby>a<rtc>b<rt>c</rt></rtc><rb>d</rb></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby>a<rtc>b<rt>c</rt></rtc><rb>d</rb></ruby>"
+      }
+    },
+    {
+      "data": "<!doctype html><math/><foo>",
+      "errors": [
+        "(1,27): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "foo": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML"
+                  },
+                  {
+                    "tag": "foo"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math></math><foo></foo></body></html>",
+        "noQuirksBodyHtml": "<math></math><foo></foo>"
+      }
+    },
+    {
+      "data": "<!doctype html><svg/><foo>",
+      "errors": [
+        "(1,26): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "foo": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  },
+                  {
+                    "tag": "foo"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg></svg><foo></foo></body></html>",
+        "noQuirksBodyHtml": "<svg></svg><foo></foo>"
+      }
+    },
+    {
+      "data": "<!doctype html><div></body><!--foo-->",
+      "errors": [
+        "(1,27): expected-one-end-tag-but-got-another"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div"
+                  }
+                ]
+              },
+              {
+                "comment": "foo"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><div></div></body><!--foo--></html>",
+        "noQuirksBodyHtml": "<div><!--foo--></div>"
+      }
+    },
+    {
+      "data": "<!doctype html><h1><div><h3><span></h1>foo",
+      "errors": [
+        "(1,39): end-tag-too-early",
+        "(1,42): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "h1": true,
+            "div": true,
+            "h3": true,
+            "span": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "h1",
+                    "children": [
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "tag": "h3",
+                            "children": [
+                              {
+                                "tag": "span"
+                              }
+                            ]
+                          },
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><h1><div><h3><span></span></h3>foo</div></h1></body></html>",
+        "noQuirksBodyHtml": "<h1><div><h3><span></span></h3>foo</div></h1>"
+      }
+    },
+    {
+      "data": "<!doctype html><p></h3>foo",
+      "errors": [
+        "(1,23): end-tag-too-early"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p>foo</p></body></html>",
+        "noQuirksBodyHtml": "<p>foo</p>"
+      }
+    },
+    {
+      "data": "<!doctype html><h3><li>abc</h2>foo",
+      "errors": [
+        "(1,31): end-tag-too-early"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "h3": true,
+            "li": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "h3",
+                    "children": [
+                      {
+                        "tag": "li",
+                        "children": [
+                          {
+                            "text": "abc"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "text": "foo"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><h3><li>abc</li></h3>foo</body></html>",
+        "noQuirksBodyHtml": "<h3><li>abc</li></h3>foo"
+      }
+    },
+    {
+      "data": "<!doctype html><table>abc<!--foo-->",
+      "errors": [
+        "(1,23): foster-parenting-character",
+        "(1,24): foster-parenting-character",
+        "(1,25): foster-parenting-character",
+        "(1,35): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "abc"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "comment": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>abc<table><!--foo--></table></body></html>",
+        "noQuirksBodyHtml": "abc<table><!--foo--></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table>  <!--foo-->",
+      "errors": [
+        "(1,34): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "text": "  "
+                      },
+                      {
+                        "comment": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table>  <!--foo--></table></body></html>",
+        "noQuirksBodyHtml": "<table>  <!--foo--></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table> b <!--foo-->",
+      "errors": [
+        "(1,23): foster-parenting-character",
+        "(1,24): foster-parenting-character",
+        "(1,25): foster-parenting-character",
+        "(1,35): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": " b "
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "comment": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body> b <table><!--foo--></table></body></html>",
+        "noQuirksBodyHtml": " b <table><!--foo--></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><select><option><option>",
+      "errors": [
+        "(1,39): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option"
+                      },
+                      {
+                        "tag": "option"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select><option></option><option></option></select></body></html>",
+        "noQuirksBodyHtml": "<select><option></option><option></option></select>"
+      }
+    },
+    {
+      "data": "<!doctype html><select><option></optgroup>",
+      "errors": [
+        "(1,42): unexpected-end-tag-in-select",
+        "(1,42): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select><option></option></select></body></html>",
+        "noQuirksBodyHtml": "<select><option></option></select>"
+      }
+    },
+    {
+      "data": "<!doctype html><select><option></optgroup>",
+      "errors": [
+        "(1,42): unexpected-end-tag-in-select",
+        "(1,42): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select><option></option></select></body></html>",
+        "noQuirksBodyHtml": "<select><option></option></select>"
+      }
+    },
+    {
+      "data": "<!doctype html><dd><optgroup><dd>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "dd": true,
+            "optgroup": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "dd",
+                    "children": [
+                      {
+                        "tag": "optgroup"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "dd"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><dd><optgroup></optgroup></dd><dd></dd></body></html>",
+        "noQuirksBodyHtml": "<dd><optgroup></optgroup></dd><dd></dd>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><math><mi><p><h1>",
+      "errors": [
+        "(1,35): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "math math": true,
+            "math mi": true,
+            "h1": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "math",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "mi",
+                            "ns": "http://www.w3.org/1998/Math/MathML",
+                            "children": [
+                              {
+                                "tag": "p"
+                              },
+                              {
+                                "tag": "h1"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><math><mi><p></p><h1></h1></mi></math></p></body></html>",
+        "noQuirksBodyHtml": "<p><math><mi><p></p><h1></h1></mi></math></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><math><mo><p><h1>",
+      "errors": [
+        "(1,35): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "math math": true,
+            "math mo": true,
+            "h1": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "math",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "mo",
+                            "ns": "http://www.w3.org/1998/Math/MathML",
+                            "children": [
+                              {
+                                "tag": "p"
+                              },
+                              {
+                                "tag": "h1"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><math><mo><p></p><h1></h1></mo></math></p></body></html>",
+        "noQuirksBodyHtml": "<p><math><mo><p></p><h1></h1></mo></math></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><math><mn><p><h1>",
+      "errors": [
+        "(1,35): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "math math": true,
+            "math mn": true,
+            "h1": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "math",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "mn",
+                            "ns": "http://www.w3.org/1998/Math/MathML",
+                            "children": [
+                              {
+                                "tag": "p"
+                              },
+                              {
+                                "tag": "h1"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><math><mn><p></p><h1></h1></mn></math></p></body></html>",
+        "noQuirksBodyHtml": "<p><math><mn><p></p><h1></h1></mn></math></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><math><ms><p><h1>",
+      "errors": [
+        "(1,35): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "math math": true,
+            "math ms": true,
+            "h1": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "math",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "ms",
+                            "ns": "http://www.w3.org/1998/Math/MathML",
+                            "children": [
+                              {
+                                "tag": "p"
+                              },
+                              {
+                                "tag": "h1"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><math><ms><p></p><h1></h1></ms></math></p></body></html>",
+        "noQuirksBodyHtml": "<p><math><ms><p></p><h1></h1></ms></math></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><math><mtext><p><h1>",
+      "errors": [
+        "(1,38): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "math math": true,
+            "math mtext": true,
+            "h1": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "math",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "mtext",
+                            "ns": "http://www.w3.org/1998/Math/MathML",
+                            "children": [
+                              {
+                                "tag": "p"
+                              },
+                              {
+                                "tag": "h1"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><math><mtext><p></p><h1></h1></mtext></math></p></body></html>",
+        "noQuirksBodyHtml": "<p><math><mtext><p></p><h1></h1></mtext></math></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><frameset></noframes>",
+      "errors": [
+        "(1,36): unexpected-end-tag-in-frameset",
+        "(1,36): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!doctype html><html c=d><body></html><html a=b>",
+      "errors": [
+        "(1,48): non-html-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "attrs": [
+              {
+                "name": "a",
+                "value": "b"
+              },
+              {
+                "name": "c",
+                "value": "d"
+              }
+            ],
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html c=\"d\" a=\"b\"><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!doctype html><html c=d><frameset></frameset></html><html a=b>",
+      "errors": [
+        "(1,63): non-html-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "attrs": [
+              {
+                "name": "a",
+                "value": "b"
+              },
+              {
+                "name": "c",
+                "value": "d"
+              }
+            ],
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html c=\"d\" a=\"b\"><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!doctype html><html><frameset></frameset></html><!--foo-->",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          },
+          {
+            "comment": "foo"
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset></html><!--foo-->",
+        "noQuirksBodyHtml": "<!--foo-->"
+      }
+    },
+    {
+      "data": "<!doctype html><html><frameset></frameset></html>  ",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              },
+              {
+                "text": "  "
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset>  </html>",
+        "noQuirksBodyHtml": "  "
+      }
+    },
+    {
+      "data": "<!doctype html><html><frameset></frameset></html>abc",
+      "errors": [
+        "(1,50): expected-eof-but-got-char",
+        "(1,51): expected-eof-but-got-char",
+        "(1,52): expected-eof-but-got-char"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "abc"
+      }
+    },
+    {
+      "data": "<!doctype html><html><frameset></frameset></html><p>",
+      "errors": [
+        "(1,52): expected-eof-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<p></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><html><frameset></frameset></html></p>",
+      "errors": [
+        "(1,53): expected-eof-but-got-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<p></p>"
+      }
+    },
+    {
+      "data": "<html><frameset></frameset></html><!doctype html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,49): unexpected-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!doctype html><body><frameset>",
+      "errors": [
+        "(1,31): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!doctype html><p><frameset><frame>",
+      "errors": [
+        "(1,28): unexpected-start-tag",
+        "(1,35): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "frame": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset",
+                "children": [
+                  {
+                    "tag": "frame"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset><frame></frameset></html>",
+        "noQuirksBodyHtml": "<p></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p>a<frameset>",
+      "errors": [
+        "(1,29): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "a"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p>a</p></body></html>",
+        "noQuirksBodyHtml": "<p>a</p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p> <frameset><frame>",
+      "errors": [
+        "(1,29): unexpected-start-tag",
+        "(1,36): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "frame": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset",
+                "children": [
+                  {
+                    "tag": "frame"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset><frame></frameset></html>",
+        "noQuirksBodyHtml": "<p> </p>"
+      }
+    },
+    {
+      "data": "<!doctype html><pre><frameset>",
+      "errors": [
+        "(1,30): unexpected-start-tag",
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "pre": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "pre"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><pre></pre></body></html>",
+        "noQuirksBodyHtml": "<pre></pre>"
+      }
+    },
+    {
+      "data": "<!doctype html><listing><frameset>",
+      "errors": [
+        "(1,34): unexpected-start-tag",
+        "(1,34): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "listing": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "listing"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><listing></listing></body></html>",
+        "noQuirksBodyHtml": "<listing></listing>"
+      }
+    },
+    {
+      "data": "<!doctype html><li><frameset>",
+      "errors": [
+        "(1,29): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "li": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "li"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><li></li></body></html>",
+        "noQuirksBodyHtml": "<li></li>"
+      }
+    },
+    {
+      "data": "<!doctype html><dd><frameset>",
+      "errors": [
+        "(1,29): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "dd": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "dd"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><dd></dd></body></html>",
+        "noQuirksBodyHtml": "<dd></dd>"
+      }
+    },
+    {
+      "data": "<!doctype html><dt><frameset>",
+      "errors": [
+        "(1,29): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "dt": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "dt"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><dt></dt></body></html>",
+        "noQuirksBodyHtml": "<dt></dt>"
+      }
+    },
+    {
+      "data": "<!doctype html><button><frameset>",
+      "errors": [
+        "(1,33): unexpected-start-tag",
+        "(1,33): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "button": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "button"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><button></button></body></html>",
+        "noQuirksBodyHtml": "<button></button>"
+      }
+    },
+    {
+      "data": "<!doctype html><applet><frameset>",
+      "errors": [
+        "(1,33): unexpected-start-tag",
+        "(1,33): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "applet": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "applet"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><applet></applet></body></html>",
+        "noQuirksBodyHtml": "<applet></applet>"
+      }
+    },
+    {
+      "data": "<!doctype html><marquee><frameset>",
+      "errors": [
+        "(1,34): unexpected-start-tag",
+        "(1,34): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "marquee": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "marquee"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><marquee></marquee></body></html>",
+        "noQuirksBodyHtml": "<marquee></marquee>"
+      }
+    },
+    {
+      "data": "<!doctype html><object><frameset>",
+      "errors": [
+        "(1,33): unexpected-start-tag",
+        "(1,33): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "object": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "object"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><object></object></body></html>",
+        "noQuirksBodyHtml": "<object></object>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><frameset>",
+      "errors": [
+        "(1,32): unexpected-start-tag-implies-table-voodoo",
+        "(1,32): unexpected-start-tag",
+        "(1,32): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table></table></body></html>",
+        "noQuirksBodyHtml": "<table></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><area><frameset>",
+      "errors": [
+        "(1,31): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "area": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "area"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><area></body></html>",
+        "noQuirksBodyHtml": "<area>"
+      }
+    },
+    {
+      "data": "<!doctype html><basefont><frameset>",
+      "errors": [
+        "(1,35): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "basefont": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "basefont"
+                  }
+                ]
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><basefont></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<basefont>"
+      }
+    },
+    {
+      "data": "<!doctype html><bgsound><frameset>",
+      "errors": [
+        "(1,34): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "bgsound": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "bgsound"
+                  }
+                ]
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><bgsound></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<bgsound>"
+      }
+    },
+    {
+      "data": "<!doctype html><br><frameset>",
+      "errors": [
+        "(1,29): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "br": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "br"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><br></body></html>",
+        "noQuirksBodyHtml": "<br>"
+      }
+    },
+    {
+      "data": "<!doctype html><embed><frameset>",
+      "errors": [
+        "(1,32): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "embed": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "embed"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><embed></body></html>",
+        "noQuirksBodyHtml": "<embed>"
+      }
+    },
+    {
+      "data": "<!doctype html><img><frameset>",
+      "errors": [
+        "(1,30): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "img": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "img"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><img></body></html>",
+        "noQuirksBodyHtml": "<img>"
+      }
+    },
+    {
+      "data": "<!doctype html><input><frameset>",
+      "errors": [
+        "(1,32): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "input": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "input"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><input></body></html>",
+        "noQuirksBodyHtml": "<input>"
+      }
+    },
+    {
+      "data": "<!doctype html><keygen><frameset>",
+      "errors": [
+        "(1,33): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "keygen": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "keygen"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><keygen></body></html>",
+        "noQuirksBodyHtml": "<keygen>"
+      }
+    },
+    {
+      "data": "<!doctype html><wbr><frameset>",
+      "errors": [
+        "(1,30): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "wbr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "wbr"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><wbr></body></html>",
+        "noQuirksBodyHtml": "<wbr>"
+      }
+    },
+    {
+      "data": "<!doctype html><hr><frameset>",
+      "errors": [
+        "(1,29): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "hr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "hr"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><hr></body></html>",
+        "noQuirksBodyHtml": "<hr>"
+      }
+    },
+    {
+      "data": "<!doctype html><textarea></textarea><frameset>",
+      "errors": [
+        "(1,46): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><textarea></textarea></body></html>",
+        "noQuirksBodyHtml": "<textarea></textarea>"
+      }
+    },
+    {
+      "data": "<!doctype html><xmp></xmp><frameset>",
+      "errors": [
+        "(1,36): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "xmp": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "xmp"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><xmp></xmp></body></html>",
+        "noQuirksBodyHtml": "<xmp></xmp>"
+      }
+    },
+    {
+      "data": "<!doctype html><iframe></iframe><frameset>",
+      "errors": [
+        "(1,42): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "iframe": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "iframe"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><iframe></iframe></body></html>",
+        "noQuirksBodyHtml": "<iframe></iframe>"
+      }
+    },
+    {
+      "data": "<!doctype html><select></select><frameset>",
+      "errors": [
+        "(1,42): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select></body></html>",
+        "noQuirksBodyHtml": "<select></select>"
+      }
+    },
+    {
+      "data": "<!doctype html><svg></svg><frameset><frame>",
+      "errors": [
+        "(1,36): unexpected-start-tag",
+        "(1,43): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "frame": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset",
+                "children": [
+                  {
+                    "tag": "frame"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset><frame></frameset></html>",
+        "noQuirksBodyHtml": "<svg></svg>"
+      }
+    },
+    {
+      "data": "<!doctype html><math></math><frameset><frame>",
+      "errors": [
+        "(1,38): unexpected-start-tag",
+        "(1,45): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "frame": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset",
+                "children": [
+                  {
+                    "tag": "frame"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset><frame></frameset></html>",
+        "noQuirksBodyHtml": "<math></math>"
+      }
+    },
+    {
+      "data": "<!doctype html><svg><foreignObject><div> <frameset><frame>",
+      "errors": [
+        "(1,51): unexpected-start-tag",
+        "(1,58): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "frame": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset",
+                "children": [
+                  {
+                    "tag": "frame"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset><frame></frameset></html>",
+        "noQuirksBodyHtml": "<svg><foreignObject><div> </div></foreignObject></svg>"
+      }
+    },
+    {
+      "data": "<!doctype html><svg>a</svg><frameset><frame>",
+      "errors": [
+        "(1,37): unexpected-start-tag",
+        "(1,44): unexpected-start-tag-ignored"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "a"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg>a</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>a</svg>"
+      }
+    },
+    {
+      "data": "<!doctype html><svg> </svg><frameset><frame>",
+      "errors": [
+        "(1,37): unexpected-start-tag",
+        "(1,44): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "frame": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset",
+                "children": [
+                  {
+                    "tag": "frame"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset><frame></frameset></html>",
+        "noQuirksBodyHtml": "<svg> </svg>"
+      }
+    },
+    {
+      "data": "<html>aaa<frameset></frameset>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,19): unexpected-start-tag",
+        "(1,30): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "aaa"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>aaa</body></html>",
+        "noQuirksBodyHtml": "aaa"
+      }
+    },
+    {
+      "data": "<html> a <frameset></frameset>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,19): unexpected-start-tag",
+        "(1,30): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "a "
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>a </body></html>",
+        "noQuirksBodyHtml": " a "
+      }
+    },
+    {
+      "data": "<!doctype html><div><frameset>",
+      "errors": [
+        "(1,30): unexpected-start-tag",
+        "(1,30): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<!doctype html><div><body><frameset>",
+      "errors": [
+        "(1,26): unexpected-start-tag",
+        "(1,36): unexpected-start-tag",
+        "(1,36): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><div></div></body></html>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><math></p>a",
+      "errors": [
+        "(1,28): unexpected-end-tag",
+        "(1,28): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "math math": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "math",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      }
+                    ]
+                  },
+                  {
+                    "text": "a"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><math></math></p>a</body></html>",
+        "noQuirksBodyHtml": "<p><math></math></p>a"
+      }
+    },
+    {
+      "data": "<!doctype html><p><math><mn><span></p>a",
+      "errors": [
+        "(1,38): unexpected-end-tag",
+        "(1,39): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "math math": true,
+            "math mn": true,
+            "span": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "math",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "mn",
+                            "ns": "http://www.w3.org/1998/Math/MathML",
+                            "children": [
+                              {
+                                "tag": "span",
+                                "children": [
+                                  {
+                                    "tag": "p"
+                                  },
+                                  {
+                                    "text": "a"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><math><mn><span><p></p>a</span></mn></math></p></body></html>",
+        "noQuirksBodyHtml": "<p><math><mn><span><p></p>a</span></mn></math></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><math></html>",
+      "errors": [
+        "(1,28): unexpected-end-tag",
+        "(1,28): expected-one-end-tag-but-got-another",
+        "(1,28): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math></math></body></html>",
+        "noQuirksBodyHtml": "<math></math>"
+      }
+    },
+    {
+      "data": "<!doctype html><meta charset=\"ascii\">",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "meta": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "meta",
+                    "attrs": [
+                      {
+                        "name": "charset",
+                        "value": "ascii"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><meta charset=\"ascii\"></head><body></body></html>",
+        "noQuirksBodyHtml": "<meta charset=\"ascii\">"
+      }
+    },
+    {
+      "data": "<!doctype html><meta http-equiv=\"content-type\" content=\"text/html;charset=ascii\">",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "meta": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "meta",
+                    "attrs": [
+                      {
+                        "name": "content",
+                        "value": "text/html;charset=ascii"
+                      },
+                      {
+                        "name": "http-equiv",
+                        "value": "content-type"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><meta http-equiv=\"content-type\" content=\"text/html;charset=ascii\"></head><body></body></html>",
+        "noQuirksBodyHtml": "<meta http-equiv=\"content-type\" content=\"text/html;charset=ascii\">"
+      }
+    },
+    {
+      "data": "<!doctype html><head><!--aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--><meta charset=\"utf8\">",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "meta": true,
+            "body": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "comment": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+                  },
+                  {
+                    "tag": "meta",
+                    "attrs": [
+                      {
+                        "name": "charset",
+                        "value": "utf8"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><!--aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--><meta charset=\"utf8\"></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--><meta charset=\"utf8\">"
+      }
+    },
+    {
+      "data": "<!doctype html><html a=b><head></head><html c=d>",
+      "errors": [
+        "(1,48): non-html-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "attrs": [
+              {
+                "name": "a",
+                "value": "b"
+              },
+              {
+                "name": "c",
+                "value": "d"
+              }
+            ],
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html a=\"b\" c=\"d\"><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!doctype html><image/>",
+      "errors": [
+        "(1,23): image-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "img": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "img"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><img></body></html>",
+        "noQuirksBodyHtml": "<img>"
+      }
+    },
+    {
+      "data": "<!doctype html>a<i>b<table>c<b>d</i>e</b>f",
+      "errors": [
+        "(1,28): foster-parenting-character",
+        "(1,31): foster-parenting-start-tag",
+        "(1,32): foster-parenting-character",
+        "(1,36): foster-parenting-end-tag",
+        "(1,36): adoption-agency-1.3",
+        "(1,37): foster-parenting-character",
+        "(1,41): foster-parenting-end-tag",
+        "(1,42): foster-parenting-character",
+        "(1,42): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "i": true,
+            "b": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "a"
+                  },
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "text": "bc"
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "de"
+                          }
+                        ]
+                      },
+                      {
+                        "text": "f"
+                      },
+                      {
+                        "tag": "table"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>a<i>bc<b>de</b>f<table></table></i></body></html>",
+        "noQuirksBodyHtml": "a<i>bc<b>de</b>f<table></table></i>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><i>a<b>b<div>c<a>d</i>e</b>f",
+      "errors": [
+        "(1,25): foster-parenting-start-tag",
+        "(1,26): foster-parenting-character",
+        "(1,29): foster-parenting-start-tag",
+        "(1,30): foster-parenting-character",
+        "(1,35): foster-parenting-start-tag",
+        "(1,36): foster-parenting-character",
+        "(1,39): foster-parenting-start-tag",
+        "(1,40): foster-parenting-character",
+        "(1,44): foster-parenting-end-tag",
+        "(1,44): adoption-agency-1.3",
+        "(1,44): adoption-agency-1.3",
+        "(1,45): foster-parenting-character",
+        "(1,49): foster-parenting-end-tag",
+        "(1,49): adoption-agency-1.3",
+        "(1,49): adoption-agency-1.3",
+        "(1,50): foster-parenting-character",
+        "(1,50): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "i": true,
+            "b": true,
+            "div": true,
+            "a": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b"
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": "c"
+                              },
+                              {
+                                "tag": "a",
+                                "children": [
+                                  {
+                                    "text": "d"
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "a",
+                            "children": [
+                              {
+                                "text": "e"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "text": "f"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><i>a<b>b</b></i><b></b><div><b><i>c<a>d</a></i><a>e</a></b><a>f</a></div><table></table></body></html>",
+        "noQuirksBodyHtml": "<i>a<b>b</b></i><b></b><div><b><i>c<a>d</a></i><a>e</a></b><a>f</a></div><table></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><i>a<b>b<div>c<a>d</i>e</b>f",
+      "errors": [
+        "(1,37): adoption-agency-1.3",
+        "(1,37): adoption-agency-1.3",
+        "(1,42): adoption-agency-1.3",
+        "(1,42): adoption-agency-1.3",
+        "(1,43): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "i": true,
+            "b": true,
+            "div": true,
+            "a": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b"
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": "c"
+                              },
+                              {
+                                "tag": "a",
+                                "children": [
+                                  {
+                                    "text": "d"
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "a",
+                            "children": [
+                              {
+                                "text": "e"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "text": "f"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><i>a<b>b</b></i><b></b><div><b><i>c<a>d</a></i><a>e</a></b><a>f</a></div></body></html>",
+        "noQuirksBodyHtml": "<i>a<b>b</b></i><b></b><div><b><i>c<a>d</a></i><a>e</a></b><a>f</a></div>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><i>a<b>b<div>c</i>",
+      "errors": [
+        "(1,25): foster-parenting-start-tag",
+        "(1,26): foster-parenting-character",
+        "(1,29): foster-parenting-start-tag",
+        "(1,30): foster-parenting-character",
+        "(1,35): foster-parenting-start-tag",
+        "(1,36): foster-parenting-character",
+        "(1,40): foster-parenting-end-tag",
+        "(1,40): adoption-agency-1.3",
+        "(1,40): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "i": true,
+            "b": true,
+            "div": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": "c"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><i>a<b>b</b></i><b><div><i>c</i></div></b><table></table></body></html>",
+        "noQuirksBodyHtml": "<i>a<b>b</b></i><b><div><i>c</i></div></b><table></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><i>a<b>b<div>c<a>d</i>e</b>f",
+      "errors": [
+        "(1,25): foster-parenting-start-tag",
+        "(1,26): foster-parenting-character",
+        "(1,29): foster-parenting-start-tag",
+        "(1,30): foster-parenting-character",
+        "(1,35): foster-parenting-start-tag",
+        "(1,36): foster-parenting-character",
+        "(1,39): foster-parenting-start-tag",
+        "(1,40): foster-parenting-character",
+        "(1,44): foster-parenting-end-tag",
+        "(1,44): adoption-agency-1.3",
+        "(1,44): adoption-agency-1.3",
+        "(1,45): foster-parenting-character",
+        "(1,49): foster-parenting-end-tag",
+        "(1,44): adoption-agency-1.3",
+        "(1,44): adoption-agency-1.3",
+        "(1,50): foster-parenting-character",
+        "(1,50): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "i": true,
+            "b": true,
+            "div": true,
+            "a": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b"
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": "c"
+                              },
+                              {
+                                "tag": "a",
+                                "children": [
+                                  {
+                                    "text": "d"
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "a",
+                            "children": [
+                              {
+                                "text": "e"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "text": "f"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><i>a<b>b</b></i><b></b><div><b><i>c<a>d</a></i><a>e</a></b><a>f</a></div><table></table></body></html>",
+        "noQuirksBodyHtml": "<i>a<b>b</b></i><b></b><div><b><i>c<a>d</a></i><a>e</a></b><a>f</a></div><table></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><i>a<div>b<tr>c<b>d</i>e",
+      "errors": [
+        "(1,25): foster-parenting-start-tag",
+        "(1,26): foster-parenting-character",
+        "(1,31): foster-parenting-start-tag",
+        "(1,32): foster-parenting-character",
+        "(1,37): foster-parenting-character",
+        "(1,40): foster-parenting-start-tag",
+        "(1,41): foster-parenting-character",
+        "(1,45): foster-parenting-end-tag",
+        "(1,45): adoption-agency-1.3",
+        "(1,46): foster-parenting-character",
+        "(1,46): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "i": true,
+            "div": true,
+            "b": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "text": "c"
+                      },
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "d"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "e"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><i>a<div>b</div></i><i>c<b>d</b></i><b>e</b><table><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<i>a<div>b</div></i><i>c<b>d</b></i><b>e</b><table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><td><table><i>a<div>b<b>c</i>d",
+      "errors": [
+        "(1,26): unexpected-cell-in-table-body",
+        "(1,36): foster-parenting-start-tag",
+        "(1,37): foster-parenting-character",
+        "(1,42): foster-parenting-start-tag",
+        "(1,43): foster-parenting-character",
+        "(1,46): foster-parenting-start-tag",
+        "(1,47): foster-parenting-character",
+        "(1,51): foster-parenting-end-tag",
+        "(1,51): adoption-agency-1.3",
+        "(1,51): adoption-agency-1.3",
+        "(1,52): foster-parenting-character",
+        "(1,52): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "i": true,
+            "div": true,
+            "b": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "i",
+                                    "children": [
+                                      {
+                                        "text": "a"
+                                      }
+                                    ]
+                                  },
+                                  {
+                                    "tag": "div",
+                                    "children": [
+                                      {
+                                        "tag": "i",
+                                        "children": [
+                                          {
+                                            "text": "b"
+                                          },
+                                          {
+                                            "tag": "b",
+                                            "children": [
+                                              {
+                                                "text": "c"
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      },
+                                      {
+                                        "tag": "b",
+                                        "children": [
+                                          {
+                                            "text": "d"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  },
+                                  {
+                                    "tag": "table"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><i>a</i><div><i>b<b>c</b></i><b>d</b></div><table></table></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><i>a</i><div><i>b<b>c</b></i><b>d</b></div><table></table></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><body><bgsound>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "bgsound": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "bgsound"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><bgsound></body></html>",
+        "noQuirksBodyHtml": "<bgsound>"
+      }
+    },
+    {
+      "data": "<!doctype html><body><basefont>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "basefont": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "basefont"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><basefont></body></html>",
+        "noQuirksBodyHtml": "<basefont>"
+      }
+    },
+    {
+      "data": "<!doctype html><a><b></a><basefont>",
+      "errors": [
+        "(1,25): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "b": true,
+            "basefont": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "basefont"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><a><b></b></a><basefont></body></html>",
+        "noQuirksBodyHtml": "<a><b></b></a><basefont>"
+      }
+    },
+    {
+      "data": "<!doctype html><a><b></a><bgsound>",
+      "errors": [
+        "(1,25): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "b": true,
+            "bgsound": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "bgsound"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><a><b></b></a><bgsound></body></html>",
+        "noQuirksBodyHtml": "<a><b></b></a><bgsound>"
+      }
+    },
+    {
+      "data": "<!doctype html><figcaption><article></figcaption>a",
+      "errors": [
+        "(1,49): end-tag-too-early"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "figcaption": true,
+            "article": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "figcaption",
+                    "children": [
+                      {
+                        "tag": "article"
+                      }
+                    ]
+                  },
+                  {
+                    "text": "a"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><figcaption><article></article></figcaption>a</body></html>",
+        "noQuirksBodyHtml": "<figcaption><article></article></figcaption>a"
+      }
+    },
+    {
+      "data": "<!doctype html><summary><article></summary>a",
+      "errors": [
+        "(1,43): end-tag-too-early"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "summary": true,
+            "article": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "summary",
+                    "children": [
+                      {
+                        "tag": "article"
+                      }
+                    ]
+                  },
+                  {
+                    "text": "a"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><summary><article></article></summary>a</body></html>",
+        "noQuirksBodyHtml": "<summary><article></article></summary>a"
+      }
+    },
+    {
+      "data": "<!doctype html><p><a><plaintext>b",
+      "errors": [
+        "(1,32): unexpected-end-tag",
+        "(1,33): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "a": true,
+            "plaintext": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "a"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "plaintext",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "text": "b"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><a></a></p><plaintext><a>b</a></plaintext></body></html>",
+        "noQuirksBodyHtml": "<p><a></a></p><plaintext><a>b</a></plaintext>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><div>a<a></div>b<p>c</p>d",
+      "errors": [
+        "(1,30): end-tag-too-early",
+        "(1,40): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "a": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "a"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "text": "b"
+                      },
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "text": "c"
+                          }
+                        ]
+                      },
+                      {
+                        "text": "d"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><div>a<a></a></div><a>b<p>c</p>d</a></body></html>",
+        "noQuirksBodyHtml": "<div>a<a></a></div><a>b<p>c</p>d</a>"
+      }
+    }
+  ],
+  "tests2.dat": [
+    {
+      "data": "<!DOCTYPE html>Test",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Test"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>Test</body></html>",
+        "noQuirksBodyHtml": "Test"
+      }
+    },
+    {
+      "data": "<textarea>test</div>test",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,24): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": "test</div>test",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><textarea>test&lt;/div&gt;test</textarea></body></html>",
+        "noQuirksBodyHtml": "<textarea>test&lt;/div&gt;test</textarea>"
+      }
+    },
+    {
+      "data": "<table><td>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-cell-in-table-body",
+        "(1,11): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><td>test</tbody></table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-cell-in-table-body"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "text": "test"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td>test</td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td>test</td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<frame>test",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,7): unexpected-start-tag-ignored"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "test"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>test</body></html>",
+        "noQuirksBodyHtml": "test"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><frameset>test",
+      "errors": [
+        "(1,29): unexpected-char-in-frameset",
+        "(1,29): unexpected-char-in-frameset",
+        "(1,29): unexpected-char-in-frameset",
+        "(1,29): unexpected-char-in-frameset",
+        "(1,29): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "test"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><frameset> te st",
+      "errors": [
+        "(1,29): unexpected-char-in-frameset",
+        "(1,29): unexpected-char-in-frameset",
+        "(1,29): unexpected-char-in-frameset",
+        "(1,29): unexpected-char-in-frameset",
+        "(1,29): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset",
+                "children": [
+                  {
+                    "text": "  "
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset>  </frameset></html>",
+        "noQuirksBodyHtml": " te st"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><frameset></frameset> te st",
+      "errors": [
+        "(1,29): unexpected-char-after-frameset",
+        "(1,29): unexpected-char-after-frameset",
+        "(1,29): unexpected-char-after-frameset",
+        "(1,29): unexpected-char-after-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              },
+              {
+                "text": "  "
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset>  </html>",
+        "noQuirksBodyHtml": " te st"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><frameset><!DOCTYPE html>",
+      "errors": [
+        "(1,40): unexpected-doctype",
+        "(1,40): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><font><p><b>test</font>",
+      "errors": [
+        "(1,38): adoption-agency-1.3",
+        "(1,38): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "font": true,
+            "p": true,
+            "b": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "font"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "children": [
+                          {
+                            "tag": "b",
+                            "children": [
+                              {
+                                "text": "test"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><font></font><p><font><b>test</b></font></p></body></html>",
+        "noQuirksBodyHtml": "<font></font><p><font><b>test</b></font></p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><dt><div><dd>",
+      "errors": [
+        "(1,28): end-tag-too-early"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "dt": true,
+            "div": true,
+            "dd": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "dt",
+                    "children": [
+                      {
+                        "tag": "div"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "dd"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><dt><div></div></dt><dd></dd></body></html>",
+        "noQuirksBodyHtml": "<dt><div></div></dt><dd></dd>"
+      }
+    },
+    {
+      "data": "<script></x",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,11): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "</x",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></x</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></x</script>"
+      }
+    },
+    {
+      "data": "<table><plaintext><td>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,18): unexpected-start-tag-implies-table-voodoo",
+        "(1,22): foster-parenting-character-in-table",
+        "(1,22): foster-parenting-character-in-table",
+        "(1,22): foster-parenting-character-in-table",
+        "(1,22): foster-parenting-character-in-table",
+        "(1,22): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "plaintext": true,
+            "table": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "plaintext",
+                    "children": [
+                      {
+                        "text": "<td>",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><plaintext><td></plaintext><table></table></body></html>",
+        "noQuirksBodyHtml": "<plaintext><td></plaintext><table></table>"
+      }
+    },
+    {
+      "data": "<plaintext></plaintext>",
+      "errors": [
+        "(1,11): expected-doctype-but-got-start-tag",
+        "(1,23): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "plaintext": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "plaintext",
+                    "children": [
+                      {
+                        "text": "</plaintext>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><plaintext></plaintext></plaintext></body></html>",
+        "noQuirksBodyHtml": "<plaintext></plaintext></plaintext>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><table><tr>TEST",
+      "errors": [
+        "(1,30): foster-parenting-character-in-table",
+        "(1,30): foster-parenting-character-in-table",
+        "(1,30): foster-parenting-character-in-table",
+        "(1,30): foster-parenting-character-in-table",
+        "(1,30): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "TEST"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>TEST<table><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "TEST<table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body t1=1><body t2=2><body t3=3 t4=4>",
+      "errors": [
+        "(1,37): unexpected-start-tag",
+        "(1,53): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "attrs": [
+                  {
+                    "name": "t1",
+                    "value": "1"
+                  },
+                  {
+                    "name": "t2",
+                    "value": "2"
+                  },
+                  {
+                    "name": "t3",
+                    "value": "3"
+                  },
+                  {
+                    "name": "t4",
+                    "value": "4"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body t1=\"1\" t2=\"2\" t3=\"3\" t4=\"4\"></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "</b test",
+      "errors": [
+        "(1,8): eof-in-attribute-name",
+        "(1,8): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE html></b test<b &=&amp>X",
+      "errors": [
+        "(1,24): invalid-character-in-attribute-name",
+        "(1,32): named-entity-without-semicolon",
+        "(1,33): attributes-in-end-tag",
+        "(1,33): unexpected-end-tag-before-html"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>X</body></html>",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "<!doctypehtml><scrIPt type=text/x-foobar;baz>X</SCRipt",
+      "errors": [
+        "(1,9): need-space-after-doctype",
+        "(1,54): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "text/x-foobar;baz"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "X</SCRipt",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script type=\"text/x-foobar;baz\">X</SCRipt</script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script type=\"text/x-foobar;baz\">X</SCRipt</script>"
+      }
+    },
+    {
+      "data": "&",
+      "errors": [
+        "(1,1): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "&",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>&amp;</body></html>",
+        "noQuirksBodyHtml": "&amp;"
+      }
+    },
+    {
+      "data": "&#",
+      "errors": [
+        "(1,2): expected-numeric-entity",
+        "(1,2): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "&#",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>&amp;#</body></html>",
+        "noQuirksBodyHtml": "&amp;#"
+      }
+    },
+    {
+      "data": "&#X",
+      "errors": [
+        "(1,3): expected-numeric-entity",
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "&#X",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>&amp;#X</body></html>",
+        "noQuirksBodyHtml": "&amp;#X"
+      }
+    },
+    {
+      "data": "&#x",
+      "errors": [
+        "(1,3): expected-numeric-entity",
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "&#x",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>&amp;#x</body></html>",
+        "noQuirksBodyHtml": "&amp;#x"
+      }
+    },
+    {
+      "data": "&#45",
+      "errors": [
+        "(1,4): numeric-entity-without-semicolon",
+        "(1,4): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>-</body></html>",
+        "noQuirksBodyHtml": "-"
+      }
+    },
+    {
+      "data": "&x-test",
+      "errors": [
+        "(1,2): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "&x-test",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>&amp;x-test</body></html>",
+        "noQuirksBodyHtml": "&amp;x-test"
+      }
+    },
+    {
+      "data": "<!doctypehtml><p><li>",
+      "errors": [
+        "(1,9): need-space-after-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "li": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "li"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p></p><li></li></body></html>",
+        "noQuirksBodyHtml": "<p></p><li></li>"
+      }
+    },
+    {
+      "data": "<!doctypehtml><p><dt>",
+      "errors": [
+        "(1,9): need-space-after-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "dt": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "dt"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p></p><dt></dt></body></html>",
+        "noQuirksBodyHtml": "<p></p><dt></dt>"
+      }
+    },
+    {
+      "data": "<!doctypehtml><p><dd>",
+      "errors": [
+        "(1,9): need-space-after-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "dd": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "dd"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p></p><dd></dd></body></html>",
+        "noQuirksBodyHtml": "<p></p><dd></dd>"
+      }
+    },
+    {
+      "data": "<!doctypehtml><p><form>",
+      "errors": [
+        "(1,9): need-space-after-doctype",
+        "(1,23): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "form": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "form"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p></p><form></form></body></html>",
+        "noQuirksBodyHtml": "<p></p><form></form>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><p></P>X",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p></p>X</body></html>",
+        "noQuirksBodyHtml": "<p></p>X"
+      }
+    },
+    {
+      "data": "&AMP",
+      "errors": [
+        "(1,4): named-entity-without-semicolon",
+        "(1,4): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "&",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>&amp;</body></html>",
+        "noQuirksBodyHtml": "&amp;"
+      }
+    },
+    {
+      "data": "&AMp;",
+      "errors": [
+        "(1,3): expected-named-entity",
+        "(1,3): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "&AMp;",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>&amp;AMp;</body></html>",
+        "noQuirksBodyHtml": "&amp;AMp;"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html><head></head><body><thisISasillyTESTelementNameToMakeSureCrazyTagNamesArePARSEDcorrectLY>",
+      "errors": [
+        "(1,110): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "thisisasillytestelementnametomakesurecrazytagnamesareparsedcorrectly": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "thisisasillytestelementnametomakesurecrazytagnamesareparsedcorrectly"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><thisisasillytestelementnametomakesurecrazytagnamesareparsedcorrectly></thisisasillytestelementnametomakesurecrazytagnamesareparsedcorrectly></body></html>",
+        "noQuirksBodyHtml": "<thisisasillytestelementnametomakesurecrazytagnamesareparsedcorrectly></thisisasillytestelementnametomakesurecrazytagnamesareparsedcorrectly>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>X</body>X",
+      "errors": [
+        "(1,24): unexpected-char-after-body"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "XX"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>XX</body></html>",
+        "noQuirksBodyHtml": "XX"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><!-- X",
+      "errors": [
+        "(1,21): eof-in-comment"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "comment": " X"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><!-- X--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!-- X-->"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><table><caption>test TEST</caption><td>test",
+      "errors": [
+        "(1,54): unexpected-cell-in-table-body",
+        "(1,58): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "text": "test TEST"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "text": "test"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><caption>test TEST</caption><tbody><tr><td>test</td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><caption>test TEST</caption><tbody><tr><td>test</td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><select><option><optgroup>",
+      "errors": [
+        "(1,41): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true,
+            "optgroup": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option"
+                      },
+                      {
+                        "tag": "optgroup"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select><option></option><optgroup></optgroup></select></body></html>",
+        "noQuirksBodyHtml": "<select><option></option><optgroup></optgroup></select>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><select><optgroup><option></optgroup><option><select><option>",
+      "errors": [
+        "(1,68): unexpected-select-in-select",
+        "(1,76): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "optgroup": true,
+            "option": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "optgroup",
+                        "children": [
+                          {
+                            "tag": "option"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "option"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "option"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select><optgroup><option></option></optgroup><option></option></select><option></option></body></html>",
+        "noQuirksBodyHtml": "<select><optgroup><option></option></optgroup><option></option></select><option></option>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><select><optgroup><option><optgroup>",
+      "errors": [
+        "(1,51): eof-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "optgroup": true,
+            "option": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "optgroup",
+                        "children": [
+                          {
+                            "tag": "option"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "optgroup"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select><optgroup><option></option></optgroup><optgroup></optgroup></select></body></html>",
+        "noQuirksBodyHtml": "<select><optgroup><option></option></optgroup><optgroup></optgroup></select>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><datalist><option>foo</datalist>bar",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "datalist": true,
+            "option": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "datalist",
+                    "children": [
+                      {
+                        "tag": "option",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "text": "bar"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><datalist><option>foo</option></datalist>bar</body></html>",
+        "noQuirksBodyHtml": "<datalist><option>foo</option></datalist>bar"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><font><input><input></font>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "font": true,
+            "input": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "font",
+                    "children": [
+                      {
+                        "tag": "input"
+                      },
+                      {
+                        "tag": "input"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><font><input><input></font></body></html>",
+        "noQuirksBodyHtml": "<font><input><input></font>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><!-- XXX - XXX -->",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "comment": " XXX - XXX "
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><!-- XXX - XXX --><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!-- XXX - XXX -->"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><!-- XXX - XXX",
+      "errors": [
+        "(1,29): eof-in-comment"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "comment": " XXX - XXX"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><!-- XXX - XXX--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!-- XXX - XXX-->"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><!-- XXX - XXX - XXX -->",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "comment": " XXX - XXX - XXX "
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><!-- XXX - XXX - XXX --><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!-- XXX - XXX - XXX -->"
+      }
+    },
+    {
+      "data": "<isindex test=x name=x>",
+      "errors": [
+        "(1,23): expected-doctype-but-got-start-tag",
+        "(1,23): deprecated-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "form": true,
+            "hr": true,
+            "label": true,
+            "input": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "form",
+                    "children": [
+                      {
+                        "tag": "hr"
+                      },
+                      {
+                        "tag": "label",
+                        "children": [
+                          {
+                            "text": "This is a searchable index. Enter search keywords: "
+                          },
+                          {
+                            "tag": "input",
+                            "attrs": [
+                              {
+                                "name": "name",
+                                "value": "isindex"
+                              },
+                              {
+                                "name": "test",
+                                "value": "x"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "hr"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><form><hr><label>This is a searchable index. Enter search keywords: <input name=\"isindex\" test=\"x\"></label><hr></form></body></html>",
+        "noQuirksBodyHtml": "<form><hr><label>This is a searchable index. Enter search keywords: <input name=\"isindex\" test=\"x\"></label><hr></form>"
+      }
+    },
+    {
+      "data": "test\ntest",
+      "errors": [
+        "(2,4): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "test\ntest"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>test\ntest</body></html>",
+        "noQuirksBodyHtml": "test\ntest"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><title>test</body></title>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "title": true
+          },
+          "doctype": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "test</body>",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><title>test&lt;/body&gt;</title></body></html>",
+        "noQuirksBodyHtml": "<title>test&lt;/body&gt;</title>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><title>X</title><meta name=z><link rel=foo><style>\nx { content:\"</style\" } </style>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "title": true,
+            "meta": true,
+            "link": true,
+            "style": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "X"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "meta",
+                    "attrs": [
+                      {
+                        "name": "name",
+                        "value": "z"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "link",
+                    "attrs": [
+                      {
+                        "name": "rel",
+                        "value": "foo"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": "\nx { content:\"</style\" } ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><title>X</title><meta name=\"z\"><link rel=\"foo\"><style>\nx { content:\"</style\" } </style></body></html>",
+        "noQuirksBodyHtml": "<title>X</title><meta name=\"z\"><link rel=\"foo\"><style>\nx { content:\"</style\" } </style>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><select><optgroup></optgroup></select>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "optgroup": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "optgroup"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select><optgroup></optgroup></select></body></html>",
+        "noQuirksBodyHtml": "<select><optgroup></optgroup></select>"
+      }
+    },
+    {
+      "data": " \n ",
+      "errors": [
+        "(2,1): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": " \n "
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>  <html>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "  "
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><script>\n</script>  <title>x</title>  </head>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "title": true,
+            "body": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "\n",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "  "
+                  },
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "x"
+                      }
+                    ]
+                  },
+                  {
+                    "text": "  "
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><script>\n</script>  <title>x</title>  </head><body></body></html>",
+        "noQuirksBodyHtml": "<script>\n</script>  <title>x</title>  "
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html><body><html id=x>",
+      "errors": [
+        "(1,38): non-html-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "attrs": [
+              {
+                "name": "id",
+                "value": "x"
+              }
+            ],
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html id=\"x\"><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>X</body><html id=\"x\">",
+      "errors": [
+        "(1,36): non-html-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "attrs": [
+              {
+                "name": "id",
+                "value": "x"
+              }
+            ],
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html id=\"x\"><head></head><body>X</body></html>",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><head><html id=x>",
+      "errors": [
+        "(1,32): non-html-root"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "attrs": [
+              {
+                "name": "id",
+                "value": "x"
+              }
+            ],
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html id=\"x\"><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>X</html>X",
+      "errors": [
+        "(1,24): expected-eof-but-got-char"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "XX"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>XX</body></html>",
+        "noQuirksBodyHtml": "XX"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>X</html> ",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X "
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>X </body></html>",
+        "noQuirksBodyHtml": "X "
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>X</html><p>X",
+      "errors": [
+        "(1,26): expected-eof-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "X"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>X<p>X</p></body></html>",
+        "noQuirksBodyHtml": "X<p>X</p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>X<p/x/y/z>",
+      "errors": [
+        "(1,19): unexpected-character-after-solidus-in-tag",
+        "(1,21): unexpected-character-after-solidus-in-tag",
+        "(1,23): unexpected-character-after-solidus-in-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  },
+                  {
+                    "tag": "p",
+                    "attrs": [
+                      {
+                        "name": "x",
+                        "value": ""
+                      },
+                      {
+                        "name": "y",
+                        "value": ""
+                      },
+                      {
+                        "name": "z",
+                        "value": ""
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>X<p x=\"\" y=\"\" z=\"\"></p></body></html>",
+        "noQuirksBodyHtml": "X<p x=\"\" y=\"\" z=\"\"></p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><!--x--",
+      "errors": [
+        "(1,22): eof-in-comment-double-dash"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "comment": "x"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><!--x--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!--x-->"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><table><tr><td></p></table>",
+      "errors": [
+        "(1,34): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "p"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><p></p></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><p></p></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE <!DOCTYPE HTML>><!--<!--x-->-->",
+      "errors": [
+        "(1,20): expected-space-or-right-bracket-in-doctype",
+        "(1,25): unknown-doctype",
+        "(1,35): unexpected-char-in-comment"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true,
+          "escaped": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "<!doctype"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": ">",
+                    "escaped": true
+                  },
+                  {
+                    "comment": "<!--x"
+                  },
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE <!doctype><html><head></head><body>&gt;<!--<!--x-->--&gt;</body></html>",
+        "noQuirksBodyHtml": "&gt;<!--<!--x-->--&gt;"
+      }
+    },
+    {
+      "data": "<!doctype html><div><form></form><div></div></div>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "form": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "form"
+                      },
+                      {
+                        "tag": "div"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><div><form></form><div></div></div></body></html>",
+        "noQuirksBodyHtml": "<div><form></form><div></div></div>"
+      }
+    }
+  ],
+  "tests20.dat": [
+    {
+      "data": "<!doctype html><p><button><button>",
+      "errors": [
+        "(1,34): unexpected-start-tag-implies-end-tag",
+        "(1,34): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button"
+                      },
+                      {
+                        "tag": "button"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button></button><button></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button></button><button></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><address>",
+      "errors": [
+        "(1,35): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "address": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "address"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><address></address></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><address></address></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><blockquote>",
+      "errors": [
+        "(1,38): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "blockquote": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "blockquote"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><blockquote></blockquote></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><blockquote></blockquote></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><menu>",
+      "errors": [
+        "(1,32): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "menu": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "menu"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><menu></menu></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><menu></menu></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><p>",
+      "errors": [
+        "(1,29): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "p"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><p></p></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><p></p></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><ul>",
+      "errors": [
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "ul": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "ul"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><ul></ul></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><ul></ul></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><h1>",
+      "errors": [
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "h1": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "h1"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><h1></h1></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><h1></h1></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><h6>",
+      "errors": [
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "h6": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "h6"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><h6></h6></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><h6></h6></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><listing>",
+      "errors": [
+        "(1,35): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "listing": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "listing"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><listing></listing></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><listing></listing></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><pre>",
+      "errors": [
+        "(1,31): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "pre": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "pre"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><pre></pre></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><pre></pre></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><form>",
+      "errors": [
+        "(1,32): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "form": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "form"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><form></form></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><form></form></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><li>",
+      "errors": [
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "li": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "li"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><li></li></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><li></li></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><dd>",
+      "errors": [
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "dd": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "dd"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><dd></dd></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><dd></dd></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><dt>",
+      "errors": [
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "dt": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "dt"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><dt></dt></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><dt></dt></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><plaintext>",
+      "errors": [
+        "(1,37): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "plaintext": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "plaintext"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><plaintext></plaintext></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><plaintext></plaintext></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><table>",
+      "errors": [
+        "(1,33): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "table"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><table></table></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><table></table></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><hr>",
+      "errors": [
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "hr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "hr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><hr></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><hr></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button><xmp>",
+      "errors": [
+        "(1,31): expected-named-closing-tag-but-got-eof",
+        "(1,31): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true,
+            "xmp": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "xmp"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><xmp></xmp></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><xmp></xmp></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><button></p>",
+      "errors": [
+        "(1,30): unexpected-end-tag",
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "button": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "button",
+                        "children": [
+                          {
+                            "tag": "p"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><button><p></p></button></p></body></html>",
+        "noQuirksBodyHtml": "<p><button><p></p></button></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><address><button></address>a",
+      "errors": [
+        "(1,42): end-tag-too-early"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "address": true,
+            "button": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "address",
+                    "children": [
+                      {
+                        "tag": "button"
+                      }
+                    ]
+                  },
+                  {
+                    "text": "a"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><address><button></button></address>a</body></html>",
+        "noQuirksBodyHtml": "<address><button></button></address>a"
+      }
+    },
+    {
+      "data": "<!doctype html><address><button></address>a",
+      "errors": [
+        "(1,42): end-tag-too-early"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "address": true,
+            "button": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "address",
+                    "children": [
+                      {
+                        "tag": "button"
+                      }
+                    ]
+                  },
+                  {
+                    "text": "a"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><address><button></button></address>a</body></html>",
+        "noQuirksBodyHtml": "<address><button></button></address>a"
+      }
+    },
+    {
+      "data": "<p><table></p>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,14): unexpected-end-tag-implies-table-voodoo",
+        "(1,14): unexpected-end-tag",
+        "(1,14): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "p"
+                      },
+                      {
+                        "tag": "table"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><p></p><table></table></p></body></html>",
+        "noQuirksBodyHtml": "<p></p><p></p><table></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><svg>",
+      "errors": [
+        "(1,20): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg></svg></body></html>",
+        "noQuirksBodyHtml": "<svg></svg>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><figcaption>",
+      "errors": [
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "figcaption": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "figcaption"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p></p><figcaption></figcaption></body></html>",
+        "noQuirksBodyHtml": "<p></p><figcaption></figcaption>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><summary>",
+      "errors": [
+        "(1,27): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "summary": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "summary"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p></p><summary></summary></body></html>",
+        "noQuirksBodyHtml": "<p></p><summary></summary>"
+      }
+    },
+    {
+      "data": "<!doctype html><form><table><form>",
+      "errors": [
+        "(1,34): unexpected-form-in-table",
+        "(1,34): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "form": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "form",
+                    "children": [
+                      {
+                        "tag": "table"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><form><table></table></form></body></html>",
+        "noQuirksBodyHtml": "<form><table></table></form>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><form><form>",
+      "errors": [
+        "(1,28): unexpected-form-in-table",
+        "(1,34): unexpected-form-in-table",
+        "(1,34): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "form": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "form"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><form></form></table></body></html>",
+        "noQuirksBodyHtml": "<table><form></form></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><form></table><form>",
+      "errors": [
+        "(1,28): unexpected-form-in-table",
+        "(1,42): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "form": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "form"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><form></form></table></body></html>",
+        "noQuirksBodyHtml": "<table><form></form></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><svg><foreignObject><p>",
+      "errors": [
+        "(1,38): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg foreignObject": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "foreignObject",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "p"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><foreignObject><p></p></foreignObject></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><foreignObject><p></p></foreignObject></svg>"
+      }
+    },
+    {
+      "data": "<!doctype html><svg><title>abc",
+      "errors": [
+        "(1,30): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg title": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "title",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "text": "abc"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><title>abc</title></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><title>abc</title></svg>"
+      }
+    },
+    {
+      "data": "<option><span><option>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,22): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "option": true,
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "option",
+                    "children": [
+                      {
+                        "tag": "span",
+                        "children": [
+                          {
+                            "tag": "option"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><option><span><option></option></span></option></body></html>",
+        "noQuirksBodyHtml": "<option><span><option></option></span></option>"
+      }
+    },
+    {
+      "data": "<option><option>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "option": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "option"
+                  },
+                  {
+                    "tag": "option"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><option></option><option></option></body></html>",
+        "noQuirksBodyHtml": "<option></option><option></option>"
+      }
+    },
+    {
+      "data": "<math><annotation-xml><div>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,27): unexpected-html-element-in-foreign-content",
+        "(1,27): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math annotation-xml": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "annotation-xml",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "div"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><annotation-xml></annotation-xml></math><div></div></body></html>",
+        "noQuirksBodyHtml": "<math><annotation-xml><div></div></annotation-xml></math>"
+      }
+    },
+    {
+      "data": "<math><annotation-xml encoding=\"application/svg+xml\"><div>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,58): unexpected-html-element-in-foreign-content",
+        "(1,58): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math annotation-xml": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "annotation-xml",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "attrs": [
+                          {
+                            "name": "encoding",
+                            "value": "application/svg+xml"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "div"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><annotation-xml encoding=\"application/svg+xml\"></annotation-xml></math><div></div></body></html>",
+        "noQuirksBodyHtml": "<math><annotation-xml encoding=\"application/svg+xml\"><div></div></annotation-xml></math>"
+      }
+    },
+    {
+      "data": "<math><annotation-xml encoding=\"application/xhtml+xml\"><div>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,60): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math annotation-xml": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "annotation-xml",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "attrs": [
+                          {
+                            "name": "encoding",
+                            "value": "application/xhtml+xml"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><annotation-xml encoding=\"application/xhtml+xml\"><div></div></annotation-xml></math></body></html>",
+        "noQuirksBodyHtml": "<math><annotation-xml encoding=\"application/xhtml+xml\"><div></div></annotation-xml></math>"
+      }
+    },
+    {
+      "data": "<math><annotation-xml encoding=\"aPPlication/xhtmL+xMl\"><div>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,60): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math annotation-xml": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "annotation-xml",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "attrs": [
+                          {
+                            "name": "encoding",
+                            "value": "aPPlication/xhtmL+xMl"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><annotation-xml encoding=\"aPPlication/xhtmL+xMl\"><div></div></annotation-xml></math></body></html>",
+        "noQuirksBodyHtml": "<math><annotation-xml encoding=\"aPPlication/xhtmL+xMl\"><div></div></annotation-xml></math>"
+      }
+    },
+    {
+      "data": "<math><annotation-xml encoding=\"text/html\"><div>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,48): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math annotation-xml": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "annotation-xml",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "attrs": [
+                          {
+                            "name": "encoding",
+                            "value": "text/html"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><annotation-xml encoding=\"text/html\"><div></div></annotation-xml></math></body></html>",
+        "noQuirksBodyHtml": "<math><annotation-xml encoding=\"text/html\"><div></div></annotation-xml></math>"
+      }
+    },
+    {
+      "data": "<math><annotation-xml encoding=\"Text/htmL\"><div>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,48): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math annotation-xml": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "annotation-xml",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "attrs": [
+                          {
+                            "name": "encoding",
+                            "value": "Text/htmL"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><annotation-xml encoding=\"Text/htmL\"><div></div></annotation-xml></math></body></html>",
+        "noQuirksBodyHtml": "<math><annotation-xml encoding=\"Text/htmL\"><div></div></annotation-xml></math>"
+      }
+    },
+    {
+      "data": "<math><annotation-xml encoding=\" text/html \"><div>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,50): unexpected-html-element-in-foreign-content",
+        "(1,50): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math annotation-xml": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "annotation-xml",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "attrs": [
+                          {
+                            "name": "encoding",
+                            "value": " text/html "
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "div"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><annotation-xml encoding=\" text/html \"></annotation-xml></math><div></div></body></html>",
+        "noQuirksBodyHtml": "<math><annotation-xml encoding=\" text/html \"><div></div></annotation-xml></math>"
+      }
+    }
+  ],
+  "tests21.dat": [
+    {
+      "data": "<svg><![CDATA[foo]]>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,20): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>foo</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>foo</svg>"
+      }
+    },
+    {
+      "data": "<math><![CDATA[foo]]>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,21): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "text": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math>foo</math></body></html>",
+        "noQuirksBodyHtml": "<math>foo</math>"
+      }
+    },
+    {
+      "data": "<div><![CDATA[foo]]>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,7): expected-dashes-or-doctype",
+        "(1,20): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "comment": "[CDATA[foo]]"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><!--[CDATA[foo]]--></div></body></html>",
+        "noQuirksBodyHtml": "<div><!--[CDATA[foo]]--></div>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[foo",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>foo</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>foo</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[foo",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>foo</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>foo</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,14): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg></svg></body></html>",
+        "noQuirksBodyHtml": "<svg></svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[]]>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg></svg></body></html>",
+        "noQuirksBodyHtml": "<svg></svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[]] >]]>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,21): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "]] >",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>]] &gt;</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>]] &gt;</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[]] >]]>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,21): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "]] >",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>]] &gt;</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>]] &gt;</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[]]",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "]]"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>]]</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>]]</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[]",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,15): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "]"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>]</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>]</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[]>a",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "]>a",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>]&gt;a</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>]&gt;a</svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><svg><![CDATA[foo]]]>",
+      "errors": [
+        "(1,36): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "foo]"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg>foo]</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>foo]</svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><svg><![CDATA[foo]]]]>",
+      "errors": [
+        "(1,37): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "foo]]"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg>foo]]</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>foo]]</svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><svg><![CDATA[foo]]]]]>",
+      "errors": [
+        "(1,38): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "foo]]]"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg>foo]]]</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>foo]]]</svg>"
+      }
+    },
+    {
+      "data": "<svg><foreignObject><div><![CDATA[foo]]>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,27): expected-dashes-or-doctype",
+        "(1,40): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg foreignObject": true,
+            "div": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "foreignObject",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "div",
+                            "children": [
+                              {
+                                "comment": "[CDATA[foo]]"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg><foreignObject><div><!--[CDATA[foo]]--></div></foreignObject></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><foreignObject><div><!--[CDATA[foo]]--></div></foreignObject></svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[<svg>]]>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,22): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "<svg>",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>&lt;svg&gt;</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>&lt;svg&gt;</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[</svg>a]]>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,24): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "</svg>a",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>&lt;/svg&gt;a</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>&lt;/svg&gt;a</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[<svg>a",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,20): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "<svg>a",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>&lt;svg&gt;a</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>&lt;svg&gt;a</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[</svg>a",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,21): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "</svg>a",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>&lt;/svg&gt;a</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>&lt;/svg&gt;a</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[<svg>]]><path>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,28): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg path": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "<svg>",
+                        "escaped": true
+                      },
+                      {
+                        "tag": "path",
+                        "ns": "http://www.w3.org/2000/svg"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>&lt;svg&gt;<path></path></svg></body></html>",
+        "noQuirksBodyHtml": "<svg>&lt;svg&gt;<path></path></svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[<svg>]]></path>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,29): unexpected-end-tag",
+        "(1,29): unexpected-end-tag",
+        "(1,29): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "<svg>",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>&lt;svg&gt;</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>&lt;svg&gt;</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[<svg>]]><!--path-->",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,33): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "escaped": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "<svg>",
+                        "escaped": true
+                      },
+                      {
+                        "comment": "path"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>&lt;svg&gt;<!--path--></svg></body></html>",
+        "noQuirksBodyHtml": "<svg>&lt;svg&gt;<!--path--></svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[<svg>]]>path",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,26): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "<svg>path",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>&lt;svg&gt;path</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>&lt;svg&gt;path</svg>"
+      }
+    },
+    {
+      "data": "<svg><![CDATA[<!--svg-->]]>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,27): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "text": "<!--svg-->",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg>&lt;!--svg--&gt;</svg></body></html>",
+        "noQuirksBodyHtml": "<svg>&lt;!--svg--&gt;</svg>"
+      }
+    }
+  ],
+  "tests22.dat": [
+    {
+      "data": "<a><b><big><em><strong><div>X</a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,33): adoption-agency-1.3",
+        "(1,33): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "b": true,
+            "big": true,
+            "em": true,
+            "strong": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "big",
+                            "children": [
+                              {
+                                "tag": "em",
+                                "children": [
+                                  {
+                                    "tag": "strong"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "big",
+                    "children": [
+                      {
+                        "tag": "em",
+                        "children": [
+                          {
+                            "tag": "strong",
+                            "children": [
+                              {
+                                "tag": "div",
+                                "children": [
+                                  {
+                                    "tag": "a",
+                                    "children": [
+                                      {
+                                        "text": "X"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a><b><big><em><strong></strong></em></big></b></a><big><em><strong><div><a>X</a></div></strong></em></big></body></html>",
+        "noQuirksBodyHtml": "<a><b><big><em><strong></strong></em></big></b></a><big><em><strong><div><a>X</a></div></strong></em></big>"
+      }
+    },
+    {
+      "data": "<a><b><div id=1><div id=2><div id=3><div id=4><div id=5><div id=6><div id=7><div id=8>A</a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,91): adoption-agency-1.3",
+        "(1,91): adoption-agency-1.3",
+        "(1,91): adoption-agency-1.3",
+        "(1,91): adoption-agency-1.3",
+        "(1,91): adoption-agency-1.3",
+        "(1,91): adoption-agency-1.3",
+        "(1,91): adoption-agency-1.3",
+        "(1,91): adoption-agency-1.3",
+        "(1,91): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "b": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "div",
+                        "attrs": [
+                          {
+                            "name": "id",
+                            "value": "1"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "a"
+                          },
+                          {
+                            "tag": "div",
+                            "attrs": [
+                              {
+                                "name": "id",
+                                "value": "2"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "tag": "a"
+                              },
+                              {
+                                "tag": "div",
+                                "attrs": [
+                                  {
+                                    "name": "id",
+                                    "value": "3"
+                                  }
+                                ],
+                                "children": [
+                                  {
+                                    "tag": "a"
+                                  },
+                                  {
+                                    "tag": "div",
+                                    "attrs": [
+                                      {
+                                        "name": "id",
+                                        "value": "4"
+                                      }
+                                    ],
+                                    "children": [
+                                      {
+                                        "tag": "a"
+                                      },
+                                      {
+                                        "tag": "div",
+                                        "attrs": [
+                                          {
+                                            "name": "id",
+                                            "value": "5"
+                                          }
+                                        ],
+                                        "children": [
+                                          {
+                                            "tag": "a"
+                                          },
+                                          {
+                                            "tag": "div",
+                                            "attrs": [
+                                              {
+                                                "name": "id",
+                                                "value": "6"
+                                              }
+                                            ],
+                                            "children": [
+                                              {
+                                                "tag": "a"
+                                              },
+                                              {
+                                                "tag": "div",
+                                                "attrs": [
+                                                  {
+                                                    "name": "id",
+                                                    "value": "7"
+                                                  }
+                                                ],
+                                                "children": [
+                                                  {
+                                                    "tag": "a"
+                                                  },
+                                                  {
+                                                    "tag": "div",
+                                                    "attrs": [
+                                                      {
+                                                        "name": "id",
+                                                        "value": "8"
+                                                      }
+                                                    ],
+                                                    "children": [
+                                                      {
+                                                        "tag": "a",
+                                                        "children": [
+                                                          {
+                                                            "text": "A"
+                                                          }
+                                                        ]
+                                                      }
+                                                    ]
+                                                  }
+                                                ]
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a><b></b></a><b><div id=\"1\"><a></a><div id=\"2\"><a></a><div id=\"3\"><a></a><div id=\"4\"><a></a><div id=\"5\"><a></a><div id=\"6\"><a></a><div id=\"7\"><a></a><div id=\"8\"><a>A</a></div></div></div></div></div></div></div></div></b></body></html>",
+        "noQuirksBodyHtml": "<a><b></b></a><b><div id=\"1\"><a></a><div id=\"2\"><a></a><div id=\"3\"><a></a><div id=\"4\"><a></a><div id=\"5\"><a></a><div id=\"6\"><a></a><div id=\"7\"><a></a><div id=\"8\"><a>A</a></div></div></div></div></div></div></div></div></b>"
+      }
+    },
+    {
+      "data": "<a><b><div id=1><div id=2><div id=3><div id=4><div id=5><div id=6><div id=7><div id=8><div id=9>A</a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,101): adoption-agency-1.3",
+        "(1,101): adoption-agency-1.3",
+        "(1,101): adoption-agency-1.3",
+        "(1,101): adoption-agency-1.3",
+        "(1,101): adoption-agency-1.3",
+        "(1,101): adoption-agency-1.3",
+        "(1,101): adoption-agency-1.3",
+        "(1,101): adoption-agency-1.3",
+        "(1,101): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "b": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "div",
+                        "attrs": [
+                          {
+                            "name": "id",
+                            "value": "1"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "a"
+                          },
+                          {
+                            "tag": "div",
+                            "attrs": [
+                              {
+                                "name": "id",
+                                "value": "2"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "tag": "a"
+                              },
+                              {
+                                "tag": "div",
+                                "attrs": [
+                                  {
+                                    "name": "id",
+                                    "value": "3"
+                                  }
+                                ],
+                                "children": [
+                                  {
+                                    "tag": "a"
+                                  },
+                                  {
+                                    "tag": "div",
+                                    "attrs": [
+                                      {
+                                        "name": "id",
+                                        "value": "4"
+                                      }
+                                    ],
+                                    "children": [
+                                      {
+                                        "tag": "a"
+                                      },
+                                      {
+                                        "tag": "div",
+                                        "attrs": [
+                                          {
+                                            "name": "id",
+                                            "value": "5"
+                                          }
+                                        ],
+                                        "children": [
+                                          {
+                                            "tag": "a"
+                                          },
+                                          {
+                                            "tag": "div",
+                                            "attrs": [
+                                              {
+                                                "name": "id",
+                                                "value": "6"
+                                              }
+                                            ],
+                                            "children": [
+                                              {
+                                                "tag": "a"
+                                              },
+                                              {
+                                                "tag": "div",
+                                                "attrs": [
+                                                  {
+                                                    "name": "id",
+                                                    "value": "7"
+                                                  }
+                                                ],
+                                                "children": [
+                                                  {
+                                                    "tag": "a"
+                                                  },
+                                                  {
+                                                    "tag": "div",
+                                                    "attrs": [
+                                                      {
+                                                        "name": "id",
+                                                        "value": "8"
+                                                      }
+                                                    ],
+                                                    "children": [
+                                                      {
+                                                        "tag": "a",
+                                                        "children": [
+                                                          {
+                                                            "tag": "div",
+                                                            "attrs": [
+                                                              {
+                                                                "name": "id",
+                                                                "value": "9"
+                                                              }
+                                                            ],
+                                                            "children": [
+                                                              {
+                                                                "text": "A"
+                                                              }
+                                                            ]
+                                                          }
+                                                        ]
+                                                      }
+                                                    ]
+                                                  }
+                                                ]
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a><b></b></a><b><div id=\"1\"><a></a><div id=\"2\"><a></a><div id=\"3\"><a></a><div id=\"4\"><a></a><div id=\"5\"><a></a><div id=\"6\"><a></a><div id=\"7\"><a></a><div id=\"8\"><a><div id=\"9\">A</div></a></div></div></div></div></div></div></div></div></b></body></html>",
+        "noQuirksBodyHtml": "<a><b></b></a><b><div id=\"1\"><a></a><div id=\"2\"><a></a><div id=\"3\"><a></a><div id=\"4\"><a></a><div id=\"5\"><a></a><div id=\"6\"><a></a><div id=\"7\"><a></a><div id=\"8\"><a><div id=\"9\">A</div></a></div></div></div></div></div></div></div></div></b>"
+      }
+    },
+    {
+      "data": "<a><b><div id=1><div id=2><div id=3><div id=4><div id=5><div id=6><div id=7><div id=8><div id=9><div id=10>A</a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,112): adoption-agency-1.3",
+        "(1,112): adoption-agency-1.3",
+        "(1,112): adoption-agency-1.3",
+        "(1,112): adoption-agency-1.3",
+        "(1,112): adoption-agency-1.3",
+        "(1,112): adoption-agency-1.3",
+        "(1,112): adoption-agency-1.3",
+        "(1,112): adoption-agency-1.3",
+        "(1,112): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "b": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "div",
+                        "attrs": [
+                          {
+                            "name": "id",
+                            "value": "1"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "a"
+                          },
+                          {
+                            "tag": "div",
+                            "attrs": [
+                              {
+                                "name": "id",
+                                "value": "2"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "tag": "a"
+                              },
+                              {
+                                "tag": "div",
+                                "attrs": [
+                                  {
+                                    "name": "id",
+                                    "value": "3"
+                                  }
+                                ],
+                                "children": [
+                                  {
+                                    "tag": "a"
+                                  },
+                                  {
+                                    "tag": "div",
+                                    "attrs": [
+                                      {
+                                        "name": "id",
+                                        "value": "4"
+                                      }
+                                    ],
+                                    "children": [
+                                      {
+                                        "tag": "a"
+                                      },
+                                      {
+                                        "tag": "div",
+                                        "attrs": [
+                                          {
+                                            "name": "id",
+                                            "value": "5"
+                                          }
+                                        ],
+                                        "children": [
+                                          {
+                                            "tag": "a"
+                                          },
+                                          {
+                                            "tag": "div",
+                                            "attrs": [
+                                              {
+                                                "name": "id",
+                                                "value": "6"
+                                              }
+                                            ],
+                                            "children": [
+                                              {
+                                                "tag": "a"
+                                              },
+                                              {
+                                                "tag": "div",
+                                                "attrs": [
+                                                  {
+                                                    "name": "id",
+                                                    "value": "7"
+                                                  }
+                                                ],
+                                                "children": [
+                                                  {
+                                                    "tag": "a"
+                                                  },
+                                                  {
+                                                    "tag": "div",
+                                                    "attrs": [
+                                                      {
+                                                        "name": "id",
+                                                        "value": "8"
+                                                      }
+                                                    ],
+                                                    "children": [
+                                                      {
+                                                        "tag": "a",
+                                                        "children": [
+                                                          {
+                                                            "tag": "div",
+                                                            "attrs": [
+                                                              {
+                                                                "name": "id",
+                                                                "value": "9"
+                                                              }
+                                                            ],
+                                                            "children": [
+                                                              {
+                                                                "tag": "div",
+                                                                "attrs": [
+                                                                  {
+                                                                    "name": "id",
+                                                                    "value": "10"
+                                                                  }
+                                                                ],
+                                                                "children": [
+                                                                  {
+                                                                    "text": "A"
+                                                                  }
+                                                                ]
+                                                              }
+                                                            ]
+                                                          }
+                                                        ]
+                                                      }
+                                                    ]
+                                                  }
+                                                ]
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a><b></b></a><b><div id=\"1\"><a></a><div id=\"2\"><a></a><div id=\"3\"><a></a><div id=\"4\"><a></a><div id=\"5\"><a></a><div id=\"6\"><a></a><div id=\"7\"><a></a><div id=\"8\"><a><div id=\"9\"><div id=\"10\">A</div></div></a></div></div></div></div></div></div></div></div></b></body></html>",
+        "noQuirksBodyHtml": "<a><b></b></a><b><div id=\"1\"><a></a><div id=\"2\"><a></a><div id=\"3\"><a></a><div id=\"4\"><a></a><div id=\"5\"><a></a><div id=\"6\"><a></a><div id=\"7\"><a></a><div id=\"8\"><a><div id=\"9\"><div id=\"10\">A</div></div></a></div></div></div></div></div></div></div></div></b>"
+      }
+    },
+    {
+      "data": "<cite><b><cite><i><cite><i><cite><i><div>X</b>TEST",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,46): adoption-agency-1.3",
+        "(1,50): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "cite": true,
+            "b": true,
+            "i": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "cite",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "cite",
+                            "children": [
+                              {
+                                "tag": "i",
+                                "children": [
+                                  {
+                                    "tag": "cite",
+                                    "children": [
+                                      {
+                                        "tag": "i",
+                                        "children": [
+                                          {
+                                            "tag": "cite",
+                                            "children": [
+                                              {
+                                                "tag": "i"
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "i",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "tag": "div",
+                                "children": [
+                                  {
+                                    "tag": "b",
+                                    "children": [
+                                      {
+                                        "text": "X"
+                                      }
+                                    ]
+                                  },
+                                  {
+                                    "text": "TEST"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><cite><b><cite><i><cite><i><cite><i></i></cite></i></cite></i></cite></b><i><i><div><b>X</b>TEST</div></i></i></cite></body></html>",
+        "noQuirksBodyHtml": "<cite><b><cite><i><cite><i><cite><i></i></cite></i></cite></i></cite></b><i><i><div><b>X</b>TEST</div></i></i></cite>"
+      }
+    }
+  ],
+  "tests23.dat": [
+    {
+      "data": "<p><font size=4><font color=red><font size=4><font size=4><font size=4><font size=4><font size=4><font color=red><p>X",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,116): unexpected-end-tag",
+        "(1,117): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "font": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "attrs": [
+                          {
+                            "name": "size",
+                            "value": "4"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "font",
+                            "attrs": [
+                              {
+                                "name": "color",
+                                "value": "red"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "tag": "font",
+                                "attrs": [
+                                  {
+                                    "name": "size",
+                                    "value": "4"
+                                  }
+                                ],
+                                "children": [
+                                  {
+                                    "tag": "font",
+                                    "attrs": [
+                                      {
+                                        "name": "size",
+                                        "value": "4"
+                                      }
+                                    ],
+                                    "children": [
+                                      {
+                                        "tag": "font",
+                                        "attrs": [
+                                          {
+                                            "name": "size",
+                                            "value": "4"
+                                          }
+                                        ],
+                                        "children": [
+                                          {
+                                            "tag": "font",
+                                            "attrs": [
+                                              {
+                                                "name": "size",
+                                                "value": "4"
+                                              }
+                                            ],
+                                            "children": [
+                                              {
+                                                "tag": "font",
+                                                "attrs": [
+                                                  {
+                                                    "name": "size",
+                                                    "value": "4"
+                                                  }
+                                                ],
+                                                "children": [
+                                                  {
+                                                    "tag": "font",
+                                                    "attrs": [
+                                                      {
+                                                        "name": "color",
+                                                        "value": "red"
+                                                      }
+                                                    ]
+                                                  }
+                                                ]
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "attrs": [
+                          {
+                            "name": "color",
+                            "value": "red"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "font",
+                            "attrs": [
+                              {
+                                "name": "size",
+                                "value": "4"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "tag": "font",
+                                "attrs": [
+                                  {
+                                    "name": "size",
+                                    "value": "4"
+                                  }
+                                ],
+                                "children": [
+                                  {
+                                    "tag": "font",
+                                    "attrs": [
+                                      {
+                                        "name": "size",
+                                        "value": "4"
+                                      }
+                                    ],
+                                    "children": [
+                                      {
+                                        "tag": "font",
+                                        "attrs": [
+                                          {
+                                            "name": "color",
+                                            "value": "red"
+                                          }
+                                        ],
+                                        "children": [
+                                          {
+                                            "text": "X"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><font size=\"4\"><font color=\"red\"><font size=\"4\"><font size=\"4\"><font size=\"4\"><font size=\"4\"><font size=\"4\"><font color=\"red\"></font></font></font></font></font></font></font></font></p><p><font color=\"red\"><font size=\"4\"><font size=\"4\"><font size=\"4\"><font color=\"red\">X</font></font></font></font></font></p></body></html>",
+        "noQuirksBodyHtml": "<p><font size=\"4\"><font color=\"red\"><font size=\"4\"><font size=\"4\"><font size=\"4\"><font size=\"4\"><font size=\"4\"><font color=\"red\"></font></font></font></font></font></font></font></font></p><p><font color=\"red\"><font size=\"4\"><font size=\"4\"><font size=\"4\"><font color=\"red\">X</font></font></font></font></font></p>"
+      }
+    },
+    {
+      "data": "<p><font size=4><font size=4><font size=4><font size=4><p>X",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,58): unexpected-end-tag",
+        "(1,59): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "font": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "attrs": [
+                          {
+                            "name": "size",
+                            "value": "4"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "font",
+                            "attrs": [
+                              {
+                                "name": "size",
+                                "value": "4"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "tag": "font",
+                                "attrs": [
+                                  {
+                                    "name": "size",
+                                    "value": "4"
+                                  }
+                                ],
+                                "children": [
+                                  {
+                                    "tag": "font",
+                                    "attrs": [
+                                      {
+                                        "name": "size",
+                                        "value": "4"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "attrs": [
+                          {
+                            "name": "size",
+                            "value": "4"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "font",
+                            "attrs": [
+                              {
+                                "name": "size",
+                                "value": "4"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "tag": "font",
+                                "attrs": [
+                                  {
+                                    "name": "size",
+                                    "value": "4"
+                                  }
+                                ],
+                                "children": [
+                                  {
+                                    "text": "X"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><font size=\"4\"><font size=\"4\"><font size=\"4\"><font size=\"4\"></font></font></font></font></p><p><font size=\"4\"><font size=\"4\"><font size=\"4\">X</font></font></font></p></body></html>",
+        "noQuirksBodyHtml": "<p><font size=\"4\"><font size=\"4\"><font size=\"4\"><font size=\"4\"></font></font></font></font></p><p><font size=\"4\"><font size=\"4\"><font size=\"4\">X</font></font></font></p>"
+      }
+    },
+    {
+      "data": "<p><font size=4><font size=4><font size=4><font size=\"5\"><font size=4><p>X",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,73): unexpected-end-tag",
+        "(1,74): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "font": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "attrs": [
+                          {
+                            "name": "size",
+                            "value": "4"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "font",
+                            "attrs": [
+                              {
+                                "name": "size",
+                                "value": "4"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "tag": "font",
+                                "attrs": [
+                                  {
+                                    "name": "size",
+                                    "value": "4"
+                                  }
+                                ],
+                                "children": [
+                                  {
+                                    "tag": "font",
+                                    "attrs": [
+                                      {
+                                        "name": "size",
+                                        "value": "5"
+                                      }
+                                    ],
+                                    "children": [
+                                      {
+                                        "tag": "font",
+                                        "attrs": [
+                                          {
+                                            "name": "size",
+                                            "value": "4"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "attrs": [
+                          {
+                            "name": "size",
+                            "value": "4"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "font",
+                            "attrs": [
+                              {
+                                "name": "size",
+                                "value": "4"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "tag": "font",
+                                "attrs": [
+                                  {
+                                    "name": "size",
+                                    "value": "5"
+                                  }
+                                ],
+                                "children": [
+                                  {
+                                    "tag": "font",
+                                    "attrs": [
+                                      {
+                                        "name": "size",
+                                        "value": "4"
+                                      }
+                                    ],
+                                    "children": [
+                                      {
+                                        "text": "X"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><font size=\"4\"><font size=\"4\"><font size=\"4\"><font size=\"5\"><font size=\"4\"></font></font></font></font></font></p><p><font size=\"4\"><font size=\"4\"><font size=\"5\"><font size=\"4\">X</font></font></font></font></p></body></html>",
+        "noQuirksBodyHtml": "<p><font size=\"4\"><font size=\"4\"><font size=\"4\"><font size=\"5\"><font size=\"4\"></font></font></font></font></font></p><p><font size=\"4\"><font size=\"4\"><font size=\"5\"><font size=\"4\">X</font></font></font></font></p>"
+      }
+    },
+    {
+      "data": "<p><font size=4 id=a><font size=4 id=b><font size=4><font size=4><p>X",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,68): unexpected-end-tag",
+        "(1,69): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "font": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "attrs": [
+                          {
+                            "name": "id",
+                            "value": "a"
+                          },
+                          {
+                            "name": "size",
+                            "value": "4"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "font",
+                            "attrs": [
+                              {
+                                "name": "id",
+                                "value": "b"
+                              },
+                              {
+                                "name": "size",
+                                "value": "4"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "tag": "font",
+                                "attrs": [
+                                  {
+                                    "name": "size",
+                                    "value": "4"
+                                  }
+                                ],
+                                "children": [
+                                  {
+                                    "tag": "font",
+                                    "attrs": [
+                                      {
+                                        "name": "size",
+                                        "value": "4"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "attrs": [
+                          {
+                            "name": "id",
+                            "value": "a"
+                          },
+                          {
+                            "name": "size",
+                            "value": "4"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "font",
+                            "attrs": [
+                              {
+                                "name": "id",
+                                "value": "b"
+                              },
+                              {
+                                "name": "size",
+                                "value": "4"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "tag": "font",
+                                "attrs": [
+                                  {
+                                    "name": "size",
+                                    "value": "4"
+                                  }
+                                ],
+                                "children": [
+                                  {
+                                    "tag": "font",
+                                    "attrs": [
+                                      {
+                                        "name": "size",
+                                        "value": "4"
+                                      }
+                                    ],
+                                    "children": [
+                                      {
+                                        "text": "X"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><font size=\"4\" id=\"a\"><font size=\"4\" id=\"b\"><font size=\"4\"><font size=\"4\"></font></font></font></font></p><p><font size=\"4\" id=\"a\"><font size=\"4\" id=\"b\"><font size=\"4\"><font size=\"4\">X</font></font></font></font></p></body></html>",
+        "noQuirksBodyHtml": "<p><font size=\"4\" id=\"a\"><font size=\"4\" id=\"b\"><font size=\"4\"><font size=\"4\"></font></font></font></font></p><p><font size=\"4\" id=\"a\"><font size=\"4\" id=\"b\"><font size=\"4\"><font size=\"4\">X</font></font></font></font></p>"
+      }
+    },
+    {
+      "data": "<p><b id=a><b id=a><b id=a><b><object><b id=a><b id=a>X</object><p>Y",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,64): end-tag-too-early",
+        "(1,67): unexpected-end-tag",
+        "(1,68): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "b": true,
+            "object": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "attrs": [
+                          {
+                            "name": "id",
+                            "value": "a"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "b",
+                            "attrs": [
+                              {
+                                "name": "id",
+                                "value": "a"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "tag": "b",
+                                "attrs": [
+                                  {
+                                    "name": "id",
+                                    "value": "a"
+                                  }
+                                ],
+                                "children": [
+                                  {
+                                    "tag": "b",
+                                    "children": [
+                                      {
+                                        "tag": "object",
+                                        "children": [
+                                          {
+                                            "tag": "b",
+                                            "attrs": [
+                                              {
+                                                "name": "id",
+                                                "value": "a"
+                                              }
+                                            ],
+                                            "children": [
+                                              {
+                                                "tag": "b",
+                                                "attrs": [
+                                                  {
+                                                    "name": "id",
+                                                    "value": "a"
+                                                  }
+                                                ],
+                                                "children": [
+                                                  {
+                                                    "text": "X"
+                                                  }
+                                                ]
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "attrs": [
+                          {
+                            "name": "id",
+                            "value": "a"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "b",
+                            "attrs": [
+                              {
+                                "name": "id",
+                                "value": "a"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "tag": "b",
+                                "attrs": [
+                                  {
+                                    "name": "id",
+                                    "value": "a"
+                                  }
+                                ],
+                                "children": [
+                                  {
+                                    "tag": "b",
+                                    "children": [
+                                      {
+                                        "text": "Y"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><b id=\"a\"><b id=\"a\"><b id=\"a\"><b><object><b id=\"a\"><b id=\"a\">X</b></b></object></b></b></b></b></p><p><b id=\"a\"><b id=\"a\"><b id=\"a\"><b>Y</b></b></b></b></p></body></html>",
+        "noQuirksBodyHtml": "<p><b id=\"a\"><b id=\"a\"><b id=\"a\"><b><object><b id=\"a\"><b id=\"a\">X</b></b></object></b></b></b></b></p><p><b id=\"a\"><b id=\"a\"><b id=\"a\"><b>Y</b></b></b></b></p>"
+      }
+    }
+  ],
+  "tests24.dat": [
+    {
+      "data": "<!DOCTYPE html>&NotEqualTilde;",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "≂̸"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>≂̸</body></html>",
+        "noQuirksBodyHtml": "≂̸"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>&NotEqualTilde;A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "≂̸A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>≂̸A</body></html>",
+        "noQuirksBodyHtml": "≂̸A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>&ThickSpace;",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "  "
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>  </body></html>",
+        "noQuirksBodyHtml": "  "
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>&ThickSpace;A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "  A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>  A</body></html>",
+        "noQuirksBodyHtml": "  A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>&NotSubset;",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "⊂⃒"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>⊂⃒</body></html>",
+        "noQuirksBodyHtml": "⊂⃒"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>&NotSubset;A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "⊂⃒A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>⊂⃒A</body></html>",
+        "noQuirksBodyHtml": "⊂⃒A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>&Gopf;",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "𝔾"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>𝔾</body></html>",
+        "noQuirksBodyHtml": "𝔾"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html>&Gopf;A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "𝔾A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>𝔾A</body></html>",
+        "noQuirksBodyHtml": "𝔾A"
+      }
+    }
+  ],
+  "tests25.dat": [
+    {
+      "data": "<!DOCTYPE html><body><foo>A",
+      "errors": [
+        "(1,27): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "foo": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "foo",
+                    "children": [
+                      {
+                        "text": "A"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><foo>A</foo></body></html>",
+        "noQuirksBodyHtml": "<foo>A</foo>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><area>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "area": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "area"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><area>A</body></html>",
+        "noQuirksBodyHtml": "<area>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><base>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "base": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "base"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><base>A</body></html>",
+        "noQuirksBodyHtml": "<base>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><basefont>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "basefont": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "basefont"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><basefont>A</body></html>",
+        "noQuirksBodyHtml": "<basefont>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><bgsound>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "bgsound": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "bgsound"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><bgsound>A</body></html>",
+        "noQuirksBodyHtml": "<bgsound>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><br>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "br": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "br"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><br>A</body></html>",
+        "noQuirksBodyHtml": "<br>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><col>A",
+      "errors": [
+        "(1,26): unexpected-start-tag-ignored"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>A</body></html>",
+        "noQuirksBodyHtml": "A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><command>A",
+      "errors": [
+        "eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "command": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "command",
+                    "children": [
+                      {
+                        "text": "A"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><command>A</command></body></html>",
+        "noQuirksBodyHtml": "<command>A</command>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><menuitem>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "menuitem": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "menuitem"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><menuitem>A</body></html>",
+        "noQuirksBodyHtml": "<menuitem>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><embed>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "embed": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "embed"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><embed>A</body></html>",
+        "noQuirksBodyHtml": "<embed>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><frame>A",
+      "errors": [
+        "(1,28): unexpected-start-tag-ignored"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>A</body></html>",
+        "noQuirksBodyHtml": "A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><hr>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "hr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "hr"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><hr>A</body></html>",
+        "noQuirksBodyHtml": "<hr>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><img>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "img": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "img"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><img>A</body></html>",
+        "noQuirksBodyHtml": "<img>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><input>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "input": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "input"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><input>A</body></html>",
+        "noQuirksBodyHtml": "<input>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><keygen>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "keygen": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "keygen"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><keygen>A</body></html>",
+        "noQuirksBodyHtml": "<keygen>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><link>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "link": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "link"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><link>A</body></html>",
+        "noQuirksBodyHtml": "<link>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><meta>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "meta": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "meta"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><meta>A</body></html>",
+        "noQuirksBodyHtml": "<meta>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><param>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "param": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "param"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><param>A</body></html>",
+        "noQuirksBodyHtml": "<param>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><source>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "source": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "source"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><source>A</body></html>",
+        "noQuirksBodyHtml": "<source>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><track>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "track": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "track"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><track>A</body></html>",
+        "noQuirksBodyHtml": "<track>A"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><wbr>A",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "wbr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "wbr"
+                  },
+                  {
+                    "text": "A"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><wbr>A</body></html>",
+        "noQuirksBodyHtml": "<wbr>A"
+      }
+    }
+  ],
+  "tests26.dat": [
+    {
+      "data": "<!DOCTYPE html><body><a href='#1'><nobr>1<nobr></a><br><a href='#2'><nobr>2<nobr></a><br><a href='#3'><nobr>3<nobr></a>",
+      "errors": [
+        "(1,47): unexpected-start-tag-implies-end-tag",
+        "(1,51): adoption-agency-1.3",
+        "(1,74): unexpected-start-tag-implies-end-tag",
+        "(1,74): adoption-agency-1.3",
+        "(1,81): unexpected-start-tag-implies-end-tag",
+        "(1,85): adoption-agency-1.3",
+        "(1,108): unexpected-start-tag-implies-end-tag",
+        "(1,108): adoption-agency-1.3",
+        "(1,115): unexpected-start-tag-implies-end-tag",
+        "(1,119): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "nobr": true,
+            "br": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "attrs": [
+                      {
+                        "name": "href",
+                        "value": "#1"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "1"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "nobr"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "nobr",
+                    "children": [
+                      {
+                        "tag": "br"
+                      },
+                      {
+                        "tag": "a",
+                        "attrs": [
+                          {
+                            "name": "href",
+                            "value": "#2"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "a",
+                    "attrs": [
+                      {
+                        "name": "href",
+                        "value": "#2"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "2"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "nobr"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "nobr",
+                    "children": [
+                      {
+                        "tag": "br"
+                      },
+                      {
+                        "tag": "a",
+                        "attrs": [
+                          {
+                            "name": "href",
+                            "value": "#3"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "a",
+                    "attrs": [
+                      {
+                        "name": "href",
+                        "value": "#3"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "3"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "nobr"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><a href=\"#1\"><nobr>1</nobr><nobr></nobr></a><nobr><br><a href=\"#2\"></a></nobr><a href=\"#2\"><nobr>2</nobr><nobr></nobr></a><nobr><br><a href=\"#3\"></a></nobr><a href=\"#3\"><nobr>3</nobr><nobr></nobr></a></body></html>",
+        "noQuirksBodyHtml": "<a href=\"#1\"><nobr>1</nobr><nobr></nobr></a><nobr><br><a href=\"#2\"></a></nobr><a href=\"#2\"><nobr>2</nobr><nobr></nobr></a><nobr><br><a href=\"#3\"></a></nobr><a href=\"#3\"><nobr>3</nobr><nobr></nobr></a>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><b><nobr>1<nobr></b><i><nobr>2<nobr></i>3",
+      "errors": [
+        "(1,37): unexpected-start-tag-implies-end-tag",
+        "(1,41): adoption-agency-1.3",
+        "(1,50): unexpected-start-tag-implies-end-tag",
+        "(1,50): adoption-agency-1.3",
+        "(1,57): unexpected-start-tag-implies-end-tag",
+        "(1,61): adoption-agency-1.3",
+        "(1,62): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "nobr": true,
+            "i": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "1"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "nobr"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "nobr",
+                    "children": [
+                      {
+                        "tag": "i"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "2"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "nobr"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "nobr",
+                    "children": [
+                      {
+                        "text": "3"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><b><nobr>1</nobr><nobr></nobr></b><nobr><i></i></nobr><i><nobr>2</nobr><nobr></nobr></i><nobr>3</nobr></body></html>",
+        "noQuirksBodyHtml": "<b><nobr>1</nobr><nobr></nobr></b><nobr><i></i></nobr><i><nobr>2</nobr><nobr></nobr></i><nobr>3</nobr>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><b><nobr>1<table><nobr></b><i><nobr>2<nobr></i>3",
+      "errors": [
+        "(1,44): foster-parenting-start-tag",
+        "(1,48): foster-parenting-end-tag",
+        "(1,48): adoption-agency-1.3",
+        "(1,51): foster-parenting-start-tag",
+        "(1,57): foster-parenting-start-tag",
+        "(1,57): nobr-already-in-scope",
+        "(1,57): adoption-agency-1.2",
+        "(1,58): foster-parenting-character",
+        "(1,64): foster-parenting-start-tag",
+        "(1,64): nobr-already-in-scope",
+        "(1,68): foster-parenting-end-tag",
+        "(1,68): adoption-agency-1.2",
+        "(1,69): foster-parenting-character",
+        "(1,69): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "nobr": true,
+            "i": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "1"
+                          },
+                          {
+                            "tag": "nobr",
+                            "children": [
+                              {
+                                "tag": "i"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "tag": "nobr",
+                                "children": [
+                                  {
+                                    "text": "2"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "nobr"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "nobr",
+                            "children": [
+                              {
+                                "text": "3"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "table"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><b><nobr>1<nobr><i></i></nobr><i><nobr>2</nobr><nobr></nobr></i><nobr>3</nobr><table></table></nobr></b></body></html>",
+        "noQuirksBodyHtml": "<b><nobr>1<nobr><i></i></nobr><i><nobr>2</nobr><nobr></nobr></i><nobr>3</nobr><table></table></nobr></b>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><b><nobr>1<table><tr><td><nobr></b><i><nobr>2<nobr></i>3",
+      "errors": [
+        "(1,56): unexpected-end-tag",
+        "(1,65): unexpected-start-tag-implies-end-tag",
+        "(1,65): adoption-agency-1.3",
+        "(1,72): unexpected-start-tag-implies-end-tag",
+        "(1,76): adoption-agency-1.3",
+        "(1,77): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "nobr": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "i": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "1"
+                          },
+                          {
+                            "tag": "table",
+                            "children": [
+                              {
+                                "tag": "tbody",
+                                "children": [
+                                  {
+                                    "tag": "tr",
+                                    "children": [
+                                      {
+                                        "tag": "td",
+                                        "children": [
+                                          {
+                                            "tag": "nobr",
+                                            "children": [
+                                              {
+                                                "tag": "i"
+                                              }
+                                            ]
+                                          },
+                                          {
+                                            "tag": "i",
+                                            "children": [
+                                              {
+                                                "tag": "nobr",
+                                                "children": [
+                                                  {
+                                                    "text": "2"
+                                                  }
+                                                ]
+                                              },
+                                              {
+                                                "tag": "nobr"
+                                              }
+                                            ]
+                                          },
+                                          {
+                                            "tag": "nobr",
+                                            "children": [
+                                              {
+                                                "text": "3"
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><b><nobr>1<table><tbody><tr><td><nobr><i></i></nobr><i><nobr>2</nobr><nobr></nobr></i><nobr>3</nobr></td></tr></tbody></table></nobr></b></body></html>",
+        "noQuirksBodyHtml": "<b><nobr>1<table><tbody><tr><td><nobr><i></i></nobr><i><nobr>2</nobr><nobr></nobr></i><nobr>3</nobr></td></tr></tbody></table></nobr></b>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><b><nobr>1<div><nobr></b><i><nobr>2<nobr></i>3",
+      "errors": [
+        "(1,42): unexpected-start-tag-implies-end-tag",
+        "(1,42): adoption-agency-1.3",
+        "(1,46): adoption-agency-1.3",
+        "(1,46): adoption-agency-1.3",
+        "(1,55): unexpected-start-tag-implies-end-tag",
+        "(1,55): adoption-agency-1.3",
+        "(1,62): unexpected-start-tag-implies-end-tag",
+        "(1,66): adoption-agency-1.3",
+        "(1,67): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "nobr": true,
+            "div": true,
+            "i": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "1"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "nobr"
+                          },
+                          {
+                            "tag": "nobr"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "tag": "i"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "i",
+                        "children": [
+                          {
+                            "tag": "nobr",
+                            "children": [
+                              {
+                                "text": "2"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "nobr"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "3"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><b><nobr>1</nobr></b><div><b><nobr></nobr><nobr></nobr></b><nobr><i></i></nobr><i><nobr>2</nobr><nobr></nobr></i><nobr>3</nobr></div></body></html>",
+        "noQuirksBodyHtml": "<b><nobr>1</nobr></b><div><b><nobr></nobr><nobr></nobr></b><nobr><i></i></nobr><i><nobr>2</nobr><nobr></nobr></i><nobr>3</nobr></div>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><b><nobr>1<nobr></b><div><i><nobr>2<nobr></i>3",
+      "errors": [
+        "(1,37): unexpected-start-tag-implies-end-tag",
+        "(1,41): adoption-agency-1.3",
+        "(1,55): unexpected-start-tag-implies-end-tag",
+        "(1,55): adoption-agency-1.3",
+        "(1,62): unexpected-start-tag-implies-end-tag",
+        "(1,66): adoption-agency-1.3",
+        "(1,67): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "nobr": true,
+            "div": true,
+            "i": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "1"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "nobr"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "tag": "i"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "i",
+                        "children": [
+                          {
+                            "tag": "nobr",
+                            "children": [
+                              {
+                                "text": "2"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "nobr"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "3"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><b><nobr>1</nobr><nobr></nobr></b><div><nobr><i></i></nobr><i><nobr>2</nobr><nobr></nobr></i><nobr>3</nobr></div></body></html>",
+        "noQuirksBodyHtml": "<b><nobr>1</nobr><nobr></nobr></b><div><nobr><i></i></nobr><i><nobr>2</nobr><nobr></nobr></i><nobr>3</nobr></div>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><b><nobr>1<nobr><ins></b><i><nobr>",
+      "errors": [
+        "(1,37): unexpected-start-tag-implies-end-tag",
+        "(1,46): adoption-agency-1.3",
+        "(1,55): unexpected-start-tag-implies-end-tag",
+        "(1,55): adoption-agency-1.3",
+        "(1,55): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "nobr": true,
+            "ins": true,
+            "i": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "1"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "tag": "ins"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "nobr",
+                    "children": [
+                      {
+                        "tag": "i"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "tag": "nobr"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><b><nobr>1</nobr><nobr><ins></ins></nobr></b><nobr><i></i></nobr><i><nobr></nobr></i></body></html>",
+        "noQuirksBodyHtml": "<b><nobr>1</nobr><nobr><ins></ins></nobr></b><nobr><i></i></nobr><i><nobr></nobr></i>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><b><nobr>1<ins><nobr></b><i>2",
+      "errors": [
+        "(1,42): unexpected-start-tag-implies-end-tag",
+        "(1,42): adoption-agency-1.3",
+        "(1,46): adoption-agency-1.3",
+        "(1,50): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "nobr": true,
+            "ins": true,
+            "i": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "1"
+                          },
+                          {
+                            "tag": "ins"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "nobr"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "nobr",
+                    "children": [
+                      {
+                        "tag": "i",
+                        "children": [
+                          {
+                            "text": "2"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><b><nobr>1<ins></ins></nobr><nobr></nobr></b><nobr><i>2</i></nobr></body></html>",
+        "noQuirksBodyHtml": "<b><nobr>1<ins></ins></nobr><nobr></nobr></b><nobr><i>2</i></nobr>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><b>1<nobr></b><i><nobr>2</i>",
+      "errors": [
+        "(1,35): adoption-agency-1.3",
+        "(1,44): unexpected-start-tag-implies-end-tag",
+        "(1,44): adoption-agency-1.3",
+        "(1,49): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "nobr": true,
+            "i": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "1"
+                      },
+                      {
+                        "tag": "nobr"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "nobr",
+                    "children": [
+                      {
+                        "tag": "i"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "tag": "nobr",
+                        "children": [
+                          {
+                            "text": "2"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><b>1<nobr></nobr></b><nobr><i></i></nobr><i><nobr>2</nobr></i></body></html>",
+        "noQuirksBodyHtml": "<b>1<nobr></nobr></b><nobr><i></i></nobr><i><nobr>2</nobr></i>"
+      }
+    },
+    {
+      "data": "<p><code x</code></p>\n",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,11): invalid-character-in-attribute-name",
+        "(1,12): unexpected-character-after-solidus-in-tag",
+        "(1,21): unexpected-end-tag",
+        "(2,0): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "code": true
+          },
+          "attrWithFunnyChar": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "code",
+                        "attrs": [
+                          {
+                            "name": "code",
+                            "value": ""
+                          },
+                          {
+                            "name": "x<",
+                            "value": ""
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "code",
+                    "attrs": [
+                      {
+                        "name": "code",
+                        "value": ""
+                      },
+                      {
+                        "name": "x<",
+                        "value": ""
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "\n"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><code x<=\"\" code=\"\"></code></p><code x<=\"\" code=\"\">\n</code></body></html>",
+        "noQuirksBodyHtml": "<p><code x<=\"\" code=\"\"></code></p><code x<=\"\" code=\"\">\n</code>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><svg><foreignObject><p><i></p>a",
+      "errors": [
+        "(1,45): unexpected-end-tag",
+        "(1,46): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg foreignObject": true,
+            "p": true,
+            "i": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "foreignObject",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "p",
+                            "children": [
+                              {
+                                "tag": "i"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": "a"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><svg><foreignObject><p><i></i></p><i>a</i></foreignObject></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><foreignObject><p><i></i></p><i>a</i></foreignObject></svg>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><table><tr><td><svg><foreignObject><p><i></p>a",
+      "errors": [
+        "(1,60): unexpected-end-tag",
+        "(1,61): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "svg svg": true,
+            "svg foreignObject": true,
+            "p": true,
+            "i": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "svg",
+                                    "ns": "http://www.w3.org/2000/svg",
+                                    "children": [
+                                      {
+                                        "tag": "foreignObject",
+                                        "ns": "http://www.w3.org/2000/svg",
+                                        "children": [
+                                          {
+                                            "tag": "p",
+                                            "children": [
+                                              {
+                                                "tag": "i"
+                                              }
+                                            ]
+                                          },
+                                          {
+                                            "tag": "i",
+                                            "children": [
+                                              {
+                                                "text": "a"
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><svg><foreignObject><p><i></i></p><i>a</i></foreignObject></svg></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><svg><foreignObject><p><i></i></p><i>a</i></foreignObject></svg></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><math><mtext><p><i></p>a",
+      "errors": [
+        "(1,38): unexpected-end-tag",
+        "(1,39): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mtext": true,
+            "p": true,
+            "i": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mtext",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "p",
+                            "children": [
+                              {
+                                "tag": "i"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": "a"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mtext><p><i></i></p><i>a</i></mtext></math></body></html>",
+        "noQuirksBodyHtml": "<math><mtext><p><i></i></p><i>a</i></mtext></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><table><tr><td><math><mtext><p><i></p>a",
+      "errors": [
+        "(1,53): unexpected-end-tag",
+        "(1,54): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "math math": true,
+            "math mtext": true,
+            "p": true,
+            "i": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "math",
+                                    "ns": "http://www.w3.org/1998/Math/MathML",
+                                    "children": [
+                                      {
+                                        "tag": "mtext",
+                                        "ns": "http://www.w3.org/1998/Math/MathML",
+                                        "children": [
+                                          {
+                                            "tag": "p",
+                                            "children": [
+                                              {
+                                                "tag": "i"
+                                              }
+                                            ]
+                                          },
+                                          {
+                                            "tag": "i",
+                                            "children": [
+                                              {
+                                                "text": "a"
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><math><mtext><p><i></i></p><i>a</i></mtext></math></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><math><mtext><p><i></i></p><i>a</i></mtext></math></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><div><!/div>a",
+      "errors": [
+        "(1,28): expected-dashes-or-doctype",
+        "(1,34): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          },
+          "doctype": true,
+          "comment": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "comment": "/div"
+                      },
+                      {
+                        "text": "a"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><div><!--/div-->a</div></body></html>",
+        "noQuirksBodyHtml": "<div><!--/div-->a</div>"
+      }
+    },
+    {
+      "data": "<button><p><button>",
+      "errors": [
+        "Line 1 Col 8 Unexpected start tag (button). Expected DOCTYPE.",
+        "Line 1 Col 19 Unexpected start tag (button) implies end tag (button).",
+        "Line 1 Col 19 Expected closing tag. Unexpected end of file."
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "button": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "button",
+                    "children": [
+                      {
+                        "tag": "p"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "button"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><button><p></p></button><button></button></body></html>",
+        "noQuirksBodyHtml": "<button><p></p></button><button></button>"
+      }
+    }
+  ],
+  "tests3.dat": [
+    {
+      "data": "<head></head><style></style>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,20): unexpected-start-tag-out-of-my-head"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style"
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style></style></head><body></body></html>",
+        "noQuirksBodyHtml": "<style></style>"
+      }
+    },
+    {
+      "data": "<head></head><script></script>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,21): unexpected-start-tag-out-of-my-head"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script"
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script></script></head><body></body></html>",
+        "noQuirksBodyHtml": "<script></script>"
+      }
+    },
+    {
+      "data": "<head></head><!-- --><style></style><!-- --><script></script>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,28): unexpected-start-tag-out-of-my-head",
+        "(1,52): unexpected-start-tag-out-of-my-head"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "script": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style"
+                  },
+                  {
+                    "tag": "script"
+                  }
+                ]
+              },
+              {
+                "comment": " "
+              },
+              {
+                "comment": " "
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style></style><script></script></head><!-- --><!-- --><body></body></html>",
+        "noQuirksBodyHtml": "<!-- --><style></style><!-- --><script></script>"
+      }
+    },
+    {
+      "data": "<head></head><!-- -->x<style></style><!-- --><script></script>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "style": true,
+            "script": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "comment": " "
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "x"
+                  },
+                  {
+                    "tag": "style"
+                  },
+                  {
+                    "comment": " "
+                  },
+                  {
+                    "tag": "script"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><!-- --><body>x<style></style><!-- --><script></script></body></html>",
+        "noQuirksBodyHtml": "<!-- -->x<style></style><!-- --><script></script>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html><head></head><body><pre>\n</pre></body></html>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "pre": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "pre"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><pre></pre></body></html>",
+        "noQuirksBodyHtml": "<pre></pre>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html><head></head><body><pre>\nfoo</pre></body></html>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "pre": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "pre",
+                    "children": [
+                      {
+                        "text": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><pre>foo</pre></body></html>",
+        "noQuirksBodyHtml": "<pre>foo</pre>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html><head></head><body><pre>\n\nfoo</pre></body></html>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "pre": true
+          },
+          "doctype": true,
+          "extraNL": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "pre",
+                    "children": [
+                      {
+                        "text": "\nfoo",
+                        "extraNL": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><pre>\n\nfoo</pre></body></html>",
+        "noQuirksBodyHtml": "<pre>\n\nfoo</pre>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html><head></head><body><pre>\nfoo\n</pre></body></html>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "pre": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "pre",
+                    "children": [
+                      {
+                        "text": "foo\n"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><pre>foo\n</pre></body></html>",
+        "noQuirksBodyHtml": "<pre>foo\n</pre>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html><head></head><body><pre>x</pre><span>\n</span></body></html>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "pre": true,
+            "span": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "pre",
+                    "children": [
+                      {
+                        "text": "x"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "span",
+                    "children": [
+                      {
+                        "text": "\n"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><pre>x</pre><span>\n</span></body></html>",
+        "noQuirksBodyHtml": "<pre>x</pre><span>\n</span>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html><head></head><body><pre>x\ny</pre></body></html>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "pre": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "pre",
+                    "children": [
+                      {
+                        "text": "x\ny"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><pre>x\ny</pre></body></html>",
+        "noQuirksBodyHtml": "<pre>x\ny</pre>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html><head></head><body><pre>x<div>\ny</pre></body></html>",
+      "errors": [
+        "(2,7): end-tag-too-early"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "pre": true,
+            "div": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "pre",
+                    "children": [
+                      {
+                        "text": "x"
+                      },
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "text": "\ny"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><pre>x<div>\ny</div></pre></body></html>",
+        "noQuirksBodyHtml": "<pre>x<div>\ny</div></pre>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><pre>&#x0a;&#x0a;A</pre>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "pre": true
+          },
+          "doctype": true,
+          "extraNL": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "pre",
+                    "children": [
+                      {
+                        "text": "\nA",
+                        "extraNL": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><pre>\n\nA</pre></body></html>",
+        "noQuirksBodyHtml": "<pre>\n\nA</pre>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><HTML><META><HEAD></HEAD></HTML>",
+      "errors": [
+        "(1,33): two-heads-are-not-better-than-one"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "meta": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "meta"
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><meta></head><body></body></html>",
+        "noQuirksBodyHtml": "<meta>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><HTML><HEAD><head></HEAD></HTML>",
+      "errors": [
+        "(1,33): two-heads-are-not-better-than-one"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<textarea>foo<span>bar</span><i>baz",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,35): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": "foo<span>bar</span><i>baz",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><textarea>foo&lt;span&gt;bar&lt;/span&gt;&lt;i&gt;baz</textarea></body></html>",
+        "noQuirksBodyHtml": "<textarea>foo&lt;span&gt;bar&lt;/span&gt;&lt;i&gt;baz</textarea>"
+      }
+    },
+    {
+      "data": "<title>foo<span>bar</em><i>baz",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,30): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "foo<span>bar</em><i>baz",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><title>foo&lt;span&gt;bar&lt;/em&gt;&lt;i&gt;baz</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title>foo&lt;span&gt;bar&lt;/em&gt;&lt;i&gt;baz</title>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><textarea>\n</textarea>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><textarea></textarea></body></html>",
+        "noQuirksBodyHtml": "<textarea></textarea>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><textarea>\nfoo</textarea>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><textarea>foo</textarea></body></html>",
+        "noQuirksBodyHtml": "<textarea>foo</textarea>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><textarea>\n\nfoo</textarea>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "doctype": true,
+          "extraNL": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": "\nfoo",
+                        "extraNL": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><textarea>\n\nfoo</textarea></body></html>",
+        "noQuirksBodyHtml": "<textarea>\n\nfoo</textarea>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><html><head></head><body><ul><li><div><p><li></ul></body></html>",
+      "errors": [
+        "(1,60): end-tag-too-early"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ul": true,
+            "li": true,
+            "div": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ul",
+                    "children": [
+                      {
+                        "tag": "li",
+                        "children": [
+                          {
+                            "tag": "div",
+                            "children": [
+                              {
+                                "tag": "p"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "li"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><ul><li><div><p></p></div></li><li></li></ul></body></html>",
+        "noQuirksBodyHtml": "<ul><li><div><p></p></div></li><li></li></ul>"
+      }
+    },
+    {
+      "data": "<!doctype html><nobr><nobr><nobr>",
+      "errors": [
+        "(1,27): unexpected-start-tag-implies-end-tag",
+        "(1,33): unexpected-start-tag-implies-end-tag",
+        "(1,33): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "nobr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "nobr"
+                  },
+                  {
+                    "tag": "nobr"
+                  },
+                  {
+                    "tag": "nobr"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><nobr></nobr><nobr></nobr><nobr></nobr></body></html>",
+        "noQuirksBodyHtml": "<nobr></nobr><nobr></nobr><nobr></nobr>"
+      }
+    },
+    {
+      "data": "<!doctype html><nobr><nobr></nobr><nobr>",
+      "errors": [
+        "(1,27): unexpected-start-tag-implies-end-tag",
+        "(1,40): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "nobr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "nobr"
+                  },
+                  {
+                    "tag": "nobr"
+                  },
+                  {
+                    "tag": "nobr"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><nobr></nobr><nobr></nobr><nobr></nobr></body></html>",
+        "noQuirksBodyHtml": "<nobr></nobr><nobr></nobr><nobr></nobr>"
+      }
+    },
+    {
+      "data": "<!doctype html><html><body><p><table></table></body></html>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p></p><table></table></body></html>",
+        "noQuirksBodyHtml": "<p></p><table></table>"
+      }
+    },
+    {
+      "data": "<p><table></table>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "table"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><table></table></p></body></html>",
+        "noQuirksBodyHtml": "<p></p><table></table>"
+      }
+    }
+  ],
+  "tests4.dat": [
+    {
+      "data": "direct div content",
+      "errors": [],
+      "fragment": {
+        "name": "div"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "direct div content"
+          }
+        ],
+        "html": "direct div content",
+        "noQuirksBodyHtml": "direct div content"
+      }
+    },
+    {
+      "data": "direct textarea content",
+      "errors": [],
+      "fragment": {
+        "name": "textarea"
+      },
+      "document": {
+        "props": {
+          "tags": {}
+        },
+        "tree": [
+          {
+            "text": "direct textarea content"
+          }
+        ],
+        "html": "direct textarea content",
+        "noQuirksBodyHtml": "direct textarea content"
+      }
+    },
+    {
+      "data": "textarea content with <em>pseudo</em> <foo>markup",
+      "errors": [],
+      "fragment": {
+        "name": "textarea"
+      },
+      "document": {
+        "props": {
+          "tags": {},
+          "escaped": true
+        },
+        "tree": [
+          {
+            "text": "textarea content with <em>pseudo</em> <foo>markup",
+            "escaped": true
+          }
+        ],
+        "html": "textarea content with &lt;em&gt;pseudo&lt;/em&gt; &lt;foo&gt;markup",
+        "noQuirksBodyHtml": "textarea content with <em>pseudo</em> <foo>markup</foo>"
+      }
+    },
+    {
+      "data": "this is &#x0043;DATA inside a <style> element",
+      "errors": [],
+      "fragment": {
+        "name": "style"
+      },
+      "document": {
+        "props": {
+          "tags": {},
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "text": "this is &#x0043;DATA inside a <style> element",
+            "no_escape": true
+          }
+        ],
+        "html": "this is &#x0043;DATA inside a <style> element",
+        "noQuirksBodyHtml": "this is CDATA inside a <style> element</style>"
+      }
+    },
+    {
+      "data": "</plaintext>",
+      "errors": [],
+      "fragment": {
+        "name": "plaintext"
+      },
+      "document": {
+        "props": {
+          "tags": {},
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "text": "</plaintext>",
+            "no_escape": true
+          }
+        ],
+        "html": "</plaintext>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "setting html's innerHTML",
+      "errors": [],
+      "fragment": {
+        "name": "html"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "head"
+          },
+          {
+            "tag": "body",
+            "children": [
+              {
+                "text": "setting html's innerHTML"
+              }
+            ]
+          }
+        ],
+        "html": "<head></head><body>setting html's innerHTML</body>",
+        "noQuirksBodyHtml": "setting html's innerHTML"
+      }
+    },
+    {
+      "data": "<title>setting head's innerHTML</title>",
+      "errors": [],
+      "fragment": {
+        "name": "head"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "title": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "title",
+            "children": [
+              {
+                "text": "setting head's innerHTML"
+              }
+            ]
+          }
+        ],
+        "html": "<title>setting head's innerHTML</title>",
+        "noQuirksBodyHtml": "<title>setting head's innerHTML</title>"
+      }
+    }
+  ],
+  "tests5.dat": [
+    {
+      "data": "<style> <!-- </style>x",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": " <!-- ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "x"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style> <!-- </style></head><body>x</body></html>",
+        "noQuirksBodyHtml": "<style> <!-- </style>x"
+      }
+    },
+    {
+      "data": "<style> <!-- </style> --> </style>x",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,34): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": " <!-- ",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": " "
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "--> x",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style> <!-- </style> </head><body>--&gt; x</body></html>",
+        "noQuirksBodyHtml": "<style> <!-- </style> --&gt; x"
+      }
+    },
+    {
+      "data": "<style> <!--> </style>x",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": " <!--> ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "x"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style> <!--> </style></head><body>x</body></html>",
+        "noQuirksBodyHtml": "<style> <!--> </style>x"
+      }
+    },
+    {
+      "data": "<style> <!---> </style>x",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": " <!---> ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "x"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style> <!---> </style></head><body>x</body></html>",
+        "noQuirksBodyHtml": "<style> <!---> </style>x"
+      }
+    },
+    {
+      "data": "<iframe> <!---> </iframe>x",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "iframe": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "iframe",
+                    "children": [
+                      {
+                        "text": " <!---> ",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "x"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><iframe> <!---> </iframe>x</body></html>",
+        "noQuirksBodyHtml": "<iframe> <!---> </iframe>x"
+      }
+    },
+    {
+      "data": "<iframe> <!--- </iframe>->x</iframe> --> </iframe>x",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,36): unexpected-end-tag",
+        "(1,50): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "iframe": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "iframe",
+                    "children": [
+                      {
+                        "text": " <!--- ",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "->x --> x",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><iframe> <!--- </iframe>-&gt;x --&gt; x</body></html>",
+        "noQuirksBodyHtml": "<iframe> <!--- </iframe>-&gt;x --&gt; x"
+      }
+    },
+    {
+      "data": "<script> <!-- </script> --> </script>x",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,37): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "script": true,
+            "body": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": " <!-- ",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": " "
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "--> x",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><script> <!-- </script> </head><body>--&gt; x</body></html>",
+        "noQuirksBodyHtml": "<script> <!-- </script> --&gt; x"
+      }
+    },
+    {
+      "data": "<title> <!-- </title> --> </title>x",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,34): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": " <!-- ",
+                        "escaped": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": " "
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "--> x",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><title> &lt;!-- </title> </head><body>--&gt; x</body></html>",
+        "noQuirksBodyHtml": "<title> &lt;!-- </title> --&gt; x"
+      }
+    },
+    {
+      "data": "<textarea> <!--- </textarea>->x</textarea> --> </textarea>x",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,42): unexpected-end-tag",
+        "(1,58): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "textarea": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "textarea",
+                    "children": [
+                      {
+                        "text": " <!--- ",
+                        "escaped": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "->x --> x",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><textarea> &lt;!--- </textarea>-&gt;x --&gt; x</body></html>",
+        "noQuirksBodyHtml": "<textarea> &lt;!--- </textarea>-&gt;x --&gt; x"
+      }
+    },
+    {
+      "data": "<style> <!</-- </style>x",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style",
+                    "children": [
+                      {
+                        "text": " <!</-- ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "x"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style> <!</-- </style></head><body>x</body></html>",
+        "noQuirksBodyHtml": "<style> <!</-- </style>x"
+      }
+    },
+    {
+      "data": "<p><xmp></xmp>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "xmp": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p"
+                  },
+                  {
+                    "tag": "xmp"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p></p><xmp></xmp></body></html>",
+        "noQuirksBodyHtml": "<p></p><xmp></xmp>"
+      }
+    },
+    {
+      "data": "<xmp> <!-- > --> </xmp>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "xmp": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "xmp",
+                    "children": [
+                      {
+                        "text": " <!-- > --> ",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><xmp> <!-- > --> </xmp></body></html>",
+        "noQuirksBodyHtml": "<xmp> <!-- > --> </xmp>"
+      }
+    },
+    {
+      "data": "<title>&amp;</title>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "&",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><title>&amp;</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title>&amp;</title>"
+      }
+    },
+    {
+      "data": "<title><!--&amp;--></title>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "<!--&-->",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><title>&lt;!--&amp;--&gt;</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title>&lt;!--&amp;--&gt;</title>"
+      }
+    },
+    {
+      "data": "<title><!--</title>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><title>&lt;!--</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title>&lt;!--</title>"
+      }
+    },
+    {
+      "data": "<noscript><!--</noscript>--></noscript>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,39): unexpected-end-tag"
+      ],
+      "script": "on",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true
+          },
+          "no_escape": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "text": "<!--",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "-->",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><noscript><!--</noscript></head><body>--&gt;</body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;!--</noscript>--&gt;"
+      }
+    },
+    {
+      "data": "<noscript><!--</noscript>--></noscript>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag"
+      ],
+      "script": "off",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "noscript": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "noscript",
+                    "children": [
+                      {
+                        "comment": "</noscript>"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><noscript><!--</noscript>--></noscript></head><body></body></html>",
+        "noQuirksBodyHtml": "<noscript>&lt;!--</noscript>--&gt;"
+      }
+    }
+  ],
+  "tests6.dat": [
+    {
+      "data": "<!doctype html></head> <head>",
+      "errors": [
+        "(1,29): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "text": " "
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head> <body></body></html>",
+        "noQuirksBodyHtml": " "
+      }
+    },
+    {
+      "data": "<!doctype html><form><div></form><div>",
+      "errors": [
+        "(1,33): end-tag-too-early-ignored",
+        "(1,38): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "form": true,
+            "div": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "form",
+                    "children": [
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><form><div><div></div></div></form></body></html>",
+        "noQuirksBodyHtml": "<form><div><div></div></div></form>"
+      }
+    },
+    {
+      "data": "<!doctype html><title>&amp;</title>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "doctype": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "&",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><title>&amp;</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title>&amp;</title>"
+      }
+    },
+    {
+      "data": "<!doctype html><title><!--&amp;--></title>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "doctype": true,
+          "escaped": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "<!--&-->",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><title>&lt;!--&amp;--&gt;</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title>&lt;!--&amp;--&gt;</title>"
+      }
+    },
+    {
+      "data": "<!doctype>",
+      "errors": [
+        "(1,9): need-space-after-doctype",
+        "(1,10): expected-doctype-name-but-got-right-bracket",
+        "(1,10): unknown-doctype"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": ""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE ><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!---x",
+      "errors": [
+        "(1,6): eof-in-comment",
+        "(1,6): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": "-x"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!---x--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!---x-->"
+      }
+    },
+    {
+      "data": "<body>\n<div>",
+      "errors": [
+        "(1,6): unexpected-start-tag",
+        "(2,5): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "div"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "text": "\n"
+          },
+          {
+            "tag": "div"
+          }
+        ],
+        "html": "\n<div></div>",
+        "noQuirksBodyHtml": "\n<div></div>"
+      }
+    },
+    {
+      "data": "<frameset></frameset>\nfoo",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(2,1): unexpected-char-after-frameset",
+        "(2,2): unexpected-char-after-frameset",
+        "(2,3): unexpected-char-after-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              },
+              {
+                "text": "\n"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset>\n</html>",
+        "noQuirksBodyHtml": "\nfoo"
+      }
+    },
+    {
+      "data": "<frameset></frameset>\n<noframes>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(2,10): expected-named-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "noframes": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              },
+              {
+                "text": "\n"
+              },
+              {
+                "tag": "noframes"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset>\n<noframes></noframes></html>",
+        "noQuirksBodyHtml": "\n<noframes></noframes>"
+      }
+    },
+    {
+      "data": "<frameset></frameset>\n<div>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(2,5): unexpected-start-tag-after-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              },
+              {
+                "text": "\n"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset>\n</html>",
+        "noQuirksBodyHtml": "\n<div></div>"
+      }
+    },
+    {
+      "data": "<frameset></frameset>\n</html>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              },
+              {
+                "text": "\n"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset>\n</html>",
+        "noQuirksBodyHtml": "\n"
+      }
+    },
+    {
+      "data": "<frameset></frameset>\n</div>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(2,6): unexpected-end-tag-after-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              },
+              {
+                "text": "\n"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset>\n</html>",
+        "noQuirksBodyHtml": "\n"
+      }
+    },
+    {
+      "data": "<form><form>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,12): unexpected-start-tag",
+        "(1,12): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "form": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "form"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><form></form></body></html>",
+        "noQuirksBodyHtml": "<form></form>"
+      }
+    },
+    {
+      "data": "<button><button>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,16): unexpected-start-tag-implies-end-tag",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "button": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "button"
+                  },
+                  {
+                    "tag": "button"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><button></button><button></button></body></html>",
+        "noQuirksBodyHtml": "<button></button><button></button>"
+      }
+    },
+    {
+      "data": "<table><tr><td></th>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,20): unexpected-end-tag",
+        "(1,20): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><caption><td>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,20): unexpected-cell-in-table-body",
+        "(1,20): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption"
+                      },
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><caption></caption><tbody><tr><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><caption></caption><tbody><tr><td></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><caption><div>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,21): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><caption><div></div></caption></table></body></html>",
+        "noQuirksBodyHtml": "<table><caption><div></div></caption></table>"
+      }
+    },
+    {
+      "data": "</caption><div>",
+      "errors": [
+        "(1,10): XXX-undefined-error",
+        "(1,15): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<table><caption><div></caption>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,31): expected-one-end-tag-but-got-another",
+        "(1,31): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><caption><div></div></caption></table></body></html>",
+        "noQuirksBodyHtml": "<table><caption><div></div></caption></table>"
+      }
+    },
+    {
+      "data": "<table><caption></table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><caption></caption></table></body></html>",
+        "noQuirksBodyHtml": "<table><caption></caption></table>"
+      }
+    },
+    {
+      "data": "</table><div>",
+      "errors": [
+        "(1,8): unexpected-end-tag",
+        "(1,13): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<table><caption></body></col></colgroup></html></tbody></td></tfoot></th></thead></tr>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,23): unexpected-end-tag",
+        "(1,29): unexpected-end-tag",
+        "(1,40): unexpected-end-tag",
+        "(1,47): unexpected-end-tag",
+        "(1,55): unexpected-end-tag",
+        "(1,60): unexpected-end-tag",
+        "(1,68): unexpected-end-tag",
+        "(1,73): unexpected-end-tag",
+        "(1,81): unexpected-end-tag",
+        "(1,86): unexpected-end-tag",
+        "(1,86): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><caption></caption></table></body></html>",
+        "noQuirksBodyHtml": "<table><caption></caption></table>"
+      }
+    },
+    {
+      "data": "<table><caption><div></div>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,27): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><caption><div></div></caption></table></body></html>",
+        "noQuirksBodyHtml": "<table><caption><div></div></caption></table>"
+      }
+    },
+    {
+      "data": "<table><tr><td></body></caption></col></colgroup></html>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,22): unexpected-end-tag",
+        "(1,32): unexpected-end-tag",
+        "(1,38): unexpected-end-tag",
+        "(1,49): unexpected-end-tag",
+        "(1,56): unexpected-end-tag",
+        "(1,56): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "</table></tbody></tfoot></thead></tr><div>",
+      "errors": [
+        "(1,8): unexpected-end-tag",
+        "(1,16): unexpected-end-tag",
+        "(1,24): unexpected-end-tag",
+        "(1,32): unexpected-end-tag",
+        "(1,37): unexpected-end-tag",
+        "(1,42): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<table><colgroup>foo",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,18): foster-parenting-character-in-table",
+        "(1,19): foster-parenting-character-in-table",
+        "(1,20): foster-parenting-character-in-table",
+        "(1,20): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "colgroup": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "foo"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>foo<table><colgroup></colgroup></table></body></html>",
+        "noQuirksBodyHtml": "foo<table><colgroup></colgroup></table>"
+      }
+    },
+    {
+      "data": "foo<col>",
+      "errors": [
+        "(1,1): unexpected-character-in-colgroup",
+        "(1,2): unexpected-character-in-colgroup",
+        "(1,3): unexpected-character-in-colgroup"
+      ],
+      "fragment": {
+        "name": "colgroup"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "col": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "col"
+          }
+        ],
+        "html": "<col>",
+        "noQuirksBodyHtml": "foo"
+      }
+    },
+    {
+      "data": "<table><colgroup></col>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,23): no-end-tag",
+        "(1,23): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "colgroup": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><colgroup></colgroup></table></body></html>",
+        "noQuirksBodyHtml": "<table><colgroup></colgroup></table>"
+      }
+    },
+    {
+      "data": "<frameset><div>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,15): unexpected-start-tag-in-frameset",
+        "(1,15): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "</frameset><frame>",
+      "errors": [
+        "(1,11): unexpected-frameset-in-frameset-innerhtml"
+      ],
+      "fragment": {
+        "name": "frameset"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "frame": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "frame"
+          }
+        ],
+        "html": "<frame>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<frameset></div>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag",
+        "(1,16): unexpected-end-tag-in-frameset",
+        "(1,16): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "</body><div>",
+      "errors": [
+        "(1,7): unexpected-close-tag",
+        "(1,12): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "body"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "div"
+          }
+        ],
+        "html": "<div></div>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<table><tr><div>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,16): unexpected-start-tag-implies-table-voodoo",
+        "(1,16): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div></div><table><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<div></div><table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "</tr><td>",
+      "errors": [
+        "(1,5): unexpected-end-tag"
+      ],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td"
+          }
+        ],
+        "html": "<td></td>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "</tbody></tfoot></thead><td>",
+      "errors": [
+        "(1,8): unexpected-end-tag",
+        "(1,16): unexpected-end-tag",
+        "(1,24): unexpected-end-tag"
+      ],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td"
+          }
+        ],
+        "html": "<td></td>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<table><tr><div><td>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,16): foster-parenting-start-tag",
+        "(1,20): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div></div><table><tbody><tr><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<div></div><table><tbody><tr><td></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<caption><col><colgroup><tbody><tfoot><thead><tr>",
+      "errors": [
+        "(1,9): unexpected-start-tag",
+        "(1,14): unexpected-start-tag",
+        "(1,24): unexpected-start-tag",
+        "(1,31): unexpected-start-tag",
+        "(1,38): unexpected-start-tag",
+        "(1,45): unexpected-start-tag"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "tr"
+          }
+        ],
+        "html": "<tr></tr>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<table><tbody></thead>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,22): unexpected-end-tag-in-table-body",
+        "(1,22): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody></tbody></table>"
+      }
+    },
+    {
+      "data": "</table><tr>",
+      "errors": [
+        "(1,8): unexpected-end-tag"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "tr"
+          }
+        ],
+        "html": "<tr></tr>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<table><tbody></body></caption></col></colgroup></html></td></th></tr>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,21): unexpected-end-tag-in-table-body",
+        "(1,31): unexpected-end-tag-in-table-body",
+        "(1,37): unexpected-end-tag-in-table-body",
+        "(1,48): unexpected-end-tag-in-table-body",
+        "(1,55): unexpected-end-tag-in-table-body",
+        "(1,60): unexpected-end-tag-in-table-body",
+        "(1,65): unexpected-end-tag-in-table-body",
+        "(1,70): unexpected-end-tag-in-table-body",
+        "(1,70): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><tbody></div>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,20): unexpected-end-tag-implies-table-voodoo",
+        "(1,20): end-tag-too-early",
+        "(1,20): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,14): unexpected-start-tag-implies-end-tag",
+        "(1,14): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table"
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table></table><table></table></body></html>",
+        "noQuirksBodyHtml": "<table></table><table></table>"
+      }
+    },
+    {
+      "data": "<table></body></caption></col></colgroup></html></tbody></td></tfoot></th></thead></tr>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,14): unexpected-end-tag",
+        "(1,24): unexpected-end-tag",
+        "(1,30): unexpected-end-tag",
+        "(1,41): unexpected-end-tag",
+        "(1,48): unexpected-end-tag",
+        "(1,56): unexpected-end-tag",
+        "(1,61): unexpected-end-tag",
+        "(1,69): unexpected-end-tag",
+        "(1,74): unexpected-end-tag",
+        "(1,82): unexpected-end-tag",
+        "(1,87): unexpected-end-tag",
+        "(1,87): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table></table></body></html>",
+        "noQuirksBodyHtml": "<table></table>"
+      }
+    },
+    {
+      "data": "</table><tr>",
+      "errors": [
+        "(1,8): unexpected-end-tag"
+      ],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "tbody": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "tbody",
+            "children": [
+              {
+                "tag": "tr"
+              }
+            ]
+          }
+        ],
+        "html": "<tbody><tr></tr></tbody>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<body></body></html>",
+      "errors": [
+        "(1,20): unexpected-end-tag-after-body-innerhtml"
+      ],
+      "fragment": {
+        "name": "html"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "head"
+          },
+          {
+            "tag": "body"
+          }
+        ],
+        "html": "<head></head><body></body>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<html><frameset></frameset></html> ",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              },
+              {
+                "text": " "
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset> </html>",
+        "noQuirksBodyHtml": " "
+      }
+    },
+    {
+      "data": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\"><html></html>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html \"-//W3C//DTD HTML 4.01//EN\" \"\""
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<param><frameset></frameset>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,17): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<param>"
+      }
+    },
+    {
+      "data": "<source><frameset></frameset>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,18): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<source>"
+      }
+    },
+    {
+      "data": "<track><frameset></frameset>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,17): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<track>"
+      }
+    },
+    {
+      "data": "</html><frameset></frameset>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-end-tag",
+        "(1,17): expected-eof-but-got-start-tag",
+        "(1,17): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "</body><frameset></frameset>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-end-tag",
+        "(1,17): unexpected-start-tag-after-body",
+        "(1,17): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": ""
+      }
+    }
+  ],
+  "tests7.dat": [
+    {
+      "data": "<!doctype html><body><title>X</title>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "title": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "X"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><title>X</title></body></html>",
+        "noQuirksBodyHtml": "<title>X</title>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><title>X</title></table>",
+      "errors": [
+        "(1,29): unexpected-start-tag-implies-table-voodoo"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "title": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "X"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><title>X</title><table></table></body></html>",
+        "noQuirksBodyHtml": "<title>X</title><table></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><head></head><title>X</title>",
+      "errors": [
+        "(1,35): unexpected-start-tag-out-of-my-head"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "X"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><title>X</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title>X</title>"
+      }
+    },
+    {
+      "data": "<!doctype html></head><title>X</title>",
+      "errors": [
+        "(1,29): unexpected-start-tag-out-of-my-head"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "title": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "title",
+                    "children": [
+                      {
+                        "text": "X"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head><title>X</title></head><body></body></html>",
+        "noQuirksBodyHtml": "<title>X</title>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><meta></table>",
+      "errors": [
+        "(1,28): unexpected-start-tag-implies-table-voodoo"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "meta": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "meta"
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><meta><table></table></body></html>",
+        "noQuirksBodyHtml": "<meta><table></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table>X<tr><td><table> <meta></table></table>",
+      "errors": [
+        "unexpected text in table",
+        "(1,45): unexpected-start-tag-implies-table-voodoo"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "meta": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "meta"
+                                  },
+                                  {
+                                    "tag": "table",
+                                    "children": [
+                                      {
+                                        "text": " "
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>X<table><tbody><tr><td><meta><table> </table></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "X<table><tbody><tr><td><meta><table> </table></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><html> <head>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": " "
+      }
+    },
+    {
+      "data": "<!doctype html> <head>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": " "
+      }
+    },
+    {
+      "data": "<!doctype html><table><style> <tr>x </style> </table>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "style": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "style",
+                        "children": [
+                          {
+                            "text": " <tr>x ",
+                            "no_escape": true
+                          }
+                        ]
+                      },
+                      {
+                        "text": " "
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><style> <tr>x </style> </table></body></html>",
+        "noQuirksBodyHtml": "<table><style> <tr>x </style> </table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><TBODY><script> <tr>x </script> </table>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "script": true
+          },
+          "doctype": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "script",
+                            "children": [
+                              {
+                                "text": " <tr>x ",
+                                "no_escape": true
+                              }
+                            ]
+                          },
+                          {
+                            "text": " "
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><script> <tr>x </script> </tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><script> <tr>x </script> </tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><applet><p>X</p></applet>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "applet": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "applet",
+                        "children": [
+                          {
+                            "tag": "p",
+                            "children": [
+                              {
+                                "text": "X"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><applet><p>X</p></applet></p></body></html>",
+        "noQuirksBodyHtml": "<p><applet><p>X</p></applet></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><p><object type=\"application/x-non-existant-plugin\"><p>X</p></object>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "object": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "object",
+                        "attrs": [
+                          {
+                            "name": "type",
+                            "value": "application/x-non-existant-plugin"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "p",
+                            "children": [
+                              {
+                                "text": "X"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><p><object type=\"application/x-non-existant-plugin\"><p>X</p></object></p></body></html>",
+        "noQuirksBodyHtml": "<p><object type=\"application/x-non-existant-plugin\"><p>X</p></object></p>"
+      }
+    },
+    {
+      "data": "<!doctype html><listing>\nX</listing>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "listing": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "listing",
+                    "children": [
+                      {
+                        "text": "X"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><listing>X</listing></body></html>",
+        "noQuirksBodyHtml": "<listing>X</listing>"
+      }
+    },
+    {
+      "data": "<!doctype html><select><input>X",
+      "errors": [
+        "(1,30): unexpected-input-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "input": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  },
+                  {
+                    "tag": "input"
+                  },
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select><input>X</body></html>",
+        "noQuirksBodyHtml": "<select></select><input>X"
+      }
+    },
+    {
+      "data": "<!doctype html><select><select>X",
+      "errors": [
+        "(1,31): unexpected-select-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  },
+                  {
+                    "text": "X"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select>X</body></html>",
+        "noQuirksBodyHtml": "<select></select>X"
+      }
+    },
+    {
+      "data": "<!doctype html><table><input type=hidDEN></table>",
+      "errors": [
+        "(1,41): unexpected-hidden-input-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "input": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "input",
+                        "attrs": [
+                          {
+                            "name": "type",
+                            "value": "hidDEN"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><input type=\"hidDEN\"></table></body></html>",
+        "noQuirksBodyHtml": "<table><input type=\"hidDEN\"></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table>X<input type=hidDEN></table>",
+      "errors": [
+        "(1,23): foster-parenting-character",
+        "(1,42): unexpected-hidden-input-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "input": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "X"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "input",
+                        "attrs": [
+                          {
+                            "name": "type",
+                            "value": "hidDEN"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body>X<table><input type=\"hidDEN\"></table></body></html>",
+        "noQuirksBodyHtml": "X<table><input type=\"hidDEN\"></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table>  <input type=hidDEN></table>",
+      "errors": [
+        "(1,43): unexpected-hidden-input-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "input": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "text": "  "
+                      },
+                      {
+                        "tag": "input",
+                        "attrs": [
+                          {
+                            "name": "type",
+                            "value": "hidDEN"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table>  <input type=\"hidDEN\"></table></body></html>",
+        "noQuirksBodyHtml": "<table>  <input type=\"hidDEN\"></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table>  <input type='hidDEN'></table>",
+      "errors": [
+        "(1,45): unexpected-hidden-input-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "input": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "text": "  "
+                      },
+                      {
+                        "tag": "input",
+                        "attrs": [
+                          {
+                            "name": "type",
+                            "value": "hidDEN"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table>  <input type=\"hidDEN\"></table></body></html>",
+        "noQuirksBodyHtml": "<table>  <input type=\"hidDEN\"></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><input type=\" hidden\"><input type=hidDEN></table>",
+      "errors": [
+        "(1,44): unexpected-start-tag-implies-table-voodoo",
+        "(1,63): unexpected-hidden-input-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "input": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "input",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": " hidden"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "input",
+                        "attrs": [
+                          {
+                            "name": "type",
+                            "value": "hidDEN"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><input type=\" hidden\"><table><input type=\"hidDEN\"></table></body></html>",
+        "noQuirksBodyHtml": "<input type=\" hidden\"><table><input type=\"hidDEN\"></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><table><select>X<tr>",
+      "errors": [
+        "(1,30): unexpected-start-tag-implies-table-voodoo",
+        "(1,35): unexpected-table-element-start-tag-in-select-in-table",
+        "(1,35): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "text": "X"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select>X</select><table><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<select>X</select><table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!doctype html><select>X</select>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "text": "X"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select>X</select></body></html>",
+        "noQuirksBodyHtml": "<select>X</select>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE hTmL><html></html>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<!DOCTYPE HTML><html></html>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<body>X</body></body>",
+      "errors": [
+        "(1,21): unexpected-end-tag-after-body"
+      ],
+      "fragment": {
+        "name": "html"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "head"
+          },
+          {
+            "tag": "body",
+            "children": [
+              {
+                "text": "X"
+              }
+            ]
+          }
+        ],
+        "html": "<head></head><body>X</body>",
+        "noQuirksBodyHtml": "X"
+      }
+    },
+    {
+      "data": "<div><p>a</x> b",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,13): unexpected-end-tag",
+        "(1,15): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "text": "a b"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><p>a b</p></div></body></html>",
+        "noQuirksBodyHtml": "<div><p>a b</p></div>"
+      }
+    },
+    {
+      "data": "<table><tr><td><code></code> </table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "code": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "code"
+                                  },
+                                  {
+                                    "text": " "
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td><code></code> </td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><code></code> </td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><b><tr><td>aaa</td></tr>bbb</table>ccc",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,10): foster-parenting-start-tag",
+        "(1,32): foster-parenting-character",
+        "(1,33): foster-parenting-character",
+        "(1,34): foster-parenting-character",
+        "(1,45): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b"
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "bbb"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "text": "aaa"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "ccc"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b></b><b>bbb</b><table><tbody><tr><td>aaa</td></tr></tbody></table><b>ccc</b></body></html>",
+        "noQuirksBodyHtml": "<b></b><b>bbb</b><table><tbody><tr><td>aaa</td></tr></tbody></table><b>ccc</b>"
+      }
+    },
+    {
+      "data": "A<table><tr> B</tr> B</table>",
+      "errors": [
+        "(1,1): expected-doctype-but-got-chars",
+        "(1,13): foster-parenting-character",
+        "(1,14): foster-parenting-character",
+        "(1,20): foster-parenting-character",
+        "(1,21): foster-parenting-character"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "A B B"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>A B B<table><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "A B B<table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "A<table><tr> B</tr> </em>C</table>",
+      "errors": [
+        "(1,1): expected-doctype-but-got-chars",
+        "(1,13): foster-parenting-character",
+        "(1,14): foster-parenting-character",
+        "(1,20): foster-parenting-character",
+        "(1,25): unexpected-end-tag",
+        "(1,26): foster-parenting-character"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "A BC"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          },
+                          {
+                            "text": " "
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>A BC<table><tbody><tr></tr> </tbody></table></body></html>",
+        "noQuirksBodyHtml": "A BC<table><tbody><tr></tr> </tbody></table>"
+      }
+    },
+    {
+      "data": "<select><keygen>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,16): unexpected-input-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "keygen": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  },
+                  {
+                    "tag": "keygen"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select></select><keygen></body></html>",
+        "noQuirksBodyHtml": "<select></select><keygen>"
+      }
+    }
+  ],
+  "tests8.dat": [
+    {
+      "data": "<div>\n<div></div>\n</span>x",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(3,7): unexpected-end-tag",
+        "(3,8): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "\n"
+                      },
+                      {
+                        "tag": "div"
+                      },
+                      {
+                        "text": "\nx"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>\n<div></div>\nx</div></body></html>",
+        "noQuirksBodyHtml": "<div>\n<div></div>\nx</div>"
+      }
+    },
+    {
+      "data": "<div>x<div></div>\n</span>x",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(2,7): unexpected-end-tag",
+        "(2,8): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "x"
+                      },
+                      {
+                        "tag": "div"
+                      },
+                      {
+                        "text": "\nx"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>x<div></div>\nx</div></body></html>",
+        "noQuirksBodyHtml": "<div>x<div></div>\nx</div>"
+      }
+    },
+    {
+      "data": "<div>x<div></div>x</span>x",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,25): unexpected-end-tag",
+        "(1,26): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "x"
+                      },
+                      {
+                        "tag": "div"
+                      },
+                      {
+                        "text": "xx"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>x<div></div>xx</div></body></html>",
+        "noQuirksBodyHtml": "<div>x<div></div>xx</div>"
+      }
+    },
+    {
+      "data": "<div>x<div></div>y</span>z",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,25): unexpected-end-tag",
+        "(1,26): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "x"
+                      },
+                      {
+                        "tag": "div"
+                      },
+                      {
+                        "text": "yz"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>x<div></div>yz</div></body></html>",
+        "noQuirksBodyHtml": "<div>x<div></div>yz</div>"
+      }
+    },
+    {
+      "data": "<table><div>x<div></div>x</span>x",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,12): foster-parenting-start-tag",
+        "(1,13): foster-parenting-character",
+        "(1,18): foster-parenting-start-tag",
+        "(1,24): foster-parenting-end-tag",
+        "(1,25): foster-parenting-start-tag",
+        "(1,32): foster-parenting-end-tag",
+        "(1,32): unexpected-end-tag",
+        "(1,33): foster-parenting-character",
+        "(1,33): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "x"
+                      },
+                      {
+                        "tag": "div"
+                      },
+                      {
+                        "text": "xx"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>x<div></div>xx</div><table></table></body></html>",
+        "noQuirksBodyHtml": "<div>x<div></div>xx</div><table></table>"
+      }
+    },
+    {
+      "data": "x<table>x",
+      "errors": [
+        "(1,1): expected-doctype-but-got-chars",
+        "(1,9): foster-parenting-character",
+        "(1,9): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "xx"
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>xx<table></table></body></html>",
+        "noQuirksBodyHtml": "xx<table></table>"
+      }
+    },
+    {
+      "data": "x<table><table>x",
+      "errors": [
+        "(1,1): expected-doctype-but-got-chars",
+        "(1,15): unexpected-start-tag-implies-end-tag",
+        "(1,16): foster-parenting-character",
+        "(1,16): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "x"
+                  },
+                  {
+                    "tag": "table"
+                  },
+                  {
+                    "text": "x"
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>x<table></table>x<table></table></body></html>",
+        "noQuirksBodyHtml": "x<table></table>x<table></table>"
+      }
+    },
+    {
+      "data": "<b>a<div></div><div></b>y",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,24): adoption-agency-1.3",
+        "(1,25): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "a"
+                      },
+                      {
+                        "tag": "div"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "b"
+                      },
+                      {
+                        "text": "y"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b>a<div></div></b><div><b></b>y</div></body></html>",
+        "noQuirksBodyHtml": "<b>a<div></div></b><div><b></b>y</div>"
+      }
+    },
+    {
+      "data": "<a><div><p></a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,15): adoption-agency-1.3",
+        "(1,15): adoption-agency-1.3",
+        "(1,15): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "div": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a"
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "a"
+                      },
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "tag": "a"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a></a><div><a></a><p><a></a></p></div></body></html>",
+        "noQuirksBodyHtml": "<a></a><div><a></a><p><a></a></p></div>"
+      }
+    }
+  ],
+  "tests9.dat": [
+    {
+      "data": "<!DOCTYPE html><math></math>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math></math></body></html>",
+        "noQuirksBodyHtml": "<math></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><math></math>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math></math></body></html>",
+        "noQuirksBodyHtml": "<math></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><math><mi>",
+      "errors": [
+        "(1,25) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mi></mi></math></body></html>",
+        "noQuirksBodyHtml": "<math><mi></mi></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><math><annotation-xml><svg><u>",
+      "errors": [
+        "(1,45) unexpected-html-element-in-foreign-content",
+        "(1,45) expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math annotation-xml": true,
+            "svg svg": true,
+            "u": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "annotation-xml",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "u"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><annotation-xml><svg></svg></annotation-xml></math><u></u></body></html>",
+        "noQuirksBodyHtml": "<math><annotation-xml><svg><u></u></svg></annotation-xml></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><select><math></math></select>",
+      "errors": [
+        "(1,35) unexpected-start-tag-in-select",
+        "(1,42) unexpected-end-tag-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select></select></body></html>",
+        "noQuirksBodyHtml": "<select></select>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><select><option><math></math></option></select>",
+      "errors": [
+        "(1,43) unexpected-start-tag-in-select",
+        "(1,50) unexpected-end-tag-in-select"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select><option></option></select></body></html>",
+        "noQuirksBodyHtml": "<select><option></option></select>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><math></math></table>",
+      "errors": [
+        "(1,34) unexpected-start-tag-implies-table-voodoo"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML"
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math></math><table></table></body></html>",
+        "noQuirksBodyHtml": "<math></math><table></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><math><mi>foo</mi></math></table>",
+      "errors": [
+        "(1,34) foster-parenting-start-token",
+        "(1,39) foster-parenting-character",
+        "(1,40) foster-parenting-character",
+        "(1,41) foster-parenting-character"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mi>foo</mi></math><table></table></body></html>",
+        "noQuirksBodyHtml": "<math><mi>foo</mi></math><table></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><math><mi>foo</mi><mi>bar</mi></math></table>",
+      "errors": [
+        "(1,34) foster-parenting-start-tag",
+        "(1,39) foster-parenting-character",
+        "(1,40) foster-parenting-character",
+        "(1,41) foster-parenting-character",
+        "(1,51) foster-parenting-character",
+        "(1,52) foster-parenting-character",
+        "(1,53) foster-parenting-character"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true,
+            "table": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mi>foo</mi><mi>bar</mi></math><table></table></body></html>",
+        "noQuirksBodyHtml": "<math><mi>foo</mi><mi>bar</mi></math><table></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><tbody><math><mi>foo</mi><mi>bar</mi></math></tbody></table>",
+      "errors": [
+        "(1,41) foster-parenting-start-tag",
+        "(1,46) foster-parenting-character",
+        "(1,47) foster-parenting-character",
+        "(1,48) foster-parenting-character",
+        "(1,58) foster-parenting-character",
+        "(1,59) foster-parenting-character",
+        "(1,60) foster-parenting-character"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true,
+            "table": true,
+            "tbody": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mi>foo</mi><mi>bar</mi></math><table><tbody></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<math><mi>foo</mi><mi>bar</mi></math><table><tbody></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><tbody><tr><math><mi>foo</mi><mi>bar</mi></math></tr></tbody></table>",
+      "errors": [
+        "(1,45) foster-parenting-start-tag",
+        "(1,50) foster-parenting-character",
+        "(1,51) foster-parenting-character",
+        "(1,52) foster-parenting-character",
+        "(1,62) foster-parenting-character",
+        "(1,63) foster-parenting-character",
+        "(1,64) foster-parenting-character"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mi>foo</mi><mi>bar</mi></math><table><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<math><mi>foo</mi><mi>bar</mi></math><table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><tbody><tr><td><math><mi>foo</mi><mi>bar</mi></math></td></tr></tbody></table>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "math math": true,
+            "math mi": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "math",
+                                    "ns": "http://www.w3.org/1998/Math/MathML",
+                                    "children": [
+                                      {
+                                        "tag": "mi",
+                                        "ns": "http://www.w3.org/1998/Math/MathML",
+                                        "children": [
+                                          {
+                                            "text": "foo"
+                                          }
+                                        ]
+                                      },
+                                      {
+                                        "tag": "mi",
+                                        "ns": "http://www.w3.org/1998/Math/MathML",
+                                        "children": [
+                                          {
+                                            "text": "bar"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><math><mi>foo</mi><mi>bar</mi></math></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><math><mi>foo</mi><mi>bar</mi></math></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><tbody><tr><td><math><mi>foo</mi><mi>bar</mi></math><p>baz</td></tr></tbody></table>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "math math": true,
+            "math mi": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "math",
+                                    "ns": "http://www.w3.org/1998/Math/MathML",
+                                    "children": [
+                                      {
+                                        "tag": "mi",
+                                        "ns": "http://www.w3.org/1998/Math/MathML",
+                                        "children": [
+                                          {
+                                            "text": "foo"
+                                          }
+                                        ]
+                                      },
+                                      {
+                                        "tag": "mi",
+                                        "ns": "http://www.w3.org/1998/Math/MathML",
+                                        "children": [
+                                          {
+                                            "text": "bar"
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  },
+                                  {
+                                    "tag": "p",
+                                    "children": [
+                                      {
+                                        "text": "baz"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><math><mi>foo</mi><mi>bar</mi></math><p>baz</p></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><math><mi>foo</mi><mi>bar</mi></math><p>baz</p></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><caption><math><mi>foo</mi><mi>bar</mi></math><p>baz</caption></table>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "math math": true,
+            "math mi": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "tag": "math",
+                            "ns": "http://www.w3.org/1998/Math/MathML",
+                            "children": [
+                              {
+                                "tag": "mi",
+                                "ns": "http://www.w3.org/1998/Math/MathML",
+                                "children": [
+                                  {
+                                    "text": "foo"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "mi",
+                                "ns": "http://www.w3.org/1998/Math/MathML",
+                                "children": [
+                                  {
+                                    "text": "bar"
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "p",
+                            "children": [
+                              {
+                                "text": "baz"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><caption><math><mi>foo</mi><mi>bar</mi></math><p>baz</p></caption></table></body></html>",
+        "noQuirksBodyHtml": "<table><caption><math><mi>foo</mi><mi>bar</mi></math><p>baz</p></caption></table>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><caption><math><mi>foo</mi><mi>bar</mi><p>baz</table><p>quux",
+      "errors": [
+        "(1,70) unexpected-html-element-in-foreign-content"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "math math": true,
+            "math mi": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "tag": "math",
+                            "ns": "http://www.w3.org/1998/Math/MathML",
+                            "children": [
+                              {
+                                "tag": "mi",
+                                "ns": "http://www.w3.org/1998/Math/MathML",
+                                "children": [
+                                  {
+                                    "text": "foo"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "mi",
+                                "ns": "http://www.w3.org/1998/Math/MathML",
+                                "children": [
+                                  {
+                                    "text": "bar"
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "p",
+                            "children": [
+                              {
+                                "text": "baz"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "quux"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><caption><math><mi>foo</mi><mi>bar</mi></math><p>baz</p></caption></table><p>quux</p></body></html>",
+        "noQuirksBodyHtml": "<table><caption><math><mi>foo</mi><mi>bar</mi><p>baz</p></math></caption></table><p>quux</p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><caption><math><mi>foo</mi><mi>bar</mi>baz</table><p>quux",
+      "errors": [
+        "(1,78) unexpected-end-tag",
+        "(1,78) expected-one-end-tag-but-got-another"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "caption": true,
+            "math math": true,
+            "math mi": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "caption",
+                        "children": [
+                          {
+                            "tag": "math",
+                            "ns": "http://www.w3.org/1998/Math/MathML",
+                            "children": [
+                              {
+                                "tag": "mi",
+                                "ns": "http://www.w3.org/1998/Math/MathML",
+                                "children": [
+                                  {
+                                    "text": "foo"
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "mi",
+                                "ns": "http://www.w3.org/1998/Math/MathML",
+                                "children": [
+                                  {
+                                    "text": "bar"
+                                  }
+                                ]
+                              },
+                              {
+                                "text": "baz"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "quux"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><caption><math><mi>foo</mi><mi>bar</mi>baz</math></caption></table><p>quux</p></body></html>",
+        "noQuirksBodyHtml": "<table><caption><math><mi>foo</mi><mi>bar</mi>baz</math></caption></table><p>quux</p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><colgroup><math><mi>foo</mi><mi>bar</mi><p>baz</table><p>quux",
+      "errors": [
+        "(1,44) foster-parenting-start-tag",
+        "(1,49) foster-parenting-character",
+        "(1,50) foster-parenting-character",
+        "(1,51) foster-parenting-character",
+        "(1,61) foster-parenting-character",
+        "(1,62) foster-parenting-character",
+        "(1,63) foster-parenting-character",
+        "(1,71) unexpected-html-element-in-foreign-content",
+        "(1,71) foster-parenting-start-tag",
+        "(1,63) foster-parenting-character",
+        "(1,63) foster-parenting-character",
+        "(1,63) foster-parenting-character"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true,
+            "p": true,
+            "table": true,
+            "colgroup": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "baz"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "colgroup"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "quux"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mi>foo</mi><mi>bar</mi></math><p>baz</p><table><colgroup></colgroup></table><p>quux</p></body></html>",
+        "noQuirksBodyHtml": "<math><mi>foo</mi><mi>bar</mi><p>baz</p></math><table><colgroup></colgroup></table><p>quux</p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><tr><td><select><math><mi>foo</mi><mi>bar</mi><p>baz</table><p>quux",
+      "errors": [
+        "(1,50) unexpected-start-tag-in-select",
+        "(1,54) unexpected-start-tag-in-select",
+        "(1,62) unexpected-end-tag-in-select",
+        "(1,66) unexpected-start-tag-in-select",
+        "(1,74) unexpected-end-tag-in-select",
+        "(1,77) unexpected-start-tag-in-select",
+        "(1,88) unexpected-table-element-end-tag-in-select-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "select": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "select",
+                                    "children": [
+                                      {
+                                        "text": "foobarbaz"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "quux"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><table><tbody><tr><td><select>foobarbaz</select></td></tr></tbody></table><p>quux</p></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><select>foobarbaz</select></td></tr></tbody></table><p>quux</p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body><table><select><math><mi>foo</mi><mi>bar</mi><p>baz</table><p>quux",
+      "errors": [
+        "(1,36) unexpected-start-tag-implies-table-voodoo",
+        "(1,42) unexpected-start-tag-in-select",
+        "(1,46) unexpected-start-tag-in-select",
+        "(1,54) unexpected-end-tag-in-select",
+        "(1,58) unexpected-start-tag-in-select",
+        "(1,66) unexpected-end-tag-in-select",
+        "(1,69) unexpected-start-tag-in-select",
+        "(1,80) unexpected-table-element-end-tag-in-select-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "table": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "text": "foobarbaz"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "quux"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><select>foobarbaz</select><table></table><p>quux</p></body></html>",
+        "noQuirksBodyHtml": "<select>foobarbaz</select><table></table><p>quux</p>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body></body></html><math><mi>foo</mi><mi>bar</mi><p>baz",
+      "errors": [
+        "(1,41) expected-eof-but-got-start-tag",
+        "(1,68) unexpected-html-element-in-foreign-content"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "baz"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mi>foo</mi><mi>bar</mi></math><p>baz</p></body></html>",
+        "noQuirksBodyHtml": "<math><mi>foo</mi><mi>bar</mi><p>baz</p></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body></body><math><mi>foo</mi><mi>bar</mi><p>baz",
+      "errors": [
+        "(1,34) unexpected-start-tag-after-body",
+        "(1,61) unexpected-html-element-in-foreign-content"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true,
+            "p": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "foo"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "text": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "baz"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><math><mi>foo</mi><mi>bar</mi></math><p>baz</p></body></html>",
+        "noQuirksBodyHtml": "<math><mi>foo</mi><mi>bar</mi><p>baz</p></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><frameset><math><mi></mi><mi></mi><p><span>",
+      "errors": [
+        "(1,31) unexpected-start-tag-in-frameset",
+        "(1,35) unexpected-start-tag-in-frameset",
+        "(1,40) unexpected-end-tag-in-frameset",
+        "(1,44) unexpected-start-tag-in-frameset",
+        "(1,49) unexpected-end-tag-in-frameset",
+        "(1,52) unexpected-start-tag-in-frameset",
+        "(1,58) unexpected-start-tag-in-frameset",
+        "(1,58) eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<math><mi></mi><mi></mi><p><span></span></p></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><frameset></frameset><math><mi></mi><mi></mi><p><span>",
+      "errors": [
+        "(1,42) unexpected-start-tag-after-frameset",
+        "(1,46) unexpected-start-tag-after-frameset",
+        "(1,51) unexpected-end-tag-after-frameset",
+        "(1,55) unexpected-start-tag-after-frameset",
+        "(1,60) unexpected-end-tag-after-frameset",
+        "(1,63) unexpected-start-tag-after-frameset",
+        "(1,69) unexpected-start-tag-after-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<math><mi></mi><mi></mi><p><span></span></p></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body xlink:href=foo><math xlink:href=foo></math>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "attrs": [
+                  {
+                    "name": "xlink:href",
+                    "value": "foo"
+                  }
+                ],
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "attrs": [
+                      {
+                        "name": "href",
+                        "ns": "http://www.w3.org/1999/xlink",
+                        "value": "foo"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body xlink:href=\"foo\"><math xlink:href=\"foo\"></math></body></html>",
+        "noQuirksBodyHtml": "<math xlink:href=\"foo\"></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body xlink:href=foo xml:lang=en><math><mi xml:lang=en xlink:href=foo></mi></math>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "attrs": [
+                  {
+                    "name": "xlink:href",
+                    "value": "foo"
+                  },
+                  {
+                    "name": "xml:lang",
+                    "value": "en"
+                  }
+                ],
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "attrs": [
+                          {
+                            "name": "href",
+                            "ns": "http://www.w3.org/1999/xlink",
+                            "value": "foo"
+                          },
+                          {
+                            "name": "lang",
+                            "ns": "http://www.w3.org/XML/1998/namespace",
+                            "value": "en"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body xlink:href=\"foo\" xml:lang=\"en\"><math><mi xml:lang=\"en\" xlink:href=\"foo\"></mi></math></body></html>",
+        "noQuirksBodyHtml": "<math><mi xml:lang=\"en\" xlink:href=\"foo\"></mi></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body xlink:href=foo xml:lang=en><math><mi xml:lang=en xlink:href=foo /></math>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "attrs": [
+                  {
+                    "name": "xlink:href",
+                    "value": "foo"
+                  },
+                  {
+                    "name": "xml:lang",
+                    "value": "en"
+                  }
+                ],
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "attrs": [
+                          {
+                            "name": "href",
+                            "ns": "http://www.w3.org/1999/xlink",
+                            "value": "foo"
+                          },
+                          {
+                            "name": "lang",
+                            "ns": "http://www.w3.org/XML/1998/namespace",
+                            "value": "en"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body xlink:href=\"foo\" xml:lang=\"en\"><math><mi xml:lang=\"en\" xlink:href=\"foo\"></mi></math></body></html>",
+        "noQuirksBodyHtml": "<math><mi xml:lang=\"en\" xlink:href=\"foo\"></mi></math>"
+      }
+    },
+    {
+      "data": "<!DOCTYPE html><body xlink:href=foo xml:lang=en><math><mi xml:lang=en xlink:href=foo />bar</math>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mi": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "attrs": [
+                  {
+                    "name": "xlink:href",
+                    "value": "foo"
+                  },
+                  {
+                    "name": "xml:lang",
+                    "value": "en"
+                  }
+                ],
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mi",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "attrs": [
+                          {
+                            "name": "href",
+                            "ns": "http://www.w3.org/1999/xlink",
+                            "value": "foo"
+                          },
+                          {
+                            "name": "lang",
+                            "ns": "http://www.w3.org/XML/1998/namespace",
+                            "value": "en"
+                          }
+                        ]
+                      },
+                      {
+                        "text": "bar"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body xlink:href=\"foo\" xml:lang=\"en\"><math><mi xml:lang=\"en\" xlink:href=\"foo\"></mi>bar</math></body></html>",
+        "noQuirksBodyHtml": "<math><mi xml:lang=\"en\" xlink:href=\"foo\"></mi>bar</math>"
+      }
+    }
+  ],
+  "tests_innerHTML_1.dat": [
+    {
+      "data": "<body><span>",
+      "errors": [
+        "(1,6): unexpected-start-tag",
+        "(1,12): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "body"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span"
+          }
+        ],
+        "html": "<span></span>",
+        "noQuirksBodyHtml": "<span></span>"
+      }
+    },
+    {
+      "data": "<span><body>",
+      "errors": [
+        "(1,12): unexpected-start-tag",
+        "(1,12): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "body"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span"
+          }
+        ],
+        "html": "<span></span>",
+        "noQuirksBodyHtml": "<span></span>"
+      }
+    },
+    {
+      "data": "<span><body>",
+      "errors": [
+        "(1,12): unexpected-start-tag",
+        "(1,12): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "div"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span"
+          }
+        ],
+        "html": "<span></span>",
+        "noQuirksBodyHtml": "<span></span>"
+      }
+    },
+    {
+      "data": "<body><span>",
+      "errors": [
+        "(1,12): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "html"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "head": true,
+            "body": true,
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "head"
+          },
+          {
+            "tag": "body",
+            "children": [
+              {
+                "tag": "span"
+              }
+            ]
+          }
+        ],
+        "html": "<head></head><body><span></span></body>",
+        "noQuirksBodyHtml": "<span></span>"
+      }
+    },
+    {
+      "data": "<frameset><span>",
+      "errors": [
+        "(1,10): unexpected-start-tag",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "body"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span"
+          }
+        ],
+        "html": "<span></span>",
+        "noQuirksBodyHtml": "<span></span>"
+      }
+    },
+    {
+      "data": "<span><frameset>",
+      "errors": [
+        "(1,16): unexpected-start-tag",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "body"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span"
+          }
+        ],
+        "html": "<span></span>",
+        "noQuirksBodyHtml": "<span></span>"
+      }
+    },
+    {
+      "data": "<span><frameset>",
+      "errors": [
+        "(1,16): unexpected-start-tag",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "div"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span"
+          }
+        ],
+        "html": "<span></span>",
+        "noQuirksBodyHtml": "<span></span>"
+      }
+    },
+    {
+      "data": "<frameset><span>",
+      "errors": [
+        "(1,16): unexpected-start-tag-in-frameset",
+        "(1,16): eof-in-frameset"
+      ],
+      "fragment": {
+        "name": "html"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "head": true,
+            "frameset": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "head"
+          },
+          {
+            "tag": "frameset"
+          }
+        ],
+        "html": "<head></head><frameset></frameset>",
+        "noQuirksBodyHtml": "<span></span>"
+      }
+    },
+    {
+      "data": "<table><tr>",
+      "errors": [
+        "(1,7): unexpected-start-tag"
+      ],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "tbody": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "tbody",
+            "children": [
+              {
+                "tag": "tr"
+              }
+            ]
+          }
+        ],
+        "html": "<tbody><tr></tr></tbody>",
+        "noQuirksBodyHtml": "<table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "</table><tr>",
+      "errors": [
+        "(1,8): unexpected-end-tag"
+      ],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "tbody": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "tbody",
+            "children": [
+              {
+                "tag": "tr"
+              }
+            ]
+          }
+        ],
+        "html": "<tbody><tr></tr></tbody>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<a>",
+      "errors": [
+        "(1,3): unexpected-start-tag-implies-table-voodoo",
+        "(1,3): eof-in-table"
+      ],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<a>",
+      "errors": [
+        "(1,3): unexpected-start-tag-implies-table-voodoo",
+        "(1,3): eof-in-table"
+      ],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<a><caption>a",
+      "errors": [
+        "(1,3): unexpected-start-tag-implies-table-voodoo",
+        "(1,13): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true,
+            "caption": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          },
+          {
+            "tag": "caption",
+            "children": [
+              {
+                "text": "a"
+              }
+            ]
+          }
+        ],
+        "html": "<a></a><caption>a</caption>",
+        "noQuirksBodyHtml": "<a>a</a>"
+      }
+    },
+    {
+      "data": "<a><colgroup><col>",
+      "errors": [
+        "(1,3): foster-parenting-start-token",
+        "(1,18): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true,
+            "colgroup": true,
+            "col": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          },
+          {
+            "tag": "colgroup",
+            "children": [
+              {
+                "tag": "col"
+              }
+            ]
+          }
+        ],
+        "html": "<a></a><colgroup><col></colgroup>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<a><tbody><tr>",
+      "errors": [
+        "(1,3): foster-parenting-start-tag"
+      ],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true,
+            "tbody": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          },
+          {
+            "tag": "tbody",
+            "children": [
+              {
+                "tag": "tr"
+              }
+            ]
+          }
+        ],
+        "html": "<a></a><tbody><tr></tr></tbody>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<a><tfoot><tr>",
+      "errors": [
+        "(1,3): foster-parenting-start-tag"
+      ],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true,
+            "tfoot": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          },
+          {
+            "tag": "tfoot",
+            "children": [
+              {
+                "tag": "tr"
+              }
+            ]
+          }
+        ],
+        "html": "<a></a><tfoot><tr></tr></tfoot>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<a><thead><tr>",
+      "errors": [
+        "(1,3): foster-parenting-start-tag"
+      ],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true,
+            "thead": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          },
+          {
+            "tag": "thead",
+            "children": [
+              {
+                "tag": "tr"
+              }
+            ]
+          }
+        ],
+        "html": "<a></a><thead><tr></tr></thead>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<a><tr>",
+      "errors": [
+        "(1,3): foster-parenting-start-tag"
+      ],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true,
+            "tbody": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          },
+          {
+            "tag": "tbody",
+            "children": [
+              {
+                "tag": "tr"
+              }
+            ]
+          }
+        ],
+        "html": "<a></a><tbody><tr></tr></tbody>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<a><th>",
+      "errors": [
+        "(1,3): unexpected-start-tag-implies-table-voodoo",
+        "(1,7): unexpected-cell-in-table-body"
+      ],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true,
+            "tbody": true,
+            "tr": true,
+            "th": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          },
+          {
+            "tag": "tbody",
+            "children": [
+              {
+                "tag": "tr",
+                "children": [
+                  {
+                    "tag": "th"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<a></a><tbody><tr><th></th></tr></tbody>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<a><td>",
+      "errors": [
+        "(1,3): unexpected-start-tag-implies-table-voodoo",
+        "(1,7): unexpected-cell-in-table-body"
+      ],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          },
+          {
+            "tag": "tbody",
+            "children": [
+              {
+                "tag": "tr",
+                "children": [
+                  {
+                    "tag": "td"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<a></a><tbody><tr><td></td></tr></tbody>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<table></table><tbody>",
+      "errors": [
+        "(1,22): unexpected-start-tag"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "table"
+          }
+        ],
+        "html": "<table></table>",
+        "noQuirksBodyHtml": "<table></table>"
+      }
+    },
+    {
+      "data": "</table><span>",
+      "errors": [
+        "(1,8): unexpected-end-tag",
+        "(1,14): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span"
+          }
+        ],
+        "html": "<span></span>",
+        "noQuirksBodyHtml": "<span></span>"
+      }
+    },
+    {
+      "data": "<span></table>",
+      "errors": [
+        "(1,14): unexpected-end-tag",
+        "(1,14): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span"
+          }
+        ],
+        "html": "<span></span>",
+        "noQuirksBodyHtml": "<span></span>"
+      }
+    },
+    {
+      "data": "</caption><span>",
+      "errors": [
+        "(1,10): XXX-undefined-error",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span"
+          }
+        ],
+        "html": "<span></span>",
+        "noQuirksBodyHtml": "<span></span>"
+      }
+    },
+    {
+      "data": "<span></caption><span>",
+      "errors": [
+        "(1,16): XXX-undefined-error",
+        "(1,22): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span",
+            "children": [
+              {
+                "tag": "span"
+              }
+            ]
+          }
+        ],
+        "html": "<span><span></span></span>",
+        "noQuirksBodyHtml": "<span><span></span></span>"
+      }
+    },
+    {
+      "data": "<span><caption><span>",
+      "errors": [
+        "(1,15): unexpected-start-tag",
+        "(1,21): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span",
+            "children": [
+              {
+                "tag": "span"
+              }
+            ]
+          }
+        ],
+        "html": "<span><span></span></span>",
+        "noQuirksBodyHtml": "<span><span></span></span>"
+      }
+    },
+    {
+      "data": "<span><col><span>",
+      "errors": [
+        "(1,11): unexpected-start-tag",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span",
+            "children": [
+              {
+                "tag": "span"
+              }
+            ]
+          }
+        ],
+        "html": "<span><span></span></span>",
+        "noQuirksBodyHtml": "<span><span></span></span>"
+      }
+    },
+    {
+      "data": "<span><colgroup><span>",
+      "errors": [
+        "(1,16): unexpected-start-tag",
+        "(1,22): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span",
+            "children": [
+              {
+                "tag": "span"
+              }
+            ]
+          }
+        ],
+        "html": "<span><span></span></span>",
+        "noQuirksBodyHtml": "<span><span></span></span>"
+      }
+    },
+    {
+      "data": "<span><html><span>",
+      "errors": [
+        "(1,12): non-html-root",
+        "(1,18): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span",
+            "children": [
+              {
+                "tag": "span"
+              }
+            ]
+          }
+        ],
+        "html": "<span><span></span></span>",
+        "noQuirksBodyHtml": "<span><span></span></span>"
+      }
+    },
+    {
+      "data": "<span><tbody><span>",
+      "errors": [
+        "(1,13): unexpected-start-tag",
+        "(1,19): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span",
+            "children": [
+              {
+                "tag": "span"
+              }
+            ]
+          }
+        ],
+        "html": "<span><span></span></span>",
+        "noQuirksBodyHtml": "<span><span></span></span>"
+      }
+    },
+    {
+      "data": "<span><td><span>",
+      "errors": [
+        "(1,10): unexpected-start-tag",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span",
+            "children": [
+              {
+                "tag": "span"
+              }
+            ]
+          }
+        ],
+        "html": "<span><span></span></span>",
+        "noQuirksBodyHtml": "<span><span></span></span>"
+      }
+    },
+    {
+      "data": "<span><tfoot><span>",
+      "errors": [
+        "(1,13): unexpected-start-tag",
+        "(1,19): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span",
+            "children": [
+              {
+                "tag": "span"
+              }
+            ]
+          }
+        ],
+        "html": "<span><span></span></span>",
+        "noQuirksBodyHtml": "<span><span></span></span>"
+      }
+    },
+    {
+      "data": "<span><thead><span>",
+      "errors": [
+        "(1,13): unexpected-start-tag",
+        "(1,19): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span",
+            "children": [
+              {
+                "tag": "span"
+              }
+            ]
+          }
+        ],
+        "html": "<span><span></span></span>",
+        "noQuirksBodyHtml": "<span><span></span></span>"
+      }
+    },
+    {
+      "data": "<span><th><span>",
+      "errors": [
+        "(1,10): unexpected-start-tag",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span",
+            "children": [
+              {
+                "tag": "span"
+              }
+            ]
+          }
+        ],
+        "html": "<span><span></span></span>",
+        "noQuirksBodyHtml": "<span><span></span></span>"
+      }
+    },
+    {
+      "data": "<span><tr><span>",
+      "errors": [
+        "(1,10): unexpected-start-tag",
+        "(1,16): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span",
+            "children": [
+              {
+                "tag": "span"
+              }
+            ]
+          }
+        ],
+        "html": "<span><span></span></span>",
+        "noQuirksBodyHtml": "<span><span></span></span>"
+      }
+    },
+    {
+      "data": "<span></table><span>",
+      "errors": [
+        "(1,14): unexpected-end-tag",
+        "(1,20): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "caption"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "span",
+            "children": [
+              {
+                "tag": "span"
+              }
+            ]
+          }
+        ],
+        "html": "<span><span></span></span>",
+        "noQuirksBodyHtml": "<span><span></span></span>"
+      }
+    },
+    {
+      "data": "</colgroup><col>",
+      "errors": [
+        "(1,11): XXX-undefined-error"
+      ],
+      "fragment": {
+        "name": "colgroup"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "col": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "col"
+          }
+        ],
+        "html": "<col>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<a><col>",
+      "errors": [
+        "(1,3): XXX-undefined-error"
+      ],
+      "fragment": {
+        "name": "colgroup"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "col": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "col"
+          }
+        ],
+        "html": "<col>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<caption><a>",
+      "errors": [
+        "(1,9): XXX-undefined-error",
+        "(1,12): unexpected-start-tag-implies-table-voodoo",
+        "(1,12): eof-in-table"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<col><a>",
+      "errors": [
+        "(1,5): XXX-undefined-error",
+        "(1,8): unexpected-start-tag-implies-table-voodoo",
+        "(1,8): eof-in-table"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<colgroup><a>",
+      "errors": [
+        "(1,10): XXX-undefined-error",
+        "(1,13): unexpected-start-tag-implies-table-voodoo",
+        "(1,13): eof-in-table"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<tbody><a>",
+      "errors": [
+        "(1,7): XXX-undefined-error",
+        "(1,10): unexpected-start-tag-implies-table-voodoo",
+        "(1,10): eof-in-table"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<tfoot><a>",
+      "errors": [
+        "(1,7): XXX-undefined-error",
+        "(1,10): unexpected-start-tag-implies-table-voodoo",
+        "(1,10): eof-in-table"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<thead><a>",
+      "errors": [
+        "(1,7): XXX-undefined-error",
+        "(1,10): unexpected-start-tag-implies-table-voodoo",
+        "(1,10): eof-in-table"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "</table><a>",
+      "errors": [
+        "(1,8): XXX-undefined-error",
+        "(1,11): unexpected-start-tag-implies-table-voodoo",
+        "(1,11): eof-in-table"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<a><tr>",
+      "errors": [
+        "(1,3): unexpected-start-tag-implies-table-voodoo"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          },
+          {
+            "tag": "tr"
+          }
+        ],
+        "html": "<a></a><tr></tr>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<a><td>",
+      "errors": [
+        "(1,3): unexpected-start-tag-implies-table-voodoo",
+        "(1,7): unexpected-cell-in-table-body"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          },
+          {
+            "tag": "tr",
+            "children": [
+              {
+                "tag": "td"
+              }
+            ]
+          }
+        ],
+        "html": "<a></a><tr><td></td></tr>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<a><td>",
+      "errors": [
+        "(1,3): unexpected-start-tag-implies-table-voodoo",
+        "(1,7): unexpected-cell-in-table-body"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          },
+          {
+            "tag": "tr",
+            "children": [
+              {
+                "tag": "td"
+              }
+            ]
+          }
+        ],
+        "html": "<a></a><tr><td></td></tr>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<a><td>",
+      "errors": [
+        "(1,3): unexpected-start-tag-implies-table-voodoo",
+        "(1,7): unexpected-cell-in-table-body"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          },
+          {
+            "tag": "tr",
+            "children": [
+              {
+                "tag": "td"
+              }
+            ]
+          }
+        ],
+        "html": "<a></a><tr><td></td></tr>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<td><table><tbody><a><tr>",
+      "errors": [
+        "(1,4): unexpected-cell-in-table-body",
+        "(1,21): unexpected-start-tag-implies-table-voodoo",
+        "(1,25): eof-in-table"
+      ],
+      "fragment": {
+        "name": "tbody"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "tr": true,
+            "td": true,
+            "a": true,
+            "table": true,
+            "tbody": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "tr",
+            "children": [
+              {
+                "tag": "td",
+                "children": [
+                  {
+                    "tag": "a"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<tr><td><a></a><table><tbody><tr></tr></tbody></table></td></tr>",
+        "noQuirksBodyHtml": "<a></a><table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "</tr><td>",
+      "errors": [
+        "(1,5): XXX-undefined-error"
+      ],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td"
+          }
+        ],
+        "html": "<td></td>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<td><table><a><tr></tr><tr>",
+      "errors": [
+        "(1,14): unexpected-start-tag-implies-table-voodoo",
+        "(1,27): eof-in-table"
+      ],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true,
+            "a": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td",
+            "children": [
+              {
+                "tag": "a"
+              },
+              {
+                "tag": "table",
+                "children": [
+                  {
+                    "tag": "tbody",
+                    "children": [
+                      {
+                        "tag": "tr"
+                      },
+                      {
+                        "tag": "tr"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<td><a></a><table><tbody><tr></tr><tr></tr></tbody></table></td>",
+        "noQuirksBodyHtml": "<a></a><table><tbody><tr></tr><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<caption><td>",
+      "errors": [
+        "(1,9): XXX-undefined-error"
+      ],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td"
+          }
+        ],
+        "html": "<td></td>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<col><td>",
+      "errors": [
+        "(1,5): XXX-undefined-error"
+      ],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td"
+          }
+        ],
+        "html": "<td></td>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<colgroup><td>",
+      "errors": [
+        "(1,10): XXX-undefined-error"
+      ],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td"
+          }
+        ],
+        "html": "<td></td>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<tbody><td>",
+      "errors": [
+        "(1,7): XXX-undefined-error"
+      ],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td"
+          }
+        ],
+        "html": "<td></td>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<tfoot><td>",
+      "errors": [
+        "(1,7): XXX-undefined-error"
+      ],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td"
+          }
+        ],
+        "html": "<td></td>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<thead><td>",
+      "errors": [
+        "(1,7): XXX-undefined-error"
+      ],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td"
+          }
+        ],
+        "html": "<td></td>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<tr><td>",
+      "errors": [
+        "(1,4): XXX-undefined-error"
+      ],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td"
+          }
+        ],
+        "html": "<td></td>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "</table><td>",
+      "errors": [
+        "(1,8): XXX-undefined-error"
+      ],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td"
+          }
+        ],
+        "html": "<td></td>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<td><table></table><td>",
+      "errors": [],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td",
+            "children": [
+              {
+                "tag": "table"
+              }
+            ]
+          },
+          {
+            "tag": "td"
+          }
+        ],
+        "html": "<td><table></table></td><td></td>",
+        "noQuirksBodyHtml": "<table></table>"
+      }
+    },
+    {
+      "data": "<td><table></table><td>",
+      "errors": [],
+      "fragment": {
+        "name": "tr"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "td": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "td",
+            "children": [
+              {
+                "tag": "table"
+              }
+            ]
+          },
+          {
+            "tag": "td"
+          }
+        ],
+        "html": "<td><table></table></td><td></td>",
+        "noQuirksBodyHtml": "<table></table>"
+      }
+    },
+    {
+      "data": "<caption><a>",
+      "errors": [
+        "(1,9): XXX-undefined-error",
+        "(1,12): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<col><a>",
+      "errors": [
+        "(1,5): XXX-undefined-error",
+        "(1,8): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<colgroup><a>",
+      "errors": [
+        "(1,10): XXX-undefined-error",
+        "(1,13): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<tbody><a>",
+      "errors": [
+        "(1,7): XXX-undefined-error",
+        "(1,10): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<tfoot><a>",
+      "errors": [
+        "(1,7): XXX-undefined-error",
+        "(1,10): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<th><a>",
+      "errors": [
+        "(1,4): XXX-undefined-error",
+        "(1,7): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<thead><a>",
+      "errors": [
+        "(1,7): XXX-undefined-error",
+        "(1,10): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<tr><a>",
+      "errors": [
+        "(1,4): XXX-undefined-error",
+        "(1,7): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "</table><a>",
+      "errors": [
+        "(1,8): XXX-undefined-error",
+        "(1,11): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "</tbody><a>",
+      "errors": [
+        "(1,8): XXX-undefined-error",
+        "(1,11): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "</td><a>",
+      "errors": [
+        "(1,5): unexpected-end-tag",
+        "(1,8): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "</tfoot><a>",
+      "errors": [
+        "(1,8): XXX-undefined-error",
+        "(1,11): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "</thead><a>",
+      "errors": [
+        "(1,8): XXX-undefined-error",
+        "(1,11): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "</th><a>",
+      "errors": [
+        "(1,5): unexpected-end-tag",
+        "(1,8): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "</tr><a>",
+      "errors": [
+        "(1,5): XXX-undefined-error",
+        "(1,8): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "a"
+          }
+        ],
+        "html": "<a></a>",
+        "noQuirksBodyHtml": "<a></a>"
+      }
+    },
+    {
+      "data": "<table><td><td>",
+      "errors": [
+        "(1,11): unexpected-cell-in-table-body",
+        "(1,15): expected-closing-tag-but-got-eof"
+      ],
+      "fragment": {
+        "name": "td"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "table",
+            "children": [
+              {
+                "tag": "tbody",
+                "children": [
+                  {
+                    "tag": "tr",
+                    "children": [
+                      {
+                        "tag": "td"
+                      },
+                      {
+                        "tag": "td"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<table><tbody><tr><td></td><td></td></tr></tbody></table>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td></td><td></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "</select><option>",
+      "errors": [
+        "(1,9): XXX-undefined-error",
+        "(1,17): eof-in-select"
+      ],
+      "fragment": {
+        "name": "select"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "option": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "option"
+          }
+        ],
+        "html": "<option></option>",
+        "noQuirksBodyHtml": "<option></option>"
+      }
+    },
+    {
+      "data": "<input><option>",
+      "errors": [
+        "(1,7): unexpected-input-in-select",
+        "(1,15): eof-in-select"
+      ],
+      "fragment": {
+        "name": "select"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "option": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "option"
+          }
+        ],
+        "html": "<option></option>",
+        "noQuirksBodyHtml": "<input><option></option>"
+      }
+    },
+    {
+      "data": "<keygen><option>",
+      "errors": [
+        "(1,8): unexpected-input-in-select",
+        "(1,16): eof-in-select"
+      ],
+      "fragment": {
+        "name": "select"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "option": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "option"
+          }
+        ],
+        "html": "<option></option>",
+        "noQuirksBodyHtml": "<keygen><option></option>"
+      }
+    },
+    {
+      "data": "<textarea><option>",
+      "errors": [
+        "(1,10): unexpected-input-in-select",
+        "(1,18): eof-in-select"
+      ],
+      "fragment": {
+        "name": "select"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "option": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "option"
+          }
+        ],
+        "html": "<option></option>",
+        "noQuirksBodyHtml": "<textarea>&lt;option&gt;</textarea>"
+      }
+    },
+    {
+      "data": "</html><!--abc-->",
+      "errors": [
+        "(1,7): unexpected-end-tag-after-body-innerhtml"
+      ],
+      "fragment": {
+        "name": "html"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "head"
+          },
+          {
+            "tag": "body"
+          },
+          {
+            "comment": "abc"
+          }
+        ],
+        "html": "<head></head><body></body><!--abc-->",
+        "noQuirksBodyHtml": "<!--abc-->"
+      }
+    },
+    {
+      "data": "</frameset><frame>",
+      "errors": [
+        "(1,11): unexpected-frameset-in-frameset-innerhtml"
+      ],
+      "fragment": {
+        "name": "frameset"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "frame": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "frame"
+          }
+        ],
+        "html": "<frame>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "",
+      "errors": [],
+      "fragment": {
+        "name": "html"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "head"
+          },
+          {
+            "tag": "body"
+          }
+        ],
+        "html": "<head></head><body></body>",
+        "noQuirksBodyHtml": ""
+      }
+    }
+  ],
+  "tricky01.dat": [
+    {
+      "data": "<b><p>Bold </b> Not bold</p>\nAlso not bold.",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,15): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "text": "Bold "
+                          }
+                        ]
+                      },
+                      {
+                        "text": " Not bold"
+                      }
+                    ]
+                  },
+                  {
+                    "text": "\nAlso not bold."
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b></b><p><b>Bold </b> Not bold</p>\nAlso not bold.</body></html>",
+        "noQuirksBodyHtml": "<b></b><p><b>Bold </b> Not bold</p>\nAlso not bold."
+      }
+    },
+    {
+      "data": "<html>\n<font color=red><i>Italic and Red<p>Italic and Red </font> Just italic.</p> Italic only.</i> Plain\n<p>I should not be red. <font color=red>Red. <i>Italic and red.</p>\n<p>Italic and red. </i> Red.</font> I should not be red.</p>\n<b>Bold <i>Bold and italic</b> Only Italic </i> Plain",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(2,58): adoption-agency-1.3",
+        "(3,67): unexpected-end-tag",
+        "(4,23): adoption-agency-1.3",
+        "(4,35): adoption-agency-1.3",
+        "(5,30): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "font": true,
+            "i": true,
+            "p": true,
+            "b": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "font",
+                    "attrs": [
+                      {
+                        "name": "color",
+                        "value": "red"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "i",
+                        "children": [
+                          {
+                            "text": "Italic and Red"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "tag": "font",
+                            "attrs": [
+                              {
+                                "name": "color",
+                                "value": "red"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "text": "Italic and Red "
+                              }
+                            ]
+                          },
+                          {
+                            "text": " Just italic."
+                          }
+                        ]
+                      },
+                      {
+                        "text": " Italic only."
+                      }
+                    ]
+                  },
+                  {
+                    "text": " Plain\n"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "I should not be red. "
+                      },
+                      {
+                        "tag": "font",
+                        "attrs": [
+                          {
+                            "name": "color",
+                            "value": "red"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "text": "Red. "
+                          },
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": "Italic and red."
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "font",
+                    "attrs": [
+                      {
+                        "name": "color",
+                        "value": "red"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "i",
+                        "children": [
+                          {
+                            "text": "\n"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "attrs": [
+                          {
+                            "name": "color",
+                            "value": "red"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": "Italic and red. "
+                              }
+                            ]
+                          },
+                          {
+                            "text": " Red."
+                          }
+                        ]
+                      },
+                      {
+                        "text": " I should not be red."
+                      }
+                    ]
+                  },
+                  {
+                    "text": "\n"
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "Bold "
+                      },
+                      {
+                        "tag": "i",
+                        "children": [
+                          {
+                            "text": "Bold and italic"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "i",
+                    "children": [
+                      {
+                        "text": " Only Italic "
+                      }
+                    ]
+                  },
+                  {
+                    "text": " Plain"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><font color=\"red\"><i>Italic and Red</i></font><i><p><font color=\"red\">Italic and Red </font> Just italic.</p> Italic only.</i> Plain\n<p>I should not be red. <font color=\"red\">Red. <i>Italic and red.</i></font></p><font color=\"red\"><i>\n</i></font><p><font color=\"red\"><i>Italic and red. </i> Red.</font> I should not be red.</p>\n<b>Bold <i>Bold and italic</i></b><i> Only Italic </i> Plain</body></html>",
+        "noQuirksBodyHtml": "\n<font color=\"red\"><i>Italic and Red</i></font><i><p><font color=\"red\">Italic and Red </font> Just italic.</p> Italic only.</i> Plain\n<p>I should not be red. <font color=\"red\">Red. <i>Italic and red.</i></font></p><font color=\"red\"><i>\n</i></font><p><font color=\"red\"><i>Italic and red. </i> Red.</font> I should not be red.</p>\n<b>Bold <i>Bold and italic</i></b><i> Only Italic </i> Plain"
+      }
+    },
+    {
+      "data": "<html><body>\n<p><font size=\"7\">First paragraph.</p>\n<p>Second paragraph.</p></font>\n<b><p><i>Bold and Italic</b> Italic</p>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(2,38): unexpected-end-tag",
+        "(4,28): adoption-agency-1.3",
+        "(4,28): adoption-agency-1.3",
+        "(4,39): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "font": true,
+            "b": true,
+            "i": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "\n"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "attrs": [
+                          {
+                            "name": "size",
+                            "value": "7"
+                          }
+                        ],
+                        "children": [
+                          {
+                            "text": "First paragraph."
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "font",
+                    "attrs": [
+                      {
+                        "name": "size",
+                        "value": "7"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "\n"
+                      },
+                      {
+                        "tag": "p",
+                        "children": [
+                          {
+                            "text": "Second paragraph."
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "text": "\n"
+                  },
+                  {
+                    "tag": "b"
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "i",
+                            "children": [
+                              {
+                                "text": "Bold and Italic"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "i",
+                        "children": [
+                          {
+                            "text": " Italic"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>\n<p><font size=\"7\">First paragraph.</font></p><font size=\"7\">\n<p>Second paragraph.</p></font>\n<b></b><p><b><i>Bold and Italic</i></b><i> Italic</i></p></body></html>",
+        "noQuirksBodyHtml": "\n<p><font size=\"7\">First paragraph.</font></p><font size=\"7\">\n<p>Second paragraph.</p></font>\n<b></b><p><b><i>Bold and Italic</i></b><i> Italic</i></p>"
+      }
+    },
+    {
+      "data": "<html>\n<dl>\n<dt><b>Boo\n<dd>Goo?\n</dl>\n</html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(4,4): end-tag-too-early",
+        "(5,5): end-tag-too-early",
+        "(6,7): expected-one-end-tag-but-got-another"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "dl": true,
+            "dt": true,
+            "b": true,
+            "dd": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "dl",
+                    "children": [
+                      {
+                        "text": "\n"
+                      },
+                      {
+                        "tag": "dt",
+                        "children": [
+                          {
+                            "tag": "b",
+                            "children": [
+                              {
+                                "text": "Boo\n"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "dd",
+                        "children": [
+                          {
+                            "tag": "b",
+                            "children": [
+                              {
+                                "text": "Goo?\n"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "text": "\n"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><dl>\n<dt><b>Boo\n</b></dt><dd><b>Goo?\n</b></dd></dl><b>\n</b></body></html>",
+        "noQuirksBodyHtml": "\n<dl>\n<dt><b>Boo\n</b></dt><dd><b>Goo?\n</b></dd></dl><b>\n</b>"
+      }
+    },
+    {
+      "data": "<html><body>\n<label><a><div>Hello<div>World</div></a></label>  \n</body></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(2,40): adoption-agency-1.3",
+        "(2,48): unexpected-end-tag",
+        "(3,7): expected-one-end-tag-but-got-another"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "label": true,
+            "a": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "\n"
+                  },
+                  {
+                    "tag": "label",
+                    "children": [
+                      {
+                        "tag": "a"
+                      },
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "tag": "a",
+                            "children": [
+                              {
+                                "text": "Hello"
+                              },
+                              {
+                                "tag": "div",
+                                "children": [
+                                  {
+                                    "text": "World"
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "text": "  \n"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>\n<label><a></a><div><a>Hello<div>World</div></a>  \n</div></label></body></html>",
+        "noQuirksBodyHtml": "\n<label><a></a><div><a>Hello<div>World</div></a>  \n</div></label>"
+      }
+    },
+    {
+      "data": "<table><center> <font>a</center> <img> <tr><td> </td> </tr> </table>",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,15): foster-parenting-start-tag",
+        "(1,16): foster-parenting-character",
+        "(1,22): foster-parenting-start-tag",
+        "(1,23): foster-parenting-character",
+        "(1,32): foster-parenting-end-tag",
+        "(1,32): end-tag-too-early",
+        "(1,33): foster-parenting-character",
+        "(1,38): foster-parenting-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "center": true,
+            "font": true,
+            "img": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "center",
+                    "children": [
+                      {
+                        "text": " "
+                      },
+                      {
+                        "tag": "font",
+                        "children": [
+                          {
+                            "text": "a"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "font",
+                    "children": [
+                      {
+                        "tag": "img"
+                      },
+                      {
+                        "text": " "
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "text": " "
+                      },
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "text": " "
+                                  }
+                                ]
+                              },
+                              {
+                                "text": " "
+                              }
+                            ]
+                          },
+                          {
+                            "text": " "
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><center> <font>a</font></center><font><img> </font><table> <tbody><tr><td> </td> </tr> </tbody></table></body></html>",
+        "noQuirksBodyHtml": "<center> <font>a</font></center><font><img> </font><table> <tbody><tr><td> </td> </tr> </tbody></table>"
+      }
+    },
+    {
+      "data": "<table><tr><p><a><p>You should see this text.",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,14): unexpected-start-tag-implies-table-voodoo",
+        "(1,17): unexpected-start-tag-implies-table-voodoo",
+        "(1,20): unexpected-start-tag-implies-table-voodoo",
+        "(1,20): closing-non-current-p-element",
+        "(1,21): foster-parenting-character",
+        "(1,22): foster-parenting-character",
+        "(1,23): foster-parenting-character",
+        "(1,24): foster-parenting-character",
+        "(1,25): foster-parenting-character",
+        "(1,26): foster-parenting-character",
+        "(1,27): foster-parenting-character",
+        "(1,28): foster-parenting-character",
+        "(1,29): foster-parenting-character",
+        "(1,30): foster-parenting-character",
+        "(1,31): foster-parenting-character",
+        "(1,32): foster-parenting-character",
+        "(1,33): foster-parenting-character",
+        "(1,34): foster-parenting-character",
+        "(1,35): foster-parenting-character",
+        "(1,36): foster-parenting-character",
+        "(1,37): foster-parenting-character",
+        "(1,38): foster-parenting-character",
+        "(1,39): foster-parenting-character",
+        "(1,40): foster-parenting-character",
+        "(1,41): foster-parenting-character",
+        "(1,42): foster-parenting-character",
+        "(1,43): foster-parenting-character",
+        "(1,44): foster-parenting-character",
+        "(1,45): foster-parenting-character",
+        "(1,45): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "a": true,
+            "table": true,
+            "tbody": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "a"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "text": "You should see this text."
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p><a></a></p><p><a>You should see this text.</a></p><table><tbody><tr></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<p><a></a></p><p><a>You should see this text.</a></p><table><tbody><tr></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<TABLE>\n<TR>\n<CENTER><CENTER><TD></TD></TR><TR>\n<FONT>\n<TABLE><tr></tr></TABLE>\n</P>\n<a></font><font></a>\nThis page contains an insanely badly-nested tag sequence.",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(3,8): unexpected-start-tag-implies-table-voodoo",
+        "(3,16): unexpected-start-tag-implies-table-voodoo",
+        "(4,6): unexpected-start-tag-implies-table-voodoo",
+        "(4,6): unexpected character token in table (the newline)",
+        "(5,7): unexpected-start-tag-implies-end-tag",
+        "(6,4): unexpected p end tag",
+        "(7,10): adoption-agency-1.3",
+        "(7,20): adoption-agency-1.3",
+        "(8,57): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "center": true,
+            "font": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "p": true,
+            "a": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "center",
+                    "children": [
+                      {
+                        "tag": "center"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "font",
+                    "children": [
+                      {
+                        "text": "\n"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "text": "\n"
+                      },
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "text": "\n"
+                              },
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "text": "\n"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "font",
+                    "children": [
+                      {
+                        "text": "\n"
+                      },
+                      {
+                        "tag": "p"
+                      },
+                      {
+                        "text": "\n"
+                      },
+                      {
+                        "tag": "a"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "tag": "font"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "font",
+                    "children": [
+                      {
+                        "text": "\nThis page contains an insanely badly-nested tag sequence."
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><center><center></center></center><font>\n</font><table>\n<tbody><tr>\n<td></td></tr><tr>\n</tr></tbody></table><table><tbody><tr></tr></tbody></table><font>\n<p></p>\n<a></a></font><a><font></font></a><font>\nThis page contains an insanely badly-nested tag sequence.</font></body></html>",
+        "noQuirksBodyHtml": "<center><center></center></center><font>\n</font><table>\n<tbody><tr>\n<td></td></tr><tr>\n</tr></tbody></table><table><tbody><tr></tr></tbody></table><font>\n<p></p>\n<a></a></font><a><font></font></a><font>\nThis page contains an insanely badly-nested tag sequence.</font>"
+      }
+    },
+    {
+      "data": "<html>\n<body>\n<b><nobr><div>This text is in a div inside a nobr</nobr>More text that should not be in the nobr, i.e., the\nnobr should have closed the div inside it implicitly. </b><pre>A pre tag outside everything else.</pre>\n</body>\n</html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(3,56): adoption-agency-1.3",
+        "(4,58): adoption-agency-1.3",
+        "(5,7): expected-one-end-tag-but-got-another"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "nobr": true,
+            "div": true,
+            "pre": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "\n"
+                  },
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "nobr"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "nobr",
+                            "children": [
+                              {
+                                "text": "This text is in a div inside a nobr"
+                              }
+                            ]
+                          },
+                          {
+                            "text": "More text that should not be in the nobr, i.e., the\nnobr should have closed the div inside it implicitly. "
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "pre",
+                        "children": [
+                          {
+                            "text": "A pre tag outside everything else."
+                          }
+                        ]
+                      },
+                      {
+                        "text": "\n\n"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>\n<b><nobr></nobr></b><div><b><nobr>This text is in a div inside a nobr</nobr>More text that should not be in the nobr, i.e., the\nnobr should have closed the div inside it implicitly. </b><pre>A pre tag outside everything else.</pre>\n\n</div></body></html>",
+        "noQuirksBodyHtml": "\n\n<b><nobr></nobr></b><div><b><nobr>This text is in a div inside a nobr</nobr>More text that should not be in the nobr, i.e., the\nnobr should have closed the div inside it implicitly. </b><pre>A pre tag outside everything else.</pre>\n\n</div>"
+      }
+    }
+  ],
+  "webkit01.dat": [
+    {
+      "data": "Test",
+      "errors": [
+        "(1,4): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "Test"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>Test</body></html>",
+        "noQuirksBodyHtml": "Test"
+      }
+    },
+    {
+      "data": "<div></div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div></div></body></html>",
+        "noQuirksBodyHtml": "<div></div>"
+      }
+    },
+    {
+      "data": "<div>Test</div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "Test"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>Test</div></body></html>",
+        "noQuirksBodyHtml": "<div>Test</div>"
+      }
+    },
+    {
+      "data": "<di",
+      "errors": [
+        "(1,3): eof-in-tag-name",
+        "(1,3): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<div>Hello</div>\n<script>\nconsole.log(\"PASS\");\n</script>\n<div>Bye</div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "Hello"
+                      }
+                    ]
+                  },
+                  {
+                    "text": "\n"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "\nconsole.log(\"PASS\");\n",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "\n"
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "Bye"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>Hello</div>\n<script>\nconsole.log(\"PASS\");\n</script>\n<div>Bye</div></body></html>",
+        "noQuirksBodyHtml": "<div>Hello</div>\n<script>\nconsole.log(\"PASS\");\n</script>\n<div>Bye</div>"
+      }
+    },
+    {
+      "data": "<div foo=\"bar\">Hello</div>",
+      "errors": [
+        "(1,15): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "foo",
+                        "value": "bar"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "text": "Hello"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div foo=\"bar\">Hello</div></body></html>",
+        "noQuirksBodyHtml": "<div foo=\"bar\">Hello</div>"
+      }
+    },
+    {
+      "data": "<div>Hello</div>\n<script>\nconsole.log(\"FOO<span>BAR</span>BAZ\");\n</script>\n<div>Bye</div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "script": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "Hello"
+                      }
+                    ]
+                  },
+                  {
+                    "text": "\n"
+                  },
+                  {
+                    "tag": "script",
+                    "children": [
+                      {
+                        "text": "\nconsole.log(\"FOO<span>BAR</span>BAZ\");\n",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "text": "\n"
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "text": "Bye"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div>Hello</div>\n<script>\nconsole.log(\"FOO<span>BAR</span>BAZ\");\n</script>\n<div>Bye</div></body></html>",
+        "noQuirksBodyHtml": "<div>Hello</div>\n<script>\nconsole.log(\"FOO<span>BAR</span>BAZ\");\n</script>\n<div>Bye</div>"
+      }
+    },
+    {
+      "data": "<foo bar=\"baz\"></foo><potato quack=\"duck\"></potato>",
+      "errors": [
+        "(1,15): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "foo": true,
+            "potato": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "foo",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "baz"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "potato",
+                    "attrs": [
+                      {
+                        "name": "quack",
+                        "value": "duck"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><foo bar=\"baz\"></foo><potato quack=\"duck\"></potato></body></html>",
+        "noQuirksBodyHtml": "<foo bar=\"baz\"></foo><potato quack=\"duck\"></potato>"
+      }
+    },
+    {
+      "data": "<foo bar=\"baz\"><potato quack=\"duck\"></potato></foo>",
+      "errors": [
+        "(1,15): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "foo": true,
+            "potato": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "foo",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "baz"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "potato",
+                        "attrs": [
+                          {
+                            "name": "quack",
+                            "value": "duck"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><foo bar=\"baz\"><potato quack=\"duck\"></potato></foo></body></html>",
+        "noQuirksBodyHtml": "<foo bar=\"baz\"><potato quack=\"duck\"></potato></foo>"
+      }
+    },
+    {
+      "data": "<foo></foo bar=\"baz\"><potato></potato quack=\"duck\">",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,21): attributes-in-end-tag",
+        "(1,51): attributes-in-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "foo": true,
+            "potato": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "foo"
+                  },
+                  {
+                    "tag": "potato"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><foo></foo><potato></potato></body></html>",
+        "noQuirksBodyHtml": "<foo></foo><potato></potato>"
+      }
+    },
+    {
+      "data": "</ tttt>",
+      "errors": [
+        "(1,2): expected-closing-tag-but-got-char",
+        "(1,8): expected-doctype-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "comment": " tttt"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<!-- tttt--><html><head></head><body></body></html>",
+        "noQuirksBodyHtml": "<!-- tttt-->"
+      }
+    },
+    {
+      "data": "<div FOO ><img><img></div>",
+      "errors": [
+        "(1,10): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "img": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "attrs": [
+                      {
+                        "name": "foo",
+                        "value": ""
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "img"
+                      },
+                      {
+                        "tag": "img"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div foo=\"\"><img><img></div></body></html>",
+        "noQuirksBodyHtml": "<div foo=\"\"><img><img></div>"
+      }
+    },
+    {
+      "data": "<p>Test</p<p>Test2</p>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,13): unexpected-end-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "text": "TestTest2"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p>TestTest2</p></body></html>",
+        "noQuirksBodyHtml": "<p>TestTest2</p>"
+      }
+    },
+    {
+      "data": "<rdar://problem/6869687>",
+      "errors": [
+        "(1,7): unexpected-character-after-solidus-in-tag",
+        "(1,8): unexpected-character-after-solidus-in-tag",
+        "(1,16): unexpected-character-after-solidus-in-tag",
+        "(1,24): expected-doctype-but-got-start-tag",
+        "(1,24): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "rdar:": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "rdar:",
+                    "attrs": [
+                      {
+                        "name": "6869687",
+                        "value": ""
+                      },
+                      {
+                        "name": "problem",
+                        "value": ""
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><rdar: problem=\"\" 6869687=\"\"></rdar:></body></html>",
+        "noQuirksBodyHtml": "<rdar: problem=\"\" 6869687=\"\"></rdar:>"
+      }
+    },
+    {
+      "data": "<A>test< /A>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,8): expected-tag-name",
+        "(1,12): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a",
+                    "children": [
+                      {
+                        "text": "test< /A>",
+                        "escaped": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a>test&lt; /A&gt;</a></body></html>",
+        "noQuirksBodyHtml": "<a>test&lt; /A&gt;</a>"
+      }
+    },
+    {
+      "data": "&lt;",
+      "errors": [
+        "(1,4): expected-doctype-but-got-chars"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "escaped": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "<",
+                    "escaped": true
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>&lt;</body></html>",
+        "noQuirksBodyHtml": "&lt;"
+      }
+    },
+    {
+      "data": "<body foo='bar'><body foo='baz' yo='mama'>",
+      "errors": [
+        "(1,16): expected-doctype-but-got-start-tag",
+        "(1,42): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "attrs": [
+                  {
+                    "name": "foo",
+                    "value": "bar"
+                  },
+                  {
+                    "name": "yo",
+                    "value": "mama"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body foo=\"bar\" yo=\"mama\"></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<body></br foo=\"bar\"></body>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,21): attributes-in-end-tag",
+        "(1,21): unexpected-end-tag-treated-as"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "br": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "br"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><br></body></html>",
+        "noQuirksBodyHtml": "<br>"
+      }
+    },
+    {
+      "data": "<bdy><br foo=\"bar\"></body>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,26): expected-one-end-tag-but-got-another"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "bdy": true,
+            "br": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "bdy",
+                    "children": [
+                      {
+                        "tag": "br",
+                        "attrs": [
+                          {
+                            "name": "foo",
+                            "value": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><bdy><br foo=\"bar\"></bdy></body></html>",
+        "noQuirksBodyHtml": "<bdy><br foo=\"bar\"></bdy>"
+      }
+    },
+    {
+      "data": "<body></body></br foo=\"bar\">",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,28): attributes-in-end-tag",
+        "(1,28): unexpected-end-tag-after-body",
+        "(1,28): unexpected-end-tag-treated-as"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "br": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "br"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><br></body></html>",
+        "noQuirksBodyHtml": "<br>"
+      }
+    },
+    {
+      "data": "<bdy></body><br foo=\"bar\">",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,12): expected-one-end-tag-but-got-another",
+        "(1,26): unexpected-start-tag-after-body",
+        "(1,26): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "bdy": true,
+            "br": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "bdy",
+                    "children": [
+                      {
+                        "tag": "br",
+                        "attrs": [
+                          {
+                            "name": "foo",
+                            "value": "bar"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><bdy><br foo=\"bar\"></bdy></body></html>",
+        "noQuirksBodyHtml": "<bdy><br foo=\"bar\"></bdy>"
+      }
+    },
+    {
+      "data": "<html><body></body></html><!-- Hi there -->",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          },
+          {
+            "comment": " Hi there "
+          }
+        ],
+        "html": "<html><head></head><body></body></html><!-- Hi there -->",
+        "noQuirksBodyHtml": "<!-- Hi there -->"
+      }
+    },
+    {
+      "data": "<html><body></body></html>x<!-- Hi there -->",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,27): expected-eof-but-got-char"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "x"
+                  },
+                  {
+                    "comment": " Hi there "
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>x<!-- Hi there --></body></html>",
+        "noQuirksBodyHtml": "x<!-- Hi there -->"
+      }
+    },
+    {
+      "data": "<html><body></body></html>x<!-- Hi there --></html><!-- Again -->",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,27): expected-eof-but-got-char"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "x"
+                  },
+                  {
+                    "comment": " Hi there "
+                  }
+                ]
+              }
+            ]
+          },
+          {
+            "comment": " Again "
+          }
+        ],
+        "html": "<html><head></head><body>x<!-- Hi there --></body></html><!-- Again -->",
+        "noQuirksBodyHtml": "x<!-- Hi there --><!-- Again -->"
+      }
+    },
+    {
+      "data": "<html><body></body></html>x<!-- Hi there --></body></html><!-- Again -->",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,27): expected-eof-but-got-char"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          },
+          "comment": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "x"
+                  },
+                  {
+                    "comment": " Hi there "
+                  }
+                ]
+              }
+            ]
+          },
+          {
+            "comment": " Again "
+          }
+        ],
+        "html": "<html><head></head><body>x<!-- Hi there --></body></html><!-- Again -->",
+        "noQuirksBodyHtml": "x<!-- Hi there --><!-- Again -->"
+      }
+    },
+    {
+      "data": "<html><body><ruby><div><rp>xx</rp></div></ruby></body></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,27): XXX-undefined-error"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "div": true,
+            "rp": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "tag": "rp",
+                            "children": [
+                              {
+                                "text": "xx"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby><div><rp>xx</rp></div></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby><div><rp>xx</rp></div></ruby>"
+      }
+    },
+    {
+      "data": "<html><body><ruby><div><rt>xx</rt></div></ruby></body></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,27): XXX-undefined-error"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ruby": true,
+            "div": true,
+            "rt": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ruby",
+                    "children": [
+                      {
+                        "tag": "div",
+                        "children": [
+                          {
+                            "tag": "rt",
+                            "children": [
+                              {
+                                "text": "xx"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ruby><div><rt>xx</rt></div></ruby></body></html>",
+        "noQuirksBodyHtml": "<ruby><div><rt>xx</rt></div></ruby>"
+      }
+    },
+    {
+      "data": "<html><frameset><!--1--><noframes>A</noframes><!--2--></frameset><!--3--><noframes>B</noframes><!--4--></html><!--5--><noframes>C</noframes><!--6-->",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true,
+            "noframes": true
+          },
+          "comment": true,
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset",
+                "children": [
+                  {
+                    "comment": "1"
+                  },
+                  {
+                    "tag": "noframes",
+                    "children": [
+                      {
+                        "text": "A",
+                        "no_escape": true
+                      }
+                    ]
+                  },
+                  {
+                    "comment": "2"
+                  }
+                ]
+              },
+              {
+                "comment": "3"
+              },
+              {
+                "tag": "noframes",
+                "children": [
+                  {
+                    "text": "B",
+                    "no_escape": true
+                  }
+                ]
+              },
+              {
+                "comment": "4"
+              },
+              {
+                "tag": "noframes",
+                "children": [
+                  {
+                    "text": "C",
+                    "no_escape": true
+                  }
+                ]
+              }
+            ]
+          },
+          {
+            "comment": "5"
+          },
+          {
+            "comment": "6"
+          }
+        ],
+        "html": "<html><head></head><frameset><!--1--><noframes>A</noframes><!--2--></frameset><!--3--><noframes>B</noframes><!--4--><noframes>C</noframes></html><!--5--><!--6-->",
+        "noQuirksBodyHtml": "<!--1--><noframes>A</noframes><!--2--><!--3--><noframes>B</noframes><!--4--><!--5--><noframes>C</noframes><!--6-->"
+      }
+    },
+    {
+      "data": "<select><option>A<select><option>B<select><option>C<select><option>D<select><option>E<select><option>F<select><option>G<select>",
+      "errors": [
+        "(1,8): expected-doctype-but-got-start-tag",
+        "(1,25): unexpected-select-in-select",
+        "(1,59): unexpected-select-in-select",
+        "(1,93): unexpected-select-in-select",
+        "(1,127): unexpected-select-in-select",
+        "(1,127): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "select": true,
+            "option": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "select",
+                    "children": [
+                      {
+                        "tag": "option",
+                        "children": [
+                          {
+                            "text": "A"
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "option",
+                    "children": [
+                      {
+                        "text": "B"
+                      },
+                      {
+                        "tag": "select",
+                        "children": [
+                          {
+                            "tag": "option",
+                            "children": [
+                              {
+                                "text": "C"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "option",
+                    "children": [
+                      {
+                        "text": "D"
+                      },
+                      {
+                        "tag": "select",
+                        "children": [
+                          {
+                            "tag": "option",
+                            "children": [
+                              {
+                                "text": "E"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "option",
+                    "children": [
+                      {
+                        "text": "F"
+                      },
+                      {
+                        "tag": "select",
+                        "children": [
+                          {
+                            "tag": "option",
+                            "children": [
+                              {
+                                "text": "G"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><select><option>A</option></select><option>B<select><option>C</option></select></option><option>D<select><option>E</option></select></option><option>F<select><option>G</option></select></option></body></html>",
+        "noQuirksBodyHtml": "<select><option>A</option></select><option>B<select><option>C</option></select></option><option>D<select><option>E</option></select></option><option>F<select><option>G</option></select></option>"
+      }
+    },
+    {
+      "data": "<dd><dd><dt><dt><dd><li><li>",
+      "errors": [
+        "(1,4): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "dd": true,
+            "dt": true,
+            "li": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "dd"
+                  },
+                  {
+                    "tag": "dd"
+                  },
+                  {
+                    "tag": "dt"
+                  },
+                  {
+                    "tag": "dt"
+                  },
+                  {
+                    "tag": "dd",
+                    "children": [
+                      {
+                        "tag": "li"
+                      },
+                      {
+                        "tag": "li"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><dd></dd><dd></dd><dt></dt><dt></dt><dd><li></li><li></li></dd></body></html>",
+        "noQuirksBodyHtml": "<dd></dd><dd></dd><dt></dt><dt></dt><dd><li></li><li></li></dd>"
+      }
+    },
+    {
+      "data": "<div><b></div><div><nobr>a<nobr>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,14): end-tag-too-early",
+        "(1,32): unexpected-start-tag-implies-end-tag",
+        "(1,32): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "b": true,
+            "nobr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "b",
+                        "children": [
+                          {
+                            "tag": "nobr",
+                            "children": [
+                              {
+                                "text": "a"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "nobr"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><b></b></div><div><b><nobr>a</nobr><nobr></nobr></b></div></body></html>",
+        "noQuirksBodyHtml": "<div><b></b></div><div><b><nobr>a</nobr><nobr></nobr></b></div>"
+      }
+    },
+    {
+      "data": "<head></head>\n<body></body>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "text": "\n"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head>\n<body></body></html>",
+        "noQuirksBodyHtml": "\n"
+      }
+    },
+    {
+      "data": "<head></head> <style></style>ddd",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,21): unexpected-start-tag-out-of-my-head"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "style": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head",
+                "children": [
+                  {
+                    "tag": "style"
+                  }
+                ]
+              },
+              {
+                "text": " "
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "ddd"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head><style></style></head> <body>ddd</body></html>",
+        "noQuirksBodyHtml": " <style></style>ddd"
+      }
+    },
+    {
+      "data": "<kbd><table></kbd><col><select><tr>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,18): unexpected-end-tag-implies-table-voodoo",
+        "(1,18): unexpected-end-tag",
+        "(1,31): unexpected-start-tag-implies-table-voodoo",
+        "(1,35): unexpected-table-element-start-tag-in-select-in-table",
+        "(1,35): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "kbd": true,
+            "select": true,
+            "table": true,
+            "colgroup": true,
+            "col": true,
+            "tbody": true,
+            "tr": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "kbd",
+                    "children": [
+                      {
+                        "tag": "select"
+                      },
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "tag": "colgroup",
+                            "children": [
+                              {
+                                "tag": "col"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "tbody",
+                            "children": [
+                              {
+                                "tag": "tr"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><kbd><select></select><table><colgroup><col></colgroup><tbody><tr></tr></tbody></table></kbd></body></html>",
+        "noQuirksBodyHtml": "<kbd><select></select><table><colgroup><col></colgroup><tbody><tr></tr></tbody></table></kbd>"
+      }
+    },
+    {
+      "data": "<kbd><table></kbd><col><select><tr></table><div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,18): unexpected-end-tag-implies-table-voodoo",
+        "(1,18): unexpected-end-tag",
+        "(1,31): unexpected-start-tag-implies-table-voodoo",
+        "(1,35): unexpected-table-element-start-tag-in-select-in-table",
+        "(1,48): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "kbd": true,
+            "select": true,
+            "table": true,
+            "colgroup": true,
+            "col": true,
+            "tbody": true,
+            "tr": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "kbd",
+                    "children": [
+                      {
+                        "tag": "select"
+                      },
+                      {
+                        "tag": "table",
+                        "children": [
+                          {
+                            "tag": "colgroup",
+                            "children": [
+                              {
+                                "tag": "col"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "tbody",
+                            "children": [
+                              {
+                                "tag": "tr"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "div"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><kbd><select></select><table><colgroup><col></colgroup><tbody><tr></tr></tbody></table><div></div></kbd></body></html>",
+        "noQuirksBodyHtml": "<kbd><select></select><table><colgroup><col></colgroup><tbody><tr></tr></tbody></table><div></div></kbd>"
+      }
+    },
+    {
+      "data": "<a><li><style></style><title></title></a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,41): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "li": true,
+            "style": true,
+            "title": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a"
+                  },
+                  {
+                    "tag": "li",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "tag": "style"
+                          },
+                          {
+                            "tag": "title"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a></a><li><a><style></style><title></title></a></li></body></html>",
+        "noQuirksBodyHtml": "<a></a><li><a><style></style><title></title></a></li>"
+      }
+    },
+    {
+      "data": "<font></p><p><meta><title></title></font>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,10): unexpected-end-tag",
+        "(1,41): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "font": true,
+            "p": true,
+            "meta": true,
+            "title": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "font",
+                    "children": [
+                      {
+                        "tag": "p"
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "p",
+                    "children": [
+                      {
+                        "tag": "font",
+                        "children": [
+                          {
+                            "tag": "meta"
+                          },
+                          {
+                            "tag": "title"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><font><p></p></font><p><font><meta><title></title></font></p></body></html>",
+        "noQuirksBodyHtml": "<font><p></p></font><p><font><meta><title></title></font></p>"
+      }
+    },
+    {
+      "data": "<a><center><title></title><a>",
+      "errors": [
+        "(1,3): expected-doctype-but-got-start-tag",
+        "(1,29): unexpected-start-tag-implies-end-tag",
+        "(1,29): adoption-agency-1.3",
+        "(1,29): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "a": true,
+            "center": true,
+            "title": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "a"
+                  },
+                  {
+                    "tag": "center",
+                    "children": [
+                      {
+                        "tag": "a",
+                        "children": [
+                          {
+                            "tag": "title"
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "a"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><a></a><center><a><title></title></a><a></a></center></body></html>",
+        "noQuirksBodyHtml": "<a></a><center><a><title></title></a><a></a></center>"
+      }
+    },
+    {
+      "data": "<svg><title><div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,17): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg title": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "title",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg><title><div></div></title></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><title><div></div></title></svg>"
+      }
+    },
+    {
+      "data": "<svg><title><rect><div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,23): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg title": true,
+            "rect": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "title",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "rect",
+                            "children": [
+                              {
+                                "tag": "div"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg><title><rect><div></div></rect></title></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><title><rect><div></div></rect></title></svg>"
+      }
+    },
+    {
+      "data": "<svg><title><svg><div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,22): unexpected-html-element-in-foreign-content",
+        "(1,22): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg title": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "title",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "svg",
+                            "ns": "http://www.w3.org/2000/svg"
+                          },
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg><title><svg></svg><div></div></title></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><title><svg><div></div></svg></title></svg>"
+      }
+    },
+    {
+      "data": "<img <=\"\" FAIL>",
+      "errors": [
+        "(1,6): invalid-character-in-attribute-name",
+        "(1,15): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "img": true
+          },
+          "attrWithFunnyChar": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "img",
+                    "attrs": [
+                      {
+                        "name": "<",
+                        "value": ""
+                      },
+                      {
+                        "name": "fail",
+                        "value": ""
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><img <=\"\" fail=\"\"></body></html>",
+        "noQuirksBodyHtml": "<img <=\"\" fail=\"\">"
+      }
+    },
+    {
+      "data": "<ul><li><div id='foo'/>A</li><li>B<div>C</div></li></ul>",
+      "errors": [
+        "(1,4): expected-doctype-but-got-start-tag",
+        "(1,23): non-void-element-with-trailing-solidus",
+        "(1,29): end-tag-too-early"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "ul": true,
+            "li": true,
+            "div": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "ul",
+                    "children": [
+                      {
+                        "tag": "li",
+                        "children": [
+                          {
+                            "tag": "div",
+                            "attrs": [
+                              {
+                                "name": "id",
+                                "value": "foo"
+                              }
+                            ],
+                            "children": [
+                              {
+                                "text": "A"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "li",
+                        "children": [
+                          {
+                            "text": "B"
+                          },
+                          {
+                            "tag": "div",
+                            "children": [
+                              {
+                                "text": "C"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><ul><li><div id=\"foo\">A</div></li><li>B<div>C</div></li></ul></body></html>",
+        "noQuirksBodyHtml": "<ul><li><div id=\"foo\">A</div></li><li>B<div>C</div></li></ul>"
+      }
+    },
+    {
+      "data": "<svg><em><desc></em>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,9): unexpected-html-element-in-foreign-content",
+        "(1,20): adoption-agency-1.3"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "em": true,
+            "desc": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg"
+                  },
+                  {
+                    "tag": "em",
+                    "children": [
+                      {
+                        "tag": "desc"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg></svg><em><desc></desc></em></body></html>",
+        "noQuirksBodyHtml": "<svg><em><desc></desc></em></svg>"
+      }
+    },
+    {
+      "data": "<table><tr><td><svg><desc><td></desc><circle>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true,
+            "svg svg": true,
+            "svg desc": true,
+            "circle": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "svg",
+                                    "ns": "http://www.w3.org/2000/svg",
+                                    "children": [
+                                      {
+                                        "tag": "desc",
+                                        "ns": "http://www.w3.org/2000/svg"
+                                      }
+                                    ]
+                                  }
+                                ]
+                              },
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "tag": "circle"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td><svg><desc></desc></svg></td><td><circle></circle></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td><svg><desc></desc></svg></td><td><circle></circle></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<svg><tfoot></mi><td>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag",
+        "(1,17): unexpected-end-tag",
+        "(1,17): unexpected-end-tag",
+        "(1,21): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg tfoot": true,
+            "svg td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "tfoot",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "td",
+                            "ns": "http://www.w3.org/2000/svg"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg><tfoot><td></td></tfoot></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><tfoot><td></td></tfoot></svg>"
+      }
+    },
+    {
+      "data": "<math><mrow><mrow><mn>1</mn></mrow><mi>a</mi></mrow></math>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "math math": true,
+            "math mrow": true,
+            "math mn": true,
+            "math mi": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "math",
+                    "ns": "http://www.w3.org/1998/Math/MathML",
+                    "children": [
+                      {
+                        "tag": "mrow",
+                        "ns": "http://www.w3.org/1998/Math/MathML",
+                        "children": [
+                          {
+                            "tag": "mrow",
+                            "ns": "http://www.w3.org/1998/Math/MathML",
+                            "children": [
+                              {
+                                "tag": "mn",
+                                "ns": "http://www.w3.org/1998/Math/MathML",
+                                "children": [
+                                  {
+                                    "text": "1"
+                                  }
+                                ]
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "mi",
+                            "ns": "http://www.w3.org/1998/Math/MathML",
+                            "children": [
+                              {
+                                "text": "a"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><math><mrow><mrow><mn>1</mn></mrow><mi>a</mi></mrow></math></body></html>",
+        "noQuirksBodyHtml": "<math><mrow><mrow><mn>1</mn></mrow><mi>a</mi></mrow></math>"
+      }
+    },
+    {
+      "data": "<!doctype html><input type=\"hidden\"><frameset>",
+      "errors": [
+        "(1,46): unexpected-start-tag",
+        "(1,46): eof-in-frameset"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "frameset": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "frameset"
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><frameset></frameset></html>",
+        "noQuirksBodyHtml": "<input type=\"hidden\">"
+      }
+    },
+    {
+      "data": "<!doctype html><input type=\"button\"><frameset>",
+      "errors": [
+        "(1,46): unexpected-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "input": true
+          },
+          "doctype": true
+        },
+        "tree": [
+          {
+            "doctype": "html"
+          },
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "input",
+                    "attrs": [
+                      {
+                        "name": "type",
+                        "value": "button"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<!DOCTYPE html><html><head></head><body><input type=\"button\"></body></html>",
+        "noQuirksBodyHtml": "<input type=\"button\">"
+      }
+    }
+  ],
+  "webkit02.dat": [
+    {
+      "data": "<foo bar=qux/>",
+      "errors": [
+        "(1,14): expected-doctype-but-got-start-tag",
+        "(1,14): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "foo": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "foo",
+                    "attrs": [
+                      {
+                        "name": "bar",
+                        "value": "qux/"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><foo bar=\"qux/\"></foo></body></html>",
+        "noQuirksBodyHtml": "<foo bar=\"qux/\"></foo>"
+      }
+    },
+    {
+      "data": "<p id=\"status\"><noscript><strong>A</strong></noscript><span>B</span></p>",
+      "errors": [
+        "(1,15): expected-doctype-but-got-start-tag"
+      ],
+      "script": "on",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "noscript": true,
+            "span": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "attrs": [
+                      {
+                        "name": "id",
+                        "value": "status"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "noscript",
+                        "children": [
+                          {
+                            "text": "<strong>A</strong>",
+                            "no_escape": true
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "span",
+                        "children": [
+                          {
+                            "text": "B"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p id=\"status\"><noscript><strong>A</strong></noscript><span>B</span></p></body></html>",
+        "noQuirksBodyHtml": "<p id=\"status\"><noscript>&lt;strong&gt;A&lt;/strong&gt;</noscript><span>B</span></p>"
+      }
+    },
+    {
+      "data": "<p id=\"status\"><noscript><strong>A</strong></noscript><span>B</span></p>",
+      "errors": [
+        "(1,15): expected-doctype-but-got-start-tag"
+      ],
+      "script": "off",
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "p": true,
+            "noscript": true,
+            "strong": true,
+            "span": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "p",
+                    "attrs": [
+                      {
+                        "name": "id",
+                        "value": "status"
+                      }
+                    ],
+                    "children": [
+                      {
+                        "tag": "noscript",
+                        "children": [
+                          {
+                            "tag": "strong",
+                            "children": [
+                              {
+                                "text": "A"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "tag": "span",
+                        "children": [
+                          {
+                            "text": "B"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><p id=\"status\"><noscript><strong>A</strong></noscript><span>B</span></p></body></html>",
+        "noQuirksBodyHtml": "<p id=\"status\"><noscript>&lt;strong&gt;A&lt;/strong&gt;</noscript><span>B</span></p>"
+      }
+    },
+    {
+      "data": "<div><sarcasm><div></div></sarcasm></div>",
+      "errors": [
+        "(1,5): expected-doctype-but-got-start-tag"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "div": true,
+            "sarcasm": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "div",
+                    "children": [
+                      {
+                        "tag": "sarcasm",
+                        "children": [
+                          {
+                            "tag": "div"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><div><sarcasm><div></div></sarcasm></div></body></html>",
+        "noQuirksBodyHtml": "<div><sarcasm><div></div></sarcasm></div>"
+      }
+    },
+    {
+      "data": "<html><body><img src=\"\" border=\"0\" alt=\"><div>A</div></body></html>",
+      "errors": [
+        "(1,6): expected-doctype-but-got-start-tag",
+        "(1,67): eof-in-attribute-value-double-quote"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body"
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body></body></html>",
+        "noQuirksBodyHtml": ""
+      }
+    },
+    {
+      "data": "<table><td></tbody>A",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-cell-in-table-body",
+        "(1,20): foster-parenting-character",
+        "(1,20): eof-in-table"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "text": "A"
+                  },
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body>A<table><tbody><tr><td></td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "A<table><tbody><tr><td></td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><td></thead>A",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-cell-in-table-body",
+        "(1,19): XXX-undefined-error",
+        "(1,20): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "text": "A"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td>A</td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td>A</td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><td></tfoot>A",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,11): unexpected-cell-in-table-body",
+        "(1,19): XXX-undefined-error",
+        "(1,20): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "tbody": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "tbody",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "text": "A"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><tbody><tr><td>A</td></tr></tbody></table></body></html>",
+        "noQuirksBodyHtml": "<table><tbody><tr><td>A</td></tr></tbody></table>"
+      }
+    },
+    {
+      "data": "<table><thead><td></tbody>A",
+      "errors": [
+        "(1,7): expected-doctype-but-got-start-tag",
+        "(1,18): unexpected-cell-in-table-body",
+        "(1,26): XXX-undefined-error",
+        "(1,27): expected-closing-tag-but-got-eof"
+      ],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "table": true,
+            "thead": true,
+            "tr": true,
+            "td": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "table",
+                    "children": [
+                      {
+                        "tag": "thead",
+                        "children": [
+                          {
+                            "tag": "tr",
+                            "children": [
+                              {
+                                "tag": "td",
+                                "children": [
+                                  {
+                                    "text": "A"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><table><thead><tr><td>A</td></tr></thead></table></body></html>",
+        "noQuirksBodyHtml": "<table><thead><tr><td>A</td></tr></thead></table>"
+      }
+    },
+    {
+      "data": "<legend>test</legend>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "legend": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "legend",
+                    "children": [
+                      {
+                        "text": "test"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><legend>test</legend></body></html>",
+        "noQuirksBodyHtml": "<legend>test</legend>"
+      }
+    },
+    {
+      "data": "<table><input>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "input": true,
+            "table": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "input"
+                  },
+                  {
+                    "tag": "table"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><input><table></table></body></html>",
+        "noQuirksBodyHtml": "<input><table></table>"
+      }
+    },
+    {
+      "data": "<b><em><foo><foo><aside></b>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "em": true,
+            "foo": true,
+            "aside": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "em",
+                        "children": [
+                          {
+                            "tag": "foo",
+                            "children": [
+                              {
+                                "tag": "foo"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "em",
+                    "children": [
+                      {
+                        "tag": "aside",
+                        "children": [
+                          {
+                            "tag": "b"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b><em><foo><foo></foo></foo></em></b><em><aside><b></b></aside></em></body></html>",
+        "noQuirksBodyHtml": "<b><em><foo><foo></foo></foo></em></b><em><aside><b></b></aside></em>"
+      }
+    },
+    {
+      "data": "<b><em><foo><foo><aside></b></em>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "em": true,
+            "foo": true,
+            "aside": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "em",
+                        "children": [
+                          {
+                            "tag": "foo",
+                            "children": [
+                              {
+                                "tag": "foo"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "em"
+                  },
+                  {
+                    "tag": "aside",
+                    "children": [
+                      {
+                        "tag": "em",
+                        "children": [
+                          {
+                            "tag": "b"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b><em><foo><foo></foo></foo></em></b><em></em><aside><em><b></b></em></aside></body></html>",
+        "noQuirksBodyHtml": "<b><em><foo><foo></foo></foo></em></b><em></em><aside><em><b></b></em></aside>"
+      }
+    },
+    {
+      "data": "<b><em><foo><foo><foo><aside></b>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "em": true,
+            "foo": true,
+            "aside": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "em",
+                        "children": [
+                          {
+                            "tag": "foo",
+                            "children": [
+                              {
+                                "tag": "foo",
+                                "children": [
+                                  {
+                                    "tag": "foo"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "aside",
+                    "children": [
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b><em><foo><foo><foo></foo></foo></foo></em></b><aside><b></b></aside></body></html>",
+        "noQuirksBodyHtml": "<b><em><foo><foo><foo></foo></foo></foo></em></b><aside><b></b></aside>"
+      }
+    },
+    {
+      "data": "<b><em><foo><foo><foo><aside></b></em>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "b": true,
+            "em": true,
+            "foo": true,
+            "aside": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "b",
+                    "children": [
+                      {
+                        "tag": "em",
+                        "children": [
+                          {
+                            "tag": "foo",
+                            "children": [
+                              {
+                                "tag": "foo",
+                                "children": [
+                                  {
+                                    "tag": "foo"
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    "tag": "aside",
+                    "children": [
+                      {
+                        "tag": "b"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><b><em><foo><foo><foo></foo></foo></foo></em></b><aside><b></b></aside></body></html>",
+        "noQuirksBodyHtml": "<b><em><foo><foo><foo></foo></foo></foo></em></b><aside><b></b></aside>"
+      }
+    },
+    {
+      "data": "<b><em><foo><foo><foo><foo><foo><foo><foo><foo><foo><foo><aside></b></em>",
+      "errors": [],
+      "fragment": {
+        "name": "div"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "b": true,
+            "em": true,
+            "foo": true,
+            "aside": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "b",
+            "children": [
+              {
+                "tag": "em",
+                "children": [
+                  {
+                    "tag": "foo",
+                    "children": [
+                      {
+                        "tag": "foo",
+                        "children": [
+                          {
+                            "tag": "foo",
+                            "children": [
+                              {
+                                "tag": "foo",
+                                "children": [
+                                  {
+                                    "tag": "foo",
+                                    "children": [
+                                      {
+                                        "tag": "foo",
+                                        "children": [
+                                          {
+                                            "tag": "foo",
+                                            "children": [
+                                              {
+                                                "tag": "foo",
+                                                "children": [
+                                                  {
+                                                    "tag": "foo",
+                                                    "children": [
+                                                      {
+                                                        "tag": "foo"
+                                                      }
+                                                    ]
+                                                  }
+                                                ]
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          },
+          {
+            "tag": "aside",
+            "children": [
+              {
+                "tag": "b"
+              }
+            ]
+          }
+        ],
+        "html": "<b><em><foo><foo><foo><foo><foo><foo><foo><foo><foo><foo></foo></foo></foo></foo></foo></foo></foo></foo></foo></foo></em></b><aside><b></b></aside>",
+        "noQuirksBodyHtml": "<b><em><foo><foo><foo><foo><foo><foo><foo><foo><foo><foo></foo></foo></foo></foo></foo></foo></foo></foo></foo></foo></em></b><aside><b></b></aside>"
+      }
+    },
+    {
+      "data": "<b><em><foo><foob><foob><foob><foob><fooc><fooc><fooc><fooc><food><aside></b></em>",
+      "errors": [],
+      "fragment": {
+        "name": "div"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "b": true,
+            "em": true,
+            "foo": true,
+            "foob": true,
+            "fooc": true,
+            "food": true,
+            "aside": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "b",
+            "children": [
+              {
+                "tag": "em",
+                "children": [
+                  {
+                    "tag": "foo",
+                    "children": [
+                      {
+                        "tag": "foob",
+                        "children": [
+                          {
+                            "tag": "foob",
+                            "children": [
+                              {
+                                "tag": "foob",
+                                "children": [
+                                  {
+                                    "tag": "foob",
+                                    "children": [
+                                      {
+                                        "tag": "fooc",
+                                        "children": [
+                                          {
+                                            "tag": "fooc",
+                                            "children": [
+                                              {
+                                                "tag": "fooc",
+                                                "children": [
+                                                  {
+                                                    "tag": "fooc",
+                                                    "children": [
+                                                      {
+                                                        "tag": "food"
+                                                      }
+                                                    ]
+                                                  }
+                                                ]
+                                              }
+                                            ]
+                                          }
+                                        ]
+                                      }
+                                    ]
+                                  }
+                                ]
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          },
+          {
+            "tag": "aside",
+            "children": [
+              {
+                "tag": "b"
+              }
+            ]
+          }
+        ],
+        "html": "<b><em><foo><foob><foob><foob><foob><fooc><fooc><fooc><fooc><food></food></fooc></fooc></fooc></fooc></foob></foob></foob></foob></foo></em></b><aside><b></b></aside>",
+        "noQuirksBodyHtml": "<b><em><foo><foob><foob><foob><foob><fooc><fooc><fooc><fooc><food></food></fooc></fooc></fooc></fooc></foob></foob></foob></foob></foo></em></b><aside><b></b></aside>"
+      }
+    },
+    {
+      "data": "<isindex action=\"x\">",
+      "errors": [],
+      "fragment": {
+        "name": "table"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "form": true,
+            "hr": true,
+            "label": true,
+            "input": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "form",
+            "attrs": [
+              {
+                "name": "action",
+                "value": "x"
+              }
+            ],
+            "children": [
+              {
+                "tag": "hr"
+              },
+              {
+                "tag": "label",
+                "children": [
+                  {
+                    "text": "This is a searchable index. Enter search keywords: "
+                  },
+                  {
+                    "tag": "input",
+                    "attrs": [
+                      {
+                        "name": "name",
+                        "value": "isindex"
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "tag": "hr"
+              }
+            ]
+          }
+        ],
+        "html": "<form action=\"x\"><hr><label>This is a searchable index. Enter search keywords: <input name=\"isindex\"></label><hr></form>",
+        "noQuirksBodyHtml": "<form action=\"x\"><hr><label>This is a searchable index. Enter search keywords: <input name=\"isindex\"></label><hr></form>"
+      }
+    },
+    {
+      "data": "<option><XH<optgroup></optgroup>",
+      "errors": [],
+      "fragment": {
+        "name": "select"
+      },
+      "document": {
+        "props": {
+          "tags": {
+            "option": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "option"
+          }
+        ],
+        "html": "<option></option>",
+        "noQuirksBodyHtml": "<option><xh<optgroup></xh<optgroup></option>"
+      }
+    },
+    {
+      "data": "<svg><foreignObject><div>foo</div><plaintext></foreignObject></svg><div>bar</div>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg foreignObject": true,
+            "div": true,
+            "plaintext": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "foreignObject",
+                        "ns": "http://www.w3.org/2000/svg",
+                        "children": [
+                          {
+                            "tag": "div",
+                            "children": [
+                              {
+                                "text": "foo"
+                              }
+                            ]
+                          },
+                          {
+                            "tag": "plaintext",
+                            "children": [
+                              {
+                                "text": "</foreignObject></svg><div>bar</div>",
+                                "no_escape": true
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg><foreignObject><div>foo</div><plaintext></foreignObject></svg><div>bar</div></plaintext></foreignObject></svg></body></html>",
+        "noQuirksBodyHtml": "<svg><foreignObject><div>foo</div><plaintext></foreignObject></svg><div>bar</div></plaintext></foreignObject></svg>"
+      }
+    },
+    {
+      "data": "<svg><foreignObject></foreignObject><title></svg>foo",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "svg svg": true,
+            "svg foreignObject": true,
+            "svg title": true
+          }
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "svg",
+                    "ns": "http://www.w3.org/2000/svg",
+                    "children": [
+                      {
+                        "tag": "foreignObject",
+                        "ns": "http://www.w3.org/2000/svg"
+                      },
+                      {
+                        "tag": "title",
+                        "ns": "http://www.w3.org/2000/svg"
+                      }
+                    ]
+                  },
+                  {
+                    "text": "foo"
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><svg><foreignObject></foreignObject><title></title></svg>foo</body></html>",
+        "noQuirksBodyHtml": "<svg><foreignObject></foreignObject><title></title></svg>foo"
+      }
+    },
+    {
+      "data": "</foreignObject><plaintext><div>foo</div>",
+      "errors": [],
+      "document": {
+        "props": {
+          "tags": {
+            "html": true,
+            "head": true,
+            "body": true,
+            "plaintext": true
+          },
+          "no_escape": true
+        },
+        "tree": [
+          {
+            "tag": "html",
+            "children": [
+              {
+                "tag": "head"
+              },
+              {
+                "tag": "body",
+                "children": [
+                  {
+                    "tag": "plaintext",
+                    "children": [
+                      {
+                        "text": "<div>foo</div>",
+                        "no_escape": true
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ],
+        "html": "<html><head></head><body><plaintext><div>foo</div></plaintext></body></html>",
+        "noQuirksBodyHtml": "<plaintext><div>foo</div></plaintext>"
+      }
+    }
+  ]
+}
index 4d914e4..e7ac940 100644 (file)
@@ -355,9 +355,9 @@ class MediaWikiTitleCodecTest extends MediaWikiTestCase {
                        // XML/HTML character entity references
                        // Note: Commented out because they are not marked invalid by the PHP test as
                        // Title::newFromText runs Sanitizer::decodeCharReferencesAndNormalize first.
-                       // array( 'A &eacute; B' ),
-                       // array( 'A &#233; B' ),
-                       // array( 'A &#x00E9; B' ),
+                       // [ 'A &eacute; B' ],
+                       // [ 'A &#233; B' ],
+                       // [ 'A &#x00E9; B' ],
                        // Subject of NS_TALK does not roundtrip to NS_MAIN
                        [ 'Talk:File:Example.svg' ],
                        // Directory navigation
index 287af29..3debe6e 100644 (file)
@@ -52,7 +52,7 @@ class UploadBaseTest extends MediaWikiTestCase {
                        [ 'ValidTitle.jpg', 'ValidTitle.jpg', UploadBase::OK,
                                'upload valid title' ],
                        /* A title with a slash */
-                       [ 'A/B.jpg', 'B.jpg', UploadBase::OK,
+                       [ 'A/B.jpg', 'A-B.jpg', UploadBase::OK,
                                'upload title with slash' ],
                        /* A title with illegal char */
                        [ 'A:B.jpg', 'A-B.jpg', UploadBase::OK,
@@ -396,6 +396,23 @@ class UploadBaseTest extends MediaWikiTestCase {
                ];
                // @codingStandardsIgnoreEnd
        }
+
+       /**
+        * @dataProvider provideCheckXMLEncodingMissmatch
+        */
+       public function testCheckXMLEncodingMissmatch( $fileContents, $evil ) {
+               $filename = $this->getNewTempFile();
+               file_put_contents( $filename, $fileContents );
+               $this->assertSame( UploadBase::checkXMLEncodingMissmatch( $filename ), $evil );
+       }
+
+       public function provideCheckXMLEncodingMissmatch() {
+               return [
+                       [ '<?xml version="1.0" encoding="utf-7"?><svg></svg>', true ],
+                       [ '<?xml version="1.0" encoding="utf-8"?><svg></svg>', false ],
+                       [ '<?xml version="1.0" encoding="WINDOWS-1252"?><svg></svg>', false ],
+               ];
+       }
 }
 
 class UploadTestHandler extends UploadBase {
index 801ab91..985554b 100644 (file)
@@ -92,6 +92,57 @@ class UserTest extends MediaWikiTestCase {
                $this->assertNotContains( 'nukeworld', $rights );
        }
 
+       /**
+        * @covers User::getRights
+        */
+       public function testUserGetRightsHooks() {
+               $user = new User;
+               $user->addGroup( 'unittesters' );
+               $user->addGroup( 'testwriters' );
+               $userWrapper = TestingAccessWrapper::newFromObject( $user );
+
+               $rights = $user->getRights();
+               $this->assertContains( 'test', $rights, 'sanity check' );
+               $this->assertContains( 'runtest', $rights, 'sanity check' );
+               $this->assertContains( 'writetest', $rights, 'sanity check' );
+               $this->assertNotContains( 'nukeworld', $rights, 'sanity check' );
+
+               // Add a hook manipluating the rights
+               $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserGetRights' => [ function ( $user, &$rights ) {
+                       $rights[] = 'nukeworld';
+                       $rights = array_diff( $rights, [ 'writetest' ] );
+               } ] ] );
+
+               $userWrapper->mRights = null;
+               $rights = $user->getRights();
+               $this->assertContains( 'test', $rights );
+               $this->assertContains( 'runtest', $rights );
+               $this->assertNotContains( 'writetest', $rights );
+               $this->assertContains( 'nukeworld', $rights );
+
+               // Add a Session that limits rights
+               $mock = $this->getMockBuilder( stdclass::class )
+                       ->setMethods( [ 'getAllowedUserRights', 'deregisterSession', 'getSessionId' ] )
+                       ->getMock();
+               $mock->method( 'getAllowedUserRights' )->willReturn( [ 'test', 'writetest' ] );
+               $mock->method( 'getSessionId' )->willReturn(
+                       new MediaWiki\Session\SessionId( str_repeat( 'X', 32 ) )
+               );
+               $session = MediaWiki\Session\TestUtils::getDummySession( $mock );
+               $mockRequest = $this->getMockBuilder( FauxRequest::class )
+                       ->setMethods( [ 'getSession' ] )
+                       ->getMock();
+               $mockRequest->method( 'getSession' )->willReturn( $session );
+               $userWrapper->mRequest = $mockRequest;
+
+               $userWrapper->mRights = null;
+               $rights = $user->getRights();
+               $this->assertContains( 'test', $rights );
+               $this->assertNotContains( 'runtest', $rights );
+               $this->assertNotContains( 'writetest', $rights );
+               $this->assertNotContains( 'nukeworld', $rights );
+       }
+
        /**
         * @dataProvider provideGetGroupsWithPermission
         * @covers User::getGroupsWithPermission
@@ -212,7 +263,7 @@ class UserTest extends MediaWikiTestCase {
         * @group medium
         * @covers User::getEditCount
         */
-       public function testEditCount() {
+       public function testGetEditCount() {
                $user = $this->getMutableTestUser()->getUser();
 
                // let the user have a few (3) edits
@@ -221,17 +272,15 @@ class UserTest extends MediaWikiTestCase {
                        $page->doEdit( (string)$i, 'test', 0, false, $user );
                }
 
-               $user->clearInstanceCache();
                $this->assertEquals(
                        3,
                        $user->getEditCount(),
                        'After three edits, the user edit count should be 3'
                );
 
-               // increase the edit count and clear the cache
+               // increase the edit count
                $user->incEditCount();
 
-               $user->clearInstanceCache();
                $this->assertEquals(
                        4,
                        $user->getEditCount(),
@@ -239,6 +288,46 @@ class UserTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * Test User::editCount
+        * @group medium
+        * @covers User::getEditCount
+        */
+       public function testGetEditCountForAnons() {
+               $user = User::newFromName( 'Anonymous' );
+
+               $this->assertNull(
+                       $user->getEditCount(),
+                       'Edit count starts null for anonymous users.'
+               );
+
+               $user->incEditCount();
+
+               $this->assertNull(
+                       $user->getEditCount(),
+                       'Edit count remains null for anonymous users despite calls to increase it.'
+               );
+       }
+
+       /**
+        * Test User::editCount
+        * @group medium
+        * @covers User::incEditCount
+        */
+       public function testIncEditCount() {
+               $user = $this->getMutableTestUser()->getUser();
+               $user->incEditCount();
+
+               $reloadedUser = User::newFromId( $user->getId() );
+               $reloadedUser->incEditCount();
+
+               $this->assertEquals(
+                       2,
+                       $reloadedUser->getEditCount(),
+                       'Increasing the edit count after a fresh load leaves the object up to date.'
+               );
+       }
+
        /**
         * Test changing user options.
         * @covers User::setOption
@@ -451,4 +540,37 @@ class UserTest extends MediaWikiTestCase {
                $this->assertGreaterThan(
                        $touched, $user->getDBTouched(), "user_touched increased with casOnTouched() #2" );
        }
+
+       /**
+        * @covers User::findUsersByGroup
+        */
+       public function testFindUsersByGroup() {
+               $users = User::findUsersByGroup( [] );
+               $this->assertEquals( 0, iterator_count( $users ) );
+
+               $users = User::findUsersByGroup( 'foo' );
+               $this->assertEquals( 0, iterator_count( $users ) );
+
+               $user = $this->getMutableTestUser( [ 'foo' ] )->getUser();
+               $users = User::findUsersByGroup( 'foo' );
+               $this->assertEquals( 1, iterator_count( $users ) );
+               $users->rewind();
+               $this->assertTrue( $user->equals( $users->current() ) );
+
+               // arguments have OR relationship
+               $user2 = $this->getMutableTestUser( [ 'bar' ] )->getUser();
+               $users = User::findUsersByGroup( [ 'foo', 'bar' ] );
+               $this->assertEquals( 2, iterator_count( $users ) );
+               $users->rewind();
+               $this->assertTrue( $user->equals( $users->current() ) );
+               $users->next();
+               $this->assertTrue( $user2->equals( $users->current() ) );
+
+               // users are not duplicated
+               $user = $this->getMutableTestUser( [ 'baz', 'boom' ] )->getUser();
+               $users = User::findUsersByGroup( [ 'baz', 'boom' ] );
+               $this->assertEquals( 1, iterator_count( $users ) );
+               $users->rewind();
+               $this->assertTrue( $user->equals( $users->current() ) );
+       }
 }
index 2de4bff..0ee4c13 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * @covers FileContentsHasherTest
  */
-class FileContentsHasherTest extends MediaWikiTestCase {
+class FileContentsHasherTest extends PHPUnit_Framework_TestCase {
 
        public function provideSingleFile() {
                return array_map( function ( $file ) {
index 4c85c3d..905d14c 100644 (file)
@@ -4,7 +4,7 @@
  * @group Hash
  */
 
-class MWCryptHashTest extends MediaWikiTestCase {
+class MWCryptHashTest extends PHPUnit_Framework_TestCase {
 
        public function testHashLength() {
                if ( MWCryptHash::hashAlgo() !== 'whirlpool' ) {
diff --git a/tests/phpunit/mocks/media/MockMediaHandlerFactory.php b/tests/phpunit/mocks/media/MockMediaHandlerFactory.php
new file mode 100644 (file)
index 0000000..54d46b0
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Media-handling base classes and generic functionality.
+ *
+ * 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 Media
+ */
+
+/**
+ * Replace all media handlers with a mock. We do not need to generate
+ * actual thumbnails to do parser testing, we only care about receiving
+ * a ThumbnailImage properly initialized.
+ *
+ * @since 1.28
+ */
+class MockMediaHandlerFactory extends MediaHandlerFactory {
+
+       private static $overrides = [
+               'image/svg+xml' => MockSvgHandler::class,
+               'image/vnd.djvu' => MockDjVuHandler::class,
+               'application/ogg' => MockOggHandler::class,
+       ];
+
+       public function __construct() {
+               // override parent
+       }
+
+       protected function getHandlerClass( $type ) {
+               if ( isset( self::$overrides[$type] ) ) {
+                       return self::$overrides[$type];
+               }
+
+               return MockBitmapHandler::class;
+       }
+
+}
index baaa66b..d817104 100755 (executable)
 // through this entry point or not.
 define( 'MW_PHPUNIT_TEST', true );
 
-$wgPhpUnitClass = 'PHPUnit_TextUI_Command';
-
 // Start up MediaWiki in command-line mode
 require_once dirname( dirname( __DIR__ ) ) . "/maintenance/Maintenance.php";
 
 class PHPUnitMaintClass extends Maintenance {
 
        public static $additionalOptions = [
-               'regex' => false,
                'file' => false,
                'use-filebackend' => false,
                'use-bagostuff' => false,
                'use-jobqueue' => false,
-               'keep-uploads' => false,
                'use-normal-tables' => false,
                'reuse-db' => false,
                'wiki' => false,
+               'profiler' => false,
        ];
 
        public function __construct() {
@@ -43,22 +40,10 @@ class PHPUnitMaintClass extends Maintenance {
                        false, # not required
                        false # no arg needed
                );
-               $this->addOption(
-                       'regex',
-                       'Only run parser tests that match the given regex.',
-                       false,
-                       true
-               );
                $this->addOption( 'file', 'File describing parser tests.', false, true );
                $this->addOption( 'use-filebackend', 'Use filebackend', false, true );
                $this->addOption( 'use-bagostuff', 'Use bagostuff', false, true );
                $this->addOption( 'use-jobqueue', 'Use jobqueue', false, true );
-               $this->addOption(
-                       'keep-uploads',
-                       'Re-use the same upload directory for each test, don\'t delete it.',
-                       false,
-                       false
-               );
                $this->addOption( 'use-normal-tables', 'Use normal DB tables.', false, false );
                $this->addOption(
                        'reuse-db', 'Init DB only if tables are missing and keep after finish.',
@@ -70,104 +55,10 @@ class PHPUnitMaintClass extends Maintenance {
        public function finalSetup() {
                parent::finalSetup();
 
-               global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType, $wgMainWANCache;
-               global $wgMainStash;
-               global $wgLanguageConverterCacheType, $wgUseDatabaseMessages;
-               global $wgLocaltimezone, $wgLocalisationCacheConf;
-               global $wgDevelopmentWarnings;
-               global $wgSessionProviders, $wgSessionPbkdf2Iterations;
-               global $wgJobTypeConf;
-               global $wgAuthManagerConfig, $wgAuth, $wgDisableAuthManager;
-
                // Inject test autoloader
-               require_once __DIR__ . '/../TestsAutoLoader.php';
-
-               // wfWarn should cause tests to fail
-               $wgDevelopmentWarnings = true;
-
-               // Make sure all caches and stashes are either disabled or use
-               // in-process cache only to prevent tests from using any preconfigured
-               // cache meant for the local wiki from outside the test run.
-               // See also MediaWikiTestCase::run() which mocks CACHE_DB and APC.
-
-               // Disabled in DefaultSettings, override local settings
-               $wgMainWANCache =
-               $wgMainCacheType = CACHE_NONE;
-               // Uses CACHE_ANYTHING in DefaultSettings, use hash instead of db
-               $wgMessageCacheType =
-               $wgParserCacheType =
-               $wgSessionCacheType =
-               $wgLanguageConverterCacheType = 'hash';
-               // Uses db-replicated in DefaultSettings
-               $wgMainStash = 'hash';
-               // Use memory job queue
-               $wgJobTypeConf = [
-                       'default' => [ 'class' => 'JobQueueMemory', 'order' => 'fifo' ],
-               ];
-
-               $wgUseDatabaseMessages = false; # Set for future resets
-
-               // Assume UTC for testing purposes
-               $wgLocaltimezone = 'UTC';
-
-               $wgLocalisationCacheConf['storeClass'] = 'LCStoreNull';
-
-               // Generic MediaWiki\Session\SessionManager configuration for tests
-               // We use CookieSessionProvider because things might be expecting
-               // cookies to show up in a FauxRequest somewhere.
-               $wgSessionProviders = [
-                       [
-                               'class' => MediaWiki\Session\CookieSessionProvider::class,
-                               'args' => [ [
-                                       'priority' => 30,
-                                       'callUserSetCookiesHook' => true,
-                               ] ],
-                       ],
-               ];
-
-               // Single-iteration PBKDF2 session secret derivation, for speed.
-               $wgSessionPbkdf2Iterations = 1;
+               self::requireTestsAutoloader();
 
-               // Generic AuthManager configuration for testing
-               $wgAuthManagerConfig = [
-                       'preauth' => [],
-                       'primaryauth' => [
-                               [
-                                       'class' => MediaWiki\Auth\TemporaryPasswordPrimaryAuthenticationProvider::class,
-                                       'args' => [ [
-                                               'authoritative' => false,
-                                       ] ],
-                               ],
-                               [
-                                       'class' => MediaWiki\Auth\LocalPasswordPrimaryAuthenticationProvider::class,
-                                       'args' => [ [
-                                               'authoritative' => true,
-                                       ] ],
-                               ],
-                       ],
-                       'secondaryauth' => [],
-               ];
-               $wgAuth = $wgDisableAuthManager ? new AuthPlugin : new MediaWiki\Auth\AuthManagerAuthPlugin();
-
-               // Bug 44192 Do not attempt to send a real e-mail
-               Hooks::clear( 'AlternateUserMailer' );
-               Hooks::register(
-                       'AlternateUserMailer',
-                       function () {
-                               return false;
-                       }
-               );
-               // xdebug's default of 100 is too low for MediaWiki
-               ini_set( 'xdebug.max_nesting_level', 1000 );
-
-               // Bug T116683 serialize_precision of 100
-               // may break testing against floating point values
-               // treated with PHP's serialize()
-               ini_set( 'serialize_precision', 17 );
-
-               // TODO: we should call MediaWikiTestCase::prepareServices( new GlobalVarConfig() ) here.
-               // But PHPUnit may not be loaded yet, so we have to wait until just
-               // before PHPUnit_TextUI_Command::main() is executed at the end of this file.
+               TestSetup::applyInitialConfig();
        }
 
        public function execute() {
@@ -188,9 +79,10 @@ class PHPUnitMaintClass extends Maintenance {
                                [ '--configuration', $IP . '/tests/phpunit/suite.xml' ] );
                }
 
+               $phpUnitClass = 'PHPUnit_TextUI_Command';
+
                if ( $this->hasOption( 'with-phpunitclass' ) ) {
-                       global $wgPhpUnitClass;
-                       $wgPhpUnitClass = $this->getOption( 'with-phpunitclass' );
+                       $phpUnitClass = $this->getOption( 'with-phpunitclass' );
 
                        # Cleanup $args array so the option and its value do not
                        # pollute PHPUnit
@@ -220,6 +112,25 @@ class PHPUnitMaintClass extends Maintenance {
                        }
                }
 
+               if ( !class_exists( 'PHPUnit_Framework_TestCase' ) ) {
+                       echo "PHPUnit not found. Please install it and other dev dependencies by
+               running `composer install` in MediaWiki root directory.\n";
+                       exit( 1 );
+               }
+               if ( !class_exists( $phpUnitClass ) ) {
+                       echo "PHPUnit entry point '" . $phpUnitClass . "' not found. Please make sure you installed
+               the containing component and check the spelling of the class name.\n";
+                       exit( 1 );
+               }
+
+               echo defined( 'HHVM_VERSION' ) ?
+                       'Using HHVM ' . HHVM_VERSION . ' (' . PHP_VERSION . ")\n" :
+                       'Using PHP ' . PHP_VERSION . "\n";
+
+               // Prepare global services for unit tests.
+               MediaWikiTestCase::prepareServices( new GlobalVarConfig() );
+
+               $phpUnitClass::main();
        }
 
        public function getDbType() {
@@ -250,25 +161,3 @@ class PHPUnitMaintClass extends Maintenance {
 
 $maintClass = 'PHPUnitMaintClass';
 require RUN_MAINTENANCE_IF_MAIN;
-
-if ( !class_exists( 'PHPUnit_Framework_TestCase' ) ) {
-       echo "PHPUnit not found. Please install it and other dev dependencies by
-running `composer install` in MediaWiki root directory.\n";
-       exit( 1 );
-}
-if ( !class_exists( $wgPhpUnitClass ) ) {
-       echo "PHPUnit entry point '" . $wgPhpUnitClass . "' not found. Please make sure you installed
-the containing component and check the spelling of the class name.\n";
-       exit( 1 );
-}
-
-echo defined( 'HHVM_VERSION' ) ?
-       'Using HHVM ' . HHVM_VERSION . ' (' . PHP_VERSION . ")\n" :
-       'Using PHP ' . PHP_VERSION . "\n";
-
-// Prepare global services for unit tests.
-// FIXME: this should be done in the finalSetup() method,
-// but PHPUnit may not have been loaded at that point.
-MediaWikiTestCase::prepareServices( new GlobalVarConfig() );
-
-$wgPhpUnitClass::main();
diff --git a/tests/phpunit/specials/SpecialSearchTest.php b/tests/phpunit/specials/SpecialSearchTest.php
new file mode 100644 (file)
index 0000000..20e88f5
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+class SpecialSearchText extends \PHPUnit_Framework_TestCase {
+       public function testSubPageRedirect() {
+               $ctx = new RequestContext;
+
+               SpecialPageFactory::executePath(
+                       Title::newFromText( 'Special:Search/foo_bar' ),
+                       $ctx
+               );
+               $url = $ctx->getOutput()->getRedirect();
+               // some older versions of hhvm have a bug that doesn't parse relative
+               // urls with a port, so help it out a little bit.
+               // https://github.com/facebook/hhvm/issues/7136
+               $url = wfExpandUrl( $url, PROTO_CURRENT );
+
+               $parts = parse_url( $url );
+               $this->assertEquals( '/w/index.php', $parts['path'] );
+               parse_str( $parts['query'], $query );
+               $this->assertEquals( 'Special:Search', $query['title'] );
+               $this->assertEquals( 'foo bar', $query['search'] );
+       }
+}
index 58de8e8..f36b51a 100644 (file)
@@ -143,4 +143,15 @@ class AutoLoaderTest extends MediaWikiTestCase {
                $this->assertFalse( $uncerealized instanceof __PHP_Incomplete_Class,
                        "unserialize() can load classes case-insensitively." );
        }
+
+       function testAutoloadOrder() {
+               $path = realpath( __DIR__ . '/../../..' );
+               $oldAutoload = file_get_contents( $path . '/autoload.php' );
+               $generator = new AutoloadGenerator( $path, 'local' );
+               $generator->initMediaWikiDefault();
+               $newAutoload = $generator->getAutoload( 'maintenance/generateLocalAutoload.php' );
+
+               $this->assertEquals( $oldAutoload, $newAutoload, 'autoload.php does not match' .
+                       ' output of generateLocalAutoload.php script.' );
+       }
 }
index 275c0d1..711eab6 100644 (file)
@@ -74,11 +74,9 @@ class ExtensionJsonValidationTest extends PHPUnit_Framework_TestCase {
                        $version <= ExtensionRegistry::MANIFEST_VERSION,
                        "$path is using a non-supported schema version"
                );
-               $retriever = new JsonSchema\Uri\UriRetriever();
-               $schema = $retriever->retrieve( 'file://' . $schemaPath );
 
-               $validator = new JsonSchema\Validator();
-               $validator->check( $data, $schema );
+               $validator = new JsonSchema\Validator;
+               $validator->check( $data, (object) [ '$ref' => 'file://' . $schemaPath ] );
                if ( $validator->isValid() ) {
                        // All good.
                        $this->assertTrue( true );
index 5c65c1e..86ce53f 100644 (file)
@@ -40,7 +40,7 @@ class ResourcesTest extends MediaWikiTestCase {
                $data = self::getAllModules();
                foreach ( $data['modules'] as $moduleName => $module ) {
                        $version = $module->getVersionHash( $data['context'] );
-                       $this->assertEquals( 8, strlen( $version ), "$moduleName must use ResourceLoader::makeHash" );
+                       $this->assertEquals( 7, strlen( $version ), "$moduleName must use ResourceLoader::makeHash" );
                }
        }
 
@@ -86,6 +86,25 @@ class ResourcesTest extends MediaWikiTestCase {
                }
        }
 
+       /**
+        * Verify that all specified messages actually exist.
+        */
+       public function testMissingMessages() {
+               $data = self::getAllModules();
+               $validDeps = array_keys( $data['modules'] );
+               $lang = Language::factory( 'en' );
+
+               /** @var ResourceLoaderModule $module */
+               foreach ( $data['modules'] as $moduleName => $module ) {
+                       foreach ( $module->getMessages() as $msgKey ) {
+                               $this->assertTrue(
+                                       wfMessage( $msgKey )->useDatabase( false )->inLanguage( $lang )->exists(),
+                                       "Message '$msgKey' required by '$moduleName' must exist"
+                               );
+                       }
+               }
+       }
+
        /**
         * Verify that all dependencies of all modules are always satisfiable with the 'targets' defined
         * for the involved modules.
index ed18205..16299aa 100644 (file)
        <testsuites>
                <testsuite name="includes">
                        <directory>includes</directory>
+                       <!-- Parser tests must be invoked via their suite -->
+                       <exclude>includes/parser/ParserIntegrationTest.php</exclude>
                </testsuite>
                <testsuite name="languages">
                        <directory>languages</directory>
                </testsuite>
                <testsuite name="parsertests">
-                       <file>includes/parser/MediaWikiParserTest.php</file>
+                       <file>suites/CoreParserTestSuite.php</file>
                        <file>suites/ExtensionsParserTestSuite.php</file>
                </testsuite>
                <testsuite name="skins">
@@ -55,7 +57,6 @@
                <exclude>
                        <group>Utility</group>
                        <group>Broken</group>
-                       <group>ParserFuzz</group>
                        <group>Stub</group>
                </exclude>
        </groups>
diff --git a/tests/phpunit/suites/CoreParserTestSuite.php b/tests/phpunit/suites/CoreParserTestSuite.php
new file mode 100644 (file)
index 0000000..e48a116
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+
+class CoreParserTestSuite extends PHPUnit_Framework_TestSuite {
+
+       public static function suite() {
+               return ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::CORE_ONLY );
+       }
+
+}
+
index 3d68b24..8d6ee07 100644 (file)
@@ -2,7 +2,7 @@
 class ExtensionsParserTestSuite extends PHPUnit_Framework_TestSuite {
 
        public static function suite() {
-               return MediaWikiParserTest::suite( MediaWikiParserTest::NO_CORE );
+               return ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::NO_CORE );
        }
 
 }
index 0e23fdd..02934fa 100644 (file)
@@ -8,10 +8,16 @@
 class ExtensionsTestSuite extends PHPUnit_Framework_TestSuite {
        public function __construct() {
                parent::__construct();
+
                $paths = [];
+               // Autodiscover extension unit tests
+               $registry = ExtensionRegistry::getInstance();
+               foreach ( $registry->getAllThings() as $info ) {
+                       $paths[] = dirname( $info['path'] ) . '/tests/phpunit';
+               }
                // Extensions can return a list of files or directories
                Hooks::run( 'UnitTestsList', [ &$paths ] );
-               foreach ( $paths as $path ) {
+               foreach ( array_unique( $paths ) as $path ) {
                        if ( is_dir( $path ) ) {
                                // If the path is a directory, search for test cases.
                                // @since 1.24
@@ -19,7 +25,7 @@ class ExtensionsTestSuite extends PHPUnit_Framework_TestSuite {
                                $fileIterator = new File_Iterator_Facade();
                                $matchingFiles = $fileIterator->getFilesAsArray( $path, $suffixes );
                                $this->addTestFiles( $matchingFiles );
-                       } else {
+                       } elseif ( file_exists( $path ) ) {
                                // Add a single test case or suite class
                                $this->addTestFile( $path );
                        }
diff --git a/tests/phpunit/suites/ParserTestFileSuite.php b/tests/phpunit/suites/ParserTestFileSuite.php
new file mode 100644 (file)
index 0000000..d3129b1
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * This is the suite class for running tests within a single .txt source file.
+ * It is not invoked directly. Use --filter to select files, or
+ * use parserTests.php.
+ */
+class ParserTestFileSuite extends PHPUnit_Framework_TestSuite {
+       private $ptRunner;
+       private $ptFileName;
+       private $ptFileInfo;
+
+       function __construct( $runner, $name, $fileName ) {
+               parent::__construct( $name );
+               $this->ptRunner = $runner;
+               $this->ptFileName = $fileName;
+               $this->ptFileInfo = TestFileReader::read( $this->ptFileName );
+
+               foreach ( $this->ptFileInfo['tests'] as $test ) {
+                       $this->addTest( new ParserIntegrationTest( $runner, $fileName, $test ),
+                               [ 'Database', 'Parser' ] );
+               }
+       }
+
+       function setUp() {
+               $this->ptRunner->addArticles( $this->ptFileInfo[ 'articles'] );
+       }
+}
diff --git a/tests/phpunit/suites/ParserTestTopLevelSuite.php b/tests/phpunit/suites/ParserTestTopLevelSuite.php
new file mode 100644 (file)
index 0000000..4284a77
--- /dev/null
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * The UnitTest must be either a class that inherits from MediaWikiTestCase
+ * or a class that provides a public static suite() method which returns
+ * an PHPUnit_Framework_Test object
+ *
+ * @group Parser
+ * @group ParserTests
+ * @group Database
+ */
+class ParserTestTopLevelSuite extends PHPUnit_Framework_TestSuite {
+       /** @var ParserTestRunner */
+       private $ptRunner;
+
+       /** @var ScopedCallback */
+       private $ptTeardownScope;
+
+       /**
+        * @defgroup filtering_constants Filtering constants
+        *
+        * Limit inclusion of parser tests files coming from MediaWiki core
+        * @{
+        */
+
+       /** Include files shipped with MediaWiki core */
+       const CORE_ONLY = 1;
+       /** Include non core files as set in $wgParserTestFiles */
+       const NO_CORE = 2;
+       /** Include anything set via $wgParserTestFiles */
+       const WITH_ALL = 3; # CORE_ONLY | NO_CORE
+
+       /** @} */
+
+       /**
+        * Get a PHPUnit test suite of parser tests. Optionally filtered with
+        * $flags.
+        *
+        * @par Examples:
+        * Get a suite of parser tests shipped by MediaWiki core:
+        * @code
+        * ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::CORE_ONLY );
+        * @endcode
+        * Get a suite of various parser tests, like extensions:
+        * @code
+        * ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::NO_CORE );
+        * @endcode
+        * Get any test defined via $wgParserTestFiles:
+        * @code
+        * ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::WITH_ALL );
+        * @endcode
+        *
+        * @param int $flags Bitwise flag to filter out the $wgParserTestFiles that
+        * will be included.  Default: ParserTestTopLevelSuite::CORE_ONLY
+        *
+        * @return PHPUnit_Framework_TestSuite
+        */
+       public static function suite( $flags = self::CORE_ONLY ) {
+               return new self( $flags );
+       }
+
+       function __construct( $flags ) {
+               parent::__construct();
+
+               $this->ptRecorder = new PhpunitTestRecorder;
+               $this->ptRunner = new ParserTestRunner( $this->ptRecorder );
+
+               if ( is_string( $flags ) ) {
+                       $flags = self::CORE_ONLY;
+               }
+               global $wgParserTestFiles, $IP;
+
+               $mwTestDir = $IP . '/tests/';
+
+               # Human friendly helpers
+               $wantsCore = ( $flags & self::CORE_ONLY );
+               $wantsRest = ( $flags & self::NO_CORE );
+
+               # Will hold the .txt parser test files we will include
+               $filesToTest = [];
+
+               # Filter out .txt files
+               foreach ( $wgParserTestFiles as $parserTestFile ) {
+                       $isCore = ( 0 === strpos( $parserTestFile, $mwTestDir ) );
+
+                       if ( $isCore && $wantsCore ) {
+                               self::debug( "included core parser tests: $parserTestFile" );
+                               $filesToTest[] = $parserTestFile;
+                       } elseif ( !$isCore && $wantsRest ) {
+                               self::debug( "included non core parser tests: $parserTestFile" );
+                               $filesToTest[] = $parserTestFile;
+                       } else {
+                               self::debug( "skipped parser tests: $parserTestFile" );
+                       }
+               }
+               self::debug( 'parser tests files: '
+                       . implode( ' ', $filesToTest ) );
+
+               $testList = [];
+               $counter = 0;
+               foreach ( $filesToTest as $fileName ) {
+                       // Call the highest level directory the extension name.
+                       // It may or may not actually be, but it should be close
+                       // enough to cause there to be separate names for different
+                       // things, which is good enough for our purposes.
+                       $extensionName = basename( dirname( $fileName ) );
+                       $testsName = $extensionName . '__' . basename( $fileName, '.txt' );
+                       $parserTestClassName = ucfirst( $testsName );
+
+                       // Official spec for class names: http://php.net/manual/en/language.oop5.basic.php
+                       // Prepend 'ParserTest_' to be paranoid about it not starting with a number
+                       $parserTestClassName = 'ParserTest_' .
+                               preg_replace( '/[^a-zA-Z0-9_\x7f-\xff]/', '_', $parserTestClassName );
+
+                       if ( isset( $testList[$parserTestClassName] ) ) {
+                               // If there is a conflict, append a number.
+                               $counter++;
+                               $parserTestClassName .= $counter;
+                       }
+                       $testList[$parserTestClassName] = true;
+
+                       // Previously we actually created a class here, with eval(). We now
+                       // just override the name.
+
+                       self::debug( "Adding test class $parserTestClassName" );
+                       $this->addTest( new ParserTestFileSuite(
+                               $this->ptRunner, $parserTestClassName, $fileName ) );
+               }
+       }
+
+       public function setUp() {
+               wfDebug( __METHOD__ );
+               $db = wfGetDB( DB_MASTER );
+               $type = $db->getType();
+               $prefix = $type === 'oracle' ?
+                       MediaWikiTestCase::ORA_DB_PREFIX : MediaWikiTestCase::DB_PREFIX;
+               MediaWikiTestCase::setupTestDB( $db, $prefix );
+               $teardown = $this->ptRunner->setDatabase( $db );
+               $teardown = $this->ptRunner->setupUploads( $teardown );
+               $this->ptTeardownScope = $teardown;
+       }
+
+       public function tearDown() {
+               wfDebug( __METHOD__ );
+               if ( $this->ptTeardownScope ) {
+                       ScopedCallback::consume( $this->ptTeardownScope );
+               }
+       }
+
+       /**
+        * Write $msg under log group 'tests-parser'
+        * @param string $msg Message to log
+        */
+       protected static function debug( $msg ) {
+               return wfDebugLog( 'tests-parser', wfGetCaller() . ' ' . $msg );
+       }
+}
index 95f28c8..e30088d 100644 (file)
@@ -74,6 +74,7 @@ return [
                        'tests/qunit/suites/resources/mediawiki/mediawiki.template.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.template.mustache.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.test.js',
+                       'tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.html.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.toc.test.js',
index b09bb28..ca26aaf 100644 (file)
                        [ 'Strasse' ]
                ],
 
+               // Data set "digraph"
+               digraphWords = [
+                       [ 'London' ],
+                       [ 'Ljubljana' ],
+                       [ 'Luxembourg' ],
+                       [ 'Njivice' ],
+                       [ 'Norwich' ],
+                       [ 'New York' ]
+               ],
+               digraphWordsSorted = [
+                       [ 'London' ],
+                       [ 'Luxembourg' ],
+                       [ 'Ljubljana' ],
+                       [ 'New York' ],
+                       [ 'Norwich' ],
+                       [ 'Njivice' ]
+               ],
+
                complexMDYDates = [
                        [ 'January, 19 2010' ],
                        [ 'April 21 1991' ],
                }
        );
 
+       tableTest(
+               'Digraphs with custom collation',
+               [ 'City' ],
+               digraphWords,
+               digraphWordsSorted,
+               function ( $table ) {
+                       mw.config.set( 'tableSorterCollation', {
+                               lj: 'lzzzz',
+                               nj: 'nzzzz'
+                       } );
+
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
+       );
+
        QUnit.test( 'Rowspan not exploded on init', 1, function ( assert ) {
                var $table = tableCreate( header, planets );
 
index a480671..0797f32 100644 (file)
                assert.deepEqual( stub.getCall( 0 ).args, [ { foo: 'bar' } ], '#saveOptions called correctly' );
        } );
 
-       QUnit.test( 'saveOptions', function ( assert ) {
+       QUnit.test( 'saveOptions without Unit Separator', function ( assert ) {
                QUnit.expect( 13 );
 
-               var api = new mw.Api();
+               var api = new mw.Api( { useUS: false } );
 
                // We need to respond to the request for token first, otherwise the other requests won't be sent
                // until after the server.respond call, which confuses sinon terribly. This sucks a lot.
                        }
                } );
        } );
+
+       QUnit.test( 'saveOptions with Unit Separator', function ( assert ) {
+               QUnit.expect( 14 );
+
+               var api = new mw.Api( { useUS: true } );
+
+               // We need to respond to the request for token first, otherwise the other requests won't be sent
+               // until after the server.respond call, which confuses sinon terribly. This sucks a lot.
+               api.getToken( 'options' );
+               this.server.respond(
+                       /meta=tokens&type=csrf/,
+                       [ 200, { 'Content-Type': 'application/json' },
+                               '{ "query": { "tokens": { "csrftoken": "+\\\\" } } }' ]
+               );
+
+               api.saveOptions( {} ).done( function () {
+                       assert.ok( true, 'Request completed: empty case' );
+               } );
+               api.saveOptions( { foo: 'bar' } ).done( function () {
+                       assert.ok( true, 'Request completed: simple' );
+               } );
+               api.saveOptions( { foo: 'bar', baz: 'quux' } ).done( function () {
+                       assert.ok( true, 'Request completed: two options' );
+               } );
+               api.saveOptions( { foo: 'bar|quux', bar: 'a|b|c', baz: 'quux' } ).done( function () {
+                       assert.ok( true, 'Request completed: bundleable with unit separator' );
+               } );
+               api.saveOptions( { foo: 'bar|quux', bar: 'a|b|c', 'baz=baz': 'quux' } ).done( function () {
+                       assert.ok( true, 'Request completed: not bundleable with unit separator' );
+               } );
+               api.saveOptions( { foo: null } ).done( function () {
+                       assert.ok( true, 'Request completed: reset an option' );
+               } );
+               api.saveOptions( { 'foo|bar=quux': null } ).done( function () {
+                       assert.ok( true, 'Request completed: reset an option, not bundleable' );
+               } );
+
+               // Requests are POST, match requestBody instead of url
+               this.server.respond( function ( request ) {
+                       switch ( request.requestBody ) {
+                               // simple
+                               case 'action=options&format=json&formatversion=2&change=foo%3Dbar&token=%2B%5C':
+                               // two options
+                               case 'action=options&format=json&formatversion=2&change=foo%3Dbar%7Cbaz%3Dquux&token=%2B%5C':
+                               // bundleable with unit separator
+                               case 'action=options&format=json&formatversion=2&change=%1Ffoo%3Dbar%7Cquux%1Fbar%3Da%7Cb%7Cc%1Fbaz%3Dquux&token=%2B%5C':
+                               // not bundleable with unit separator
+                               case 'action=options&format=json&formatversion=2&optionname=baz%3Dbaz&optionvalue=quux&token=%2B%5C':
+                               case 'action=options&format=json&formatversion=2&change=%1Ffoo%3Dbar%7Cquux%1Fbar%3Da%7Cb%7Cc&token=%2B%5C':
+                               // reset an option
+                               case 'action=options&format=json&formatversion=2&change=foo&token=%2B%5C':
+                               // reset an option, not bundleable
+                               case 'action=options&format=json&formatversion=2&optionname=foo%7Cbar%3Dquux&token=%2B%5C':
+                                       assert.ok( true, 'Repond to ' + request.requestBody );
+                                       request.respond( 200, { 'Content-Type': 'application/json' },
+                                               '{ "options": "success" }' );
+                                       break;
+                               default:
+                                       assert.ok( false, 'Unexpected request: ' + request.requestBody );
+                       }
+               } );
+       } );
 }( mediaWiki ) );
index 991725b..886e2b6 100644 (file)
@@ -38,6 +38,8 @@
                        'A < B',
                        'A > B',
                        'A | B',
+                       'A \t B',
+                       'A \n B',
                        // URL encoding
                        'A%20B',
                        'A%23B',
                assert.equal( title.getPrefixedText(), '.foo' );
        } );
 
-       QUnit.test( 'Transformation', 11, function ( assert ) {
+       QUnit.test( 'Transformation', 12, function ( assert ) {
                var title;
 
                title = new mw.Title( 'File:quux pif.jpg' );
                assert.equal( title.toText(), 'User:HAshAr' );
                assert.equal( title.getNamespaceId(), 2, 'Case-insensitive namespace prefix' );
 
-               // Don't ask why, it's the way the backend works. One space is kept of each set.
-               title = new mw.Title( 'Foo  __  \t __ bar' );
+               title = new mw.Title( 'Foo \u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000 bar' );
                assert.equal( title.getMain(), 'Foo_bar', 'Merge multiple types of whitespace/underscores into a single underscore' );
 
+               title = new mw.Title( 'Foo\u200E\u200F\u202A\u202B\u202C\u202D\u202Ebar' );
+               assert.equal( title.getMain(), 'Foobar', 'Strip Unicode bidi override characters' );
+
                // Regression test: Previously it would only detect an extension if there is no space after it
                title = new mw.Title( 'Example.js  ' );
                assert.equal( title.getExtension(), 'js', 'Space after an extension is stripped' );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js
new file mode 100644 (file)
index 0000000..41d800a
--- /dev/null
@@ -0,0 +1,685 @@
+( function ( mw, $ ) {
+       QUnit.module( 'mediawiki (mw.loader)' );
+
+       mw.loader.addSource(
+               'testloader',
+               QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/load.mock.php' )
+       );
+
+       /**
+        * The sync style load test (for @import). This is, in a way, also an open bug for
+        * ResourceLoader ("execute js after styles are loaded"), but browsers don't offer a
+        * way to get a callback from when a stylesheet is loaded (that is, including any
+        * `@import` rules inside). To work around this, we'll have a little time loop to check
+        * if the styles apply.
+        *
+        * Note: This test originally used new Image() and onerror to get a callback
+        * when the url is loaded, but that is fragile since it doesn't monitor the
+        * same request as the css @import, and Safari 4 has issues with
+        * onerror/onload not being fired at all in weird cases like this.
+        */
+       function assertStyleAsync( assert, $element, prop, val, fn ) {
+               var styleTestStart,
+                       el = $element.get( 0 ),
+                       styleTestTimeout = ( QUnit.config.testTimeout || 5000 ) - 200;
+
+               function isCssImportApplied() {
+                       // Trigger reflow, repaint, redraw, whatever (cross-browser)
+                       var x = $element.css( 'height' );
+                       x = el.innerHTML;
+                       el.className = el.className;
+                       x = document.documentElement.clientHeight;
+
+                       return $element.css( prop ) === val;
+               }
+
+               function styleTestLoop() {
+                       var styleTestSince = new Date().getTime() - styleTestStart;
+                       // If it is passing or if we timed out, run the real test and stop the loop
+                       if ( isCssImportApplied() || styleTestSince > styleTestTimeout ) {
+                               assert.equal( $element.css( prop ), val,
+                                       'style "' + prop + ': ' + val + '" from url is applied (after ' + styleTestSince + 'ms)'
+                               );
+
+                               if ( fn ) {
+                                       fn();
+                               }
+
+                               return;
+                       }
+                       // Otherwise, keep polling
+                       setTimeout( styleTestLoop );
+               }
+
+               // Start the loop
+               styleTestStart = new Date().getTime();
+               styleTestLoop();
+       }
+
+       function urlStyleTest( selector, prop, val ) {
+               return QUnit.fixurl(
+                       mw.config.get( 'wgScriptPath' ) +
+                               '/tests/qunit/data/styleTest.css.php?' +
+                               $.param( {
+                                       selector: selector,
+                                       prop: prop,
+                                       val: val
+                               } )
+               );
+       }
+
+       QUnit.test( 'Basic', 2, function ( assert ) {
+               var isAwesomeDone;
+
+               mw.loader.testCallback = function () {
+                       assert.strictEqual( isAwesomeDone, undefined, 'Implementing module is.awesome: isAwesomeDone should still be undefined' );
+                       isAwesomeDone = true;
+               };
+
+               mw.loader.implement( 'test.callback', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/callMwLoaderTestCallback.js' ) ] );
+
+               return mw.loader.using( 'test.callback', function () {
+                       assert.strictEqual( isAwesomeDone, true, 'test.callback module should\'ve caused isAwesomeDone to be true' );
+                       delete mw.loader.testCallback;
+
+               }, function () {
+                       assert.ok( false, 'Error callback fired while loader.using "test.callback" module' );
+               } );
+       } );
+
+       QUnit.test( 'Object method as module name', 2, function ( assert ) {
+               var isAwesomeDone;
+
+               mw.loader.testCallback = function () {
+                       assert.strictEqual( isAwesomeDone, undefined, 'Implementing module hasOwnProperty: isAwesomeDone should still be undefined' );
+                       isAwesomeDone = true;
+               };
+
+               mw.loader.implement( 'hasOwnProperty', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/callMwLoaderTestCallback.js' ) ], {}, {} );
+
+               return mw.loader.using( 'hasOwnProperty', function () {
+                       assert.strictEqual( isAwesomeDone, true, 'hasOwnProperty module should\'ve caused isAwesomeDone to be true' );
+                       delete mw.loader.testCallback;
+
+               }, function () {
+                       assert.ok( false, 'Error callback fired while loader.using "hasOwnProperty" module' );
+               } );
+       } );
+
+       QUnit.test( '.using( .. ) Promise', 2, function ( assert ) {
+               var isAwesomeDone;
+
+               mw.loader.testCallback = function () {
+                       assert.strictEqual( isAwesomeDone, undefined, 'Implementing module is.awesome: isAwesomeDone should still be undefined' );
+                       isAwesomeDone = true;
+               };
+
+               mw.loader.implement( 'test.promise', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/callMwLoaderTestCallback.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' );
+               } );
+       } );
+
+       QUnit.test( '.implement( styles={ "css": [text, ..] } )', 2, function ( assert ) {
+               var $element = $( '<div class="mw-test-implement-a"></div>' ).appendTo( '#qunit-fixture' );
+
+               assert.notEqual(
+                       $element.css( 'float' ),
+                       'right',
+                       'style is clear'
+               );
+
+               mw.loader.implement(
+                       'test.implement.a',
+                       function () {
+                               assert.equal(
+                                       $element.css( 'float' ),
+                                       'right',
+                                       'style is applied'
+                               );
+                       },
+                       {
+                               all: '.mw-test-implement-a { float: right; }'
+                       }
+               );
+
+               return mw.loader.using( 'test.implement.a' );
+       } );
+
+       QUnit.test( '.implement( styles={ "url": { <media>: [url, ..] } } )', 7, function ( assert ) {
+               var $element1 = $( '<div class="mw-test-implement-b1"></div>' ).appendTo( '#qunit-fixture' ),
+                       $element2 = $( '<div class="mw-test-implement-b2"></div>' ).appendTo( '#qunit-fixture' ),
+                       $element3 = $( '<div class="mw-test-implement-b3"></div>' ).appendTo( '#qunit-fixture' ),
+                       done = assert.async();
+
+               assert.notEqual(
+                       $element1.css( 'text-align' ),
+                       'center',
+                       'style is clear'
+               );
+               assert.notEqual(
+                       $element2.css( 'float' ),
+                       'left',
+                       'style is clear'
+               );
+               assert.notEqual(
+                       $element3.css( 'text-align' ),
+                       'right',
+                       'style is clear'
+               );
+
+               mw.loader.implement(
+                       'test.implement.b',
+                       function () {
+                               // Note: done() must only be called when the entire test is
+                               // complete. So, make sure that we don't start until *both*
+                               // assertStyleAsync calls have completed.
+                               var pending = 2;
+                               assertStyleAsync( assert, $element2, 'float', 'left', function () {
+                                       assert.notEqual( $element1.css( 'text-align' ), 'center', 'print style is not applied' );
+
+                                       pending--;
+                                       if ( pending === 0 ) {
+                                               done();
+                                       }
+                               } );
+                               assertStyleAsync( assert, $element3, 'float', 'right', function () {
+                                       assert.notEqual( $element1.css( 'text-align' ), 'center', 'print style is not applied' );
+
+                                       pending--;
+                                       if ( pending === 0 ) {
+                                               done();
+                                       }
+                               } );
+                       },
+                       {
+                               url: {
+                                       print: [ urlStyleTest( '.mw-test-implement-b1', 'text-align', 'center' ) ],
+                                       screen: [
+                                               // bug 40834: Make sure it actually works with more than 1 stylesheet reference
+                                               urlStyleTest( '.mw-test-implement-b2', 'float', 'left' ),
+                                               urlStyleTest( '.mw-test-implement-b3', 'float', 'right' )
+                                       ]
+                               }
+                       }
+               );
+
+               mw.loader.load( 'test.implement.b' );
+       } );
+
+       // Backwards compatibility
+       QUnit.test( '.implement( styles={ <media>: text } ) (back-compat)', 2, function ( assert ) {
+               var $element = $( '<div class="mw-test-implement-c"></div>' ).appendTo( '#qunit-fixture' );
+
+               assert.notEqual(
+                       $element.css( 'float' ),
+                       'right',
+                       'style is clear'
+               );
+
+               mw.loader.implement(
+                       'test.implement.c',
+                       function () {
+                               assert.equal(
+                                       $element.css( 'float' ),
+                                       'right',
+                                       'style is applied'
+                               );
+                       },
+                       {
+                               all: '.mw-test-implement-c { float: right; }'
+                       }
+               );
+
+               return mw.loader.using( 'test.implement.c' );
+       } );
+
+       // Backwards compatibility
+       QUnit.test( '.implement( styles={ <media>: [url, ..] } ) (back-compat)', 4, function ( assert ) {
+               var $element = $( '<div class="mw-test-implement-d"></div>' ).appendTo( '#qunit-fixture' ),
+                       $element2 = $( '<div class="mw-test-implement-d2"></div>' ).appendTo( '#qunit-fixture' ),
+                       done = assert.async();
+
+               assert.notEqual(
+                       $element.css( 'float' ),
+                       'right',
+                       'style is clear'
+               );
+               assert.notEqual(
+                       $element2.css( 'text-align' ),
+                       'center',
+                       'style is clear'
+               );
+
+               mw.loader.implement(
+                       'test.implement.d',
+                       function () {
+                               assertStyleAsync( assert, $element, 'float', 'right', function () {
+                                       assert.notEqual( $element2.css( 'text-align' ), 'center', 'print style is not applied (bug 40500)' );
+                                       done();
+                               } );
+                       },
+                       {
+                               all: [ urlStyleTest( '.mw-test-implement-d', 'float', 'right' ) ],
+                               print: [ urlStyleTest( '.mw-test-implement-d2', 'text-align', 'center' ) ]
+                       }
+               );
+
+               mw.loader.load( 'test.implement.d' );
+       } );
+
+       // @import (bug 31676)
+       QUnit.test( '.implement( styles has @import )', 7, function ( assert ) {
+               var isJsExecuted, $element,
+                       done = assert.async();
+
+               mw.loader.implement(
+                       'test.implement.import',
+                       function () {
+                               assert.strictEqual( isJsExecuted, undefined, 'script not executed multiple times' );
+                               isJsExecuted = true;
+
+                               assert.equal( mw.loader.getState( 'test.implement.import' ), 'executing', 'module state during implement() script execution' );
+
+                               $element = $( '<div class="mw-test-implement-import">Foo bar</div>' ).appendTo( '#qunit-fixture' );
+
+                               assert.equal( mw.msg( 'test-foobar' ), 'Hello Foobar, $1!', 'messages load before script execution' );
+
+                               assertStyleAsync( assert, $element, 'float', 'right', function () {
+                                       assert.equal( $element.css( 'text-align' ), 'center',
+                                               'CSS styles after the @import rule are working'
+                                       );
+
+                                       done();
+                               } );
+                       },
+                       {
+                               css: [
+                                       '@import url(\''
+                                               + urlStyleTest( '.mw-test-implement-import', 'float', 'right' )
+                                               + '\');\n'
+                                               + '.mw-test-implement-import { text-align: center; }'
+                               ]
+                       },
+                       {
+                               'test-foobar': 'Hello Foobar, $1!'
+                       }
+               );
+
+               mw.loader.using( 'test.implement.import' ).always( function () {
+                       assert.strictEqual( isJsExecuted, true, 'script executed' );
+                       assert.equal( mw.loader.getState( 'test.implement.import' ), 'ready', 'module state after script execution' );
+               } );
+       } );
+
+       QUnit.test( '.implement( dependency with styles )', 4, function ( assert ) {
+               var $element = $( '<div class="mw-test-implement-e"></div>' ).appendTo( '#qunit-fixture' ),
+                       $element2 = $( '<div class="mw-test-implement-e2"></div>' ).appendTo( '#qunit-fixture' );
+
+               assert.notEqual(
+                       $element.css( 'float' ),
+                       'right',
+                       'style is clear'
+               );
+               assert.notEqual(
+                       $element2.css( 'float' ),
+                       'left',
+                       'style is clear'
+               );
+
+               mw.loader.register( [
+                       [ 'test.implement.e', '0', [ 'test.implement.e2' ] ],
+                       [ 'test.implement.e2', '0' ]
+               ] );
+
+               mw.loader.implement(
+                       'test.implement.e',
+                       function () {
+                               assert.equal(
+                                       $element.css( 'float' ),
+                                       'right',
+                                       'Depending module\'s style is applied'
+                               );
+                       },
+                       {
+                               all: '.mw-test-implement-e { float: right; }'
+                       }
+               );
+
+               mw.loader.implement(
+                       'test.implement.e2',
+                       function () {
+                               assert.equal(
+                                       $element2.css( 'float' ),
+                                       'left',
+                                       'Dependency\'s style is applied'
+                               );
+                       },
+                       {
+                               all: '.mw-test-implement-e2 { float: left; }'
+                       }
+               );
+
+               return mw.loader.using( 'test.implement.e' );
+       } );
+
+       QUnit.test( '.implement( only scripts )', 1, function ( assert ) {
+               mw.loader.implement( 'test.onlyscripts', function () {} );
+               assert.strictEqual( mw.loader.getState( 'test.onlyscripts' ), 'ready' );
+       } );
+
+       QUnit.test( '.implement( only messages )', 2, function ( assert ) {
+               assert.assertFalse( mw.messages.exists( 'bug_29107' ), 'Verify that the test message doesn\'t exist yet' );
+
+               // jscs: disable requireCamelCaseOrUpperCaseIdentifiers
+               mw.loader.implement( 'test.implement.msgs', [], {}, { bug_29107: 'loaded' } );
+               // jscs: enable requireCamelCaseOrUpperCaseIdentifiers
+
+               return mw.loader.using( 'test.implement.msgs', function () {
+                       assert.ok( mw.messages.exists( 'bug_29107' ), 'Bug 29107: messages-only module should implement ok' );
+               }, function () {
+                       assert.ok( false, 'Error callback fired while implementing "test.implement.msgs" module' );
+               } );
+       } );
+
+       QUnit.test( '.implement( empty )', 1, function ( assert ) {
+               mw.loader.implement( 'test.empty' );
+               assert.strictEqual( mw.loader.getState( 'test.empty' ), 'ready' );
+       } );
+
+       QUnit.test( 'Broken indirect dependency', 4, function ( assert ) {
+               // don't emit an error event
+               this.sandbox.stub( mw, 'track' );
+
+               mw.loader.register( [
+                       [ 'test.module1', '0' ],
+                       [ 'test.module2', '0', [ 'test.module1' ] ],
+                       [ 'test.module3', '0', [ 'test.module2' ] ]
+               ] );
+               mw.loader.implement( 'test.module1', function () {
+                       throw new Error( 'expected' );
+               }, {}, {} );
+               assert.strictEqual( mw.loader.getState( 'test.module1' ), 'error', 'Expected "error" state for test.module1' );
+               assert.strictEqual( mw.loader.getState( 'test.module2' ), 'error', 'Expected "error" state for test.module2' );
+               assert.strictEqual( mw.loader.getState( 'test.module3' ), 'error', 'Expected "error" state for test.module3' );
+
+               assert.strictEqual( mw.track.callCount, 1 );
+       } );
+
+       QUnit.test( 'Circular dependency', 1, function ( assert ) {
+               mw.loader.register( [
+                       [ 'test.circle1', '0', [ 'test.circle2' ] ],
+                       [ 'test.circle2', '0', [ 'test.circle3' ] ],
+                       [ 'test.circle3', '0', [ 'test.circle1' ] ]
+               ] );
+               assert.throws( function () {
+                       mw.loader.using( 'test.circle3' );
+               }, /Circular/, 'Detect circular dependency' );
+       } );
+
+       QUnit.test( 'Out-of-order implementation', 9, function ( assert ) {
+               mw.loader.register( [
+                       [ 'test.module4', '0' ],
+                       [ 'test.module5', '0', [ 'test.module4' ] ],
+                       [ 'test.module6', '0', [ 'test.module5' ] ]
+               ] );
+               mw.loader.implement( 'test.module4', function () {} );
+               assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
+               assert.strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'Expected "registered" state for test.module5' );
+               assert.strictEqual( mw.loader.getState( 'test.module6' ), 'registered', 'Expected "registered" state for test.module6' );
+               mw.loader.implement( 'test.module6', function () {} );
+               assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
+               assert.strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'Expected "registered" state for test.module5' );
+               assert.strictEqual( mw.loader.getState( 'test.module6' ), 'loaded', 'Expected "loaded" state for test.module6' );
+               mw.loader.implement( 'test.module5', function () {} );
+               assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
+               assert.strictEqual( mw.loader.getState( 'test.module5' ), 'ready', 'Expected "ready" state for test.module5' );
+               assert.strictEqual( mw.loader.getState( 'test.module6' ), 'ready', 'Expected "ready" state for test.module6' );
+       } );
+
+       QUnit.test( 'Missing dependency', 13, function ( assert ) {
+               mw.loader.register( [
+                       [ 'test.module7', '0' ],
+                       [ 'test.module8', '0', [ 'test.module7' ] ],
+                       [ 'test.module9', '0', [ 'test.module8' ] ]
+               ] );
+               mw.loader.implement( 'test.module8', function () {} );
+               assert.strictEqual( mw.loader.getState( 'test.module7' ), 'registered', 'Expected "registered" state for test.module7' );
+               assert.strictEqual( mw.loader.getState( 'test.module8' ), 'loaded', 'Expected "loaded" state for test.module8' );
+               assert.strictEqual( mw.loader.getState( 'test.module9' ), 'registered', 'Expected "registered" state for test.module9' );
+               mw.loader.state( 'test.module7', 'missing' );
+               assert.strictEqual( mw.loader.getState( 'test.module7' ), 'missing', 'Expected "missing" state for test.module7' );
+               assert.strictEqual( mw.loader.getState( 'test.module8' ), 'error', 'Expected "error" state for test.module8' );
+               assert.strictEqual( mw.loader.getState( 'test.module9' ), 'error', 'Expected "error" state for test.module9' );
+               mw.loader.implement( 'test.module9', function () {} );
+               assert.strictEqual( mw.loader.getState( 'test.module7' ), 'missing', 'Expected "missing" state for test.module7' );
+               assert.strictEqual( mw.loader.getState( 'test.module8' ), 'error', 'Expected "error" state for test.module8' );
+               assert.strictEqual( mw.loader.getState( 'test.module9' ), 'error', 'Expected "error" state for test.module9' );
+               mw.loader.using(
+                       [ 'test.module7' ],
+                       function () {
+                               assert.ok( false, 'Success fired despite missing dependency' );
+                               assert.ok( true, 'QUnit expected() count dummy' );
+                       },
+                       function ( e, dependencies ) {
+                               assert.strictEqual( $.isArray( dependencies ), true, 'Expected array of dependencies' );
+                               assert.deepEqual( dependencies, [ 'test.module7' ], 'Error callback called with module test.module7' );
+                       }
+               );
+               mw.loader.using(
+                       [ 'test.module9' ],
+                       function () {
+                               assert.ok( false, 'Success fired despite missing dependency' );
+                               assert.ok( true, 'QUnit expected() count dummy' );
+                       },
+                       function ( e, dependencies ) {
+                               assert.strictEqual( $.isArray( dependencies ), true, 'Expected array of dependencies' );
+                               dependencies.sort();
+                               assert.deepEqual(
+                                       dependencies,
+                                       [ 'test.module7', 'test.module8', 'test.module9' ],
+                                       'Error callback called with all three modules as dependencies'
+                               );
+                       }
+               );
+       } );
+
+       QUnit.test( 'Dependency handling', 5, function ( assert ) {
+               var done = assert.async();
+               mw.loader.register( [
+                       // [module, version, dependencies, group, source]
+                       [ 'testMissing', '1', [], null, 'testloader' ],
+                       [ 'testUsesMissing', '1', [ 'testMissing' ], null, 'testloader' ],
+                       [ 'testUsesNestedMissing', '1', [ 'testUsesMissing' ], null, 'testloader' ]
+               ] );
+
+               function verifyModuleStates() {
+                       assert.equal( mw.loader.getState( 'testMissing' ), 'missing', 'Module not known to server must have state "missing"' );
+                       assert.equal( mw.loader.getState( 'testUsesMissing' ), 'error', 'Module with missing dependency must have state "error"' );
+                       assert.equal( mw.loader.getState( 'testUsesNestedMissing' ), 'error', 'Module with indirect missing dependency must have state "error"' );
+               }
+
+               mw.loader.using( [ 'testUsesNestedMissing' ],
+                       function () {
+                               assert.ok( false, 'Error handler should be invoked.' );
+                               assert.ok( true ); // Dummy to reach QUnit expect()
+
+                               verifyModuleStates();
+
+                               done();
+                       },
+                       function ( e, badmodules ) {
+                               assert.ok( true, 'Error handler should be invoked.' );
+                               // As soon as server spits out state('testMissing', 'missing');
+                               // it will bubble up and trigger the error callback.
+                               // Therefor the badmodules array is not testUsesMissing or testUsesNestedMissing.
+                               assert.deepEqual( badmodules, [ 'testMissing' ], 'Bad modules as expected.' );
+
+                               verifyModuleStates();
+
+                               done();
+                       }
+               );
+       } );
+
+       QUnit.test( 'Skip-function handling', 5, function ( assert ) {
+               mw.loader.register( [
+                       // [module, version, dependencies, group, source, skip]
+                       [ 'testSkipped', '1', [], null, 'testloader', 'return true;' ],
+                       [ 'testNotSkipped', '1', [], null, 'testloader', 'return false;' ],
+                       [ 'testUsesSkippable', '1', [ 'testSkipped', 'testNotSkipped' ], null, 'testloader' ]
+               ] );
+
+               function verifyModuleStates() {
+                       assert.equal( mw.loader.getState( 'testSkipped' ), 'ready', 'Module is ready when skipped' );
+                       assert.equal( mw.loader.getState( 'testNotSkipped' ), 'ready', 'Module is ready when not skipped but loaded' );
+                       assert.equal( mw.loader.getState( 'testUsesSkippable' ), 'ready', 'Module is ready when skippable dependencies are ready' );
+               }
+
+               return mw.loader.using( [ 'testUsesSkippable' ],
+                       function () {
+                               assert.ok( true, 'Success handler should be invoked.' );
+                               assert.ok( true ); // Dummy to match error handler and reach QUnit expect()
+
+                               verifyModuleStates();
+                       },
+                       function ( e, badmodules ) {
+                               assert.ok( false, 'Error handler should not be invoked.' );
+                               assert.deepEqual( badmodules, [], 'Bad modules as expected.' );
+
+                               verifyModuleStates();
+                       }
+               );
+       } );
+
+       QUnit.asyncTest( '.load( "//protocol-relative" ) - T32825', 2, function ( assert ) {
+               // This bug was actually already fixed in 1.18 and later when discovered in 1.17.
+               // Test is for regressions!
+
+               // 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'
+               );
+
+               // Confirm that mw.loader.load() works with protocol-relative URLs
+               target = target.replace( /https?:/, '' );
+
+               assert.equal( target.slice( 0, 2 ), '//',
+                       'URL must be relative to test relative URLs!'
+               );
+
+               // Async!
+               // The target calls QUnit.start
+               mw.loader.load( target );
+       } );
+
+       QUnit.asyncTest( '.load( "/absolute-path" )', 2, function ( assert ) {
+               // Forge a URL to the test callback script
+               var target = QUnit.fixurl(
+                       mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/qunitOkCall.js'
+               );
+
+               // Confirm that mw.loader.load() works with absolute-paths (relative to current hostname)
+               assert.equal( target.slice( 0, 1 ), '/', 'URL is relative to document root' );
+
+               // Async!
+               // The target calls QUnit.start
+               mw.loader.load( target );
+       } );
+
+       QUnit.test( 'Executing race - T112232', 2, function ( assert ) {
+               var done = false;
+
+               // The red herring schedules its CSS buffer first. In T112232, a bug in the
+               // state machine would cause the job for testRaceLoadMe to run with an earlier job.
+               mw.loader.implement(
+                       'testRaceRedHerring',
+                       function () {},
+                       { css: [ '.mw-testRaceRedHerring {}' ] }
+               );
+               mw.loader.implement(
+                       'testRaceLoadMe',
+                       function () {
+                               done = true;
+                       },
+                       { css: [ '.mw-testRaceLoadMe { float: left; }' ] }
+               );
+
+               mw.loader.load( [ 'testRaceRedHerring', 'testRaceLoadMe' ] );
+               return mw.loader.using( 'testRaceLoadMe', function () {
+                       assert.strictEqual( done, true, 'script ran' );
+                       assert.strictEqual( mw.loader.getState( 'testRaceLoadMe' ), 'ready', 'state' );
+               } );
+       } );
+
+       QUnit.test( 'require()', 6, function ( assert ) {
+               mw.loader.register( [
+                       [ 'test.require1', '0' ],
+                       [ 'test.require2', '0' ],
+                       [ 'test.require3', '0' ],
+                       [ 'test.require4', '0', [ 'test.require3' ] ]
+               ] );
+               mw.loader.implement( 'test.require1', function () {} );
+               mw.loader.implement( 'test.require2', function ( $, jQuery, require, module ) {
+                       module.exports = 1;
+               } );
+               mw.loader.implement( 'test.require3', function ( $, jQuery, require, module ) {
+                       module.exports = function () {
+                               return 'hello world';
+                       };
+               } );
+               mw.loader.implement( 'test.require4', function ( $, jQuery, require, module ) {
+                       var other = require( 'test.require3' );
+                       module.exports = {
+                               pizza: function () {
+                                       return other();
+                               }
+                       };
+               } );
+               return mw.loader.using( [ 'test.require1', 'test.require2', 'test.require3', 'test.require4' ] )
+               .then( function ( require ) {
+                       var module1, module2, module3, module4;
+
+                       module1 = require( 'test.require1' );
+                       module2 = require( 'test.require2' );
+                       module3 = require( 'test.require3' );
+                       module4 = require( 'test.require4' );
+
+                       assert.strictEqual( typeof module1, 'object', 'export of module with no export' );
+                       assert.strictEqual( module2, 1, 'export a number' );
+                       assert.strictEqual( module3(), 'hello world', 'export a function' );
+                       assert.strictEqual( typeof module4.pizza, 'function', 'export an object' );
+                       assert.strictEqual( module4.pizza(), 'hello world', 'module can require other modules' );
+
+                       assert.throws( function () {
+                               require( '_badmodule' );
+                       }, /is not loaded/, 'Requesting non-existent modules throws error.' );
+               } );
+       } );
+
+       QUnit.test( 'require() in debug mode', 1, function ( assert ) {
+               var path = mw.config.get( 'wgScriptPath' );
+               mw.loader.register( [
+                       [ 'test.require.define', '0' ],
+                       [ 'test.require.callback', '0', [ 'test.require.define' ] ]
+               ] );
+               mw.loader.implement( 'test.require.callback', [ QUnit.fixurl( path + '/tests/qunit/data/requireCallMwLoaderTestCallback.js' ) ] );
+               mw.loader.implement( 'test.require.define', [ QUnit.fixurl( path + '/tests/qunit/data/defineCallMwLoaderTestCallback.js' ) ] );
+
+               return mw.loader.using( 'test.require.callback' ).then( function ( require ) {
+                       var exported = require( 'test.require.callback' );
+                       assert.strictEqual( exported, 'Require worked.Define worked.',
+                               'module.exports worked in debug mode' );
+               }, function () {
+                       assert.ok( false, 'Error callback fired while loader.using "test.require.callback" module' );
+               } );
+       } );
+
+}( mediaWiki, jQuery ) );
index dd43c55..ab463a9 100644 (file)
@@ -1,5 +1,5 @@
 /*jshint -W024 */
-( function ( mw, $ ) {
+( function ( mw ) {
        var specialCharactersPageName,
                // Can't mock SITENAME since jqueryMsg caches it at load
                siteName = mw.config.get( 'wgSiteName' );
                }
        } ) );
 
-       mw.loader.addSource(
-               'testloader',
-               QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/load.mock.php' )
-       );
-
        QUnit.test( 'Initial check', 8, function ( assert ) {
                assert.ok( window.jQuery, 'jQuery defined' );
                assert.ok( window.$, '$ defined' );
                assert.equal( mw.msg( 'int-msg' ), 'Some Other Message', 'int is resolved' );
        } );
 
-       /**
-        * The sync style load test (for @import). This is, in a way, also an open bug for
-        * ResourceLoader ("execute js after styles are loaded"), but browsers don't offer a
-        * way to get a callback from when a stylesheet is loaded (that is, including any
-        * `@import` rules inside). To work around this, we'll have a little time loop to check
-        * if the styles apply.
-        *
-        * Note: This test originally used new Image() and onerror to get a callback
-        * when the url is loaded, but that is fragile since it doesn't monitor the
-        * same request as the css @import, and Safari 4 has issues with
-        * onerror/onload not being fired at all in weird cases like this.
-        */
-       function assertStyleAsync( assert, $element, prop, val, fn ) {
-               var styleTestStart,
-                       el = $element.get( 0 ),
-                       styleTestTimeout = ( QUnit.config.testTimeout || 5000 ) - 200;
-
-               function isCssImportApplied() {
-                       // Trigger reflow, repaint, redraw, whatever (cross-browser)
-                       var x = $element.css( 'height' );
-                       x = el.innerHTML;
-                       el.className = el.className;
-                       x = document.documentElement.clientHeight;
-
-                       return $element.css( prop ) === val;
-               }
-
-               function styleTestLoop() {
-                       var styleTestSince = new Date().getTime() - styleTestStart;
-                       // If it is passing or if we timed out, run the real test and stop the loop
-                       if ( isCssImportApplied() || styleTestSince > styleTestTimeout ) {
-                               assert.equal( $element.css( prop ), val,
-                                       'style "' + prop + ': ' + val + '" from url is applied (after ' + styleTestSince + 'ms)'
-                               );
-
-                               if ( fn ) {
-                                       fn();
-                               }
-
-                               return;
-                       }
-                       // Otherwise, keep polling
-                       setTimeout( styleTestLoop );
-               }
-
-               // Start the loop
-               styleTestStart = new Date().getTime();
-               styleTestLoop();
-       }
-
-       function urlStyleTest( selector, prop, val ) {
-               return QUnit.fixurl(
-                       mw.config.get( 'wgScriptPath' ) +
-                               '/tests/qunit/data/styleTest.css.php?' +
-                               $.param( {
-                                       selector: selector,
-                                       prop: prop,
-                                       val: val
-                               } )
-               );
-       }
-
-       QUnit.asyncTest( 'mw.loader', 2, function ( assert ) {
-               var isAwesomeDone;
-
-               mw.loader.testCallback = function () {
-                       QUnit.start();
-                       assert.strictEqual( isAwesomeDone, undefined, 'Implementing module is.awesome: isAwesomeDone should still be undefined' );
-                       isAwesomeDone = true;
-               };
-
-               mw.loader.implement( 'test.callback', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/callMwLoaderTestCallback.js' ) ] );
-
-               mw.loader.using( 'test.callback', function () {
-
-                       // /sample/awesome.js declares the "mw.loader.testCallback" function
-                       // which contains a call to start() and ok()
-                       assert.strictEqual( isAwesomeDone, true, 'test.callback module should\'ve caused isAwesomeDone to be true' );
-                       delete mw.loader.testCallback;
-
-               }, function () {
-                       QUnit.start();
-                       assert.ok( false, 'Error callback fired while loader.using "test.callback" module' );
-               } );
-       } );
-
-       QUnit.asyncTest( 'mw.loader with Object method as module name', 2, function ( assert ) {
-               var isAwesomeDone;
-
-               mw.loader.testCallback = function () {
-                       QUnit.start();
-                       assert.strictEqual( isAwesomeDone, undefined, 'Implementing module hasOwnProperty: isAwesomeDone should still be undefined' );
-                       isAwesomeDone = true;
-               };
-
-               mw.loader.implement( 'hasOwnProperty', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/callMwLoaderTestCallback.js' ) ], {}, {} );
-
-               mw.loader.using( 'hasOwnProperty', function () {
-
-                       // /sample/awesome.js declares the "mw.loader.testCallback" function
-                       // which contains a call to start() and ok()
-                       assert.strictEqual( isAwesomeDone, true, 'hasOwnProperty module should\'ve caused isAwesomeDone to be true' );
-                       delete mw.loader.testCallback;
-
-               }, function () {
-                       QUnit.start();
-                       assert.ok( false, 'Error callback fired while loader.using "hasOwnProperty" module' );
-               } );
-       } );
-
-       QUnit.asyncTest( 'mw.loader.using( .. ) Promise', 2, function ( assert ) {
-               var isAwesomeDone;
-
-               mw.loader.testCallback = function () {
-                       QUnit.start();
-                       assert.strictEqual( isAwesomeDone, undefined, 'Implementing module is.awesome: isAwesomeDone should still be undefined' );
-                       isAwesomeDone = true;
-               };
-
-               mw.loader.implement( 'test.promise', [ QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/callMwLoaderTestCallback.js' ) ] );
-
-               mw.loader.using( 'test.promise' )
-               .done( function () {
-
-                       // /sample/awesome.js declares the "mw.loader.testCallback" function
-                       // which contains a call to start() and ok()
-                       assert.strictEqual( isAwesomeDone, true, 'test.promise module should\'ve caused isAwesomeDone to be true' );
-                       delete mw.loader.testCallback;
-
-               } )
-               .fail( function () {
-                       QUnit.start();
-                       assert.ok( false, 'Error callback fired while loader.using "test.promise" module' );
-               } );
-       } );
-
-       QUnit.asyncTest( 'mw.loader.implement( styles={ "css": [text, ..] } )', 2, function ( assert ) {
-               var $element = $( '<div class="mw-test-implement-a"></div>' ).appendTo( '#qunit-fixture' );
-
-               assert.notEqual(
-                       $element.css( 'float' ),
-                       'right',
-                       'style is clear'
-               );
-
-               mw.loader.implement(
-                       'test.implement.a',
-                       function () {
-                               assert.equal(
-                                       $element.css( 'float' ),
-                                       'right',
-                                       'style is applied'
-                               );
-                               QUnit.start();
-                       },
-                       {
-                               all: '.mw-test-implement-a { float: right; }'
-                       }
-               );
-
-               mw.loader.load( [
-                       'test.implement.a'
-               ] );
-       } );
-
-       QUnit.asyncTest( 'mw.loader.implement( styles={ "url": { <media>: [url, ..] } } )', 7, function ( assert ) {
-               var $element1 = $( '<div class="mw-test-implement-b1"></div>' ).appendTo( '#qunit-fixture' ),
-                       $element2 = $( '<div class="mw-test-implement-b2"></div>' ).appendTo( '#qunit-fixture' ),
-                       $element3 = $( '<div class="mw-test-implement-b3"></div>' ).appendTo( '#qunit-fixture' );
-
-               assert.notEqual(
-                       $element1.css( 'text-align' ),
-                       'center',
-                       'style is clear'
-               );
-               assert.notEqual(
-                       $element2.css( 'float' ),
-                       'left',
-                       'style is clear'
-               );
-               assert.notEqual(
-                       $element3.css( 'text-align' ),
-                       'right',
-                       'style is clear'
-               );
-
-               mw.loader.implement(
-                       'test.implement.b',
-                       function () {
-                               // Note: QUnit.start() must only be called when the entire test is
-                               // complete. So, make sure that we don't start until *both*
-                               // assertStyleAsync calls have completed.
-                               var pending = 2;
-                               assertStyleAsync( assert, $element2, 'float', 'left', function () {
-                                       assert.notEqual( $element1.css( 'text-align' ), 'center', 'print style is not applied' );
-
-                                       pending--;
-                                       if ( pending === 0 ) {
-                                               QUnit.start();
-                                       }
-                               } );
-                               assertStyleAsync( assert, $element3, 'float', 'right', function () {
-                                       assert.notEqual( $element1.css( 'text-align' ), 'center', 'print style is not applied' );
-
-                                       pending--;
-                                       if ( pending === 0 ) {
-                                               QUnit.start();
-                                       }
-                               } );
-                       },
-                       {
-                               url: {
-                                       print: [ urlStyleTest( '.mw-test-implement-b1', 'text-align', 'center' ) ],
-                                       screen: [
-                                               // bug 40834: Make sure it actually works with more than 1 stylesheet reference
-                                               urlStyleTest( '.mw-test-implement-b2', 'float', 'left' ),
-                                               urlStyleTest( '.mw-test-implement-b3', 'float', 'right' )
-                                       ]
-                               }
-                       }
-               );
-
-               mw.loader.load( [
-                       'test.implement.b'
-               ] );
-       } );
-
-       // Backwards compatibility
-       QUnit.asyncTest( 'mw.loader.implement( styles={ <media>: text } ) (back-compat)', 2, function ( assert ) {
-               var $element = $( '<div class="mw-test-implement-c"></div>' ).appendTo( '#qunit-fixture' );
-
-               assert.notEqual(
-                       $element.css( 'float' ),
-                       'right',
-                       'style is clear'
-               );
-
-               mw.loader.implement(
-                       'test.implement.c',
-                       function () {
-                               assert.equal(
-                                       $element.css( 'float' ),
-                                       'right',
-                                       'style is applied'
-                               );
-                               QUnit.start();
-                       },
-                       {
-                               all: '.mw-test-implement-c { float: right; }'
-                       }
-               );
-
-               mw.loader.load( [
-                       'test.implement.c'
-               ] );
-       } );
-
-       // Backwards compatibility
-       QUnit.asyncTest( 'mw.loader.implement( styles={ <media>: [url, ..] } ) (back-compat)', 4, function ( assert ) {
-               var $element = $( '<div class="mw-test-implement-d"></div>' ).appendTo( '#qunit-fixture' ),
-                       $element2 = $( '<div class="mw-test-implement-d2"></div>' ).appendTo( '#qunit-fixture' );
-
-               assert.notEqual(
-                       $element.css( 'float' ),
-                       'right',
-                       'style is clear'
-               );
-               assert.notEqual(
-                       $element2.css( 'text-align' ),
-                       'center',
-                       'style is clear'
-               );
-
-               mw.loader.implement(
-                       'test.implement.d',
-                       function () {
-                               assertStyleAsync( assert, $element, 'float', 'right', function () {
-
-                                       assert.notEqual( $element2.css( 'text-align' ), 'center', 'print style is not applied (bug 40500)' );
-
-                                       QUnit.start();
-                               } );
-                       },
-                       {
-                               all: [ urlStyleTest( '.mw-test-implement-d', 'float', 'right' ) ],
-                               print: [ urlStyleTest( '.mw-test-implement-d2', 'text-align', 'center' ) ]
-                       }
-               );
-
-               mw.loader.load( [
-                       'test.implement.d'
-               ] );
-       } );
-
-       // @import (bug 31676)
-       QUnit.asyncTest( 'mw.loader.implement( styles has @import )', 7, function ( assert ) {
-               var isJsExecuted, $element;
-
-               mw.loader.implement(
-                       'test.implement.import',
-                       function () {
-                               assert.strictEqual( isJsExecuted, undefined, 'script not executed multiple times' );
-                               isJsExecuted = true;
-
-                               assert.equal( mw.loader.getState( 'test.implement.import' ), 'executing', 'module state during implement() script execution' );
-
-                               $element = $( '<div class="mw-test-implement-import">Foo bar</div>' ).appendTo( '#qunit-fixture' );
-
-                               assert.equal( mw.msg( 'test-foobar' ), 'Hello Foobar, $1!', 'messages load before script execution' );
-
-                               assertStyleAsync( assert, $element, 'float', 'right', function () {
-                                       assert.equal( $element.css( 'text-align' ), 'center',
-                                               'CSS styles after the @import rule are working'
-                                       );
-
-                                       QUnit.start();
-                               } );
-                       },
-                       {
-                               css: [
-                                       '@import url(\''
-                                               + urlStyleTest( '.mw-test-implement-import', 'float', 'right' )
-                                               + '\');\n'
-                                               + '.mw-test-implement-import { text-align: center; }'
-                               ]
-                       },
-                       {
-                               'test-foobar': 'Hello Foobar, $1!'
-                       }
-               );
-
-               mw.loader.using( 'test.implement.import' ).always( function () {
-                       assert.strictEqual( isJsExecuted, true, 'script executed' );
-                       assert.equal( mw.loader.getState( 'test.implement.import' ), 'ready', 'module state after script execution' );
-               } );
-       } );
-
-       QUnit.asyncTest( 'mw.loader.implement( dependency with styles )', 4, function ( assert ) {
-               var $element = $( '<div class="mw-test-implement-e"></div>' ).appendTo( '#qunit-fixture' ),
-                       $element2 = $( '<div class="mw-test-implement-e2"></div>' ).appendTo( '#qunit-fixture' );
-
-               assert.notEqual(
-                       $element.css( 'float' ),
-                       'right',
-                       'style is clear'
-               );
-               assert.notEqual(
-                       $element2.css( 'float' ),
-                       'left',
-                       'style is clear'
-               );
-
-               mw.loader.register( [
-                       [ 'test.implement.e', '0', [ 'test.implement.e2' ] ],
-                       [ 'test.implement.e2', '0' ]
-               ] );
-
-               mw.loader.implement(
-                       'test.implement.e',
-                       function () {
-                               assert.equal(
-                                       $element.css( 'float' ),
-                                       'right',
-                                       'Depending module\'s style is applied'
-                               );
-                               QUnit.start();
-                       },
-                       {
-                               all: '.mw-test-implement-e { float: right; }'
-                       }
-               );
-
-               mw.loader.implement(
-                       'test.implement.e2',
-                       function () {
-                               assert.equal(
-                                       $element2.css( 'float' ),
-                                       'left',
-                                       'Dependency\'s style is applied'
-                               );
-                       },
-                       {
-                               all: '.mw-test-implement-e2 { float: left; }'
-                       }
-               );
-
-               mw.loader.load( [
-                       'test.implement.e'
-               ] );
-       } );
-
-       QUnit.test( 'mw.loader.implement( only scripts )', 1, function ( assert ) {
-               mw.loader.implement( 'test.onlyscripts', function () {} );
-               assert.strictEqual( mw.loader.getState( 'test.onlyscripts' ), 'ready' );
-       } );
-
-       QUnit.asyncTest( 'mw.loader.implement( only messages )', 2, function ( assert ) {
-               assert.assertFalse( mw.messages.exists( 'bug_29107' ), 'Verify that the test message doesn\'t exist yet' );
-
-               // jscs: disable requireCamelCaseOrUpperCaseIdentifiers
-               mw.loader.implement( 'test.implement.msgs', [], {}, { bug_29107: 'loaded' } );
-               // jscs: enable requireCamelCaseOrUpperCaseIdentifiers
-               mw.loader.using( 'test.implement.msgs', function () {
-                       QUnit.start();
-                       assert.ok( mw.messages.exists( 'bug_29107' ), 'Bug 29107: messages-only module should implement ok' );
-               }, function () {
-                       QUnit.start();
-                       assert.ok( false, 'Error callback fired while implementing "test.implement.msgs" module' );
-               } );
-       } );
-
-       QUnit.test( 'mw.loader.implement( empty )', 1, function ( assert ) {
-               mw.loader.implement( 'test.empty' );
-               assert.strictEqual( mw.loader.getState( 'test.empty' ), 'ready' );
-       } );
-
-       QUnit.test( 'mw.loader with broken indirect dependency', 4, function ( assert ) {
-               // don't emit an error event
-               this.sandbox.stub( mw, 'track' );
-
-               mw.loader.register( [
-                       [ 'test.module1', '0' ],
-                       [ 'test.module2', '0', [ 'test.module1' ] ],
-                       [ 'test.module3', '0', [ 'test.module2' ] ]
-               ] );
-               mw.loader.implement( 'test.module1', function () {
-                       throw new Error( 'expected' );
-               }, {}, {} );
-               assert.strictEqual( mw.loader.getState( 'test.module1' ), 'error', 'Expected "error" state for test.module1' );
-               assert.strictEqual( mw.loader.getState( 'test.module2' ), 'error', 'Expected "error" state for test.module2' );
-               assert.strictEqual( mw.loader.getState( 'test.module3' ), 'error', 'Expected "error" state for test.module3' );
-
-               assert.strictEqual( mw.track.callCount, 1 );
-       } );
-
-       QUnit.test( 'mw.loader with circular dependency', 1, function ( assert ) {
-               mw.loader.register( [
-                       [ 'test.circle1', '0', [ 'test.circle2' ] ],
-                       [ 'test.circle2', '0', [ 'test.circle3' ] ],
-                       [ 'test.circle3', '0', [ 'test.circle1' ] ]
-               ] );
-               assert.throws( function () {
-                       mw.loader.using( 'test.circle3' );
-               }, /Circular/, 'Detect circular dependency' );
-       } );
-
-       QUnit.test( 'mw.loader out-of-order implementation', 9, function ( assert ) {
-               mw.loader.register( [
-                       [ 'test.module4', '0' ],
-                       [ 'test.module5', '0', [ 'test.module4' ] ],
-                       [ 'test.module6', '0', [ 'test.module5' ] ]
-               ] );
-               mw.loader.implement( 'test.module4', function () {} );
-               assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
-               assert.strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'Expected "registered" state for test.module5' );
-               assert.strictEqual( mw.loader.getState( 'test.module6' ), 'registered', 'Expected "registered" state for test.module6' );
-               mw.loader.implement( 'test.module6', function () {} );
-               assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
-               assert.strictEqual( mw.loader.getState( 'test.module5' ), 'registered', 'Expected "registered" state for test.module5' );
-               assert.strictEqual( mw.loader.getState( 'test.module6' ), 'loaded', 'Expected "loaded" state for test.module6' );
-               mw.loader.implement( 'test.module5', function () {} );
-               assert.strictEqual( mw.loader.getState( 'test.module4' ), 'ready', 'Expected "ready" state for test.module4' );
-               assert.strictEqual( mw.loader.getState( 'test.module5' ), 'ready', 'Expected "ready" state for test.module5' );
-               assert.strictEqual( mw.loader.getState( 'test.module6' ), 'ready', 'Expected "ready" state for test.module6' );
-       } );
-
-       QUnit.test( 'mw.loader missing dependency', 13, function ( assert ) {
-               mw.loader.register( [
-                       [ 'test.module7', '0' ],
-                       [ 'test.module8', '0', [ 'test.module7' ] ],
-                       [ 'test.module9', '0', [ 'test.module8' ] ]
-               ] );
-               mw.loader.implement( 'test.module8', function () {} );
-               assert.strictEqual( mw.loader.getState( 'test.module7' ), 'registered', 'Expected "registered" state for test.module7' );
-               assert.strictEqual( mw.loader.getState( 'test.module8' ), 'loaded', 'Expected "loaded" state for test.module8' );
-               assert.strictEqual( mw.loader.getState( 'test.module9' ), 'registered', 'Expected "registered" state for test.module9' );
-               mw.loader.state( 'test.module7', 'missing' );
-               assert.strictEqual( mw.loader.getState( 'test.module7' ), 'missing', 'Expected "missing" state for test.module7' );
-               assert.strictEqual( mw.loader.getState( 'test.module8' ), 'error', 'Expected "error" state for test.module8' );
-               assert.strictEqual( mw.loader.getState( 'test.module9' ), 'error', 'Expected "error" state for test.module9' );
-               mw.loader.implement( 'test.module9', function () {} );
-               assert.strictEqual( mw.loader.getState( 'test.module7' ), 'missing', 'Expected "missing" state for test.module7' );
-               assert.strictEqual( mw.loader.getState( 'test.module8' ), 'error', 'Expected "error" state for test.module8' );
-               assert.strictEqual( mw.loader.getState( 'test.module9' ), 'error', 'Expected "error" state for test.module9' );
-               mw.loader.using(
-                       [ 'test.module7' ],
-                       function () {
-                               assert.ok( false, 'Success fired despite missing dependency' );
-                               assert.ok( true, 'QUnit expected() count dummy' );
-                       },
-                       function ( e, dependencies ) {
-                               assert.strictEqual( $.isArray( dependencies ), true, 'Expected array of dependencies' );
-                               assert.deepEqual( dependencies, [ 'test.module7' ], 'Error callback called with module test.module7' );
-                       }
-               );
-               mw.loader.using(
-                       [ 'test.module9' ],
-                       function () {
-                               assert.ok( false, 'Success fired despite missing dependency' );
-                               assert.ok( true, 'QUnit expected() count dummy' );
-                       },
-                       function ( e, dependencies ) {
-                               assert.strictEqual( $.isArray( dependencies ), true, 'Expected array of dependencies' );
-                               dependencies.sort();
-                               assert.deepEqual(
-                                       dependencies,
-                                       [ 'test.module7', 'test.module8', 'test.module9' ],
-                                       'Error callback called with all three modules as dependencies'
-                               );
-                       }
-               );
-       } );
-
-       QUnit.asyncTest( 'mw.loader dependency handling', 5, function ( assert ) {
-               mw.loader.register( [
-                       // [module, version, dependencies, group, source]
-                       [ 'testMissing', '1', [], null, 'testloader' ],
-                       [ 'testUsesMissing', '1', [ 'testMissing' ], null, 'testloader' ],
-                       [ 'testUsesNestedMissing', '1', [ 'testUsesMissing' ], null, 'testloader' ]
-               ] );
-
-               function verifyModuleStates() {
-                       assert.equal( mw.loader.getState( 'testMissing' ), 'missing', 'Module not known to server must have state "missing"' );
-                       assert.equal( mw.loader.getState( 'testUsesMissing' ), 'error', 'Module with missing dependency must have state "error"' );
-                       assert.equal( mw.loader.getState( 'testUsesNestedMissing' ), 'error', 'Module with indirect missing dependency must have state "error"' );
-               }
-
-               mw.loader.using( [ 'testUsesNestedMissing' ],
-                       function () {
-                               assert.ok( false, 'Error handler should be invoked.' );
-                               assert.ok( true ); // Dummy to reach QUnit expect()
-
-                               verifyModuleStates();
-
-                               QUnit.start();
-                       },
-                       function ( e, badmodules ) {
-                               assert.ok( true, 'Error handler should be invoked.' );
-                               // As soon as server spits out state('testMissing', 'missing');
-                               // it will bubble up and trigger the error callback.
-                               // Therefor the badmodules array is not testUsesMissing or testUsesNestedMissing.
-                               assert.deepEqual( badmodules, [ 'testMissing' ], 'Bad modules as expected.' );
-
-                               verifyModuleStates();
-
-                               QUnit.start();
-                       }
-               );
-       } );
-
-       QUnit.asyncTest( 'mw.loader skin-function handling', 5, function ( assert ) {
-               mw.loader.register( [
-                       // [module, version, dependencies, group, source, skip]
-                       [ 'testSkipped', '1', [], null, 'testloader', 'return true;' ],
-                       [ 'testNotSkipped', '1', [], null, 'testloader', 'return false;' ],
-                       [ 'testUsesSkippable', '1', [ 'testSkipped', 'testNotSkipped' ], null, 'testloader' ]
-               ] );
-
-               function verifyModuleStates() {
-                       assert.equal( mw.loader.getState( 'testSkipped' ), 'ready', 'Module is ready when skipped' );
-                       assert.equal( mw.loader.getState( 'testNotSkipped' ), 'ready', 'Module is ready when not skipped but loaded' );
-                       assert.equal( mw.loader.getState( 'testUsesSkippable' ), 'ready', 'Module is ready when skippable dependencies are ready' );
-               }
-
-               mw.loader.using( [ 'testUsesSkippable' ],
-                       function () {
-                               assert.ok( true, 'Success handler should be invoked.' );
-                               assert.ok( true ); // Dummy to match error handler and reach QUnit expect()
-
-                               verifyModuleStates();
-
-                               QUnit.start();
-                       },
-                       function ( e, badmodules ) {
-                               assert.ok( false, 'Error handler should not be invoked.' );
-                               assert.deepEqual( badmodules, [], 'Bad modules as expected.' );
-
-                               verifyModuleStates();
-
-                               QUnit.start();
-                       }
-               );
-       } );
-
-       QUnit.asyncTest( 'mw.loader( "//protocol-relative" ) (bug 30825)', 2, function ( assert ) {
-               // This bug was actually already fixed in 1.18 and later when discovered in 1.17.
-               // Test is for regressions!
-
-               // 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'
-               );
-
-               // Confirm that mw.loader.load() works with protocol-relative URLs
-               target = target.replace( /https?:/, '' );
-
-               assert.equal( target.slice( 0, 2 ), '//',
-                       'URL must be relative to test relative URLs!'
-               );
-
-               // Async!
-               // The target calls QUnit.start
-               mw.loader.load( target );
-       } );
-
-       QUnit.asyncTest( 'mw.loader( "/absolute-path" )', 2, function ( assert ) {
-               // Forge a URL to the test callback script
-               var target = QUnit.fixurl(
-                       mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/qunitOkCall.js'
-               );
-
-               // Confirm that mw.loader.load() works with absolute-paths (relative to current hostname)
-               assert.equal( target.slice( 0, 1 ), '/', 'URL is relative to document root' );
-
-               // Async!
-               // The target calls QUnit.start
-               mw.loader.load( target );
-       } );
-
-       QUnit.asyncTest( 'mw.loader() executing race (T112232)', 2, function ( assert ) {
-               var done = false;
-
-               // The red herring schedules its CSS buffer first. In T112232, a bug in the
-               // state machine would cause the job for testRaceLoadMe to run with an earlier job.
-               mw.loader.implement(
-                       'testRaceRedHerring',
-                       function () {},
-                       { css: [ '.mw-testRaceRedHerring {}' ] }
-               );
-               mw.loader.implement(
-                       'testRaceLoadMe',
-                       function () {
-                               done = true;
-                       },
-                       { css: [ '.mw-testRaceLoadMe { float: left; }' ] }
-               );
-
-               mw.loader.load( [ 'testRaceRedHerring', 'testRaceLoadMe' ] );
-               mw.loader.using( 'testRaceLoadMe', function () {
-                       assert.strictEqual( done, true, 'script ran' );
-                       assert.strictEqual( mw.loader.getState( 'testRaceLoadMe' ), 'ready', 'state' );
-               } ).always( QUnit.start );
-       } );
-
        QUnit.test( 'mw.hook', 13, function ( assert ) {
                var hook, add, fire, chars, callback;
 
                );
        } );
 
-       QUnit.test( 'mw.loader.require', 6, function ( assert ) {
-               var module1, module2, module3, module4;
-
-               mw.loader.register( [
-                       [ 'test.module.require1', '0' ],
-                       [ 'test.module.require2', '0' ],
-                       [ 'test.module.require3', '0' ],
-                       [ 'test.module.require4', '0', [ 'test.module.require3' ] ]
-               ] );
-               mw.loader.implement( 'test.module.require1', function () {} );
-               mw.loader.implement( 'test.module.require2', function ( $, jQuery, require, module ) {
-                       module.exports = 1;
-               } );
-               mw.loader.implement( 'test.module.require3', function ( $, jQuery, require, module ) {
-                       module.exports = function () {
-                               return 'hello world';
-                       };
-               } );
-               mw.loader.implement( 'test.module.require4', function ( $, jQuery, require, module ) {
-                       var other = require( 'test.module.require3' );
-                       module.exports = {
-                               pizza: function () {
-                                       return other();
-                               }
-                       };
-               } );
-               module1 = mw.loader.require( 'test.module.require1' );
-               module2 = mw.loader.require( 'test.module.require2' );
-               module3 = mw.loader.require( 'test.module.require3' );
-               module4 = mw.loader.require( 'test.module.require4' );
-
-               assert.strictEqual( typeof module1, 'object', 'export of module with no export' );
-               assert.strictEqual( module2, 1, 'export a number' );
-               assert.strictEqual( module3(), 'hello world', 'export a function' );
-               assert.strictEqual( typeof module4.pizza, 'function', 'export an object' );
-               assert.strictEqual( module4.pizza(), 'hello world', 'module can require other modules' );
-
-               assert.throws( function () {
-                       mw.loader.require( '_badmodule' );
-               }, /is not loaded/, 'Requesting non-existent modules throws error.' );
-       } );
-
-       QUnit.asyncTest( 'mw.loader require in debug mode', 1, function ( assert ) {
-               var path = mw.config.get( 'wgScriptPath' );
-               mw.loader.register( [
-                       [ 'test.require.define', '0' ],
-                       [ 'test.require.callback', '0', [ 'test.require.define' ] ]
-               ] );
-               mw.loader.implement( 'test.require.callback', [ QUnit.fixurl( path + '/tests/qunit/data/requireCallMwLoaderTestCallback.js' ) ] );
-               mw.loader.implement( 'test.require.define', [ QUnit.fixurl( path + '/tests/qunit/data/defineCallMwLoaderTestCallback.js' ) ] );
-
-               mw.loader.using( 'test.require.callback', function () {
-                       QUnit.start();
-                       var exported = mw.loader.require( 'test.require.callback' );
-                       assert.strictEqual( exported, 'Require worked.Define worked.',
-                               'module.exports worked in debug mode' );
-               }, function () {
-                       QUnit.start();
-                       assert.ok( false, 'Error callback fired while loader.using "test.require.callback" module' );
-               } );
-       } );
-
-}( mediaWiki, jQuery ) );
+}( mediaWiki ) );
index d697507..4eac362 100644 (file)
                assert.strictEqual( mw.util.getParamValue( 'TEST', url ), 'a b+c d', 'Bug 30441: getParamValue must understand "+" encoding of space (multiple spaces)' );
        } );
 
-       QUnit.test( 'tooltipAccessKey', 4, function ( assert ) {
-               this.suppressWarnings();
-
-               assert.equal( typeof mw.util.tooltipAccessKeyPrefix, 'string', 'tooltipAccessKeyPrefix must be a string' );
-               assert.equal( $.type( mw.util.tooltipAccessKeyRegexp ), 'regexp', 'tooltipAccessKeyRegexp is a regexp' );
-               assert.ok( mw.util.updateTooltipAccessKeys, 'updateTooltipAccessKeys is non-empty' );
-
-               'Example [a]'.replace( mw.util.tooltipAccessKeyRegexp, function ( sub, m1, m2, m3, m4, m5, m6 ) {
-                       assert.equal( m6, 'a', 'tooltipAccessKeyRegexp finds the accesskey hint' );
-               } );
-
-               this.restoreWarnings();
-       } );
-
        QUnit.test( '$content', 2, function ( assert ) {
                assert.ok( mw.util.$content instanceof jQuery, 'mw.util.$content instance of jQuery' );
                assert.strictEqual( mw.util.$content.length, 1, 'mw.util.$content must have length of 1' );
diff --git a/tests/testHelpers.inc b/tests/testHelpers.inc
deleted file mode 100644 (file)
index d04e0fc..0000000
+++ /dev/null
@@ -1,874 +0,0 @@
-<?php
-/**
- * Recording for passing/failing tests.
- *
- * 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 Testing
- */
-
-/**
- * Interface to record parser test results.
- *
- * The ITestRecorder is a very simple interface to record the result of
- * MediaWiki parser tests. One should call start() before running the
- * full parser tests and end() once all the tests have been finished.
- * After each test, you should use record() to keep track of your tests
- * results. Finally, report() is used to generate a summary of your
- * test run, one could dump it to the console for human consumption or
- * register the result in a database for tracking purposes.
- *
- * @since 1.22
- */
-interface ITestRecorder {
-
-       /**
-        * Called at beginning of the parser test run
-        */
-       public function start();
-
-       /**
-        * Called after each test
-        * @param string $test
-        * @param integer $subtest
-        * @param bool $result
-        */
-       public function record( $test, $subtest, $result );
-
-       /**
-        * Called before finishing the test run
-        */
-       public function report();
-
-       /**
-        * Called at the end of the parser test run
-        */
-       public function end();
-
-}
-
-class TestRecorder implements ITestRecorder {
-       public $parent;
-       public $term;
-
-       function __construct( $parent ) {
-               $this->parent = $parent;
-               $this->term = $parent->term;
-       }
-
-       function start() {
-               $this->total = 0;
-               $this->success = 0;
-       }
-
-       function record( $test, $subtest, $result ) {
-               $this->total++;
-               $this->success += ( $result ? 1 : 0 );
-       }
-
-       function end() {
-               // dummy
-       }
-
-       function report() {
-               if ( $this->total > 0 ) {
-                       $this->reportPercentage( $this->success, $this->total );
-               } else {
-                       throw new MWException( "No tests found.\n" );
-               }
-       }
-
-       function reportPercentage( $success, $total ) {
-               $ratio = wfPercent( 100 * $success / $total );
-               print $this->term->color( 1 ) . "Passed $success of $total tests ($ratio)... ";
-
-               if ( $success == $total ) {
-                       print $this->term->color( 32 ) . "ALL TESTS PASSED!";
-               } else {
-                       $failed = $total - $success;
-                       print $this->term->color( 31 ) . "$failed tests failed!";
-               }
-
-               print $this->term->reset() . "\n";
-
-               return ( $success == $total );
-       }
-}
-
-class DbTestPreviewer extends TestRecorder {
-       protected $lb; // /< Database load balancer
-       protected $db; // /< Database connection to the main DB
-       protected $curRun; // /< run ID number for the current run
-       protected $prevRun; // /< run ID number for the previous run, if any
-       protected $results; // /< Result array
-
-       /**
-        * This should be called before the table prefix is changed
-        * @param TestRecorder $parent
-        */
-       function __construct( $parent ) {
-               parent::__construct( $parent );
-
-               $this->lb = wfGetLBFactory()->newMainLB();
-               // This connection will have the wiki's table prefix, not parsertest_
-               $this->db = $this->lb->getConnection( DB_MASTER );
-       }
-
-       /**
-        * Set up result recording; insert a record for the run with the date
-        * and all that fun stuff
-        */
-       function start() {
-               parent::start();
-
-               if ( !$this->db->tableExists( 'testrun', __METHOD__ )
-                       || !$this->db->tableExists( 'testitem', __METHOD__ )
-               ) {
-                       print "WARNING> `testrun` table not found in database.\n";
-                       $this->prevRun = false;
-               } else {
-                       // We'll make comparisons against the previous run later...
-                       $this->prevRun = $this->db->selectField( 'testrun', 'MAX(tr_id)' );
-               }
-
-               $this->results = [];
-       }
-
-       function getName( $test, $subtest ) {
-               if ( $subtest ) {
-                       return "$test subtest #$subtest";
-               } else {
-                       return $test;
-               }
-       }
-
-       function record( $test, $subtest, $result ) {
-               parent::record( $test, $subtest, $result );
-               $this->results[ $this->getName( $test, $subtest ) ] = $result;
-       }
-
-       function report() {
-               if ( $this->prevRun ) {
-                       // f = fail, p = pass, n = nonexistent
-                       // codes show before then after
-                       $table = [
-                               'fp' => 'previously failing test(s) now PASSING! :)',
-                               'pn' => 'previously PASSING test(s) removed o_O',
-                               'np' => 'new PASSING test(s) :)',
-
-                               'pf' => 'previously passing test(s) now FAILING! :(',
-                               'fn' => 'previously FAILING test(s) removed O_o',
-                               'nf' => 'new FAILING test(s) :(',
-                               'ff' => 'still FAILING test(s) :(',
-                       ];
-
-                       $prevResults = [];
-
-                       $res = $this->db->select( 'testitem', [ 'ti_name', 'ti_success' ],
-                               [ 'ti_run' => $this->prevRun ], __METHOD__ );
-
-                       foreach ( $res as $row ) {
-                               if ( !$this->parent->regex
-                                       || preg_match( "/{$this->parent->regex}/i", $row->ti_name )
-                               ) {
-                                       $prevResults[$row->ti_name] = $row->ti_success;
-                               }
-                       }
-
-                       $combined = array_keys( $this->results + $prevResults );
-
-                       # Determine breakdown by change type
-                       $breakdown = [];
-                       foreach ( $combined as $test ) {
-                               if ( !isset( $prevResults[$test] ) ) {
-                                       $before = 'n';
-                               } elseif ( $prevResults[$test] == 1 ) {
-                                       $before = 'p';
-                               } else /* if ( $prevResults[$test] == 0 )*/ {
-                                       $before = 'f';
-                               }
-
-                               if ( !isset( $this->results[$test] ) ) {
-                                       $after = 'n';
-                               } elseif ( $this->results[$test] == 1 ) {
-                                       $after = 'p';
-                               } else /*if ( $this->results[$test] == 0 ) */ {
-                                       $after = 'f';
-                               }
-
-                               $code = $before . $after;
-
-                               if ( isset( $table[$code] ) ) {
-                                       $breakdown[$code][$test] = $this->getTestStatusInfo( $test, $after );
-                               }
-                       }
-
-                       # Write out results
-                       foreach ( $table as $code => $label ) {
-                               if ( !empty( $breakdown[$code] ) ) {
-                                       $count = count( $breakdown[$code] );
-                                       printf( "\n%4d %s\n", $count, $label );
-
-                                       foreach ( $breakdown[$code] as $differing_test_name => $statusInfo ) {
-                                               print "      * $differing_test_name  [$statusInfo]\n";
-                                       }
-                               }
-                       }
-               } else {
-                       print "No previous test runs to compare against.\n";
-               }
-
-               print "\n";
-               parent::report();
-       }
-
-       /**
-        * Returns a string giving information about when a test last had a status change.
-        * Could help to track down when regressions were introduced, as distinct from tests
-        * which have never passed (which are more change requests than regressions).
-        * @param string $testname
-        * @param string $after
-        * @return string
-        */
-       private function getTestStatusInfo( $testname, $after ) {
-               // If we're looking at a test that has just been removed, then say when it first appeared.
-               if ( $after == 'n' ) {
-                       $changedRun = $this->db->selectField( 'testitem',
-                               'MIN(ti_run)',
-                               [ 'ti_name' => $testname ],
-                               __METHOD__ );
-                       $appear = $this->db->selectRow( 'testrun',
-                               [ 'tr_date', 'tr_mw_version' ],
-                               [ 'tr_id' => $changedRun ],
-                               __METHOD__ );
-
-                       return "First recorded appearance: "
-                               . date( "d-M-Y H:i:s", strtotime( $appear->tr_date ) )
-                               . ", " . $appear->tr_mw_version;
-               }
-
-               // Otherwise, this test has previous recorded results.
-               // See when this test last had a different result to what we're seeing now.
-               $conds = [
-                       'ti_name' => $testname,
-                       'ti_success' => ( $after == 'f' ? "1" : "0" ) ];
-
-               if ( $this->curRun ) {
-                       $conds[] = "ti_run != " . $this->db->addQuotes( $this->curRun );
-               }
-
-               $changedRun = $this->db->selectField( 'testitem', 'MAX(ti_run)', $conds, __METHOD__ );
-
-               // If no record of ever having had a different result.
-               if ( is_null( $changedRun ) ) {
-                       if ( $after == "f" ) {
-                               return "Has never passed";
-                       } else {
-                               return "Has never failed";
-                       }
-               }
-
-               // Otherwise, we're looking at a test whose status has changed.
-               // (i.e. it used to work, but now doesn't; or used to fail, but is now fixed.)
-               // In this situation, give as much info as we can as to when it changed status.
-               $pre = $this->db->selectRow( 'testrun',
-                       [ 'tr_date', 'tr_mw_version' ],
-                       [ 'tr_id' => $changedRun ],
-                       __METHOD__ );
-               $post = $this->db->selectRow( 'testrun',
-                       [ 'tr_date', 'tr_mw_version' ],
-                       [ "tr_id > " . $this->db->addQuotes( $changedRun ) ],
-                       __METHOD__,
-                       [ "LIMIT" => 1, "ORDER BY" => 'tr_id' ]
-               );
-
-               if ( $post ) {
-                       $postDate = date( "d-M-Y H:i:s", strtotime( $post->tr_date ) ) . ", {$post->tr_mw_version}";
-               } else {
-                       $postDate = 'now';
-               }
-
-               return ( $after == "f" ? "Introduced" : "Fixed" ) . " between "
-                       . date( "d-M-Y H:i:s", strtotime( $pre->tr_date ) ) . ", " . $pre->tr_mw_version
-                       . " and $postDate";
-       }
-
-       /**
-        * Close the DB connection
-        */
-       function end() {
-               $this->lb->closeAll();
-               parent::end();
-       }
-}
-
-class DbTestRecorder extends DbTestPreviewer {
-       public $version;
-
-       /**
-        * Set up result recording; insert a record for the run with the date
-        * and all that fun stuff
-        */
-       function start() {
-               $this->db->begin( __METHOD__ );
-
-               if ( !$this->db->tableExists( 'testrun' )
-                       || !$this->db->tableExists( 'testitem' )
-               ) {
-                       print "WARNING> `testrun` table not found in database. Trying to create table.\n";
-                       $this->db->sourceFile( $this->db->patchPath( 'patch-testrun.sql' ) );
-                       echo "OK, resuming.\n";
-               }
-
-               parent::start();
-
-               $this->db->insert( 'testrun',
-                       [
-                               'tr_date' => $this->db->timestamp(),
-                               'tr_mw_version' => $this->version,
-                               'tr_php_version' => PHP_VERSION,
-                               'tr_db_version' => $this->db->getServerVersion(),
-                               'tr_uname' => php_uname()
-                       ],
-                       __METHOD__ );
-               if ( $this->db->getType() === 'postgres' ) {
-                       $this->curRun = $this->db->currentSequenceValue( 'testrun_id_seq' );
-               } else {
-                       $this->curRun = $this->db->insertId();
-               }
-       }
-
-       /**
-        * Record an individual test item's success or failure to the db
-        *
-        * @param string $test
-        * @param bool $result
-        */
-       function record( $test, $subtest, $result ) {
-               parent::record( $test, $subtest, $result );
-
-               $this->db->insert( 'testitem',
-                       [
-                               'ti_run' => $this->curRun,
-                               'ti_name' => $this->getName( $test, $subtest ),
-                               'ti_success' => $result ? 1 : 0,
-                       ],
-                       __METHOD__ );
-       }
-
-       /**
-        * Commit transaction and clean up for result recording
-        */
-       function end() {
-               $this->db->commit( __METHOD__ );
-               parent::end();
-       }
-}
-
-class TestFileIterator implements Iterator {
-       private $file;
-       private $fh;
-       /**
-        * @var ParserTest|MediaWikiParserTest An instance of ParserTest (parserTests.php)
-        *  or MediaWikiParserTest (phpunit)
-        */
-       private $parserTest;
-       private $index = 0;
-       private $test;
-       private $section = null;
-       /** String|null: current test section being analyzed */
-       private $sectionData = [];
-       private $lineNum;
-       private $eof;
-       # Create a fake parser tests which never run anything unless
-       # asked to do so. This will avoid running hooks for a disabled test
-       private $delayedParserTest;
-       private $nextSubTest = 0;
-
-       function __construct( $file, $parserTest ) {
-               $this->file = $file;
-               $this->fh = fopen( $this->file, "rt" );
-
-               if ( !$this->fh ) {
-                       throw new MWException( "Couldn't open file '$file'\n" );
-               }
-
-               $this->parserTest = $parserTest;
-               $this->delayedParserTest = new DelayedParserTest();
-
-               $this->lineNum = $this->index = 0;
-       }
-
-       function rewind() {
-               if ( fseek( $this->fh, 0 ) ) {
-                       throw new MWException( "Couldn't fseek to the start of '$this->file'\n" );
-               }
-
-               $this->index = -1;
-               $this->lineNum = 0;
-               $this->eof = false;
-               $this->next();
-
-               return true;
-       }
-
-       function current() {
-               return $this->test;
-       }
-
-       function key() {
-               return $this->index;
-       }
-
-       function next() {
-               if ( $this->readNextTest() ) {
-                       $this->index++;
-                       return true;
-               } else {
-                       $this->eof = true;
-               }
-       }
-
-       function valid() {
-               return $this->eof != true;
-       }
-
-       function setupCurrentTest() {
-               // "input" and "result" are old section names allowed
-               // for backwards-compatibility.
-               $input = $this->checkSection( [ 'wikitext', 'input' ], false );
-               $result = $this->checkSection( [ 'html/php', 'html/*', 'html', 'result' ], false );
-               // some tests have "with tidy" and "without tidy" variants
-               $tidy = $this->checkSection( [ 'html/php+tidy', 'html+tidy' ], false );
-               if ( $tidy != false ) {
-                       if ( $this->nextSubTest == 0 ) {
-                               if ( $result != false ) {
-                                       $this->nextSubTest = 1; // rerun non-tidy variant later
-                               }
-                               $result = $tidy;
-                       } else {
-                               $this->nextSubTest = 0; // go on to next test after this
-                               $tidy = false;
-                       }
-               }
-
-               if ( !isset( $this->sectionData['options'] ) ) {
-                       $this->sectionData['options'] = '';
-               }
-
-               if ( !isset( $this->sectionData['config'] ) ) {
-                       $this->sectionData['config'] = '';
-               }
-
-               $isDisabled = preg_match( '/\\bdisabled\\b/i', $this->sectionData['options'] ) &&
-                       !$this->parserTest->runDisabled;
-               $isParsoidOnly = preg_match( '/\\bparsoid\\b/i', $this->sectionData['options'] ) &&
-                       $result == 'html' &&
-                       !$this->parserTest->runParsoid;
-               $isFiltered = !preg_match( "/" . $this->parserTest->regex . "/i", $this->sectionData['test'] );
-               if ( $input == false || $result == false || $isDisabled || $isParsoidOnly || $isFiltered ) {
-                       # disabled test
-                       return false;
-               }
-
-               # We are really going to run the test, run pending hooks and hooks function
-               wfDebug( __METHOD__ . " unleashing delayed test for: {$this->sectionData['test']}" );
-               $hooksResult = $this->delayedParserTest->unleash( $this->parserTest );
-               if ( !$hooksResult ) {
-                       # Some hook reported an issue. Abort.
-                       throw new MWException( "Problem running requested parser hook from the test file" );
-               }
-
-               $this->test = [
-                       'test' => ParserTest::chomp( $this->sectionData['test'] ),
-                       'subtest' => $this->nextSubTest,
-                       'input' => ParserTest::chomp( $this->sectionData[$input] ),
-                       'result' => ParserTest::chomp( $this->sectionData[$result] ),
-                       'options' => ParserTest::chomp( $this->sectionData['options'] ),
-                       'config' => ParserTest::chomp( $this->sectionData['config'] ),
-               ];
-               if ( $tidy != false ) {
-                       $this->test['options'] .= " tidy";
-               }
-               return true;
-       }
-
-       function readNextTest() {
-               # Run additional subtests of previous test
-               while ( $this->nextSubTest > 0 ) {
-                       if ( $this->setupCurrentTest() ) {
-                               return true;
-                       }
-               }
-
-               $this->clearSection();
-               # Reset hooks for the delayed test object
-               $this->delayedParserTest->reset();
-
-               while ( false !== ( $line = fgets( $this->fh ) ) ) {
-                       $this->lineNum++;
-                       $matches = [];
-
-                       if ( preg_match( '/^!!\s*(\S+)/', $line, $matches ) ) {
-                               $this->section = strtolower( $matches[1] );
-
-                               if ( $this->section == 'endarticle' ) {
-                                       $this->checkSection( 'text' );
-                                       $this->checkSection( 'article' );
-
-                                       $this->parserTest->addArticle(
-                                               ParserTest::chomp( $this->sectionData['article'] ),
-                                               $this->sectionData['text'], $this->lineNum );
-
-                                       $this->clearSection();
-
-                                       continue;
-                               }
-
-                               if ( $this->section == 'endhooks' ) {
-                                       $this->checkSection( 'hooks' );
-
-                                       foreach ( explode( "\n", $this->sectionData['hooks'] ) as $line ) {
-                                               $line = trim( $line );
-
-                                               if ( $line ) {
-                                                       $this->delayedParserTest->requireHook( $line );
-                                               }
-                                       }
-
-                                       $this->clearSection();
-
-                                       continue;
-                               }
-
-                               if ( $this->section == 'endfunctionhooks' ) {
-                                       $this->checkSection( 'functionhooks' );
-
-                                       foreach ( explode( "\n", $this->sectionData['functionhooks'] ) as $line ) {
-                                               $line = trim( $line );
-
-                                               if ( $line ) {
-                                                       $this->delayedParserTest->requireFunctionHook( $line );
-                                               }
-                                       }
-
-                                       $this->clearSection();
-
-                                       continue;
-                               }
-
-                               if ( $this->section == 'endtransparenthooks' ) {
-                                       $this->checkSection( 'transparenthooks' );
-
-                                       foreach ( explode( "\n", $this->sectionData['transparenthooks'] ) as $line ) {
-                                               $line = trim( $line );
-
-                                               if ( $line ) {
-                                                       $this->delayedParserTest->requireTransparentHook( $line );
-                                               }
-                                       }
-
-                                       $this->clearSection();
-
-                                       continue;
-                               }
-
-                               if ( $this->section == 'end' ) {
-                                       $this->checkSection( 'test' );
-                                       do {
-                                               if ( $this->setupCurrentTest() ) {
-                                                       return true;
-                                               }
-                                       } while ( $this->nextSubTest > 0 );
-                                       # go on to next test (since this was disabled)
-                                       $this->clearSection();
-                                       $this->delayedParserTest->reset();
-                                       continue;
-                               }
-
-                               if ( isset( $this->sectionData[$this->section] ) ) {
-                                       throw new MWException( "duplicate section '$this->section' "
-                                               . "at line {$this->lineNum} of $this->file\n" );
-                               }
-
-                               $this->sectionData[$this->section] = '';
-
-                               continue;
-                       }
-
-                       if ( $this->section ) {
-                               $this->sectionData[$this->section] .= $line;
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Clear section name and its data
-        */
-       private function clearSection() {
-               $this->sectionData = [];
-               $this->section = null;
-
-       }
-
-       /**
-        * Verify the current section data has some value for the given token
-        * name(s) (first parameter).
-        * Throw an exception if it is not set, referencing current section
-        * and adding the current file name and line number
-        *
-        * @param string|array $tokens Expected token(s) that should have been
-        * mentioned before closing this section
-        * @param bool $fatal True iff an exception should be thrown if
-        * the section is not found.
-        * @return bool|string
-        * @throws MWException
-        */
-       private function checkSection( $tokens, $fatal = true ) {
-               if ( is_null( $this->section ) ) {
-                       throw new MWException( __METHOD__ . " can not verify a null section!\n" );
-               }
-               if ( !is_array( $tokens ) ) {
-                       $tokens = [ $tokens ];
-               }
-               if ( count( $tokens ) == 0 ) {
-                       throw new MWException( __METHOD__ . " can not verify zero sections!\n" );
-               }
-
-               $data = $this->sectionData;
-               $tokens = array_filter( $tokens, function ( $token ) use ( $data ) {
-                       return isset( $data[$token] );
-               } );
-
-               if ( count( $tokens ) == 0 ) {
-                       if ( !$fatal ) {
-                               return false;
-                       }
-                       throw new MWException( sprintf(
-                               "'%s' without '%s' at line %s of %s\n",
-                               $this->section,
-                               implode( ',', $tokens ),
-                               $this->lineNum,
-                               $this->file
-                       ) );
-               }
-               if ( count( $tokens ) > 1 ) {
-                       throw new MWException( sprintf(
-                               "'%s' with unexpected tokens '%s' at line %s of %s\n",
-                               $this->section,
-                               implode( ',', $tokens ),
-                               $this->lineNum,
-                               $this->file
-                       ) );
-               }
-
-               return array_values( $tokens )[0];
-       }
-}
-
-/**
- * An iterator for use as a phpunit data provider. Provides the test arguments
- * in the order expected by NewParserTest::testParserTest().
- */
-class TestFileDataProvider extends TestFileIterator {
-       function current() {
-               $test = parent::current();
-               if ( $test ) {
-                       return [
-                               $test['test'],
-                               $test['input'],
-                               $test['result'],
-                               $test['options'],
-                               $test['config'],
-                       ];
-               } else {
-                       return $test;
-               }
-       }
-}
-
-/**
- * A class to delay execution of a parser test hooks.
- */
-class DelayedParserTest {
-
-       /** Initialized on construction */
-       private $hooks;
-       private $fnHooks;
-       private $transparentHooks;
-
-       public function __construct() {
-               $this->reset();
-       }
-
-       /**
-        * Init/reset or forgot about the current delayed test.
-        * Call to this will erase any hooks function that were pending.
-        */
-       public function reset() {
-               $this->hooks = [];
-               $this->fnHooks = [];
-               $this->transparentHooks = [];
-       }
-
-       /**
-        * Called whenever we actually want to run the hook.
-        * Should be the case if we found the parserTest is not disabled
-        * @param ParserTest|NewParserTest $parserTest
-        * @return bool
-        * @throws MWException
-        */
-       public function unleash( &$parserTest ) {
-               if ( !( $parserTest instanceof ParserTest || $parserTest instanceof NewParserTest ) ) {
-                       throw new MWException( __METHOD__ . " must be passed an instance of ParserTest or "
-                               . "NewParserTest classes\n" );
-               }
-
-               # Trigger delayed hooks. Any failure will make us abort
-               foreach ( $this->hooks as $hook ) {
-                       $ret = $parserTest->requireHook( $hook );
-                       if ( !$ret ) {
-                               return false;
-                       }
-               }
-
-               # Trigger delayed function hooks. Any failure will make us abort
-               foreach ( $this->fnHooks as $fnHook ) {
-                       $ret = $parserTest->requireFunctionHook( $fnHook );
-                       if ( !$ret ) {
-                               return false;
-                       }
-               }
-
-               # Trigger delayed transparent hooks. Any failure will make us abort
-               foreach ( $this->transparentHooks as $hook ) {
-                       $ret = $parserTest->requireTransparentHook( $hook );
-                       if ( !$ret ) {
-                               return false;
-                       }
-               }
-
-               # Delayed execution was successful.
-               return true;
-       }
-
-       /**
-        * Similar to ParserTest object but does not run anything
-        * Use unleash() to really execute the hook
-        * @param string $hook
-        */
-       public function requireHook( $hook ) {
-               $this->hooks[] = $hook;
-       }
-
-       /**
-        * Similar to ParserTest object but does not run anything
-        * Use unleash() to really execute the hook function
-        * @param string $fnHook
-        */
-       public function requireFunctionHook( $fnHook ) {
-               $this->fnHooks[] = $fnHook;
-       }
-
-       /**
-        * Similar to ParserTest object but does not run anything
-        * Use unleash() to really execute the hook function
-        * @param string $hook
-        */
-       public function requireTransparentHook( $hook ) {
-               $this->transparentHooks[] = $hook;
-       }
-
-}
-
-/**
- * Initialize and detect the DjVu files support
- */
-class DjVuSupport {
-
-       /**
-        * Initialises DjVu tools global with default values
-        */
-       public function __construct() {
-               global $wgDjvuRenderer, $wgDjvuDump, $wgDjvuToXML, $wgFileExtensions, $wgDjvuTxt;
-
-               $wgDjvuRenderer = $wgDjvuRenderer ? $wgDjvuRenderer : '/usr/bin/ddjvu';
-               $wgDjvuDump = $wgDjvuDump ? $wgDjvuDump : '/usr/bin/djvudump';
-               $wgDjvuToXML = $wgDjvuToXML ? $wgDjvuToXML : '/usr/bin/djvutoxml';
-               $wgDjvuTxt = $wgDjvuTxt ? $wgDjvuTxt : '/usr/bin/djvutxt';
-
-               if ( !in_array( 'djvu', $wgFileExtensions ) ) {
-                       $wgFileExtensions[] = 'djvu';
-               }
-       }
-
-       /**
-        * Returns true if the DjVu tools are usable
-        *
-        * @return bool
-        */
-       public function isEnabled() {
-               global $wgDjvuRenderer, $wgDjvuDump, $wgDjvuToXML, $wgDjvuTxt;
-
-               return is_executable( $wgDjvuRenderer )
-                       && is_executable( $wgDjvuDump )
-                       && is_executable( $wgDjvuToXML )
-                       && is_executable( $wgDjvuTxt );
-       }
-}
-
-/**
- * Initialize and detect the tidy support
- */
-class TidySupport {
-       private $internalTidy;
-       private $externalTidy;
-
-       /**
-        * Determine if there is a usable tidy.
-        */
-       public function __construct() {
-               global $wgTidyBin;
-
-               $this->internalTidy = extension_loaded( 'tidy' ) &&
-                       class_exists( 'tidy' ) && !wfIsHHVM();
-
-               $this->externalTidy = is_executable( $wgTidyBin ) ||
-                       Installer::locateExecutableInDefaultPaths( [ $wgTidyBin ] )
-                       !== false;
-       }
-
-       /**
-        * Returns true if we should use internal tidy.
-        *
-        * @return bool
-        */
-       public function isInternal() {
-               return $this->internalTidy;
-       }
-
-       /**
-        * Returns true if tidy is usable
-        *
-        * @return bool
-        */
-       public function isEnabled() {
-               return $this->internalTidy || $this->externalTidy;
-       }
-}